【機械学習】多層パーセプトロンについて

今回から何回かに分けて、機械学習におけるニューラルネットワークのアーキテクチャについて書いていきたいと思います。ブログのネタが切れたわけではありません

はじめに

このシリーズ(になるといいな)では、ニューラルネットワークの構成要素についてある程度知っている人を対象にしています。といっても、私自身のメインの分野ではないため、間違っていることもあるかもしれません。間違いがあれば、Twitter等で指摘していただけると助かります。

多層パーセプトロンとは

ニューラルネットワーク(以下NN)の基本的なモデルの一つに、MLP(Multi-Layer Perceptron)があります。MLPは、入力層(Input Layer)、1つ以上の隠れ層(Hidden Layer)、出力層(Output Layer)で構成されます。隠れ層を複数持つことが「多層」の由来です。
各層では、前の層のニューロンと次の層のニューロンがすべて接続されています。この仕組みを全結合(Fully Connected)といい、全結合を行う層を全結合層(Fully Connected Layer / Dense Layer)と呼びます。つまり、ある層の各ニューロンは、前の層の出力をすべて受け取って計算します。

MNISTを分類するMLPの構成は、次のようになります。

入力画像 (1 x 28 x 28)
        |
    Flatten (784)
        |
    Linear (784 -> 512) -> ReLU
        |
    Linear (512 -> 256) -> ReLU
        |
    Linear (256 -> 10)
        |
   各数字のスコア (0~9)

入力を x、重みを W、バイアスを b とすると、全結合層の線形変換とReLUを組み合わせた計算は次のようになります。

yj = ReLU( ∑ i=1 n Wji xi + bj )

ここでxiは入力のi番目の値、Wjiは入力iから出力jへの重みです。iを1からnまで足し合わせているため、出力jはすべての入力を使って計算されます。

この計算のあとに活性化関数を適用することで、単純な線形変換だけでは表現できない複雑な関係を学習できます。隠れ層ではReLU、Sigmoid、tanhなどが使われますが、現在はReLUがよく使われます。ReLUは、入力が0未満の場合は0にし、0以上の場合は入力をそのまま出力する関数です。

ReLU(z) = max(0,z)

活性化関数については、次回以降に専用の記事で扱う予定です。

この例では、入力画像を784個の値に変換してから全結合層へ入力します。最終層の10個の出力は、それぞれ数字の0~9に対応するスコアです。最もスコアの大きい番号を予測結果として選びます。

PyTorchでMLPを作ってみる

今回は、PyTorchを使って、MNISTデータセットの数字を分類するMLPを作ってみます。

import torch
import torch.nn as nn

class MLP(nn.Module):
    def __init__(self):
        super().__init__()
        self.flatten = nn.Flatten()
        self.fc1 = nn.Linear(28*28, 512)
        self.relu1 = nn.ReLU()
        self.fc2 = nn.Linear(512, 256)
        self.relu2 = nn.ReLU()
        self.fc3 = nn.Linear(256, 10)
        
    def forward(self, x):
        x = self.flatten(x)
        x = self.fc1(x)
        x = self.relu1(x)
        x = self.fc2(x)
        x = self.relu2(x)
        x = self.fc3(x)
        return x

今回は3層MLPを作成しました。入力層は28x28の画像を受け取り、隠れ層は512ニューロンと256ニューロン、出力層は10ニューロン(0~9の数字)となっています。また、隠れ層の活性化関数にはReLUを使用しています。

学習コードなどは省略します。すべてコードはこちらにあります。

nn.ModuleはPyTorchのニューラルネットワークの基本クラスであり、ニューラルネットワークを定義する際に継承して使用します。forwardメソッドは、入力データがネットワークを通過する際の計算を定義します。

nn.Flatten()は、入力データを1次元に変換するための層です。MNISTの画像は28x28の2次元配列ですが、全結合層に入力するためには784個の値が並ぶ1次元のベクトルに変換する必要があります。

nn.Linear()は、全結合層を定義するためのクラスです。引数には、入力の次元数と出力の次元数を指定します。例えばnn.Linear(28*28, 512)は、784個の入力を受け取り、512個の値を出力します。

このコードでは、最後の層のあとにSoftmaxを置いていません。CrossEntropyLossを損失関数に使う場合、損失関数の内部でSoftmaxに相当する処理が行われるためです。推論時に確率として確認したい場合は、出力に対してtorch.softmaxを適用します。

このようにPyTorchを使うことで、簡単にMLPを構築することができます。

隠れ層はnn.Linearとnn.ReLUの組み合わせを増やすことで追加できます。しかし、層やニューロンを無闇に増やしても性能が向上するとは限りません。学習が頭打ちになったり、過学習の原因になったりするので注意が必要です。入力データを1次元に変換するため、画像の位置関係を活かしにくいという弱点もあります。そのため、画像認識ではCNNなど別のアーキテクチャが使われることもあります。

Hugo で構築されています。
テーマ Stack は Jimmy によって設計されています。