学习pytorch(三)简单卷积神经网络训练MNIST(利用GPU计算)

简单卷积神经网络训练MNIST(利用GPU计算)

本篇不分享神经网络的知识,只分享pytorch下如何构建简单的卷积神经网络模型并让模型学习MNIST手写数字识别。学习和预测过程利用GPU计算减轻cpu的负担。在pytorch中,把计算工作交给GPU的操作很简单,操作都用python ‘’’ ‘’’ 注释标出

import torch
from torchvision import datasets #这个包包含了多个数据集,这些数据集都是torch.nn.datasets类的子类。可以供DataLoader加载。
from torch.utils.data import DataLoader #做mini_batcch时的数据读取工具。
import torchvision.transforms #从图像转化为供机器学习的tensor时用到的工具。

batch_size = 64 #minibatch 每个batch的所包含的数据样本数。
''' 转移GPU操作步骤 '''
device=torch.device('cuda:0') #电脑上有GPU,这是GPU。后面进行运算将在GPU进行。


# MNIST是若干张大小28*28单通道的手写数字图片数据集,这个数据集为torchvision自带,可以用两句代码下载到本地。
#(主函数的train_data=datasets.MNIST('MNIST',True,transformer,download=True)
#    test_data=datasets.MNIST('MNIST',True,transformer,download=True)这两句分别是加载数据集和测试集, # 后面有详细的解释)

class net(torch.nn.Module):
#构建简单卷积神经网络。把单通道的28*28的图片经过卷积,池化,激活变成20通道,4*4的张量。再把张量通过全连接降维,变成1维张量,含10个元素。
    def __init__(self):
        super(net, self).__init__()
        self.convl1=torch.nn.Conv2d(1,10,5) #第一层卷积,把1通道输入变成10通道,卷积核大小为5*5,这个大小可以设置成其他(一般为奇数),卷积
        #核大小会影响卷积输出的宽和高,但不会影响通道数。
        self.convl2=torch.nn.Conv2d(10,20,5) #第二层卷积,把10通道卷积成20通道,卷积核大小为5*5。
        self.pooling=torch.nn.MaxPool2d(2)#池化层,大小为2*2。(一般都设置为2*2)
        self.activate=torch.nn.ReLU()#激活层。
        self.linear=torch.nn.Linear(320,10)#全链接线性层,本模型经过卷积,池化后,每个样本的形状为20*4*4。供320个元素,通过全连接,让其变为10个元素。
    def forward(self,x):
        x=x.view(-1,1,28,28)#首先,把数据组织方式转化成(样本数,通道,高,宽)样本数让程序自动计算,设置为-1.计算方法为:总数据量/每个样本的数据量1*28*28.
        x=self.convl1(x)#把标准化的数据放入卷积层,输出形状为(样本数,10通道,高24,宽24)
        x=self.pooling(x)#进行池化,输出形状为(样本数,10通道,高12,宽12)
        x=self.activate(x)#非线性激活
        x=self.convl2(x)#放入第二卷积层,输出形状为(样本数,20通道,高8,宽8)
        x=self.pooling(x)#池化,结果形状为(样本数,20通道,高4,宽4)
        x=self.activate(x)#非线性激活
        x=x.view(-1,320)#把形状改变为(样本数,长度:20*4*4)
        x=self.linear(x)#全连接层结果为(样本数,长度10)
        return x

def train(train_loader,model,criterion,opitmizer,epoch):
    #训练时,每更新300次权重,就输出这300次的损失平均值。设置的bacth_size是64,每次更新是根据64个样本更新的。每输出一次,就有300*64个样本参与训练
    loss_sum=0.0 #300次更新的损失和
    for index,(x,y) in enumerate(train_loader): #train_loader是管理数据集的一个对象,他将样本随机分成若干组,每组64个。可以通过for循环
        #迭代取出每个组。通过enumerate函数还可以同时得到这个组的在train_loader的下标。
        ''' 转移GPU操作步骤 '''
        x=x.to(device) #把数据从cpu搬到GPU
        ''' 转移GPU操作步骤 '''
        y=y.to(device) #把数据从cpu搬到GPU device是程序开始时连接好的GPU对象,如果没有GPU则应删掉这句。
        y_hat=model(x) #在GPU中计算预测值
        loss=criterion(y_hat,y) #计算损失
        loss_sum+=loss.item() #把损失求和记录,记录满300次则输出
        opitmizer.zero_grad() #梯度清0
        loss.backward() #反向传播
        opitmizer.step() #用优化器更新权重

        if(index%300==299):#300次计算则输出以便观察。
            print(epoch,'.',index//300,':',loss_sum/300)
            loss_sum=0.0
def test(test_loader,model,epoch):#测试
    total=0#记录本次测试总的样本数
    correct=0#记录本次测试预测正确的样本数
    for x,y in test_loader:
    ''' 转移GPU操作步骤 '''
        x=x.to(device) #把数据搬到GPU
        ''' 转移GPU操作步骤 '''
        y=y.to(device)
        y_hat=model(x) #计算预测值
        _,guess=torch.max(y_hat,1) #torch.max函数可以沿着某个维度计算这个维度的最大值,第0个维度是样本,第1个维度是每个样本的10特征,我们要
                                    #返回值是一个 tensor。第一个是最大值的数值,第二个是引索。我们只关心引索,注意这个guess这个tensor包含了
                                    #本次运算所有样本的最大值的引索。
        correct+=(guess==y).sum().item() #guess==y将返回一个tensor。对应位置相等就是1.通过sum就可以知道参与计算的64个样本中猜对了几个。通过
                                        #item取标量。
        total+=y.size(0) #累加总样本数。
    print('test',epoch,':',correct/total*100,'%')

if __name__=='__main__':
    transformer=torchvision.transforms.Compose([torchvision.transforms.ToTensor(),torchvision.transforms.Normalize(0.1307, 0.3018)])
    #Compose这个对象,可以组合多个转化器,当有数据传进来时,他会依次调用转化器进行转化。他接受一个列表,列表的每个元素为实例化的转化器对象。
    #ToTensor对象,可以把数据转化为pytorch的tensor,无论输入来源是以什么顺序组织数据的,它都会把数据标准化为(样本,通道,高,宽)。
    #Normalize对象可以数据转化为正态分布,0.1307是均值,0.3018是标准差,这两个数据是MNIST的经验数值,数组按这个分布,学习效果最好。对于其他未必。
    train_data=datasets.MNIST('MNIST',True,transformer,download=True)
    #载入MNIST数据集,第一个参数是想要把数据集保存在哪里,第二个参数设置为True则载入训练集,false则载入测试集,第三个参数是转化器。download参数是如果在第一个参数的文件夹找不到数据集,是否要联网下载。
    test_data=datasets.MNIST('MNIST',True,transformer,download=True)
    train_loader=DataLoader(train_data,batch_size,shuffle=True,num_workers=2)
    #第一个参数是载入的数据集对象,它必须是torch.nn.datasets的子类。第二个参数顾名思义。第三个参数是是否需要打乱样本后再分组。第四个参数是进程数。
    test_loader=DataLoader(test_data,batch_size,shuffle=False,num_workers=2)
    model=net()
    ''' 转移GPU操作步骤 '''
    model.to(device) #把模型搬进GPU,模型和数据要在一起,不能模型在cpu,数据在Gpu
    criterion=torch.nn.CrossEntropyLoss() #选用交叉熵损失器。
    ''' 转移GPU操作步骤 '''
    criterion.to(device)# 损失器搬入GpU
    opitmizer=torch.optim.SGD(model.parameters(),lr=0.01,momentum=0.5) #带冲量的优化器
    for epoch in range(10):#学习10轮。
        train(train_loader,model,criterion,opitmizer,epoch)
        test(test_loader,model,epoch)

'''
if __name__=='__main__': #这段代码可以输出卷积层后数据的形状,以便确定全链接层的输入维度。使用这段代码前,请先删除模型forward方法里的
{
# x=x.view(-1,320) 把形状改变为(样本数,长度:20*4*4)
# x=self.linear(x) 全连接层结果为(样本数,长度10)  
}
#这两句。
    x=torch.Tensor(28,28)
    model=net()
    print(model(x).size())
'''

猜你喜欢

转载自blog.csdn.net/ld_long/article/details/113757711
今日推荐