人工知能に関する断創録

このブログでは人工知能のさまざまな分野について調査したことをまとめています(更新停止: 2019年12月31日)

Chainerによる多層パーセプトロンの実装

これまでDeep LearningのアルゴリズムをTheanoで実装してきた(2015/4/29)けれど、ここらで巷で大人気のライブラリChainerにも手を出してみた。Theanoの勉強を始めたあとすぐにChainerが公開された(2015/6/9)がユーザや情報が増えるまで待っていた感じ(笑)最近はコードや実験結果などを公開してくれる人が増えてきたので非常に参考になっている。目についたものはてぶに登録しているので、興味を持った手法はがしがし勉強して追試していきたい。

Chainerのバージョンは1.3.2をベースにしている。1.3からPyCUDA/scikit-cudaを独自ライブラリのCuPyに置き換えたとのことで、以前のコードは少し修正しないと動かないようだ。その分、1.3からはインストールがシンプルになっていてとてもうれしい。1.1のころは、Chainerと直接関係ないPyCUDAとscikit-cudaのところで意味不明なエラーが頻発してて泣きそうだった・・・

今回は、最初ということでChainerに付属している多層パーセプトロンのコードを動かしてみるところから始めた。そろそろ食傷気味ではあるが、MNISTの数字画像データを分類するというおなじみのタスク。

実装のデフォルトの設定は、

  • 隠れ層の数は2つ
  • 隠れ層のユニット数は1000(だけど何か気持ち悪い(笑)ので1024にした)
  • 活性化関数はReLU
  • Dropout使用
  • 最適化はAdam

となっている。自分の理解もかねてコメントを追加したコードがこちら。dataモジュールはChainer付属のものをそのままコピーした。このモジュールは自分で書かずにscikit-learnのfetch_mldata()を使うともっと簡単に書けそう。

コードはサンプルとほとんど同じだけれど、一部変えている。

  • forward()で訓練時は損失のみ返して、テスト時は精度のみを返すように変更
  • 各エポックのテスト精度をファイルに出力するコードを追加
  • 実行時間を出力

1つめの変更はIntroduction to Chainerのp.32を参考にした。前にTheanoで実装したとき(2015/6/18)も訓練時は損失のみ返し、テスト時は精度のみを返していたのでこっちのほうがしっくりくる。

CPUとGPUの切り替えはスクリプトのオプションに指定すればよい。うちのマシンはGPUが1枚しかないので0番目を使っている。最初、GPUを使うときは正の値を入れるのだと勘違いして--gpu 1としてよくわからないエラーが出てはまっていた。

python train_mnist.py          # for CPU
python train_mnist.py --gpu 0  # for GPU

CPUだと538秒、GPUだと90秒で実行できた。

ただ動かすだけだけだとつまらないので設定をいろいろ変えて収束がどのように変わるか調べてみよう。テスト精度をファイルに出力しているのでそれをプロットしてみた。すべてのグラフは横軸がエポック、縦軸がテスト精度にしている。一つだけ変えて残りはデフォルトの設定のまま。

隠れ層の数を変えたら?

f:id:aidiary:20151005220623p:plain

隠れ層の数が1のとき最大のように見える。4や5にすると明らかに(0.005なので誤差の範囲かな?)悪化している。隠れ層の数が多いとモデルが複雑化するため過学習しやすいのかもしれない。隠れ層の数が多ければ多いほどよいというものではないようだ。

隠れ層のユニット数を変えたら?

f:id:aidiary:20151005220632p:plain

隠れ層のユニット数が大きいほど収束が速い(エポックの最初の方で最高精度に達する)傾向にありそう。デフォルトの1024くらいから精度が飽和しているのでこれ以上増やしても意味がなさそうだ。

活性化関数を変えたら?

よく使われるsigmoid、tanh、ReLUの3つを比較してみた。マニュアルを見るともっとあるみたいだけど今回は省略。

f:id:aidiary:20151005220639p:plain

ReLUの収束が明らかに早い。でも秒で測った実行時間はほとんど変わらない。sigmoid(85秒)、tanh(84秒)、ReLU(89秒)。なぜか実装が単純なはずのReLUが遅かった。

Dropoutの有無

f:id:aidiary:20151005220646p:plain

Dropoutを使った方が最終的な精度が少しよいかな?という程度。もっと複雑なモデルだと効いてくるのかも。

Optimizerを変えたら?

最後にChainerで実装されているSGD(確率的勾配降下法)、AdaGrad、Adamの3種類のOptimizerを比較してみた。Optimizerのパラメータはすべてデフォルト。

f:id:aidiary:20151005220702p:plain

よく使われる確率的勾配降下法に比べてAdamは収束がすごく速い!これからはAdamを使おう。アルゴリズムはよくわかってないけど・・・

Theanoに比べるとネットワーク構造や設定が簡単に変えられるので非常に便利だと感じた。その分、更新するパラメータや更新式がブラックボックス化されていてもやもや感もある。Theanoの実装をもとにアルゴリズムをしっかり勉強して、その後にChainerで実装がよさそう。というわけでTheanoの実装もがんばって最後まで続けたい(笑)Autoencoderで止まっちゃってるな~。

参考