【PyTorchチュートリアル】WHAT IS TORCH.NN REALLY?の6、畳み込みニューラルネット

1_プログラミング

やっと畳み込みニューラルネットを実装するそうです。

ここまでのチュートリアルでかなりシンプルに実装できそうだね。

PyTorchチュートリアル「WHAT IS TORCH.NN REALLY?」の「Switch to CNN」から行なっていきます。

こんな人の役に立つかも

・機械学習プログラミングを勉強している人

・Pytorchのチュートリアルを行なっている人

スポンサーリンク

CNNへの切り替え

次に、3つの畳み込み層をもつニューラルネットワークを作成します。

畳み込み層としてPyTorchにあらかじめ定義してある「Conv2d」クラスを利用します。これから作成する畳み込み層は3層とします。それぞれの畳み込み層は、活性化関数「ReLU」を通ります。そして、最後に「アベレージプーリング」を行います。

PyTorchの「view」はnumpyでいう「reshape」メソッドです。

テンソルのサイズを変化させるやつだね。

#畳み込みニューラルネットワークのモデル定義
class Mnist_CNN(nn.Module):
    def __init__(self):
        super().__init__()
        self.conv1 = nn.Conv2d(1, 16, kernel_size=3, stride=2, padding=1)
        self.conv2 = nn.Conv2d(16, 16, kernel_size=3, stride=2, padding=1)
        self.conv3 = nn.Conv2d(16, 10, kernel_size=3, stride=2, padding=1)

    def forward(self, xb):
        xb = xb.view(-1, 1, 28, 28)
        xb = F.relu(self.conv1(xb))
        xb = F.relu(self.conv2(xb))
        xb = F.relu(self.conv3(xb))
        xb = F.avg_pool2d(xb, 4)
        return xb.view(-1, xb.size(1))

#学習率を変数「lr」に格納
lr = 0.1

次に、モデルをインスタンス化して、オプティマイザを作成、fit関数を実行します。

勾配降下法(SGD)の「momentum」パラメータを設定しています。このパラメータを設定すると、前回の重みに「momentum」倍して加算するというMomentumSGDと呼ばれる方法になります。一般的に、訓練が早くなるようです。今回のチュートリアルでは「momentum」を0.9としたSGDを利用するようです。

model = Mnist_CNN()
opt = optim.SGD(model.parameters(), lr=lr, momentum=0.9)

fit(epochs, model, loss_func, opt, train_dl, valid_dl)
1エポック目
Loss:0.3962
2エポック目
Loss:0.2382

nn.Sequential

torch.nnには、コードを単純化するために使用できるもう一つのクラス、「sequential」もあります。 Sequentialオブジェクトは、その中に含まれる各モジュールを順次実行し、ニューラルネットワークを記述する最も簡単な方法です。

また、特定の関数から、オリジナルの層を作成することもできます。

たとえば、PyTorchにはビューレイヤーがないため、ネットワーク用にビューレイヤーを作成する必要があります。 Lambdaは、Sequentialでネットワークを定義するときに使用できるレイヤーを作成します。 (Lambdaクラスを利用して次のようにSequentialでのオリジナルの層が定義できます。)

ここでいうビューレイヤーは、viewメソッドを実行してデータを整えるような層のことみたいです。

#LambdaでSequentialに与える空の層を定義する。
class Lambda(nn.Module):
    def __init__(self, func):
        super().__init__()
        self.func = func

    def forward(self, x):
        return self.func(x)

#実際に層の中で行う動作を関数として記載、Lambdaクラスの入力となる。
def preprocess(x):
    return x.view(-1, 1, 28, 28)

ビューレイヤー、データを「view」で変換するような層をnn.Moduleを継承したLambdaというクラスで作成しています。また、「preprocess」という関数でviewメソッドを呼び出し、画像データの形式へと変換を行なっています。(ここでは4階のテンソルの形式に変換)

#Sequentialで畳み込みニューラルネットワークモデルを定義
model = nn.Sequential(
    #先ほど定義したLambdaクラスにpreprocessメソッドで入力することで層のように振る舞う
    #Lambda①
    Lambda(preprocess),
    nn.Conv2d(1, 16, kernel_size=3, stride=2, padding=1),
    nn.ReLU(),
    nn.Conv2d(16, 16, kernel_size=3, stride=2, padding=1),
    nn.ReLU(),
    nn.Conv2d(16, 10, kernel_size=3, stride=2, padding=1),
    nn.ReLU(),
    nn.AvgPool2d(4),
    #Lambda②
    Lambda(lambda x: x.view(x.size(0), -1)),
)

opt = optim.SGD(model.parameters(), lr=lr, momentum=0.9)

fit(epochs, model, loss_func, opt, train_dl, valid_dl)
1エポック目
Loss:0.4856
2エポック目
Loss:0.2668

Sequentialは、層の流れと処理の流れがそのまま記載されているので直感的に理解しやすい形になっています。

Lambdaについてもう少し考えてみます。

定義したLambdaクラスはSequentialでモデルを定義する際に、先のプログラムのように利用します。Sequentialの一番最初のデータ変換として利用しています(Lambda①)。また、出力時にテンソルの形状を変更するのにも利用しています(Lambda②)。

Lambda②のように関数として定義していなくても、Pythonの関数である「lambda関数」を利用してLambdaクラスの引数として入力することも可能です。

Lambdaクラスで層を作成するイメージは、

①Sequentialに層として与えるための空の層をLambdaクラスで作成

②Lambdaクラスに引数として関数を与えることでオリジナルの動作をする

といった感じでしょうか。

Sequentialの項目というよりは、SequentialでのLambdaクラスの使い方といった内容になりました。

今回の内容を更新したチュートリアルのプログラムをGitHubに配置しておきます。

続きの記事はこちらになります。

タイトルとURLをコピーしました