模型在rv1126上跑起来遇到的坑

rknn对模型是彩色图片输入的很友好,如果输入是灰度图片,需要好好理解它的整套数据处理流程。
在这里插入图片描述
上面是数据处理的整个流程,cpu拿到的图片数据,需要经过一系列的预处理(颜色通道转换,归一化,量化,通道转换)这一过程是在rknn_inputs_set里面完成。
我的模型信息:
在这里插入图片描述
模型输入1个,单通道,数据类型是uint8, 量化类型为asymmetric affine非对称量化,即float32和uint8之间的转换。fl zp scale都是量化的参数。
在这里插入图片描述
模型输出2个,一个数据类型为float16 没有量化
第二个类型为uint8, 量化类型为asymmetric affine.

rknn_inputs_set耗时严重

我碰到的问题就是rknn_inputs_set非常的耗时!大概33ms左右!我是单通道图片,1400640,fmt是NCHW,也就是需要进行中间两步即可。先归一化成float32,再量化为uint8,然后把数据从cpu拷贝到npu,不知道为啥耗时这么长。最后问了rk的工作人员,说是对彩色图片内部做了优化,而灰度图片没有。
所以只能自己做优化。
rk的文档提供了两种方式:
1.是rknn_inputs_set里设置pass_through=1,相当于数据不再需要rknn_inputs_set做预处理,我自己处理好。我理解的这种应该是只节省了预处理的时间,拷贝的时间是仍然需要的。我之前自己实现了归一化和量化步骤(官网也有demo),耗时在10ms左右。后来我仔细阅读文档,发现如果三通道均值和方差相同,则输入 uint8 数据等于归一化后量化的 uint8 数据。
在这里插入图片描述
所以我的归一化和量化是可以省略的,也就是中间两步也省了,图片原始数据直接透传给NPU。这时rknn_inputs_set变成了1ms左右!我理解的拷贝耗时呢???
2.第二种是零拷贝,即把拷贝也省了。官方给了很详细的demo。原理大概是在内核核申请一片内存,数据放到这片内存上,npu直接去拿。不需要从cpu拷贝到npu。

rknn_outputs_set耗时

我已经设置了output_optimize=1,耗时在14ms左右,耗时主要是因为第二个输出,需要从uint8变为float32。
为了解决这个问题,我决定自己在外面做反量化。
反量化比量化简单很多,就是((float)qnt - (float)zp) * scale 我的模型第二个输出zp = 136 scale = 4.625256.

验证结果:
按照uint8_t直接把结果拿出来:

outputs[1].want_float = 0;
uint8_t* pdes = (uint8_t*)outputs[1].buf;
cv::Mat des_mat(400/8 * 256, 640/8, CV_8UC1, pdes);
std::cout << "des " << des_mat << std::endl;

输出结果:
在这里插入图片描述
耗时变成8ms。
按照float把结果拿出来:

outputs[1].want_float = 1;
float* pdes = (float*)outputs[1].buf;
cv::Mat des_mat(400/8 * 256, 640/8, CV_32FC1, pdes);
std::cout << "des " << des_mat << std::endl;

输出结果:
在这里插入图片描述
耗时19ms。
拿第一个做反量化运算:(124-136)* 4.625256 = −55.503072 恰好等于float拿出来的结果。所以这种方式是ok。

注意opencv恰好有数据类型转换的函数convertTo

/** @brief Converts an array to another data type with optional scaling.

    The method converts source pixel values to the target data type. saturate_cast\<\> is applied at
    the end to avoid possible overflows:

    \f[m(x,y) = saturate \_ cast<rType>( \alpha (*this)(x,y) +  \beta )\f]
    @param m output matrix; if it does not have a proper size or type before the operation, it is
    reallocated.
    @param rtype desired output matrix type or, rather, the depth since the number of channels are the
    same as the input has; if rtype is negative, the output matrix will have the same type as the input.
    @param alpha optional scale factor.
    @param beta optional delta added to the scaled values.
     */
    void convertTo( OutputArray m, int rtype, double alpha=1, double beta=0 ) const;

只需要转换一下参数即可。代码如下:

    uint8_t* pdes = (uint8_t*)outputs[1].buf;
    cv::Mat des_mat(400/8 * 256, 640/8, CV_8UC1, pdes);
    cv::Mat des;
    des.convertTo(des, CV_32FC1, 4.625256, -136*4.625256);

其实第一个输出也可以做类似的工作,模型的输出是float16, 我们可以自己在外面转为float32,(32 位浮点 和16 位浮点数相互转换 请参考 IEEE-754标准)。我看了一下,第一个也outputs[1].want_float = 0;模型输出耗时为4ms,测试convertTo的耗时为3.6855。加上第一个输出convertTo的耗时,和rk内部转换耗时8ms差不多,可以不采用自己转换。而计算描述子是放在后端5hz的,我们可以把第二个输出的convertTo到后端,不计入整个提点耗时里面。
故第一个输出采用want_float = 1,由rk内部转换;第二个采用want_float = 0,自己转换;

输出结果和在电脑的输出差异很大

应该是量化带来的误差。
1.首先确保 float 类型的精度和原始平台测试结果相近,rknn.build(do_quantization=False)
结果如下:
在电脑跑出来的结果
在这里插入图片描述
没有量化的结果
在这里插入图片描述
可见结果还是比较相近,误差在百分位上面。
2.测试量化后的精度
在这里插入图片描述
明显已经误差很大了,我用的图片数量10张,文档建议给出大于200张图片。
结果还是还上面一样
在这里插入图片描述
结果还是和上面一样,说明不是图片量太少的问题,需要分析每一层的量化精度。
rknn.accuracy_analysis(inputs='./image/dataset1.txt', target='rv1126')分析。
首先找到每一层对应的npu表示术语。

x = self.relu(self.conv1a(data))convolution_at_input0.1_1_1_out0_nhwc_1_400_640_64.tensor relu_at_89_2_2_out0_nhwc_1_400_640_64.tensor
x = self.relu(self.conv1b(x))convolution_at_input.3_3_3_out0_nhwc_1_400_640_64.tensor relu_at_101_4_4_out0_nhwc_1_400_640_64.tensor
x = self.pool(x)max_pooling_at_input.4_5_5_out0_nhwc_1_200_320_64.tensor
x = self.relu(self.conv2a(x))convolution_at_input.5_6_6_out0_nhwc_1_200_320_64.tensor relu_at_122_7_7_out0_nhwc_1_200_320_64.tensor
x = self.relu(self.conv2b(x))convolution_at_input.6_8_8_out0_nhwc_1_200_320_64.tensor relu_at_134_9_9_out0_nhwc_1_200_320_64.tensor
x = self.pool(x)max_pooling_at_input.7_10_10_out0_nhwc_1_100_160_64.tensor
x =self.relu(self.conv3a(x))convolution_at_input.8_11_11_out0_nhwc_1_100_160_128.tensor relu_at_155_12_12_out0_nhwc_1_100_160_128.tensor
x = self.relu(self.conv3b(x))convolution_at_input.9_13_13_out0_nhwc_1_100_160_128.tensor relu_at_167_14_14_out0_nhwc_1_100_160_128.tensor
x = self.pool(x)max_pooling_at_input.10_15_15_out0_nhwc_1_50_80_128.tensor
x = self.relu(self.conv4a(x))convolution_at_input.11_16_16_out0_nhwc_1_50_80_128.tensor relu_at_188_17_17_out0_nhwc_1_50_80_128.tensor
x = self.relu(self.conv4b(x))convolution_at_input.12_18_18_out0_nhwc_1_50_80_128.tensor relu_at_200_19_19_out0_nhwc_1_50_80_128.tensor

之后就是分开部分了,由于我们只关心描述子,就只看描述子部分的。

cDa = self.relu(self.convDa(x))convolution_at_input.1_40_34_out0_nhwc_1_50_80_256.tensor relu_at_235_41_35_out0_nhwc_1_50_80_256.tensor
descriptors = self.convDb(cDa)convolution_at_246_43_37_out0_nhwc_1_50_80_256.tensor
  • 3
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值