ディープラーニング勉強会 AutoEncoder

  • 78
    Like
  • 1
    Comment
More than 1 year has passed since last update.

某所ディープラーニング勉強会の発表資料です。

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}$
      • 微分しやすい
  • $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

勾配法