一 总结前期的模型的收集训练工作。
- 图片清理:除了中期报告中提到过的对图片格式做整理剔除外,又做了一轮手工清理,将肉眼都难以识别的图片,直接删除。
- 训练:参考catsdogs例程。这里我是在win10+anconda环境运行的。在中期报告中,未能正确驱动起自己的2060显卡,在安装好pytorh之后,安装包时选择ai8x-training下的requirements-win-cu11.txt。
python train.py --epochs 300 --optimizer Adam --lr 0.0008 --wd 0 --deterministic --compress policies/schedule-catsdogs.yaml --model ai85cdnet --dataset zodiac --confusion --param-hist --embedding --device MAX78000 --data data --use-bias
在ai8x-training训练需要3个文件,训练模型,这里直接使用的是ai85cdnet;载入训练、测试数据的脚本文件 ai8x-training\datasets\zodiac.py 这个文件指明了,训练数据和测试数据的来源、对测试数据的变换以及输出内容的数量。训练过程最为漫长,在2060显卡的加持下,上边的脚本,执行了超过8小时,才完成。而且训练完成后的结果并不理想,总是50%左右,已经尝试多次,始终无法提高。该步骤会生成训练结果文件qat_best.pth.tar,这里需要留意,训练步骤如果少于10次(即 epochs 后边参数小于10),则无法产生该文件。
- 模型转换。在ai8x-synthesis 下执行以下命令,由qat_best.pth.tar生成zodiac-q.pth.tar。
python quantize.py ../ai8x-training/logs/2023.11.20-130436/qat_best.pth.tar ../ai8x-training/logs/2023.11.20-130436/zodiac-q.pth.tar --device MAX78000 -v
-
模型评估。在 ai8x-training下执行以下命令。
python train.py --model ai85cdnet --dataset zodiac --confusion --evaluate --exp-load-weights-from ./logs/2023.11.20-130436/zodiac-q.pth.tar -8 --device MAX78000
- 生成测试样本。在 ai8x-training下执行以下命令。这次命令会产生一个sample_zodiac.npy文件,需要把这个文件拷贝到ai8x-synthesis\tests下,下一步操作会用到这个文件。
python train.py --model ai85cdnet --save-sample 10 --dataset zodiac --evaluate --exp-load-weights-from ./logs/2023.11.20-130436/zodiac-q.pth.tar -8 --device MAX78000 --data data --use-bias
- 生成MAX78000可用的工程。在ai8x-synthesis 下执行以下命令。第一条命令是设置电脑Git环境的,不一定需要,如果执行命令提示git错误,就需要设置一下。留意命令中“--fifo”参数,catsdogs例程中的脚本是没有这个参数的,但是我在实际跑的过程中,发现如果不带这个参数,会报fifo错误,无法生成工程。
set GIT_PYTHON_GIT_EXECUTABLE=C:\Program Files\Git\bin\git.exe python ai8xize.py --verbose --test-dir "sdk/Examples/MAX78000/CNN" --prefix zodiac --checkpoint-file ../ai8x-training/logs/2023.11.20-130436/zodiac-q.pth.tar --config-file networks/zodiac-hwc.yaml --fifo --device MAX78000 --compact-data --mexpress --softmax --overwrite
二、完成训练。生成一个MAX78000能跑的工程。接下来就是做单片机的开发了。
以前玩过MAX78000的摄像头,这个摄像头分辨率不高,拍照速度也不快,这里为了测试方便,就想给板子添加一个屏幕,能同步显示摄像头看见的物品,方便操作。尝试了手头的ST7789屏幕,折腾了几天,没能将ST7789驱动起来。最终还是放弃了ST7789,购买了一块官方兼容的ili9341屏幕。
- 系统初始化。已生成的MAX78000中,已经对CNN模型部分和串口部分进行了初始化。所以在这个工程中,需要额外添加摄像头、TFT屏幕做初始化。这里需要留意这个全局变量。
static const uint32_t input_0[] = SAMPLE_INPUT_0;
这个变量是用来给CNN模型提供推理数据的变量,在原始的工程中,这个变量给赋值了一个样例图片,样例图片在sampledata.h文件中提供的,这里将const修饰符去掉,并且给出一个长度(128*128*3),作为全局变量,在读取图片后将图片数据写入这个变量中。
图片输入,提供了两个途径:1 SD卡图片输入;2 摄像头输入。从SD卡输入图片,目的是为了更直观地验证模型,所以也是预先准备了10张图片,将图片修改为128X128的位图格式,用0~9数字方式命名,放入SD卡根目录中。
参考了第一期老师们的作业,添加了SD卡读取和TFT字体驱动对应的文件。添加以上文件后,还需要修改工程中project.mk文件,否则会有头文件依赖问题。# Add your config here! MXC_OPTIMIZE_CFLAGS = -O2 BOARD=FTHR_RevA LIB_SDHC = 1 $(info Note: This project is designed and tested for the OV7692 only.) override CAMERA=OV7692 # Set a higher optimization level. The increased performance # is required for the CameraIF DMA code to work within the # timing requirements of the Parallel Camera Interface. MXC_OPTIMIZE_CFLAGS = -O2 # Place build files specific to FTHR_RevA here. ifeq "$(BOARD)" "FTHR_RevA" # Only Enable if 2.4" TFT is connected to Feather #PROJ_CFLAGS+=-DTFT_ENABLE IPATH += TFT/fthr VPATH += TFT/fthr endif
- 数据处理。无论从SD卡,还是从摄像头读取到的图像数据,都是使用rgb888的格式(需要留意:摄像头读取到的数据每个像素是4位的,SD卡读取的数据是bgr顺序的)。要显示到tft屏幕上,需要转换为rgb565格式。给cnn模型提供数据,则是要每个像素都转换为一个uint32的整形,并写入input_0变量数组中。这里需要留意,因为是单片机,没有那么大的内存,所以是对图片逐行处理的,每次处理一行(128X3)的数据,并且处理完一行,就在tft上绘制1行。
// RGB565 buffer for TFT //将摄像头获取的RGB888的图片,转换为RGB565格式,用于TFT显示 uint8_t data565[IMAGE_SIZE_X * 2]; void cover_rgb888_rgb565(int pos, uint8_t *data, uint8_t step) { uint8_t r, g, b; uint16_t rgb; uint32_t cnnval = 0; uint16_t offset = 0; for (int i = 0; i < step * IMAGE_SIZE_X; i += step) { if (step == 4) { r = data[i]; g = data[i + 1]; b = data[i + 2]; } if (step == 3) { b = data[i]; g = data[i + 1]; r = data[i + 2]; } cnnval = ((b << 16) | (g << 8) | r) ^ 0x00808080; input_0[pos++] = cnnval; rgb = ((r & 0b11111000) << 8) | ((g & 0b11111100) << 3) | (b >> 3); data565[offset++] = (rgb >> 8) & 0xFF; data565[offset++] = rgb & 0xFF; // printf("pos=%d i=%d [%d,%d,%d] ", pos, i, r, g, b); // printf(" [%d,%d] ",data565[i / 2],data565[i / 2+1]); // printf(" %ld\n",cnnval); } }
- 模型推理。准备好数据后就进入推理环节了。这一过程基本保留了自动生成工程中的流程。但是例程中有个函数“check_output”,这个是在推理完成后做的一个操作。不是很清楚是干嘛用的,保留这个操作,总是会报错!
在这里也是卡了很久,最后发现将这个步骤忽略掉,整个流程就通畅了。
三、心得体会:
机器学习已经渗透到了生活中了。基本原理能够明白,但是自己上手做一个完整的项目,感觉还是挺辛苦的。在训练模型过程中,因为训练时间很长,导致问题重现的代价很大。但是坚持下来还是收获满满。这个项目总算是完整地走下来了,虽然模型准确率还是比较差,但是整改机器学习的过程还是能掌握了。感谢电子森林,也感谢群里各位老师!
图片较大,放百度云共享了。链接:https://pan.baidu.com/s/1Ix6iN3vxxtLHNMQdcDmMZQ
提取码:4zj8