Pytorch學習筆記(一)搭建一個CNN神經網絡

本文根據pytorch官方教程編寫,如果有興趣的人可以直接去查看pytorch的官方文檔。
那麼現在就直接開始吧!
第一步就是直接導入包

import torch.nn as nn
import torch.nn.functional as F
import torch

然後嘛!就是通過繼承nn.Module來編寫模型類。

class Net(nn.Module):
    def __init__(self):
        super(Net,self).__init__()#繼承nn.Module中的代碼
        self.conv1=nn.Conv2d(1,6,5)#輸入通道數爲1,輸出通道數爲6,卷積核大小爲(5,5),步長默認爲1
        self.conv2=nn.Conv2d(6,16,5)
        self.Pool=nn.MaxPool2d(2,2)#最大池化層 2*2
        #開始構建線性層
        self.fc1=nn.Linear(16*5*5,120)#輸入層大小爲400,隱含層大小爲120
        self.fc2=nn.Linear(120,84)#隱含層1大小爲120,隱含層2大小爲84
        self.fc3=nn.Linear(84,10)#隱含層2大小爲84,輸出層大小爲10
    def forward(self,x):
        x=self.Pool(F.relu(self.conv1(x)))#進行池化操作,F.relu()是激活函數
        x=self.Pool(F.relu(self.conv2(x)))
        x=x.view(-1,self.num_flat_features(x))#通過view函數將張量x變成 16*5*5的一維向量

        x=F.relu(self.fc1(x))#在第一個全連接層經過激活函數relu,之後在傳到後面去
        x=F.relu(self.fc2(x))
        x=self.fc3(x)#最後一層,因此不用激活函數激活了
        return  x
    def num_flat_features(self,x):
        size=x.size()[1:]#獲取第一層全連接層的大小,輸出爲torch.Size([16, 5, 5])

        num_features=1
        for s in size:
            num_features*=s
        return num_features

現在開始精彩一些的地方吧!
比如輸出一下我們的模型,看看他的內部機構

net=Net()#實例化一個模型
print(net)

如果你的代碼沒有寫錯的話,你就會得到下面的這些輸出。
如果你能結合我們上面的代碼觀看的話,你就會發現,這些東西就是我們所定義的卷積層,池化層以及全連接層。

Net(
  (conv1): Conv2d(1, 6, kernel_size=(5, 5), stride=(1, 1))
  (conv2): Conv2d(6, 16, kernel_size=(5, 5), stride=(1, 1))
  (Pool): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
  (fc1): Linear(in_features=400, out_features=120, bias=True)
  (fc2): Linear(in_features=120, out_features=84, bias=True)
  (fc3): Linear(in_features=84, out_features=10, bias=True)
)

在這裏插入圖片描述
大致形狀就是這樣了,後面的這些線我就不連了,懶得連。還有就是別忘了他們還有卷積層和池化層。

如果我們來觀察一下模型的參數吧!

params=list(net.parameters())#所謂參數就是對這些卷積層,全連接層的一個縮寫。如params[0]就是conv1 (6,1,5,5),params[1]就是conv1的輸出量爲6
print(len(params))
print(params[0].size())#建議把這從0-9幾個參數都看一下,也許你會對這種模型的構建有一個更加清晰的認知

輸出如下:

10
torch.Size([6, 1, 5, 5])

再來,我們來看這個編寫出來的模型,會有什麼樣的輸出呢!

input =torch.randn(1,1,32,32)#隨機創建一個輸入張量
out=net(input)#先試試將其放入模型得到的結果
print(out)

輸出如下:

tensor([[ 0.0013, -0.0339, -0.0306,  0.0887, -0.0689, -0.0216, -0.0481, -0.0103,
         -0.0592, -0.0732]], grad_fn=<AddmmBackward>)

由此我們可以看到,他的輸出結果正好就是(1,10)的張量,也就是我們的最後一層全連接層的輸出。因爲最後的這一層是一共只有十個神經元,所以輸出時自然會是10個輸出。
如果你的項目是做的01標籤輸出的話,記得直接把輸出層設置成2個神經元喲!

接着我們開始反向傳播,並計算其損失值吧!

net.zero_grad()#神經網絡中的梯度緩衝區全部清零,否則就會與已經有的梯度混到一起
out.backward(torch.randn(1,10))#開始使用隨機梯度開始反向傳播,反向傳播前conv1的偏置項爲空或者說全零

output=net(input)#再次獲取其輸出
target=torch.randn(10)#確定一個目標值,size要爲(1,10)
target=target.view(1,-1)
criterion=nn.MSELoss()#定義損失函數
loss=criterion(output,target)#通過輸出結果與目標值來計算損失函數,或者說是實際網絡輸出值與預期值之前的差距
print(loss)

輸出結果如下:

tensor(0.6017, grad_fn=<MseLossBackward>)

看到他的grad_fn屬性了嗎?
他們的grad_fn屬性已經變成了我們定義的MSELoss()了。

上面的代碼是不合格的。它只是給了一個目標,然後就直接計算其損失值。
我們的模型都是需要訓練的,因此它應該擁有一個優化迭代器。
對此,torch給了我們一個包 optim。
我們經過會用梯度下降的方法來進行迭代優化:
其公式如下:

weight = weight - learning_rate * gradient

當然,這並不是我們這裏要使用的優化算法。
寫出這個只是爲了讓你稍微對這個優化算法,有一些簡單的認知,就是我們學習線性神經網絡時候的梯度下降算法,只是這裏改成了其他更加優秀的算法罷了。
我們現在使用這個包來定義一個隨機梯度下降算法的優化器吧!
SGD(Stochastic gradient descent)

import torch.optim as optim
optimizer=optim.SGD(net.parameters(),lr=0.01)#定義SGD優化器,傳入模型參數和學習率
optimizer.zero_grad()
output=net(input)
loss=criterion(output,target)
loss.backward()#根據誤差進行反向傳播
optimizer.step()
print(loss)

輸出如下:

tensor(0.6186, grad_fn=<MseLossBackward>)

我們這樣當然是看不出什麼東西的,但是我們能夠看到這個loss的值,進行了一些改變,當然我們的學習率定得太小了。所以並不太明顯。

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