手把手教你寫一個用pytorch實現的Lenet5


最近為了實現HR-net在學習pytorch,然后突然發現這個框架簡直比tensorflow要方便太多太多啊,我本來其實不太喜歡python,但是這個框架使用的流暢性真的讓我非常的喜歡,下面我就開始介紹從0開始編寫一個Lenet並用它來訓練cifar10。

1.首先需要先找到Lenet的結構圖再考慮怎么去實現它,在網上找了一個供參考

2.需要下載好cifar-10的數據集,在pytorch下默認的是下載cifar-10-python版本的,由於官網速度較慢,我直接提供度娘網盤的鏈接:鏈接:https://pan.baidu.com/s/18LNEZmGVkzEwf3SgOrO2rw  密碼:n1h7

3.下載好數據集后,需要定義網絡的結構,根據圖我們可以看出,整個lenet只有兩個卷積層,兩個池化層(其實應該叫降采樣層,那個時候還沒有池化),三個全連接層。

pytorch中有一個容器,叫做Sequential,你可以在這個容器里添加你需要使用的卷積,池化,全連接操作,但是,這個Sequential它只能包含類方法定義的層,而不能包含像torch.Functional里面的函數方法(可能我說的不專業,見諒),所以如果當你想自己定義某個層的話,例如在輸入全連接層之前,需要將形如[batch_size,channel,higth,width]的tensor轉化成[batch_size,channel*higth*width]這種形式,那我如果想在Sequential這個容器里加入這一個操作該怎么辦呢,這時候就需要我們繼承nn.Module這個類來實現,具體的方法如下

import torch
import torch.nn as nn
class Flatten(nn.Module):
    def __init__(self):
        super(Flatten, self).__init__()
    def forward(self,input):
        out=input.view(input.size(0),-1)
        return out

好了,介紹完Sequential我們就開始實現這個網絡的結構吧

#文件名是Lenet5.py
import torch
import torch.nn as nn
from pytorch__lesson.pytorch_mnist.main import Flatten
class Lenet(nn.Module):
    def __init__(self):
        super(Lenet, self).__init__()
        self.net=nn.Sequential(
            nn.Conv2d(3,6,5,stride=1,padding=0),
            nn.MaxPool2d(kernel_size=2,stride=2,padding=0),
            nn.Conv2d(6,16,5,stride=1,padding=0),
            nn.MaxPool2d(kernel_size=2,stride=2,padding=0),
            Flatten(),
            nn.Linear(400,120),
            nn.ReLU(inplace=True),
            nn.Linear(120,84),
            nn.ReLU(inplace=True),
            nn.Linear(84,10),
            nn.ReLU(inplace=True)
        )
        # self.criteon=nn.CrossEntropyLoss()
    def forward(self,x):
        logits= self.net(x)
        # pred=nn.Softmax(logits,dim=1),這一行不需要寫,因為在CrossEntropyLoss這一步包含了softmax的操作
        return logits
# net=Lenet()
# input=torch.randn(2,3,32,32)
# out=net(input)
# print(out.shape)

其中這里面的Flatten就是上面代碼的Flatten類。因為它繼承了nn.Module因此可以直接將其放在Sequential里面了,以后定義任何網絡,我們都可以使用這個類來進行tensor的展平操作。

4.接下來就可以定義訓練部分的代碼了

import torch
from torchvision import datasets,transforms
from torch.utils.data import DataLoader
import torch.nn as nn
import torch.optim as optim
import torch.functional as F
from pytorch__lesson.pytorch_mnist.Lenet5 import Lenet

batch_size=32
def main():
    # cifar表示的是在當前的目錄下新建一個叫cifar的文件夾,這個方法一次只能加載一張
    cifar_train=datasets.CIFAR10('cifar',train=True,transform=transforms.Compose([
        transforms.Resize((32,32)),
        transforms.ToTensor()
    ]),download=True)
    # 這個方法才能保證一次讀取進來的是一個batch_size大小的數據
    cifar_train_loader=DataLoader(cifar_train,batch_size=batch_size,shuffle=True)

    cifar_test=datasets.CIFAR10('cifar',train=False,transform=transforms.Compose([
        transforms.Resize((32,32)),
        transforms.ToTensor()
    ]))
    cifar_test_loader=DataLoader(cifar_test,batch_size=batch_size,shuffle=False)

    x,label=iter(cifar_train_loader).next()
    print('x shapex:',x.shape,'label shape:',label.shape)

    # use CrossEntropy as the loss function
    criteon=nn.CrossEntropyLoss()
    # use Lenet() function to build a model
    # net=Lenet().to(device) 將模型放入cuda上進行加速
    net=Lenet()
    optimizer=optim.Adam(net.parameters(),lr=1e-3)
    # device=torch.device('cuda')
    # net=Lenet().to(device) 將模型放入cuda上進行加速
    print(net)
    for epoch in range(1000):
        for batchidx,(x,label) in enumerate(cifar_train_loader):
            # 生成軟對數
            # 將網絡轉化成train的模式
            net.train()
            logits=net(x)
            # x,label=x.to(device),label.to(device)
            # 使用crossentropyloss的就不需要將logits放入到softmax中了,直接就可以計算出loss
            loss=criteon(logits,label)

            #接下來進行反向的傳播,先是將梯度清零,再進行反向傳播,再進行梯度更新
            optimizer.zero_grad()
            loss.backward()
            optimizer.step()
        # loss是一個tensor scalor 是一個長度為0的標量
        print(epoch,loss.item())

        net.eval()
        with torch.no_grad():
            # 將整個網絡轉換成test模式或者validation模式
            # test這一部分不需要構造計算圖也不需要統計梯度,因此將這部分放在函數torch.no_grad()
            total_correct=0
            total_num=0
            for x,label in cifar_test_loader:
                #  如果有gpu的話先將x和label放入gup進行加速
                # [batch_size,10]
                logits=net(x)
                # 取出最大下標的索引[b]
                pred=logits.argmax(dim=1)
                # eq函數調用后會返回一個byte,true或者false估計,然后需要將其轉換成float類型再通過item()函數來提取它的值
                total_correct+=torch.eq(label,pred).float().sum()
                total_num+=x.size(0)
            acc=total_correct/total_num
            print(epoch,'the acc of the test is :',(acc*100))

if __name__=='__main__':
    main()

因為我的電腦沒有英偉達的顯卡,不支持cuda加速,因此的話沒辦法都訓練出來截圖,如果有N卡的,可以自己試試,注釋寫的比較詳細,我就不再贅述了,不是很難。

ps:我太嘮叨了吧😂


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM