实验环境:

  1. Pytorch 1.7.0
  2. torchvision 0.8.2
  3. Python 3.8
  4. CUDA10.2 + cuDNN v7.6.5
  5. Win10 + Pycharm
  6. GTX1660, 6G

网络结构采用最简洁的类VGG结构,即全部由3*3卷积和最大池化组成,后面接一个全连接层用于分类,网络大小仅18M左右。

神经网络结构图:

Pytorch上搭建网络:

class Block(nn.Module):def __init__(self, inchannel, outchannel, res=True):super(Block, self).__init__()self.res = res     # 是否带残差连接self.left = nn.Sequential(nn.Conv2d(inchannel, outchannel, kernel_size=3, padding=1, bias=False),nn.BatchNorm2d(outchannel),nn.ReLU(inplace=True),nn.Conv2d(outchannel, outchannel, kernel_size=3, padding=1, bias=False),nn.BatchNorm2d(outchannel),)if stride != 1 or inchannel != outchannel:self.shortcut = nn.Sequential(nn.Conv2d(inchannel, outchannel, kernel_size=1, bias=False),nn.BatchNorm2d(outchannel),)else:self.shortcut = nn.Sequential()self.relu = nn.Sequential(nn.ReLU(inplace=True),)def forward(self, x):out = self.left(x)if self.res:out += self.shortcut(x)out = self.relu(out)return outclass myModel(nn.Module):def __init__(self, cfg=[64, 'M', 128,  'M', 256, 'M', 512, 'M'], res=True):super(myModel, self).__init__()self.res = res       # 是否带残差连接self.cfg = cfg       # 配置列表self.inchannel = 3   # 初始输入通道数self.futures = self.make_layer()# 构建卷积层之后的全连接层以及分类器:self.classifier = nn.Sequential(nn.Dropout(0.4),            # 两层fc效果还差一些nn.Linear(4 * 512, 10), )   # fc,最终Cifar10输出是10类def make_layer(self):layers = []for v in self.cfg:if v == 'M':layers.append(nn.MaxPool2d(kernel_size=2, stride=2))else:layers.append(Block(self.inchannel, v, self.res))self.inchannel = v    # 输入通道数改为上一层的输出通道数return nn.Sequential(*layers)def forward(self, x):out = self.futures(x)# view(out.size(0), -1): change tensor size from (N ,H , W) to (N, H*W)out = out.view(out.size(0), -1)out = self.classifier(out)return out

该网络可以很方便的改造成带残差的,只要在初始化网络时,将参数res设为True即可,并可改变cfg配置列表来方便的修改网络层数。

Pytorch上训练:

所选数据集为Cifar-10,该数据集共有60000张带标签的彩色图像,这些图像尺寸32*32,分为10个类,每类6000张图。这里面有50000张用于训练,每个类5000张,另外10000用于测试,每个类1000张。
训练策略如下:

  1. 优化器:momentum=0.9 的 optim.SGD,adam在很多情况下能加速收敛,但因为是自适应学习率,在训练后期存在不能收敛到全局极值点的问题,所以采用能手动调节学习率的SGD,现在很多比赛和论文中也是采用该策略。设置weight_decay=5e-3,即设置较大的L2正则来降低过拟合。
# 定义损失函数和优化器
loss_func = nn.CrossEntropyLoss()
optimizer = optim.SGD(model.parameters(), lr=LR, momentum=0.9, weight_decay=5e-3)
  1. 学习率:optim.lr_scheduler.MultiStepLR,参数设为:milestones=[int(num_epochs * 0.56), int(num_epochs * 0.78)], gamma=0.1,即在0.56倍epochs和0.78时分别下降为前一阶段学习率的0.1倍。
# 学习率调整策略 MultiStep:
scheduler = optim.lr_scheduler.MultiStepLR(optimizer=optimizer,milestones=[int(num_epochs * 0.56), int(num_epochs * 0.78)],gamma=0.1, last_epoch=-1)

在每个epoch训练完的时候一定要记得step一下,不然不会更新学习率,可以通过get_last_lr()来查看最新的学习率

# 更新学习率并查看当前学习率
scheduler.step()
print('\t last_lr:', scheduler.get_last_lr())
  1. 数据策略:
    实验表明,针对cifar10数据集,随机水平翻转、随机遮挡、随机中心裁剪能有效提高验证集准确率,而旋转、颜色抖动等则无效。
     norm_mean = [0.485, 0.456, 0.406]      # 均值norm_std = [0.229, 0.224, 0.225]       # 方差      transforms.Normalize(norm_mean, norm_std),                    #将[0,1]归一化到[-1,1]transforms.RandomHorizontalFlip(),                            # 随机水平镜像transforms.RandomErasing(scale=(0.04, 0.2), ratio=(0.5, 2)),  # 随机遮挡transforms.RandomCrop(32, padding=4)                          # 随机中心裁剪
  1. 超参数:
batch_size = 512     # 约占用显存4G
num_epochs = 200     # 训练轮数
LR = 0.01            # 初始学习率

实验结果:best_acc= 94.71%


另外,将网络改成14层的带残差结构后,准确率上升到了95.56%,但是网络大小也从18M到了43M。以下是14层残差网络的全部代码,8层的只需修改cfg和初始化时的res参数:
cfg=[64, ‘M’, 128, 128, ‘M’, 256, 256, ‘M’, 512, 512,‘M’] 修改为 [64, ‘M’, 128, ‘M’, 256, ‘M’, 512, ‘M’]

# *_* coding : UTF-8 *_*
# 开发人员: csu·pan-_-||
# 开发时间: 2020/12/29 15:17
# 文件名称: battey_class.py
# 开发工具: PyCharm
# 功能描述: 自建CNN对cifar10进行分类import torch
from torchvision import datasets, transforms
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import DataLoader
import onnx
import time
import numpy as np
import matplotlib.pyplot as pltclass Block(nn.Module):def __init__(self, inchannel, outchannel, res=True, stride=1):super(Block, self).__init__()self.res = res     # 是否带残差连接self.left = nn.Sequential(nn.Conv2d(inchannel, outchannel, kernel_size=3, padding=1, stride=stride, bias=False),nn.BatchNorm2d(outchannel),nn.ReLU(inplace=True),nn.Conv2d(outchannel, outchannel, kernel_size=3, padding=1, stride=1, bias=False),nn.BatchNorm2d(outchannel),)if stride != 1 or inchannel != outchannel:self.shortcut = nn.Sequential(nn.Conv2d(inchannel, outchannel, kernel_size=1, bias=False),nn.BatchNorm2d(outchannel),)else:self.shortcut = nn.Sequential()self.relu = nn.Sequential(nn.ReLU(inplace=True),)def forward(self, x):out = self.left(x)if self.res:out += self.shortcut(x)out = self.relu(out)return outclass myModel(nn.Module):def __init__(self, cfg=[64, 'M', 128, 128, 'M', 256, 256, 'M', 512, 512,'M'], res=True):super(myModel, self).__init__()self.res = res       # 是否带残差连接self.cfg = cfg       # 配置列表self.inchannel = 3   # 初始输入通道数self.futures = self.make_layer()# 构建卷积层之后的全连接层以及分类器:self.classifier = nn.Sequential(nn.Dropout(0.4),           # 两层fc效果还差一些nn.Linear(4 * 512, 10), )   # fc,最终Cifar10输出是10类def make_layer(self):layers = []for v in self.cfg:if v == 'M':layers.append(nn.MaxPool2d(kernel_size=2, stride=2))else:layers.append(Block(self.inchannel, v, self.res))self.inchannel = v    # 输入通道数改为上一层的输出通道数return nn.Sequential(*layers)def forward(self, x):out = self.futures(x)# view(out.size(0), -1): change tensor size from (N ,H , W) to (N, H*W)out = out.view(out.size(0), -1)out = self.classifier(out)return outall_start = time.time()
# 使用torchvision可以很方便地下载Cifar10数据集,而torchvision下载的数据集为[0,1]的PILImage格式
# 我们需要将张量Tensor归一化到[-1,1]
norm_mean = [0.485, 0.456, 0.406]  # 均值
norm_std = [0.229, 0.224, 0.225]  # 方差
transform_train = transforms.Compose([transforms.ToTensor(),  # 将PILImage转换为张量# 将[0,1]归一化到[-1,1]transforms.Normalize(norm_mean, norm_std),transforms.RandomHorizontalFlip(),  # 随机水平镜像transforms.RandomErasing(scale=(0.04, 0.2), ratio=(0.5, 2)),  # 随机遮挡transforms.RandomCrop(32, padding=4)  # 随机中心裁剪])transform_test = transforms.Compose([transforms.ToTensor(),transforms.Normalize(norm_mean, norm_std)])# 超参数:
batch_size = 256
num_epochs = 200   # 训练轮数
LR = 0.01          # 初始学习率# 选择数据集:
trainset = datasets.CIFAR10(root='Datasets', train=True, download=True, transform=transform_train)
testset = datasets.CIFAR10(root='Datasets', train=False, download=True, transform=transform_test)
# 加载数据:
train_data = DataLoader(dataset=trainset, batch_size=batch_size, shuffle=True)
valid_data = DataLoader(dataset=testset, batch_size=batch_size, shuffle=False)
cifar10_classes = ('plane', 'car', 'bird', 'cat', 'deer', 'dog', 'frog', 'horse', 'ship', 'truck')train_data_size = len(trainset)
valid_data_size = len(testset)print('train_size: {:4d}  valid_size:{:4d}'.format(train_data_size, valid_data_size))device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")model = myModel(res=True)# 定义损失函数和优化器
loss_func = nn.CrossEntropyLoss()
optimizer = optim.SGD(model.parameters(), lr=LR, momentum=0.9, weight_decay=5e-3)# 学习率调整策略 MultiStep:
scheduler = optim.lr_scheduler.MultiStepLR(optimizer=optimizer,milestones=[int(num_epochs * 0.56), int(num_epochs * 0.78)],gamma=0.1, last_epoch=-1)# 训练和验证:
def train_and_valid(model, loss_function, optimizer, epochs=10):model.to(device)history = []best_acc = 0.0best_epoch = 0for epoch in range(epochs):epoch_start = time.time()print("Epoch: {}/{}".format(epoch + 1, epochs))model.train()train_loss = 0.0train_acc = 0.0valid_loss = 0.0valid_acc = 0.0for i, (inputs, labels) in enumerate(train_data):inputs = inputs.to(device)labels = labels.to(device)# 因为这里梯度是累加的,所以每次记得清零optimizer.zero_grad()outputs = model(inputs)loss = loss_function(outputs, labels)loss.backward()optimizer.step()train_loss += loss.item() * inputs.size(0)ret, predictions = torch.max(outputs.data, 1)correct_counts = predictions.eq(labels.data.view_as(predictions))acc = torch.mean(correct_counts.type(torch.FloatTensor))train_acc += acc.item() * inputs.size(0)with torch.no_grad():model.eval()for j, (inputs, labels) in enumerate(valid_data):inputs = inputs.to(device)labels = labels.to(device)outputs = model(inputs)loss = loss_function(outputs, labels)valid_loss += loss.item() * inputs.size(0)ret, predictions = torch.max(outputs.data, 1)correct_counts = predictions.eq(labels.data.view_as(predictions))acc = torch.mean(correct_counts.type(torch.FloatTensor))valid_acc += acc.item() * inputs.size(0)# 更新学习率并查看当前学习率scheduler.step()print('\t last_lr:', scheduler.get_last_lr())avg_train_loss = train_loss / train_data_sizeavg_train_acc = train_acc / train_data_sizeavg_valid_loss = valid_loss / valid_data_sizeavg_valid_acc = valid_acc / valid_data_sizehistory.append([avg_train_loss, avg_valid_loss, avg_train_acc, avg_valid_acc])if best_acc < avg_valid_acc:best_acc = avg_valid_accbest_epoch = epoch + 1epoch_end = time.time()print("\t Training: Loss: {:.4f}, Accuracy: {:.4f}%, ""\n\t Validation: Loss: {:.4f}, Accuracy: {:.4f}%, Time: {:.3f}s".format(avg_train_loss, avg_train_acc * 100, avg_valid_loss, avg_valid_acc * 100,epoch_end - epoch_start))print("\t Best Accuracy for validation : {:.4f} at epoch {:03d}".format(best_acc, best_epoch))torch.save(model, '%s/' % 'cifar10_my' + '%02d' % (epoch + 1) + '.pt')  # 保存模型# # 存储模型为onnx格式:# d_cuda = torch.rand(1, 3, 32, 32, dtype=torch.float).to(device='cuda')# onnx_path = '%s/' % 'cifar10_shuffle' + '%02d' % (epoch + 1) + '.onnx'# torch.onnx.export(model.to('cuda'), d_cuda, onnx_path)# shape_path = '%s/' % 'cifar10_shuffle' + '%02d' % (epoch + 1) + '_shape.onnx'# onnx.save(onnx.shape_inference.infer_shapes(onnx.load(onnx_path)), shape_path)# print('\t export shape success...')return model, historytrained_model, history = train_and_valid(model, loss_func, optimizer, num_epochs)history = np.array(history)
# Loss曲线
plt.figure(figsize=(10, 10))
plt.plot(history[:, 0:2])
plt.legend(['Tr Loss', 'Val Loss'])
plt.xlabel('Epoch Number')
plt.ylabel('Loss')
# 设置坐标轴刻度
plt.xticks(np.arange(0, num_epochs + 1, step=10))
plt.yticks(np.arange(0, 2.05, 0.1))
plt.grid()  # 画出网格
plt.savefig('cifar10_shuffle_' + '_loss_curve1.png')# 精度曲线
plt.figure(figsize=(10, 10))
plt.plot(history[:, 2:4])
plt.legend(['Tr Accuracy', 'Val Accuracy'])
plt.xlabel('Epoch Number')
plt.ylabel('Accuracy')
# 设置坐标轴刻度
plt.xticks(np.arange(0, num_epochs + 1, step=10))
plt.yticks(np.arange(0, 1.05, 0.05))
plt.grid()  # 画出网格
plt.savefig('cifar10_shuffle_' + '_accuracy_curve1.png')all_end = time.time()
all_time = round(all_end - all_start)
print('all time: ', all_time, ' 秒')
print("All Time: {:d} 分 {:d} 秒".format(all_time // 60, all_time % 60))

Pytorch实战:8层神经网络实现Cifar-10图像分类验证集准确率94.71%相关推荐

  1. 深度神经网络训练过程中为什么验证集上波动很大_图神经网络的新基准

    作者 | 李光明 编辑 | 贾 伟 编者注:本文解读论文与我们曾发文章<Bengio 团队力作:GNN 对比基准横空出世,图神经网络的「ImageNet」来了>所解读论文,为同一篇,不同作 ...

  2. 深度学习入门——利用卷积神经网络训练CIFAR—10数据集

    CIFAR-10数据集简介 CIFAR-10是由Hinton的学生Alex Krizhevsky和Ilya Sutskever整理的一个用于普适物体的小型数据集.它一共包含10个类别的RGB彩色图片: ...

  3. 【土堆 pytorch实战】P22神经网络搭建

    P22搭建CIFAR-10 卷积层1:输入H=32,kernel_size=5,dilation=1 输出H=32,代入公式得 32=32+2∗padding−4−1stride+132=\frac{ ...

  4. 基于SVM的思想做CIFAR 10图像分类

    #SVM 回顾一下之前的SVM,找到一个间隔最大的函数,使得正负样本离该函数是最远的,是否最远不是看哪个点离函数最远,而是找到一个离函数最近的点看他是不是和该分割函数离的最近的. 使用large ma ...

  5. 【Pytorch实战6】一个完整的分类案例:迁移学习分类蚂蚁和蜜蜂(Res18,VGG16)

    参考资料: <深度学习之pytorch实战计算机视觉> Pytorch官方教程 Pytorch官方文档 本文是采用pytorch进行迁移学习的实战演练,实战目的是为了进一步学习和熟悉pyt ...

  6. 神经网络结构优化:这篇论文让你无惧梯度消失或爆炸,轻松训练万层神经网络...

    关注上方"深度学习技术前沿",选择"星标公众号", 资源干货,第一时间送达! 来源 | 机器之心 深度学习在众多领域都取得了显著进展,但与此同时也存在一个问题: ...

  7. 毕设路线—pytorch环境下的深度学习的高光谱图像分类问题

    毕设快要结束了,一路走来一直记录着点点滴滴的技术内容,主要想写给自己看吧,作为一个项目整理的大致框架,改完最终定稿,再填补每一部分的细节. 另外如果以后有做这个方向的朋友看到了,希望能提供一点小小的帮 ...

  8. 神经网络中验证集起了什么作用?

    在神经网络训练过程中,验证集(Validation set)起到以下几个作用: 模型选择(Model selection):验证集用于在训练过程中评估多个模型的性能,从而选择表现最佳的模型.这包括在不 ...

  9. 机器学习算法------1.10 交叉验证,网格搜索(交叉验证,网格搜索(模型选择与调优)API、鸢尾花案例增加K值调优)

    文章目录 1.10 交叉验证,网格搜索 学习目标 1 什么是交叉验证(cross validation) 1.1 分析 1.2 为什么需要交叉验证 2 什么是网格搜索(Grid Search) 3 交 ...

最新文章

  1. mysql www.school.com_MySQL 基础学习
  2. OpenCV-裁剪图片
  3. 斯坦福CS229机器学习课程的数学基础(概率论)翻译完成
  4. 飞桨框架2.0RC新增模型保存、加载方案,与用户场景完美匹配,更全面、更易用
  5. Apache Camel Test Framework(MOCK)
  6. qsettings mysql_qt连接mysql
  7. python有什么隐藏功能_Python的隐藏功能
  8. shell学习脚本-tomcat停止脚本
  9. AI队列长度检测:使用YOLO进行图像中的对象检测
  10. MFC体系结构(3)
  11. CodeForces 230A
  12. EF/SQL/新闻中分页应用
  13. Latex 排版第一页出现空白页
  14. 大用户量下Open***部署方案(二)
  15. 安全模块之FIPS 140-2标准和国密标准对比
  16. 第一个hadoop程序(hadoop2.4.0集群+Eclipse环境)
  17. 【C语言 穷举法编程实例——韩信点兵问题(苏小红版C语言(第3版))】
  18. rxjava背压_Rx系列第十八篇:RxJava之背压策略
  19. 小总结:git的使用
  20. sql not like 数据遗漏

热门文章

  1. android ca,如何在Android设备上安装可信CA证书?
  2. 入网许可证_进网许可证、电信设备入网许可证详解!
  3. 计算用户浏览时长(微信浏览器)
  4. 复杂网络分析库NetworkX学习笔记(4):统计指标计算
  5. 上海大学c语言吉米试题,求c语言大神学长学姐解答题目
  6. 秒变配色高手!怎么都不会错的6条网页设计配色原则
  7. android 6.0 7.0,Android 6.0/7.0可升级机型名单出炉
  8. BestMPRBaseVtk-003-修改工程,搬运官方代码并尝试理解-2
  9. 简单测试IP地址连通性
  10. CTFHub——Web技能树