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

More than 3 years have 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


勾配法