1. 程式人生 > >pytorch入門——邊學邊練05卷積神經網路

pytorch入門——邊學邊練05卷積神經網路

訪問本站觀看效果更佳

寫在前面

前面講了一堆堆基礎的東西,現在我們再來看看複雜一點的知識吧。後續會再講講RNNResnet等等。慢慢來吧~後面會慢慢複雜起來。後面的文章結構大體上分為兩大塊:介紹網路結構、介紹如何用pytorch實現。限於篇幅,可能會有一些知識點分散在前面的文章裡,如果有問題可以看看找找前面的文章。本節原始碼地址convolutional_neural_network

卷積神經網路的基礎概念

卷積神經網路(Convolutional Neural Network, CNN)是一種前饋神經網路,它的人工神經元可以響應一部分覆蓋範圍內的周圍單元,對於大型影象處理有出色表現。
卷積神經網路由一個或多個卷積層和頂端的全連線層(對應經典的神經網路)組成,同時也包括關聯權重和池化層(pooling layer)。這一結構使得卷積神經網路能夠利用輸入資料的二維結構。與其他深度學習結構相比,卷積神經網路在影象和語音識別方面能夠給出更好的結果。這一模型也可以使用反向傳播演算法進行訓練。相比較其他深度、前饋神經網路,卷積神經網路需要考量的引數更少,使之成為一種頗具吸引力的深度學習結構。關於卷積的介紹網上資料非常多,這裡不專門介紹了。我們重點看實現。

卷積神經網路的結構

我們看看上面的描述:卷積層以及全連線層還有池化層。
pytorch裡全連線層,我們之前已經遇到過很多次了,簡單的說就是利用一個nn.Linear
再說一下卷積層,我們直接來看看pytorch裡是怎麼來構建一個卷積層的。github上原始碼conv.py。程式碼很少,裡面有很多程式碼還是類似的,比如Conv1d Conv2d Conv3d其實差不多,挑一個Conv2d說一下。

Conv2d繼承於_ConvNd,我們先一起看看要怎麼定義一個卷積的基本操作。首先是設計需要的引數,網路的輸入和輸出維度是要有的,這個是基本操作。想一想線性的情況下還有bias,卷積層應該也要有。那麼卷積操作特有的引數大概有哪些呢?然後卷積核的大小也要有,stride

控制步幅。padding控制填充量也要有。基本的要素就是上述內容。還有其它的一些引數我們來看看原始碼吧!

class _ConvNd(Module):

    def __init__(self, in_channels, out_channels, kernel_size, stride,
                 padding, dilation, transposed, output_padding, groups, bias):
        super(_ConvNd, self).__init__()
        if in_channels % groups != 0:
            raise ValueError('in_channels must be divisible by groups')
        if out_channels % groups != 0:
            raise ValueError('out_channels must be divisible by groups')
        self.in_channels = in_channels
        self.out_channels = out_channels
        self.kernel_size = kernel_size
        self.stride = stride
        self.padding = padding
        self.dilation = dilation
        self.transposed = transposed
        self.output_padding = output_padding
        self.groups = groups
        if transposed:
            self.weight = Parameter(torch.Tensor(
                in_channels, out_channels // groups, *kernel_size))
        else:
            self.weight = Parameter(torch.Tensor(
                out_channels, in_channels // groups, *kernel_size))
        if bias:
            self.bias = Parameter(torch.Tensor(out_channels))
        else:
            self.register_parameter('bias', None)
        self.reset_parameters()

dilation可以控制核點之間的間距,有點像在矩陣上撒芝麻。group控制輸入和輸出間的連線。當group=1時所有輸入都卷積到所有輸出,當group=2時操作變得等同於並排具有兩個轉換層,每個轉換輸入通道的一半,並且產生輸出通道的一半,並且隨後連線。當group= in_channels每個輸入通道都使用自己的一組過濾器進行卷積。其它的引數我們用到了再詳細說。
那麼Conv2d的數學公式又是什麼呢?我們需要形式化的表述一下它。

screenshot from 2018-08-27 11-35-56

$ \star $代表互相關函式。現在大部分的深度學習教程中都把卷積定義為影象矩陣和卷積核的按位點乘。實際上,這種操作亦應該是互相關(cross-correlation),而卷積需要把卷積核順時針旋轉180度然後再做點乘。N是 batch size, C 代表 a number of channels,H 代表輸入畫素高度,W 代表輸入畫素寬度。

class Conv2d(_ConvNd):
    def __init__(self, in_channels, out_channels, kernel_size, stride=1,
                 padding=0, dilation=1, groups=1, bias=True):
        kernel_size = _pair(kernel_size)
        stride = _pair(stride)
        padding = _pair(padding)
        dilation = _pair(dilation)
        super(Conv2d, self).__init__(
            in_channels, out_channels, kernel_size, stride, padding, dilation,
            False, _pair(0), groups, bias)

    def forward(self, input):
        return F.conv2d(input, self.weight, self.bias, self.stride,
        self.padding, self.dilation, self.groups)

是不是真的真的非常簡單。
再說說池化層,我們這裡只關注它的形式化表示。其實網上一搜概念很容易理解。
screenshot from 2018-08-27 11-31-34

具體實現

下面我們來實現卷積神經網路。直接上程式碼吧!

# Convolutional neural network (two convolutional layers)
class ConvNet(nn.Module):
    def __init__(self, num_classes=10):
        super(ConvNet, self).__init__()
        self.layer1 = nn.Sequential(
            nn.Conv2d(1, 16, kernel_size=5, stride=1, padding=2),
            nn.BatchNorm2d(16),
            nn.ReLU(),
            nn.MaxPool2d(kernel_size=2, stride=2))
        self.layer2 = nn.Sequential(
            nn.Conv2d(16, 32, kernel_size=5, stride=1, padding=2),
            nn.BatchNorm2d(32),
            nn.ReLU(),
            nn.MaxPool2d(kernel_size=2, stride=2))
        self.fc = nn.Linear(7*7*32, num_classes)
        
    def forward(self, x):
        out = self.layer1(x)
        out = self.layer2(out)
        out = out.reshape(out.size(0), -1)
        out = self.fc(out)
        return out

這裡採用了一個串型網路結構,這種寫法幾乎和Keras一樣。這裡定義了layer1``layer2我們需要注意的就是輸入引數和輸出引數的大小。注意一下區別,我們之前定義線性nn.Linear時是輸入維度(對mnist就是784),而此處是輸入in_channels,也就是通道數。mnist的通道數為1BatchNorm可以理解為一種加快收斂的手段,詳細的資訊請參見Batch Normalization: Accelerating Deep Network Training by Reducing Internal Covariate Shift,這裡不多講了。
損失函式和優化器如下:

# Loss and optimizer
criterion = nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate)

小結

下面我們再梳理一遍程式主體。

  • 設定device,用DataLoaer載入資料
  • 定義網路結構,設定各種引數
  • 訓練模型
  • 測試模型
  • 儲存模型
    基本上按照這個步驟,我們就能搭建起一個簡單的模型。後面我們再來試試更為複雜的情況吧!