中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

pytorch如何實現ResNet結構

發布時間:2021-05-17 11:21:59 來源:億速云 閱讀:262 作者:小新 欄目:開發技術

這篇文章主要介紹了pytorch如何實現ResNet結構,具有一定借鑒價值,感興趣的朋友可以參考下,希望大家閱讀完這篇文章之后大有收獲,下面讓小編帶著大家一起了解一下。

1.ResNet的創新

現在重新稍微系統的介紹一下ResNet網絡結構。 ResNet結構首先通過一個卷積層然后有一個池化層,然后通過一系列的殘差結構,最后再通過一個平均池化下采樣操作,以及一個全連接層的得到了一個輸出。ResNet網絡可以達到很深的層數的原因就是不斷的堆疊殘差結構而來的。

1)亮點

網絡中的亮點 :

  • 超深的網絡結構( 突破1000 層)

  • 提出residual 模塊

  • 使用Batch Normalization 加速訓練( 丟棄dropout)

但是,一般來說,并不是一直的加深神經網絡的結構就會得到一個更好的結果,一般太深的網絡會出現過擬合的現象嚴重,可能還沒有一些淺層網絡要好。

pytorch如何實現ResNet結構

2)原因

其中有兩個原因:

  • 梯度消失或梯度爆炸

當層數過多的時候,假設每一層的誤差梯度都是一個小于1的數值,當進行方向傳播的過程中,每向前傳播一層,都要乘以一個小于1的誤差梯度,當網絡越來越深時,所成的小于1的系數也就越來越多,此時梯度便越趨近于0,這樣梯度便會越來越小。這便會造成梯度消失的現象。

而當所成的誤差梯度是一個大于1的系數,而隨著網絡層數的加深,梯度便會越來越大,這便會造成梯度爆炸的現象。

  • 退化問題(degradation problem)

當解決了梯度消失或者梯度爆炸的問題之后,其實網絡的效果可能還是不盡如意,還可能有退化問題。為此,ResNet提出了殘差結構來解決這個退化問題。 也正是因為有這個殘差的結構,所以才可以搭建這么深的網絡。

pytorch如何實現ResNet結構

2.ResNet的結構

殘差結構如圖所示

pytorch如何實現ResNet結構

作圖是針對ResNet-18/34層淺層網絡的結構,右圖是ResNet-50/101/152層深層網絡的結構,其中注意:主分支與shortcut 的輸出特征矩陣shape。

一下表格為網絡的一些主要參數

pytorch如何實現ResNet結構

可以看見,不同層數的網絡結構其實框架是類似的,不同的至少堆疊的殘差結構的數量。

1)淺層的殘差結構

pytorch如何實現ResNet結構

需要注意,有些殘差結構的ShortCut是實線,而有的是虛線,這兩者是不同的。對于左圖來說,ShortCut是實線,這表明輸入與輸出的shape是一樣的,所以可以直接的進行相加。而對于右圖來說,其輸入的shape與輸出的shape是不一樣的,這時候需要調整步長stribe與kernel size來使得兩條路(主分支與捷徑分支)所處理好的shape是一模一樣的。

2)深層的殘差結構

pytorch如何實現ResNet結構

同樣的,需要注意,主分支與shortcut 的輸出特征矩陣shape必須相同,同樣的通過步長來調整。

但是注意原論文中:

右側虛線殘差結構的主分支上、第一個1x1卷積層的步距是2,第二個3x3卷積層的步距是1.

而在pytorch官方實現的過程中是第一個1x1卷積層的步距是1,第二個3x3卷積層步距是2,這樣能夠在ImageNet的top1上提升大概0.5%的準確率。

所以在conv3_x,conv4_x,conv5_x中所對應的殘差結構的第一層,都是指虛線的殘差結構,其他的殘差結構是實線的殘差結構。

3)總結

對于每個大模塊中的第一個殘差結構,需要通過虛線分支來調整殘差結構的輸入與輸出是同一個shape。此時使用了下采樣的操作函數。
對于每個大模塊中的其他剩余的殘差結構,只需要通過實線分支來調整殘差網絡結構,因為其輸出和輸入本身就是同一個shape的。

對于第一個大模塊的第一個殘差結構,其第二個3x3的卷積中,步長是1的,而其他的三個大模塊的步長均為2.
在每一個大模塊的維度變換中,主要是第一個殘差結構使得shape減半,而模塊中其他的殘差結構都是沒有改變shape的。也真因為沒有改變shape,所以這些殘差結構才可以直接的通過實線進行相加。

3.Batch Normalization

Batch Normalization的目的是使我們的一批(Batch)特征矩陣feature map滿足均值為0,方差為1的分布規律。

pytorch如何實現ResNet結構

其中:
μ,σ_2在正向傳播過程中統計得到
γ,β在反向傳播過程中訓練得到

Batch Normalization是google團隊在2015年論文《Batch Normalization: Accelerating Deep Network Training by Reducing Internal Covariate Shift》提出的。通過該方法能夠加速網絡的收斂并提升準確率。

具體的相關原理見:Batch Normalization詳解以及pytorch實驗

4.參考代碼

import torch
import torch.nn as nn

# 分類數目
num_class = 5
# 各層數目
resnet18_params = [2, 2, 2, 2]
resnet34_params = [3, 4, 6, 3]
resnet50_params = [3, 4, 6, 3]
resnet101_params = [3, 4, 23, 3]
resnet152_params = [3, 8, 36, 3]


# 定義Conv1層
def Conv1(in_planes, places, stride=2):
    return nn.Sequential(
        nn.Conv2d(in_channels=in_planes,out_channels=places,kernel_size=7,stride=stride,padding=3, bias=False),
        nn.BatchNorm2d(places),
        nn.ReLU(inplace=True),
        nn.MaxPool2d(kernel_size=3, stride=2, padding=1)
    )


# 淺層的殘差結構
class BasicBlock(nn.Module):
    def __init__(self,in_places,places, stride=1,downsampling=False, expansion = 1):
        super(BasicBlock,self).__init__()
        self.expansion = expansion
        self.downsampling = downsampling

        # torch.Size([1, 64, 56, 56]), stride = 1
        # torch.Size([1, 128, 28, 28]), stride = 2
        # torch.Size([1, 256, 14, 14]), stride = 2
        # torch.Size([1, 512, 7, 7]), stride = 2
        self.basicblock = nn.Sequential(
            nn.Conv2d(in_channels=in_places, out_channels=places, kernel_size=3, stride=stride, padding=1, bias=False),
            nn.BatchNorm2d(places),
            nn.ReLU(inplace=True),
            nn.Conv2d(in_channels=places, out_channels=places, kernel_size=3, stride=1, padding=1, bias=False),
            nn.BatchNorm2d(places * self.expansion),
        )

        # torch.Size([1, 64, 56, 56])
        # torch.Size([1, 128, 28, 28])
        # torch.Size([1, 256, 14, 14])
        # torch.Size([1, 512, 7, 7])
        # 每個大模塊的第一個殘差結構需要改變步長
        if self.downsampling:
            self.downsample = nn.Sequential(
                nn.Conv2d(in_channels=in_places, out_channels=places*self.expansion, kernel_size=1, stride=stride, bias=False),
                nn.BatchNorm2d(places*self.expansion)
            )
        self.relu = nn.ReLU(inplace=True)

    def forward(self, x):
        # 實線分支
        residual = x
        out = self.basicblock(x)

        # 虛線分支
        if self.downsampling:
            residual = self.downsample(x)

        out += residual
        out = self.relu(out)
        return out


# 深層的殘差結構
class Bottleneck(nn.Module):

    # 注意:默認 downsampling=False
    def __init__(self,in_places,places, stride=1,downsampling=False, expansion = 4):
        super(Bottleneck,self).__init__()
        self.expansion = expansion
        self.downsampling = downsampling

        self.bottleneck = nn.Sequential(
            # torch.Size([1, 64, 56, 56]),stride=1
            # torch.Size([1, 128, 56, 56]),stride=1
            # torch.Size([1, 256, 28, 28]), stride=1
            # torch.Size([1, 512, 14, 14]), stride=1
            nn.Conv2d(in_channels=in_places,out_channels=places,kernel_size=1,stride=1, bias=False),
            nn.BatchNorm2d(places),
            nn.ReLU(inplace=True),
            # torch.Size([1, 64, 56, 56]),stride=1
            # torch.Size([1, 128, 28, 28]), stride=2
            # torch.Size([1, 256, 14, 14]), stride=2
            # torch.Size([1, 512, 7, 7]), stride=2
            nn.Conv2d(in_channels=places, out_channels=places, kernel_size=3, stride=stride, padding=1, bias=False),
            nn.BatchNorm2d(places),
            nn.ReLU(inplace=True),
            # torch.Size([1, 256, 56, 56]),stride=1
            # torch.Size([1, 512, 28, 28]), stride=1
            # torch.Size([1, 1024, 14, 14]), stride=1
            # torch.Size([1, 2048, 7, 7]), stride=1
            nn.Conv2d(in_channels=places, out_channels=places * self.expansion, kernel_size=1, stride=1, bias=False),
            nn.BatchNorm2d(places * self.expansion),
        )

        # torch.Size([1, 256, 56, 56])
        # torch.Size([1, 512, 28, 28])
        # torch.Size([1, 1024, 14, 14])
        # torch.Size([1, 2048, 7, 7])
        if self.downsampling:
            self.downsample = nn.Sequential(
                nn.Conv2d(in_channels=in_places, out_channels=places*self.expansion, kernel_size=1, stride=stride, bias=False),
                nn.BatchNorm2d(places*self.expansion)
            )
        self.relu = nn.ReLU(inplace=True)

    def forward(self, x):
        # 實線分支
        residual = x
        out = self.bottleneck(x)

        # 虛線分支
        if self.downsampling:
            residual = self.downsample(x)

        out += residual
        out = self.relu(out)

        return out


class ResNet(nn.Module):
    def __init__(self,blocks, blockkinds, num_classes=num_class):
        super(ResNet,self).__init__()

        self.blockkinds = blockkinds
        self.conv1 = Conv1(in_planes = 3, places= 64)

        # 對應淺層網絡結構
        if self.blockkinds == BasicBlock:
            self.expansion = 1
            # 64 -> 64
            self.layer1 = self.make_layer(in_places=64, places=64, block=blocks[0], stride=1)
            # 64 -> 128
            self.layer2 = self.make_layer(in_places=64, places=128, block=blocks[1], stride=2)
            # 128 -> 256
            self.layer3 = self.make_layer(in_places=128, places=256, block=blocks[2], stride=2)
            # 256 -> 512
            self.layer4 = self.make_layer(in_places=256, places=512, block=blocks[3], stride=2)

            self.fc = nn.Linear(512, num_classes)

        # 對應深層網絡結構
        if self.blockkinds == Bottleneck:
            self.expansion = 4
            # 64 -> 64
            self.layer1 = self.make_layer(in_places = 64, places= 64, block=blocks[0], stride=1)
            # 256 -> 128
            self.layer2 = self.make_layer(in_places = 256,places=128, block=blocks[1], stride=2)
            # 512 -> 256
            self.layer3 = self.make_layer(in_places=512,places=256, block=blocks[2], stride=2)
            # 1024 -> 512
            self.layer4 = self.make_layer(in_places=1024,places=512, block=blocks[3], stride=2)

            self.fc = nn.Linear(2048, num_classes)

        self.avgpool = nn.AvgPool2d(7, stride=1)

        # 初始化網絡結構
        for m in self.modules():
            if isinstance(m, nn.Conv2d):
                # 采用了何凱明的初始化方法
                nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu')
            elif isinstance(m, nn.BatchNorm2d):
                nn.init.constant_(m.weight, 1)
                nn.init.constant_(m.bias, 0)

    def make_layer(self, in_places, places, block, stride):

        layers = []

        # torch.Size([1, 64, 56, 56])  -> torch.Size([1, 256, 56, 56]), stride=1 故w,h不變
        # torch.Size([1, 256, 56, 56]) -> torch.Size([1, 512, 28, 28]), stride=2 故w,h變
        # torch.Size([1, 512, 28, 28]) -> torch.Size([1, 1024, 14, 14]),stride=2 故w,h變
        # torch.Size([1, 1024, 14, 14]) -> torch.Size([1, 2048, 7, 7]), stride=2 故w,h變
        # 此步需要通過虛線分支,downsampling=True
        layers.append(self.blockkinds(in_places, places, stride, downsampling =True))

        # torch.Size([1, 256, 56, 56]) -> torch.Size([1, 256, 56, 56])
        # torch.Size([1, 512, 28, 28]) -> torch.Size([1, 512, 28, 28])
        # torch.Size([1, 1024, 14, 14]) -> torch.Size([1, 1024, 14, 14])
        # torch.Size([1, 2048, 7, 7]) -> torch.Size([1, 2048, 7, 7])
        # print("places*self.expansion:", places*self.expansion)
        # print("block:", block)
        # 此步需要通過實線分支,downsampling=False, 每個大模塊的第一個殘差結構需要改變步長
        for i in range(1, block):
            layers.append(self.blockkinds(places*self.expansion, places))

        return nn.Sequential(*layers)


    def forward(self, x):

        # conv1層
        x = self.conv1(x)   # torch.Size([1, 64, 56, 56])

        # conv2_x層
        x = self.layer1(x)  # torch.Size([1, 256, 56, 56])
        # conv3_x層
        x = self.layer2(x)  # torch.Size([1, 512, 28, 28])
        # conv4_x層
        x = self.layer3(x)  # torch.Size([1, 1024, 14, 14])
        # conv5_x層
        x = self.layer4(x)  # torch.Size([1, 2048, 7, 7])

        x = self.avgpool(x) # torch.Size([1, 2048, 1, 1]) / torch.Size([1, 512])
        x = x.view(x.size(0), -1)   # torch.Size([1, 2048]) / torch.Size([1, 512])
        x = self.fc(x)      # torch.Size([1, 5])

        return x

def ResNet18():
    return ResNet(resnet18_params, BasicBlock)

def ResNet34():
    return ResNet(resnet34_params, BasicBlock)

def ResNet50():
    return ResNet(resnet50_params, Bottleneck)

def ResNet101():
    return ResNet(resnet101_params, Bottleneck)

def ResNet152():
    return ResNet(resnet152_params, Bottleneck)


if __name__=='__main__':
    # model = torchvision.models.resnet50()

    # 模型測試
    # model = ResNet18()
    # model = ResNet34()
    # model = ResNet50()
    # model = ResNet101()
    model = ResNet152()
    # print(model)

    input = torch.randn(1, 3, 224, 224)
    out = model(input)
    print(out.shape)

pytorch的優點

1.PyTorch是相當簡潔且高效快速的框架;2.設計追求最少的封裝;3.設計符合人類思維,它讓用戶盡可能地專注于實現自己的想法;4.與google的Tensorflow類似,FAIR的支持足以確保PyTorch獲得持續的開發更新;5.PyTorch作者親自維護的論壇 供用戶交流和求教問題6.入門簡單

感謝你能夠認真閱讀完這篇文章,希望小編分享的“pytorch如何實現ResNet結構”這篇文章對大家有幫助,同時也希望大家多多支持億速云,關注億速云行業資訊頻道,更多相關知識等著你來學習!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

宜昌市| 伊通| 西宁市| 临沭县| 诏安县| 江阴市| 桃园市| 阳朔县| 宣武区| 深圳市| 图片| 合山市| 台中县| 鸡泽县| 长乐市| 柳州市| 井研县| 佛教| 宁安市| 文登市| 贞丰县| 龙胜| 广西| 临泽县| 府谷县| 武威市| 西乡县| 万盛区| 盱眙县| 呼玛县| 工布江达县| 阿鲁科尔沁旗| 治县。| 法库县| 朝阳区| 平凉市| 明溪县| 叶城县| 津南区| 吉首市| 南岸区|