JDAL E資格の受験用に、深層学習day1を勉強した備忘録
#ニューラルネットワーク 全体像
例)動物の特徴より、犬、猫、ネズミを分類するモデル
入力層:説明変数(入力)$x$が入る層
⇒身長、体重、ひげの本数、毛の平均長さ、耳の大きさ、など
中間層:入力層の出力に重み$W$を掛け合わせ、バイアス$b$を足して活性化関数$f$により変換され、次の層へと情報が伝わる
⇒入力層をもとに何かしらの重みとバイアスを学習する
出力層:最終的な結果を出力する
⇒犬の確率、猫の確率、ネズミの確率を出力
⇒それぞれの出力(予測値)を元に、目的変数と予測値の比較(差分)を計算。
その差分を誤差関数を用いて入力層・中間層のパラメータ(重み、バイアス)を更新する
(微分を行い傾きがゼロになるようにパラメータを更新)
<確認テスト>
1.ディープラーニングは、結局何をやろうとしているか2行以内で述べよ。
⇒ロジックを元にしたプログラムではなく、複数の隠れ層を持つNNという数学的なネットワークを用いて、
入力値から出力値を変換する
2.次の中のどの値の最適化が最終目的
①入力値、②出力値、③重み、④バイアス、⑤総入力、⑥中間層入力、⑦学習率
⇒③重み[$W$]と④バイアス[$b$]
<確認テスト>
以下のニューラルネットワークを紙に書け。
入力層:2ノード1層
中間層:3ノード2層
出力層:1ノード1層
#Secction1)入力層~中間層
・入力層は、説明変数(入力)$x$が入る層である。
・中間層(隠れ層)の出力$f(x)$は、入力層の出力に重み$W$を掛け合わせ、
バイアス$b$を足して活性化関数$f$により変換され、次の層へと情報が伝わる。
f(x)=wTx+b
<確認テスト>
入力層~中間層の図式に動物分類の例を入れる
<確認テスト>
次の数式をPythonにて実装
u=w1x1+w2x2+w3x3+w4x4+b=Wx+b
import numpy as np
u = np.dot(x, W) + b
<確認テスト>
1-1のファイルから中間層の出力を定義しているコードを示せ
# 中間層出力
z = functions.sigmoid(u)
print_vec("中間層出力", z)
#Secction2)活性化関数
活性化関数は、入力の和$wTx(+b)$を出力に変換する関数$f$のこと。
ニューラルネットワークでは、活性化関数に非線形関数を用いる。
(例)
シグモイド関数
課題:勾配消失問題
\sigma(x)=\dfrac{1}{1+\mathrm{e}^{-ax}}
ステップ関数
課題:0-1の間を表現できず、線形分離可能なものしか学習できなかった
{f(x) = \left\{
\begin{array}{ll}
1 & (x \geq 0) \\
0 & (x \lt 0)
\end{array}
\right.
}
ReLU
RELU関数。勾配消失問題の回避とスパース化(モデルの中身がシンプルになる)に貢献
{f(x) = \left\{
\begin{array}{ll}
x & (x \geq 0) \\
0 & (x \lt 0)
\end{array}
\right.
}
・全結合NN-単層・複数ノード
⇒活性化関数の効果で一部の出力は弱く、一部は強く伝播される
<確認(復習)テスト>
線形と非線形の違いを図にかいて簡易に説明せよ。
z1 = functions.sigmoid(u)
上記の説明:functions.py というファイルの中にsigmoid関数が定義されている。
下記のように定義されている関数を呼び出して使う
# 中間層の活性化関数
# シグモイド関数(ロジスティック関数)
def sigmoid(x):
return 1/(1 + np.exp(-x))
#Secction3)出力層
出力層の出力は目的に合致したものである必要がある(例えば、各クラスの」確率)
⇒あるデータ①:犬の確率:0.82、猫の確率:0.07、ネズミの確率:0.11 よって判定は犬
②:犬の確率:0.32、猫の確率:0.38、ネズミの確率:0.30 よって判定は猫
・誤差関数
上記の出力データに対し、訓練(正解)データと比較する誤差関数を使って誤差を算出する
二乗誤差:$E(w) = \displaystyle{\frac{1}{2}\sum_i ( y^{(i)} - d^{(i)} ) )^2}=\frac{1}{2}||\mathbf{y}-\mathbf{d}||^2$
・出力層の活性化関数
出力層用の活性化関数
ソフトマックス関数
恒等写像
シグモイド関数(ロジスティック関数)
<実装演習>
# 順伝播(3層・複数ユニット)
# ウェイトとバイアスを設定
# ネートワークを作成
def init_network():
print("##### ネットワークの初期化 #####")
network = {}
#試してみよう
#_各パラメータのshapeを表示
#_ネットワークの初期値ランダム生成
network['W1'] = np.array([
[0.1, 0.3, 0.5],
[0.2, 0.4, 0.6]
])
network['W2'] = np.array([
[0.1, 0.4],
[0.2, 0.5],
[0.3, 0.6]
])
network['W3'] = np.array([
[0.1, 0.3],
[0.2, 0.4]
])
network['b1'] = np.array([0.1, 0.2, 0.3])
network['b2'] = np.array([0.1, 0.2])
network['b3'] = np.array([1, 2])
print_vec("重み1", network['W1'] )
print_vec("重み2", network['W2'] )
print_vec("重み3", network['W3'] )
print_vec("バイアス1", network['b1'] )
print_vec("バイアス2", network['b2'] )
print_vec("バイアス3", network['b3'] )
return network
# プロセスを作成
# x:入力値
def forward(network, x):
print("##### 順伝播開始 #####")
W1, W2, W3 = network['W1'], network['W2'], network['W3']
b1, b2, b3 = network['b1'], network['b2'], network['b3']
# 1層の総入力
u1 = np.dot(x, W1) + b1
# 1層の総出力
z1 = functions.relu(u1)
# 2層の総入力
u2 = np.dot(z1, W2) + b2
# 2層の総出力
z2 = functions.relu(u2)
# 出力層の総入力
u3 = np.dot(z2, W3) + b3
# 出力層の総出力
y = u3
print_vec("総入力1", u1)
print_vec("中間層出力1", z1)
print_vec("総入力2", u2)
print_vec("出力1", z1)
print("出力合計: " + str(np.sum(z1)))
return y, z1, z2
# 入力値
x = np.array([1., 2.])
print_vec("入力", x)
# ネットワークの初期化
network = init_network()
y, z1, z2 = forward(network, x)
出力結果
*** 入力 ***
[1. 2.]
ネットワークの初期化
*** 重み1 ***
[[0.1 0.3 0.5]
[0.2 0.4 0.6]]
*** 重み2 ***
[[0.1 0.4]
[0.2 0.5]
[0.3 0.6]]
*** 重み3 ***
[[0.1 0.3]
[0.2 0.4]]
*** バイアス1 ***
[0.1 0.2 0.3]
*** バイアス2 ***
[0.1 0.2]
*** バイアス3 ***
[1 2]
順伝播開始
*** 総入力1 ***
[0.6 1.3 2. ]
*** 中間層出力1 ***
[0.6 1.3 2. ]
*** 総入力2 ***
[1.02 2.29]
*** 出力1 ***
[0.6 1.3 2. ]
出力合計: 3.9
・二乗する理由:正の値にするため
・1/2する理由:微分するときに簡単にするため(誤差逆伝播の計算で誤差関数の微分を用いる)
コードで表示では
def mean_squared_error(d, y):
return np.mean(np.square(y - d)) / 2
def softmax(x):
if x.ndim == 2:
x = x.T
x = x - np.max(x, axis=0)
y = np.exp(x) / np.sum(np.exp(x), axis=0)
return y.T
x = x - np.max(x) # オーバーフロー対策
return np.exp(x) / np.sum(np.exp(x))
#Secction4)勾配降下法
勾配降下法は、誤差$E(w)$を最小化するパラメータ$w$を探査するための方法
勾配降下法の種類:
勾配降下法
確率的勾配降下法
ミニバッチ勾配降下法
<勾配降下法>
特徴
・大きい場合には発散し収束しない。
・小さい場合は発散はしないが、収束まで時間がかかったり、
正しい収束点ではないところで計算が終わる可能性がある
<確率的勾配降下法(SGD)>
特徴
・データが多いときのコストの削減
・望まない局所極小解を回避する
・オンライン学習が出来る
<ミニバッチ勾配降下法>
特徴
・ミニバッチ勾配降下には計算機の計算資源を有効利用できるメリットがある
オンライン学習はデータをランダムに1件ずつ投入してモデルを更新していく手法
特徴
・1件ずつデータを投入するためメモリ使用量も少なく、モデル学習にかかる計算負荷も小さい
・バッチ学習が不得意とするリアルタイムでモデル更新を頻繁に行うケースにも適用しやすい
{W^{t+1}=W^t+\varepsilon∇E
}
network[key] -= learning_rate * grad[key]
{∇E=\frac{\partial E}{\partial W}=\begin{bmatrix}\frac{\partial E}{\partial w_1}・・・\frac{\partial E}{\partial w_M}\end{bmatrix}
}
grad = backward(x, d, z1, y)S4)
#Secction5)誤差逆伝播法
誤差勾配の計算
数値微分⇒計算量が非常に大きくなる
誤差逆伝播法⇒算出される誤差を、出力側から順に微分し、前の層へと伝播
最小限の計算で各パラメータでの微分値を解析的に計算する方法
計算結果(=誤差)から微分を逆算することで、不要な再帰的計算を避けて微分を算出できる
誤差逆伝播法は計算コストが抑えられるメリットがあるが、勾配消失問題を引き起こす原因となる
# 出力層でのデルタ
delta2 = functions.d_mean_squared_error(d, y)
一度微分したものを、delta2と定義すると、
b2の勾配、 W2の勾配、中間層でのデルタにて使用することができる。
# 出力層でのデルタ
delta2 = functions.d_mean_squared_error(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)
## 試してみよう
delta1 = np.dot(delta2, W2.T) * functions.d_sigmoid(z1)
delta2 = functions.d_mean_squared_error(d, y)
delta1 = np.dot(delta2, W2.T) * functions.d_sigmoid(z1)
grad['W1'] = np.dot(x.T, delta1)
<よく出る問題>
シグモイド関数の微分について記述する
(誤差逆伝播法において、シグモイド関数を微分すると最大値が0.25となり勾配消失問題が発生しやすい)
下記のように定義したとき
{sigmoid(z) = \frac{1}{1 + e^{-z}}
}
{f(z) = sigmoid(z) \\
}
としたときに、$f(z)$の微分を行うと
{f(z)' = (1 - f(z)) \cdot f(z)
}
となる
pythonにて実装してみると
import matplotlib.pyplot as plt
import numpy as np
def sigmoid(x):
return 1 / (1 + np.exp(-x))
x = np.linspace(-10, 10)
#シグモイド関数
y = sigmoid(x)
plt.plot(x, y)
#シグモイド関数の微分
dy = (1 - sigmoid(x)) * sigmoid(x)
plt.plot(x, dy)
plt.show()