简单的一批
可以跳过的部分
首先把图像分类技术应用到比赛中肯定有几个目标:
- 能正常由数据集训练出图像分类模型。
- 能够调用模型并获得不错的分类效果。
- 能画图, 能把图片放进论文里。
这三点TensorFlow做的都不错, 唯一不足的就是TensorFlow官方推荐语言是脚本语言Python, 脚本语言拿来写程序终究是会遇到性能上的硬伤, 而这会让本来性能就很一般的TensorFlow变得更加的不适合在Arm-Linux上运行。
所以这应该也是我最后一次应用TensorFlow到比赛的调用场景。(当然训练或许还是得用他, 比如给K210训练MoblieNet之类的。毕竟用脚本语言写训练脚本我觉得很合适。而且没有密集型计算, 密集型计算全是调用的C++和cuda。)
首先要确定需求, 由需求选择技术。
在我的实际测试中, 大致把需求分为以下几种:
- 在K210这种RAM较少但是算力中等的平台。
- 在普通Arm-Linux上这种RAM中等但是算力较差的平台(没错, 即使是RK3399这种Cortex A72在CPU条件下的神经网络算力也是远远不如K210的)。
- 在有cuda加速的设备上, 如Jetson-Linux和笔记本电脑。
对于第一种需求, 需要选择模型体积文件较小的神经网络, 比如 MoblieNet
图像分类网络 或者 Yolo-MobileNetV2
或 Yolo-Fastest
图像识别网络。因为RAM不足, 所以会牺牲一点准确率。
对于第二种需求, 需要选择体积文件合适并且对算力要求较低的网络 比如在帧率要求不高的图像分类场合可以选择 Xception
提高准确率或者选择牺牲准确率提高帧率 比如 YOLO-Fastest
对于第三种需求, 则只有一个目的: 准确率。自然选择 YOLO v4
或者 Xception
等。
如题, 本篇博客主要针对于需求2 3的图像分类技术。
图像预处理
本篇文档所有代码在文章最后有开源链接, 穿插在文章中的代码只是负责讲解, 直接复制是跑不起来的。
我不太明白TensorFlow为什么这么多预处理的api都会自动拉伸图像改变他原来的长宽比。这样很明显会丢失图像细节和削弱图像特征。
所以不如自己先预处理一下提高准确度。因为有些目标物本来就不是长宽1:1的形状, 而手动裁剪太浪费时间, 所以我选择预先这样处理。
(事实证明我这样做对模型准确率有较明显的提升的效果, 同样的垃圾分类数据集, 不预处理只能达到93%的正确率, 预处理后到了97.69%)
我把长边缩放为512像素, 短边在随机位置填充随机纯色背景。
一般的模型所需要的图片大小一般都远小于512, 所以凑个整, 体积小了也方便保存。
1 | if(input_height > input_width): |
预处理Python脚本开源地址:
未来会开源到这个下面, 等我打完这个比赛闲下来整理一下吧。
https://github.com/h13-0/Tensorflow-Study-Note
至于dataset格式的话, 个人推荐采用TensorFlow官方的 ImageDataGenerator.flow_from_directory()
所需要的格式。
1 | dataset |
也就是dataset下分别为 Test
Train
Valid
文件夹。
这三个文件夹下再放入存有不同种类图片的文件夹。
开始训练
下载脚本:
https://github.com/h13-0/Tensorflow-Study-Note
建议一个项目用一个文件夹, 使用的时候把本文件夹拷贝一份即可。
先暂时不更改所使用的网络模型。
更改 Train.py
的 Base_Dir
为你的 dataset
路径,
然后创建在当前终端的路径(或项目路径)下创建 log
weights
文件夹, 分别保存了 Tensorboard
数据 和 权重数据。
根据你的dataset修改 classes
以及 epoch
和 learning_rate
根据你的电脑性能更改 batch_size
然后终端输入 python ./Train.py
(建议使用终端打开, 注意在当前路径下是否有 log
和 weights
文件夹)
或双击 Train.py
(使用本方法则需要注意是否有 log
和 weights
文件夹)
验证模型
下载脚本:
https://github.com/h13-0/Tensorflow-Study-Note
修改 Base_Dir
和 Weight_File_Path
。
然后运行。
画图
1 | tensorboard --logdir #Your log dir here |
然后根据提示访问网页
还是蛮好看的。
具体每张图的作用请右转百度。
我原本很想把 embedding
加进去, 可惜这玩意对运存要求太高了, 我就不加了。
就是这玩意儿
更换模型
假设我要切换为上一篇博客所讲的 InceptionResNet V2
找到这一行:
1 | base_model = tf.keras.applications.Xception(weights="imagenet",include_top=False,input_shape=(299,299,3),pooling="avg") |
改为:
1 | base_model = tf.keras.applications.InceptionResNetV2(weights="imagenet",include_top=False,input_shape=(299,299,3),pooling="avg") |
注意我修改了哪里!!!
然后在(在绝大多数模型中都不需要这样更改, 比如InceptionResNet V2, 觉得自己运气好的可以跳过)
https://github.com/keras-team/keras-applications/blob/master/keras_applications/
下找到你用的模型的脚本, 即:
https://github.com/keras-team/keras-applications/blob/master/keras_applications/inception_resnet_v2.py
(或者ide中跳转到 InceptionResNetV2
的函数定义, 或者在ide中按住 Ctrl 单击 InceptionResNetV2
)
找到类似于这样的语句(根据include_top判断和修改输出神经层的语句)
1 | if include_top: |
看看在 include_top
为 Ture
的时候比 False
的(avg
池化)时候多了哪一层输出网络。
在本网络中, include_top
为 Ture
的时候在网络末端添加了
1 | x = layers.GlobalAveragePooling2D(name='avg_pool')(x) |
include_top
为 False
的(avg
池化)时候网络末端为
1 | x = layers.GlobalAveragePooling2D()(x) |
多了一层神经元数量为 classes
的 Dense
。
那就把
1 | outputs = layers.Dense(classes, activation='softmax')(base_model.output) |
改为他多出来的网络即可(在较为重量级的网络下不需要更改, 但是轻量级的网络可能不一样)。
当然, 本脚本也没有针对轻量级网络做优化就是了。。。
这里
1 | Xception |
等, 是不需要修改的。
但是
1 | MobileNet |
等, 是需要修改的。
最后, 核对官方建议分辨率是否为(299,299), 如不是, 请修改 Image_Shape
。
性能问题
调用时性能不足:
如果含有是含有nvidia的GPU的设备, 请在调用之前启用GPU加速
1
2
3physical_devices = tf.config.experimental.list_physical_devices('GPU')
for physical_device in physical_devices:
tf.config.experimental.set_memory_growth(physical_device, True)或者试图同时在训练和验证的时候缩小输入神经网络的图片(不建议)
训练时性能不足:
- 如果能启用cuda加速, 建议使用darknet框架代替TensorFlow。
- 去淘宝租GPU服务器。
- 加钱换GPU
据某些网友测试
cuda加速时速度:
Darknet > ncnn > pytorch > TensorFlow
Arm-CPU速度:
ncnn > Darknet > TensorFlow
至少我实测在Arm-CPU下跑 Yolo-Fastest
320*320 的时候
Darknet: 3000ms
ncnn: 450ms
还是有一定可信度的。
(并且cuda下训练 YOLO V4
的时候, Darknet比TensorFlow快很多)
Demo
https://github.com/h13-0/Garbage-Classification
TODO:
- 整理预处理脚本
- 整理训练脚本
- 整理验证脚本
- 上传视频