Toybrick

标题: onnx模型转换为rknn模型, 输出结果不正确 [打印本页]

作者: ddcat1991    时间: 2020-2-27 12:48
标题: onnx模型转换为rknn模型, 输出结果不正确
本帖最后由 ddcat1991 于 2020-2-27 12:53 编辑

这是一个基于SSD改的人脸检测的模型。但是转换后, onnx和rknn模型输出的结果差异很大。
环境:
* Debian10
* rknn 1.3.0
* onnx 1.4.1
* tf 1.14

Win:
* onnxruntime 1.1.1


* onnx模型可转换为rknn模型
* 未开启量化

这个onnx模型来源于:
https://github.com/Linzaer/Ultra-Light-Fast-Generic-Face-Detector-1MB,  移除了最后的softmax层(rknn-softmanx出来的结果全是1, 不知道为什么).
(原模型直接不能把模型形状也保存到.pth里, 但是可以导出onnx)

使用 https://github.com/daquexian/onnx-simplifier 简化后(python3 -m onnxsim input_onnx_model output_onnx_model),
输出的模型为toto2.onnx。这个onnx模型的输出结果是正确的,但是RKNN输出的结果很不对。

ONNX输出:


RKNN输出:


不知道问题出在哪里,有以下几个疑问:

* 尝试过把onnx->pb->rknn, 但是出来的pb模型用netron看变得很复杂(tf可能没有专门对应分组卷积的函数) , rknn加载pb转换为rknn的模型会直接加载失败。
* 尝试过在onnx->rknn, 用1.2W张图量化,结果也不正确
* 原模型中使用了group=1的分组卷积,不知道rknn,是不是不支持分组卷积的操作
* 或者原模型是float32的, 转换后rknn模型为float16, 精度损失导致的。



最后相关的模型和代码附件如下:
[url=]gather.zip[/url]







作者: jefferyzhang    时间: 2020-2-28 10:44
可以尝试以下调试方法:

1. 不要量化,一层一层往上修改output,核对rknn和onnx计算的结果(比对输出的值,而不是比对推理图片),找到出问题的层
2. 对比rknn-toolkit pc仿真和实机推理结果是否有正确的。
3. 尝试用其他的架构转换。 你说的tf转换后netron结果很乱是正常的,因为onnx转换tf后子图概念丢失,就变成op散开了,但是不影响计算结果。
作者: ddcat1991    时间: 2020-2-28 20:25
本帖最后由 ddcat1991 于 2020-2-28 20:36 编辑

谢谢你的回复, 我尝试了打印网络中间层的输出(没有使用量化),发现在第一个Conv-Relu后的输出,计算的结果差异就很大。


> totoal-difference: 88042.95  # onnx与rknn模型 在第一个Conv-Relu操作后输出的权重 差值的绝对值之和
> totoal-weight: 115533.96  # 总权重绝对值之和
> drift-rate: 76.2053%  # 当前层输出的 数值差异百分比


[attach]878[/attach]

输出为第一个Conv-Relu层

[attach]880[/attach]

模型转换和比较的代码如下:
  1. import onnx
  2. from onnx import helper, TensorProto
  3. import numpy as np


  4. # Common - Input Setting
  5. np.random.seed(1234)
  6. image = np.random.random((1, 3, 240, 320))*2 - 1.0  # 原模型输入为归一化后 -1~1之间的结果
  7. image = image.astype(np.float32)

  8. intermidiate_y = '187'
  9. intermidiate_shape = [1, 16, 120, 160]
  10. # intermidiate_shape = [1, 4, 8, 10]
  11. new_model_name = 'toto_%s.onnx' % intermidiate_y


  12. # load model
  13. model = onnx.load_model("named_toto.onnx")


  14. # add output
  15. intermediate_layer_value_info = helper.make_tensor_value_info(intermidiate_y, TensorProto.FLOAT, intermidiate_shape)
  16. # intermediate_layer_value_info = helper.ValueInfoProto()
  17. # intermediate_layer_value_info.name = intermidiate_y
  18. model.graph.output.extend([intermediate_layer_value_info])
  19. onnx.save(model, new_model_name)
  20. onnx.checker.check_model(model)


  21. # run model
  22. import onnxruntime as ort
  23. ort_session = ort.InferenceSession(new_model_name)
  24. input_name = ort_session.get_inputs()[0].name

  25. conf_onnx, boxes_onnx, tmp_onnx = ort_session.run(None, {input_name: image})


  26. # ================ rknn model ========================
  27. from rknn.api import RKNN
  28. # Create RKNN object
  29. rknn = RKNN(verbose=True)
  30. print('--> Loading model')
  31. rknn.load_onnx(model=new_model_name)
  32. print('done')
  33. rknn.config(batch_size=1)
  34. rknn.init_runtime()
  35. # Build model
  36. print('--> Building model')
  37. rknn.build(do_quantization=False)
  38. print('done')
  39. rknn.export_rknn('./model.rknn')
  40. print('image.shape:', image.shape)
  41. conf_rknn, boxes_rknn, tmp_rknn = rknn.inference(inputs=[image])


  42. ## ========================== Result Compare ===============================
  43. print('********************* Intermidiante Layer Number: %s *******************' % intermidiate_y)
  44. tmp_onnx = np.squeeze(tmp_onnx)
  45. tmp_rknn = np.squeeze(tmp_rknn)
  46. tmp_onnx = tmp_onnx.reshape(-1,tmp_onnx.shape[-1])
  47. tmp_rknn = tmp_rknn.reshape(-1,tmp_rknn.shape[-1])

  48. # for idx in range(tmp_onnx.shape[0],2):
  49. for idx in range(0,4):
  50.     print('********************* Onnx:%d ****************************' % idx)
  51.     print(tmp_onnx[idx,:20])
  52.     print('********************* Rknn:%d ****************************' % idx)
  53.     print(tmp_rknn[idx,:20])

  54. difference = np.sum(np.abs(tmp_onnx-tmp_rknn))
  55. total_weight = np.sum(np.abs(tmp_onnx))
  56. print('totoal-difference:', difference)
  57. print('totoal-weight:', total_weight)
  58. print('drift-rate: %.4f%%' % (difference/total_weight*100))
复制代码




[attach]881[/attach]

* 第一个Conv-Relu层就出错了, 第一个Conv是标准的卷积,没有使用分组卷积 (没有开启量化)
*  对比rknn-toolkit pc仿真: 仿真和npu输出结果相同, 但rknn模型与onnx模型的第一层 (Conv-Relu) 的输出差异很大 (误差 76%)
* 目前只尝试了pb的转换,但从pb转换出的模型npu加载失败。



目前使用的版本是 1.3.0, 还有什么别的办法吗?
现在大家一般用哪种模型转换成rknn的比较多呢?


作者: jefferyzhang    时间: 2020-3-2 09:46
1. 我们NPU是专用计算器,非通用计算单元,其中最主要的优化是Conv2D。不支持depth-wise卷积。
2. depth-wise卷积的功能是牺牲conv2d有限精度的情况下提高端侧(主要是CPU)运算效率。但我们不需要他牺牲,我们NPU可以很好高效的运行conv2d。

你说的第一层就conv+relu出错的情况我们还没遇到过,我把这个问题报给NPU部门先
作者: jefferyzhang    时间: 2020-3-2 09:52
还有我看你都是在板子上操作,请先核对下npu-drv版本号和toolkit是不是匹配,是否都是1.3.0.
驱动不对的话一切都是错的。
作者: ddcat1991    时间: 2020-3-2 14:46
确认了一下版本,  API和DRV都是1.3.0的。我再写个简单的conv2d的模型转换试试。

PS: 刚刚你提到, rknn不支持depth-wise的卷积, 就是说比如由onnx->rknn, 遇到depth-wise的卷积, 模型转换的api会把它当成普通卷积计算吗?

[attach]887[/attach]

作者: zengwubin    时间: 2020-3-24 10:24
你好,请问你有转换成功推理吗??? 我想转换1 M with  landmarks的模型,输出的conf也是全为1
作者: ddcat1991    时间: 2020-3-25 12:49
本帖最后由 ddcat1991 于 2020-3-25 12:52 编辑
zengwubin 发表于 2020-3-24 10:24
你好,请问你有转换成功推理吗??? 我想转换1 M with  landmarks的模型,输出的conf也是全为1 ...

用pytorch写了个简单的卷积网络, 保存为onnx->rknn, conv层的转换结果都不正确。
后来我用tensorflow,pb 写了个小模型试了一下, 结果出入就比较小了。你要不考虑转换成pb试试?
作者: jefferyzhang    时间: 2020-3-30 15:26
NPU回复是说1.3.1已经解决,目前1.3.1还没正式发布,我先发给你一个beta版本测下:

rknn_toolkit v1.3.1 beta3:
链接: https://pan.baidu.com/s/1Kn2FGAdF_j3CMLNEsC3OPw 提取码: rcds
作者: jefferyzhang    时间: 2020-3-31 14:22
NPU部门回复:
在 onnx_edit.py 里, rknn.config 漏掉了一个参数 reorder_channel='0 1 2', rknn.inference 也漏掉了 data_format='nchw' 的参数,这两个参数加上就可以推理正确。
作者: ddcat1991    时间: 2020-3-31 16:04
本帖最后由 ddcat1991 于 2020-3-31 16:06 编辑
jefferyzhang 发表于 2020-3-31 14:22
NPU部门回复:
在 onnx_edit.py 里, rknn.config 漏掉了一个参数 reorder_channel='0 1 2', rknn.inferen ...

是这个问题,添上就解决了,板子上推理也正确了。谢谢版主!( toolkit 1.3.0-NPU, 1.3.1-PC 都ok )
作者: zengwubin    时间: 2020-4-6 11:04
ddcat1991 发表于 2020-3-31 16:04
是这个问题,添上就解决了,板子上推理也正确了。谢谢版主!( toolkit 1.3.0-NPU, 1.3.1-PC 都ok ) ...

onnx_edit.py 这个文件在哪里,没找到啊
作者: zengwubin    时间: 2020-4-6 11:49
请问转换rknn时你是怎么移除softmax层的,能不能详细讲下谢谢我用1MB with landmars 转换时卡在knn-softmanx出来的结果全是1了
作者: ddcat1991    时间: 2020-4-7 10:26
zengwubin 发表于 2020-4-6 11:49
请问转换rknn时你是怎么移除softmax层的,能不能详细讲下谢谢我用1MB with landmars 转换时卡在k ...

把1mb vision/ssd/ssd.py 里  forward函数下 从94 行开始 "confidences = F.softmax(confidences, dim=2)" 和后面的注释掉就可以了
作者: sotnly    时间: 2020-6-5 14:26
大佬,我也刚好在哢这个模型,有好些问题,有偿求助,可以的话加我QQ:405631964,谢谢了
作者: sotnly    时间: 2020-6-5 14:42
我也是用Ultra-Light-Fast-Generic-Face-Detector-1MB,这个的RFB-640的模型,我做了相应的裁剪,然后也用onnxsim做了模型简化,但是当我把简化的模型转为rknn的时候就报错了:

IndexError:_Map_base::at
我百度了,说是我的torch.onnx.export需要个  keep_initializers_as_inputs 这个参数,我现在的版本根本就没有这个参数,我的就是pytorch1.2.0直接转的onnx1.4.1的,所以不知道该怎么继续,如果你也有这个经历,麻烦告知,谢谢
作者: yongxin3344520    时间: 2020-9-27 15:02
jefferyzhang 发表于 2020-3-2 09:52
还有我看你都是在板子上操作,请先核对下npu-drv版本号和toolkit是不是匹配,是否都是1.3.0.
驱动不对的话 ...

请问,如何查看 npu-drv版本号 和 toolkit 版本 ? npu-drv版本如何升级 ?
作者: aijle1981    时间: 2021-8-13 20:52
参考楼主的代码做转换是可以的,但是加了pre_compile,输出全部变成了0,不知道为什么。
  1. import onnx
  2. from onnx import helper, TensorProto
  3. import numpy as np


  4. # Common - Input Setting
  5. np.random.seed(1234)
  6. image = np.random.random((1, 3, 240, 320))*2 - 1.0  # 原模型输入为归一化后 -1~1之间的结果
  7. image = image.astype(np.float32)

  8. intermidiate_y = '187'
  9. intermidiate_shape = [1, 16, 120, 160]
  10. # intermidiate_shape = [1, 4, 8, 10]
  11. new_model_name = 'toto_%s.onnx' % intermidiate_y

  12. modelName = "version-RFB-320_without_postprocessing"

  13. # load model
  14. model = onnx.load_model(modelName +".onnx")

  15. # add output
  16. intermediate_layer_value_info = helper.make_tensor_value_info(intermidiate_y, TensorProto.FLOAT, intermidiate_shape)
  17. # intermediate_layer_value_info = helper.ValueInfoProto()
  18. # intermediate_layer_value_info.name = intermidiate_y

  19. # model.graph.output.extend([intermediate_layer_value_info])
  20. onnx.save(model, new_model_name)
  21. onnx.checker.check_model(model)


  22. # run model
  23. import onnxruntime as ort
  24. ort_session = ort.InferenceSession(new_model_name)
  25. input_name = ort_session.get_inputs()[0].name

  26. conf_onnx, boxes_onnx = ort_session.run(None, {input_name: image})


  27. # ================ rknn model ========================
  28. from rknn.api import RKNN
  29. # Create RKNN object
  30. rknn1 = RKNN(verbose=False)
  31. print('--> Loading model')
  32. rknn1.load_onnx(model=new_model_name)
  33. print('done')
  34. rknn1.config(batch_size=1,reorder_channel='0 1 2', mean_values=[[0, 0, 0]], std_values=[[1, 1, 1]], target_platform="rv1109")

  35. # Build model
  36. print('--> Building model')
  37. rknn1.build(do_quantization=False)
  38. print('done')
  39. rknn1.export_rknn('./tmp.rknn')
  40. rknn1.release()

  41. # Create RKNN object
  42. rknn2 = RKNN(verbose=False)
  43. print('--> Loading model2')
  44. rknn2.load_onnx(model=new_model_name)
  45. print('done')
  46. rknn2.config(batch_size=1,reorder_channel='0 1 2', mean_values=[[0, 0, 0]], std_values=[[1, 1, 1]], target_platform="rv1109")

  47. # Build model
  48. print('--> Building pre_compile model')
  49. rknn2.build(do_quantization=False, pre_compile=True)
  50. print('done')
  51. rknn2.export_rknn(modelName +'.rknn')
  52. rknn2.release()

  53. rknn = RKNN(verbose=True)
  54. rknn.load_rknn('./tmp.rknn')
  55. rknn.config(batch_size=1,reorder_channel='0 1 2', mean_values=[[0, 0, 0]], std_values=[[1, 1, 1]], target_platform="rv1109")
  56. rknn.init_runtime()
  57. print('image.shape:', image.shape)
  58. conf_rknn, boxes_rknn = rknn.inference(inputs=[image],data_format='nchw')
  59. rknn.release()

  60. tmp_onnx = conf_onnx
  61. tmp_rknn = conf_rknn
  62. ## ========================== Result Compare ===============================
  63. print('********************* Intermidiante Layer Number: %s *******************' % intermidiate_y)
  64. tmp_onnx = np.squeeze(tmp_onnx)
  65. tmp_rknn = np.squeeze(tmp_rknn)
  66. tmp_onnx = tmp_onnx.reshape(-1,tmp_onnx.shape[-1])
  67. tmp_rknn = tmp_rknn.reshape(-1,tmp_rknn.shape[-1])

  68. # for idx in range(tmp_onnx.shape[0],2):
  69. for idx in range(0,4):
  70.     print('********************* Onnx:%d ****************************' % idx)
  71.     print(tmp_onnx[idx,:20])
  72.     print('********************* Rknn:%d ****************************' % idx)
  73.     print(tmp_rknn[idx,:20])

  74. difference = np.sum(np.abs(tmp_onnx-tmp_rknn))
  75. total_weight = np.sum(np.abs(tmp_onnx))
  76. print('totoal-difference:', difference)
  77. print('totoal-weight:', total_weight)
  78. print('drift-rate: %.4f%%' % (difference/total_weight*100))
复制代码





欢迎光临 Toybrick (https://t.rock-chips.com/) Powered by Discuz! X3.3