0609-搭建ResNet網路

二十三歲的有德發表於2021-04-26

0609-搭建ResNet網路

pytorch完整教程目錄:https://www.cnblogs.com/nickchen121/p/14662511.html

一、ResNet 網路概述

Kaiming He 的深度殘差網路(ResNet)相比較傳統的深度深度神經網路,解決了訓練極深網路的梯度消失問題。

這裡選取 ResNet34 講解 ResNet 的網路結構,它的網路結構如下圖所示:

在上述的網路中,除了最開始的卷積池化和最後的池化全連線之外,網路中有很多結構相似的單元,這些重複單元的共同點就是有個跨層直連的 shortcut。ResNet 中將一個跨層直連的單元稱為 Residual block,它的結構如下圖所示:

0609-搭建ResNet網路

對於 Residual block,左邊部分是普通的卷積網路結構,右邊是直連,如果輸入和輸出的通道數不一致,或者它的步長不為 1,就需要有一個專門的單元將二者裝換成一致的,讓它們可以相加。

並且從上圖可以發現 Residual block 的大小也是有規律的,在最開始的 pool 之後又連續的幾個一模一樣的 Residual block 單元,這些單元的通道數一樣,在這裡我們把這幾個擁有多個 Residual block 單元的結構稱作 layer,注意這個 layer 和之前介紹的 layer 不同,這裡的 layer 是幾個層的集合。

由於 Redisual block 和 layer 出現了很多次,我們可以把它們實現為一個子 Module 或函式。在這裡我們把 Residual block 實現為一個子 Module,而讓 layer 實現為一個函式。

下面我們將盡量按照這三個規則去實現 ResNet 網路:

  • 對模型中的重複部分,實現為子 module 或用函式生成相應的 module
  • nn.Modulenn.Funcitonal 結合使用
  • 儘量使用 nn.Sequential

二、利用 torch 實現 ResNet34 網路

import torch as t
from torch import nn
from torch.nn import functional as F


class ResidualBlock(nn.Module):
    """
    實現子 module:Residual Block
    """

    def __init__(self, inchannel, outchannel, stride=1, shortcut=None):
        super(ResidualBlock, self).__init__()

        # 由於 Residual Block 分為左右兩部分,因此定義左右兩邊的 layer
        # 定義左邊
        self.left = nn.Sequential(
            # Conv2d 引數:in_channel,out_channel,kernel_size,stride,padding
            nn.Conv2d(inchannel, outchannel, 3, stride, 1, bias=False),
            nn.BatchNorm2d(outchannel),
            nn.ReLU(inplace=True),
            nn.Conv2d(outchannel, outchannel, 3, 1, 1, bias=False),
            nn.BatchNorm2d(outchannel))
        # 定義右邊
        self.right = shortcut

    def forward(self, x):
        out = self.left(x)
        residual = x if self.right is None else self.right(x)  # 檢測右邊直連的情況
        out += residual
        return F.relu(out)


class ResNet(nn.Module):
    """
    實現主 module:ResNet34
    ResNet34 包含多個 layer,每個 layer 又包含多個 residual block
    用子 module 實現 residual block,用 _make_layer 函式實現 layer
    """

    def __init__(self, num_classes=1000):
        super(ResNet, self).__init__()
        # 前幾層影像轉換
        self.pre = nn.Sequential(
            nn.Conv2d(3, 64, 7, 2, 3, bias=False),
            nn.BatchNorm2d(64),
            nn.ReLU(inplace=True),
            nn.MaxPool2d(3, 2, 1),
        )

        # 重複的 layer 分別有 3,4,6,3 個 residual block
        self.layer1 = self._make_layer(64, 128, 3)
        self.layer2 = self._make_layer(128, 256, 4, stride=2)
        self.layer3 = self._make_layer(256, 512, 6, stride=2)
        self.layer4 = self._make_layer(512, 512, 3, stride=2)

        # 分類用的全連線
        self.fc = nn.Linear(512, num_classes)

    def _make_layer(self, inchannel, outchannel, block_num, stride=1):
        """
        構建 layer,包含多個 residual block
        """
        shortcut = nn.Sequential(
            nn.Conv2d(inchannel, outchannel, 1, stride, bias=False),
            nn.BatchNorm2d(outchannel))

        layers = []
        layers.append(ResidualBlock(inchannel, outchannel, stride, shortcut))

        for i in range(1, block_num):
            layers.append(ResidualBlock(outchannel, outchannel))

        return nn.Sequential(*layers)

    def forward(self, x):
        x = self.pre(x)

        x = self.layer1(x)
        x = self.layer2(x)
        x = self.layer3(x)
        x = self.layer4(x)

        x = F.avg_pool2d(x, 7)
        x = x.view(x.size(0), -1)

        return self.fc(x)
res_net = ResNet()
inp = t.autograd.Variable(t.randn(1, 3, 224, 224))
output = res_net(inp)
output.size()
torch.Size([1, 1000])

不到 50 行程式碼便實現了這樣一個網路,看起來是那麼不可思議,如果對此感興趣的同學還可以取消嘗試實現 Google 的 Inception 網路。

三、torchvision 中的 resnet34網路呼叫

前面我們講過一個 hub 模組,裡面儲存了很多網路結構。不僅如此,和 torch 配套的影像工具包 torchvision 也實現了深度學習中的大多數經典的模型,其中就包括了 ResNet34,非常簡單,可以通過以下兩行程式碼呼叫這個網路:

from torchvision import models
res_net = models.resnet34()
inp = t.autograd.Variable(t.randn(1, 3, 224, 224))
output = res_net(inp)
output.size()
torch.Size([1, 1000])

本例中的 ResNet34 的實現參考了 torchvision 中的實現並做了一定的調整,有興趣的同學可以去閱讀相對應的原始碼。

四、第六章總結

這一章詳細的介紹了 torch 中的 nn 工具箱,但是你說詳細嗎?又不是那麼詳細,還有很多很多地方我們需要去補漏,如果想深入各個部分的同學們,可以去參考官方文件。

當然,我更建議學一部分常用的基礎,然後在實踐中學習更加完善、更加系統的知識體系。因此,我們將在未來的實戰專案中不斷地鞏固 nn 這個工具箱的使用。

最後,隨著 nn 的落幕,torch 的地基也算是落幕了,剩下的都是一些邊邊角角的知識點,但是還是借用古人的一句話:路漫漫其修遠兮,任重而道遠。

相關文章