ラビットチャレンジ
ラビットチャレンジはE資格の受験に必要な協会認定講座のプログラムです。
ラビットチャレンジでは認定プログラムを修了してE資格の受験資格を習得するために、指定のレポートとテストで合格点を取得しなければなりません。
今回はStage3深層学習前編(day2)についてまとめます。
- Stage1 応用数学
- Stage2 機械学習
- stage3 深層学習前編(day1, day2)
- stage4 深層学習後編(day3, day4)
Section1 勾配消失問題
誤差逆伝播法が下位層に進んでいくにつれて勾配がどんどん緩やかになっていく。
そのため、勾配降下法による更新では下位層のパラメータはほとんど変わらず、訓練は最適値に収束しなくなる。
なかでも活性化関数にシグモイド関数を使った場合には、大きな値を入力したときに出力の変化が微小であるため勾配消失問題を引き起こすことがあった。
勾配消失問題を解決するには以下の3方法が考えられる
・活性化関数の選択
・重みの初期値設定
・バッチ正規化
活性化関数の選択
ReLU関数を使うことで勾配消失問題の回避を行うことができる
シグモイド関数 | ReLU関数 |
---|---|
![]() |
![]() |
初期値の設定方法
重みの初期設定値を工夫することで勾配消失問題の回避を行うことができる
・重みの初期設定値にXavierの初期値を設定する
重みの要素を前の層のノード数の平方根で除算した値にする
ある程度のバラツキを持ち、活性化関数の表現力を保つことができる
S字カーブの活性化関数に有効
ne['w1'] = np.random.randn(input_size, hidden_size) / np.sqrt(input_size)
ne['w2'] = np.random.randn(input_size, hidden_size) / np.sqrt(input_size)
・重みの初期設定値にHeの初期値を設定する
重みの要素を前の層のノード数の平方根で除算した値に対し$\sqrt 2$を掛け合わせた値
ReLU+Heできれいに分布する
ne['w1'] = np.random.randn(input_size, hidden_size) / np.sqrt(input_size) * np.sqrt(2)
ne['w2'] = np.random.randn(input_size, hidden_size) / np.sqrt(input_size) * np.sqrt(2)
バッチ正規化
バッチ正規化とはミニバッチ単位で入力値のデータの偏りを抑制する手法である。
活性化関数に値を渡す前後にバッチ正規化の処理を孕んだ層を加える。
Section2 学習最適化手法
学習率の値が大きい場合に最適値にいつまでもたどり着かず発散してしまう。
学習率の値が小さい場合に収束するまでに時間がかかるか、大域局所最適地に収束しづらくなってしまう。
学習率もハイパーパラメータであるため、初期値を決定する必要があるが、学習率最適化手法を利用して学習率を最適化することができる。
最適化の手法にはいくつかある
・モメンタム、AdaGrad、RMStop、Adamなど
最適化手法 | 内容 | メリット |
---|---|---|
勾配降下法 | 誤差をパラメータで微分したものと学習率の積を減算する | |
モメンタム | 誤差をパラメータで微分したものと、学習率の積を減算した後、現在の重みに前回の重みを減算した値と慣性の積を加算する | 局所的最適解にはならず、大域的最適解になる。 谷間に近づいてから最も低い位置(最適値)に行くまでの時間が早い |
AdaGrad | 誤差をパラメータで微分したものと再学習した学習率の積を減算する。 | 勾配の緩やかな斜面に対して、最適地に近づける。 |
RMStop | 誤差をパラメータで微分したものと再定義した学習率の積を減算する | 局所的最適解にはならず、大域的最適解になる。 ハイパーパラメータの調整が必要な場合が少ない。 |
Adam | モメンタムの過去の勾配の指数関数的減衰平均 RMSPropの、過去勾配の2乗の指数関数的減衰平均 それぞれを孕んだ最適化アルゴリズム |
モメンタムとRMSPropのいいとこどり。 |
勾配降下法
w^{t+1} = w^{t}-\epsilon \nabla E
モメンタム
V_t = \mu V_{t-1} -\epsilon \nabla E\\
w^{t+1} = w^{t}+V_t\\
※$\mu$:慣性
Adagrad
h_0 = \theta
h_t = h_{t-1}+(\nabla E)^2\\
w^{t+1} = w^{t}- \epsilon\frac{1}{\sqrt h_t + \theta} \nabla E
RMStop
h_t = \alpha h_{t-1}+(1-\alpha)(\nabla E)^2\\
w^{t+1} = w^{t}- \epsilon\frac{1}{\sqrt h_t + \theta} \nabla E
Section3 過学習
過学習とはテスト誤差と訓練誤差で乖離し性能があがらないこと。
特定の訓練サンプルに対して特化して学習してしまっている。
原因としては、パラメータの数が多い、値が適切でない、ノードが多いなど様々な原因がある。
これらを正則化を使ってネットワークの自由度(表現力)を制約することで、過学習を抑制することができる。
手法としては、L1正則化、L2正則化、ドロップアウトなどがある
L1,L2正則化
誤差関数にPノルムを加える
E_n(W)+\frac{1}{p}\lambda||x||\\
||x||_p=(|x_1|^p+...+|x_n|^p)^{\frac{1}{p}}
P=1の場合はL1正則化(Lasso)という
P=2の場合はL2正則化(Ridge)という
L1の場合はマンハッタン距離($x+y$)となり、L2の場合はユークリッド距離($\sqrt{x^2+y^2}$)となる
ドロップアウト
ドロップアウトとはランダムにノードを削除して学習させることである。
データ量を変化させずに、異なるモデルを学習させることができる
Section4 畳み込みニューラルネットワーク
畳み込みニューラルネットワーク(CNN)では次元間でつながりのあるデータを扱うことができる。
例えば、画像だと隣のピクセルとつながりがある。
入力層⇒畳み込み層⇒プーリング層⇒全結合層⇒出力
畳み込み層
畳み込み層では画像の場合、縦、横、チャンネルの3次元のデータをそのまま学習し、次に伝えることができる。
4x4の画像に対して3x3のフィルターによって2x2の値が求まる。
このときそれぞれ隣の値を見ているため次元のつながりを保っている
入力画像とフィルターを掛け合わせて出力する
左上の3x3とフィルターの3x3を掛け合わせ足したものをも出力画像の左上に設定
右上の3x3とフィルターの3x3を掛け合わせ足したものをも出力画像の右上に設定
このように入力画像より小さなフィルターを通すと出力画像は小さくなる
バイアス
出力画像にバイアス値を足す
パディング
入力画像の周りにデータを追加する
追加する値は0や端の値などいろいろな追加方法がある
値を追加することで出力される画像が小さくならない
ストライド
フィルターの移動量と変更する
ストライド1どと隣の移動し、ストライド2だと1つ飛ばして移動する
チャンネル
フィルターの数を表す
プーリング層
対称領域から特定の値を取り出す
MAXプーリング
対称領域の中で最大のものを取得する
平均値プーリング
対称領域の平均値を取得する
Section5 最新のCNN
※CNNは常に進化しているため現在の最新ではありません
AlexNetのモデル説明
2012年のILSVRCで優勝したCNN
過学習を防ぐためにサイズ4096の全結合層の出力にドロップアウトを採用している
ILSVRはImageNet large Scale Visual Recognition Challengeの略
ImageNetと呼ばれる大規模画像データセットに対して画像認識を行い精度を争うコンペティションである。
ImageNetには1枚の画像に対して、1つの正解データが付与されている
画像は以下からダウンロードすることが可能
https://image-net.org/
サイトによると1,000のオブジェクトクラス、1,281,167枚のトレーニング画像、50,000枚の検証画像、100,000ののテスト画像が含まれKaggleで使用することが可能。
ILSVRC2010の1000カテゴリ例
https://image-net.org/challenges/LSVRC/2010/browse-synsets.php
・フライドポテト、マッシュポテトの、黒オリーブ
・サンゴの木、日本パゴダツリー
・冷蔵庫、洗濯機
など多岐にわたる
確認テスト
確認テスト1-1:連鎖率(10P)
問:連鎖率の原理を使い、dz/dxを求めよ
z=t^2\\
t=x+y
解答:
\dfrac{\partial z}{\partial x} = \dfrac{\partial z}{\partial t}\dfrac{\partial t}{\partial x}\\
\dfrac{\partial z}{\partial t} = 2t\\
\dfrac{\partial x}{\partial t} = 1\\
\dfrac{\partial z}{\partial x} = \dfrac{\partial z}{\partial t}\dfrac{\partial t}{\partial x}=2t \times 1 = 2(x+y)
確認テスト1-2:シグモイド関数(18P)
問:シグモイド関数を微分した時、入力値が0の時に最大値をとる。
その値ととして正しいものを選択肢から選べ。
解答:
シグモイド関数を微分した結果
f(x) = \frac{1}{1+e^{-x}}\\
f(x)'=(1-f(x))f(x)
となり$x=0$とすると
f(x)'=(1-f(x))f(x)\\
f(x)'=(1-f(0))f(0) =(1-0.5)(0.5) = 0.25
コードで確認
import matplotlib.pyplot as plt
import numpy as np
def sigmoid(x):
return 1 / (1 + np.exp(-x))
x = np.linspace(-10, 10, 100)
# シグモイド
y = sigmoid(x)
plt.plot(x, y)
plt.show()
# シグモイド関数の微分
y = (1 - sigmoid(x)) * sigmoid(x)
plt.plot(x, y)
plt.show()
print('シグモイド関数を微分した結果:{0:0.2f}'.format((1 - sigmoid(0)) * sigmoid(0)))
結果
シグモイド関数 | シグモイド関数を微分 |
---|---|
![]() |
![]() |
シグモイド関数を微分した結果:0.25
グラフで見ても0.25となっていることが確認できる
確認テスト1-3:シグモイド関数(26P)
問:重みの初期値に0を設定すると、どのような問題が発生するか簡潔に説明せよ。
解答:全ての重みの値が均一に更新されるため、多数の重みをもつ意味がなくなる
import sys, os
sys.path.append(os.pardir) # 親ディレクトリのファイルをインポートするための設定
import numpy as np
from common import functions
import matplotlib.pyplot as plt
# 行列を1行で表示
def _print_matrix(name, mat):
mat_dim1 = mat.reshape(mat.size)
mat_size = mat_dim1.size
print(name + ':[', end = '')
for i in range(mat_size):
print('{0:.2f}'.format(mat_dim1[i]), end = '')
if i != mat_size - 1:
print(', ', end = '')
print('] ', end = '')
# ウェイトとバイアスを初期化
def init_network():
print("##### ネットワークの初期化 #####")
network = {}
network['W1'] = np.array([
[0.0, 0.0, 0.0],
[0.0, 0.0, 0.0]
])
network['W2'] = np.array([
[0.0, 0.0],
[0.0, 0.0],
[0.0, 0.0]
])
network['b1'] = np.array([0.0, 0.0, 0.00])
network['b2'] = np.array([0.0, 0.0])
_print_matrix("w1", network['W1'])
_print_matrix("w2", network['W2'])
_print_matrix("b1", network['b1'])
_print_matrix("b2", network['b2'])
print('')
return network
# 順伝播
def forward(network, x):
W1, W2 = network['W1'], network['W2']
b1, b2 = network['b1'], network['b2']
u1 = np.dot(x, W1) + b1
z1 = functions.relu(u1)
u2 = np.dot(z1, W2) + b2
y = functions.softmax(u2)
return y, z1
# 誤差逆伝播
def backward(x, d, z1, y):
grad = {}
W1, W2 = network['W1'], network['W2']
b1, b2 = network['b1'], network['b2']
# 出力層でのデルタ
delta2 = functions.d_sigmoid_with_loss(d, y)
# b2の勾配
grad['b2'] = np.sum(delta2, axis=0)
# W2の勾配
grad['W2'] = np.dot(z1.T, delta2)
# 中間層でのデルタ
delta1 = np.dot(delta2, W2.T) * functions.d_relu(z1)
# b1の勾配
grad['b1'] = np.sum(delta1, axis=0)
# W1の勾配
grad['W1'] = np.dot(x.T, delta1)
return grad
# 訓練データ
x = np.array([[1.0, 5.0]])
# 目標出力
d = np.array([[0, 1]])
# 学習率
learning_rate = 0.01
network = init_network()
print("##### 結果表示 #####")
for i in range(1000):
y, z1 = forward(network, x)
# 誤差
loss = functions.cross_entropy_error(d, y)
grad = backward(x, d, z1, y)
for key in ('W1', 'W2', 'b1', 'b2'):
network[key] -= learning_rate * grad[key]
print('# 更新{0:4d}回目'.format(i + 1), end = '')
_print_matrix('W1', network['W1'])
_print_matrix('W2', network['W2'])
_print_matrix('b1', network['b1'])
_print_matrix('b2', network['b2'])
print('')
結果
ネットワークの初期化
w1:[0.00, 0.00, 0.00, 0.00, 0.00, 0.00] w2:[0.00, 0.00, 0.00, 0.00, 0.00, 0.00] b1:[0.00, 0.00, 0.00] b2:[0.00, 0.00]
結果表示
更新 1回目W1:[0.00, 0.00, 0.00, 0.00, 0.00, 0.00] W2:[0.00, 0.00, 0.00, 0.00, 0.00, 0.00] b1:[0.00, 0.00, 0.00] b2:[-0.01, 0.01]
更新 2回目W1:[0.00, 0.00, 0.00, 0.00, 0.00, 0.00] W2:[0.00, 0.00, 0.00, 0.00, 0.00, 0.00] b1:[0.00, 0.00, 0.00] b2:[-0.01, 0.01]
更新 999回目W1:[0.00, 0.00, 0.00, 0.00, 0.00, 0.00] W2:[0.00, 0.00, 0.00, 0.00, 0.00, 0.00] b1:[0.00, 0.00, 0.00] b2:[-1.45, 1.45]
更新1000回目W1:[0.00, 0.00, 0.00, 0.00, 0.00, 0.00] W2:[0.00, 0.00, 0.00, 0.00, 0.00, 0.00] b1:[0.00, 0.00, 0.00] b2:[-1.45, 1.45]
確認テスト1-4:バッチ正規化(29P)
問:一般的に考えられるバッチ正規化の効果を2点挙げよ。
解答:
学習が安定化する
学習速度が速くなる
確認テスト2-1:学習率最適化手法(44P)
問:モメンタム、AdaGrad、RMStopの特徴をそれぞれ簡潔に説明せよ。
解答:
・モメンタム|AdaGrad|RMStop|
|:---:|:---:|:---:|:---:|
|・局所的最適解にならない
・大域的最適解となる|・勾配の緩やかな斜面に対して最適値に近づける
・学習率が徐々に小さくなるので鞍点問題を引き起こす|・AdaGradの鞍点問題に対応したもの||
確認テスト3-1:正則化
問:機械学習で使われる線形モデルの正則化はモデルの重みを制限することで可能となる。
前述の線形モデルの正則化手法の中にリッジ回帰という手法があり、その特徴として正しいものを選択しなさい。
a) ハイパーパラメータを大きな値に設定するとすべての重みが限りなく0に近づく
b) ハイパーパラメータを0に設定すると、非線形回帰となる
c) バイアス項についても正則化される
d) リッジ回帰の場合、隠れ層に対して正則化項を加える
解答:a
bはラッソ回帰
確認テスト3-2:L1正則化 (64P)
問:下図についてL1正則化を表しているグラフはどちらか答えよ
※図はラビットチャレンジより
解答:右側
L1はマンハッタン距離になるため角がある
L2はユークリッド距離になるため丸くなる
確認テスト4-1:畳み込み (95P)
問:サイズ6x6の入力画像を、サイズ2x2のフィルタで畳み込んだ時の出力画像のサイズを答えよ。
なおストライドとパディングは1とする
解答:
公式
\frac{画像の高さ+2\times パディング高-フィルタ高}{ストライド}+1
値を入れると
\frac{6+2\times 1-2}{1}+1 = 7
答え:7x7
コード実装
勾配消失問題の解決 (53P)
勾配消失問題が発生している学習に対して、活性化関数の変更、重みの初期値の変更を行う
元々の活性化関数(シグモイド)
hidden_d_f = functions.d_sigmoid
ReLUに変更
hidden_d_f = functions.d_relu
Xavierの初期値を適用
network['W1'] = np.random.randn(input_layer_size, hidden_layer_1_size) / (np.sqrt(input_layer_size))
network['W2'] = np.random.randn(hidden_layer_1_size, hidden_layer_2_size) / (np.sqrt(hidden_layer_1_size))
network['W3'] = np.random.randn(hidden_layer_2_size, output_layer_size) / (np.sqrt(hidden_layer_2_size))
Heの初期値を適用
network['W1'] = np.random.randn(input_layer_size, hidden_layer_1_size) / np.sqrt(input_layer_size) * np.sqrt(2)
network['W2'] = np.random.randn(hidden_layer_1_size, hidden_layer_2_size) / np.sqrt(hidden_layer_1_size) * np.sqrt(2)
network['W3'] = np.random.randn(hidden_layer_2_size, output_layer_size) / np.sqrt(hidden_layer_2_size) * np.sqrt(2)
重みの初期値はランダム | 重みの初期値にXavierの初期値を使用 | 重みの初期値にHeの初期値を使用 | |
---|---|---|---|
活性化関数にシグモイド関数を使用 | ![]() |
![]() |
![]() |
活性化関数にReLUを使用 | ![]() |
![]() |
![]() |
ReLUを使った方が精度が高い
また、重みの初期値を変えることで勾配消失問題が解消している
バッチ正規化を確認する
※活性化関数にはシグモイド関数、重みにXavierの初期値を使用
バッチ正規化を使わない | バッチ正規化を使う |
---|---|
![]() |
![]() |
バッチ正規化を使うことで勾配消失問題が解消しているい
SDGで学習率を変えてみる
0.1 | 0.5 | 1.0 |
---|---|---|
![]() |
![]() |
![]() |
モメンタムで慣性$mu$と活性化関数を変えてみる
0.5 | 0.9 | 0.5/LeRU | 0.9/LeRU |
---|---|---|---|
![]() |
![]() |
![]() |
![]() |
AdaGrad/RMStop/Adamを試してみる
AdaGrad | RMStop | Adam |
---|---|---|
![]() |
![]() |
![]() |
学習最適化過学習の解決 (73P)
L1/L2正則化、ドロップアウトを試してみる
正則化なし | L2(0.1) | L1(0.005) | Drop(0.15) | L1(0.004)+Drop(0.08) |
---|---|---|---|---|
![]() |
![]() |
![]() |
![]() |
![]() |
畳み込み層の確認 (90P)
畳み込みの確認
畳み込み1層 | 畳み込み2層 | 畳み込み6層 |
---|---|---|
![]() |
![]() |
![]() |
終わらないため入力データおよびエポック数を削減し確認したが
非常に時間がかかる・・・