くますきIT日記

IT系資格、競技プログラミングの情報を書いていきます。

E資格の勉強④深層学習day2

目次

本投稿の目的

深層学習(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となってしまう。

  

keisan.casio.jp

 

勾配消失問題の解決法

【要点】

複数の解決方法が存在する。

 ①活性化関数の選択:

  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

ichi.pro

 →考察:バッチ正規化の目的 = バッチごとにバラつく可能性がある入力を安定させて、学習も安定させてしまおう、という考え。

 

Section2:学習率最適化手法

【要点】

 学習率:

  更新量を反映させる際の割合。適切な値を設定する必要がある。

   学習率が大きすぎる場合:

    → 最適解にたどり着かない。

     ※ふり幅が大きすぎる振り子のイメージ。

   学習率が小さすぎる場合:

    → 最適解にたどり着つまでに時間を要する。局所最適解から動けなくなる。

      ※ふり幅が小さすぎる振り子のイメージ。

 

  → 基本的な方針として、学習率を動的にして、

    初めは大きく、学習が進むにつれて小さくする。

 

 学習率最適化手法:

  学習率を調整する方法。

   ・モメンタム(慣性):勾配降下法の調整に加えて、以下を加算する。

    → 絶対値[現在 - 前回の重さ] × 慣性(前回動いた分の慣性が働くイメージ)

   ・AdaGrad

   ・RMSProp

   ・★Adam★ ←最も主流の手法。モメンタムとRMSPropの長所を持っている。

 

【考察結果】

確認テスト1:モメンタム・AdaGrad・RMSPropの特徴をそれぞれ簡潔に説明せよ。

 (回答)

 ・モメンタム:

  学習率が一定。最適解に近づく時間が早い。大域的最適解に移動できる。

 ・AdaGrad:

  学習率が可変。モメンタムより事前設定必要なハイパーパラメータが少ないため、設定が容易。

  (モメンタム=学習率と慣性。AdaGrad=学習率のみ)

  学習が進むほど学習率が低下する。鞍点問題※の問題がある。

  ※馬の鞍のように、なだらかな地点(精度が上がる点が遠くにある)場合、

   学習を行えなくなる問題。(学習量が小さい → 精度が上がる点までたどり着けない → 学習できない)  

 ・RMSProp:

  学習率が可変。AdaGradを改善した物。

 

【演習】

SGD

(実行結果)

f:id:you_it_blog:20211020160552p:plain

→ 学習データ、検証データ共に精度が上がらない。

 

(実行結果※バッチ正規化あり)

f:id:you_it_blog:20211020162232p:plain

→ ある程度改善した。60%の割合で正解できる。

 

②momentum

(実行結果)

f:id:you_it_blog:20211020160816p:plain

→ 学習データ、検証データ共に精度が上がらない。

 

(実行結果※バッチ正規化あり)

f:id:you_it_blog:20211020162327p:plain

→ 大きく改善した。(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]))

(実行結果)

f:id:you_it_blog:20211020163238p:plain

→ 学習データ、検証データ共に精度が上がらない。

 

(実行結果※バッチ正規化あり)

f:id:you_it_blog:20211020162737p:plain

→ 大きく改善した。(80%の精度)

 

④RMSProp

(実行結果)

f:id:you_it_blog:20211020163152p:plain

→ バッチ正規化なしでも、非常に高い精度。

 

⑤Adam

(実行結果)

f:id:you_it_blog:20211020160459p:plain

→ バッチ正規化なしでも、非常に高い精度。

Section3:過学習

【要点】

過学習については、機械学習で学習済み。(忘れている場合、機械学習のレポートを参照する)

ここでは、「正則化 = 過学習を防止する方法」について、記載する。

 ・L1正則化、L2正則化
  誤差関数に各ノルムを加えて重みを抑える方法。ノルムは以下が参考になる。

  ※L1ノルム:ダイヤ型(マンハッタン距離)、L2ノルム:円(ユークリッド距離)と認識しておく。
manabitimes.jp

 

 ・ドロップアウト

  ノードをランダムに削除して学習させる。

  ※一般的には隠れ層(中間層)の場合は50%ほどのノードを無効化するとよい、とのこと。

sonickun.hatenablog.com

 

【考察結果】

確認テスト1:線形モデルの正則化の手法の中にリッジ回帰という手法があり、その特徴として正しい物を選択しなさい。

 (a)ハイパーパラメータを大きな値に設定すると、全ての重みが限りなく0に近づく。

 (b)ハイパーパラメータを0に設定すると、非線形回帰となる。

 (c)バイアス項についても、正則化される。

 (d)リッジ回帰の場合、隠れ層に対して正則加工を加える。

 

(回答)

 (a)ハイパーパラメータを大きな値に設定すると、全ての重みが限りなく0に近づく。

(考察)

 リッジ回帰(L2ノルム)のハイパーパラメータを大きくする → 罰則が大きい → 重みが小さくなる。

 

確認テスト2:下図について、L1正則化を表しているグラフはどちらかを答えよ。

f:id:you_it_blog:20211020225627p:plain

(回答)

 L1ノルム = マンハッタン距離なのでダイヤ型。答えは右の図。

 

【演習】

過学習が発生しているデータ。

 → 練習用データの精度は高いが、テスト用データの判定率が低い。

f:id:you_it_blog:20211020231127p:plain

 

②L2正則化

f:id:you_it_blog:20211020231301p:plain

 

③L1正則化

f:id:you_it_blog:20211020231412p:plain

 

ドロップアウト

パターン1:ドロップアウト割合=15%

 →比較的、適切に学習できるようになっているが、最終的に精度が乖離(≒過学習)となっている。

f:id:you_it_blog:20211020231458p:plain

 

パターン2:ドロップアウト割合=50%

 →全く学習が進んでいない。(ドロップアウトしすぎている)

f:id:you_it_blog:20211020233158p:plain

 

ドロップアウト + L1正則化

パターン1:ドロップアウト割合=8% かつ 正則化強度=0.004

 →L2正則化に近い形となっている。

f:id:you_it_blog:20211020231658p:plain

 

パターン2:ドロップアウト割合=12% かつ 正則化強度=0.006 (パターン1の1.5倍)

 →精度を上げ切れていない。正則化が強すぎると思われる。

f:id:you_it_blog:20211020233635p:plain

 

Section4:畳み込みニューラルネットワーク(CNN)の概念

【要点】

CNN:画像や動画認識で広く使われるモデル。以下のような構造を持つ。

 ・入力層→畳み込み層→プーリング層→全結合層→出力層

ja.wikipedia.org


※学習のターゲットとして、LeNetを題材としている。

ja.wikipedia.org

 

【各層の説明】

畳み込み層:

 3次元の空間画像も学習できるような層。

 出力 = 活性化関数(入力 * フィルター(≒重み) + バイアス)

 ★用語★

 バイアス:

  バイアス項と同じ。

 パディング:

  入力(中途半端なサイズ)を処理しやすいサイズに調整する。(埋める)

  調整分は0埋めか、周囲と同じ値などを設定する。

 ストライド

  フィルターの移動量。

 チャンネル:

  入力を判定するフィルターの数の様な物。異なる複数のフィルターを使用して、

  識別性能を向上させる。

 

プーリング層:

 入力(=畳み込み層の出力)から、特定の値を取得する。(最大値や平均値)

 単語の意味を把握すると覚えやすい。

 

 

※プーリング:統計学には「グループをたばねる」という意味。

toukeigaku-jouhou.info

 

【考察結果】

確認テスト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のみ、確認テスト、実装なし。