Pytorch:cifar10官方例程代码复盘,详解

笔者暑假跑了一个简单的cifar10分类来进行深度学习入门,当时不求甚解,很多地方没有理解,于是最近就将全部代码仔细复盘了一下,期间踩了些坑,同时写下自己的理解,不正确之处还请大佬斧正。
  先附上官方例程的代码。
  https://pytorch.org/tutorials/
以下是我的代码,对官方例程进行了一些修改,在代码中每一句都有标注。

import torch
from torch import nn
from torchvision import models
import torchvision
import torchvision.transforms as transforms
import torch.utils.data as Data
import torch.optim as optim
import torch.nn.functional as F

device = torch.device("cuda:0" if torch.cuda.is_available() else"cpu")  
#调用cuda,两种方式,一种是张量.cuda(),一种是张量.to(device)
transform = transforms.Compose(      #数据预处理,张量化与归一化,在torchvision.datasets时调用
   [transforms.ToTensor(),
    transforms.Normalize((0.5,0.5,0.5),(0.5,0.5,0.5))]
)

#下载与载入torchvision中提供的cifar10数据集,root为数据集下载路径,可以使用相对路径也可以使用绝对路径
dataset = torchvision.datasets.CIFAR10(root='/home/ericzhao/下载',train=True,transform= transform,target_transform=None,download=True)
trainloader =  Data.DataLoader(dataset,batch_size=4,shuffle=False,num_workers=2)

#训练集的载入,batch_size对训练效果有影响,后面再讨论
testset = torchvision.datasets.CIFAR10(root = '/home/ericzhao/下载',train=False,transform = transform,download=True)
testloader = Data.DataLoader(testset,batch_size = 4,shuffle=False,num_workers=2 )

#因为cifar10是一个十分类数据集,所以定义它的十个类别
classes = ('plane','car', 'bird', 'cat',
'deer', 'dog', 'frog', 'horse', 'ship', 'truck')

#自定义一个网络,继承父类nn.Module
class Net(nn.Module):
   def __init__(self):
       super(Net, self).__init__()    #继承父类的构造函数
       self.conv1 = nn.Conv2d(3, 6, 3)  #由于输入是一个32*32*3的图像,所以通道数为3。卷积核数为6,卷积核大小我改成了3*3
#经过第一个卷积,此时输入为6*30*30
       self.pool = nn.MaxPool2d(2, 2)  #池化层,下采样,步长为2,2*2进行采样,所以此时输出为6*15*15
       self.conv2 = nn.Conv2d(6, 16, 3) #卷积层,输出为16*13*13
        

       self.fc1 = nn.Linear(16* 6 * 6, 120)   #全连接层,输入为16*6*6,输出为120
       self.fc2 = nn.Linear(120, 84)      #全连接层,输入120,输出84
       self.fc3 = nn.Linear(84, 10)     #全连接层,因为是10分类,所以最后输出10

   def forward(self, x):       #定义前向函数,就是层与层之间的连接关系
       x = self.pool(F.relu(self.conv1(x)))   # -->卷积-->激活-->池化,x=15*15*6
       x = self.pool(F.relu(self.conv2(x)))   #-->卷积-->激活-->池化,x=  6*6*16 ,下采样,13/2=6.5,取6
       x = x.view(-1, 16* 6* 6)     #将输入拉成16*6*6列,因此为1行的二维张量
       x = F.relu(self.fc1(x))        #全连接层1+激活
       x = F.relu(self.fc2(x))       #全连接层2+激活
       x = self.fc3(x)                    #全连接层3
       return x                          #返回输出

resnet = Net()   #调用自定义Net类
resnet.cuda()   #调用cuda
criterion = nn.CrossEntropyLoss()  #调用loss函数
optimizer = optim.SGD(resnet.parameters(),lr = 0.003,momentum=0.9)  #调用优化器

#训练部分
for epoch in range(5):

   running_loss = 0.0   #先给loss置0
   for i,data in enumerate(trainloader,0):     #trainloader的数据下标给i,数据赋给data

       inputs,labels = data   #cifar10的数据集带有标签,所以数据给inputs,标签值给labels,由于batch_size=4,所以inputs 的torch.size =([4,3,32,32])

       inputs = inputs.cuda()
       labels = labels.cuda()   #调用GPU
       optimizer.zero_grad()  #优化器参数清0

       outputs = resnet(inputs)  #将图像数据输入resnet网络中,得到输出,由上面得知,此时outputs维度为[4,10]
       loss = criterion(outputs,labels)  #将输出和标签值输入loss计算函数,计算loss
      # 关于nn.CrossEntropyLoss()计算公式:https://blog.csdn.net/geter_CS/article/details/84857220
       loss.backward()                #反向传播
       optimizer.step()                #参数优化

       running_loss += loss.item()
       if i%200 ==199:                       #每200次输出一下平均loss
           print('[%d,%5d] loss: %.3f'%
                     (epoch + 1,i +1,running_loss/200))
           running_loss = 0.0  
print('Finished Training')


#测试部分
correct = 0
total = 0
with torch.no_grad():   #torch.no_grad() 是一个上下文管理器,被该语句 wrap 起来的部分将不会track 梯度。
   for data in testloader:             #将测试数据集的图像和标签分别赋值给imgaes和labels
       images, labels = data
       images,labels = images.cuda(),labels.cuda()
       outputs = resnet(images)     #outputs的size,[4,16*6*6]



       _, predicted = torch.max(outputs.data, 1)    #把输出张量里的每一行的最大值筛出来,赋给predicted
       total += labels.size(0)               #计算输入的总数量

       correct += (predicted == labels).sum().item()   #判断相等,相等求和

print('Accuracy of the network on the 10000 test images: %d %%' % (
           100 * correct / total))

接下来说说踩的坑
一开始,为了图省事,我用的是 torchivision.models.resnet50,就是预加载模型。代码很简单:

import torchvision
net = torchvision.models.resnet50(pretained = true)

但是在加载了预训练模型后,我本以为的极好的训练效果并没有出现,与之相反,收敛速度很慢,训练速度也很慢,2个epoch结束,loss=2,acc只有20不到。于是我又换回了官方例程里的简单两层卷积网络,跑了两个epoch,acc达到54%。
我一开始的理解,resnet模型过深,cifar10的输入图像分辨率只有32*32,在几次maxpool采样后,跑到后面,一个像素点可能都没有了,所以效果不好。
但是我在后来调整了batch_size为128后,收敛速度变快,训练速度变快,采用resenet50网络预训练模型后,2个epoch,acc到了76,而官方例程里的代码,由于只有2个卷积层深度,所以batch_size较小,收敛速度与效果也不错。
结论:一般来说,在合理的范围之内,越大的 batch size 使下降方向越准确,震荡越小;batch size 如果过大,则可能会出现局部最优的情况。小的 bath size 引入的随机性更大,难以达到收敛,极少数情况下可能会效果变好。
目前,用resenet50,优化器采用SGD,调整学习率为0.002,在官方例程的代码基础下,20个epoch,最好ACC能够达到85%。这次主要目的为学习,不追求高acc,所以就到这里了。

参考文章:
《pytorch损失函数之nn.CrossEntropyLoss()、nn.NLLLoss()》:
https://blog.csdn.net/geter_CS/article/details/84857220
《深度学习中Batch size对训练效果的影响》:https://blog.csdn.net/juronghui/article/details/78612653

  • 1
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值