Pytorch进阶(3)------手写数字识别之深度神经网络实现

        接续之前的工作,搭建了一个深度网络并利用它进行手写数字识别,可以说取得了较好的效果,当然,网络的参数是根据一本书来的,这本书是一个日本人写的,叫《深度学习入门:基于Python的理论与实现》,作者在书本的第八章给出了一个深度网络的模型,我在pytorch中将这个模型实现了一遍(书作者并没有用任何框架,而是用python和几个库实现的)达到了作者所说的网络能实现的精度,下面附上代码:

import torch
import torch.nn as nn
import time
from torch import optim 
from torch.autograd import variable
from torch.utils.data.dataloader import DataLoader
from torchvision import datasets,transforms

epoch=20
batch_num=100
lr_rate=0.001
sum_loss=0.0

class deepNet(nn.Module):
    def __init__(self):
        super().__init__()
        self.cov1=nn.Sequential(
                nn.Conv2d(in_channels=1,out_channels=16,kernel_size=(3,3),stride=1,padding=1),
                nn.ReLU(),
                nn.Conv2d(in_channels=16,out_channels=16,kernel_size=(3,3),stride=1,padding=1),
                nn.ReLU(),
                nn.MaxPool2d(kernel_size=2, stride=2)
         )
        self.cov2=nn.Sequential(
                nn.Conv2d(in_channels=16,out_channels=32,kernel_size=(3,3),stride=1,padding=1),
                nn.ReLU(),
                nn.Conv2d(in_channels=32,out_channels=32,kernel_size=(3,3),stride=1,padding=2),
                nn.ReLU(),
                nn.MaxPool2d(kernel_size=2, stride=2)
         )
        self.cov3=nn.Sequential(
                nn.Conv2d(in_channels=32,out_channels=64,kernel_size=(3,3),stride=1,padding=1),
                nn.ReLU(),
                nn.Conv2d(in_channels=64,out_channels=64,kernel_size=(3,3),stride=1,padding=1),
                nn.ReLU(),
                nn.MaxPool2d(kernel_size=2, stride=2)
         )
        self.layer=nn.Sequential(
                 nn.Linear(in_features=1024, out_features=50, bias=False),
                 nn.BatchNorm1d(50),
                 nn.ReLU(),
                 nn.Dropout(0.5),
                 nn.Linear(in_features=50, out_features=10, bias=False),
                 nn.Dropout(0.5),
                 nn.Softmax()
                )
        
    def forward(self,x):
        x=self.cov1(x)
        x=self.cov2(x)
        x=self.cov3(x)
        x=x.view(-1,4*4*64)
        x=self.layer(x)
        return x

data_tf = transforms.Compose([transforms.ToTensor(),
     transforms.Normalize([0.5], [0.5])])    

train_set=datasets.MNIST(root='data',train=True,transform=data_tf,download=True)
test_set=datasets.MNIST(root='data',train=False,transform=data_tf,download=True)
train_loader=DataLoader(train_set,batch_size=batch_num,shuffle=True)
test_loader=DataLoader(test_set,batch_size=batch_num,shuffle=False)

since = time.time()

net=deepNet()

if torch.cuda.is_available():
   net = net.cuda()

sum_true=0.0
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(net.parameters(), lr=lr_rate)
for e in range(epoch):
    net.train()
    for data in train_loader:
        img,label=data

        if torch.cuda.is_available():
           img = img.cuda()
           label = label.cuda()
        
        img=variable(img)
        label=variable(label)
        output=net(img)
        
        loss=criterion(output,label)
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
        sum_loss+=loss.item()
    print('loss is :{:.16f}'.format(sum_loss/len(train_set)))
    sum_loss=0.0
    sum_true=0
    
    net.eval()
    for data in test_loader:
        img,label=data
        
        if torch.cuda.is_available():
           img = img.cuda()
           label = label.cuda()
        out=net(img)
        _,predict=torch.max(out,1)
        num_true=(predict==label).sum()
        sum_true+=num_true.item()
    print('accuracy rate is :{:.4f}'.format(sum_true/len(test_set)))
    
time_elapsed = time.time() - since
print('Training complete in {:.0f}s'.format(time_elapsed) )     

网络的搭建并不是很难,但是每层的参数设置需要注意,特别是卷积层的输入和输出,这个网络大部分的卷积层的参数都是一样的,3*3大小的卷积核,填充和步长都为1(只有一个卷积层的填充为2,需要注意),这就意味着经过卷积层后图像大小不变,这个有计算公式,这里就不专门给出了,上面提到的书中都有写。池化层采用的是2*2的方块,也就是说可以将图片的宽和高变为原来的一半,batch_size和学习率我都是按那本书给出的网络设置的(那本书的配套源代码上有),在全连接层中,加入了dropout,这个操作就是随机关闭一些神经元,让它们在这轮训练中不参与计算,主要是为了防止过拟合,具体原理我也不是特别清楚,说到这里有一点特别要注意,我们只在训练中采用dropout,而在测试中不能用,因此pytorch提供了nn.eval()和nn.train(),即评估模式和训练模式,用了nn.eval()之后dropout函数就不会再起作用了,而再次用nn.train()又能再次开启训练模式,即能够使用dropout(我是每训练一次epoch就测试一次准确率的,因此nn.eval()和nn.train()我都要放在循环里,如果是等模型完全训练好了之后再测准确率,那nn.train()这句话就不需要了),总结来说就是相当于开关一样的作用吧,本来是开的状态开始训练,等要测试的时候再把它关掉,下一个epoch开始训练时又需要打开,如此循环。以下是我得到的实验结果:

Pytorch进阶(3)------手写数字识别之深度神经网络实现_第1张图片

我设置了20个epoch,最后准确率是99.3%多(之前还到过99.42%),跟书作者所得到的差不多了,说明这个网络的性能是相当好的,其实这个网络是参考VGGNet制作的,因为VGGNet的输入要224*224,而手写数字只有28*28,所以不能直接用。

到这里,手写数字识别的研究要告一段落了,接下来要开始新的篇章。

你可能感兴趣的:(Pytorch,神经网络)