昨日で定期試験の採点もおわり,夏休み気分のたかたかです.ちうわけで久しぶりの研究ねた.Denoising Autoencoder の実験をしてみる (2) - まんぼう日記 のつづきです.MNIST で denoising autoencoder の実験をやってみました.前回との違いは,
- 学習が一括修正やったんを SGD にして,学習データ数も多くした
- ノイズとして,画像の平行移動も導入した
- 隠れ層の活性化関数として,線形(恒等写像)に加えて Rectified Linear (ReLu) も試した
- 出力だけじゃなくて重みも可視化してみた
ってところです.
実験条件
データ
前回( Denoising Autoencoder の実験をしてみる (2) - まんぼう日記 )はMNIST の手書き数字画像のうち2000枚で学習,2000枚でテスト,っていう小規模なものでしたが,今回は学習用6万枚のうち5万枚を学習データとし,テスト用1万枚をテストデータとしました.学習用の残り1万枚は検査用にとっときましたが,今回は実質使ってません.MNIST の画像は 28 x 28 画素なので,データの次元数は D = 768 です.
このデータにノイズを加えたものを作ります.以下,例によってノイズを加えていない元の学習データを \( X_{\rm org} \),これにノイズを加えたものを \( X_{\rm noisy } \) と表記します.ノイズの入れ方は,次の3通り:
- 画素値を確率 0.1 で最大値または最小値にする(いわゆる「ごま塩」ノイズ,前回のノイズとは微妙に違う)
- 画像を水平垂直にそれぞれ -2 から +2 画素の範囲でランダムに平行移動させる(以下「シフト」と呼びます)
- シフト + ごま塩
左はテストデータオリジナルの最初の50個,右はそれにノイズを加えたもの(シフト + ごま塩).
ネットワークの構造と学習法
ネットワークは素子数 D - H - D の3層で,H は 100 と 1000 の二通り.活性化関数は,隠れ層については線形または ReLu(ReLuの場合はしきい値あり),出力層は線形.コスト関数は平均二乗誤差.前回と違い,SGD で学習します.学習に関する細かいパラメータについては,プログラムを参照してください.
学習データの与え方は,例によって次の3通り:
- 入力出力とも \( X_{\rm org} \)
- 入力出力とも \( X_{\rm noisy} \)
- 入力は \( X_{\rm noisy} \),出力は \( X_{\rm org} \)
1. と 2. は従来の autoencoder,3. が denoising autoencoder です.前回と違い,ノイズは学習の繰り返し毎に新たに生成しなおして,同じ学習データでもepoch毎に異なるノイズがのるようにしてます.
プログラム
Python + Theano + NumPy + OpenCV で作成.
- ex150808.py: 実験用.ニューラルネットの学習には, Denoising Autoencoder の実験をしてみる - まんぼう日記 で作った nnet150718.py を使ってます.
- mnist150808.py: MNISTのデータ読み込み.
- mnist_sub150808.py: MNISTのデータにノイズをのせたりとかいろいろ.
実験1: 隠れ層ニューロン数 H = 100,線形
この実験では,ノイズは全て「シフト+ごま塩」.結果はこんなんなりました.
入力 | 出力教師 | 誤差(L) | 誤差(T) | ノイズありテストデータに対する出力 |
---|---|---|---|---|
\(X_{\rm org}\) | \(X_{\rm org}\) | 4.5 | 58 | |
\(X_{\rm noisy}\) | \(X_{\rm noisy}\) | 36 | 60 | |
\(X_{\rm noisy}\) | \(X_{\rm org}\) | 34 | 34 |
この表の誤差(L)と誤差(T)は意味が違うので要注意.誤差(L)は,学習データに対するコスト関数の値,つまり,ネットワークの出力と教師との平均二乗誤差です.一方,誤差(T)の方は,ノイズありテストデータを入力したときのネットワーク出力とノイズなしのオリジナルとの平均二乗誤差です.
隠れ層ニューロン数 H がデータの次元数 D より小さいので,どの条件でも denoising はそれなりにできてます.テストデータに対する出力画像を見ると denoising autoencoder の場合はいまいちですが,誤差的にはこれが一番よい値.
こちらは重みの値を可視化したもの.上と同じ順番.入力から隠れ層への重みを描く方が普通かもしれませんが,ここでは,隠れ層から出力層への重みを,H = 100 個の隠れ層ニューロン毎に描いてます.白が正,黒が負,灰色が 0.どれも見て面白いもんではないですね.
実験2: 隠れ層ニューロン数 H = 1000,線形
実験1とは H が違うだけ.したがって,ノイズは全て「シフト+ごま塩」.
入力 | 出力教師 | 誤差(L) | 誤差(T) | ノイズありテストデータに対する出力 |
---|---|---|---|---|
\(X_{\rm org}\) | \(X_{\rm org}\) | 0.012 | 83 | |
\(X_{\rm noisy}\) | \(X_{\rm noisy}\) | \(3.6\times10^{-11}\) | 95 | |
\(X_{\rm noisy}\) | \(X_{\rm org}\) | 34 | 34 |
前回( Denoising Autoencoder の実験をしてみる (2) - まんぼう日記 )と同様の結果です.隠れ層ニューロン数 H がデータの次元数 D より大きいので,最後の denoising autoencoder 以外は恒等写像を学習しちゃって,ほとんど denoising できてません.denoising autoencoder の方は,H を大きくしても変わりなし.
あ,重みの可視化の方は,1000個のうち100個の値のみを示してます.
実験3: 隠れ層ニューロン数 H = 1000,ReLu
隠れ層ニューロンの活性化関数を Rectified Linear にしてみました.denoising でない autoencoder の場合,実験2と同じで恒等写像にしかならなかったので,結果は省略します.denoising autoencoder の場合について,ノイズののせ方を変えたらどうなるかを見てみました.
ノイズ | ノイズありテストデータに対する出力 | 隠れ層-出力層の重み |
---|---|---|
ごま塩 | ||
シフト | ||
両方 |
誤差(T)の値は,順に 4.6, 10.8, 16 となりました.
活性化関数を ReLu にしたら,獲得される内部表現ががらっと変わりました.しかも,ノイズののせ方によっても全く違います.面白い.特にごま塩ノイズの場合,まるでスパースコーディングしたみたいな重みになってますね.ReLu ではニューロンへの入力の総和が一定以下なら出力が 0 になってしまうので,自然にスパースな表現が獲得されるのでしょう.
しかし,これだけノイズののせ方に依存して結果が変わるとなると,deep なネットワークの事前学習なんかに使う場合,どんなノイズを使うかの選択に気ぃ使わなあきませんね.