相同输入的情况下onnx模型与其转换后的ncnn模型推理结果存在偏差
日志或报错信息
相同输入的情况下onnx模型与其转换后的ncnn模型推理结果数据中在不接近0或1的情况下存在较大偏差

编译/运行环境
onnx推理环境:windows 10/Intel Core i7 9700/python==3.6/onnxruntime==1.4.0 ncnn推理环境:Android 10.0/展锐 SC9863A/8x ARM Cortex-A55 @1600MHz/ARMv8-A ncnn库版本commitid:6b2495cc243f2d8e829523b700f32db1f5d50f78
复现步骤 | 再現方法
1.编写onnx模型推理代码
# onnx模型推理关键代码
onnx_session = onnxruntime.InferenceSession(test_model.onnx)
img_part= cv2.imread(test_iimage.bmp)
img_part = np.expand_dims(img_part, axis=0)
inputs_part = {self.onet_det_session.get_inputs()[0].name: img_part}
outs_part = self.onet_det_session.run(None, inputs_part)
np.set_printoptions(threshold=np.inf) # 保证array数组元素完全输出
np.set_printoptions(formatter={'float': '{: 0.6f}'.format}) # 保留小数点后六位
print('outs_part: ', outs_part)
2.onnx模型转ncnn模型
cd $WORK_DIR/ncnn
mkdir -p build
cd build
rm -rf *
cmake ..
make -j8
$WORK_DIR/ncnn/build/tools/onnx/onnx2ncnn test_model.onnx test_model.param test_model.bin
3.编写ncnn推理代码
/*推理关键代码*/
//ncnn推理输入数据构造(Android Bitmap->ncnn::Mat)
const float meanValues[3] = {127.5f, 127.5f, 127.5f};
const float normValues[3] = {1.0f / 127.5f, 1.0f / 127.5f, 1.0f / 127.5f};
ncnn::Mat input = ncnn::Mat::from_android_bitmap(env, inputBitmap, ncnn::Mat::PIXEL_BGR2RGB);
input.substract_mean_normalize(meanValues, normValues);
//ncnn推理
ncnn::Extractor extractor = net->create_extractor();
ncnn::Mat out;
extractor.input("x", input);
extractor.extract("sigmoid_0.tmp_0", out);
//推理结果打印
LOGI("output data -> ncnn data w=%d h=%d c=%d", out.w, out.h, out.c);
pretty_print(out, "dbnet_output_data_info");
/*结果打印代码*/
void pretty_print(ncnn::Mat &m, char *objectName)
{
int oneLinePrintNum = 0;
LOGD("%s print start", objectName);
FILE *file = fopen("/sdcard/Android/data/com.pax.ocr.testdemo/files/stdout.txt","a+");
for (int q=0; q<m.c; q++)
{
const float *ptr = m.channel(q);
for (int y=0; y<m.h; y++)
{
for (int x=0; x<m.w; x++)
{
oneLinePrintNum++;
if(oneLinePrintNum%5==0) {
char inferenceOut[256];
sprintf(inferenceOut, "%f %f %f %f %f\n", ptr[x-4], ptr[x-3], ptr[x-2], ptr[x-1], ptr[x]);
fwrite(inferenceOut,strlen(inferenceOut),1, file);
}
}
ptr += m.w;
}
}
fclose(file);
LOGD("%s print end", objectName);
}
4.分别运行onnx推理代码和ncnn推理代码,并对比打印输出结果
其他
onnx模型推理数据精度默认为float32 ncnn模型推理数据精度手动设置为了fp32
net->opt.use_fp16_packed = false;
net->opt.use_fp16_storage = false;
net->opt.use_fp16_arithmetic = false;
测试素材及模型文件详见附件 bug_files.zip 该模型为ocr文本检测模型,两种模型在推理后处理后的宏观检测结果上看不出太大差别,但是个别情况下ncnn的推理结果没有onnx准确,同时导致后续的字符识别不准确,麻烦帮忙看看是什么原因。
另外再上传一下我这边测试打印的完整推理输出文本文件: output_txt.zip
目前已排除整个OCR流程中的预处理和后处理造成的结果差异,主要是文本区域边缘概率点两模型推理结果在0.1~0.9之间存在较大差距,致使后处理检测到的文本框位置存在几个像素的差别,最终导致文本识别精度的差异。
请问这个问题有办法规避吗,或者减小两种模型的推理误差?
请问这个问题有好的解决办法吗,目前该问题在相机动态抓帧OCR识别中误差比较明显,连续扫描的错误率很高,与PC端跑转换前的ONNX模型相差较大,本人已反复检查预处理/后处理相关代码,未发现什么问题,所以应是ncnn推理过程中产生的误差。
经过大量测试对比优化,最后发现是不同运行平台的cpu浮点运算机制存在一定差异,无法通过代码修改消除,只能尽可能的减小计算差异 另外还有个比较关键的问题点就是在C++环境下做ncnn文字识别的时候,需要使用ncnn自带的copy_make_border接口对待识别图像做标准尺寸填充,且不要使用opencv的相关接口来代替,可以有效提升精度,这是与python进行onnx模型推理的最大差别,目前在代码层面已经将精度做到了最优