StatsFragments

Python, R, Rust, 統計, 機械学習とか

Theano で Deep Learning <4> : Denoising オートエンコーダ

Python Theano を使って Deep Learning の理論とアルゴリズムを学ぶ会、第四回。

目次

DeepLearning 0.1 について、対応する記事のリンクを記載。

第一回 MNIST データをロジスティック回帰で判別する
第二回 多層パーセプトロン
第三回 畳み込みニューラルネットワーク
第四回 Denoising オートエンコーダ (今回)
第五回 多層 Denoising オートエンコーダ
第六回の準備1 networkx でマルコフ確率場 / 確率伝搬法を実装する -
第六回の準備2 ホップフィールドネットワーク -
第六回 制約付きボルツマンマシン
Deep Belief Networks
Hybrid Monte-Carlo Sampling
Recurrent Neural Networks with Word Embeddings
LSTM Networks for Sentiment Analysis
Modeling and generating sequences of polyphonic music with the RNN-RBM

オートエンコーダ (Autoencoders)

まずは ( Denoisingでない ) ふつうのオートエンコーダについて。

オートエンコーダを使う目的

ニューラルネットワークの層が深くなっていくと、誤差逆伝播でネットワーク全体を学習させるのは時間がかかるし、過学習もしやすくなっていく。全体を誤差逆伝播で学習させることが難しいなら、各層をそれぞれ事前に学習 (pretraining) させて、あらかじめ特徴抽出しやすい初期値をもたせておけばいいよね!というのがオートエンコーダの考え方。オートエンコーダは、最終的につくりたい多層ニューラルネットワークの各層を効率的に事前学習させるための仕組みであって、この構造がそのまま多層ニューラルネットワークに含まれるわけではない。

多層ニューラルネットワークの各層がそれぞれどういった特徴抽出をすればよいか?というのは 事前学習の時点ではわからない。そのためオートエンコーダは教師なし学習になる。オートエンコーダで仮定されるのは、"隠れ層の出力から入力が復元できる (隠れ層で情報が失われていない) なら よい初期値といえそう" ということ。オートエンコーダは構造としては多層パーセプトロンと同じく、入力層 - 隠れ層 - 出力層からなる 3 層ニューラルネットワークをつくる。

オートエンコーダの数式表現

オートエンコーダを DeepLearning 0.1 Denoising Autoencoders (dA) の式をベースに図示するとこんな感じになる。真ん中の隠れ層にあたる部分が事前学習させたい層。デコード部分 + 出力層 (破線) は事前学習のために追加されたダミーで、最終的につくりたいニューラルネットワークには含まれない。

f:id:sinhrks:20141214232002p:plain

  • { W } : 入力層 - 隠れ層の間 (エンコーダ) で適用される係数行列。次元は 入力データの次元 { D } x 隠れ層のユニット数 { D_h }
  • { b } : 入力層 - 隠れ層の間 (エンコーダ) で適用される重みベクトル。次元は 隠れ層のユニット数 { D_h }
  • { s } : 隠れ層の活性化関数。シグモイド関数もしくは { tanh } (ハイパボリックタンジェント) をよく使う。
  • { W' } : 隠れ層 - 出力層の間 (デコーダ) で適用される係数行列。次元は 隠れ層のユニット数 { D_h } x 入力データの次元 { D } 。元文書のサンプルでは { W' = W^T } としており、これを Tied weight という。
  • { b' } : 隠れ層 - 出力層の間 (デコーダ) で適用される重みベクトル。次元は 入力データの次元 { D }

補足 オートエンコーダは次元圧縮として働く、つまり入力の次元 { D } > 隠れ層のユニット数 { D_h } であることが多いようだが、必須条件ではない。

オートエンコーダの動きを要約すると、

  1. { D } 次元の入力を { W }, { b } によって { D_h } 次元へと写像 (エンコード) し、
  2. それを { W' = W^T } , { b' } によって元の { D } 次元次元へ写像 (デコード) し、
  3. 出力と入力から計算される損失関数を最小化する

もの。本当に事前学習させたいものは { W, b } だが、損失関数の評価のために { b' } も (Tied weight でない場合 { W' } も ) 事前学習することになる。

補足 元文書では 入力ベクトルの各値が [0, 1] からなる 2 値データの場合について記載している。より一般化したものは Generalized Denoising Auto-Encoders as Generative Models にある。

オートエンコーダの損失関数

多層パーセプトロン では 負の対数尤度 + 正則化項 ( L2ノルム )を使っていたが、オートエンコーダでは交差エントロピー関数 { L_H(x, z) } を使っている。

f:id:sinhrks:20141214225510p:plain

ここからのスクリプトで、オートエンコーダをあらわすクラス dA が定義されている。多層パーセプトロンHiddenLayer クラスと LogisticRegression クラスの 2 つで表現されていたが、 dA クラスは それ自体で オートエンコーダ (上図) の全体を表現している。使っている Theano の機能 / 内部的な処理は 多層パーセプトロンの場合と 同じなので概要だけ。

  • get_corrupted_input メソッド: Denoising オートエンコーダで使うメソッド。ふつうのオートエンコーダでは、入力 x と同じデータをそのまま返す。
  • get_hidden_values メソッド: 隠れ層からの出力 { y } を計算するメソッド
  • get_reconstructed_input メソッド: 出力層からの出力 { z } を計算するメソッド

これらを使って、損失関数 (交差エントロピー関数) は以下のように計算される。

    tilde_x = self.get_corrupted_input(self.x, corruption_level)
    y = self.get_hidden_values(tilde_x)
    z = self.get_reconstructed_input(y)

    L = - T.sum(self.x * T.log(z) + (1 - self.x) * T.log(1 - z), axis=1)

元文書ではこの後、損失関数に正則化項入れなくていいのか?という話が続いている。特に 隠れ層のユニット数が入力層のユニット数以上の場合には 入力層 - 隠れ層 で恒等変換が学習されてしまうと隠れ層の存在意義がない。が、参照されている論文 によると、確率的勾配降下法 (SGD) で学習させた場合は L2 正則化に似た動きになり、ある程度はうまいこといくらしい。

ほか、オートエンコーダが恒等関数を学習してしまうのを防ぐ方法はいくつか提案されている。

  • 入力層のユニット数 > 隠れ層のユニット数 とする ( サンプルスクリプトでは 入力 784次元に対し 隠れ層 500次元 )
  • 損失関数に正則化項を入れる (スパースオートエンコーダ)
  • 学習データにランダムなノイズを入れる (Denoising オートエンコーダ : 次セクション)

Denoising オートエンコーダ

入力に対して学習のたびにランダムなノイズを付与し、それらについて事前学習を行うことで より汎化能力の高い初期値を与えるための仕組み。損失関数はノイズの入っていない入力に対して評価されるため、Denoising オートエンコーダでは以下 2 つを学習していることになる。

  • 入力された情報を維持したまま、よい特徴を抽出する
  • 入力に含まれるノイズを除去する

入力にノイズを与えているのは、上述の get_corrupted_input メソッド。具体的な処理は、

  • corruption_level で与えられた比率で 0 を含む二項分布の乱数を生成
  • 生成した乱数を入力画像の対応する要素にかける (行列積ではなく、各要素の積)

例えば 10 x 10 の入力があったとき、ノイズ 30% ( corruption_level=0.3 ) で二項分布の乱数を生成すると、

corruption_level = 0.3

rng = numpy.random.RandomState(123)
theano_rng = RandomStreams(rng.randint(2 ** 30))
theano_rng.binomial(size=(10, 10), n=1, p=1 - corruption_level,
                    dtype=theano.config.floatX).eval())
# [[ 1.  1.  1.  1.  1.  0.  1.  0.  1.  1.]
#  [ 1.  1.  1.  1.  0.  0.  1.  1.  1.  1.]
#  [ 1.  1.  0.  1.  1.  0.  1.  1.  0.  1.]
#  [ 1.  0.  0.  0.  1.  1.  1.  0.  1.  1.]
#  [ 1.  1.  0.  1.  1.  0.  0.  0.  0.  1.]
#  [ 1.  1.  0.  1.  0.  0.  1.  0.  1.  1.]
#  [ 1.  1.  0.  1.  1.  1.  1.  1.  1.  1.]
#  [ 1.  1.  1.  1.  0.  1.  1.  1.  0.  1.]
#  [ 1.  1.  1.  0.  0.  1.  1.  1.  0.  1.]
#  [ 1.  0.  1.  0.  1.  0.  1.  1.  1.  1.]]

普通の画像について 同じロジックで作成したノイズを適用すると以下のような結果になる。ガウシアンノイズではなく 2値のノイズなので画像が欠損するような感じだ。

f:id:sinhrks:20141214201324p:plain

付与するノイズが 0% ( corruption_level=0 ) ならふつうのオートエンコーダになる。このノイズ付与が事前学習にどのように作用するのか、次のセクションで比較されている。

全部まとめて

これまでと同じく、最後にあるスクリプトを保存して実行すればよい。実行結果 に添付されている以下2つの画像が "dA_plots" フォルダ内に出力される

  • filters_corruption_0.png : ふつうのオートエンコーダで事前学習した特徴 ( { W } によって学習されたもののうち 100個分 )
  • filters_corruption_30.png : Denoising オートエンコーダ ( ノイズ30% ) で事前学習した特徴 (同上)

また、実行には、以下に添付されている util.py が必要。

Miscellaneous — DeepLearning 0.1 documentation

どんな感じで学習していくのかをみるため、Epoch の経過ごとにアニメーションさせてみた。

  • 左側 : ふつうのオートエンコーダで事前学習した特徴
  • 右側 : Denoising オートエンコーダ ( ノイズ30% ) で事前学習した特徴

ふつうのオートエンコーダでもいくつかは意味がありそうな特徴 (スパースな特徴 = 白/黒がはっきりわかれるもの) を抽出できている。が、Denoising オートエンコーダのほうが より多くのセルで はっきりとした特徴を早く抽出できているように見える。

f:id:sinhrks:20141213231111g:plain

※ Denoising オートエンコーダはノイズが乗ったデータから学習しているため、損失関数の値 (cost) は大きい。

アニメーション部分のプログラム

※ Epoch 計算に時間がかかるため計算のたびにプロット出力し、手動でアニメーションGIFとして結合した。

まとめ

オートエンコーダでの事前学習により、多層ニューラルネットワークによりよい初期値を持たせることができる。このとき、入力にノイズをのせる (Denoising オートエンコーダ) ことで より特徴らしい特徴が抽出できる。

12/21追記 続きはこちら。

深層学習 (機械学習プロフェッショナルシリーズ)

深層学習 (機械学習プロフェッショナルシリーズ)