詳細な GoogLeNet ネットワーク構造

詳細な GoogLeNet ネットワーク構造

バージョンに関係なく、構造に重点を置いています

Christian Szegedy, Wei Liu, Yangqing Jia, Pierre Sermanet, Scott Reed,Dragomir Anguelov, Dumitru Erhan, Vincent Vanhoucke, Andrew Rabinovich.
Going Deeper with Convolutions
https://arxiv.org/abs/1409.4842v1

1 GoogLeNet の概要

GoogLeNet の開発について、できるだけ短い文章で説明しましょう。

1.1 はじめに

GoogLeNet は Google がリリースしたディープ ニューラル ネットワーク アーキテクチャであり、LeNet に敬意を表して Goog LeNetと名付けられました。
論文のタイトルは、映画「インセプション」からインスピレーションを得た「畳み込みでさらに深くなる」です。「もっと深く掘り下げる必要がある」インスパイヤされた。
ここに画像の説明を挿入

1.2 動機

従来の VGG ではネットワーク構造の深さや幅によって効果が異なり、一般的にネットワークが深く広いほど予測効果が高いと直感的に感じています。GoogLeNet の論文では、高品質のモデルを入手する最も安全な方法は次のとおりであるとも指摘しています。モデルに奥行きを加える(レイヤー数) またはモデルの幅を増やす(層コアまたはニューロンの数) しかし、一般に、より深いまたはより広いネットワークには次の問題が発生します。

1) ネットワークが深くなるほど勾配がなくなり、モデルの最適化が困難になる;
2) パラメーターが多すぎるため、過学習が起こりやすくなる トレーニング データセットが限られている場合、この問題は次のようになります。 3 )
コンピューティング リソースの要件が高く、トレーニング プロセス中に多くのパラメーターがゼロになる傾向があり、リソースを無駄にします。

要約すると、大規模なネットワークは過剰適合する傾向があり、計算の複雑さが高くなりすぎますこれら 2 つの点について、GoogLeNet は、完全な接続と畳み込み演算の代わりにスパース接続を使用するのが最も基本的な方法であると考えています

GoogleNet は、ニューラル ネットワーク構造のスパース性を維持し、密行列の高い計算性能を最大限に活用するという出発点に基づいて、この目標を達成するためにInceptionと呼ばれるモジュール構造を提案しました。

Inception はネットワーク・イン・ネットワーク (Network In Network) 構造であり、この構造に基づいてネットワーク全体の幅と深さを拡張することができ、2 ~ 3 倍のパフォーマンス向上をもたらすことができます。Inception には現在 v1 ~ v4 の合計 4 つがありますバージョン。
ここに画像の説明を挿入

2 インセプションの詳しい説明

インセプションはネットワーク内のネットワークであり、その中には以下のものが含まれます。4つのサブネット、この構造は CNN で一般的に使用されます。コンボリューション (1x1、3x3、5x5)プーリング操作 (3x3)スタックすると (畳み込みとプーリング、チャネルの追加後は同じサイズになります)、一方ではネットワークの幅が広がり、他方ではネットワークのサイズへの適応性が高まります。論文内の Inception のコードと Inception モジュールについて詳しく説明しましょう。

class Inception(nn.Module):
    def __init__(self, input_channels, n1x1, n3x3_reduce, n3x3, n5x5_reduce, n5x5, pool_proj):
        super().__init__()

        # 1x1conv branch
        self.b1 = nn.Sequential(
            nn.Conv2d(input_channels, n1x1, kernel_size=1),
            nn.BatchNorm2d(n1x1),
            nn.ReLU(inplace=True)
        )

        # 1x1conv -> 3x3conv branch
        self.b2 = nn.Sequential(
            nn.Conv2d(input_channels, n3x3_reduce, kernel_size=1),
            nn.BatchNorm2d(n3x3_reduce),
            nn.ReLU(inplace=True),
            nn.Conv2d(n3x3_reduce, n3x3, kernel_size=3, padding=1),
            nn.BatchNorm2d(n3x3),
            nn.ReLU(inplace=True)
        )

        # 1x1conv -> 5x5conv branch
        # use 2 3x3 conv filters stacked instead of 1 5x5 filters to obtain the same receptive field with fewer parameters
        self.b3 = nn.Sequential(
            nn.Conv2d(input_channels, n5x5_reduce, kernel_size=1),
            nn.BatchNorm2d(n5x5_reduce),
            nn.ReLU(inplace=True),
            nn.Conv2d(n5x5_reduce, n5x5, kernel_size=3, padding=1),
            nn.BatchNorm2d(n5x5, n5x5),
            nn.ReLU(inplace=True),
            nn.Conv2d(n5x5, n5x5, kernel_size=3, padding=1),
            nn.BatchNorm2d(n5x5),
            nn.ReLU(inplace=True)
        )

        # 3x3pooling -> 1x1conv
        # same conv
        self.b4 = nn.Sequential(
            nn.MaxPool2d(3, stride=1, padding=1),
            nn.Conv2d(input_channels, pool_proj, kernel_size=1),
            nn.BatchNorm2d(pool_proj),
            nn.ReLU(inplace=True)
        )

    def forward(self, x):
        return torch.cat([self.b1(x), self.b2(x), self.b3(x), self.b4(x)], dim=1)

ここに画像の説明を挿入

2.1 1x1conv ブランチ

1x1conv ブランチは上図の一番左のブランチで、1x1 畳み込みを使用してネットワークを広げ、BatchNorm を実行して最後にアクティブ化します。

2.2 1x1conv -> 3x3conv ブランチ

このステップの畳み込みカーネルのサイズは 3x3 ですが、3x3 畳み込みが実行される前に、特徴マップはまず 1x1 畳み込み層のパラメータ解除を通過します (1x1 畳み込みはネットワーク パラメータを大幅に削減します)。

2.3 1x1conv -> 5x5conv ブランチ

最初に 1x1 畳み込みを実行してパラメータを減らし、次に 5x5 畳み込み層を通過して特徴抽出を行います。InceptionV1 では、特徴抽出に
kernel=5 畳み込みカーネルを使用します。V2 では 5x5 を 2 つの 3x3 コンボリューション カーネルに置き換えました。なぜなら、この 2 つは同等であり、3x3 畳み込みパラメータは 5x5 畳み込み演算の約 1/3 であるからです。したがって、コード内の 2 つの 3x3 畳み込み演算は、実際には図の右側にある 5x5 畳み込み演算になります。

2.4 3x3プーリング -> 1x1conv

ここではプーリング カーネルが使用されていますが、具体的な操作は畳み込みに似ています
前のプーリング操作のストライドは畳み込みカーネルと同じサイズであり、満たされていないため、プーリング層後の HxW 特徴マップのサイズは H/sx W/s になります。ここでのプーリング操作のストライド =
1 、パディング = 1、カーネル = 3、実際には、機能マップのサイズはプーリング操作後に変更されません。プーリング層を使用して畳み込み演算とは異なる特徴表現を抽出する

2.5 GoogleNet の始まり

Inception と GoogLeNet を組み合わせて使用​​する場合の操作は、操作内のパラメーターとともに理解しやすくなります。

class GoogleNet(nn.Module):

    def __init__(self, num_class=100):
        super().__init__()
        self.prelayer = nn.Sequential(
            nn.Conv2d(3, 64, kernel_size=3, padding=1, bias=False),
            nn.BatchNorm2d(64),
            nn.ReLU(inplace=True),
            nn.Conv2d(64, 64, kernel_size=3, padding=1, bias=False),
            nn.BatchNorm2d(64),
            nn.ReLU(inplace=True),
            nn.Conv2d(64, 192, kernel_size=3, padding=1, bias=False),
            nn.BatchNorm2d(192),
            nn.ReLU(inplace=True),
        )
        
        self.maxpool = nn.MaxPool2d(3, stride=2, padding=1)

        self.a3 = Inception(192, 64, 96, 128, 16, 32, 32)
        self.b3 = Inception(256, 128, 128, 192, 32, 96, 64)

        

    def forward(self, x):
        x = self.prelayer(x)
        x = self.maxpool(x)
        x = self.a3(x)
        x = self.b3(x)

        return x

上記は GoogleNet のコードの一部であり、主に 7 つの入力パラメータを持つ Inception 部分を示しています。

Inception(input_channels, n1x1, n3x3_reduce, n3x3, n5x5_reduce, n5x5, pool_proj)
Inception(192			, 	64,			96,	  128,			16, 32,		 32)

たとえば、1x1conv -> 3x3conv ブランチ:入力チャネル = 192 では、ネットワークはまず 1x1 畳み込みカーネルを使用してチャネルを 96 に減らし、次に特徴抽出に 3x3 畳み込みを使用します。抽出されたチャネルは 128 で、その他は似たような枝

では、self.a3通過後のネットワークのチャンネル出力はどうなるのでしょうか?

実はsele.a3入っているんです2番目、4番目、6番目、7番目のパラメータの合計、つまり 64+128+32+32=256これは、
Inception の次の層のself.b3input_channelsでもあります。

2.6 もう一つ

上記の点を理解してください。GoogleNet には何も不思議なことはありません。率直に言って、インセプションがモジュール化された後、モジュールは直列に接続されます、前述の3つの問題は発生しません。

2.6.1 さらに深く掘り下げる必要がある

冒頭で述べた「We need to go Deep」は映画『インセプション』の表現でもあり、映画ではスクリーンショットを使って画像マクロや縦方向のマルチペインを表示するのが一般的だ。
ここに画像の説明を挿入

2.6.2 補助分類器

GoogLeNet は補助分類子を使用します。GoogleNet には合計 22 のレイヤーがあり、最後のレイヤーの出力に加えて、中間ノードの分類効果も非常に優れている可能性があります (たとえば、葉の病気と害虫の分類タスクでは、浅い組織の特徴により多くの注意が払われます。)、したがって、GoogLeNet は中間層の出力を分類として受け取り、それをより小さい重み (0.3 と 0.3) で最終的な分類結果に追加します。このような補助分類ノードは合計 2 つあります。

補助分類器はモデルの融合に相当し、同時にバックプロパゲーションの勾配信号をネットワークに追加し、ある程度の正則化を実現します。

3 ソースコードとネットワーク構造

以下は、補助分類器を使用しない GoogLeNet のソース コードです。


import torch
import torch.nn as nn

class Inception(nn.Module):
    def __init__(self, input_channels, n1x1, n3x3_reduce, n3x3, n5x5_reduce, n5x5, pool_proj):
        super().__init__()

        #1x1conv branch
        self.b1 = nn.Sequential(
            nn.Conv2d(input_channels, n1x1, kernel_size=1),
            nn.BatchNorm2d(n1x1),
            nn.ReLU(inplace=True)
        )

        #1x1conv -> 3x3conv branch
        self.b2 = nn.Sequential(
            nn.Conv2d(input_channels, n3x3_reduce, kernel_size=1),
            nn.BatchNorm2d(n3x3_reduce),
            nn.ReLU(inplace=True),
            nn.Conv2d(n3x3_reduce, n3x3, kernel_size=3, padding=1),
            nn.BatchNorm2d(n3x3),
            nn.ReLU(inplace=True)
        )

        #1x1conv -> 5x5conv branch
        #we use 2 3x3 conv filters stacked instead
        #of 1 5x5 filters to obtain the same receptive
        #field with fewer parameters
        self.b3 = nn.Sequential(
            nn.Conv2d(input_channels, n5x5_reduce, kernel_size=1),
            nn.BatchNorm2d(n5x5_reduce),
            nn.ReLU(inplace=True),
            nn.Conv2d(n5x5_reduce, n5x5, kernel_size=3, padding=1),
            nn.BatchNorm2d(n5x5, n5x5),
            nn.ReLU(inplace=True),
            nn.Conv2d(n5x5, n5x5, kernel_size=3, padding=1),
            nn.BatchNorm2d(n5x5),
            nn.ReLU(inplace=True)
        )

        #3x3pooling -> 1x1conv
        #same conv
        self.b4 = nn.Sequential(
            nn.MaxPool2d(3, stride=1, padding=1),
            nn.Conv2d(input_channels, pool_proj, kernel_size=1),
            nn.BatchNorm2d(pool_proj),
            nn.ReLU(inplace=True)
        )

    def forward(self, x):
        return torch.cat([self.b1(x), self.b2(x), self.b3(x), self.b4(x)], dim=1)


class GoogleNet(nn.Module):

    def __init__(self, num_class=100):
        super().__init__()
        self.prelayer = nn.Sequential(
            nn.Conv2d(3, 64, kernel_size=3, padding=1, bias=False),
            nn.BatchNorm2d(64),
            nn.ReLU(inplace=True),
            nn.Conv2d(64, 64, kernel_size=3, padding=1, bias=False),
            nn.BatchNorm2d(64),
            nn.ReLU(inplace=True),
            nn.Conv2d(64, 192, kernel_size=3, padding=1, bias=False),
            nn.BatchNorm2d(192),
            nn.ReLU(inplace=True),
        )

        #although we only use 1 conv layer as prelayer,
        #we still use name a3, b3.......
        self.a3 = Inception(192, 64, 96, 128, 16, 32, 32)
        self.b3 = Inception(256, 128, 128, 192, 32, 96, 64)

        ##"""In general, an Inception network is a network consisting of
        ##modules of the above type stacked upon each other, with occasional
        ##max-pooling layers with stride 2 to halve the resolution of the
        ##grid"""
        self.maxpool = nn.MaxPool2d(3, stride=2, padding=1)

        self.a4 = Inception(480, 192, 96, 208, 16, 48, 64)
        self.b4 = Inception(512, 160, 112, 224, 24, 64, 64)
        self.c4 = Inception(512, 128, 128, 256, 24, 64, 64)
        self.d4 = Inception(512, 112, 144, 288, 32, 64, 64)
        self.e4 = Inception(528, 256, 160, 320, 32, 128, 128)

        self.a5 = Inception(832, 256, 160, 320, 32, 128, 128)
        self.b5 = Inception(832, 384, 192, 384, 48, 128, 128)

        #input feature size: 8*8*1024
        self.avgpool = nn.AdaptiveAvgPool2d((1, 1))
        self.dropout = nn.Dropout2d(p=0.4)
        self.linear = nn.Linear(1024, num_class)

    def forward(self, x):
        x = self.prelayer(x)
        x = self.maxpool(x)
        x = self.a3(x)
        x = self.b3(x)

        x = self.maxpool(x)

        x = self.a4(x)
        x = self.b4(x)
        x = self.c4(x)
        x = self.d4(x)
        x = self.e4(x)

        x = self.maxpool(x)

        x = self.a5(x)
        x = self.b5(x)

        #"""It was found that a move from fully connected layers to
        #average pooling improved the top-1 accuracy by about 0.6%,
        #however the use of dropout remained essential even after
        #removing the fully connected layers."""
        x = self.avgpool(x)
        x = self.dropout(x)
        x = x.view(x.size()[0], -1)
        x = self.linear(x)

        return x

def googlenet():
    return GoogleNet()



ここに画像の説明を挿入

4 つのデイリークラシック

東陽馬生旭を送る

宋蓮(明代)

   私は若い頃勉強漬けでした。家は貧乏で、読む本を書くわけにもいかず、毎回蔵書館から借りて手書きで記録し、毎日返済しています。とても寒いし、硯は硬いし、指も曲げ伸ばしできないし、怠けてはいけません。収録終了後はお見送りさせていただき、あえて約束の時間を超過することはありません。そのため、余暇を本で過ごす人が多く、ユ・インさんもあらゆる種類の本を読まなければなりません。それは単に戴冠するだけでなく、聖人や聖者の道を称賛するものでもあります。彼は師のいない有名人にも悩まされており、何百マイルも離れたところまで旅をし、村の開拓者たちに質問をします。彼の弟子の弟子である初代デーデ・ロン・ワンズンは彼の部屋を満たし、彼は言葉を失いませんでした。ユー・リーは左右で待って、身をかがめて質問したり、彼が攻撃的な場合には、より敬意を持って礼儀正しくなり、あえて一言も言わなかった。したがって、ユウは愚かではありますが、彼の死について何かを学びました。

   教師だった頃、スーツケースや荷物を持って山や谷の奥深くを歩きました。寒さの厳しい冬、強風、数メートルの深さの大雪が降り、知らず知らずのうちに足の皮膚が荒れてしまいます。志社では四肢が固まって動くことができませんが、女性はスープを手に持って肥料を与え、布団で覆うと、長い時間が経つと平和になります。逆境の中で生きている主人は、新鮮な脂肪の味を感じずに、毎日また食事をします。同じ家の学生は全員刺繍が施されており、祝英の宝物が飾られた帽子をかぶり、腰には白玉の指輪、左側にはナイフ、右側には臭い顔があり、神のようです; 十分な喜びを持っている人彼らの口頭または言葉によるサービスが他の人たちと同じくらい優れているかどうかはわかりません。ガイユの勤勉さはこれと同じくらい難しいです。今は老いて大した功績はありませんが、幸いにも紳士の一人であり、皇帝の寵愛を受け継ぎ、大臣を務めた後は日々の侍従や相談役として働き、また世間にも偽りを与えています。彼の名前を呼びます。

   現在、生徒は皆太雪で勉強しており、県判事は毎日穀倉からお供え物を持っており、両親は1歳の時に秋歌の遺物を持っており、寒さと飢えの危険はありません。求めても言わず、求めても得られない人、所有するのに適した本はすべてここに集められており、手元に記録する必要はなく、他の人に任せておけば後で見ることができます。彼らの中には、業が磨かれず、徳が成就しない人もおり、性質が劣っていなければ、心は他の耳ほど良くありません。

   東陽の馬生君は太雪に来て2年目ですが、前任者たちは彼を良い人だと呼んでいます。覺王朝の都は原住民の息子として生まれ、志志として長い本を書き、その弁舌は非常に流暢でした。彼らと話し合って、仲良く話して、野蛮人のように見えます。彼は若い頃学問に専念して優れた学者だったと主張している。彼は親戚に会いに戻る予定ですが、私にとって昔のことを話すのは難しいです。ユ・ミアンの故郷の人々は学者であり、ユ・ミアンの野心もまた同じだと言われていますが、私の財産の繁栄を誇り、村人を誇りに思っていると私を中傷する人々、誰がそれを与えたか知っていますか?

おすすめ

転載: blog.csdn.net/weixin_43427721/article/details/122147162