1.はじめに
深層学習day1について学習した内容をまとめました.
2.ニューラルネットワーク
・脳の神経回路網をアルゴリズム化したもの
・入力層,中間層,出力層で構成されている.
・中間層が多数になるとDNN(Deep Neural Network)と呼ぶ.
・回帰や分類に用いることができる.
確認テスト1
問:ディープラーニングはなにをしようとしているか?
解答:多数の中間層を設けることで,入力に対する欲しい出力を得るための近似関数を作成する.
確認テスト2
問:次の中のどの値の最適化が最終目的か.全て選べ.
①入力値[X] ②出力値[Y] ③重み[W] ④バイアス[b]
⑤総入力[u] ⑥中間層入力[z] ⑦学習率[ρ]
解答:②重みと④バイアス
確認テスト3
問:次のネットワークを紙にかけ.
入力層︓2ノード1層,中間層︓3ノード2層,出力層︓1ノード1層
3.入力層~中間層
①入力値[X] ②出力値[Y] ③重み[W] ④バイアス[b] ⑤総入力[u] ⑥出力[z] ⑦活性化関数[f]とし,
入力層のノード数が4,中間層のノード数が1の時,
中間層の総入力uの式は次のようになる.
\boldsymbol{w}=[w_1,w_2,w_3,w_4] \\
\boldsymbol{x}=[x_1,x_2,x_3,x_4] \\
u =\boldsymbol{wx} =w_1x_1+w_2x_2+w_3x_3+w_4x_4+b
確認テスト4
問:上の式をpythonで書け.
解答:下記
import numpy as np
# 入力
x = np.random.rand(4)
print("入力", x)
# 重み
W = np.random.rand(4)
print("重み", W)
# バイアス
b = np.random.rand(1)
print("バイアス", b)
# 総入力
u = np.dot(x, W) + b
print("総入力", u)
確認テスト5
問:1-1のファイルから中間層の出力を定義しているソースを抜き出せ.
解答:下記
# 2層の総入力
u2 = np.dot(z1, W2) + b2
# 2層の総出力
z2 = functions.relu(u2)
4.活性化関数
ニューラルネットワークにおいて,次の層への出力の大きさを決める非線形の関数.
入力値の値によって,次の層への信号のON/OFFや強弱を定める働きをもつ.
以下の3つは主に中間層に用いられる(シグモイド関数は出力層にも用いられる).
ステップ関数
→しきい値を超えたら発火する関数であり,出力は常に1か0.
→パーセプトロン(ニューラルネットワークの前身)で利用された関数.
→0~1間の間を表現できず,線形分離可能なものしか学習できなかった(微分も不可).
f(x) = \left\{
\begin{array}{ll}
1 & (x \geq 0) \\
0 & (x \lt 0)
\end{array}
\right.
シグモイド関数
→非線形で微分可能な関数.
→パーセプトロン(ニューラルネットワークの前身)で利用された関数.
→0~1の間を緩やかに変化する関数で,ステップ関数ではON/OFFしかない状態に対し,
信号の強弱を伝えられるようになった.
→大きな値では出力の変化が微小なため,勾配消失問題を引き起こす事があった.
f(u) = \frac{1}{1+e^{-u}}
ReLU関数
→よく使われている活性化関数
→入力が負であれば0,正であればそのままの値を出力する.
→勾配消失問題の回避とスパース化に貢献することで良い成果をもたらしている.
f(x) = \left\{
\begin{array}{ll}
x & (x > 0) \\
0 & (x \leq 0)
\end{array}
\right.
確認テスト6
問:線形と非線形の違いを図にかいて簡易に説明せよ.
解答:下記に線形な関数のグラフを示す.
線形関数は次のような条件を満たす関数である.
それぞれ上から順に加法性,斉次性と呼ぶ.
逆に非線形関数は満たさない関数である.
\begin{align}
f(x+y) &= f(x) + f(y) \\ \\
f(kx) &= kf(x)
\end{align}
確認テスト7
問:配布されたソースコードより該当する箇所を抜き出せ.
解答:下記
z1 = functions.sigmoid(u)
演習:活性化関数それぞれのグラフを描画するために作成したコード
import matplotlib.pyplot as plt
def step(x):
y = np.where(x<=0, 0, 1)
return y
def sigmoid(x):
y = 1./(1. + np.exp(-x))
return y
def relu(x):
y = np.maximum(0,x)
return y
# xの値を生成
x = np.arange(-10, 10, 0.01)
# xに対するyを求める
y = relu(x)
# グラフ描画
plt.plot(x,y)
plt.xlabel("x")
plt.ylabel("y")
plt.show()
5.出力層
各ラベルの予測確率,または回帰の値を出力する層である.
誤差関数
正解データ(ラベル)と出力値(予測値)の誤差を示す関数
例:二乗誤差関数
E(w) = \frac{1}{2}\sum_{i=1}^{N}(\hat{y_i}-y_i)^2
確認テスト8
問:なぜ引き算でなく二乗するか述べよ.また,1/2はどういう意味を持つか述べよ.
解答:二乗しないと正負の誤差同士で打ち消しあってしまうから.
→二乗することで誤差の符号が正の値になるようにする.
1/2は微分したときに出てくる2を約分して計算を楽にするためにある.
出力層における活性化関数
タスクに応じて用いる活性化関数は異なる.
・回帰 → 恒等写像
f(u) = u
・二値分類 → シグモイド関数(ソフトマックス関数でも機能する)
・多値分類 →ソフトマックス関数
f(i,u) = \frac{e^u_i}{\sum_{k=1}^{K}e^{u_k}}
分類問題における誤差関数
交差エントロピー誤差関数
E(w) = -\sum_{i=1}^{N}y_i\log{\hat{y_i}}
確認テスト9
問:ソフトマックス関数のソースコードを1行ずつ説明せよ.
解答:下記
# softmaxという名前で関数定義
def softmax(x):
# もしxの次元数が2であれば
if x.ndim == 2:
# xを転置する
x = x.T
# xの最大値を引く
x = x - np.max(x, axis=0)
# 分母に指数関数のxの合計,分子に指数関数のxで計算結果をyに代入
y = np.exp(x) / np.sum(np.exp(x), axis=0)
# yを転置して返す
return y.T
# xの最大値を引く
x = x - np.max(x)
# 分母に指数関数のxの合計,分子に指数関数のxで計算結果をyに代入
return np.exp(x) / np.sum(np.exp(x))
確認テスト10
問:交差エントロピー誤差関数のソースコードを1行ずつ説明せよ.
解答:下記
# d(ラベル),y(出力値)として関数を定義
def cross_entropy_error(d, y):
#もしyの次元数が1であれば
if y.ndim == 1:
# d,yそれぞれの形状を(1,それぞれの大きさ)に変える.
d = d.reshape(1, d.size)
y = y.reshape(1, y.size)
# 教師データがone-hot-vectorの場合,正解ラベルのインデックスに変換
if d.size == y.size:
d = d.argmax(axis=1)
# yの形状の0番目の数を変数batch_sizeに代入する.
batch_size = y.shape[0]
# 交差エントロピー誤関数の値を返す(バッチサイズで平均をとっている).
return -np.sum(np.log(y[np.arange(batch_size), d] + 1e-7)) / batch_size
演習(多値分類)
# 多クラス分類
import numpy as np
def print_vec(text, vec):
print("*** " + text + " ***")
print(vec)
#print("shape: " + str(x.shape))
print("")
def relu(x):
y = np.maximum(0,x)
return y
def softmax(x):
# もしxの次元数が2であれば
if x.ndim == 2:
# xを転置する
x = x.T
# xの最大値を引く
x = x - np.max(x, axis=0)
# 分母に指数関数のxの合計,分子に指数関数のxで計算結果をyに代入
y = np.exp(x) / np.sum(np.exp(x), axis=0)
# yを転置して返す
return y.T
# xの最大値を引く
x = x - np.max(x)
# 分母に指数関数のxの合計,分子に指数関数のxで計算結果をyに代入
return np.exp(x) / np.sum(np.exp(x))
def cross_entropy_error(d, y):
#もしyの次元数が1であれば
if y.ndim == 1:
# d,yそれぞれの形状を(1,それぞれの大きさ)に変える.
d = d.reshape(1, d.size)
y = y.reshape(1, y.size)
# 教師データがone-hot-vectorの場合,正解ラベルのインデックスに変換
if d.size == y.size:
d = d.argmax(axis=1)
# yの形状の0番目の数を変数batch_sizeに代入する.
batch_size = y.shape[0]
# 交差エントロピー誤関数の値を返す(バッチサイズで平均をとっている).
return -np.sum(np.log(y[np.arange(batch_size), d] + 1e-7)) / batch_size
# ウェイトとバイアスを設定
# ネートワークを作成
def init_network():
print("##### ネットワークの初期化 #####")
#_各パラメータのshapeを表示
#_ネットワークの初期値ランダム生成
network = {}
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.7, 1.0],
[0.2, 0.5, 0.8, 1.1],
[0.3, 0.6, 0.9, 1.2]
])
network['b1'] = np.array([0.1, 0.2, 0.3])
network['b2'] = np.array([0.1, 0.2, 0.3, 0.4])
print_vec("重み1", network['W1'] )
print_vec("重み2", network['W2'] )
print_vec("バイアス1", network['b1'] )
print_vec("バイアス2", network['b2'] )
return network
# プロセスを作成
# x:入力値
def forward(network, x):
print("##### 順伝播開始 #####")
W1, W2 = network['W1'], network['W2']
b1, b2 = network['b1'], network['b2']
# 1層の総入力
u1 = np.dot(x, W1) + b1
# 1層の総出力
z1 = relu(u1)
# 2層の総入力
u2 = np.dot(z1, W2) + b2
# 出力値
y = softmax(u2)
print_vec("総入力1", u1)
print_vec("中間層出力1", z1)
print_vec("総入力2", u2)
print_vec("出力1", y)
print("出力合計: " + str(np.sum(y)))
return y, z1
## 事前データ
# 入力値
x = np.array([1., 2.])
# 目標出力
d = np.array([0, 0, 0, 1])
# ネットワークの初期化
network = init_network()
# 出力
y, z1 = forward(network, x)
# 誤差
loss = cross_entropy_error(d, y)
## 表示
print("\n##### 結果表示 #####")
print_vec("出力", y)
print_vec("訓練データ", d)
print_vec("誤差", loss)
実装結果
##### ネットワークの初期化 #####
*** 重み1 ***
[[0.1 0.3 0.5]
[0.2 0.4 0.6]]
*** 重み2 ***
[[0.1 0.4 0.7 1. ]
[0.2 0.5 0.8 1.1]
[0.3 0.6 0.9 1.2]]
*** バイアス1 ***
[0.1 0.2 0.3]
*** バイアス2 ***
[0.1 0.2 0.3 0.4]
##### 順伝播開始 #####
*** 総入力1 ***
[0.6 1.3 2. ]
*** 中間層出力1 ***
[0.6 1.3 2. ]
*** 総入力2 ***
[1.02 2.29 3.56 4.83]
*** 出力1 ***
[0.01602796 0.05707321 0.20322929 0.72366954]
出力合計: 1.0
##### 結果表示 #####
*** 出力 ***
[0.01602796 0.05707321 0.20322929 0.72366954]
*** 訓練データ ***
[0 0 0 1]
*** 誤差 ***
0.3234202933601941
6.勾配降下法
深層学習の目的は,誤差関数Eを最小にすることであった.
→勾配降下法:誤差関数Eが最小になるようなパラメータを探索し,最適化する.
全サンプルの平均誤差を用いる.
式は以下になる.
w^{t+1} = w^t - \eta\frac{\partial E}{\partial w}
確認テスト11
問:勾配降下法の式に該当するソースコードを探してみよう.
解答:下記
network[key] -= learning_rate * grad[key]
grad = backward(x, d, z1, y)
確率的勾配降下法(SGD:Stochastic Gradient Descent)
勾配降下法では,全サンプルを学習に利用していたが,
SGDでは,全サンプルの中からランダムに選ばれた誤差のみを学習に用いる.
メリットとして以下が挙げられる.
・データが冗⻑な場合の計算コストの軽減
・望まない局所的最適解に収束するリスクの軽減
・オンライン学習ができる
ミニバッチ勾配降下法
オンライン学習とバッチ学習の中間的な特徴を持つ学習手法
→ランダムに分割したデータの集合(ミニバッチ)に属するサンプルの平均誤差を利用
確率的勾配降下法のメリットを損なわず,計算機の計算資源を有効利用できる.
→CPUを利用したスレッド並列化やGPUを利用したSIMD並列化
式は以下になる.
\begin{align}
w^{t+1} &= w^t - \eta\frac{\partial E}{\partial w} \\
E_t &= \frac{1}{N_t}\sum_{n\in{D_t}}^{} E_n \\
N_t &= |D_t|
\end{align}
確認テスト12
問:オンライン学習とは何か2行でまとめよ.
解答:学習データが入ってくるたびにパラメータを更新し,学習を進めていく方法である.
1回の学習あたりのコストが低い,外れ値の影響が受けやすいといった特徴がある.
確認テスト13
問:勾配降下法の式の意味を図に書いて説明せよ.
解答:図を以下に示す.
傾きが負であれば重みを増やし,傾きが正であれば重みを減らすことで
誤差関数Eを最小化する(どれぐらい変化させるかは学習率で調整).
7.誤差逆伝搬法
算出された誤差を出力層側から順に微分し,後ろの層へと伝播する.
→最小限の計算で各パラメータでの微分値を解析的に計算する手法
微分時には数値微分が用いられる.
式は以下に示す.
\frac{\partial E}{\partial w_m} \approx \frac{E(w_m+h)-E_m(w_m-h)}{2h}
これに加え,チェインルール(連鎖率)を用いることで,
不要な再帰的計算を避けて微分を算出できる.
確認テスト14
問:誤差逆伝播法では,不要な再帰的処理を避ける事が出来る.
既に行った計算結果を保持しているソースコードを抽出せよ.
解答:下記
delta2 = functions.d_mean_squarred_error(d, y)
delta1 = np.dot(delta2, W2.T) * functions.d_sigmoid(z1)
確認テスト15
解答:下記
delta1 = np.dot(delta2, W2.T) * functions.d_sigmoid(z1)
grad['W1'} = np.dot(x.T, delta1)
8.参考
ニューラルネットワークや勾配降下法,誤差逆伝搬法について:
絶対に理解させる誤差逆伝播法【深層学習】
https://www.youtube.com/watch?v=0itH0iDO8BE