Theanoによる積層自己符号化器の実装
Theanoによる雑音除去自己符号化器の実装(2015/12/9)の続き。今回は自己符号化器を積み上げて積層自己符号化器を実装した。
多層パーセプトロンは隠れ層の数を増やす(Deepにしていく)とより複雑なモデルを表現できるようになるが、誤差が伝播されずに重みが更新されなくなる勾配消失問題(Vanishing gradient problem)が生じる。この問題を解決するため多層パーセプトロンの重みの初期値をランダムに設定するのではなくより良い重みを設定しておく事前学習(pre-training)というテクニックが提案されている。
このより良い重みを自己符号化器(2015/12/3)を順に積み上げることで設定するのが積層自己符号化器(Stacked autoencoder)。自己符号化器で初期値を決めたらあとは普通に多層パーセプトロン(2015/6/18)と同じ誤差逆伝播法で重みを更新すればよい。この重み更新はfine-tuningと呼ばれている。
積層自己符号化器の2つの側面
積層自己符号化器の構造は
- 自己符号化器を複数積み上げたもの
- 複数の隠れ層を持つ多層パーセプトロン
という二つの側面がある。たとえば、下のようなニューラルネットを考えると自己符号化器が3つ(ただし、符号化部分のみで復号化部分は省略)という側面と隠れ層が3つの多層パーセプトロンという側面がある。出力層だけは少し特殊。ニューラルネットで何らかの分類をしたいときは多層パーセプトロン(2015/6/18)と同じようにロジスティック回帰層をくっつければよい。
(注)入力のユニット数より隠れ層のユニット数が小さくないと自己符号化器として意味がないと理解していたがチュートリアルでは入力が784で隠れ層が1000と大きくなっていたため上の図でもそのようにしている。
積層自己符号化器の学習は、pre-trainingフェーズで重みの初期値を求めて、fine-tuningフェーズで重みを更新するという2つのフェーズからなる。最初のpre-trainingではこのニューラルネットを自己符号化器を積み上げたものとみなして入力を再現するような重みを教師なしで1層ずつ求める。次のfine-tuningでは自己符号化器で求めた重みをそのまま引き継いで、多層パーセプトロンとみなして教師ありで重みを更新する。出力層の重みは自己符号化器に含まれないためランダムに初期化し、fine-tuningでのみ更新する。
というのが私が理解した積層自己符号化器の概要。というわけでDeep Learning Tutorialを参考にTheanoによる実装を詳しくみていきたい。これまで自己符号化器と書いてきたが実装では過学習に強い雑音除去自己符号化器(2015/12/9)を使っている。
ソースコード全体はここ。以下のまとめではポイントになる部分のコードの抜粋しか載せていない。
ネットワークの構成・重みの共有
積層自己符号化器の実装では、自己符号化器と多層パーセプトロンの間で重みを共有するのが大きなポイント。たとえば、先の図の赤色の重み (W, b) はAutoencoder 0とHidden Layer 0が共有し、黄色の重み (W, b) はAutoencoder 1とHidden Layer 1が共有していることを意味する。
このように重みを共有しておけばpre-trainingの自己符号化器で求めた重みの初期値をfine-tuningの多層パーセプトロンに速やかに(というか何もしなくても)引き継げる。Deep Learning Tutorialの実装では、多層パーセプトロンの隠れ層の重みを自己符号化器が参照する方法で重みの共有が実装されている(逆でもよいか?)。
ネットワークを構成するコードは隠れ層の数(self.n_layers
)を可変にするため下のようなforループで書かれている。入力層に近い方から順番に構築して積み上げていることがわかる。HiddenLayer
クラスは多層パーセプトロン(2015/6/18)で実装したものをDenoisingAutoencoder
は雑音除去自己符号化器(2015/12/3)で実装したものを再利用している。pre-trainingではDenoisingAutoencoder
のコスト関数とパラメータ更新式をfine-tuningではHiddenLayer
のコスト関数とパラメータ更新式をそれぞれ使う。
# ネットワークを構築 # 隠れ層の数だけループして積み上げていく for i in xrange(self.n_layers): # ユニット数 if i == 0: input_size = n_ins else: input_size = hidden_layers_sizes[i - 1] # 隠れ層への入力データ if i == 0: layer_input = self.x else: layer_input = self.hidden_layers[-1].output # 多層パーセプトロンの隠れ層 # fine-tuningで重みを更新するため hidden_layer = HiddenLayer(rng=numpy_rng, input=layer_input, n_in=input_size, n_out=hidden_layers_sizes[i], activation=T.nnet.sigmoid) self.hidden_layers.append(hidden_layer) self.params.extend(hidden_layer.params) # 自己符号化器だが重みは多層パーセプトロンの隠れ層と共有 autoencoder_layer = DenoisingAutoencoder(numpy_rng=numpy_rng, theano_rng=theano_rng, input=layer_input, n_visible=input_size, n_hidden=hidden_layers_sizes[i], W=hidden_layer.W, bhid=hidden_layer.b) self.autoencoder_layers.append(autoencoder_layer) # MNISTの分類ができるように最後にロジスティック回帰層を追加 self.log_layer = LogisticRegression( input=self.hidden_layers[-1].output, n_in=hidden_layers_sizes[-1], n_out=n_outs) self.params.extend(self.log_layer.params)
self.hidden_layers
のリストはforループを回すたびに隠れ層のオブジェクトが追加されていく。そのためself.hidden_layers[-1]
で一つ前の隠れ層にアクセスできる。自己符号化器の重みとバイアスは隠れ層の重みとバイアスを設定して共有している。そのため自己符号化器のW
とb
はself.params
に追加する必要がない。
8.4 Tips and Tricksを読むとこの実装は効率が悪いとのこと。2つ目や3つ目の自己符号化器への入力を得たいときすでに前の層において出力を計算済みなのにそれを保存していないため再度入力から順にフィードフォワードして出力を再計算する必要がある。ただフィードフォワードの計算は高速だし、GPUのメモリ不足の方が心配の種だから実装的にこれでよいのかもしれない。
pre-training関数の作成
次はpre-training。pre-trainingでは先のニューラルネットを自己符号化器を積み上げたものとみなして浅い層から順に訓練していく。チュートリアルの実装ではpre-trainingを行う関数のリスト(自己符号化器が3つのときは3つの関数がある)を生成して返すメソッドを定義している。
def pretraining_functions(self, train_set_x, batch_size): """自己符号化器を学習するpre-training用の関数リストを返す 教師なし学習なのでxのみを渡す""" # 学習に使うミニバッチのインデックス index = T.lscalar('index') # 複数の自己符号化器で異なる値を指定できるようにシンボル化する corruption_level = T.scalar('corruption') learning_rate = T.scalar('lr') batch_begin = index * batch_size batch_end = batch_begin + batch_size # 自己符号化器を学習する関数を生成 # 入力層に近い方から順番に追加する pretrain_functions = [] for autoencoder in self.autoencoder_layers: # 誤差と更新式を計算するシンボルを取得 cost, updates = autoencoder.get_cost_updates(corruption_level, learning_rate) fn = theano.function( inputs=[ index, # Paramにした引数を関数呼び出し時に与えるときはPython変数名ではなく、 # Tensorの引数の名前(corruption, lr)で指定できる theano.Param(corruption_level, default=0.2), theano.Param(learning_rate, default=0.1) ], outputs=cost, updates=updates, givens={ self.x: train_set_x[batch_begin:batch_end] } ) pretrain_functions.append(fn) return pretrain_functions
自己符号化器は教師なし学習なので引数にはtrain_set_x
のみでラベルは必要ない。自己符号化器が複数あるだけで訓練方法はTheanoによる自己符号化器の実装(2015/12/3)と同じで目新しい部分はない。自己符号化器ごとに独立に学習率と汚染度を設定できるようにinputs
に引数を与えているのが少し新しい。
Theanoのややこしい部分だがこの時点では自己符号化器の学習を行う関数リストを返すだけで実行はしない。
fine-tuning関数の作成
次にfine-tuning。自己符号化器の訓練が終わった後に実行される。ニューラルネットを多層パーセプトロンとみなして教師あり(分類ラベルを使う)で学習する。こちらも訓練を行う関数を返すだけで実行はしない。こちらも書き方が少し違うだけでTheanoによる多層パーセプトロンの実装(2015/6/18)とほぼ同じ。
def build_finetune_functions(self, datasets, batch_size, learning_rate): """fine-tuning用の関数を返す""" train_set_x, train_set_y = datasets[0] valid_set_x, valid_set_y = datasets[1] test_set_x, test_set_y = datasets[2] n_valid_batches = valid_set_x.get_value(borrow=True).shape[0] / batch_size n_test_batches = test_set_x.get_value(borrow=True).shape[0] / batch_size index = T.lscalar('index') gparams = T.grad(self.finetune_cost, self.params) updates = [ (param, param - gparam * learning_rate) for param, gparam in zip(self.params, gparams) ] train_model = theano.function( inputs=[index], outputs=self.finetune_cost, updates=updates, givens={ self.x: train_set_x[index * batch_size: (index + 1) * batch_size], self.y: train_set_y[index * batch_size: (index + 1) * batch_size] }, name='train') test_score_i = theano.function( inputs=[index], outputs=self.errors, givens={ self.x: test_set_x[index * batch_size: (index + 1) * batch_size], self.y: test_set_y[index * batch_size: (index + 1) * batch_size] }, name='test') valid_score_i = theano.function( inputs=[index], outputs=self.errors, givens={ self.x: valid_set_x[index * batch_size: (index + 1) * batch_size], self.y: valid_set_y[index * batch_size: (index + 1) * batch_size] }, name='validate') def valid_score(): """各ミニバッチのvalid誤差のリストを返す""" return [valid_score_i(i) for i in xrange(n_valid_batches)] def test_score(): """各ミニバッチのtest誤差のリストを返す""" return [test_score_i(i) for i in xrange(n_test_batches)] return train_model, valid_score, test_score
test_score_i
やvalid_score_i
のように_i
がついているのはindex
で指定した一つのミニバッチの誤差(スコア)を返すからみたいだ。実際は全ミニバッチの誤差をまとめて計算して誤差リストを返す関数を返している。
積層自己符号化器の訓練と評価
ネットワークの構築
最後にこれまでの関数を使ってモデルを訓練する流れを整理したい。まず、積層自己符号化器のオブジェクトを作る。これで先の図のようなニューラルネットが構築される。
sda = StackedDenoisingAutoencoder( numpy_rng, 28 * 28, hidden_layers_sizes, 10, corruption_levels)
例では、hidden_layers_sizes=[1000, 1000, 1000]
でcorruption_levels=[0.1, 0.2, 0.3]
となっている。入力が28x28=784ユニットなのに隠れ層が1000ユニットで自己符号化器が砂時計型にならないけれどよいのだろうか?
pre-training
まずはpre-trainingで重みの初期値を決める。
# Pre-training print "getting the pre-training functions ..." pretraining_functions = sda.pretraining_functions(train_set_x=train_set_x, batch_size=batch_size) print "pre-training the model ..." for i in xrange(sda.n_layers): # pre-trainingのエポック数は固定 for epoch in xrange(pretraining_epochs): c = [] for batch_index in xrange(n_train_batches): c.append(pretraining_functions[i](index=batch_index, corruption=corruption_levels[i], lr=pretrain_lr)) print "Pre-training layer %i, epoch %d, cost %f" % (i, epoch, np.mean(c))
最初のforループで浅い層から深い層へ順番に自己符号化器を学習していることがわかる。エポック数は固定でpretraining_epochs=15
となっている。初期値を決めるだけなので厳密な収束判定は行わずに割と適当でよいのだろうか。実行すると
building the model ... getting the pre-training functions ... pre-training the model ... Pre-training layer 0, epoch 0, cost 71.648659 Pre-training layer 0, epoch 1, cost 63.687855 ・・・ Pre-training layer 0, epoch 13, cost 59.473030 Pre-training layer 0, epoch 14, cost 59.331242 Pre-training layer 1, epoch 0, cost 482.215973 Pre-training layer 1, epoch 1, cost 464.952728 ・・・ Pre-training layer 1, epoch 13, cost 451.862030 Pre-training layer 1, epoch 14, cost 451.500214 Pre-training layer 2, epoch 0, cost 196.515244 Pre-training layer 2, epoch 1, cost 183.888687 ・・・ Pre-training layer 2, epoch 13, cost 176.494202 Pre-training layer 2, epoch 14, cost 176.348206 The pretraining code for file stacked_autoencoder.py ran for 72.73m
こんな感じで層ごとに自己符号化器を学習してエポックが進むと誤差が低下することがわかる。層を深くして自己符号化器の数が多くなると学習にけっこう時間がかかる。たった3つの隠れ層の初期値を決めるだけでGPUでも72分かかった。事前学習の効果は本当にあるのだろうか・・・
fine-tuning
pre-trainingが終わったら最後にfine-tuning。これはTheanoによる多層パーセプトロンの実装(2015/6/18)とほとんど同じでearly-stoppingで収束判定している。
# Fine-tuning print "getting the fine-tuning functions ..." train_model, validate_model, test_model = sda.build_finetune_functions( datasets=datasets, batch_size=batch_size, learning_rate=finetune_lr ) print "fine-tuning the model ..." while (epoch < training_epochs) and (not done_looping): epoch = epoch + 1 for minibatch_index in xrange(n_train_batches): train_model(minibatch_index) iter = (epoch - 1) * n_train_batches + minibatch_index if (iter + 1) % validation_frequency == 0: validation_losses = validate_model() this_validation_loss = np.mean(validation_losses) if this_validation_loss < best_validation_loss: if this_validation_loss < best_validation_loss * improvement_threshold: patience = max(patience, iter * patience_increase) best_validation_loss = this_validation_loss best_iter = iter test_losses = test_model() test_score = np.mean(test_losses) # patienceを超えたらループを終了 if patience <= iter: done_looping = True break
Validation誤差は以下のように収束する。
最終的に75エポックで収束し、テスト誤差は1.4%となった。つまり、正解率は98.6%。実行時間はGPUで162分・・・
Optimization complete with the best validation score of 1.360000 %, on iteration 1900000, with test performance 1.400000 % The training code for file stacked_autoencoder.py ran for 162.11m
初期値をランダムではなく、なるべく良いように設定しておくと深いニューラルネットがうまく学習できるというのが事前学習のアイデアだ。ニューラルネットではないが同様のアイデアは他のアルゴリズムでもあった。たとえば、混合ガウス分布(GMM)の学習(2010/5/21)で初期クラスタの位置をランダムに設定せずにK-meansの結果を使うなど。比較的シンプルなアイデアに見えるが事前学習が発見されるのにこんなに長い時間かかったのが不思議に思える。知ってみれば簡単でも最初に思いついて実行するのはやはり難しいものなのだろうか。
今回の積層自己符号化器の実装自体は以前に実装した自己符号化器と多層パーセプトロンの組み合わせなのでそんなに難しくなかった。なかったけれど・・・うーん、事前学習で初期値を決めた効果は本当にあったのだろうか?次回はここら辺を実験で検証してみたい。
参考
- Stacked Denoising Autoencoders (SdA)
- TheanoでDeep Learning <5> : 多層Denoisingオートエンコーダ
- 深層学習 (機械学習プロフェッショナルシリーズ) の5.6節 ディープネットの事前学習
- 深層学習: Deep Learning の1.7.2節積層自己符号化器
2016年の目標
あけましておめでとうございます。
今年もいくつか目標を立てた。メインは去年と同じく深層学習(Deep Learning)の深耕にした。
機械学習・深層学習の深耕
- Deep Learning Tutorialの読破とTheanoでの実装を続ける
- パターン生成・コンテンツ生成(2015/12/30)に関する従来研究のサーベイ
- 深層強化学習(Deep Reinforcement Learning)の理解
- 1週間に1本は論文を熟読する
- TensorFlowをさわってみる
- Computer vision: models, learning and inference(2015/12/31)の読破
- 情報理論の勉強
- 多様体学習(manifold learning)の勉強
- Sparse Codingの勉強
- 上記に関係する数学の習得
- 作者: 甘利俊一
- 出版社/メーカー: 筑摩書房
- 発売日: 2011/04/08
- メディア: 単行本
- 購入: 4人 クリック: 48回
- この商品を含むブログ (11件) を見る
Information Theory, Inference and Learning Algorithms
- 作者: David J. C. MacKay
- 出版社/メーカー: Cambridge University Press
- 発売日: 2003/09
- メディア: ペーパーバック
- クリック: 4回
- この商品を含むブログ (5件) を見る
スパース性に基づく機械学習 (機械学習プロフェッショナルシリーズ)
- 作者: 冨岡亮太
- 出版社/メーカー: 講談社
- 発売日: 2015/12/19
- メディア: 単行本(ソフトカバー)
- この商品を含むブログを見る
その他
- Python3への移行
- MOT・イノベーション・マーケティング関係の本を月1冊以上読む
- 英語ライティングを鍛える
- 作者: Bill Lubanovic,斎藤康毅,長尾高弘
- 出版社/メーカー: オライリージャパン
- 発売日: 2015/12/01
- メディア: 単行本(ソフトカバー)
- この商品を含むブログを見る
イノベーションの最終解 (ハーバード・ビジネス・セレクション)
- 作者: クレイトン・M・クリステンセン,スコット・D・アンソニー,エリック・A・ロス,玉田俊平太,櫻井祐子
- 出版社/メーカー: 翔泳社
- 発売日: 2014/07/08
- メディア: 単行本
- この商品を含むブログ (6件) を見る
The Elements of Style, Fourth Edition
- 作者: William Strunk Jr.,E. B. White
- 出版社/メーカー: Longman
- 発売日: 1999/07/23
- メディア: ペーパーバック
- 購入: 49人 クリック: 171回
- この商品を含むブログ (49件) を見る
今年は技術だけではなく、ビジネス寄りの話(技術をどう役立てるか?お金にするか?)も少し勉強してみたいと考えている。実践で身につけるのが一般的なのだろうけれど・・・とりあえず評価の高い本を片っ端から読んでみることから始めたい。
また今年はカンファレンス、講演会、展示会、勉強会などもう少し積極的に参加して見聞を広めてみようと思っている。昔はよく参加していたのだけれど最近はとんと出不精になっていた。
そんなわけで今年もブログは続けようと思いますのでよろしくお願いいたします。
2015年まとめ
2015年目標(2015/1/11)のつづき。今年ももう終わりということで目標に対する振り返り。今年は4つ目標を立てていた。
(1) Deep Learningの勉強 ◎
Deep Learningの勉強・研究は今年から本格的に始めた。主にDeep Learning Tutorialを読み、Theanoで実装しながら基本的なアルゴリズムを理解することに集中した。ロジスティック回帰、多層パーセプトロン、畳み込みニューラルネットワーク、自己符号化器、制約ボルツマンマシン、Deep Belief Networkあたりまで教科書に載っているような手法はだいたい理解できた。話題のTensorFlowは来年かな?
あとはこの二冊を熟読した。基礎は一通り抑えたのでこれで最新の論文も読めるようになるかな?
- 作者: 岡谷貴之
- 出版社/メーカー: 講談社
- 発売日: 2015/04/08
- メディア: 単行本(ソフトカバー)
- この商品を含むブログ (7件) を見る
- 作者: 麻生英樹,安田宗樹,前田新一,岡野原大輔,岡谷貴之,久保陽太郎,ボレガラダヌシカ,人工知能学会,神嶌敏弘
- 出版社/メーカー: 近代科学社
- 発売日: 2015/11/05
- メディア: 単行本
- この商品を含むブログ (1件) を見る
(2) Pythonによるモンテカルロ法入門 △
Pythonによるモンテカルロ法の実装はやらなかった。ただ、制約ボルツマンマシンの理解にMCMCが必要になりアルゴリズムは抑えたので△。
(3) pandasとscikit-learnの習得と活用 ○
Pythonのデータ分析ライブラリpandasと機械学習ライブラリscikit-learnは使えるようになった。scikit-learnに関してはSciPy2015のチュートリアルを参考に4つ記事を書いた。
(4) 複雑系の勉強 ×
複雑系の勉強は残念ながらやらなかった。Deep Learningの方で手一杯だった。
(5) その他
統計的声質変換
Pythonで音声信号処理(2011/5/14)の応用の一環として統計的声質変換の実験をした。音声の扱い方がわかってきた。
ベイズ推定とグラフィカルモデル:コンピュータビジョン基礎
機械学習とコンピュータビジョンの勉強のためにComputer vision: models, learning and inferenceを読み始めた。テキストは無料で手に入る。あと玉木先生がYoutubeで講義を配信している。PRMLよりわかりやすい。これもまだ途中なので来年も続けたい。
Synapses, Neurons and Brains
人工知能学会誌の脳神経系シミュレーションの特集をよりよく理解するためCourseraで Synapses, Neurons and Brains という神経科学の講義を受講した。ニューロンの発火の仕組みから脳シミュレーションまで。Idan Segev教授の説明が非常に熱心だったのが印象に残った。でも試験はすごく細かくてものすごーくイライラした(笑)この講義内容に関してはTwitterでときおりつぶやいただけでブログには全くまとめていなかった。Twitterはあとから見直しにくいから少し問題だな。
読んだ or 読んでいる本
今年(じゃないのもあるけど)は人工知能に関する本が豊作だったのでいくつか読んだ。
- 作者: 松尾豊
- 出版社/メーカー: KADOKAWA / 中経出版
- 発売日: 2015/03/10
- メディア: Kindle版
- この商品を含むブログ (24件) を見る
- 作者: 小林雅一
- 出版社/メーカー: 講談社
- 発売日: 2015/03/20
- メディア: Kindle版
- この商品を含むブログ (10件) を見る
- 作者: ジェイムズ・バラット
- 出版社/メーカー: ダイヤモンド社
- 発売日: 2015/06/22
- メディア: Kindle版
- この商品を含むブログ (2件) を見る
- 作者: ジョージザルカダキス
- 出版社/メーカー: 日経BP社
- 発売日: 2015/09/16
- メディア: Kindle版
- この商品を含むブログを見る
The Quest for Artificial Intelligence
- 作者: Nils J. Nilsson
- 出版社/メーカー: Cambridge University Press
- 発売日: 2009/10/30
- メディア: Kindle版
- この商品を含むブログを見る
- 作者: ミチオ・カク
- 出版社/メーカー: NHK出版
- 発売日: 2015/02/27
- メディア: Kindle版
- この商品を含むブログ (1件) を見る
- 作者: マイケル・S.ガザニガ,Michael S. Gazzaniga,藤井留美
- 出版社/メーカー: 紀伊國屋書店
- 発売日: 2014/08/28
- メディア: 単行本
- この商品を含むブログ (9件) を見る
- 作者: クリストフ・コッホ,土谷尚嗣,小畑史哉
- 出版社/メーカー: 岩波書店
- 発売日: 2014/08/07
- メディア: 単行本
- この商品を含むブログ (8件) を見る
今年はKindle Paperwhiteを買ったこともあって電子書籍を頻繁に買うようになったのが大きな変化かな。著作権が切れて大安売りしている吉川英治の歴史小説(三国志、新水滸伝、新平家物語、私本太平記、新書太閤記、宮本武蔵)に無茶苦茶はまっていた。
最後に
- 出版社/メーカー: ベセスダ・ソフトワークス
- 発売日: 2015/12/17
- メディア: DVD-ROM
- この商品を含むブログ (3件) を見る