MicroNet实战分享!使用MicroNet实现图像分类(二)
来源:投稿 作者:AI浩
编辑:学姐
上回我们说到了MicroNet实战:使用MicroNet实现图像分类(一),完成了前期的准备工作今天第二部也来了。废话不多,这就来学。(文末福利)
配置参数
本次训练采用的参数是M3的配置参数,详细的配置参数在utils/defaults.py文件,参数如下:
训练
完成上面的步骤后,就开始train脚本的编写,新建train.py.
导入项目使用的库
设置全局参数
设置学习率、BatchSize、epoch等参数,判断环境中是否存在GPU,如果没有则使用CPU。建议使用GPU,CPU太慢了。
设置存放权重文件的文件夹,如果文件夹存在删除再建立。
接下来,查看全局参数:
model_lr:学习率,根据实际情况做调整。
BATCH_SIZE:batchsize,根据显卡的大小设置。
EPOCHS:epoch的个数,一般300够用。
use_amp:是否使用混合精度。
classes:类别个数。
resume:是否接着上次模型继续训练。
model_path:模型的路径。如果resume设置为True时,就采用model_path定义的模型继续训练。
CLIP_GRAD:梯度的最大范数,在梯度裁剪里设置。
Best_ACC:记录最高ACC得分。
图像预处理与增强
数据处理比较简单,加入了Cutout、做了Resize和归一化,定义Mixup函数。
这里注意下Resize的大小,由于MixNet的输入是224×224的大小,所以要Resize为224×224。
读取数据
使用pytorch默认读取数据的方式,然后将dataset_train.class_to_idx打印出来,预测的时候要用到。
将dataset_train.class_to_idx保存到txt文件或者json文件中。
class_to_idx的结果:{‘Black-grass’: 0, ‘Charlock’: 1, ‘Cleavers’: 2, ‘Common Chickweed’: 3, ‘Common wheat’: 4, ‘Fat Hen’: 5, ‘Loose Silky-bent’: 6, ‘Maize’: 7, ‘Scentless Mayweed’: 8, ‘Shepherds Purse’: 9, ‘Small-flowered Cranesbill’: 10, ‘Sugar beet’: 11}
设置模型
设置loss函数,train的loss为:SoftTargetCrossEntropy,val的loss:nn.CrossEntropyLoss()。
设置模型为MicroNet,num_classes设置为12。如果resume为True,则加载模型接着上次训练。
优化器设置为adamW。
学习率调整策略选择为余弦退火。
开启混合精度训练,声明pytorch自带的混合精度 torch.cuda.amp.GradScaler()。
检测可用显卡的数量,如果大于1,并且开启多卡训练的情况下,则要用torch.nn.DataParallel加载模型,开启多卡训练。
注:torch.nn.DataParallel方式,默认不能开启混合精度训练的,如果想要开启混合精度训练,则需要在模型的forward前面加上@autocast()函数。

如果不开启混合精度则要将@autocast()去掉,否则loss一直试nan。
定义训练和验证函数
训练函数 训练的主要步骤:
1、使用AverageMeter保存自定义变量,包括loss,ACC1,ACC5。
2、判断迭代的数据是否是奇数,由于mixup_fn只能接受偶数,所以如果不是偶数则要减去一位,让其变成偶数。但是有可能最后一次迭代只有一条数据,减去后就变成了0,所以还要判断不能小于2,如果小于2则直接中断本次循环。
3、将数据输入mixup_fn生成mixup数据,然后输入model计算loss。
4、optimizer.zero_grad() 梯度清零,把loss关于weight的导数变成0。
5、如果使用混合精度,则 with torch.cuda.amp.autocast(),开启混合精度。 计算loss。 scaler.scale(loss).backward(),梯度放大。 torch.nn.utils.clip_grad_norm_,梯度裁剪,放置梯度爆炸。 scaler.step(optimizer) ,首先把梯度值unscale回来,如果梯度值不是inf或NaN,则调用optimizer.step()来更新权重,否则,忽略step调用,从而保证权重不更新。 更新下一次迭代的scaler。 否则,直接反向传播求梯度。torch.nn.utils.clip_grad_norm_函数执行梯度裁剪,防止梯度爆炸。
6、torch.cuda.synchronize(),等待上面所有的操作执行完成。
7、接下来,更新loss,ACC1,ACC5的值。
等待一个epoch训练完成后,计算平均loss和平均acc
验证函数
验证集和训练集大致相似,主要步骤:
定义参数,test_loss测试的loss,total_num总的验证集的数量,val_list验证集的label,pred_list预测的label。
在val的函数上面添加@torch.no_grad(),作用:所有计算得出的tensor的requires_grad都自动设置为False。即使一个tensor(命名为x)的requires_grad = True,在with torch.no_grad计算,由x得到的新tensor(命名为w-标量)requires_grad也为False,且grad_fn也为None,即不会对w求导。
使用验证集的loss函数求出验证集的loss。
调用accuracy函数计算ACC1和ACC5
更新loss_meter、acc1_meter、acc5_meter的参数。
本次epoch循环完成后,求得本次epoch的acc、loss。
如果acc比Best_ACC大,则保存模型。
调用训练和验证方法
调用训练函数和验证函数的主要步骤:
1、定义参数:
is_set_lr,是否已经设置了学习率,当epoch大于一定的次数后,会将学习率设置到一定的值,并将其置为True。
log_dir:记录log用的,将有用的信息保存到字典中,然后转为json保存起来。
train_loss_list:保存每个epoch的训练loss。
val_loss_list:保存每个epoch的验证loss。
train_acc_list:保存每个epoch的训练acc。
val_acc_list:保存么每个epoch的验证acc。
epoch_list:存放每个epoch的值。
循环epoch:
调用train函数,得到 train_loss, train_acc,并将分别放入train_loss_list,train_acc_list,然后存入到logdir字典中。
调用验证函数,得到val_list, pred_list, val_loss, val_acc。将val_loss, val_acc分别放入val_loss_list和val_acc_list中,然后存入到logdir字典中。
保存log。
打印本次的测试报告。
如果epoch大于600,将学习率设置为固定的1e-6。
绘制loss曲线和acc曲线。
运行以及结果查看
完成上面的所有代码就可以开始运行了。点击右键,然后选择“run train.py”即可,运行结果如下:

在每个epoch测试完成之后,打印验证集的acc、recall等指标。

绘制acc曲线

绘制loss曲线:训练了1000个epoch,最好的成绩能达到93.X%
测试
测试,我们采用一种通用的方式。
测试集存放的目录如下图:
测试的主要逻辑:
定义类别,这个类别的顺序和训练时的类别顺序对应,一定不要改变顺序!!!!
定义transforms,transforms和验证集的transforms一样即可,别做数据增强。
加载model,并将模型放在DEVICE里,
循环 读取图片并预测图片的类别,在这里注意,读取图片用PIL库的Image。不要用CV2,transforms不支持。
循环里面的主要逻辑:
使用Image.open读取图片
使用transform_test对图片做归一化和标椎化。
img.unsqueeze_(0) 增加一个维度,由(3,224,224)变为(1,3,224,224)
Variable(img).to(DEVICE):将数据放入DEVICE中。
model(img):执行预测。
_, pred = torch.max(out.data, 1):获取预测值的最大下角标。
运行结果:

图像分类的论文资料有同学需要吗!都是CVPR高分论文!
关注【学姐带你玩AI】公众号
回复“CVPR”免费获取~注意看规则