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開始訓練時又需要打開,如此循環。以下是我得到的實驗結果:

我設置了20個epoch,最後準確率是99.3%多(之前還到過99.42%),跟書作者所得到的差不多了,說明這個網絡的性能是相當好的,其實這個網絡是參考VGGNet製作的,因爲VGGNet的輸入要224*224,而手寫數字只有28*28,所以不能直接用。

到這裏,手寫數字識別的研究要告一段落了,接下來要開始新的篇章。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章