垃圾分类模型想上maixpy(2):https://developer.aliyun.com/article/1407163
1-5
对比Params与模型文件实际体积。
结果:模型实际大小与Params大小是可以对上的,参数应该是以float32存储。我把“字节”与“位”搞混了,应该是一个字节为8位。疑惑:为什么nncase量化后,模型文件体积还是没有显著减小?例如tfilte文件大小为436KB,量化后的kmodel大小仍有380KB。但从float32到uint8,大小理应变为四分之一。
看tensorflow的视频,卷积层参数量如何计算。
结果:P a r a m = C o u t ∗ ( W ∗ H ∗ C i n + 1 ) 。输出特征图的通道数决定过滤器的个数,输入特征图的通道数决定每个过滤器的通道数。
链接:https://blog.csdn.net/m0_63238256/article/details/128619369
重新训练mobilenet_v2。
发现:使用batch_size=128和使用batch_size=32时每个step的耗时差不多,于是整体使用batch_size=128时准确率上升更快。然而训练准确率达到92%时,测试准确率却只有70%。
使用batch_size=32时,也出现了过拟合。训练5个epoch时准确率从78%到了81%,但是测试准确率却突然从73%掉到了58%。再训练5个epoch后训练准确率为84%,测试准确率又回到了74%。
问题:过拟合真的是噩梦。
发现2:转换为kmodel后的体积发生了变化。tflite是8.47MB没有变,上次kmodel是8.44MB,这次kmodel是6.31MB。使用Netron依然打不开模型,板子上也依然加载不了。
问题2:据chatGPT和mainxpy群里的“小老鼠”所说,转换模型时的WARN: Conv2D_19 Fallback to float conv2d due to weights divergence没有关系。那导致加载模型时ValueError的原因是什么呢?
发现3:因为使用的cifar10数据集,图片分辨率为(32, 32),所以网络中出现了(1, 1)分辨率的图片被(3, 3)卷积核卷积的情况,可能是它导致了问题。
改用图片分辨率为(224, 224)的数据集训练mobilenet_v2 。
进度:卡在了调整数据集中图片大小这一步,找了个猫狗数据集,数据集中图片的分辨率是不固定的。虽然opencv的resize操作可以调整图片大小,但是我不知道怎么把这个操作插入到tensorflow训练模型的流水线中去。
贪图一些API的方便,却又不熟悉这些API的使用。
解决方案:继续学习tensorflow课程视频,了解对自己数据集的处理流程
1-8
miblenet_v2在horse-or-human数据集上训练失败。
发现:层
tf.keras.layers.BatchNormalization()
似乎会导致问题,多次使用该层容易导致像是过拟合的现象。下面是五层卷积的简单模型,插入了三层BatchNormalization,产生的训练数据:
Epoch 1/5 16/16 [=====] - 7s 345ms/step - loss: 2.6593 - acc: 0.7812 - val_loss: 0.6854 - val_acc: 0.7617 Epoch 2/5 16/16 [=====] - 5s 305ms/step - loss: 0.0719 - acc: 0.9793 - val_loss: 0.7076 - val_acc: 0.4570 Epoch 3/5 16/16 [=====] - 5s 320ms/step - loss: 0.3488 - acc: 0.8887 - val_loss: 0.6957 - val_acc: 0.4688 Epoch 4/5 16/16 [=====] - 5s 321ms/step - loss: 0.0114 - acc: 0.9980 - val_loss: 0.6909 - val_acc: 0.5000 Epoch 5/5 16/16 [=====] - 6s 373ms/step - loss: 0.2425 - acc: 0.9503 - val_loss: 0.8048 - val_acc: 0.5000
使用mobilenet_v2时,现象类似,train_acc可以在90%以上,val_acc恒为50%整。
令人不解的是,当我使用训练集数据进行评估,acc是在50%附近浮动,那为何训练时会显示那么高的准确率?
model.evaluate(train_generator) 129/129 [==============================] - 10s 77ms/step - loss: 0.8571 - acc: 0.4869
删除mobilenet_v2中的所有BatchNormalization层,模型却无法收敛了,train_acc在50%附件徘徊。
使用的mobilenet_v2模型代码来自MobileNet V2 网络结构的原理与 Tensorflow2.0 实现 。
想法:换个数据集会不会不一样?毕竟mobilenet_v2之前在mnist数据集上是可以正常训练的。
猜测:1、我的mobilenet_v2代码有问题,与官方的不同;2、数据集不合适。
方案:1、调用 model.summary() 查看keras中官方mobienet_v2的模型结构,与它对齐;2、改用自己的4分类垃圾数据集训练。
1-9
对齐mobilenet_v2模型结构 。
步骤1:先试试keras中的mobilenet_v2能不能正常训练;
结果:train_acc正常上升。(补充:这步疏忽了,没有测试val_acc,后面发现是有问题)
步骤2:对比两个模型的结构,以及训练时准确率;
发现:相对官方模型的train_acc随epoch稳定上升,我的模型train_acc显得不稳定。仔细对比模型结构,发现我模型尾部多了个卷积层。
# 例 - 调用keras官方mobilenet_v2 tf.keras.applications.mobilenet_v2.MobileNetV2(input_shape=(224,224,3), weights=None, classes=4)
在原来CSDN找到的代码中,该卷积层与Reshape层搭配,发挥全连接层的作用。因为nncase-v0.2.0不支持Reshape层(Reshape算子是支持的,但是Reshape层会产生不被支持的Shape算子),我就把Reshape删了,补了全连接层Dense,但是我没有把那个卷积层删掉。于是,相当于多了一层。
# 原代码中的用法
# 原代码中的用法 x = tf.keras.layers.GlobalAveragePooling2D()(x) x = tf.keras.layers.Reshape((1,1,1280))(x) x = tf.keras.layers.Conv2D(classes, (1,1), padding='same')(x) x = tf.keras.layers.Reshape((classes,))(x) x = tf.keras.layers.Activation('softmax')(x) # 正确修改方式 x = tf.keras.layers.GlobalAveragePooling2D(keepdims=True)(x) x = tf.keras.layers.Flatten()(x) x = tf.keras.layers.Dense(classes)(x) x = tf.keras.layers.Activation('softmax')(x)
改成上述“正确修改方式“后,训练效果与官方模型接近。
进一步训练mobilenet_v2模型,尝试转换和上板 。
突发:Colab的GPU限额了,正在摸索阿里的天池实验室。
原来的代码与天池实验室默认环境不兼容,但此时发现我的Colab解封了,哈哈。
问题:发现官方的模型也不能正常训练;训练时train_acc可以上升,但我将训练集作为验证集得到的val_acc在25%左右。
Epoch 1/2 64/64 - 53s 820ms/step - loss: 1.5922 - acc: 0.3819 - val_loss: 1.4714 - val_acc: 0.2489 Epoch 2/2 64/64 - 53s 834ms/step - loss: 1.5034 - acc: 0.3931 - val_loss: 1.4462 - val_acc: 0.2489
分析:是模型的问题,还是训练过程的问题?
- 要说模型,但之前mobilenet_v2模型在mnist和cifar-10数据集上都可以正常预测;
- 要说训练过程,但之前该流程在horse-or-human上训练也可以正常预测。
怎么组合起来就不行了呢?
发现:只要换成下面注释调的那几行代码,模型训练就由 ”正常“ 变为 ”val_acc恒为50%“。
from tensorflow.keras.optimizers import RMSprop # model = tf.keras.applications.mobilenet_v2.MobileNetV2(input_shape=(150,150,3), weights=None, classes=2) model.compile( loss='binary_crossentropy', # loss='categorical_crossentropy', optimizer=RMSprop(lr=0.001), metrics=['acc']) from tensorflow.keras.preprocessing.image import ImageDataGenerator train_datagen = ImageDataGenerator(rescale=1/255) train_generator = train_datagen.flow_from_directory( 'horse-or-human', target_size=(150,150), batch_size=32, class_mode='binary' # class_mode='categorical' ) validation_datagen = ImageDataGenerator(rescale=1/255) validation_generator = validation_datagen.flow_from_directory( 'validation-horse-or-human', target_size=(150,150), batch_size=32, class_mode='binary' # class_mode='categorical' ) history = model.fit( train_generator, steps_per_epoch=16, # 控制单个epoch的大小,以batch为单位。 epochs=5, verbose=1, validation_data=validation_generator )
发现2:令人疑惑的情况越来越多了,在tensorflow官方手写数字识别的例子(数据集已被我换成cifar-10)中,换上我的mobilenet_v2时val_acc正常,调用的官方mobilenet_v2模型却val_acc=10%。
参见我的notebook:mobilenet_v2尝试-mnist 。
感觉Colab的环境不太稳定,容易突然卡住或者变慢;而且有时会提示配额上限。
也许我还是需要在自己的电脑上搭建个tensorflow环境。
1-10
给我的电脑安装tensorflow的GPU环境 。
(不用)查看某个已安装包的信息
pip show <包名>
查看电脑的CUDA版本,在命令行输入
nvidia-smi
tensorflow-gpu需要搭配cuda和cudnn使用。
我的CUDA版本:11.7
官方链接:tensorflow-gpu的版本关系
计划版本搭配:python3.7,tensorflow_gpu-2.6.0,cudnn8.2.1,对应的CUDA应该是11.2。
包管理工具:使用conda install
结果:安装成功,很顺利。
解决conda install时CondaSSLError的问题。
参考:[报错解决]CondaSSLError
在参考博客的评论区看到说环境变量的问题,我想起来我以前为了在命令行窗口运行非anaconda的python,把一些环境变量删掉了,后来忘记补上。一股脑补上一些环境变量后,重启终端窗口,就好了。
D:\anaconda D:\anaconda\Scripts D:\anaconda\Library\bin D:\anaconda\Library\mingw-w64\bin
将cifar-10数据集用ImageDataGenerator加载,训练mobilenet_v2 。
怀疑:ImageDataGenerator的使用流程可能存在导致问题;
理由:
我之前直接用API调cifar-10数据集,是可以正常训练mobilenet_v2模型的。
换到ImageDataGenerator从文件夹加载图片后,就出现 “训练时train_acc上涨,val_acc不变;在训练集上的评估acc像没训练” 的情况。
下一步:何必用ImageDataGenerator?我可以自己写代码从文件夹读图片,读成images+labels的形式,与正确训练的条件对齐。
重写数据加载部分代码,训练mobilenet_v2 。
结果:在cifar-10上训练成功,在horse-or-human和我的GarbageForMaixHub上训练失败。
越来越疑惑,是谁的问题?模型?数据集?
1-11
尝试mobilenet_v2模型从训练到板上推理的流程能否走通,暂不考虑模型准确性 。
问题:转kmodel时报错:Fatal: KPU allocator cannot allocate more memory. ;搜索问题发现,可能是KPU限制特征图大小不能超过2MB。但我最大的一层特征图尺寸为(1, 112, 112, 96),大小是 1 , 204 , 224 1,204,2241,204,224 ,1MB多一点而已。
尝试:将训练时输入尺寸减半,从(224,224)到(112,112)
结果:转换得到了kmodel,大小为2.25MB
问题:E (4139792902) SYSCALL: Out of memory 。烧录后,加载模型会卡住没反应,打开串口终端就报这个错误。
可能方案:换更小的固件,参见:使用mind+运行口罩识别模型显示内存不足(已解决)
分析:应该是板子是的main.py文件里有加载模型的操作,加载我刚刚烧录的模型,就会导致Out of memory
查看内存大小,参见:内存管理 - Sipeed Wiki
import gc print(gc.mem_free() / 1024) # stack mem import Maix print(Maix.utils.heap_free() / 1024) # heap mem ''' 我的 502.9687 2532.0 官方示例 352.0937 4640.0 '''
内存好像是偏小,可以换个小固件、或者减小模型输入图片尺寸试试。
换固件:maixpy_v0.6.2_84_g8fcd84a58_openmv_kmodel_v4_with_ide_support.bin,再查看内存大小为[502.9687, 3912.0]
再减小模型输入尺寸 :从(112, 112)到(96, 96)。96应该是当前模型结构能用的最小输入尺寸了,再小就会出现(2, 2)的特征图被(3, 3)的卷积核处理的情况,这在k210上会导致加载失败(上次mobilenet_v2在cifar-10上训练的模型就是加载时报错)。
结果:模型加载成功。
import sensor, lcd, image import KPU as kpu # 测试板子是否响应 print('\nboard start...') # 载入模型 model_addr = 0x300000 print('load model...') task = kpu.load(model_addr) print('hello, world') ''' 终端输出 board start... load model... hello, world '''
下一步:测试模型能否产生预测输出