Toybrick

onnx模型转换为rknn模型, 输出结果不正确

ddcat1991

注册会员

积分
126
发表于 2020-2-27 12:48:13    查看: 1219|回复: 14 | [复制链接]    打印 | 显示全部楼层
本帖最后由 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]






本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
回复

使用道具 举报

jefferyzhang

超级版主

积分
5440
发表于 2020-2-28 10:44:08 | 显示全部楼层
可以尝试以下调试方法:

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

使用道具 举报

ddcat1991

注册会员

积分
126
 楼主| 发表于 2020-2-28 20:25:05 | 显示全部楼层
本帖最后由 ddcat1991 于 2020-2-28 20:36 编辑

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


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




输出为第一个Conv-Relu层



模型转换和比较的代码如下:
  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))
复制代码






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



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

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
回复

使用道具 举报

jefferyzhang

超级版主

积分
5440
发表于 2020-3-2 09:46:05 | 显示全部楼层
1. 我们NPU是专用计算器,非通用计算单元,其中最主要的优化是Conv2D。不支持depth-wise卷积。
2. depth-wise卷积的功能是牺牲conv2d有限精度的情况下提高端侧(主要是CPU)运算效率。但我们不需要他牺牲,我们NPU可以很好高效的运行conv2d。

你说的第一层就conv+relu出错的情况我们还没遇到过,我把这个问题报给NPU部门先
回复

使用道具 举报

jefferyzhang

超级版主

积分
5440
发表于 2020-3-2 09:52:34 | 显示全部楼层
还有我看你都是在板子上操作,请先核对下npu-drv版本号和toolkit是不是匹配,是否都是1.3.0.
驱动不对的话一切都是错的。
回复

使用道具 举报

ddcat1991

注册会员

积分
126
 楼主| 发表于 2020-3-2 14:46:03 | 显示全部楼层
确认了一下版本,  API和DRV都是1.3.0的。我再写个简单的conv2d的模型转换试试。

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


本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
回复

使用道具 举报

zengwubin

注册会员

积分
194
发表于 2020-3-24 10:24:13 | 显示全部楼层
你好,请问你有转换成功推理吗??? 我想转换1 M with  landmarks的模型,输出的conf也是全为1
回复

使用道具 举报

ddcat1991

注册会员

积分
126
 楼主| 发表于 2020-3-25 12:49:11 | 显示全部楼层
本帖最后由 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

超级版主

积分
5440
发表于 2020-3-30 15:26:45 | 显示全部楼层
NPU回复是说1.3.1已经解决,目前1.3.1还没正式发布,我先发给你一个beta版本测下:

rknn_toolkit v1.3.1 beta3:
链接: https://pan.baidu.com/s/1Kn2FGAdF_j3CMLNEsC3OPw 提取码: rcds
回复

使用道具 举报

jefferyzhang

超级版主

积分
5440
发表于 2020-3-31 14:22:53 | 显示全部楼层
NPU部门回复:
在 onnx_edit.py 里, rknn.config 漏掉了一个参数 reorder_channel='0 1 2', rknn.inference 也漏掉了 data_format='nchw' 的参数,这两个参数加上就可以推理正确。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

产品中心 购买渠道 开源社区 Wiki教程 资料下载 关于Toybrick


快速回复 返回顶部 返回列表