ResNetの原理分析とコード実装の詳細な理解

ディレクトリ

1.問題

2.解決方法

3.なぜ機能するのか

4.ネットワークの実装

5.ネットワーク入力部

6.ネットワークの中間畳み込み部分

7.残余ブロックの実装

8.ネットワーク出力部

9.ネットワークの特性

 

10.次のように要約


1.問題

勾配消失と勾配爆発の問題は、初期収束を妨げますこの問題は、初期化正規化と中間層正規化によって解決されます。収束の問題を解決した後、劣化の現象が再び発生します。層の数が増えると、精度が増加し、その後急激に減少します。また、この低下は過剰適合によって引き起こされるものではなく、ネットワークに適切なレイヤーを追加すると、トレーニングエラーが大きくなります。ネットワークの深さが増加しても、モデルの精度は常に向上するわけではありません。この問題は、ネットワークが深くなるとテストエラーが高くなるだけでなく、トレーニングエラーも高くなるため、過剰適合によって引き起こされるものではありません。 。これは、より深いネットワークには勾配の消失/爆発の問題が伴うため、ネットワークの収束が妨げられるためと著者は提案しています。ネットワークの深さを深めるがネットワークのパフォーマンスを低下させるこの現象は、低下問題と呼ばれます。つまり、深度が大きくなると、大幅な劣化が発生し、ネットワークのトレーニングエラーとテストエラーの両方が大幅に増加し、この劣化の問題を解決するためにResNetが生まれました。

2、修正する方法

したがって、著者はソリューションを提案します。浅いアーキテクチャにさらにレイヤーを追加します。より深いモデルの場合:追加されたレイヤーはアイデンティティマッピングで、他のレイヤーは学習された浅いモデルからコピーされます。この場合、より深いモデルは、対応するより浅いネットワークよりも高いトレーニングエラーを生成してはなりません。残差学習の基本単位:
                                             ここに画像の説明を挿入

元のネットワーク入力x、出力H(x)を期待しています。ここで、H(x)= F(x)+ xとすると、ネットワークは、残差F(x)= H(x)-xを出力することを学習するだけで済みます。

                        

入力がxであると仮定すると、2つの完全に接続された層によって学習されたマッピングはH(x)です。つまり、2つの層をH(x)に漸近的に適合させることができます。H(x)とxの次元が同じであると仮定すると、H(x)をフィットすることは、残差関数H(x)-xをフィットすることと同等です。残差関数をF(x)= H(x)-xとし、元の関数はF(x)+ xになるため、クロスレイヤー接続は元のネットワークに直接追加されます。ここでのクロスレイヤー接続も非常にシンプルです。つまり、xのIDマッピングは過去に渡されます。


3、なぜ機能するのか

  1. 適応深度:ネットワークの劣化の問題は、多層ネットワークでアイデンティティマップをフィッティングする難しさを例示しています。つまり、H(x)をxにフィッティングすることは困難ですが、残差構造を使用した後、アイデンティティマップをフィッティングすると、すべてのネットワークパラメーターを0に直接学習するのは簡単で、IDマッピングのクロスレイヤー接続のみを残します。したがって、ネットワークをそれほど深くする必要がない場合は、中央のIDマッピングを少し増やし、それ以外の場合は少し少なくすることができます。
  2. 「差動増幅器」:最適なH(x)がアイデンティティマップに近いと仮定すると、ネットワークはアイデンティティマップ以外の小さな変動を見つける可能性が高くなります。
  3. 緩和勾配が消える:残差構造の入力xの導出がわかります。クロスレイヤー接続が存在するため、勾配の合計により、F(x)のxへの微分に1が追加されます

通常のネットワークとディープ残差ネットワークの最大の違いは、ディープ残差ネットワークには、入力を後続のレイヤーに直接接続する多くのバイパスブランチがあり、後続のレイヤーが残差を直接学習できるようにすることです。これらのブランチはショートカットと呼ばれます。従来の畳み込み層または完全に接続された層では、多かれ少なかれ情報の損失と、情報が送信される際の損失の問題があります。ResNetはこの問題をある程度解決します。入力情報を出力に直接バイパスして情報の整合性を保護することにより、ネットワーク全体で入力と出力の違いの一部を学習するだけでよくなり、学習目標と難易度が簡素化されます。


4.ネットワークの実装

Res18、Res34、Res50、Res101:ResNet 5つの主要な形態がある ;、Res152
入力部、出力部及び中間部畳み込み:以下に示すように、3つの主要部分を含む各ネットワーク(畳み込み中間部含むように(図のStage1〜Stage4は合計4つのステージです)ResNetのバリアントは豊富ですが、すべて上記の構造的特徴に従っており、ネットワーク間の違いは主にブロックパラメーターと中間たたみ込み部分の数の違いによるものです。ResNet18を例にして、ネットワーク全体がどのように実装されているかを見てみましょう。
img

class ResNet(nn.Module):
    def forward(self, x):
        # 输入
        x = self.conv1(x)
        x = self.bn1(x)
        x = self.relu(x)
        x = self.maxpool(x)

        # 中间卷积
        x = self.layer1(x)
        x = self.layer2(x)
        x = self.layer3(x)
        x = self.layer4(x)

        # 输出
        x = self.avgpool(x)
        x = x.view(x.size(0), -1)
        x = self.fc(x)

        return x

# 生成一个res18网络
def resnet18(pretrained=False, **kwargs):
    model = ResNet(BasicBlock, [2, 2, 2, 2], **kwargs)
    if pretrained:
        model.load_state_dict(model_zoo.load_url(model_urls['resnet18']))
    return model

(1)ネットワークに入った後、データは最初に入力部分(conv1、bn1、relu、maxpool)を通過します;
(2)次に中間畳み込み部分(layer1、layer2、layer3、layer4、ここでのレイヤーは前述の段階に対応します)に入ります;
(3)最後に、データは平均プーリングと完全に接続されたレイヤー(avgpool、fc)の後に出力されます;
具体的には、resnet18と他のresシリーズネットワークの違いは主にlayer1〜layer4で、他のコンポーネントは類似しています。

5.ネットワーク入力部

すべてのResNetネットワークの入力部分は、サイズ= 7x7、ストライド= 2、最大プーリングサイズ= 3x3、ストライド= 2の大規模な畳み込みカーネルです。この手順により、224x224の入力画像のサイズは56x56になります。機能マップにより、ストレージに必要なサイズが大幅に削減されます。

self.conv1 = nn.Conv2d(3, 64, kernel_size=7, stride=2, padding=3, bias=False)
self.bn1 = nn.BatchNorm2d(64)
self.relu = nn.ReLU(inplace=True)
self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)

6.ネットワークの中間畳み込み部分

中段の畳み込み部分は主に下図の青枠部分であり、3×3回の畳み込みを重ねることで情報を抽出します。赤いボックス内の[2、2、2、2]および[3、4、6、3]は、bolckの繰り返される積み重ね時間を表します。

img

先ほど呼び出したresnet18()関数にResNet(BasicBlock、[2、2、2、2]、* kwargs)という文があります。ここでの[2、2、2、2]は、図の赤いボックスと一致しています。このコード行をResNet(BasicBlock、[3、4、6、3]、* kwargs)に変更すると、res34ネットワークが得られます。

7.残余ブロックの実装

残差ブロックの実装方法を詳しく見てみましょう。下の図に示す基本ブロックでは、入力データが2つのパスに分割されます。1つのパスは2つの3 * 3畳み込みを通過し、もう1つのパスは直接短絡されます。合計はreluによって出力されます。これは非常に簡単です。

img

class BasicBlock(nn.Module):
    expansion = 1

    def __init__(self, inplanes, planes, stride=1, downsample=None):
        super(BasicBlock, self).__init__()
        self.conv1 = conv3x3(inplanes, planes, stride)
        self.bn1 = nn.BatchNorm2d(planes)
        self.relu = nn.ReLU(inplace=True)
        self.conv2 = conv3x3(planes, planes)
        self.bn2 = nn.BatchNorm2d(planes)
        self.downsample = downsample
        self.stride = stride

    def forward(self, x):
        identity = x

        out = self.conv1(x)
        out = self.bn1(out)
        out = self.relu(out)

        out = self.conv2(out)
        out = self.bn2(out)

        if self.downsample is not None:
            identity = self.downsample(x)

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

        return out

img

8.ネットワーク出力部

ネットワーク出力部分は非常にシンプルです。グローバルアダプティブスムーズプーリングにより、すべての機能マップが1 * 1に引き込まれます。res18の場合、1x512x7x7の入力データは1x512x1x1に引き込まれ、完全に接続されたレイヤーに接続されて出力され、出力ノードの数も増えます予測されたカテゴリの数と一致しています。

self.avgpool = nn.AdaptiveAvgPool2d((1, 1))
self.fc = nn.Linear(512 * block.expansion, num_classes)

9.ネットワークの特性

img

ResNet全体はドロップアウトを使用せず、すべてBNを使用します。さらに:

  1. VGGに触発されて、たたみ込み層は主に3×3たたみ込みです。
  2. 同じ出力フィーチャマップサイズのレイヤー、つまり同じステージ、同じ数の3x3フィルター。
  3. フィーチャーマップのサイズが半分になると、各レイヤーの時間の複雑さを維持するためにフィルターの数が2倍になります。
  4. 各ステージは、ステップサイズが2のたたみ込み層を介してダウンサンプリングを実行しますが、このダウンサンプリングは、各ステージの最初のたたみ込みで1回だけ完了します。
  5. ネットワークは、平均的なプーリングレイヤーとソフトマックスの1000方向に完全に接続されたレイヤーで終わります実際には、適応型グローバル平均プーリングは一般的にエンジニアリングで使用されます。

図のネットワーク構造から、畳み込み後、完全に接続された層の前にグローバル平均プーリング(GAP)構造があります。

10.次のように要約

  1. 従来の分類ネットワークと比較すると、ここは完全に接続されたレイヤーではなく、プールに接続されています。プーリングはパラメーターを必要としないため、完全に接続されたレイヤーと比較して、多数のパラメーターを切り離すことができます。7x7フィーチャーマップの場合、直接プーリングは、完全に接続されたレイヤーへの切り替えと比較して、パラメーターを約50倍節約できます。これには2つの効果があります。1つはコンピューティングリソースを節約することであり、もう1つはモデルの過剰適合を防ぎ、汎化能力を向上させることです。
  2. ここではグローバル平均プーリングが使用されています。一部の論文の実験結果では、平均プーリングの効果は最大プーリングの効果よりもわずかに優れていますが、最大プーリングの効果はそれほど悪くありません。**実際の使用プロセスでは、必要に応じていくつかの調整を行うことができます。たとえば、マルチ分類問題は、グローバル最大プーリングを使用するのに適しています。
     

 

943の元の記事を公開 136のような 330,000以上を訪問

おすすめ

転載: blog.csdn.net/weixin_36670529/article/details/105224997