目录

前言

1.数据集介绍

2.使用的工具介绍

3.搭建ResNet

3.1ResNet结构分析

3.2网络结构搭建与代码实现

3.3中间过程特征提取

总结


前言

在对CNN有了一定的了解后,尝试搭建ResNet18网络来处理CIFAR-10数据集,通过这此的模型搭建,来熟悉层数较少的ResNet的代码实现,以及测试ResNet网络在处理CIFAR-10数据集上的性能,但是由于ResNet网络结构比较复杂,并且设备的原因算力不行,可能这次模型的性能比较低,在可行的基础上对ResNet18网络进行一些简化,以及改造。

1.数据集介绍

CIFAR-10数据集由10个类的60000个32x32彩色图像组成,每个类有6000个图像。有50000个训练图像和10000个测试图像。
数据集分为五个训练批次和一个测试批次,每个批次有10000个图像。测试批次包含来自每个类别的恰好1000个随机选择的图像。训练批次以随机顺序包含剩余图像,但一些训练批次可能包含来自一个类别的图像比另一个更多。总体来说,五个训练集之和包含来自每个类的正好5000张图像。里面类别包括飞机,汽车,鸟,猫,鹿,狗,青蛙,马,船,卡车十个类别。

2.使用的工具介绍

主要是TensorboardX和Netron进行可视化,具体介绍参照我之前的cnn处理MNIST数据集文章https://blog.csdn.net/qq_41845951/article/details/118814447

3.搭建ResNet

3.1ResNet结构分析

当网络结构增加的时候,网络的 深度也会增加,随之也会产生梯度离散或者梯度爆炸的情况,深层次网络结构主要是梯度离散,简而言之就是,网络的深度达到某个临界值,网络的性能会降低。

因此,ResNet结构出现,用来解决这一问题,

上图就是ResNet的最基本的结构,在基本的CNN网络结构中加了shortcut结构,

之所以网络深度增加后网络的性能会降低,是因为反向传播中,梯度在某个地方小于1了,那么梯度是会越来越接近于0,导致梯度无法更新,再也学不到新的东西,对于shortcut结构,我们是在这个是用现在的梯度,加上求导之前的梯度,相当于增加了梯度,于是,梯度弥散的现象会减小。

从特征方面来看,对于卷积操作,每一次卷积,特征会更加的细致,当然也会丢掉一些有用的特征,从而导致深层次的网络性能会有所降低,shortcut操作,可以理解为当前的特征加上经过一个Bottleneck(上图的结构层)后的特征,保存了更多的特征信息。

因此,ResNet能够保证网络在加上了一个Bottleneck后,至少不比原来的网络层性能低

3.2网络结构搭建与代码实现

数据集的导入以及预处理,

resize()调整图片尺寸

ToTensor()转换成tensor

Normalize()归一化预处理,可以看我之前的文章

batch_size用来测试或者训练的图片的数量,shuffle数据打乱

batchsz = 100cifar_train = datasets.CIFAR10('cifar', True, transform=transforms.Compose([transforms.Resize((32, 32)),transforms.ToTensor(),transforms.Normalize(mean=[0.485, 0.456, 0.406],std=[0.229, 0.224, 0.225])]), download=True)cifar_train = DataLoader(cifar_train, batch_size=batchsz, shuffle=True)cifar_test = datasets.CIFAR10('cifar', False, transform=transforms.Compose([transforms.Resize((32, 32)),transforms.ToTensor(),transforms.Normalize(mean=[0.485, 0.456, 0.406],std=[0.229, 0.224, 0.225])]), download=True)cifar_test = DataLoader(cifar_test, batch_size=batchsz, shuffle=True)x, label = iter(cifar_train).next()print('x:', x.shape, 'label:', label.shape)

选取图片进行查看,为了方便,只显示了一个通道,因此是灰色的

# 查看部分数据fig, axis = plt.subplots(4, 6, figsize=(15, 10))images, labels = next(iter(cifar_train))for i, ax in enumerate(axis.flat):with torch.no_grad():image, label = images[i][:1,:,:], labels[i]ax.imshow(image.view(32,32), cmap='binary')plt.show()

搭建模型,首先定义一个Bottleneck,也就是一个单位网络层

其中主要结构为池化-归一-池化-归一

当然还有一个维度的处理,如果输入的channel与输出的channel不同,那么F(x)与x不能相加,于是有个维度的处理,使[b,ch_in,h,w] 可以变换成[b,ch_out,h,w]

而后是shortcut操作out = self.extra(x)+out

class Bottleneck(nn.Module):def __init__(self,ch_in,ch_out):super(Bottleneck,self).__init__()self.conv1 = nn.Conv2d(ch_in,ch_out,kernel_size=3,stride=1,padding=1)self.bn1 = nn.BatchNorm2d(ch_out)self.conv2 = nn.Conv2d(ch_out,ch_out,kernel_size=3,stride=1,padding=1)self.bn2 = nn.BatchNorm2d(ch_out)self.extra = nn.Sequential()if ch_out !=ch_in:# [b,ch_in,h,w] ==>[b,ch_out,h,w]self.extra = nn.Sequential(nn.Conv2d(ch_in,ch_out,kernel_size=1,stride=1),nn.BatchNorm2d(ch_out))def forward(self,x):#param x:[b,ch,h,w]out = F.relu(self.bn1(self.conv1(x)))out = self.bn2(self.conv2(out))#short cut.#element-wise add:[b,ch_in,h,w]  with [b,ch_out,h,w]out = self.extra(x)+outreturn out

接着搭建ResNet18网络,

结构为池化-归一-Bottleneck1-Bottleneck2-Bottleneck3-Bottleneck4,最后再摊平一维化

class ResNet18(nn.Module):def __init__(self):super(ResNet18,self).__init__()self.conv1 = nn.Sequential(nn.Conv2d(3,16,kernel_size=3,stride=1,padding=1),nn.BatchNorm2d(16))#followed 4 blcself.blk1 = Bottleneck(16,16)self.blk2 = Bottleneck(16,32)# self.blk3 = Bottleneck(128, 256)# self.blk4 = Bottleneck(256, 512)self.outlayer = nn.Linear(32*32*32,10)def forward(self,x):x = F.relu(self.conv1(x))x = self.blk1(x)x = self.blk2(x)# x = self.blk3(x)# x = self.blk4(x)# print(x.shape)x = x.view(x.size(0),-1)x = self.outlayer(x)return x

初始化模型,loss,以及选择优化器,并将他们放入cuda中加速,并打印出网络结构

device = torch.device('cuda')model = ResNet18().to(device)criteon = nn.CrossEntropyLoss().to(device)optimizer = optim.Adam(model.parameters(), lr=1e-3)print(model)

打印出网络结构为

ResNet18(
  (conv1): Sequential(
    (0): Conv2d(3, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
  )
  (blk1): ResBlock(
    (conv1): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (bn1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    (conv2): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (bn2): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    (extra): Sequential()
  )
  (blk2): ResBlock(
    (conv1): Conv2d(64, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (bn1): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    (conv2): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (bn2): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    (extra): Sequential(
      (0): Conv2d(64, 128, kernel_size=(1, 1), stride=(1, 1))
      (1): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    )
  )
  (blk3): ResBlock(
    (conv1): Conv2d(128, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (bn1): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    (conv2): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (bn2): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    (extra): Sequential(
      (0): Conv2d(128, 256, kernel_size=(1, 1), stride=(1, 1))
      (1): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    )
  )
  (blk4): ResBlock(
    (conv1): Conv2d(256, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (bn1): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    (conv2): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (bn2): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    (extra): Sequential(
      (0): Conv2d(256, 512, kernel_size=(1, 1), stride=(1, 1))
      (1): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    )
  )
  (outlayer): Linear(in_features=524288, out_features=10, bias=True)
)

由于cuda内存不足,为了更快的运行,就暂时只有两个block进行训练,缩减网络的复杂度,更快的得出结果。

class Bottleneck(nn.Module):def __init__(self):super(ResNet18,self).__init__()self.conv1 = nn.Sequential(nn.Conv2d(3,16,kernel_size=3,stride=1,padding=1),nn.BatchNorm2d(16))#followed 4 blcself.blk1 = Bottleneck(16,16)self.blk2 = Bottleneck(16,32)self.outlayer = nn.Linear(32*32*32,10)def forward(self,x):x = F.relu(self.conv1(x))x = self.blk1(x)x = self.blk2(x)# print(x.shape)x = x.view(x.size(0),-1)x = self.outlayer(x)return x

训练,并保存训练中的loss以及acc等,训练部分代码讲解可以参考我的文章

train_loss = []Acc = []for epoch in range(20):model.train()running_loss = 0for batchidx, (x, label) in enumerate(cifar_train):# [b, 3, 32, 32]# [b]x, label = x.to(device), label.to(device)logits = model(x)# logits: [b, 10]# label:  [b]# loss: tensor scalarloss = criteon(logits, label)# backpropoptimizer.zero_grad()loss.backward()optimizer.step()running_loss += loss.item()train_loss.append(running_loss / len(cifar_train))print('epoch:',epoch+1, 'loss:', loss.item())model.eval()with torch.no_grad():# testtotal_correct = 0total_num = 0for x, label in cifar_test:# [b, 3, 32, 32]# [b]x, label = x.to(device), label.to(device)# [b, 10]logits = model(x)# [b]pred = logits.argmax(dim=1)# [b] vs [b] => scalar tensorcorrect = torch.eq(pred, label).float().sum().item()total_correct += correcttotal_num += x.size(0)# print(correct)acc = total_correct / total_numAcc.append(total_correct / total_num)print(epoch, 'test acc:', acc)

test acc: 0.6602

可视化loss和acc

plt.plot(train_loss, label='Loss')
plt.plot(Acc, label='Acc')

优化模型,加入非线性激活函数,使用3个Bottleneck,设置stride变量,用stride=2以及池化层降维,调整参数,使模型能够在cuda上运行。

class Bottleneck(nn.Module):def __init__(self,ch_in,ch_out,stride):super(Bottleneck,self).__init__()self.conv1 = nn.Conv2d(ch_in,ch_out,kernel_size=3,stride=1,padding=1)self.bn1 = nn.BatchNorm2d(ch_out)nn.ReLU(inplace=True)self.conv2 = nn.Conv2d(ch_out,ch_out,kernel_size=3,stride=stride,padding=1)self.bn2 = nn.BatchNorm2d(ch_out)nn.ReLU(inplace=True)self.extra = nn.Sequential()if ch_out !=ch_in:# [b,ch_in,h,w] ==>[b,ch_out,h,w]self.extra = nn.Sequential(nn.Conv2d(ch_in,ch_out,kernel_size=1,stride=stride),nn.BatchNorm2d(ch_out))self.relu = nn.ReLU(inplace=True)def forward(self,x):#param x:[b,ch,h,w]out = F.relu(self.bn1(self.conv1(x)))out = self.bn2(self.conv2(out))#short cut.#element-wise add:[b,ch_in,h,w]  with [b,ch_out,h,w]out = self.extra(x)+outreturn self.relu(out)
class ResNet18(nn.Module):def __init__(self):super(ResNet18,self).__init__()self.conv1 = nn.Sequential(nn.Conv2d(3,32,kernel_size=3,stride=1,padding=1),nn.BatchNorm2d(32),nn.ReLU(inplace=True),nn.AvgPool2d(kernel_size=3, stride=2, padding=1),)#followed 4 blcself.blk1 = Bottleneck(32,32,1)self.blk2 = Bottleneck(32,64,1)self.blk3 = Bottleneck(64,128,2)# self.blk4 = Bottleneck(256, 512)self.outlayer = nn.Linear(128*8*8,10)def forward(self,x):x = F.relu(self.conv1(x))x = self.blk1(x)x = self.blk2(x)x = self.blk3(x)# x = self.blk4(x)# print(x.shape)x = x.view(x.size(0),-1)x = self.outlayer(x)return x

test acc: 0.8018

可视化loss以及acc

通过对模型的优化,正确率提升了15%左右,最终loss: 0.1143,acc:acc: 0.8018,可以看到还是存在过拟合现象

可能是因为cuda内存有限,没有使用标准的4个Bottleneck,以及很多地方进行了降维操作,并且train数据集偏少

3.3中间过程特征提取

conv1层,这一层输出有32个通道

blk1层,输出有64个通道,截取部分特征

blk2层,有32个通道,截取部分特征

blk3层,有16个通道

可以看出每深一层,特征就更加细致,同时有不少特征都是相似的。

总结

本次搭建了最简单的ResNet18,熟悉了ResNet的原理以及结构,

但是由于设备原因,只用了3个Bottleneck,以及很多地方进行了降维操作,并且train数据集偏少,如果再设备允许的情况下,正确率还会再提升的

其次,由于是第一次接触这个网络,确实有很多方面可能没理解到,对于网络的优化可能没有做到位,过后会继续深入学习,争取改正这个模型。

使用ResNet18处理cifar10数据集相关推荐

  1. 使用ResNet18实现CIFAR10数据集的训练

     如果对你有用的话,希望能够点赞支持一下,这样我就能有更多的动力更新更多的学习笔记了.

  2. 简介ResNet18并用其对CIFAR-10数据集进行分类

    ResNet,是2015年何恺明大佬发表在CVPR上的一篇文章,运用了残差连接这个概念.该论文一出,直接引爆了整个cv界.并且在2016年ImageNet上ResNet获得第一名.而ResNet至今被 ...

  3. NNDL 实验六 卷积神经网络(5)使用预训练resnet18实现CIFAR-10分类

    目录 5.5实践:基于ResNet18网络完成图像分类任务 5.5.1数据处理 5.5.1.1数据集介绍 5.5.1.2 数据读取 5.5.1.2 数据集划分 5.5.2模型构建 5.5.2.1使用R ...

  4. Pytorch实战2:ResNet-18实现Cifar-10图像分类(测试集分类准确率95.170%)

    版权说明:此文章为本人原创内容,转载请注明出处,谢谢合作! Pytorch实战2:ResNet-18实现Cifar-10图像分类 实验环境: Pytorch 0.4.0 torchvision 0.2 ...

  5. Caffe2——cifar10数据集创建lmdb或leveldb类型的数据

    Caffe2--cifar10数据集创建lmdb或leveldb类型的数据 时间:2015-05-05 15:52:31      阅读:5183      评论:0      收藏:1      [ ...

  6. 基于Keras搭建cifar10数据集训练预测Pipeline

    基于Keras搭建cifar10数据集训练预测Pipeline 钢笔先生关注 0.5412019.01.17 22:52:05字数 227阅读 500 Pipeline 本次训练模型的数据直接使用Ke ...

  7. caffe学习(五):cifar-10数据集训练及测试(Ubuntu)

    简介 网站链接:CIFAR-10 CIFAR-10数据集包括由10个类别的事物,每个事物各有6000张彩色图像,每张图片的大小是32*32. 整个数据集被分成了5个训练集和1个测试集,各有10000张 ...

  8. TF之CNN:基于CIFAR-10数据集训练、检测CNN(2+2)模型(TensorBoard可视化)

    TF之CNN:基于CIFAR-10数据集训练.检测CNN(2+2)模型(TensorBoard可视化) 目录 1.基于CIFAR-10数据集训练CNN(2+2)模型代码 2.检测CNN(2+2)模型 ...

  9. Dataset之CIFAR-10:CIFAR-10数据集简介、下载、使用方法之详细攻略

    Dataset之CIFAR-10:CIFAR-10数据集简介.下载.使用方法之详细攻略 目录 CIFAR-10简介 1.与MNIST 数据集中目比, CIFAR-10 真高以下不同点 2.Tensor ...

最新文章

  1. 梯度下降法 —— 经典的优化方法
  2. 2021年春季学期-信号与系统-第八次作业参考答案-第二小题
  3. Android背景渐变色(shape,gradient)
  4. Java_BigInteger
  5. 2017.4.26 组合数问题 思考记录
  6. Python中for循环之range、enumerate函数
  7. 文本表达:解决BERT中的各向异性方法总结
  8. 处理数字音乐文件用计算机软件,处理数字音乐文件用计算机软件商标
  9. [Microsoft Lync] Find a previous conversation - Chat History
  10. fremaker遍历list_Freemarker中如何遍历List
  11. 小米3的卡槽,卡住了
  12. Python多子图绘制
  13. 面试中的最常被问到的两种锁
  14. Linux内存泄漏检测方法总结
  15. navicat的使用以及学生信息表的操作
  16. 從受虐狂向虐待狂轉變
  17. 制作马赛克拼图的工具:TurboMosaic Mac版
  18. 大屏幕和笔记本适应 html,大屏幕 才够劲!超大屏幕笔记本推荐
  19. 中国移动10086客服中心工作人员的血和泪
  20. 649. Dota2 参议院

热门文章

  1. 通过网页版堡垒机访问服务器失败,堡垒机远程连接服务器被拒绝
  2. vm-quick-start使用
  3. 什么都不会的学习之路——Java多线程
  4. 鲜为人知的26个植物的秘密
  5. 记忆减退之----DB9---232 连接图
  6. 安利2款绝美的壁纸软件,超好用!
  7. 浙江计算机二级操作题内容,浙江省计算机二级上机操作题(新).doc
  8. R语言ggplot统计图之:小提琴图geom_violin
  9. 【精易wed 浏览器实现旋转滑动验证码】
  10. 职业攻略:成为一名游戏代练||成为一名程序员