【画像分類】【ディープラーニング】【Pytorch版】GoogLeNet(InceptionV2)モデルアルゴリズムの詳細説明

【画像分類】【ディープラーニング】【Pytorch版】GoogLeNet(InceptionV2)モデルアルゴリズムの詳細説明


序文

GoogLeNet (InceptionV2) は、Google の Ioffe 氏、Sergey 氏らによって「バッチ正規化: 内部共変量シフトの削減によるディープ ネットワーク トレーニングの加速 [ICML-2015]」[論文] で開発されました。記事で提案されているバッチ正規化による改良モデルは、畳み込み層と InceptionV1 に基づく活性化関数の間に BN 層を挿入することです。主な特徴は、正規化 (標準化) 部分になることです。モデル アーキテクチャの最適化を行い、各トレーニング ミニバッチの正規化を実行します。


GoogLeNet (InceptionV2) の説明

内部共変量シフト問題: ネットワーク トレーニング プロセスにはパラメータの更新が伴います。人為的に正規化された入力層のデータに加えて、入力データの分布も後続モデルの各層のパラメータは常に変化します。これは、前の層のパラメータの更新が次の層の入力データ分布の変化につながるためです。各層の入力データ分布が変化すると、後続の層は新しい入力分布に再適応する必要があり、トレーニングの複雑さが増加します。モデルの深さが増すにつれて、入力分布が大きく変化すると、各層の固有値分布は、活性化関数 (活性化関数) の出力区間の上限と下限に向かって徐々に移動します。飽和間隔は、データ分布を正規化するために従来の機械学習で一般的に使用される方法です。画像は特徴を抽出する前にホワイトニングされます。入力データが同じ特徴分布を持つものとし、特徴間の相関を除去します。つまり、入力データは、平均 0、単位分散が 0 の正規分布に変換されます。 BN 層の目的は、中間ネットワーク層に入力される特徴マップが、平均 0、分散 1 の分布則を満たすようにすることです。 ホワイトニング
畳み込みニューラル ネットワークの内部共変量シフト (ICS) 効果を改善するための解決策は、畳み込み層とソースである活性化関数の間にバッチ正規化 (BN) 層を挿入することです。ホワイトニング操作の場合、)、ネットワークは長期的には勾配消失または勾配爆発の問題に悩まされる可能性があり、モデルのトレーニングを続行できなくなります。さらに、各層の入力分布の変化が不安定で一貫性がないため、ネットワークが最適解に収束することが困難になり、ネットワークの過剰適合やネットワーク トレーニングの遅延につながる可能性があります。

Batch Normalization公式

BN 層の計算は通常、畳み込み層の後、活性化関数の前に行われ、深層ネットワークの中央にある特徴値 (または隠れた値、中間値) を標準化します。トレーニング プロセス中、BN レイヤーの標準化された平均と分散の計算は、データ全体の平均と分散ではなく、現在のバッチの平均と分散に依存し、変換と再構成が実行され、学習可能なパラメーターが得られます。 γ γ が導入されています。c b b β
バッチ正規化の前方伝播プロセスは、トレーニング フェーズとテスト フェーズで異なります。
トレーニング フェーズ: BN レイヤーは、トレーニング データの各バッチの平均と分散を使用して正規化します。バッチデータの標準偏差が異なります。バッチ正規化では、次の手順が実行されます。

  1. 计算 m m m输受信料の均等値: μ B ← 1 m Σ i = 1 m x i {\mu _B} \leftarrow \frac{ 1}{ {\rm{m}}}\シグマ _{ {\rm{i}} = 1}^{\rm{m}}{x_i} メートルB メートル1 Si=1m バツi
  2. 计算 m m m输受信料の方差: σ B 2 ← 1 m Σ i = 1 m ( x i − μ B ) 2 \sigma _B^2 \leftarrow \frac{1}{ {\rm{m}}}\シグマ _{ {\rm{i}} = 1}^{\rm{m}}{\left( { {x_i} - {\mu _B}} \right)^2} pB2 メートル1 Si=1m (xi メートルB )2
  3. m m m个输入数据进行标准化(正太化): x i ∧ ← x i − μ B σ B 2 + ε \mathop { {x_i}}\limits^ \wedge \leftarrow \frac{ { {x_i} - {\mu _B}}}{ {\sqrt {\sigma _B^2 + \varepsilon } }} バツi pB2 +ε バツi μB
  4. m m m スケールとバイアス変換用の入力データ: y i = γ x i ∧ + β {y_i} = \gamma \mathop { {x_i}}\limits^ \wedge + \beta そしてi =cバツi +β

入力データは B B に分割されますB バッチ、各バッチのデータ量は m m m个。

テスト フェーズ: 通常、入力されるテスト サンプルは 1 つだけで、使用される平均と分散は、トレーニング後のデータセット全体の平均と分散であり、スライド平均によって計算されます。方法。バッチ正規化では、次の手順が実行されます。

  1. 计算输入数据的均值: E [ x ] = E B [ μ B ] E\left[ x \right] = {E_B}\left[ { {\mu _B}} \right] そして[x]=そしてB [μB ]
  2. 計算输受信料の方差: V a r [ x ] = m m − 1 E B [ σ B 2 ] Var\left[ x \right] = \frac{ {\rm{m}}}{ { {\rm{m - 1}}}}{E_B}\left[ {\sigma _B^2} \right] ヴァr[x]=m1m そしてB [σB2 ]
  3. 入力データのスケーリングとバイアス: y = γ V a r [ x ] + ε x + ( β − γ E [ x ] V a r [ x ] + ε ) y = \frac{\ガンマ }{ {\sqrt {Var\left[ x \right] + \varepsilon } }}x + \left( {\beta - \frac{ {\gamma E\left[ x \right]}}{ {\sqrt {Var\left[ x \right] + \varepsilon } }}} \right) そして=ヴァr[x] +ε γ バツ+(βヴァr[x] +ε γE[x] )

バッチ正規化の逆伝播
这部分看不懂问题也不大,博主也是花了半天才弄懂的,只是一堆求导

ℓ \彼 は損失額損失

1. x i ∧ {\mathop { {x_i}}\limits^ \wedge } バツi の勾配: ∂ ℓ ∂ x i ∧ = ∂ ℓ ∂ y i ⋅ ∂ y i ∂ x i = ∂ ℓ ∂ y i ⋅ γ \frac{ {\partial \ell }}{ {\partial \mathop { {x_i}}\limits^ \wedge }} = \frac{ {\partial \ell }}{ {\部分{ {\rm{y}}_i}}} \cdot \frac{ {\部分{ {\rm{y}}_i}}}{ {\部分{ {\rm{x}}_i}}} = \frac{ {\partial \ell }}{ {\部分{ {\rm{y}}_i}}} \cdot \gamma バツi =yi xi yi =yi γ
2. σ B 2 {\sigma _B^2} pB2 の勾配: ∂ ℓ ∂ σ B 2 = { ∑ i = 1 m ∂ ℓ ∂ x i ∧ ∂ x i ∧ ∂ σ B 2 ∂ x i ∧ ∂ σ B 2 = ( x i − μ B ) − 1 2 ( σ B 2 + ε ) − 3 2 = ∑ i = 1 m ∂ ℓ ∂ x i ∧ ⋅ ( x i − μ B ) − 1 2 ( σ B 2 + ε ) − 3 2 \frac{ {\partial \ell }}{ {\partial \sigma _B^2}} = \left\{ {\begin{array}{c} {\sum _{ {\rm{i}} = 1}^{\rm{m}}\frac{ {\partial \ell }}{ {\partial \mathop { {x_i}}\limits^ \wedge }}\frac{ {\partial \mathop { {x_i}}\limits^ \wedge }}{ {\partial \sigma _B^2}}}\\ {\frac{ {\partial \mathop { {x_i}}\limits^ \wedge }}{ {\partial \sigma _B^2}} = \left( { {x_i} - {\mu _B}} \right)\frac{ { - 1}}{2}{ {\left( {\sigma _B^2 + \varepsilon } \right)}^{\frac{ { - 3}}{2}}}} \end{array}} \right。 = \合計 _{ {\rm{i}} = 1}^{\rm{m}}\frac{ {\partial \ell }}{ {\partial \mathop { {x_i}}\limits^ \wedge }} \cdot \left( { {x_i} - {\mu _B}} \right)\frac{ { - 1}}{2}{\left( {\sigma _B^2 + \varepsilon } \right)^{\frac{ { - 3}}{2}}} σB2 = i=1m バツi σB2 バツi σB2 バツi =(xi メートルB )21 (σB2 +ε)23 =i=1m バツi (xi メートルB )21 (σB2 +ε)23
3. μ B {\mu _B} メートルB の勾配: ∂ ℓ ∂ μ B = { ∑ i = 1 m ∂ ℓ ∂ x i ∧ ∂ x i ∧ ∂ μ B + ∂ ℓ ∂ σ B 2 ∂ σ B 2 ∂ μ B ∂ x i ∧ ∂ μ B = − 1 σ B 2 + ε ∂ σ B 2 ∂ μ B = ∑ i = 1 m − 2 ( x i − μ B ) m = ∑ i = 1 m ∂ ℓ ∂ x i ∧ ⋅ − 1 σ B 2 + ε + ∂ ℓ ∂ σ B 2 ⋅ ∑ i = 1 m − 2 ( x i − μ B ) m \frac{ {\partial \ell }}{ {\partial {\mu _B}}} = \left\{ {\begin{array}{c} {\sum _{ {\rm{i}} = 1}^{\rm{m}}\frac{ {\partial \ell }}{ {\partial \mathop { {x_i}}\limits^ \wedge }}\frac{ {\partial \mathop { {x_i}}\limits^ \wedge }}{ {\partial {\mu _B}}} + \frac{ {\partial \ell }}{ {\partial \sigma _B^2}}\frac{ {\partial \sigma _B^2}}{ {\partial {\mu _B}}}}\\ {\frac{ {\partial \mathop { {x_i}}\limits^ \wedge }}{ {\partial {\mu _B}}} = \frac{ { - 1}}{ {\sqrt {\sigma _B^2 + \varepsilon } }}}\\ {\frac{ {\partial \sigma _B^2}}{ {\partial {\mu _B}}} = \sum _{ {\rm{i}} = 1}^{\rm{m}}\frac{ { - 2\left( { {x_i} - {\mu _B}} \right)}}{m}} \end{array}} \right。 = \合計 _{ {\rm{i}} = 1}^{\rm{m}}\frac{ {\partial \ell }}{ {\partial \mathop { {x_i}}\limits^ \wedge }} \cdot \frac{ { - 1}}{ {\sqrt {\sigma _B^2 + \varepsilon } }} + \frac{ {\partial \ell }}{ {\partial \sigma _B^2}} \cdot \sum _{ {\rm{i}} = 1}^{\rm{m}}\frac{ { - 2\left( { {x_i} - {\mu _B}} \right)}}{m} μB = i=1m バツi μB バツi +σB2 μB σB2 μB バツi =pB2 +ε 1 μB σB2 =i=1m メートル2(xi μB ) =i=1m バツi pB2 +e 1 +σB2 i=1m メートル2(xi メートルB )
4. x i {x_i} バツi の勾配: ∂ ℓ ∂ x i = { ∂ ℓ ∂ x i ∧ ∂ x i ∧ ∂ x i + ∂ ℓ σ B 2 σ B 2 ∂ x i + ∂ ℓ ∂ μ B ∂ μ B ∂ x i ∂ x i ∧ ∂ x i = 1 σ B 2 + ε σ B 2 ∂ x i = 2 m ( x i − μ B ) ( 1 − 1 m ) + 2 m ∑ k = 1 , k ! = i m ( x k − μ B ) (− 1 m ) = 2 m ( x i − μ B ) + 2 m ∑ k = 1 m ( x k − μ B ) (− 1 m ) = 2 m ( x i − μ B ) + 0 ∂ μ B ∂ x i = 1 m = ∂ ℓ ∂ x i ∧ ⋅ 1 σ B 2 + ε + ∂ ℓ σ B 2 ⋅ 2 m ( x i − μ B ) + ∂ ℓ ∂ μ B 1 m \frac{ {\partial \ell }}{ {\partial {x_i}}} = \left\{ {\begin{array}{c} {\frac{ {\partial \ell }}{ {\partial \mathop { {x_i}}\limits^ \wedge }}\frac{ {\partial \mathop { {x_i}}\limits^ \wedge }}{ {\partial {x_i}}} + \frac{ {\partial \ell }}{ {\sigma _B^2}}\frac{ {\sigma _B^2}}{ {\partial {x_i}}} + \frac{ {\partial \ell }}{ {\partial {\mu _B}}}\frac{ {\部分 {\mu _B}}}{ {\partial {x_i}}}}\\ {\frac{ {\partial \mathop { {x_i}}\limits^ \wedge }}{ {\partial {x_i}}} = \frac{1}{ {\sqrt {\sigma _B^2 + \varepsilon } }}}\\ {\frac{ {\sigma _B^2}}{ {\partial {x_i}}} = \frac{2}{m}\left( { {x_i} - {\mu _B}} \right)\left( {1 - \frac{1}{m}} \right) + \frac{2}{m}\合計_{ {\rm{k}} = 1,k! = i}^{\rm{m}}\left( { {x_{\rm{k}}} - {\mu _B}} \right)\left( { - \frac{1}{m}} \right) = \frac{ 2}{m}\left( { {x_i} - {\mu _B}} \right) + \frac{2}{m}\sum _{ {\rm{k}} = 1}^{\rm{m}}\left( { {x_{\rm{k}}} - {\mu _B}} \right)\left( { - \frac{1}{m}} \right) = \frac{ 2}{m}\left( { {x_i} - {\mu _B}} \right)}+0\\ {\frac{ {\部分 {\mu _B}}}{ {\partial {x_i}}} = \frac{1}{m}} \end{array} = } \right.\frac{ {\partial \ell }}{ {\partial \mathop { {x_i}}\limits^ \wedge }} \cdot \frac{1}{ {\sqrt {\sigma _B^2 + \varepsilon } }} + \frac{ {\partial \ell }}{ {\sigma _B^2}} \cdot \frac{2}{m}\left( { {x_i} - {\mu _B}} \right) + \frac{ {\partial \ell }}{ {\partial {\mu _B}}}\frac{1}{m} xi = バツi xi バツi +pB2 xi pB2 +μB xi μB xi バツi =pB2 +ε 1 xi pB2 =メートル2 (xi メートルB )(1メートル1 )+メートル2 k=1k!=im (xk メートルB )(メートル1 )=メートル2 (xi メートルB )+メートル2 k=1m (xk メートルB )(メートル1 )=メートル2 (xi メートルB )+0xi μB =メートル1 =バツi pB2 +e 1 +pB2 メートル2 (xi メートルB )+μB メートル1
5. γ {\gamma} γ の勾配: ∂ ℓ ∂ γ = ∑ i = 1 m ∂ ℓ ∂ y i ⋅ x i ∧ \frac{ {\partial \ell }}{ {\partial \gamma }} = \sum _{ {\rm{i}} = 1}^{\rm{m}}\frac{ {\partial \ell }}{ {\部分{ {\rm{y}}_i}}} \cdot \mathop { {x_i}}\limits^ \ウェッジ γ =i=1m yi バツi
6. β {\beta } β の勾配: ∂ ℓ ∂ β = ∑ i = 1 m ∂ ℓ ∂ y i \frac{ {\partial \ell }}{ {\partial \beta }} = \sum _{ {\rm{i}} = 1}^{\rm{m}}\frac{ {\partial \ell }}{ {\部分{ {\rm{y}}_i}}} β =i=1m yi

InceptionV2 の構造

VggNet では、大きなコンボリューション カーネルを小さなコンボリューション カーネルに置き換えることが提案されています。これにより、受容野の一貫した範囲を維持しながらパラメータの数が削減されます。 VggNet では、2 つの 3×3 コンボリューション カーネルをスタッキングすると、5×5 コンボリューション カーネルを等価的に置き換えることができ、3 つの 3×3 コンボリューション カーネルをスタッキングすると、等価的に 7×7 コンボリューション カーネルを置き換えることができます。 2 つの 3×3 コンボリューション カーネルを備えた InceptionV1 構造内。

128 個の 5x5 コンボリューション カーネルを使用して 512 チャネルの特徴マップを畳み込みます
パラメータ量: 512×5×5×128=1638400
計算量: 512×5×5×128×W×H=1638400×W×H
W×H は、畳み込み層の入力および出力の特徴マップ サイズが残っていると仮定した場合の特徴マップ サイズです。一貫性
2 セットの 128 個の 3x3 コンボリューション カーネルを使用して、512 チャネルの特徴マップをコンボリューションします
パラメータ量: 512×3×3×128+128×3× 3 ×128=737280
計算量:512×3×3×128×W×H+128×3×3×128×W×H=737280×W×H

InceptionV2 の特別な構造

GoogLeNet (InceptionV2) の中間層には、いくつかの特別な InceptionV2 構造が表示されます。その機能は、GoogLeNet (InceptionV1) モデルの MaxPool を置き換えることです。この構造は、1×1 畳み込み層ブランチを破棄し、プーリング ブランチも破棄します。A 1 ×1 畳み込み層が追加され、この構造の出力特徴マップのサイズは入力特徴マップのサイズの半分に縮小されます。

GoogLeNet (InceptionV2) モデル構造

次の図は、元の論文に記載されている GoogLeNet (InceptionV2) モデル構造の詳細な概略図です。

GoogLeNet (InceptionV2) モデルは補助分類子ブランチを放棄します。 主に InceptionV2 モジュール、畳み込み層、およびプーリングで構成されます。分類器部分は完全に接続された層で構成されます。 バックボーン部分:
GoogLeNet は画像分類で 2 つの部分に分かれています:

読者は、元の論文でマークされているチャネル番号の一部が間違っており、コードを書くときに一致できないことに注意してください。

ブロガーは GoogLeNet (InceptionV1) の構造を真似て、次のような GoogLeNet (InceptionV2) の構造を描きました。


GoogLeNet(InceptionV2) Pytorch コード

畳み込み層グループ: 畳み込み層 + BN 層 + 活性化関数

# 卷积组: Conv2d+BN+ReLU
class BasicConv2d(nn.Module):
    def __init__(self, in_channels, out_channels, kernel_size, stride=1, padding=0):
        super(BasicConv2d, self).__init__()
        self.conv = nn.Conv2d(in_channels, out_channels, kernel_size, stride, padding)
        self.bn = nn.BatchNorm2d(out_channels)
        self.relu = nn.ReLU(inplace=True)
    def forward(self, x):
        x = self.conv(x)
        x = self.bn(x)
        x = self.relu(x)
        return x

InceptionV2 モジュール: 畳み込み層グループ + プーリング層

# InceptionV2:BasicConv2d+MaxPool2d
class InceptionV2A(nn.Module):
    def __init__(self, in_channels, ch1x1, ch3x3red, ch3x3, ch3x3redX2, ch3x3X2, pool_proj):
        super(InceptionV2A, self).__init__()
        # 1×1卷积
        self.branch1 = BasicConv2d(in_channels, ch1x1, kernel_size=1)
        # 1×1卷积+3×3卷积
        self.branch2 = nn.Sequential(
            BasicConv2d(in_channels, ch3x3red, kernel_size=1),
            BasicConv2d(ch3x3red, ch3x3, kernel_size=3, padding=1)   # 保证输出大小等于输入大小
        )
        # 1×1卷积++3×3卷积+3×3卷积
        self.branch3 = nn.Sequential(
            BasicConv2d(in_channels, ch3x3redX2, kernel_size=1),
            BasicConv2d(ch3x3redX2, ch3x3X2, kernel_size=3, padding=1),
            BasicConv2d(ch3x3X2, ch3x3X2, kernel_size=3, padding=1)         # 保证输出大小等于输入大小
        )
        # 3×3池化+1×1卷积
        self.branch4 = nn.Sequential(
            nn.MaxPool2d(kernel_size=3, stride=1, padding=1),
            BasicConv2d(in_channels, pool_proj, kernel_size=1)
        )
    def forward(self, x):
        branch1 = self.branch1(x)
        branch2 = self.branch2(x)
        branch3 = self.branch3(x)
        branch4 = self.branch4(x)
        # 拼接
        outputs = [branch1, branch2, branch3, branch4]
        return torch.cat(outputs, 1)

InceptionV2 特別モジュール (3 つのブランチ): 畳み込み層グループ + プーリング層

# InceptionV2:BasicConv2d+MaxPool2d
class InceptionV2B(nn.Module):
    def __init__(self, in_channels, ch1x1, ch3x3red, ch3x3, ch3x3redX2, ch3x3X2, pool_proj):
        super(InceptionV2B, self).__init__()
        # ch1x1:没有1×1卷积
        # 1×1卷积+3×3卷积,步长为2
        self.branch1 = nn.Sequential(
            BasicConv2d(in_channels, ch3x3red, kernel_size=1),
            BasicConv2d(ch3x3red, ch3x3, kernel_size=3, stride=2, padding=1)   # 保证输出大小等于输入大小
        )
        # 1×1卷积+3×3卷积+3×3卷积,步长为2
        self.branch2 = nn.Sequential(
            BasicConv2d(in_channels, ch3x3redX2, kernel_size=1),
            BasicConv2d(ch3x3redX2, ch3x3X2, kernel_size=3, padding=1),   # 保证输出大小等于输入大小
            BasicConv2d(ch3x3X2, ch3x3X2, kernel_size=3, stride=2, padding=1)         # 保证输出大小等于输入大小
        )
        # 3×3池化,步长为2
        self.branch3 = nn.Sequential(nn.MaxPool2d(kernel_size=3, stride=2, padding=1))
        # pool_proj:池化层后不再接卷积层

完全なコード

import torch.nn as nn
import torch
import torch.nn.functional as F
from torchsummary import summary

class GoogLeNetV2(nn.Module):
    def __init__(self, num_classes=1000, aux_logits=True, init_weights=False):
        super(GoogLeNetV2, self).__init__()
        self.aux_logits = aux_logits

        self.conv1 = BasicConv2d(3, 64, kernel_size=7, stride=2, padding=3)
        self.maxpool1 = nn.MaxPool2d(3, stride=2, ceil_mode=True)
        self.conv2 = BasicConv2d(64, 64, kernel_size=1)
        self.conv3 = BasicConv2d(64, 192, kernel_size=3, padding=1)
        self.maxpool2 = nn.MaxPool2d(3, stride=2, ceil_mode=True)

        self.inception3a = InceptionV2A(192, 64, 64, 64, 64, 96, 32)
        self.inception3b = InceptionV2A(256, 64, 64, 96, 64, 96, 64)
        self.inception3c = InceptionV2B(320, 0, 128, 160, 64, 96, 0)

        self.inception4a = InceptionV2A(576, 224, 64, 96, 96, 128, 128)
        self.inception4b = InceptionV2A(576, 192, 96, 128, 96, 128, 128)
        self.inception4c = InceptionV2A(576, 160, 128, 160, 128, 128, 128)
        self.inception4d = InceptionV2A(576, 96, 128, 192, 160, 160, 128)
        self.inception4e = InceptionV2B(576, 0, 128, 192, 192, 256, 0)

        self.inception5a = InceptionV2A(1024, 352, 192, 320, 160, 224, 128)
        self.inception5b = InceptionV2A(1024, 352, 192, 320, 160, 224, 128)

        self.avgpool = nn.AdaptiveAvgPool2d((1, 1))
        self.dropout = nn.Dropout(0.4)
        self.fc = nn.Linear(1024, num_classes)
        if init_weights:
            self._initialize_weights()
	def forward(self, x):
        # N x 3 x 224 x 224
        x = self.conv1(x)
        # N x 64 x 112 x 112
        x = self.maxpool1(x)
        # N x 64 x 56 x 56
        x = self.conv2(x)
        # N x 64 x 56 x 56
        x = self.conv3(x)
        # N x 192 x 56 x 56
        x = self.maxpool2(x)
        # N x 192 x 28 x 28
        x = self.inception3a(x)
        # N x 256 x 28 x 28
        x = self.inception3b(x)
        # N x 320 x 28 x 28
        x = self.inception3c(x)
        # N x 576 x 14 x 14
        x = self.inception4a(x)
        # N x 576 x 14 x 14
        x = self.inception4b(x)
        # N x 576 x 14 x 14
        x = self.inception4c(x)
        # N x 576 x 14 x 14
        x = self.inception4d(x)
        # N x 576 x 14 x 14
        x = self.inception4e(x)
        # N x 1024 x 7 x 7
        x = self.inception5a(x)
        # N x 1024 x 7 x 7
        x = self.inception5b(x)
        # N x 1024 x 7 x 7
        x = self.avgpool(x)
        # N x 1024 x 1 x 1
        x = torch.flatten(x, 1)
        # N x 1024
        x = self.dropout(x)
        x = self.fc(x)
        # N x 1000(num_classes)
        return x
    # 对模型的权重进行初始化操作
    def _initialize_weights(self):
        for m in self.modules():
            if isinstance(m, nn.Conv2d):
                nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu')
                if m.bias is not None:
                    nn.init.constant_(m.bias, 0)
            elif isinstance(m, nn.Linear):
                nn.init.normal_(m.weight, 0, 0.01)
                nn.init.constant_(m.bias, 0)

# InceptionV2:BasicConv2d+MaxPool2d
class InceptionV2A(nn.Module):
    def __init__(self, in_channels, ch1x1, ch3x3red, ch3x3, ch3x3redX2, ch3x3X2, pool_proj):
        super(InceptionV2A, self).__init__()
        # 1×1卷积
        self.branch1 = BasicConv2d(in_channels, ch1x1, kernel_size=1)
        # 1×1卷积+3×3卷积
        self.branch2 = nn.Sequential(
            BasicConv2d(in_channels, ch3x3red, kernel_size=1),
            BasicConv2d(ch3x3red, ch3x3, kernel_size=3, padding=1)   # 保证输出大小等于输入大小
        )
        # 1×1卷积++3×3卷积+3×3卷积
        self.branch3 = nn.Sequential(
            BasicConv2d(in_channels, ch3x3redX2, kernel_size=1),
            BasicConv2d(ch3x3redX2, ch3x3X2, kernel_size=3, padding=1),
            BasicConv2d(ch3x3X2, ch3x3X2, kernel_size=3, padding=1)         # 保证输出大小等于输入大小
        )
        # 3×3池化+1×1卷积
        self.branch4 = nn.Sequential(
            nn.MaxPool2d(kernel_size=3, stride=1, padding=1),
            BasicConv2d(in_channels, pool_proj, kernel_size=1)
        )
    def forward(self, x):
        branch1 = self.branch1(x)
        branch2 = self.branch2(x)
        branch3 = self.branch3(x)
        branch4 = self.branch4(x)
        # 拼接
        outputs = [branch1, branch2, branch3, branch4]
        return torch.cat(outputs, 1)

# InceptionV2:BasicConv2d+MaxPool2d
class InceptionV2B(nn.Module):
    def __init__(self, in_channels, ch1x1, ch3x3red, ch3x3, ch3x3redX2, ch3x3X2, pool_proj):
        super(InceptionV2B, self).__init__()
        # ch1x1:没有1×1卷积
        # 1×1卷积+3×3卷积,步长为2
        self.branch1 = nn.Sequential(
            BasicConv2d(in_channels, ch3x3red, kernel_size=1),
            BasicConv2d(ch3x3red, ch3x3, kernel_size=3, stride=2, padding=1)   # 保证输出大小等于输入大小
        )
        # 1×1卷积+3×3卷积+3×3卷积,步长为2
        self.branch2 = nn.Sequential(
            BasicConv2d(in_channels, ch3x3redX2, kernel_size=1),
            BasicConv2d(ch3x3redX2, ch3x3X2, kernel_size=3, padding=1),   # 保证输出大小等于输入大小
            BasicConv2d(ch3x3X2, ch3x3X2, kernel_size=3, stride=2, padding=1)         # 保证输出大小等于输入大小
        )
        # 3×3池化,步长为2
        self.branch3 = nn.Sequential(nn.MaxPool2d(kernel_size=3, stride=2, padding=1))
        # pool_proj:池化层后不再接卷积层

    def forward(self, x):
        branch1 = self.branch1(x)
        branch2 = self.branch2(x)
        branch3 = self.branch3(x)
        # 拼接
        outputs = [branch1,branch2, branch3]
        return torch.cat(outputs, 1)

# 卷积组: Conv2d+BN+ReLU
class BasicConv2d(nn.Module):
    def __init__(self, in_channels, out_channels, kernel_size, stride=1, padding=0):
        super(BasicConv2d, self).__init__()
        self.conv = nn.Conv2d(in_channels, out_channels, kernel_size, stride, padding)
        self.bn = nn.BatchNorm2d(out_channels)
        self.relu = nn.ReLU(inplace=True)
    def forward(self, x):
        x = self.conv(x)
        x = self.bn(x)
        x = self.relu(x)
        return x

if __name__ == '__main__':
    device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
    model = GoogLeNetV2().to(device)
    summary(model, input_size=(3, 224, 224))

summary ではネットワーク構造とパラメータを出力できるため、構築されたネットワーク構造を簡単に確認できます。


要約する

バッチ正規化の原理と畳み込みニューラル ネットワークにおけるその役割をできるだけ簡単かつ詳細に紹介し、GoogLeNet (InceptionV2) モデルの構造と pytorch コードについて説明します。

おすすめ

転載: blog.csdn.net/yangyu0515/article/details/134315481