詳解 MNIST 入門 Pytorch

MNIST雖然很簡單,但是值得我們學習的東西還是有很多的。

項目雖然簡單,但是個人建議還是將各個模塊分開創建,特別是對於新人而言,模塊化的創建更加清晰、易懂,我們看下項目都包含哪些部分:

  • CNN模塊:卷積神經網絡的組成;
  • train模塊:利用CNN模型 對 MNIST數據集 進行訓練並保存模型
  • test模塊:加載訓練好的模型對測試集數據進行測試
  • cnn.pt : train 的CNN模型

注意!
有GPU的小夥伴儘量使用GPU訓練,GPU的訓練速度比CPU的訓練速度高許多倍,可以節約大量訓練時間

在這裏插入圖片描述


CNN 模塊

MNIST的識別算法有很多,在此提供的是 卷積神經網絡CNN ,其他算法也同樣可以取得很好的識別效果,有興趣的小夥伴可以自己嘗試下。

在此就不得不提 Pytorch的優勢了,都知道 Pytorch 是動態計算模型。但是何爲動態計算模型呢?

  • 在此對比 Tensorflow。在流行的神經網絡架構中, Tensorflow 就是最典型的靜態計算架構。使用 Tensorflow 就必須先搭建好這樣一個計算系統, 一旦搭建好了, 就不能改動了 (也有例外), 所有的計算都會在這種圖中流動, 當然很多情況下這樣就夠了, 我們不需要改動什麼結構。
  • 不動結構當然可以提高效率. 但是一旦計算流程不是靜態的, 計算圖要變動. 最典型的例子就是 RNN, 有時候 RNN 的 time step 不會一樣, 或者在 training 和 testing 的時候, batch_size 和 time_step 也不一樣, 這時, Tensorflow 就頭疼了。
  • 如果用一個動態計算圖的 Pytorch, 我們就好理解多了, 寫起來也簡單多了. PyTorch 支持在運行過程中根據運行參數動態改變應用模型。可以簡單理解爲:一種是先定義後使用,另一種是邊使用邊定義。動態計算圖模式是 PyTorch 的天然優勢之一,Google 2019年 3 月份發佈的 TensorFlow 2.0 Alpha 版本中的 Eager Execution,被認爲是在動態計算圖模式上追趕 PyTorch 的舉措。

如果暫時看不懂的小夥伴,可以先不管,先往後學習,等將來需要的時候再回頭思考這段話。

CNN 模塊分析

CNN 模塊主要分爲兩個部分,一個是定義CNN模塊,另一個是將各個模塊組成前向傳播通道

  • super() 函數 是用於調用父類(超類)的一個方法。
    用來解決多重繼承問題的,直接用類名調用父類方法在使用單繼承的時候沒問題,但是如果使用多繼承,會涉及到查找順序(MRO)、重複調用(鑽石繼承)等種種問題。
    super(SimpleCNN, self) 首先找到 SimpleCNN 的父類(就是類 nn.Module ),然後把類 SimpleCNN 的對象轉換爲類 nn.Module 的對象

  • nn.Sequential() 是一個有順序的容器,將 神經網絡模塊 按照傳入構造器的順序依次被添加到計算圖中執行。由於每一個神經網絡模塊都繼承於nn.Module,通過索引的方式利用add_module函數將 nn.Sequential()模塊 添加到現有模塊中。

  • forward() 是前向傳播函數,將之前定義好的每層神經網絡模塊串聯起來,同時也定義了模型的輸入參數

#  !/usr/bin/env  python
#  -*- coding:utf-8 -*-
# @Time   :  2020.
# @Author :  綠色羽毛
# @Email  :  [email protected]
# @Blog   :  https://blog.csdn.net/ViatorSun
# @Note   :  


from torch import nn

class SimpleCNN(nn.Module):
    def __init__(self):
        super(SimpleCNN, self).__init__()
        self.layer1 = nn.Sequential( 
        						nn.Conv2d(1,16,kernel_size=3) ,
                                nn.BatchNorm2d(16) ,
                                nn.ReLU(inplace=True))

        self.layer2 = nn.Sequential( 
        						nn.Conv2d(16,32,kernel_size=3) ,
                                nn.BatchNorm2d(32) ,
                                nn.ReLU(inplace=True) ,
                                nn.MaxPool2d(kernel_size=2 , stride=2))

        self.layer3 = nn.Sequential( 
        						nn.Conv2d(32,64,kernel_size=3) ,
                                nn.BatchNorm2d(64) ,
                                nn.ReLU(inplace=True))

        self.layer4 = nn.Sequential( 
        						nn.Conv2d(64,128,kernel_size=3) ,
                                nn.BatchNorm2d(128) ,
                                nn.ReLU(inplace=True) ,
                                nn.MaxPool2d(kernel_size=2 , stride=2))

        self.fc = nn.Sequential(nn.Linear(128*4*4,1024) ,
                                nn.ReLU(inplace=True) ,
                                nn.Linear(1024,128) ,
                                nn.ReLU(inplace=True) ,
                                nn.Linear(128,10) )
    def forward( self , x):
        x = self.layer1(x)
        x = self.layer2(x)
        x = self.layer3(x)
        x = self.layer4(x)
        x = x.view(x.size(0) , -1)
        fc_out = self.fc(x)
        return fc_out


train 模塊

CNN 模塊分析
#  !/usr/bin/env  python
#  -*- coding:utf-8 -*-
# @Time   :  2020.
# @Author :  綠色羽毛
# @Email  :  [email protected]
# @Blog   :  https://blog.csdn.net/ViatorSun
# @Note   :  


import torch
import CNN
from torch import nn , optim
from torchvision import datasets
from torchvision import transforms
from torch.autograd import Variable
from torch.utils.data import DataLoader


# 定義超參數
learning_rate = 1e-2      # 學習率
batch_size    = 128       # 批的大小
epoches_num   = 20        # 遍歷訓練集的次數


# 下載訓練集 MNIST 手寫數字訓練集
train_dataset = datasets.MNIST( root='./data', train=True, transform=transforms.ToTensor(), download=True )
train_loader  = DataLoader( train_dataset, batch_size=batch_size, shuffle=True )


# 定義model 、loss 、optimizer
model = CNN.SimpleCNN()
criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD( model.parameters(), lr=learning_rate )


if torch.cuda.is_available():
    print("CUDA is enable!")
    model = model.cuda()


# 開始訓練
for epoch in range(epoches_num):
    print('*' * 40)
    running_loss = 0.0
    running_acc  = 0.0

    # 訓練
    for i, data in enumerate(train_loader, 1 ):
        img, label = data

        if torch.cuda.is_available():
            img = Variable(img).cuda()
            label = Variable(label).cuda()
        else:
            img = Variable(img)
            label = Variable(label)

        # 前向傳播
        out = model(img)
        loss = criterion(out, label)
        running_loss += loss.item() * label.size(0)
        _ , pred = torch.max(out, 1)
        num_correct = (pred == label).sum()
        accuracy = (pred == label).float().mean()
        running_acc += num_correct.item()

        # 反向傳播
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()

    print('Finish  {}  Loss: {:.6f}, Acc: {:.6f}'.format( epoch+1 , running_loss / (len(train_dataset)), running_acc / (len(train_dataset))))


# 保存模型
torch.save(model, 'cnn.pt')

test 模塊

#  !/usr/bin/env  python
#  -*- coding:utf-8 -*-
# @Time   :  2020.
# @Author :  綠色羽毛
# @Email  :  [email protected]
# @Blog   :  https://blog.csdn.net/ViatorSun
# @Note   :  


import torch
from torch import nn
from torchvision import datasets
from torchvision import transforms
from torch.autograd import Variable
from torch.utils.data import DataLoader

# 定義超參數
batch_size  = 128       # 批的大小

# 下載訓練集 MNIST 手寫數字測試集
test_dataset  = datasets.MNIST( root='./data', train=False, transform=transforms.ToTensor())
test_loader   = DataLoader(test_dataset , batch_size=batch_size, shuffle=False)

# 加載 Train 模型
model = torch.load('cnn.pt')
criterion = nn.CrossEntropyLoss()
model.eval()
eval_acc  = 0
eval_loss = 0


# 測試
for data in test_loader:
    img, label = data
    if torch.cuda.is_available():
        img   = Variable(img  ).cuda()
        label = Variable(label).cuda()
    else:
        img   = Variable(img  )
        label = Variable(label)

    out  = model(img)
    loss = criterion(out, label)
    eval_loss += loss.item() * label.size(0)

    _ , pred = torch.max(out,1)
    num_correct = (pred==label).sum()
    eval_acc += num_correct.item()
    print('Test Loss: {:.6f}   ,   Acc: {:.6f}'.format( eval_loss/(len(test_dataset)), eval_acc/(len(test_dataset)) ))
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章