垃圾分类模型想上maixpy(1):https://developer.aliyun.com/article/1407162?spm=a2c6h.13148508.setting.17.79f64f0ecKMDuK
1-1
关于模型部署,MaixPy文档的这一部分中可能有些有用的参考:部署模型到 Maix-I(M1) K210 系列开发板 - Sipeed Wiki 。
- 实际用数字图片进行测试时,手写数字识别的模型无法产生正确的输出。
猜测:输入需要进行预处理。训练时使用了img = img / 255.0 ,但是在Maixpy中我不知道如何达成这一步操作。
我尝试了 image.div() 接口,但发现 /255 时就相当于数学中 /1 ,/125 就相当于数学中 /0.5 这种。也就是说,我无法使用这个接口让图像中的像素值变小。接着我使用了如下代码进行测试:
code:10图测试
import sensor, lcd, image import KPU as kpu # 测试图像列表 img_names = ['num0', 'num1', 'num2', 'num3', 'num4', 'num5', 'num6', 'num7', 'num8', 'num9'] # 载入模型 model_addr = 0x300000 task = kpu.load(model_addr) # 批量测试 for i in range(10): img = image.Image("/flash/" + img_names[i] + ".jpg") img = img.resize(28, 28) img = img.to_grayscale(copy=False) img.pix_to_ai() success = kpu.set_outputs(task, 0, 1, 1, 10) fmap = kpu.forward(task, img) plist=fmap[:] pmax=max(plist) max_index=plist.index(pmax) print('\ni = ', i) print(plist) print(max_index)
对于用于测试的10张图片,输出的 plist
都是:
(3.402823e+38, -3.402823e+38, 3.402823e+38, -3.402823e+38, 3.402823e+38, 3.402823e+38, -3.402823e+38, 3.402823e+38, 3.402823e+38, 3.402823e+38)
很容易发现它只有两个值:3.402823e+38 和 -3.402823e+38,看起来像是溢出了。是不是没有预处理的原因呢?
想对image对象进行/255的预处理,结果发现它没有小数,可以使用image.morph()对每个像素进行除法操作,但是得到的结果只能是0~255。
发现列表和元组都支持小数和除法操作。
有没有什么方便的办法将图像转换为列表或元组呢?运行模型的时候是否支持使用列表或元组作为输入?转换速度如何?
发现fmap = kpu.forward(task, img)中 img 可以是一维的列表。
测试10张手写数字的图片,模型都正确预测。哦耶!
下面的代码实现了对手写数字图片文件的预测:
code:正确预测
import sensor, image, time import KPU as kpu def to_list_1(img): return [x/255 for x in img[:]] # 载入图片 img = image.Image("/flash/num0.jpg") img = img.resize(28, 28) img = img.to_grayscale(copy=False) print() print(img) img = to_list_1(img) print(img[:10]) # 载入模型 model_addr = 0x300000 task = kpu.load(model_addr) # 推理 success = kpu.set_outputs(task, 0, 1, 1, 10) print(success) fmap = kpu.forward(task, img) print(fmap) # 处理模型输出 plist=fmap[:] pmax=max(plist) max_index=plist.index(pmax) print(plist) print(max_index) print()
通过板子摄像头识别的效果要稍差一些,但是通常还是可以产生正确的输出;使用的是mnist测试集中的图片。
对于自己手写的数字,效果比较糟糕。
拖动、缩放图片,可能导致预测结果变化。
猜测:训练集中的数据场景比较单一,又没有经过数据增强。
可以发现两种情况下直方图的差异较大,左为自己手写,右为mnist数据集中的图片。
目前问题:对一张图片的预测时间太长,例如在手写数字识别的例子中为0.7秒。
1-3
尝试将float参数类型的mnist分类模型使用nncase-0.2量化为uint8类型,成功。
目的:提升推理速度。nncase文档中提到使用uint8可以获得k210的kpu加速。
效果:模型推理时间从700ms左右减为40ms左右;从摄像头获得输入,可以获得正确的预测结果。
疑惑:为什么量化需要输入数据集作为参数呢?使用的编译参数为
ncc compile mnist_float.tflite mnist_float.kmodel -i tflite -o kmodel -t k210 --inference-type uint8 --dataset ./dataset_mnist --input-std 1 --input-mean 0
ncc compile mobilenet_v2.tflite mobilenet_v2.kmodel -i tflite -o kmodel -t k210 --inference-type uint8 --dataset ./dataset_garbage --input-std 1 --input-mean 0 --dump-weights-range --weights-quantize-threshold 64.000000
不进行量化(建议量化,因为摄像头输入本身是uint8):
ncc compile cifar10.tflite cifar10.kmodel -i tflite -o kmodel -t k210 --inference-type float
1-4
以moblienet-v2网络在mnist上训练了一个模型,然后转为tflite,分析了网络使用算子的支持情况。
mobilenet-v2网络代码来源:MobileNet V2 网络结构的原理与 Tensorflow2.0 实现 。
下表为mobilenet-v2中的算子支持情况:
算子 | 支持 |
Conv2D | ✅ |
Relu6 | ✅ |
DepthwiseConv2D | ✅ |
Add | ✅ |
Mean | ✅ |
Shape | ❌ |
StridedSlice | ✅ |
Pack | ❌ |
Reshape | ✅ |
Softmax | ✅ |
修改网络后,开始尝试tflite转kmodel。
bug:Fatal: Invalid dataset, should contain one file at least ,但文件夹明明非空啊。
发现:文件夹的名字打错了,修正后得到了kmodel。
问题:转换过程中显示WARN: Conv2D_19 Fallback to float conv2d due to weights divergence ,暂时不知有什么影响。
连接板子运行时,加载模型失败。
bug:ValueError: [MAIXPY]kpu: load error:2003, ERR_KMODEL_FORMAT: layer_header.body_size <= 0
参考:Fallback to float conv2d due to weight divergence · Issue #164 · kendryte/nncase (github.com) 。
bug2:使用Netron查看网络结构时显示Error loading kmodel. Unsupported version ‘4’ layer ‘strided_slice’.
发现:若不进行量化,可以在Netron正常查看结构,但仍加载时仍然有ValueError。
猜测:是否网络中单层体积太大?记得maixpy文档中说单层不能超过2MB(不知道是指参数量、输出特征图,还是什么)。使用model.summary()查看网络各层,最大的一层Param为410880。不知道每个参数占多大的空间,我们先假定为8字节,于是这层占用内存为410880 ∗ 8 / 102 4 2 = 3.13 M B 410880*8/1024^2=3.13MB410880∗8/1024
2
=3.13MB 。但这样就有一个矛盾,.tflite模型文件的实际大小为8.47MB,如果我按上面的计算方法,将所有层参数的参数大小加起来,会远超这个数值。
疑惑:卷积层的参数量是怎么计算的?前一层输出尺寸为(None, 1, 1, 320),本身输出为(None, 1, 1, 1280)。于是( 320 + 1 ) ∗ 1280 = 410880 (320 + 1) * 1280 = 410880(320+1)∗1280=410880 。但这不应该是全连接层的计算方法吗?我理解中卷积层应该P a r a m =filter∗kernel 。
垃圾分类模型想上maixpy(3):https://developer.aliyun.com/article/1407164?spm=a2c6h.13148508.setting.15.79f64f0ecKMDuK