MXNet チュートリアル : トレーニングと推論 Module

MXNet チュートリアル : トレーニングと推論 Module (翻訳/解説)
翻訳 : (株)クラスキャット セールスインフォメーション
日時 : 02/18/2017

* 本ページは、MXNet 本家サイトの Training and Inference Module Tutorial を翻訳した上で適宜、補足説明したものです:
    http://mxnet.io/tutorials/python/module.html

Module によるトレーニングと推論 – Module インターフェイスによる単純な深層ニューラルネットワーク。Module パッケージは事前定義されたネットワークを実行するための中間レベルと高レベルなインターフェイスを提供します。

* サンプルコードの動作確認はしておりますが、適宜、追加改変しています。
* このページのグラフ画像はすべて自作しています。

 

トレーニングや推論のために一般的に使用されるコードを module (あるいは短く mod) パッケージ内にモジュール化しました。このパッケージは事前定義されたネットワークを実行するための中間レベルと高レベルなインターフェイスを提供します。

 

基本的な使い方

準備

このチュートリアルでは、10 クラスと人工的なデータセットのための単純な MLP (multilayer perception) を使用します。

import mxnet as mx
from data_iter import SyntheticData

# mlp
net = mx.sym.Variable('data')
net = mx.sym.FullyConnected(net, name='fc1', num_hidden=64)
net = mx.sym.Activation(net, name='relu1', act_type="relu")
net = mx.sym.FullyConnected(net, name='fc2', num_hidden=10)
net = mx.sym.SoftmaxOutput(net, name='softmax')
# synthetic 10 classes dataset with 128 dimension 
data = SyntheticData(10, 128)
mx.viz.plot_network(net)

Module を作成する

もっとも広く使われるモジュール・クラスは Module で、これは Symbol と一つまたはそれ以上の Executor をラップします。

以下を指定することで module を構築します :

  • symbol : ネットワーク Symbol
  • context : 実行のためのデバイス (or デバイスのリスト)
  • data_names : データ変数名のリスト
  • label_names : ラベル変数名のリスト

最後の2つの引数についてのより詳細は Modue API を参照してください。
ここでは $data$ と命名された一つのデータ data と、$softmax\_label$ という名前の一つのラベルだけがあり、これは $SoftMaxOutput$ 演算子のために指定された名前 softmax に従って自動的に命名されます。

mod = mx.mod.Module(symbol=net, 
                    context=mx.cpu(),
                    data_names=['data'], 
                    label_names=['softmax_label'])

トレーニング、予測、そして評価

Module は高レベル API をトレーニング、予測そして評価のために提供します。module を fit させるためには、幾つかの DataIters とともに単に fit 関数を呼ぶだけです。

# @@@ AUTOTEST_OUTPUT_IGNORED_CELL
import logging
logging.basicConfig(level=logging.INFO)

batch_size=32
mod.fit(data.get_iter(batch_size), 
        eval_data=data.get_iter(batch_size),
        optimizer='sgd',
        optimizer_params={'learning_rate':0.1},
        eval_metric='acc',
        num_epoch=5)

訳注 : 以下は実際の出力です。

INFO:root:Epoch[0] Train-accuracy=0.112500
INFO:root:Epoch[0] Time cost=0.162
INFO:root:Epoch[0] Validation-accuracy=0.084375
INFO:root:Epoch[1] Train-accuracy=0.109375
INFO:root:Epoch[1] Time cost=0.065
INFO:root:Epoch[1] Validation-accuracy=0.109375
INFO:root:Epoch[2] Train-accuracy=0.100000
INFO:root:Epoch[2] Time cost=0.038
INFO:root:Epoch[2] Validation-accuracy=0.209375
INFO:root:Epoch[3] Train-accuracy=0.178125
INFO:root:Epoch[3] Time cost=0.062
INFO:root:Epoch[3] Validation-accuracy=0.293750
INFO:root:Epoch[4] Train-accuracy=0.318750
INFO:root:Epoch[4] Time cost=0.059
INFO:root:Epoch[4] Validation-accuracy=0.521875

module で予測するためには、単に $DataIter$ で $preict()$ を呼び出します。それは全ての予測結果を集めて返します。

y = mod.predict(data.get_iter(batch_size))
'shape of predict: %s' % (y.shape,)
'shape of predict: (320, 10)'

他の便利な API は、予測結果がメモリに fit するには大きすぎる場合のための予測のための iter_predict です :

# @@@ AUTOTEST_OUTPUT_IGNORED_CELL
for preds, i_batch, batch in mod.iter_predict(data.get_iter(batch_size)):
    pred_label = preds[0].asnumpy().argmax(axis=1)
    label = batch.label[0].asnumpy().astype('int32')
    print('batch %d, accuracy %f' % (i_batch, float(sum(pred_label==label))/len(label)))
batch 0, accuracy 0.500000
batch 1, accuracy 0.437500
batch 2, accuracy 0.406250
batch 3, accuracy 0.375000
batch 4, accuracy 0.500000
batch 5, accuracy 0.625000
batch 6, accuracy 0.406250
batch 7, accuracy 0.406250
batch 8, accuracy 0.500000
batch 9, accuracy 0.468750

予測出力を必要とせず、テストセット上で評価することだけが必要であるならば、DataIter と EvalMetric で score() 関数を呼ぶことができます :

# @@@ AUTOTEST_OUTPUT_IGNORED_CELL
score = mod.score(data.get_iter(batch_size), ['mse', 'acc'])
list(score) # zip object, python 3
[('mse', 26.633767890930176), ('accuracy', 0.515625)]

Save と Load

checkpoint コールバックを使うことで各トレーニング epoch における module パラメータを保存できます。

# @@@ AUTOTEST_OUTPUT_IGNORED_CELL
# construct a callback function to save checkpoints
model_prefix = 'mx_mlp'
checkpoint = mx.callback.do_checkpoint(model_prefix)

mod = mx.mod.Module(symbol=net)
mod.fit(data.get_iter(batch_size), num_epoch=5, epoch_end_callback=checkpoint)
INFO:root:Epoch[0] Train-accuracy=0.084375
INFO:root:Epoch[0] Time cost=0.083
INFO:root:Saved checkpoint to "mx_mlp-0001.params"
INFO:root:Epoch[1] Train-accuracy=0.109375
INFO:root:Epoch[1] Time cost=0.062
INFO:root:Saved checkpoint to "mx_mlp-0002.params"
INFO:root:Epoch[2] Train-accuracy=0.109375
INFO:root:Epoch[2] Time cost=0.037
INFO:root:Saved checkpoint to "mx_mlp-0003.params"
INFO:root:Epoch[3] Train-accuracy=0.140625
INFO:root:Epoch[3] Time cost=0.049
INFO:root:Saved checkpoint to "mx_mlp-0004.params"
INFO:root:Epoch[4] Train-accuracy=0.118750
INFO:root:Epoch[4] Time cost=0.044
INFO:root:Saved checkpoint to "mx_mlp-0005.params"

保存された module パラメータをロードするためには、load_checkpoint 関数を呼び出します。それは Symbol と関連付けられたパラメータをロードします。そしてロードされたパラメータを module にセットできます。

sym, arg_params, aux_params = mx.model.load_checkpoint(model_prefix, 3)
print(sym.tojson() == net.tojson())

# assign the loaded parameters to the module
mod.set_params(arg_params, aux_params)
True

あるいは保存された checkpoint からトレーニングを再開することを望むだけならば、set_params() を呼び出す代わりに、ロードされたパラメータを渡して、fit() を直接呼び出すこともできます、その結果 random から初期化する代わりにfit() はこれらのパラメータから開始すべきことを知り得ます。また begin_epoch を設定すれば fit() は以前の保存された epoch から再開したことが分かります。

# @@@ AUTOTEST_OUTPUT_IGNORED_CELL
mod = mx.mod.Module(symbol=sym)
mod.fit(data.get_iter(batch_size),
        num_epoch=5,
        arg_params=arg_params, 
        aux_params=aux_params,
        begin_epoch=3)
INFO:root:Epoch[3] Train-accuracy=0.131250
INFO:root:Epoch[3] Time cost=0.076
INFO:root:Epoch[4] Train-accuracy=0.112500
INFO:root:Epoch[4] Time cost=0.063

 

計算 “機械” としての Module

既に基本トレーニングと推論のためにどのように module 化するかを見ました。今 module のより柔軟な使い方を示していきます。

module は計算コンポーネントを表しています。module の設計目的は計算 “機械” を抽象化すること、Symbol プログラムとデータを受け入れることで、 forward, backward、パラメータ更新、etc. が実行可能になります。API を簡単に柔軟に使用できることを目標とし、特に 命令型 API を複数の module と一緒に動作させるために使う必要がある (e.g. stochastic depth network) 時の場合です。

module は幾つかのステートを持ちます :

  • 初期ステート, Initial state – メモリはまだ割り当てられておらず、計算への用意はまだです。
  • バインドされる, Binded. – 入力、出力のための shape、そしてパラメータは全て既知となり、メモリが割り当てられ、計算のための準備ができています。
  • パラメータが初期化される, Parameter initialized. – module のためのパラメータ、パラメータ初期化前の計算は未定義出力という結果になるでしょう。
  • オプティマイザーが初期化される, Optimizer installed. – オプティマイザーは module にインストールされます。この後、勾配が計算された (foward-backward) 後で module パラメータはオプティマイザーに従って更新されます。

次のコードは単純化された $fit()$ を実装しています。ここで initializer, optimizer, そして metric を含む他のコンポーネントを使いました、これらは他の notebooks で説明されます。

# @@@ AUTOTEST_OUTPUT_IGNORED_CELL
# initial state
mod = mx.mod.Module(symbol=net)

# bind, tell the module the data and label shapes, so
# that memory could be allocated on the devices for computation
train_iter = data.get_iter(batch_size)
mod.bind(data_shapes=train_iter.provide_data, label_shapes=train_iter.provide_label)

# init parameters
mod.init_params(initializer=mx.init.Xavier(magnitude=2.))

# init optimizer
mod.init_optimizer(optimizer='sgd', optimizer_params=(('learning_rate', 0.1), ))

# use accuracy as the metric
metric = mx.metric.create('acc')

# train one epoch, i.e. going over the data iter one pass
for batch in train_iter:
    mod.forward(batch, is_train=True)       # compute predictions
    mod.update_metric(metric, batch.label)  # accumulate prediction accuracy
    mod.backward()                          # compute gradients
    mod.update()                            # update parameters using SGD
    
# training accuracy
print(metric.get())
('accuracy', 0.465625)

演算子のほか、module は多くの有用な情報を提供します。

基本名 :

  • data_names: 必要とされるデータの名前を示す文字列リスト。
  • output_names: 出力の名前を示す文字列リスト。

ステート情報

  • binded: bool, 計算のために必要なメモリ・バッファが割り当てられたかを示します。
  • for_training: module がトレーニングのためにバインドされたか (if binded)。
  • params_initialized: bool, このモジュールのパラメータが初期化されたかを示します。
  • optimizer_initialized: bool, オプティマイザーが定義され初期化されたかを示します。
  • inputs_need_grad: bool, 入力データに関する勾配が必要であるかを示します。composition of modules を実装する時に有用でしょう。

入力/出力情報

  • data_shapes: (name, shape) のリスト。理論的には、メモリが割り当てられてからは、データ配列を直接提供できます。しかしデータ並列化の場合、データ配列は外部の世界から見えるのと同じ shape ではないかもしれません。
  • label_shapes: (name, shape) のリスト。module がラベルを必要としないならば (e.g. it does not contains a loss function at the top)、あるいは module がトレーニングのためにバインドされないならば、これは [] かもしれません。
  • output_shapes: module の出力のための (name, shape) リスト。

パラメータ (for modules with parameters)

  • get_params(): タプル (arg_params, aux_params) を返します。それらの各々は NDArray マッピングへの名前の辞書です。それらの NDArray はいつも CPU 上にあります。計算のために使われる実際のパラメータは他のデバイス (GPUs) にあるかもしれませんが、この関数は最新のパラメータ(のコピー)を取得します。
  • get_outputs(): 以前の forward 演算の出力を得る。
  • get_input_grads(): 以前の backward 演算において計算された入力に関する勾配を得ます。
print((mod.data_shapes, mod.label_shapes, mod.output_shapes))
print(mod.get_params())
([DataDesc[data,(32, 128),<class 'numpy.float32'>,NCHW]], [DataDesc[softmax_label,(32,),<class 'numpy.float32'>,NCHW]], [('softmax_output', (32, 10))])
({'fc1_bias': <NDArray 64 @cpu(0)>, 'fc2_weight': <NDArray 10x64 @cpu(0)>, 'fc2_bias': <NDArray 10 @cpu(0)>, 'fc1_weight': <NDArray 64x128 @cpu(0)>}, {})

 

More on Modules

Module は新しい module の実装を単純化します。例えば :

  • SequentialModule は複数の module を一緒に連鎖できます。
  • BucketingModule は bucketing を処理することが可能で、様々な長さの入力と出力のために有用です。
  • PythonModule は、カスタマイズされた modules を実装することをユーザに簡単にするために多くの API を empty 関数として実装しています。

module API を使用したコードサンプルのリストのためには example/module をまた参照してください。

 

実装

module は python で実装され、python/mxnet/module にあります。

 

Futher Readings

 

Next Steps

 

以上