データサイエンティスト(仮)

元素粒子論博士。今はデータサイエンティスト(仮)。

GluonでDeep Learning:Tutorialを眺めてみる

導入

2017年10月12日(現地時間)に、MicrosoftAWSがGluonというDeep Learningのライブラリを公開しました。
www.itmedia.co.jp

Gluonとは、自然界の基本的な相互作用の一つ「強い相互作用」を伝える素粒子のことです。glue(のり)にちなんでのりのように強く粒子を結びつけるといった感覚で名付けられました。ちなみに、ゲージ群でいうとSU(3)の随伴表現にアサインされます()。

立ち位置としては、TheanoやTensorflowに対するKerasに近い印象です。AWSがサポートしてるDeep LearningフレームワークであるMXNetをサポートしており、コーディングを単純化・簡単化するようなものとなっています。Kerasと異なる点は、MXNetの一部のようになっている点でしょうか。Gluonは、インポートするのはあくまでMXNetで、その中のライブラリとしてGluonがある、といった感じになっています(触ってみた印象です)。

現在、Github上でDocumentやTutorialが公開されています。
github.com

こちらを見れば雰囲気はわかると思いますが、日本での紹介がまだ見当たらなかったので、眺めるついでに紹介をしたいと思います。

インストール方法

anacondaなどでPythonを導入していると、pipですぐインストールできます。ただし、インストールするのはmxnetです。

$ pip install mxnet

おそらく問題なくインストールできると思います。

Tutorial:MNIST

Githubのページには、MNISTで多層パーセプトロンMLP)の実装例が載っています。
github.com

以下、Tutorialを眺めていきます。

ライブラリのインポート

mxnet、および必要なライブラリをインポートします。Gluonに対応するライブラリはgluonです。numpyは、Deep Learningフレームワークに入力するデータの型をnp.XX32にする、とかいったデータの扱いまわりで必要なので、一緒にインポートしています。

import mxnet as mx
from mxnet import gluon, autograd, ndarray
import numpy as np

使用データセット:MNIST

手書き数字の画像と数字のラベルがセットになっているオープンなデータセットであるMNISTを使います。手書き数字の画像は28×28ピクセルで、各ピクセルに0から255の値が入っている2次元配列です(要はグレースケール)。各ピクセルの値を最大1になるように正規化の処理をし、型もnp.float32とします。このような処理をしないと、フレームワークによっては学習がうまく進まなかったりエラーになったりします。また、後で処理しますが、今回はMLPを想定しているので、画像データは28×28=784次元のベクトルデータとします。

Tutorialでは、MNISTのデータをmxnetのサイトからダウンロードしてきています。

train_data = mx.gluon.data.DataLoader(mx.gluon.data.vision.MNIST(train=True, transform=lambda data, label: (data.astype(np.float32)/255, label)),
                                      batch_size=32, shuffle=True)
test_data = mx.gluon.data.DataLoader(mx.gluon.data.vision.MNIST(train=False, transform=lambda data, label: (data.astype(np.float32)/255, label)),
                                     batch_size=32, shuffle=False)

mx.gluon.data.DataLoaderですが、ロードしてきた(or 読み込んだ)データに対して、ミニバッチに分割してくれるメソッドです。ミニバッチのサイズや、シャッフルしてミニバッチにデータをアサインするかなどを指定できます。

trainデータは60000万枚、testデータは10000枚の画像データになっています。

ネットワーク構築

この部分がGluonの売りの一つになると考えられます。Gluonでは、Sequential()というメソッドを定義し、そこに積み木のように層を足していく感覚でネットワークのモデルを構築できます。KerasやCognitive Toolkitと似ていますね。

# モデルの初期化:Sequentialメソッドをインスタンス化
net = gluon.nn.Sequential()
# addメソッドで層を追加する
with net.name_scope():
    net.add(gluon.nn.Dense(128, activation="relu"))
    net.add(gluon.nn.Dense(64, activation="relu")) 
    net.add(gluon.nn.Dense(10)) # 出力層

Decse()は全結合層を足すメソッドで、ここでは引数としてユニットの数と活性化関数を指定しています。1層目と2層目のユニットの数はそれぞれ128と64で、活性化関数は両方ともReLUです。出力層に関しては、0~9の10種類の数字のクラス分類のタスクを考えるので、出力層の数は10とします。

出力層に関してコメントですが、活性化関数は学習の際に損失関数とセットで指定する思想のようです。ここはCognitive Toolkitと似ています。この思想のキモチは、モデルは「学習されるパラメータがある部分」ということなんだろうと個人的には思っています。クラス分類をする際のソフトマックス層は学習するパラメータはないので、ネットワークの出力を目的関数へ変換する際の一部分という位置づけになっているのだと思います。

学習の設定

ここはかなりCognitive Toolkit的です。パラメータの初期化、損失関数、最適化手法などを決め、それをTrainerというメソッド(どういう学習をするかのスケジューリングを司るもの)に放り込みます。

# パラメータ初期化:標準偏差0.05の正規乱数を利用
net.collect_params().initialize(mx.init.Normal(sigma=0.05))

# 損失関数はクロスエントロピー
softmax_cross_entropy = gluon.loss.SoftmaxCrossEntropyLoss()

# 最適化手法はSGDで、学習率は0.1
trainer = gluon.Trainer(net.collect_params(), 'sgd', {'learning_rate': .1})

これでネットワークのパラメータを学習する準備ができました。

学習

先程までの設定に従って学習していきます。何回データ学習に使うかの回数であるepoch数は10にしています。前にコメントしましたが、入力はミニバッチごとに投入、入力の形式はreshapeにより1次元のベクトルに変換しています。誤差逆伝搬に関係する微分操作などは、autogradが司っています。

# エポック数は10
epochs = 10
for e in range(epochs):
    for i, (data, label) in enumerate(train_data):
        data = data.as_in_context(mx.cpu()).reshape((-1, 784))
        label = label.as_in_context(mx.cpu())
        with autograd.record(): # 誤差逆伝搬をやる
            output = net(data) 
            loss = softmax_cross_entropy(output, label)
            loss.backward()
        trainer.step(data.shape[0])
        curr_loss = ndarray.mean(loss).asscalar()
    print("Epoch {}. Current Loss: {}.".format(e, curr_loss))

# 出力
Epoch 0. Current Loss: 0.08900555968284607.
Epoch 1. Current Loss: 0.2502848505973816.
Epoch 2. Current Loss: 0.2667727470397949.
Epoch 3. Current Loss: 0.017404090613126755.
Epoch 4. Current Loss: 0.006391774397343397.
Epoch 5. Current Loss: 0.07984966039657593.
Epoch 6. Current Loss: 0.009250413626432419.
Epoch 7. Current Loss: 0.062348250299692154.
Epoch 8. Current Loss: 0.06544484943151474.
Epoch 9. Current Loss: 0.0023109368048608303.

実行すると、上のような感じでもりもり損失関数が小さくなっていきます。

その他、Gluonでできそうなこと

以上がTutorialになります。Tutorialだけだと予測も予実比較もしていないので、ネットワークの性能が知りたい場合は適宜追加で書く必要があります。

Documentを見る限りだと、MLP以外にもネットワークのモデルはサポートしているようです(今の時代当たり前だと思いますが)。ざっと見てみると

  • 畳み込みニューラルネットワーク(CNN)
    • 1, 2, 3次元の畳み込み層
    • プーリング(Max, Avg, GlobalMax)
    • 他テクニック
      • BatchNormalizationやDropout
      • ActivationにLeakyReLUがある
  • 再帰ニューラルネットワーク(RNN)
    • RNN
    • LSTM
    • GRU
    • Cell
      • RNNCell, GRUCell, RecurrentCell, SequentialRNNCell, BidirectionalCell, DropoutCell, ZoneoutCell, ResidualCell

など、代表的なネットワークは作れる状態のようです。

Tutorialの内容の通り、比較的簡単にDeep Learningを試せるようなライブラリとなっています。また、今はMXNetだけですが、今後はCognitive ToolkitやCaffe2もサポートする予定とのことです。というわけで、複数のフレームワークを横断して使いやすいものになることが期待されるため、これからDeep Learningをやりたいと考えている人は検討するとよいのかもしれません。