多注释:用PyTorch实现卷积神经网络对MNIST手写数字数据集的分类

2020-12-28 11:32:49 浏览数 (1)

参考链接: 卷积神经网络在mnist数据集上的应用 Python

本文将为尽可能多的代码作注释,用PyTorch实现对手写数字数据集MNIST的分类,我也是一个PyTorch的初学者,如果你也是一个刚学pytorch没多久的朋友,希望我的注释能够让您尽可能看明白。因个人水平有限,如有什么写错的地方,敬请指正。 

代码如下: 

import torch     

from torch import nn,optim

from torch.autograd import Variable

from torch.utils.data import DataLoader

from torchvision import datasets,transforms

############################# 先定义网络结构 ################################

class Net(nn.Module):      #如果不太熟悉python中 类 的一些使用,欢迎看我的另一篇讲解Python中类的基本使用方法的文章

    def __init__(self):

        super(Net,self).__init__()

        self.conv=nn.Sequential(     #输入的数据集里的图像大小为28行*28列*1通道

            nn.Conv2d(1,64,kernel_size=3,padding=1), #卷积后,大小变为28*28*64

            nn.BatchNorm2d(64),   #卷积完之后来一下batch normalization,加快收敛

            nn.ReLU(),   #加入ReLU作为激活函数

            nn.Conv2d(64,128,kernel_size=3,padding=1),  #卷积后张量大小变为28*28*128

            nn.BatchNorm2d(128),

            nn.ReLU(),

            nn.MaxPool2d(stride=2,kernel_size=2))  #pooling后变为 14*14*128

        self.dense=nn.Sequential(

            nn.Linear(14*14*128,1024),   #经过一层全连接层,原张量大小变为1024

            nn.ReLU(),

            nn.Dropout(p=0.5),    #加入dropout,防止过拟合,增强泛化能力  

            nn.Linear(1024,10))      #在经过一层全连接层,原张量大小变为10

    def forward(self,x):

            x=self.conv(x)

            x=x.view(-1,14*14*128)    #在从卷积层进入全连接层时,一定要有这个操作,view()是按行优先再一行一行重排

            x=self.dense(x)

            return x

#########################  定义超参数  ##########################################

batch_size=200   #定义每次对多少个样本样本求梯度来负梯度下降损失函数

num_epochs=3      #我没有GPU,跑这个特别慢,我就跑3个epoch意思一下,大概需要两个小时左右,做好心理准备

########################  数据读取   #######################################

#定义数据预处理方式

data_tf=transforms.Compose(   #Compose()把各种预处理操作组合到一起

    [transforms.ToTensor(),   #ToTensor()把图片转换成张量形式Tensor,在转化的过程中自动把图片标准化了,也就是说Tensor的范围是0~1,

     transforms.Normalize([0.5],[0.5])])  #传入的参数分别为均值,方差,其实现的操作是减去均值再除以方差,这样,图片中每个像素的值就转换到了-1~1之间

#读取数据,初次下载需要等待一小会

train_dataset = datasets.MNIST(root='./data',train=True,transform=data_tf,download=True)   #训练集

test_dataset=datasets.MNIST(root='./data',train=False,transform=data_tf,download=True) #测试集

train_loader=DataLoader(train_dataset,batch_size=batch_size,shuffle=True) #数据迭代器

test_loader=DataLoader(test_dataset,batch_size=batch_size,shuffle=False)

################## 导入网络并设计损失函数和优化方法 ####################################################

model=Net()  

if torch.cuda.is_available():  #判断是否有显卡

    model=model.cuda()

criterion=nn.CrossEntropyLoss()   #使用交叉熵函数作为损失函数

optimizer=optim.Adam(model.parameters())  #使用Adam优化方法,自动调整学习速率参数

###############################训练模型####################################################

correct=0

total=0

for epoch in range(num_epochs):

    for i, data in enumerate(train_loader): #enumeirate()的用法就像是一一列举,他得到的是 数据对应的编号 i, 和 数据data

        img,label=data

        if torch.cuda.is_available():

            img=Variable(img).cuda()

            label=Variable(label).cuda()

        else:

            img=Variable(img)

            label=Variable(label)

        #forward 

        out=model(img)   #得到输出

        loss=criterion(out,label)  #输出和真实的值之间求损失函数(偏差了多少)

        #backward

        optimizer.zero_grad() 

        loss.backward()

        optimizer.step()

        predicted=torch.argmax(out,1)  

        total =label.size(0)  #一个数据他有一个大小,就是这个size,通过比较输出predicted和 标签label 的每一个对应位置相比较,看有多少个位置是对的,这样预测对了的位置和总的所有位置total相除,就得到了准确率

        correct =(predicted==label).sum().item()  #(p==l).sum().item()就是看predicted和label有多少个对应位置相等,并把相等的位置个数加到correct中,如果你还不理解,试试去运行下下面这段注释里的代码

#import torch

#import numpy as np 

#data1 = np.array([

#    [1,2,3],

#    [2,20,4]])

#data1_torch = torch.from_numpy(data1)

#data2 = np.array([

#    [2,29,3],

#    [2,3,4]])    #试试不断改变data1 和data2 中的值,看他们对应有几个数字相等,再观察输出

#data2_torch = torch.from_numpy(data2)

#p = (data1_torch == data2_torch) #对比后相同的值会为1,不同则会为0

#print (p)

#d1 = p.sum() #将所有的值相加,得到的仍是tensor类别的int值

#print (d1)

#d2 = d1.item() #转成python数字

#print (d2)    

        if (i 1)0==0:

            print('Epoch[{}/{}],batch:{},loss:{:.6f},train_acc:{:.6f}'.format(epoch 1,num_epochs,i 1,loss.item(),correct/total))

##########################   测试 ########################################################

correct=0

total=0

for i,data in enumerate(test_loader):

    img,label=data

    preds=model(img)

    predicted=torch.argmax(preds,1)

    total =label.size(0)

    correct =(predicted==label).sum().item()  

accuracy=correct/total

print('测试数据准确率:{}'.format(accuracy))

运行结果如下: 

Epoch[1/3],batch:100,loss:0.100244,train_acc:0.866000 Epoch[1/3],batch:200,loss:0.075039,train_acc:0.914100 Epoch[1/3],batch:300,loss:0.077120,train_acc:0.932650 Epoch[2/3],batch:100,loss:0.085433,train_acc:0.943863 Epoch[2/3],batch:200,loss:0.052274,train_acc:0.950950 Epoch[2/3],batch:300,loss:0.054377,train_acc:0.955475 Epoch[3/3],batch:100,loss:0.063429,train_acc:0.959350 Epoch[3/3],batch:200,loss:0.063174,train_acc:0.962219 Epoch[3/3],batch:300,loss:0.127647,train_acc:0.964394 测试数据准确率:0.9796

0 人点赞