某所ディープラーニング勉強会の発表資料です。
AutoEncoderって何?
出力データが入力データをそのまま再現する3層ニューラルネットだよ。
普通は、隠れ層の次元を入力層&出力層より小さくするよ。
AutoEncoderの意味は?
次元削減だよ。
データが分布する多様体を推定しているともいえるよ。
主成分分析に似てるけど、シグモイド関数とかの非線形変換もいけるよ。
AutoEncoderはいつ使うの?
事前学習だよ。ニューラルネット全体で学習を行うための良い初期パラメータを得るのに行うよ。
訓練データで教師無し学習をしていくよ。n層目まで完了したらn+1層目の学習をするよ。
誤差の逆伝播が遠くの層へうまく伝わらない問題を解決したよ。
計算式
アフィン変換(線形変換+平行移動)して活性化関数に食わせる。
入力層:$x \in [0,1]^d$
隠れ層:$y = f_{W, b}(x) = s(Wx + b) \in [0,1]^{d'}$
出力層:$z = g_{W', b'}(y) = s(W'y + b') \in [0,1]^d$
目的関数:$\min_{W, b, W', b'} {1 \over n}\sum_{i=1}^n L(x^{(i)}, g(f(x^{(i)})))$
- $W'$は$W$の転置行列と制約かけてよい
- $s(x_k)$は活性化関数、ベクトルの要素ごとに適用
- 標準シグモイド関数 $1 \over 1 + e^{-x_k}$
- 微分しやすい
- 標準シグモイド関数 $1 \over 1 + e^{-x_k}$
- $L(x,z)$は損失関数
- 二乗誤差 $\sum_{k=1}^d (x_k-z_k)^2$
- ベルヌーイ分布のクロスエントロピー $-\sum_{k=1}^d x_k \log z_k + (1-x_k) \log (1 - z_k)$
- $x_k$,$z_k$を、0,1の二値のうち$x_k$,$z_k$の確率で1を取る確率分布とみなす
- $x_k$の確率のもと$z_k$の情報量の期待値がクロスエントロピー
- $i$は訓練データのインデックス
勾配法による学習
$W^{new} = W^{old} - {\epsilon \over n} \sum_{i=1}^n {\partial L(x^{(i)}, g(f(x^{(i)}))) \over \partial W}$
$b^{new} = b^{old} - {\epsilon \over n} \sum_{i=1}^n {\partial L(x^{(i)}, g(f(x^{(i)}))) \over \partial b}$
${b'}^{new} = {b'}^{old} - {\epsilon \over n} \sum_{i=1}^n {\partial L(x^{(i)}, g(f(x^{(i)}))) \over \partial b'}$
- $\partial L \over \partial W$は、k行l列目の要素が$\partial L \over \partial W_{k,l}$の行列を表す
- $\partial L \over \partial b$は、k番目の要素が$\partial L \over \partial b_k$のベクトルを表す
$W' = W^T$
活性化関数を標準シグモイド関数
損失関数をベルヌーイ分布のクロスエントロピー
として偏微分がんばると、
$ {\partial L \over \partial W} = (W(x -z) * y * (1 - y))x^T + ((x -z)y^T)^T$
$ {\partial L \over \partial b} = W(x-z) * y * (1-y) $
$ {\partial L \over \partial b'} = x -z $
- アスタリスクは要素積
確率的勾配降下法
オンライン学習(⇔バッチ学習)の一種。
ランダムに選んだデータの微分値で補正する。
$W^{new} = W^{old} - {\epsilon \over n} {\partial L(x^{(i)}, g(f(x^{(i)}))) \over \partial W}$
$b^{new} = b^{old} - {\epsilon \over n} {\partial L(x^{(i)}, g(f(x^{(i)}))) \over \partial b}$
${b'}^{new} = {b'}^{old} - {\epsilon \over n} {\partial L(x^{(i)}, g(f(x^{(i)}))) \over \partial b'}$
ここで$i$はランダムに選択
経験的に、最急降下法より、収束が速く収束値も良い。
Denoising Autoencoder
訓練データにノイズを加えたものを入力し、出力は元の訓練データとの誤差を取る。
ノイズに対して頑強になる。
- ガウスノイズ:正規分布に従った値を加える
- マスキングノイズ:ランダムに選んだ要素を0にする
- 胡麻塩ノイズ:ランダムに選んだ要素を0か1にする
Denoising Autoencoderの実装
「ScalaによるDeep Learningの実装 - Yusuke Sugomori's Blog」のDenoising Autoencoderの実装を、数式に近づけて書きなおしてみたのが下記コード。
ベクトル・行列演算さえ扱えれば、言語によらずさくっと書けるはず。
import scala.math
import scala.util.Random
import MatrixImplicits._ // 自作のimplicit classによるSeqラッパ
class dA(train_N: Int, n_visible: Int, n_hidden: Int, seed: Int) {
val rng = new Random(seed)
var W = Seq.fill(n_hidden, n_visible)(uniform(-1.0 / n_visible, 1.0 / n_visible))
var hbias = Seq.fill(n_hidden)(0.0)
var vbias = Seq.fill(n_visible)(0.0)
def uniform(min: Double, max: Double): Double = rng.nextDouble() * (max - min) + min
def sigmoid(x: Double): Double = 1.0 / (1.0 + math.exp(-x))
def corrupted(x: Seq[Double], p: Double): Seq[Double] = x.map(_ * (if (rng.nextDouble() < p) 0.0 else 1.0))
def encode(x: Seq[Double]): Seq[Double] = ((W mXc x) + hbias).map(sigmoid)
def decode(y: Seq[Double]): Seq[Double] = ((W.T mXc y) + vbias).map(sigmoid)
def train(x: Seq[Double], learning_rate: Double, corruption_level: Double) {
val tilde_x = corrupted(x, corruption_level)
val y = encode(tilde_x)
val z = decode(y)
val L_vbias = x - z
val L_hbias = (W mXc L_vbias) * y * y.map(1.0 - _)
vbias = vbias + L_vbias.map(_ * learning_rate / train_N)
hbias = hbias + L_hbias.map(_ * learning_rate / train_N)
W = W + ((L_hbias cXr tilde_x) + (y cXr L_vbias)).map2(_ * learning_rate / train_N)
}
}
参考文献
ディープラーニング
AutoEncoder
- 楽しいAutoEncoderと学習の世界 - Swanky Street
- Denoising Autoencoderとその一般化 - @beam2d
-
Denoising Autoencodersにおける確率的勾配降下法(数式の導出) - Yusuke Sugomori's Blog
- 最急降下法の数式を導出している気がする
- Extracting and Composing Robust Features with Denoising Autoencoders
- Stacked Denoising Autoencoders: Learning Useful Representations in a Deep Network with a Local Denoising Criterion