糞糞糞ネット弁慶

読んだ論文についてメモを書きます.趣味の話は http://repose.hatenablog.com

節操の無いクソイナゴ野郎なのでdeep learningで使われるautoencoder実装した

身内でdeep learningの勉強会をやったらできそうだったので実装した.
読んだのは大体ここらへん.
NEURAL NETS FOR VISION(CVPR2012 tutorial)
CS294A Lecture notes Sparse autoencoder
ImageNet Classification with Deep Convolutional Neural Networks

autoencoder

autoencoderはunsupervised feature learningの一種.Convolutional Neural Netとは違って,最後の判別器の予測誤差をback propagationさせる,という事はせずある種特徴抽出で完結させている.
autoencoderを一言でまとめると,「次元削減を繰り返すNeural Netを多段に繋げて特徴抽出を行う手法」と言える.
具体的には次のように動作する.
https://dl.dropbox.com/u/187646/blogs/fig1.png
まずはじめに入力層,隠れ層,出力層で構成される3層Neural Netを考え,「元データを入力として,出力が元データと同じ値になる」ように学習を行う.例えば隠れ層の次元数が入力層の次元数より少ない場合は,低次元に次元圧縮(encode)し,再度次元を復元した際(decode)の誤差が最小になるようにする.「ここだけ見るとPCAみたいなもんだなガハハ」とは諸々の資料に載っている.
この時,普通のNeural Netだと最小化すべき目的関数は

という感じになる.二項目はL2 normではパラメータ,は全てのニューロンの重み.
autoencoderではここに更に「隠れ層の状態がなるべくsparseになるようにする」という制約を入れる.これが入ると

という感じになる.はパラメータ.
で,あとはこれに従っていつもの Neural Net のように学習をする.
https://dl.dropbox.com/u/187646/blogs/fig2.png
ここで学習が終わったNeural Netの二層目までを考えるとひとつの次元削減器ができている事が分かる.
https://dl.dropbox.com/u/187646/blogs/fig3.png
これを2つ目のNeural Netの入力としてまた新たな隠れ層(encoder)を学習する.これを何度も繰り返す事のがautoencoder.

疑問: ただのでかい Neural Net じゃいけないのか

Deep Learning via Hessian-free Optimization
「3層のNeural Netを積み重ねるのではなく,一つの Neural Netで学習してはいけないのか」というのが真っ先に浮かぶ疑問.
しかし,単一の深いNeural Netではvanishing gradient problemと呼ばれる問題が発生するらしい.
以下はあやふやな理解だが,誤差項がback propagationで遡っていくに従って弱まってしまい十分な学習ができないという問題.
これに対応するには,できるだけ浅い層のNNを作って最適化していくのが有効である,と上記資料で述べられている.

so a 2nd-order optimizer will pursue d at a reasonable rate, an elegant solution to the vanishing gradient problem of 1st-order optimizers

Deep Learning via Hessian-free Optimization

ここらへんの話,ぐぐっても古い資料(論文が1ページごとに画像としてPDF化されたもの)ばかりが引っかかったり,日本語では自分のブログしか出てこないので詳しい人に聞きたい.

実装

https://github.com/ybenjo/tiny_autoencoder
とりあえず動けばいいのでRubyで書いた.それっぽく動いてるので多分合ってる筈.

require './autoencoder.rb'

# wget http://archive.ics.uci.edu/ml/machine-learning-databases/spambase/wine.data
input_data = [ ]
open('./wine.data', 'r'){|f|
  f.each do |l|
    next if !l.include?(",")
    # skip label(-1)
    input_data.push l.chomp.split(",")[0...-1].map(&:to_f)
  end
}

# input -> dim(20) -> dim(10) -> dim(5)
ac = AutoEncoder.new(input_data)
ac.train({size: 20}, 10)
ac.train({size: 10}, 10)
ac.train({size: 5}, 10)
# check
ac.latest_encoded_data.each_with_index do |e, i|
    puts "#{input_data[i]} #=> #{e}"
end

こんな感じで書くと元の次元から20次元,10次元,5次元と次元削減できる.次元上げても良い.
で,実装してみると楽には楽だけど,中間層の数,学習の繰り返し数,学習時のパラメータなど,設定しなきゃならないパラメータが地味に多い.また,どのencoderでどれぐらい次元を落として,それを何段階繰り返すか,などはかなり問題固有だったり,もしくはチューニングや勘の世界なんじゃないかと思う.
なのでdeep learningで全部決めてくれて何も考えずにハッピーハッピーというわけにはまだ行かないと思う.