Toybrick

简单 op 通过pytorch->onnx->rknn 转化后推断结果很奇怪

kkkaaa

中级会员

积分
203
楼主
发表于 2020-4-27 16:58:20    查看: 7506|回复: 7 | [复制链接]    打印 | 只看该作者
本帖最后由 kkkaaa 于 2020-4-27 16:59 编辑

以下是我实验的一个例子,转换 nn.Relu op,分别输出 torch, onnx, rknn 的推断结果。torch 和 onnx 一致,rknn 的结果和前两者是反的,也就是说 rknn_outputs = rknn.inference(inputs=[rknn_input])[0][0][::-1] 的话,三个模型的结果一致。
似乎和 channel 有关,但是还是有点奇怪


  1. """ torch -> onnx -> rknn """

  2. import torch
  3. import numpy as np
  4. from torch import nn


  5. model_name = "little_model_func_conv"
  6. ONNX_MODEL = model_name + '.onnx'
  7. RKNN_MODEL = model_name + '_from_onnx' + '.rknn'

  8. image_size_h = 2
  9. image_size_w =1
  10. num_channel =3

  11. # === torch 模型初始化 ===


  12. class ToyModel(nn.Module):
  13.     def __init__(self, ):
  14.         super(ToyModel, self).__init__()
  15.         self.op = nn.ReLU()

  16.     def forward(self, x):
  17.         x = self.op(x)
  18.         return x

  19. net = ToyModel()

  20. print("==== network ====")
  21. print(net)
  22. net.eval()

  23. # === 转化1: torch2onnx ===
  24. print("--> torch model inference result")

  25. input_tensor = torch.Tensor(np.arange(num_channel * image_size_h * image_size_w).reshape(1, num_channel, image_size_h, image_size_w))
  26. torch_out = torch.onnx._export(net, input_tensor, ONNX_MODEL, export_params=True)

  27. # === 转化2: onnx2rknn & rknn inference ===
  28. from rknn.api import RKNN
  29. rknn = RKNN()

  30. print('--> Loading model')
  31. ret = rknn.load_onnx(model=ONNX_MODEL)
  32. if ret != 0:
  33.     print('Load resnet50v2 failed!')
  34.     exit(ret)
  35. print('done')

  36. # Build model
  37. print('--> Building model')
  38. ret = rknn.build(do_quantization=False, dataset='./dataset.txt')
  39. if ret != 0:
  40.     print('Build resnet50 failed!')
  41.     exit(ret)
  42. print('done')

  43. # Export rknn model
  44. print('--> Export RKNN model')
  45. ret = rknn.export_rknn(RKNN_MODEL)
  46. if ret != 0:
  47.     print('Export resnet50v2.rknn failed!')
  48.     exit(ret)
  49. print('done')

  50. # === rknn inference ===
  51. # init runtime environment
  52. print("--> Init runtime environment")
  53. ret = rknn.init_runtime()
  54. if ret != 0:
  55.     print("Init runtime environment failed")
  56.     exit(ret)
  57. print('done')

  58. # inference
  59. print("--> Running rknn model")
  60. rknn_input = input_tensor.numpy()[0].transpose(1,2,0)
  61. print('----> rknn input')
  62. print(rknn_input)
  63. rknn_outputs = rknn.inference(inputs=[rknn_input])[0][0]  #[::-1]

  64. # === torch inference ===
  65. print('----> torch input')
  66. print(input_tensor)
  67. torch_inference_result = net(input_tensor)[0].detach().cpu().numpy()

  68. # === onnx inference ===
  69. import onnxruntime
  70. def to_numpy(tensor):
  71.     return tensor.detach().cpu().numpy() if tensor.requires_grad else tensor.cpu().numpy()

  72. ort_session = onnxruntime.InferenceSession(ONNX_MODEL)
  73. ort_inputs = {ort_session.get_inputs()[0].name: to_numpy(input_tensor)}
  74. ort_outs = ort_session.run([x.name for x in ort_session.get_outputs()], ort_inputs)


  75. # === compare & show results ===
  76. print("~~~~~~ torch model infer output ~~~~~~")
  77. print(torch_inference_result)
  78. print("~~~~~~ onnx model infer output ~~~~~~")
  79. print(ort_outs)
  80. print("~~~~~~ rknn model infer output ~~~~~~")
  81. print(rknn_outputs)
复制代码

输出结果如下
  1. ----> rknn input
  2. [[[0. 2. 4.]]

  3. [[1. 3. 5.]]]
  4. ----> torch input
  5. tensor([[[[0.],
  6.           [1.]],

  7.          [[2.],
  8.           [3.]],

  9.          [[4.],
  10.           [5.]]]])
  11. ~~~~~~ torch model infer output ~~~~~~
  12. [[[0.]
  13.   [1.]]

  14. [[2.]
  15.   [3.]]

  16. [[4.]
  17.   [5.]]]
  18. ~~~~~~ onnx model infer output ~~~~~~
  19. [array([[[[0.],
  20.          [1.]],

  21.         [[2.],
  22.          [3.]],

  23.         [[4.],
  24.          [5.]]]], dtype=float32)]
  25. ~~~~~~ rknn model infer output ~~~~~~
  26. [[[4.]
  27.   [5.]]

  28. [[2.]
  29.   [3.]]

  30. [[0.]
  31.   [1.]]]
复制代码




回复

使用道具 举报

kkkaaa

中级会员

积分
203
沙发
 楼主| 发表于 2020-4-27 17:33:07 | 只看该作者
我还试过其他 op, 比如 nn.Conv2d, nn.ConvTranspose2d, 这些 op 就更看不出 rknn 结果和 torch 结果的联系
回复

使用道具 举报

leok

版主

积分
894
板凳
发表于 2020-4-27 17:45:23 | 只看该作者
请确认rknn toolkit的版本,1.0.0以前版本输出是NHWC,1.0.0之后版本输出和原始模型一致。
回复

使用道具 举报

kkkaaa

中级会员

积分
203
地板
 楼主| 发表于 2020-4-27 17:47:15 | 只看该作者
leok 发表于 2020-4-27 17:45
请确认rknn toolkit的版本,1.0.0以前版本输出是NHWC,1.0.0之后版本输出和原始模型一致。 ...

版本是 1.3.1b1
回复

使用道具 举报

kkkaaa

中级会员

积分
203
5#
 楼主| 发表于 2020-4-27 17:49:15 | 只看该作者
leok 发表于 2020-4-27 17:45
请确认rknn toolkit的版本,1.0.0以前版本输出是NHWC,1.0.0之后版本输出和原始模型一致。 ...

我好像发现问题所在了
rknn_input = input_tensor.numpy()[0][::-1].transpose(1,2,0)
如果事先把 rknn_input 的 channel 维倒换过来,各种 op 的结果都一致了
但还是十分不解这是怎么回事
回复

使用道具 举报

leok

版主

积分
894
6#
发表于 2020-4-27 20:37:24 | 只看该作者
kkkaaa 发表于 2020-4-27 17:49
我好像发现问题所在了
rknn_input = input_tensor.numpy()[0][::-1].transpose(1,2,0)
如果事先把 rknn_in ...

rknn toolkit默认inference的data_format是nhwc
回复

使用道具 举报

kkkaaa

中级会员

积分
203
7#
 楼主| 发表于 2020-4-27 21:10:55 | 只看该作者
本帖最后由 kkkaaa 于 2020-4-27 21:13 编辑
leok 发表于 2020-4-27 20:37
rknn toolkit默认inference的data_format是nhwc

嗯嗯,但是如果只做rknn_input = input_tensor.numpy()[0].transpose(1,2,0),也就是只把 channel 维移到最后,结果还是颠倒的。

需要 rknn_input = input_tensor.numpy()[0][::-1].transpose(1,2,0), 也就是先把 channel 维倒过来,再把 channel 维移到最后, onnx 和 rknn 的推断结果才一致
回复

使用道具 举报

kkkaaa

中级会员

积分
203
8#
 楼主| 发表于 2020-5-14 21:51:54 | 只看该作者
本帖最后由 kkkaaa 于 2020-5-14 21:57 编辑

我知道原因了
因为我没写
  1. rknn.config(reorder_channel="0 1 2")
复制代码

不指定 config 的话,当channel 个数等于3的时候看起来是默认 reorder_channel = "2 1 0"
回复

使用道具 举报

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

本版积分规则

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


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