赞
踩
本文通过记录在pytorch中训练CIFAR-10数据集的一些过程,实现一个基本的数据集的分类,并在此过程中加强对图片、张量、CNN网络的理解,并尝试去总结一些训练技巧,记录一个新手对数据及网络的理解。
CIFAR-10数据集包含10个类别的60000个32x32彩色图像,每个类别6000个图像。 有50000张训练图像和10000张测试图像。数据集地址如下:
示例如下:
接下来我们要做的事情便是利用各种CNN的网络模型,在训练集上训练我们的模型,然后在测试集上测试模型的泛化能力,然后不断调节各种超参数及网络模型的细节,已到达在测试集上有更高的分类准确率的目的。
代码实现如下:
import torch import torch.nn as nn import torch.optim as optim import torchvision import torchvision.datasets as datasets import torchvision.transforms as transforms import matplotlib.pyplot as plt import io import sys import os #超参数定义 EPOCH = 100 BATCH_SIZE = 64 LR = 0.001 #数据集加载 #对训练集及测试集数据的不同处理组合 transform_train = transforms.Compose([ transforms.RandomHorizontalFlip(), transforms.RandomGrayscale(), transforms.ToTensor(), transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))]) transform_test = transforms.Compose([ transforms.ToTensor(), transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))]) #将数据加载进来,本地已经下载好, root=os.getcwd()为自动获取与源码文件同级目录下的数据集路径 train_data = datasets.CIFAR10(root=os.getcwd(), train=True,transform=transform_train,download=False) test_data =datasets.CIFAR10(root=os.getcwd(),train=False,transform=transform_test,download=False) #数据分批 from torch.utils.data import DataLoader #使用DataLoader进行数据分批,dataset代表传入的数据集,batch_size表示每个batch有多少个样本 #shuffle表示在每个epoch开始的时候,对数据进行重新排序 #数据分批之前:torch.Size([3, 32, 32]):Tensor[[32*32][32*32][32*32]],每一个元素都是归一化之后的RGB的值;数据分批之后:torch.Size([64, 3, 32, 32]) #数据分批之前:train_data([50000[3*[32*32]]]) #数据分批之后:train_loader([50000/64*[64*[3*[32*32]]]]) train_loader = DataLoader(dataset=train_data,batch_size=BATCH_SIZE,shuffle=True,num_workers=2) test_loader = DataLoader(dataset=test_data,batch_size=BATCH_SIZE,shuffle=True,num_workers=2) #模型加载,有多种内置模型可供选择 model = torchvision.models.densenet201(pretrained=False) #定义损失函数,分类问题使用交叉信息熵,回归问题使用MSE criterion = nn.CrossEntropyLoss() #torch.optim来做算法优化,该函数甚至可以指定每一层的学习率,这里选用Adam来做优化器,还可以选其他的优化器 optimizer = optim.Adam(model.parameters(),lr=LR) #设置GPU device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu') #模型和输入数据都需要to device mode = model.to(device) #模型训练 from torch.utils.tensorboard import SummaryWriter writer = SummaryWriter('cifar-10') for epoch in range(EPOCH): for i,data in enumerate(train_loader): #取出数据及标签 inputs,labels = data #数据及标签均送入GPU或CPU inputs,labels = inputs.to(device),labels.to(device) #前向传播 outputs = model(inputs) #计算损失函数 loss = criterion(outputs,labels) #清空上一轮的梯度 optimizer.zero_grad() #反向传播 loss.backward() #参数更新 optimizer.step() #利用tensorboard,将训练数据可视化 if i%50 == 0: writer.add_scalar("Train/Loss", loss.item(), epoch*len(train_loader)+i) #print('it’s training...{}'.format(i)) print('epoch{} loss:{:.4f}'.format(epoch+1,loss.item())) #保存模型参数 torch.save(model,'cifar10_densenet161.pt') print('cifar10_densenet161.pt saved') #模型加载 model = torch.load('cifar10_densenet161.pt') #测试 #model.eval() model.train() correct,total = 0,0 for j,data in enumerate(test_loader): inputs,labels = data inputs,labels = inputs.to(device),labels.to(device) #前向传播 outputs = model(inputs) _, predicted = torch.max(outputs.data,1) total =total+labels.size(0) correct = correct +(predicted == labels).sum().item() #准确率可视化 if j%20 == 0: writer.add_scalar("Train/Accuracy", 100.0*correct/total, j) print('准确率:{:.4f}%'.format(100.0*correct/total))
以下整理训练过程的大致情况
trick:
A:model.eval()换成了model.train()
B:pretrained=True
C:优化器使用SGD
D:0-50,lr=0.01;50-150,lr=0.001,150-200,lr=0.0001
E:transform_train,transform_test
F:num_workers=2
先试了一下各个模型在同等条件下的表现,EPOCH选的比较小,是为了更快的得出结果:
Model | EPOCH | BATCH_SIZE | LR | Tricks | Min_Loss | Accuracy |
---|---|---|---|---|---|---|
Resnet50 | 10 | 64 | 0.001 | - | 0.7573 | 68.0700% |
Resnet101 | 10 | 64 | 0.001 | - | 1.1134 | 52.9000% |
Resnet150 | 10 | 64 | 0.001 | - | 1.3076 | 51.0700% |
Resnet18 | 10 | 64 | 0.001 | - | 0.2364 | 67.7100% |
Resnet34 | 10 | 64 | 0.001 | - | 0.4274 | 71.9800% |
Densenet161 | 10 | 64 | 0.001 | - | 0.1355 | 73.8000% |
Densenet201 | 10 | 64 | 0.001 | - | 0.2540 | 77.7300% |
可以看出:同样使用Resnet,并不是网络层数越多网络表现越好;同等条件下Densenet的表现更好,而且很明显;同样使用Densenet,网络层数越深,效果越好,但实际运行时耗时较多。
选用Densenet161及Densenet201作为基准模型,开始调整其他参数:
Model | EPOCH | BATCH_SIZE | LR | Tricks | Min_Loss | Accuracy |
---|---|---|---|---|---|---|
Densenet161 | 10 | 64 | 0.001 | - | 0.1355 | 73.8000% |
30 | 64 | 0.001 | - | 0.0039 | 79.4600% | |
200 | 64 | 0.001 | - | 0.0000(e73) 0.0002(e37) | 81.5100% | |
Densenet161 | 10 | 64 | 0.001 | A | 0.1401 | 78.0000% |
10 | 32 | 0.001 | A | 0.1470 | 78.8200% | |
10 | 128 | 0.001 | A | 0.2270 | 77.8300% | |
10 | 512 | 0.001 | A | 0.2391 | 74.5100% | |
10 | 64 | 0.0001 | A | 0.1392 | 64.0600% | |
37 | 32 | 0.001 | A | 0.0012 | 79.4100% | |
10 | 64 | 0.001 | A+B | 0.5316 | 76.6300% | |
Densenet201 | 10 | 64 | 0.001 | - | 0.2540 | 77.7300% |
10 | 64 | 0.001 | C | 1.0627 | 53.7300% | |
200 | 64 | 0.001 | D | 0.0000(e62) | 80.8600% | |
10 | 64 | 0.001 | E+F | 0.3051 | 78.8100% |
可以看出,在epoch=10、batch_size=64,lr=0.001的同等条件下,densenet201的效果要好于densenet161,而且batch_size=32的效果也要更好,但分批设置LR(D)的效果还没看出来,从数据看几个trick中,A、E、F对于提升准确率也是有帮助的,B貌似起到了负效果,C严重影响了模型的训练。接下来设置一组参数:
Model | EPOCH | BATCH_SIZE | LR | Tricks | Times | Min_Loss | Accuracy |
---|---|---|---|---|---|---|---|
densenet201 | 100 | 32 | 0.001 | AEF | 8H | 0.0001(E87) | 83.2100% |
果然,效果达到了最佳,那还能不能继续改进呢。比如加上D(分段设置lr),会不会更好一点呢:经过一晚上的训练之后结果如下:
(D:e1-e24:0.01,e25-e79:0.001,e80-e100:0.0005)
Model | EPOCH | BATCH_SIZE | LR | Tricks | Times | Min_Loss | Accuracy |
---|---|---|---|---|---|---|---|
densenet201 | 100 | 32 | 0.001 | ADEF | 8H | 0.0002(E86) | 82.5300% |
效果上来看没有之前好。那就这样吧,在这个上面浪费了太多的时间,暂且这样。
总结:网络层数的加深一般能起到比较积极的作用;训练的循环次数加深在一定范围内对网络模型训练的正向作用较大;合适且较小的BATCH_SIZE能在数据量一定的情况下提升训练效果;变化的学习率可能会起到比较好的作用,但是这个变化本身也是很难设定的;优化器的选取也十分重要;数据的预处理、模型的小细节也可以为优化网络作出一份贡献。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。