読者です 読者をやめる 読者になる 読者になる

戯けた姿も生きる道ですか?

鬱屈とした学生の記録。備忘録。メモ。基本的に冗談。

過学習を防ぐために正則化とかドロップアウトをするやつ

ニューラルネットワーク 機械学習

odoketasugata.hatenablog.com
↑でちょっとだけ過学習が起きていたので,その回避策である正則化ドロップアウトを実装する.手書き数字digitsを多層パーセプトロンで学習して認識するやつの続き.

これが前回の誤差関数のグラフ
f:id:kenijl1116:20161210152300p:plain
epoch=80以降で汎化誤差が上昇していった.正則化ドロップアウトをすることによって学習回数を増やしていっても汎化誤差が減少し続け,より良い性能を持った分類器になることを期待する.

正則化

重みの数(ネットワークの自由度)は多ければ多いほど色んなことを表現できるが,学習時に誤差関数が浅い局所解にはまってしまう.これが過学習.でも重みの数は減らしたくない.そこで,重み自身の大きさに比例した値を引くことで大きくなりすぎない(小さくなりすぎない)よう制約を付ける.
誤差関数を次のように書き換える.
E_{batch}(\mathbf{w}) = \frac{1}{N_{batch}} \sum_{n} E_{n}(\mathbf{w}) + \frac{\lambda}{2} ||\mathbf{w}||^2
ただし,||\mathbf{w}||^2は全ての重みの要素の2乗の和.すると,この勾配は
\frac{\partial E_{batch}(\mathbf{w})}{\partial \mathbf{w}} = \frac{1}{N_{batch}} \sum_{n} \nabla E_{n} + {\lambda} \mathbf{w}
よって重み更新量は
\Delta W = \mu \Delta W' - \epsilon(\partial W + \lambda W)
となる.ただし,\Delta W'は前回の更新量.バイアスには正則化は適応しない.

  • 前回のコードからの変更点

誤差関数の正則化項を計算する.
(訂正しました.言い訳:卒研のプログラムをC言語で書いていたので...)

# 正則化項
def L2reg(W):
    return (W * W).sum() * lam * 0.5
    """
    nn, mm = W.shape
    sumw = 0.
    for i in xrange(nn):
        for j in xrange(mm):
            sumw += W[i, j] * W[i, j]
    return sumw * lam * 0.5
    """

誤差関数に2つの重みの正則化項を加える

# 誤差関数
cross_entropy(Y, D) + L2reg(W_1) + L2reg(W_2)

重みの勾配にも正則化項を加える

# 誤差勾配計算
delta_2 = Y - D
grad_2_E = np.dot(delta_2, Z.T) / float(batchsize)
grad_2_b = np.dot(delta_2, one) / float(batchsize)
dW_2 = mu * dW_2_ - epsilon * (grad_2_E + lam * W_2)
db_2 = mu * db_2_ - epsilon * grad_2_b

delta_1 = sigmoid_dot(U_1) * np.dot(W_2.T, delta_2)
grad_1_E = np.dot(delta_1, X.T) / float(batchsize)
grad_1_b = np.dot(delta_1, one) / float(batchsize)
dW_1 = mu * dW_1_ - epsilon * (grad_1_E + lam * W_1)
db_1 = mu * db_1_ - epsilon * grad_1_b
  • 結果

誤差推移
f:id:kenijl1116:20161211170133p:plain
このように汎化誤差は過学習を起こすことなく減少し続けた.しかし,訓練誤差の減少は正則化しない場合に比べて緩やかで,正則化することによって学習速度が下がった.でも明らかに汎化誤差は前回よりも小さくなっている.

ドロップアウト

各層で確率的に選出されたユニットで学習を行うこと.これの意味は,いくつかのネットワークを独立に学習させ,推論するときにそれらのネットワークの平均を取るということ.一般的に複数のネットワークの平均を取ると推論の精度は向上するらしい.
選出されなかったユニット達をないモノとして順伝播させていくので,それらのユニットの出力値を0としてしまう.それによって収束するまでに時間がかかるはず.

  • 前回のコードからの変更点

ユニット選出

# 入力層選出確率
p_in = 0.9
# 中間層選出確率
p_mid = 0.5
# ユニット選出
drop_in = np.array([rand.binomial(n=1, p=p_in, size=dim)]).T
drop_mid = np.array([rand.binomial(n=1, p=p_mid, size=M)]).T

# 入力データ
X = mini_batch[n] * drop_in

# 中間層を計算
U_1 = np.dot(W_1, X) + np.dot(b_1, one.T)
Z = sigmoid(U_1) * drop_mid

推論時は各層の全ユニットに選出確率をかける

# 入力層
x = np.array([data_test[n]]).T * p_in

# 中間層を計算
u_1 = np.dot(W_1, x) + b_1
z = sigmoid(u_1) * p_mid

# 出力層を計算
u_2 = np.dot(W_2, z) + b_2
y = softmax(u_2)
  • 結果

誤差推移
f:id:kenijl1116:20161211180543p:plain
過学習を起こすことなく誤差は減少した.汎化誤差より訓練誤差が大きいのは,各層でいくつかのユニットがドロップアウトされた状態での出力値で誤差を計算しているから(だと思われる).

正則化ドロップアウト

正則化ドロップアウト両方を行う.収束するのに時間がかかるはずだから学習回数を2000回に増やす.

多層パーセプトロンの構成

入力層 中間層×1 出力層
ユニット数 64 40 10
活性化関数 - ロジスティックシグモイド関数 ソフトマックス関数
誤差関数 - - 交差エントロピー

パラメータ

学習回数 2000
バッチサイズ 10
学習係数 0.01
モメンタム・パラメータ 0.9
正則化パラメータ 0.0001
入力層選出確率 0.9
中間層選出確率 0.5
  • コード

https://github.com/Jirouken/digits/blob/master/regularization_dropout.py

  • 結果

誤差推移
f:id:kenijl1116:20161211183444p:plain
これまで見た3つよりも汎化誤差は小さい.しかし,劇的に小さくなるということはなかった.予想に反して収束までに時間はかかっていなかった.学習回数が増えていっても過学習は起こらなかった.テストデータでの正解率は97%程になり,前回よりも向上した.2%だけ...

認識

では前回同様,俺が書いた数字を認識してもらおう.
f:id:kenijl1116:20161211221408p:plain
f:id:kenijl1116:20161211221421p:plain
う~~ん...数字を書いた画像が悪いのかな?32×32ピクセルに書いて認識するときに8×8ピクセルに変換してるんだけど...

次は畳み込みニューラルネットワークの勉強しよっと

参考

「深層学習」 岡谷貴之

深層学習 (機械学習プロフェッショナルシリーズ)

深層学習 (機械学習プロフェッショナルシリーズ)