在神经网络和深度学习领域,Yann LeCun可以说是元老级人物。他于1998年在 IEEE 上发表了一篇42页的长文,文中首次提出卷积-池化-全连接的神经网络结构,由LeCun提出的七层网络命名LeNet5,因而也为他赢得了卷积神经网络之父的美誉。CNN在近几年的发展历程中,从经典的LeNet5网络到最近号称最好的图像分类网络EfficientNet,大量学者不断的做出了努力和创新。
在后面的一些图像算法过程中,图像分类的网络结构起到了举足轻重的作用,他们都依赖于分类网络做主干特征提取网络,有一定的程度体现出特征提取能力。
本文讲述基于chainer的整个图像分类框架的搭建,从训练到预测,在更改网络算法结构情况下,整体框架结构保持不变,保证了后续使用的便利。
因ImageNet数据集过于庞大,在学习训练上比较费资源,因此本次数据集使用中国象棋数据集,中国象棋红黑棋子一共有14种类,经过预处理后会得到单独象棋图像,如图:
数据集主要以文件夹形式进行区分,每一个类别代表一个文件夹,如图:
在训练的时候直接把此文件夹目录放入代码中即可,对于训练集和验证集,代码会在这里的文件夹中做一个根据提供的划分比例随机拆分。
import numpy as np
import chainer,cupy
import chainer.functions as F
from chainer import optimizers
from chainer.training import StandardUpdater, extensions,Extension
from chainer.datasets import TransformDataset
from chainer.links import Classifier
数据文件夹读取,这里为的是加载每一张图像对应的label,并且以列表的形式保存起来,如图:
这里解释一下,输入参数DataDir是数据集的目录,里边包含对应label的文件夹,train_split=0.9代表训练集与验证机比例为9:1,最后返回list结构,每个list元素是字符串结构:‘path,label’。
数据列表有了,下面就是做一个数据加载器,如图:
这一个类主要是做迭代用的,用于在图像训练的过程中给模型数据。
有时候可能需要对图像做增强,提升模型的效果,这里再构建中可选可不选,如图:
这里的功能是对图像做一些随机裁剪、随机翻转、随机旋转角度、随机增强图像等操作,因为模型的矩阵输入口是一个固定大小的结构,这里也对图像做了resize操作,aux代表是否要增强图像。因此最后的调用代码如下:
train_data = Classification_TransformDataset(ClassificationDataSet(self.train_list), ('img', 'label'), Classification_TrainTransform(image_size = self.image_size,aux=self.aux,mean=self.mean))
val_data = Classification_TransformDataset(ClassificationDataSet(self.val_list), ('img', 'label'), Classification_ValTransform(image_size = self.image_size, mean=self.mean))
self.train_iter = chainer.iterators.SerialIterator(train_data, self.batch_size)
self.val_iter = chainer.iterators.SerialIterator(val_data, self.batch_size, repeat=False, shuffle=False)
train_iter、val_iter就是训练和验证的时候所需要的数据迭代器
self.extractor = None
self.model = Classifier(self.extractor)
if self.gpu_devices >= 0:
self.model.to_gpu()
self.extractor是对应的分类模型结构,这里先不做设置,设置为None,在后续的模型构建中,只需要修改此处即可。
self.model.to_gpu()代表模型使用GPU跑
Classifier是chainer提供的一个类,此类在训练过程中可以计算acc和loss,也是简化使用人员的工作。
首先我们先理解一下chainer的训练结构,如图:
从图中我们可以了解到,首先我们需要设置一个Trainer,这个可以理解为一个大大的训练板块,然后做一个Updater,这个从图中可以看出是把训练的数据迭代器和优化器链接到更新器中,实现对模型的正向反向传播,更新模型参数。然后还有就是Extensions,此处的功能是在训练的中途进行操作可以随时做一些回调(描述可能不太对),比如做一些模型评估,修改学习率,可视化验证集等操作。
因此我们只需要严格按照此图建设训练步骤基本上没有什么大问题,下面一步一步设置
设置优化器:
optimizer = optimizers.MomentumSGD(lr=learning_rate, momentum=0.9)
optimizer.setup(self.model)
设置update和trainer:
updater = chainer.training.StandardUpdater(self.train_iter, optimizer, device=self.gpu_devices)
trainer = chainer.training.Trainer(updater, (TrainNum, 'epoch'), out=ModelPath)
Extensions功能设置:
# 修改学习率
trainer.extend(
extensions.ExponentialShift('lr', 0.9, init=learning_rate),
trigger=chainer.training.triggers.ManualScheduleTrigger([50,80,150,200,280,350], 'epoch'))
# 每过一次迭代验证集跑一次
trainer.extend(
extensions.Evaluator(self.val_iter, self.model, device=self.gpu_devices),
trigger=chainer.training.triggers.ManualScheduleTrigger([each for each in range(1,TrainNum)], 'epoch'))
# 可视化验证集效果
trainer.extend(Classification_VIS(
self.model.predictor,
self.val_list,
self.classes_names,
size=(self.image_size,self.image_size),
mean=self.mean,
trigger=chainer.training.triggers.ManualScheduleTrigger([each for each in range(1,TrainNum)], 'epoch'),
device=self.gpu_devices,
ModelPath=ModelPath))
# 模型保存
trainer.extend(
chainer.training.extensions.snapshot_object(self.extractor, 'Ctu_final_Model.npz'),
trigger=chainer.training.triggers.ManualScheduleTrigger([each for each in range(1,TrainNum)], 'epoch'))
# 日志及文件输出
log_interval = 0.1, 'epoch'
trainer.extend(chainer.training.extensions.LogReport(filename='ctu_log.json',trigger=log_interval))
trainer.extend(chainer.training.extensions.observe_lr(), trigger=log_interval)
trainer.extend(extensions.dump_graph("main/loss", filename='ctu_net.net'))
最后配置完之后只需要一行代码即可开始训练
trainer.run()
模型预测主要还是输入为opencv格式,在数据预处理之前与前面数据加载时做的操作一致就行,直接上代码:
因为本代码是以对象形式编写的,因此调用起来也是很方便的,如下显示:
# 训练代码
ctu = Ctu_Classification(USEGPU='0',image_size=224)
ctu.InitModel(r'D:/Ctu/Ctu_Project_DL/DataSet/DataSet_Classification_Chess/DataImage',train_split=0.9,batch_size=4,alpha=1)
ctu.train(TrainNum=150,learning_rate=0.0001, ModelPath='result_Model')
# 预测代码
ctu = Ctu_Classification(USEGPU='0')
ctu.LoadModel('./result_Model')
cv2.namedWindow("result", 0)
cv2.resizeWindow("result", 640, 480)
img_cv = ctu.read_image(os.path.join(root, f))
if img_cv is None:
res = ctu.predict_simple(predict_cvs)
print(res['predict_output'],res['time'])
cv2.imshow("result", predict_cvs)
cv2.waitKey()
本文章主要是基于chainer的图像分类的基本框架,下边的主要是开始对各个模型如何再次框架中应用起来并把结果展示出来。