Toybrick

ONNX转换中的Bug

undefined

新手上路

积分
21
楼主
发表于 2020-5-20 12:04:41    查看: 7753|回复: 4 | [复制链接]    打印 | 只看该作者
在用rknn_toolkit=1.3.0转换ONNX时,发现对于ConvTranspose和PRelu存在尺寸判断错误。
测试使用代码如下:
  1. import os
  2. import onnx
  3. from onnx import optimizer, shape_inference, version_converter, TensorProto
  4. import onnxruntime
  5. import numpy as np
  6. import cv2
  7. # import torch
  8. from rknn.api import RKNN
  9. np.random.seed(100)
  10. # torch.manual_seed(100)


  11. class ONNXModule(object):
  12.     def __init__(self, name):
  13.         super().__init__()
  14.         self._name = name
  15.         self._node_list, self._init_tensor, self._inpt_tensor, self._out_tensor = [], [], [], []
  16.         self._graph = None
  17.     @property
  18.     def name(self):
  19.         return self._name

  20.     @property
  21.     def model(self):
  22.         inpt_value_info = []
  23.         out_value_info = []
  24.         init_value_data = []
  25.         for init_t in self._inpt_tensor + self._init_tensor:
  26.             inpt_value_info.append(onnx.helper.make_tensor_value_info(
  27.                 init_t[0], init_t[2], tuple(init_t[1])))
  28.         for init_t in self._init_tensor:
  29.             init_value_data.append(onnx.helper.make_tensor(
  30.                 init_t[0], init_t[2], tuple(init_t[1]),
  31.                 np.random.randn(
  32.                     *init_t[1]).astype(np.float32, order='C').flatten()))
  33.         for init_t in self._out_tensor:
  34.             out_value_info.append(onnx.helper.make_tensor_value_info(
  35.                 init_t[0], init_t[2], tuple(init_t[1])))

  36.         _graph = onnx.helper.make_graph(
  37.             self._node_list, self._name,
  38.             inpt_value_info, out_value_info, init_value_data
  39.         )

  40.         return onnx.helper.make_model(_graph)

  41.     def __call__(self, inpt):
  42.         pass


  43. class M0(ONNXModule):
  44.     # Good one
  45.     def __init__(self):
  46.         super().__init__('M0')
  47.         # 1x3x10x10
  48.         # self.conv = torch.nn.Conv2d(
  49.         #     3, 13, kernel_size=(2, 2), stride=2)
  50.         conv = onnx.helper.make_node(
  51.             'Conv',
  52.             inputs=['input', 'conv_W', 'conv_b'],
  53.             outputs=['conv_1'],
  54.             kernel_shape=[2, 2],
  55.             strides=[2, 2]
  56.         )
  57.         # 1x13x5x5
  58.         # self.relu = torch.nn.ReLU()
  59.         relu = onnx.helper.make_node(
  60.             'Relu',
  61.             inputs=['conv_1'],
  62.             outputs=['relu_1'],
  63.         )
  64.         self._node_list.extend([conv, relu])
  65.         self._inpt_tensor.append(('input', [1, 3, 10, 10], TensorProto.FLOAT))
  66.         self._init_tensor.append(('conv_W', [13, 3, 2, 2], TensorProto.FLOAT))
  67.         self._init_tensor.append(('conv_b', [13], TensorProto.FLOAT))
  68.         self._out_tensor.append(('relu_1', [1, 13, 5, 5], TensorProto.FLOAT))

  69.     def __call__(self, inpt):
  70.         pass
  71.         # x = self.conv(inpt)
  72.         # x = self.relu(x)
  73.         # return x


  74. class M1(ONNXModule):
  75.     # Deconv Out Channel Error
  76.     def __init__(self):
  77.         super().__init__('M1')
  78.         # 1x3x10x10
  79.         # self.deconv = torch.nn.ConvTranspose2d(
  80.         #     3, 13, kernel_size=(2, 2), stride=2)
  81.         deconv = onnx.helper.make_node(
  82.             'ConvTranspose',
  83.             inputs=['input', 'deconv_W', 'deconv_b'],
  84.             outputs=['deconv_1'],
  85.             kernel_shape=[2, 2],
  86.             strides=[2, 2]
  87.         )
  88.         # 1x13x20x20
  89.         # self.relu = torch.nn.ReLU()
  90.         relu = onnx.helper.make_node(
  91.             'Relu',
  92.             inputs=['deconv_1'],
  93.             outputs=['relu_1'],
  94.         )
  95.         self._node_list.extend([deconv, relu])
  96.         self._inpt_tensor.append(('input', [1, 3, 10, 10], TensorProto.FLOAT))
  97.         self._init_tensor.append(
  98.             ('deconv_W', [3, 13, 2, 2], TensorProto.FLOAT))
  99.         self._init_tensor.append(('deconv_b', [13], TensorProto.FLOAT))
  100.         self._out_tensor.append(('relu_1', [1, 13, 20, 20], TensorProto.FLOAT))

  101.     def forward(self, inpt):
  102.         pass
  103.         # x = self.deconv(inpt)
  104.         # x = self.relu(x)
  105.         # return x


  106. class M2(ONNXModule):
  107.     # PReLU error
  108.     def __init__(self):
  109.         super().__init__('M2')
  110.         # 1x3x10x10
  111.         # self.conv = torch.nn.ConvTranspose2d(
  112.         #     3, 6, kernel_size=(3, 3), stride=1, padding=1)
  113.         conv = onnx.helper.make_node(
  114.             'Conv',
  115.             inputs=['input', 'conv_W', 'conv_b'],
  116.             outputs=['conv_1'],
  117.             kernel_shape=[3, 3],
  118.             strides=[1, 1],
  119.             pads=[1, 1, 1, 1]
  120.         )
  121.         # 1x6x10x10
  122.         # self.prelu = torch.nn.PReLU(6)
  123.         prelu = onnx.helper.make_node(
  124.             'PRelu',
  125.             inputs=['conv_1', 'slop_1'],
  126.             outputs=['prelu_1']
  127.         )
  128.         self._node_list.extend([conv, prelu])

  129.         self._inpt_tensor.append(('input', [1, 3, 10, 10], TensorProto.FLOAT))
  130.         self._init_tensor.append(('conv_W', [6, 3, 3, 3], TensorProto.FLOAT))
  131.         self._init_tensor.append(('conv_b', [6], TensorProto.FLOAT))
  132.         self._init_tensor.append(('slop_1', [1, 6, 1, 1], TensorProto.FLOAT))
  133.         self._out_tensor.append(('prelu_1', [1, 6, 10, 10], TensorProto.FLOAT))

  134.     def forward(self, inpt):
  135.         pass
  136.         # x = self.conv(inpt)
  137.         # x = self.prelu(x)
  138.         # return x

  139. class M3(ONNXModule):
  140.     # Sub error
  141.     def __init__(self):
  142.         super().__init__('M3')
  143.         # 1x3x10x10
  144.         conv_1 = onnx.helper.make_node(
  145.             'Conv',
  146.             inputs=['input', 'conv_1W', 'conv_1b'],
  147.             outputs=['conv_1'],
  148.             kernel_shape=[2, 2],
  149.             strides=[2, 2],
  150.             pads=[0, 0, 0, 0]
  151.         )
  152.         # 1x3x10x10
  153.         max_pool = onnx.helper.make_node(
  154.             'MaxPool',
  155.             inputs=['input'],
  156.             outputs=['maxpool_1'],
  157.             kernel_shape=[2, 2],
  158.             strides=[2, 2]
  159.         )
  160.         sub = onnx.helper.make_node(
  161.             'Sub',
  162.             inputs=['maxpool_1', 'conv_1'],
  163.             outputs=['result']
  164.         )
  165.         self._node_list.extend([conv_1, max_pool, sub])

  166.         self._inpt_tensor.append(('input', [1, 3, 10, 10], TensorProto.FLOAT))
  167.         self._init_tensor.append(('conv_1W', [3, 3, 2, 2], TensorProto.FLOAT))
  168.         self._init_tensor.append(('conv_1b', [3], TensorProto.FLOAT))
  169.         self._out_tensor.append(('result', [1, 3, 5, 5], TensorProto.FLOAT))

  170.     def forward(self, inpt):
  171.         pass

  172. def export_onnx(inst_, input_, name):
  173.     output = inst_(input_)
  174.     onnx_m = inst_.model
  175.     onnx_m = shape_inference.infer_shapes(onnx_m)
  176.     onnx_m = optimizer.optimize(onnx_m)
  177.     onnx.checker.check_model(onnx_m)
  178.     onnx.save(onnx_m, name)


  179. def export_rknn(onnx_, rknn_, dataset_f='./dataset.txt'):
  180.     assert os.path.exists(onnx_), f"onnx file '{onnx_}' not exist"
  181.     rknn = RKNN()
  182.     rknn.config(
  183.         channel_mean_value='127.5 127.5 127.5 127.5', reorder_channel='0 1 2',
  184.         quantized_dtype='dynamic_fixed_point-16',
  185.         epochs=1000)
  186.     rknn.load_onnx(model=onnx_)
  187.     rknn.build(do_quantization=True, dataset=dataset_f)
  188.     rknn.export_rknn(rknn_)
  189.     rknn.release()


  190. def run_rknn(rknn_, onnx_, inpt):
  191.     rknn = RKNN()
  192.     rknn.config(
  193.         channel_mean_value='127.5 127.5 127.5 127.5', reorder_channel='0 1 2')
  194.     rknn.load_rknn(rknn_)

  195.     rknn.init_runtime()
  196.     rk_out = rknn.inference([inpt], data_format='nchw')

  197.     t_inpt = inpt.astype(np.float32, order='C')
  198.     t_inpt -= np.array([127.5, 127.5, 127.5], dtype=np.float32).reshape(1, 3, 1, 1)
  199.     t_inpt /= 127.5
  200.     # with torch.no_grad():
  201.     #     th_out = inst_(t_inpt)
  202.     onrt = onnxruntime.InferenceSession(onnx_, None)
  203.     th_out = onrt.run([], {'input': t_inpt})
  204.     if isinstance(th_out, (list, tuple)):
  205.         pass
  206.     else:
  207.         th_out = [th_out]
  208.     # th_out = [t.cpu().numpy() for t in th_out]
  209.     for rk_o, th_o in zip(rk_out, th_out):
  210.         print(rk_o.shape, th_o.shape)
  211.         assert rk_o.size == th_o.size, "size mismatch ({rk_o.size}) ({th_o.size})"
  212.         rk_o = rk_o.reshape(th_o.shape)
  213.         print('abs mean: ', np.abs(rk_o - th_o).mean())

  214.     # rknn.release()


  215. DATASET_NUM = 1
  216. # dataset_file = open('Data/TOM/')
  217. all_img = np.random.randint(0, high=256, size=(
  218.     DATASET_NUM, 10, 10, 3), dtype=np.uint8)
  219. dataset_f = open('./dataset.txt', 'w')
  220. os.makedirs('input', exist_ok=True)
  221. for i in range(DATASET_NUM):
  222.     img = all_img[i]
  223.     cv2.imwrite(f'input/{i}.png', img)
  224.     dataset_f.write(f'input/{i}.png\n')
  225. dataset_f.close()

  226. test_data = cv2.imread('input/0.png')
  227. test_data = cv2.cvtColor(test_data, cv2.COLOR_BGR2RGB)
  228. test_data = np.transpose(
  229.     test_data[None, :, :, :], (0, 3, 1, 2)).copy()  # input used for test
  230. # input_data = torch.from_numpy(test_data).float()  # input used for export
  231. input_data = test_data


  232. M0_inst = M0()
  233. M1_inst = M1()
  234. M2_inst = M2()
  235. # M3_inst = M3()
  236. m_l = [M0_inst, M1_inst, M2_inst]
  237. n_l = [m.name for m in m_l]
  238. ox_l = [m.name+'.onnx' for m in m_l]
  239. rk_l = [m.name+'.rknn' for m in m_l]

  240. for m, ox in zip(m_l, ox_l):
  241.     export_onnx(m, input_data, ox)

  242. for ox, rk, m, n in zip(ox_l, rk_l, m_l, n_l):
  243.     print(f"=====start {n}========")
  244.     try:
  245.         export_rknn(ox, rk)
  246.         print(f"{n} export_rknn ok")
  247.         run_rknn(rk, ox, test_data)
  248.         print(f"{n} run_rknn ok")
  249.     except (Exception, TypeError, NameError) as ex:
  250.         print(ex)
  251.         print(f"{n} failed")
  252.     finally:
  253.         print(f"======end {n}=========")
  254.         print()
复制代码
在M1中使用ConvTranpose,在M2中使用PRelu,分别报如下错误
M1:
E ValueError: output_shape does not match filter's output channels, 3 != 13

M2:
E ValueError: Dimensions must be equal, but are 6 and 10 for 'PRelu_prelu_1_1_1/mul' (op: 'Mul') with input shapes: [1,6,1,1], [1,10,10,6].
回复

使用道具 举报

jefferyzhang

版主

积分
13580
沙发
发表于 2020-5-20 14:30:38 | 只看该作者
这个onnx,有尝试过用onnx自己加载并推理成功么?
回复

使用道具 举报

undefined

新手上路

积分
21
板凳
 楼主| 发表于 2020-5-20 14:44:53 | 只看该作者
本帖最后由 undefined 于 2020-5-20 14:55 编辑
jefferyzhang 发表于 2020-5-20 14:30
这个onnx,有尝试过用onnx自己加载并推理成功么?

这个里面使用了onnxruntime计算真值,如果build时报错或量化后误差较大应该都是有问题的
每个模型onnxruntime运行都是没有问题的,可以单独把run_rknn里面的onnxruntime代码取出来进行测试

使用的环境是python 3.6
onnxruntime==1.2.0
onnx==1.4.1
rknn_toolkit==1.3.2


回复

使用道具 举报

jefferyzhang

版主

积分
13580
地板
发表于 2020-5-20 16:06:41 | 只看该作者
问题已经上报了
回复

使用道具 举报

jefferyzhang

版主

积分
13580
5#
发表于 2020-5-28 20:30:20 | 只看该作者
NPU Team回复:253412
M2的问题后续 RKNN Toolkit 1.3.3 版本会修复。
M1的问题后续需要更新 RKNN Toolkit 1.3.3 和 最新的npu驱动才可修复。
回复

使用道具 举报

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

本版积分规则

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


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