1. ホーム
  2. スクリプト・コラム
  3. パイソン

Python LeNetネットワークの説明とpytorchでの実装

2022-01-08 01:46:07

1. LeNetの紹介

LeNetニューラルネットワークは、ディープラーニング三国志の一人で、CNN(Convolutional Neural Network)の父でもあるYan LeCunが提唱したものです。LeNetの構造はCNNの構造を確立し、畳み込み層、Pooling層、ReLU層など、現在ニューラルネットワークに見られるものの多くはLeNetのネットワーク構造に見られる。LeNetは1990年代に提案されたが、当時は大規模な学習データがなく、コンピュータのハードウェアの性能も低かったため、LeNetのニューラルネットワークは複雑な問題を扱うにはあまり有効ではなかった。LeNetのネットワーク構造は比較的単純であるが、ニューラルネットワークの入門的な学習に適しているに過ぎない。

2. LetNetネットワークモデル

LeNetネットワークモデルは、一般にLeNet-5と呼ばれており、このモデルを勉強していると、こんな絵を見たことがあるのではないでしょうか?

画像

この図も元の論文のモデル図なので、ちょっと見づらいかもしれませんが、次の図は、私がdrawioというソフトで作ったネットワークモデル図で、次のようなものです。

画像

訂正、上図の2層目のConv2dの結果は10になるはずですが、5と表記されています

畳み込みニューラルネットワークの基礎を学んだ方なら、この図がはっきり読めるはずだと思います。図の左上の計算の右側にも計算式が書いてありますが、上の図は入力形状と出力形状の各層について詳しく書き出しました。計算式とモデルの一般的な構造は、次の図も見てください(上下の図に一緒に対応すると、より分かりやすいと思います)。

LeNet-5のネットワークモデルは、畳み込み層、最大プーリング層、完全連結層、relu, softmax活性化関数を含むだけです。入力画像のサイズと各層の畳み込みカーネルの数、ステップサイズはすべてモデルによって設定されており、一般に任意に変更することはできないはずである。フラット化操作は、平坦化操作とも呼ばれる。完全連結層への入力は1つの特徴量と1次元のベクトルであることは周知の通りですが、畳み込みネットワークの特徴量から抽出された特徴行列は1次元ではなく、完全連結層に送られるべきものなので、1次元に平坦化するフラット化操作が必要なのです。

3. PytorchによるLeNetの実装

Pythonのコードは以下の通りです。

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

'''
    Notes:
    1. LeNet is a 5-layer network
    2. nn.ReLU(inplace=True) The parameter True is to modify the tensor passed down from the upper layer network Conv2d directly, which can save memory for computing and not to store other variables.
    3. The dimensional annotations of this model omit the size of N(batch_size), i.e. input(3, 32, 32) -->input(N, 3, 32, 32)
    4.nn.init.xavier_uniform_(m.weight)
     Generate values with a uniform distribution, fill the input tensor or variable, and sample the values in the resulting tensor from U(-a, a)
     where a = gain * sqrt( 2/(fan_in + fan_out))* sqrt(3),
     gain is the optional scaling factor, default is 1
     'fan_in' retains the magnitude of the variance of the weights in forward propagation, 'fan_out' retains the magnitude in backward propagation
    5.nn.init.constant_(m.bias, 0)
      Fill a constant 0 for all dimensional tensor
'''


class LeNet(nn.Module):
    def __init__(self, num_classes=10, init_weights=False):
        super(LeNet, self). __init__()
        self.conv1 = nn.Conv2d(in_channels=3, out_channels=16, kernel_size=5, stride=1)
        self.maxpool1 = nn.MaxPool2d(kernel_size=2, stride=2)
        self.relu = nn.ReLU(True)

        self.conv2 = nn.Conv2d(in_channels=16, out_channels=32, kernel_size=5, stride=1)
        self.maxpool2 = nn.MaxPool2d(kernel_size=2, stride=2)

        self.fc1 = nn.Linear(32 * 5 * 5, 120)
        self.fc2 = nn.Linear(120, 84)
        self.fc3 = nn.Linear(84, num_classes)

        if init_weights:
            self._initialize_weights()

    def forward(self, x):
        x = self.conv1(x) # input(3, 32, 32) output(16, 28, 28)
        x = self.relu(x) # activation function
        x = self.maxpool1(x) # output(16, 14, 14)
        x = self.conv2(x) # output(32, 10, 10)
        x = self.relu(x) # activation function
        x = self.maxpool2(x) # output(32, 5, 5)
        x = torch.flatten(x, start_dim=1) # output(32*5*5) N for batch_size
        x = self.fc1(x) # output(120)
        x = self.relu(x) # activation function
        x = self.fc2(x) # output(84)
        x = self.relu(x) # activation function
        x = self.fc3(x) # output(num_classes)

        return x

    def _initialize_weights(self):
        for m in self.modules():
            if isinstance(m, nn.Conv2d):
                nn.init.xavier_uniform_(m.weight)
                if m.bias is not None:
                    nn.init.constant_(m.bias, 0)
            elif isinstance(m, nn.Linear):
                nn.init.xavier_uniform_(m.weight)
                nn.init.constant_(m.bias, 0)


または

次のものは、自分自身のウェイトとオフセットを初期化せず、デフォルトの初期化を使用します。

import torch.nn as nn
import torch.nn.functional as F
import torch


class LeNet(nn.Module):
    def __init__(self):
        super(LeNet, self). __init__()
        self.conv1 = nn.Conv2d(3, 16, 5)
        self.pool1 = nn.MaxPool2d(2, 2)
        self.conv2 = nn.Conv2d(16, 32, 5)
        self.pool2 = nn.MaxPool2d(2, 2)
        self.fc1 = nn.Linear(32*5*5, 120)
        self.fc2 = nn.Linear(120, 84)
        self.fc3 = nn.Linear(84, 10)

    def forward(self, x):
        x = F.relu(self.conv1(x)) # output(16, 28, 28)
        x = self.pool1(x) # output(16, 14, 14)
        x = F.relu(self.conv2(x)) # output(32, 10, 10)
        x = self.pool2(x) # output(32, 5, 5)
        x = x.view(x.size(0), -1) # output(32*5*5)
        x = F.relu(self.fc1(x)) # output(120)
        x = F.relu(self.fc2(x)) # output(84)
        x = self.fc3(x) # output(10)
        return x

nn.Linearは完全連結層、最後の完全連結層以外は再アクティブ化する必要がある、デフォルトではパディング操作なし

nn.Conv2dは対応するパラメータの順番を覚えておく必要があります:

1.in_channels: チャンネル数または入力の深さ

2.out_channels: 出力のチャンネル数または深さ

3.kernel_size: コンボリューションカーネルの大きさ

4.stride:ステップサイズ、デフォルト1

5.パディング:パディングの大きさ、デフォルトは0

6.dilation: 拡張の大きさ、デフォルトは1、現在は使用されていない

7.グループ:グループの数、デフォルト1

8. bias: デフォルト True, boolean, バイアス値を使用するかどうか

9. padding_mode: デフォルトのパディングを0にする。

順番を覚えていなくても大丈夫ですが、パラメータ名は覚えておく必要があります

参考記事 LeNetネットワークモデルの学習と予測に関するpytorchの実装

以上、Python LeNetネットワークとpytorchの実装について詳しく説明しました。Python LeNetネットワークの実装に関するより詳しい情報は、BinaryDevelopの他の関連記事にもご注目ください