E資格の勉強④深層学習day2
目次
- 本投稿の目的
- Section1:勾配消失問題
- Section2:学習率最適化手法
- Section3:過学習
- Section4:畳み込みニューラルネットワーク(CNN)の概念
- Section5:最新のCNN
本投稿の目的
深層学習(day2)に関する学習のまとめ
Section1:勾配消失問題
勾配消失問題
【要点】
勾配消失問題:
誤差伝搬法では、パラメータの更新量は「出力層 → 入力層」方向に計算していく。
しかし、パラメータの更新量 = 傾きが0になる値を微分で求めるが、
活性化関数に「シグモイド関数」を使用している場合、入力値が大きいと傾きがほぼ0になり更新量が消えてしまう。
結果、階層(入力層に近い層)のパラメータが一切更新されなくなり、学習が止まってしまう。
これが勾配消失問題。
【考察結果】
確認テスト1:シグモイド関数を微分した時、入力値が0の時に最大値をとる。その値として正しいものを選択肢から選べ。
(1)0.15
(2)0.25
(3)0.35
(4)0.45
(回答)
(2)0.25
(補足)
シグモイド関数:f(x) = [1 / (1 + exp^x)]
シグモイド関数の微分:f'(x) = f(x)(1−f(x))
→ これにx=0を代入すると、0.5× (1-0.5) = 0.25
数値の0乗 = 1になるため、簡単に計算できた。
※最大でも0.25と小さい。
仮に、入力値が「5」の場合、微分結果は「0.00664・・」まで小さくなる。
→ パラメータの更新量がほぼ0となってしまう。
勾配消失問題の解決法
【要点】
複数の解決方法が存在する。
①活性化関数の選択:
ReLu関数を使用する。
入力が0以下 :0
入力が0を超える:入力
→ 微分結果が大きくなるので、勾配消失を防止できる。
②重み(W)の初期値設定
Xavier(ザビエル)を使用する。
→ 重みの要素を、前層のノード数の平方根で割った値
③バッチ正規化
ミニバッチ単位で、データを正規化(最大1、最小0)する。
【考察結果】
確認テスト1:重みの初期値に0を設定すると、どのような問題が発生するか。簡潔に説明せよ。
(回答)
重みの初期値に0を設定(正しく表現すると、重みに同じ値を設定する)と
順準伝搬の結果が同一となる
→ 逆伝搬法による更新量も同一となる
→ 全く同じ動作をするノードが複数存在することになる。
(ノードを多数持つ意味が無くなってしまう)
確認テスト2:一般的に考えられるバッチ正規化の効果を2点挙げよ。
(回答)
①ミニバッチごとに発生する入力データの差を抑制できる。
→ミニバッチごとの学習のブレを(過剰や過少な学習)を防止できる。
→学習の速度と精度の向上を期待できる。
②入力の分布によっては、勾配消失が発生する可能性がある。
→入力データを適切な範囲に変換することで、適切な更新量を維持できる。
→勾配消失問題の抑制。
(参考)
https://www.renom.jp/ja/notebooks/tutorial/basic_algorithm/batch_normalization/notebook.html
→考察:バッチ正規化の目的 = バッチごとにバラつく可能性がある入力を安定させて、学習も安定させてしまおう、という考え。
Section2:学習率最適化手法
【要点】
学習率:
更新量を反映させる際の割合。適切な値を設定する必要がある。
学習率が大きすぎる場合:
→ 最適解にたどり着かない。
※ふり幅が大きすぎる振り子のイメージ。
学習率が小さすぎる場合:
→ 最適解にたどり着つまでに時間を要する。局所最適解から動けなくなる。
※ふり幅が小さすぎる振り子のイメージ。
→ 基本的な方針として、学習率を動的にして、
初めは大きく、学習が進むにつれて小さくする。
学習率最適化手法:
学習率を調整する方法。
・モメンタム(慣性):勾配降下法の調整に加えて、以下を加算する。
→ 絶対値[現在 - 前回の重さ] × 慣性(前回動いた分の慣性が働くイメージ)
・AdaGrad
・RMSProp
・★Adam★ ←最も主流の手法。モメンタムとRMSPropの長所を持っている。
【考察結果】
確認テスト1:モメンタム・AdaGrad・RMSPropの特徴をそれぞれ簡潔に説明せよ。
(回答)
・モメンタム:
学習率が一定。最適解に近づく時間が早い。大域的最適解に移動できる。
・AdaGrad:
学習率が可変。モメンタムより事前設定必要なハイパーパラメータが少ないため、設定が容易。
(モメンタム=学習率と慣性。AdaGrad=学習率のみ)
学習が進むほど学習率が低下する。鞍点問題※の問題がある。
※馬の鞍のように、なだらかな地点(精度が上がる点が遠くにある)場合、
学習を行えなくなる問題。(学習量が小さい → 精度が上がる点までたどり着けない → 学習できない)
・RMSProp:
学習率が可変。AdaGradを改善した物。
【演習】
①SGD
(実行結果)
→ 学習データ、検証データ共に精度が上がらない。
(実行結果※バッチ正規化あり)
→ ある程度改善した。60%の割合で正解できる。
②momentum
(実行結果)
→ 学習データ、検証データ共に精度が上がらない。
(実行結果※バッチ正規化あり)
→ 大きく改善した。(90%近い精度)
③AdaGrad(momentumのアルゴリズムを基に作成してみた)
(作成したアルゴリズム)
for key in ('W1', 'W2', 'W3', 'b1', 'b2', 'b3'):
# ===========================================
# momentum
# ===========================================
# if i == 0:
# h[key] = np.zeros_like(network.params[key])
# h[key] = momentum * h[key] - learning_rate * grad[key]
# network.params[key] += h[key]
# ===========================================
# AdaGrad
# ===========================================
if i == 0:
h[key] = np.full_like(network.params[key], 1e-4)
else:
h[key] += np.square(grad[key])
network.params[key] -= learning_rate * grad[key] / (np.sqrt(h[key]))
(実行結果)
→ 学習データ、検証データ共に精度が上がらない。
(実行結果※バッチ正規化あり)
→ 大きく改善した。(80%の精度)
④RMSProp
(実行結果)
→ バッチ正規化なしでも、非常に高い精度。
⑤Adam
(実行結果)
→ バッチ正規化なしでも、非常に高い精度。
Section3:過学習
【要点】
過学習については、機械学習で学習済み。(忘れている場合、機械学習のレポートを参照する)
ここでは、「正則化 = 過学習を防止する方法」について、記載する。
・L1正則化、L2正則化:
誤差関数に各ノルムを加えて重みを抑える方法。ノルムは以下が参考になる。
※L1ノルム:ダイヤ型(マンハッタン距離)、L2ノルム:円(ユークリッド距離)と認識しておく。
manabitimes.jp
・ドロップアウト:
ノードをランダムに削除して学習させる。
※一般的には隠れ層(中間層)の場合は50%ほどのノードを無効化するとよい、とのこと。
【考察結果】
確認テスト1:線形モデルの正則化の手法の中にリッジ回帰という手法があり、その特徴として正しい物を選択しなさい。
(a)ハイパーパラメータを大きな値に設定すると、全ての重みが限りなく0に近づく。
(b)ハイパーパラメータを0に設定すると、非線形回帰となる。
(c)バイアス項についても、正則化される。
(d)リッジ回帰の場合、隠れ層に対して正則加工を加える。
(回答)
(a)ハイパーパラメータを大きな値に設定すると、全ての重みが限りなく0に近づく。
(考察)
リッジ回帰(L2ノルム)のハイパーパラメータを大きくする → 罰則が大きい → 重みが小さくなる。
確認テスト2:下図について、L1正則化を表しているグラフはどちらかを答えよ。
(回答)
L1ノルム = マンハッタン距離なのでダイヤ型。答えは右の図。
【演習】
①過学習が発生しているデータ。
→ 練習用データの精度は高いが、テスト用データの判定率が低い。
②L2正則化
③L1正則化
パターン1:ドロップアウト割合=15%
→比較的、適切に学習できるようになっているが、最終的に精度が乖離(≒過学習)となっている。
パターン2:ドロップアウト割合=50%
→全く学習が進んでいない。(ドロップアウトしすぎている)
パターン1:ドロップアウト割合=8% かつ 正則化強度=0.004
→L2正則化に近い形となっている。
パターン2:ドロップアウト割合=12% かつ 正則化強度=0.006 (パターン1の1.5倍)
→精度を上げ切れていない。正則化が強すぎると思われる。
Section4:畳み込みニューラルネットワーク(CNN)の概念
【要点】
CNN:画像や動画認識で広く使われるモデル。以下のような構造を持つ。
・入力層→畳み込み層→プーリング層→全結合層→出力層
※学習のターゲットとして、LeNetを題材としている。
【各層の説明】
畳み込み層:
3次元の空間画像も学習できるような層。
出力 = 活性化関数(入力 * フィルター(≒重み) + バイアス)
★用語★
バイアス:
バイアス項と同じ。
パディング:
入力(中途半端なサイズ)を処理しやすいサイズに調整する。(埋める)
調整分は0埋めか、周囲と同じ値などを設定する。
フィルターの移動量。
チャンネル:
入力を判定するフィルターの数の様な物。異なる複数のフィルターを使用して、
識別性能を向上させる。
プーリング層:
入力(=畳み込み層の出力)から、特定の値を取得する。(最大値や平均値)
単語の意味を把握すると覚えやすい。
※プーリング:統計学には「グループをたばねる」という意味。
【考察結果】
確認テスト1:サイズ6×6の入力画像を、サイズ2×2のフィルタで畳み込んだ時の出力画像のサイズを答えよ。なお、ストライドとパディングは1とする。
(回答)
5×5
(イメージ)
以下の★部分が縦横1マスずつ動くイメージ。
――――――
★★〇〇〇〇
★★〇〇〇〇
〇〇〇〇〇〇
〇〇〇〇〇〇
〇〇〇〇〇〇
〇〇〇〇〇〇
――――――
【演習】
★畳み込み層★
・畳み込み層の実装
'''
input_data: 入力値
filter_h: フィルターの高さ
filter_w: フィルターの横幅
stride: ストライド
pad: パディング
'''
def im2col(input_data, filter_h, filter_w, stride=1, pad=0):
# N: number, C: channel, H: height, W: width
N, C, H, W = input_data.shape
out_h = (H + 2 * pad - filter_h)//stride + 1
out_w = (W + 2 * pad - filter_w)//stride + 1
img = np.pad(input_data, [(0,0), (0,0), (pad, pad), (pad, pad)], 'constant')
col = np.zeros((N, C, filter_h, filter_w, out_h, out_w))
for y in range(filter_h):
y_max = y + stride * out_h
for x in range(filter_w):
x_max = x + stride * out_w
col[:, :, y, x, :, :] = img[:, :, y:y_max:stride, x:x_max:stride]
col = col.transpose(0, 4, 5, 1, 2, 3) # (N, C, filter_h, filter_w, out_h, out_w) -> (N, filter_w, out_h, out_w, C, filter_h)
col = col.reshape(N * out_h * out_w, -1)
return col
・畳み込み層の使用
input_data = np.random.rand(2, 1, 4, 4)*100//1 # number, channel, height, widthを表す
print('========== input_data ===========\n', input_data)
filter_h = 3
filter_w = 3
stride = 1
pad = 0
col = im2col(input_data, filter_h=filter_h, filter_w=filter_w, stride=stride, pad=pad)
print('============= col ==============\n', col)
・出力
========== input_data ===========
[[[[77. 89. 22. 24.]
[51. 27. 81. 89.]
[87. 71. 64. 12.]
[12. 34. 17. 85.]]]
[[[20. 92. 53. 41.]
[66. 54. 6. 29.]
[ 3. 59. 64. 23.]
[18. 82. 92. 37.]]]]
============= col ==============
[[77. 89. 22. 51. 27. 81. 87. 71. 64.]
[89. 22. 24. 27. 81. 89. 71. 64. 12.]
[51. 27. 81. 87. 71. 64. 12. 34. 17.]
[27. 81. 89. 71. 64. 12. 34. 17. 85.]
[20. 92. 53. 66. 54. 6. 3. 59. 64.]
[92. 53. 41. 54. 6. 29. 59. 64. 23.]
[66. 54. 6. 3. 59. 64. 18. 82. 92.]
[54. 6. 29. 59. 64. 23. 82. 92. 37.]]
→入力 ※イメージ:4×4ピクセルの画像2枚の、赤の強さ(RGBのR)
→出力 ※イメージ:3×3のフィルタで入力を抜き出した結果。
★プーリング層★
・プーリング層の実装
def forward(self, x):
# FN: filter_number, C: channel, FH: filter_height, FW: filter_width
FN, C, FH, FW = self.W.shape
N, C, H, W = x.shape
# 出力値のheight, width
out_h = 1 + int((H + 2 * self.pad - FH) / self.stride)
out_w = 1 + int((W + 2 * self.pad - FW) / self.stride)
# xを行列に変換
col = im2col(x, FH, FW, self.stride, self.pad)
# フィルターをxに合わせた行列に変換
col_W = self.W.reshape(FN, -1).T
out = np.dot(col, col_W) + self.b
# 計算のために変えた形式を戻す
out = out.reshape(N, out_h, out_w, -1).transpose(0, 3, 1, 2)
self.x = x
self.col = col
self.col_W = col_W
return out
★CNN全体★
※PCの性能が低いのでデータを大幅に削減する。
# 処理に時間のかかる場合はデータを削減
x_train, d_train = x_train[:50], d_train[:50]
# x_train, d_train = x_train[:5000], d_train[:5000]
x_test, d_test = x_test[:10], d_test[:10]
x_test, d_test = x_test[:1000], d_test[:1000]
Section5:最新のCNN
【要点】
AlexNet:
2012年の画像認識コンテストで優勝したモデル。
サイズ4096の全結合層の出力にドロップアウトを使用して、過学習を防止している。ja.wikipedia.org
【考察】
GPUを用いて高速な学習を実現している。とのこと。
→このあたりになると、単なる個人PCでは難しいと思われる。
※本Sectionのみ、確認テスト、実装なし。