Chainerによるニューラルネットワーク

Chainer

chainerの最大の特徴は、ネットワーク構築と学習を同時に行なう「Define-by-Run」方式を採用している点

データを処理しながら、計算グラフを構築する
データが流れるたびに構造を変更できるため、動的なネットワーク構造ができる

計算グラフとは、計算過程をグラフで表現したもの。

Chainerでは、計算グラフをフォーワード時に毎回生成している。
chainer.Variableやchainer.Functionにおけるchainer.functions.relu()やchainer.function.concat()などのオブジェクトが計算グラフにおけるノードとなる。

Chainクラス

Chainクラスは、Variable、Function、Linkといった構成要素を統合/管理し、ニューラルネットワークモデルを定義するためのクラス

このChainクラスによって、ニューラルネットの最大の特徴である合成可能性が実現される

Chainクラスを継承して作成したネットワーク構造のインスタンスをモデルオブジェクトとして使用する。

Linkクラス

Linkクラスは、全結合層などの重みやバイアスなどのパラメータを持つネットワーク構成部品を提供するクラス
Linkクラスは計算グラフにおいて、chainer.Functionとchainer.Parameterを含んだ複数ノードとして表示される。

chainer.linksモジュールではモデルを構築するための様々な関数が提供されている。
Linkオブジェクトは、chainer.functionsモジュールとは異なりパラメータを保持する。

Functionクラス

Functionクラスの主な機能は、関数をバックワード時にグラフとして追跡可能とすること、およびフォワード、バックワードの具体的な処理の実行

関数が、Variableオブジェクトに適用されたとき、forwardメソッドは入力変数をinputsとして保持し、backwardメソッドで使用する。
forwardメソッドからの出力は再びVariaableオブジェクトになり、後続のFunctionクラスへ入力され、再びFunctionとValiableオブジェクトとの関連付けを行なう

chainer,function.Functionクラスを継承して独自処理を作成できる。
Functionクラスを継承せずに作成した関数でVariableオブジェクトを経由しないで計算を行った場合、計算グラフのノードとして追跡されないためそこで計算グラフが途切れてしまう。

Optimizeクラス

Optimizerクラス
Optimizerクラスは、最適化メソッドの基本的な機能を提供する
Optimizerクラスを使用し、パラメータを最適化するには2つのステップが必要

  1. setupメソッドを用いて、Linkクラス内のパラメータを登録
  2. updateメソッドwを用いてパラメータを更新

パラメータの更新はロス関数に基づいて実行される。

setupについては、次のコードを参照
updateについては、トレーニングループにコメントを入れている。

Chainerプログラムと解説

ここで紹介するプログラムは、以下のサイトから引用しています。
https://github.com/chainer/chainer/blob/master/examples/mnist/train_mnist.py
https://qiita.com/elm200/items/82306f23712bb5a1c9b1

インポートするもの

import numpy as np
import chainer
from chainer import Chain, Variable, optimizers
import chainer.functions as F
import chainer.links as L

ネットワーク構築

class MLP(chainer.Chain):
    
    def __init__(self, n_units, n_out):
        super(MLP, self).__init__()
        with self.init_scope():
            self.l1 = L.Linear(None, n_units)
            self.l2 = L.Linear(None, n_units)
            self.l3 = L.Linear(None, n_out)

    def forward(self, x):
        h1 = F.relu(self.l1(x))
        h2 = F.relu(self.l2(h1))
        return self.l3(h2)

accuracyの計算

https://qiita.com/elm200/items/82306f23712bb5a1c9b1を参照した。
正解率と損失を計算する関数check_accuracyを定義する。

def check_accuracy(model, xs, ts):
    ys = model(xs)
    loss = F.softmax_cross_entropy(ys, ts)
    ys = np.argmax(ys.data, axis=1)
    cors = (ys == ts)
    num_cors = sum(cors)
    accuracy = num_cors / ts.shape[0]
    return accuracy, loss

データセットを読み込む

train, test = chainer.datasets.get_mnist()
xs, ts = train._datasets
txs, tts = test._datasets

インスタンス生成

model = MLP(50, 10)

最適化アルゴリズム(optimizer)の選択

#最適化アルゴリズムの選択
optimizer = chainer.optimizers.Adam()
#setupメソッド
optimizer.setup(model)

学習

トレーニングループとは、入力データとなるミニバッチの作成からパラメータの更新までのディープラーニングの学習に関する一連の操作を指す。

#エポック処理
for i in range(epoch):
    #ミニバッチ
    for j in range(600):
        #勾配の初期化
        model.zerograds()
        x = xs[(j * bm):((j + 1) * bm)]
        t = ts[(j * bm):((j + 1) * bm)]
        t = Variable(np.array(t, "i"))
        
        #フォワード処理
        y = model(x)
        
        #lossの計算
        loss = F.softmax_cross_entropy(y, t)
        
        ##ロスの勾配計算(バックワード処理)
        loss.backward()

        #パラメータの更新
        optimizer.update()
        
    accuracy_train, loss_train = check_accuracy(model, xs, ts)
    accuracy_test, _           = check_accuracy(model, txs, tts)
    
    print("Epoch %d loss(train) = %f, accuracy(train) = %f, accuracy(test) = %f" % (i + 1, loss_train.data, accuracy_train, accuracy_test))

プログラム全部

import numpy as np
import chainer
from chainer import Chain, Variable, optimizers
import chainer.functions as F
import chainer.links as L

class MLP(chainer.Chain):
    
    def __init__(self, n_units, n_out):
        super(MLP, self).__init__()
        with self.init_scope():
            self.l1 = L.Linear(None, n_units)
            self.l2 = L.Linear(None, n_units)
            self.l3 = L.Linear(None, n_out)

    def forward(self, x):
        h1 = F.relu(self.l1(x))
        h2 = F.relu(self.l2(h1))
        return self.l3(h2)


def check_accuracy(model, xs, ts):
    ys = model(xs)
    loss = F.softmax_cross_entropy(ys, ts)
    ys = np.argmax(ys.data, axis=1)
    cors = (ys == ts)
    num_cors = sum(cors)
    accuracy = num_cors / ts.shape[0]
    return accuracy, loss

train, test = chainer.datasets.get_mnist()
xs, ts = train._datasets
txs, tts = test._datasets

model = MLP(50, 10)

optimizer = chainer.optimizers.Adam()
optimizer.setup(model)

epoch = 100
bm = 100

#エポック処理
for i in range(epoch):
    #ミニバッチ
    for j in range(600):
        model.zerograds()
        x = xs[(j * bm):((j + 1) * bm)]
        t = ts[(j * bm):((j + 1) * bm)]
        t = Variable(np.array(t, "i"))
        y = model(x)
        loss = F.softmax_cross_entropy(y, t)
        loss.backward()
        optimizer.update()
        
    accuracy_train, loss_train = check_accuracy(model, xs, ts)
    accuracy_test, _           = check_accuracy(model, txs, tts)
    
    print("Epoch %d loss(train) = %f, accuracy(train) = %f, accuracy(test) = %f" % (i + 1, loss_train.data, accuracy_train, accuracy_test))

参考

https://github.com/chainer/chainer/blob/master/examples/mnist/train_mnist.py
https://qiita.com/elm200/items/82306f23712bb5a1c9b1


おすすめ