SGDによるDeepLearningの学習

61
SGDによるDeep Learningの『学習』 @ゼロから作るDeep Learning4章後半 jang

Transcript of SGDによるDeepLearningの学習

Page 1: SGDによるDeepLearningの学習

SGDによるDeep Learningの『学習』@ゼロから作るDeep Learning4章後半

jang

Page 2: SGDによるDeepLearningの学習

さらりとした復習

● 4章のテーマは「学習(training)」○ 学習とは、ある問題に回答するニューラルネットワークにおける最適な「重み( weight)」と「バイアス

(bias)」を見つけるプロセスのこと

※当発表とGopherくんは関係ありません

Page 3: SGDによるDeepLearningの学習

さらりとした復習

● (耳タコだけど)機械学習とディープラーニングの違い○ 機械学習……特徴量は人が与える必要がある(=めんどい、むずかしい )

○ ディープラーニング……特徴量は、訓練データとニューラルネットワークを基にした汎用的なプロセス

を経て算出される

Page 4: SGDによるDeepLearningの学習

さらりとした復習

● 学習プロセスに2種類のデータを用いる

○ 訓練データ(training data set)……学習を行うためのデータ

○ テストデータ(test data set)……学習結果の検証を行うためのデータ。学習結果の汎用性や

過学習(overfitting)の程度を検証するため、期待値は同じだが入力値の異なるデータを用いること

が好ましい

■ e.g.答えが同じ”5”のMNISTデータでも、訓練データに利用したイメージとは筆跡の異なる

データを使う

Page 5: SGDによるDeepLearningの学習

さらりとした復習

● 出力値と教師データの乖離を、損失関数を使って指標化する○ 損失関数の計算結果が大きいほど乖離している(=悪い )

○ (繰り返しになるけど)損失関数の計算結果が小さくなる「重み」と「バイアス」を探すことが

学習の目的

Page 6: SGDによるDeepLearningの学習

さらりとした復習

● 本章で紹介されている損失関数(loss function)○ 二乗和誤差(Mean Squared Error/MSE)

■ mse' ts ys = (/2) $ sumElements $ (**2) $ ys - ts

○ 交差エントロピー誤差(Cross-entropy Error)

■ cee' ts ys = negate $ sumElements $ ts * log `cmap` ys

Page 7: SGDによるDeepLearningの学習

さらりとした復習

● 訓練データを1つずつを学習させるより、複数の訓練データをまとめて学習させて、

汎用性や精度を上げたいンゴ  → ミニバッチの出番!

Page 8: SGDによるDeepLearningの学習

さらりとした復習

● 訓練データ(全量)の真部分集合をまとめて計算することを『ミニバッチ』と呼ぶa. 訓練データのセットから一部を抽出してリスト生成

b. 結果をニューラルネットワークで投射

c. 結果と教師データのペアを損失関数で投射

d. a.〜c.を繰り返す

e. すべての結果を足し算で畳み込む

f. a.〜c.を繰り返した回数で按分する

● 数式で表現すると:

● このミニバッチ方式で学習することを『ミニバッチ学習』と呼ぶ

Page 9: SGDによるDeepLearningの学習

本日のメニュー

● 学習アルゴリズム○ ミニバッチの復習

○ 勾配の計算

○ パラメータ更新

● 勾配法(確率的勾配降下法/SGD)○ 微分とは

○ 微分の実装あれこれ

○ 偏微分

○ 勾配法の計算

● まとめ

Page 10: SGDによるDeepLearningの学習

本日のメニュー

● 学習アルゴリズム○ ミニバッチの復習

○ 勾配の計算

○ パラメータ更新

● 勾配法(確率的勾配降下法/SGD)○ 微分とは

○ 微分の実装あれこれ

○ 偏微分

○ 勾配法の計算

● まとめ

まだるっこしいので、Pythonの勉強がてらコードベースで話を進めましょう!!

Page 11: SGDによるDeepLearningの学習

本章のサンプルコードの置き場所

● 本章で作るプログラムのうち、メイン関数に相当するコードがこちら

● コードベースで理解できればおっけーっしょ!

○ ch04/train_neuralnet.py

https://github.com/oreilly-japan/deep-learning-from-scratch/tree/master/ch04

Page 12: SGDによるDeepLearningの学習

定義部分から眺めていく

● まず学習の手続きが始まる前の定義部分をみていく○ インポート文

○ 下準備(画像読み込み、ハイパーパラメータなどの設定)

○ クラス

Page 13: SGDによるDeepLearningの学習

インポート文

● 全体的に省略します

Page 14: SGDによるDeepLearningの学習

訓練データとテストデータを読み込む

● これもいいでしょう

(x_train, t_train), (x_test, t_test) = load_mnist(normalize=True, one_hot_label=True)

Page 15: SGDによるDeepLearningの学習

ニューラルネットワークをつくる

network = TwoLayerNet(input_size=784, hidden_size=50, output_size=10)

● 命名からして2層ネットワークっぽい○ 入力層:784○ 隠れ層:50 # 1層目のニューロン数

○ 出力層:10 # 2層目のニューロン数

Page 16: SGDによるDeepLearningの学習

ハイパーパラメータを設定する

● ハイパーパラメータ=人の手で調整する必要のある値

iters_num = 10000 # ミニバッチを繰り返す回数

train_size = x_train.shape[0] # 訓練データ全量のサイズ

batch_size = 100 # ミニバッチのサイズ

learning_rate = 0.1 # 学習率 → 後述

Page 17: SGDによるDeepLearningの学習

エポックを定義する

● 学習はミニバッチを繰り返すことで行われる

● ${ミニバッチのサイズ} * ${ミニバッチの繰り返し回数} = ${訓練データの個数}に達

するサイクルをエポックと呼ぶ○ 今回の例では訓練データの総数が 60,000個で、ミニバッチサイズが 100なので、600回周期がエ

ポックにあたる

○ ミニバッチを1,200回繰り返せば2エポック、 1800回で3エポック……

iter_per_epoch = max(train_size / batch_size, 1) # エポックの定義

Page 18: SGDによるDeepLearningの学習

ミニバッチの繰り返しとエポックのサイクル

● ミニバッチをiters_num回繰り返す

● ちなみに……42行目でエポックのサイクルが制御されている○ ハッシュするため、ゼロ除算にならないよう iter_per_epochを最低1とする配慮がなされてる(前スラ

イド参照)

iter_per_epoch = max(train_size / batch_size, 1) # エポックの定義(温かい配慮)

.

.

.if i % iter_per_epoch == 0: # 42行目

Page 19: SGDによるDeepLearningの学習

ここまでがメイン関数内の定義部分

● 次は2層ニューラルネットワークのクラス定義を説明するゾ

● メイン関数内のロジック部分は最後の〆にします

Page 20: SGDによるDeepLearningの学習

2層ネットワークをクラス定義する

class TwoLayerNet:● 振る舞いは以下を持ってるようだ(コンストラクタ除く)

def predict(self, x):def loss(self, x, t):def accuracy(self, x, t):def numerical_gradient(self, x, t):def gradient(self, x, t):

Page 21: SGDによるDeepLearningの学習

TwoLayerNetのコンストラクタ

● クラスが保持する値を理解しておこう

def __init__(self, input_size, hidden_size, output_size, weight_init_std=0.01):

● self……自己参照(余談:はじめて知ったselfの仕様に感動!)

● input_size……入力層の数っぽい

● hidden_size……隠れ層の数っぽい

● output_size……出力層の数っぽい

● weight_init_std……デフォルト値が設定されてるけど役割が謎

Page 22: SGDによるDeepLearningの学習

TwoLayerNetのコンストラクタ

● 内部実装はこんな感じ○ 連想配列に各層の重みとバイアスを格納している

def __init__(self, input_size, hidden_size, output_size, weight_init_std=0.01): self.params = {} self.params['W1'] = weight_init_std * np.random.randn(input_size, hidden_size) self.params['b1'] = np.zeros(hidden_size) self.params['W2'] = weight_init_std * np.random.randn(hidden_size, output_size) self.params['b2'] = np.zeros(output_size)

Page 23: SGDによるDeepLearningの学習

TwoLayerNetのコンストラクタ

● np.random.randnは引数に与えられた行と列の数だけガウス分布を基にしたラン

ダム値を吐き出す○ このランダム値にweight_init_stdを掛けて、n-1層目とn層目のニューロンの組み合わせに与える

『重み』にしているようだ

○ ちなみにバイアス値はすべて 0とされている

def __init__(self, input_size, hidden_size, output_size, weight_init_std=0.01): self.params = {} self.params['W1'] = weight_init_std * np.random.randn(input_size, hidden_size) self.params['b1'] = np.zeros(hidden_size) self.params['W2'] = weight_init_std * np.random.randn(hidden_size, output_size) self.params['b2'] = np.zeros(output_size)

Page 24: SGDによるDeepLearningの学習

TwoLayerNetのpredictメソッド

● 3章で習ったニューラルネットワークの推論処理の実装ですね○ 活性化関数は入力層 -> 1層目でシグモイド、1層目 -> 出力層でソフトマックス

def predict(self, x): W1, W2 = self.params['W1'], self.params['W2'] b1, b2 = self.params['b1'], self.params['b2']

a1 = np.dot(x, W1) + b1 z1 = sigmoid(a1) a2 = np.dot(z1, W2) + b2 y = softmax(a2) return y

Page 25: SGDによるDeepLearningの学習

TwoLayerNetのlossメソッド

● predictの計算結果から損失関数(交差エントロピー誤差)の計算をしている

def loss(self, x, t): y = self.predict(x) return cross_entropy_error(y, t)

Page 26: SGDによるDeepLearningの学習

TwoLayerNetのaccuracyメソッド

● 認識精度を計算している○ 以下の汚いコメント参照

def accuracy(self, x, t): y = self.predict(x) y = np.argmax(y, axis=1) # NL計算結果の各配列2次元目の最大値の添字

t = np.argmax(t, axis=1) # 同じく教師データ版の添字

accuracy = np.sum(y == t) / float(x.shape[0]) # 添字が一致=正解扱い

# 正解数 ÷ 訓練データ数=認識精度

return accuracy

Page 27: SGDによるDeepLearningの学習

TwoLayerNetのnumerical_gradientメソッド

def numerical_gradient(self, x, t):

loss_W = lambda W: self.loss(x, t)

grads = {}

grads['W1'] = numerical_gradient(loss_W, self.params['W1'])

grads['b1'] = numerical_gradient(loss_W, self.params['b1'])

grads['W2'] = numerical_gradient(loss_W, self.params['W2'])

grads['b2'] = numerical_gradient(loss_W, self.params['b2'])

return grads

Page 28: SGDによるDeepLearningの学習

なにこれ? → numerical_gradient

● 答え:与えられた座標における勾配を計算する関数○ 勾配の計算には偏微分が使われる

■ 本章の偏微分の実装には数値微分が使われている

● 数値微分とは、微分の計算方法(アプローチ)のひとつ

Page 29: SGDによるDeepLearningの学習

高校で数学を捨てた人間の反応

Page 30: SGDによるDeepLearningの学習

なにこれ? → numerical_gradient

● 答え:与えられた座標における勾配を計算する関数○ 勾配の計算には偏微分が使われる

■ 本章の偏微分の実装には数値微分が使われる

● 数値微分は、微分の実装方法のひとつ

下の用語から説明していくンゴよ

Page 31: SGDによるDeepLearningの学習

微分(differential)とは?

● ここから少し教科書に戻ってみる

● 微分の定義:

● 極限をプログラムで書くのか……ゴクリ

● あるいは数式を文字列にして解析……

→ 回避方法があるらしい

Page 32: SGDによるDeepLearningの学習

数値微分:厳密→ベストエフォートへ

● 微分の定義を緩くする:

● これが数値微分(numerical differentiation)

● これなら実装できそうだ!

○ ⊿xを小さい値にするほど、厳密な意味の微分に近づけることができる

○ 極力小さい値 = 丸め誤差の影響を受けない程度に小さい値

■ 余談だがPython標準のfloatはC/Javaでいうdouble相当の精度らしい

■ Numpyにもっと高精度の floatも実装されている: float128

Page 33: SGDによるDeepLearningの学習

数値微分:ベストエフォート+αへ

● 『ベスト』ってなんだよ(哲学)

● 中心差分を使えばもっと精度が出る:

要はf(x)からf(x+⊿x)の間の傾きと、f(x)からf(x-⊿x)の間の傾きを平均化したもの

f(x-⊿x)

f(x+⊿x)

Page 34: SGDによるDeepLearningの学習

gradient.pyのnumerical_gradientdef numerical_gradient(f, x): h = 1e-4 # 数値微分の⊿xに相当する値

grad = np.zeros_like(x) it = np.nditer(x, flags=['multi_index'], op_flags=['readwrite']) while not it.finished: # xの要素数だけループさせる

idx = it.multi_index tmp_val = x[idx] x[idx] = float(tmp_val) + h fxh1 = f(x) # f(x+⊿x) x[idx] = tmp_val - h fxh2 = f(x) # f(x-⊿x) grad[idx] = (fxh1 - fxh2) / (2*h) # 中心差分を取得する

x[idx] = tmp_val it.iternext()

return grad

Page 35: SGDによるDeepLearningの学習

偏微分(partial differential)とは?

● こいつは初見……

● アリティ2の関数があったとする:

● x0に関する偏微分:

○ なんやこのグロ記号……

Page 36: SGDによるDeepLearningの学習

偏微分(partial differential)とは?

● 偏微分とは……

複数の変数を備える関数に対して、微分対象の変数以外を定数で固定し、変数1

つずつを微分する手法

● 定数を微分すると結果は”0”、ってことは……

●            のとき、x0を偏微分(=x1を固定)する場合、

●               となる

● もうグロくない

Page 37: SGDによるDeepLearningの学習

勾配(gradient)とは?

● N個の変数を持つ関数に偏微分を行って、N次元のベクトルを得る

このベクトルが『勾配』である

-2.0〜2.0を0.25刻みで格子点を取り、

各点の勾配を視覚化した図 →

Page 38: SGDによるDeepLearningの学習

TwoLayerNetのnumerical_gradientメソッド(再)

仮実装のラムダ式でgrads[*]の勾配が取れてる(?)理由を調べ切れなかった 分かる人、だれか……

if numerical_gradient(self, x, t):

# 以下のloss_Wは仮実装# 本来的な実装としては偏微分するために最低4個の引数を持つラムダ式を置くべき# その内訳は『W1』『W2』『b1』『b2』 loss_W = lambda W: self.loss(x, t)

grads = {} grads['W1'] = numerical_gradient(loss_W, self.params['W1']) # 入力層 -> 隠れ層の重みの勾配 grads['b1'] = numerical_gradient(loss_W, self.params['b1']) # 入力層 -> 隠れ層のバイアスの勾配 grads['W2'] = numerical_gradient(loss_W, self.params['W2']) # 隠れ層 -> 出力層の重みの勾配 grads['b2'] = numerical_gradient(loss_W, self.params['b2']) # 隠れ層 -> 出力層のバイアスの勾配

return grads

Page 39: SGDによるDeepLearningの学習

TwoLayerNetのgradientメソッド

● 誤差逆伝播法で実装した版らしい○ 本章で取り上げている順伝播(数値微分版)よりも、むっちゃ速い

● 次章ご担当の方、解説オナシャス!

Page 40: SGDによるDeepLearningの学習

TwoLayerNetのクラス定義はここまで

● ここからメイン関数に戻る

Page 41: SGDによるDeepLearningの学習

ミニバッチのイテレーション部分@25〜46行目

for i in range(iters_num): batch_mask = np.random.choice(train_size, batch_size) x_batch = x_train[batch_mask] t_batch = t_train[batch_mask] # 勾配の計算

grad = network.numerical_gradient(x_batch, t_batch) #grad = network.gradient(x_batch, t_batch) # パラメータの更新

for key in ('W1', 'b1', 'W2', 'b2'): network.params[key] -= learning_rate * grad[key] loss = network.loss(x_batch, t_batch) train_loss_list.append(loss) if i % iter_per_epoch == 0: train_acc = network.accuracy(x_train, t_train) test_acc = network.accuracy(x_test, t_test) train_acc_list.append(train_acc) test_acc_list.append(test_acc) print("train acc, test acc | " + str(train_acc) + ", " + str(test_acc))

Page 42: SGDによるDeepLearningの学習

ミニバッチのイテレーション部分

for i in range(iters_num): batch_mask = np.random.choice(train_size, batch_size) x_batch = x_train[batch_mask] t_batch = t_train[batch_mask] # 勾配の計算

grad = network.numerical_gradient(x_batch, t_batch) #grad = network.gradient(x_batch, t_batch) # パラメータの更新

for key in ('W1', 'b1', 'W2', 'b2'): network.params[key] -= learning_rate * grad[key] loss = network.loss(x_batch, t_batch) train_loss_list.append(loss) if i % iter_per_epoch == 0: train_acc = network.accuracy(x_train, t_train) test_acc = network.accuracy(x_test, t_test) train_acc_list.append(train_acc) test_acc_list.append(test_acc) print("train acc, test acc | " + str(train_acc) + ", " + str(test_acc))

この辺は1エポック毎のデータを収集してるだけなので省くよ

この辺は学習成果の確認(損失関数の計算結果の履歴取得)なので省くよ

Page 43: SGDによるDeepLearningの学習

ミニバッチのイテレーション部分

for i in range(iters_num): batch_mask = np.random.choice(train_size, batch_size) x_batch = x_train[batch_mask] t_batch = t_train[batch_mask] # 勾配の計算

grad = network.numerical_gradient(x_batch, t_batch) #grad = network.gradient(x_batch, t_batch) # パラメータの更新

for key in ('W1', 'b1', 'W2', 'b2'): network.params[key] -= learning_rate * grad[key]

Page 44: SGDによるDeepLearningの学習

ミニバッチのイテレーション部分

for i in range(iters_num): batch_mask = np.random.choice(train_size, batch_size) x_batch = x_train[batch_mask] t_batch = t_train[batch_mask] # 勾配の計算

grad = network.numerical_gradient(x_batch, t_batch) #grad = network.gradient(x_batch, t_batch) # パラメータの更新

for key in ('W1', 'b1', 'W2', 'b2'): network.params[key] -= learning_rate * grad[key]

Page 45: SGDによるDeepLearningの学習

ミニバッチの訓練&テストデータを抽出する

● 訓練データ総数(60,000個)から無作為にミニバッチサイズ(100個)を選んでいる

batch_mask = np.random.choice(train_size, batch_size) x_batch = x_train[batch_mask] t_batch = t_train[batch_mask]

Page 46: SGDによるDeepLearningの学習

勾配を計算する

● TwoLayerNetのメソッドを使って勾配を計算しているだけ

● ここまで至って平和

# 勾配の計算

grad = network.numerical_gradient(x_batch, t_batch) #grad = network.gradient(x_batch, t_batch)# 公式サンプルでは後者がデフォ

# ただし後者は数値微分ではなく、誤差逆伝播による実装を行っているので注意

Page 47: SGDによるDeepLearningの学習

学習する

● ついに来ました

● イテレーションの締め括りに、TwoLayerNetのコンストラクタで宣言されていた『重

み』『バイアス』の値を書き換えている

= まさに学習させている箇所じゃないか!!

● ここは『勾配法(gradient method)』を使って学習させている

● 『勾配法』ってなんぞ?  → 次のスライドへ

# パラメータの更新

for key in ('W1', 'b1', 'W2', 'b2'): network.params[key] -= learning_rate * grad[key] # ココ!

Page 48: SGDによるDeepLearningの学習

● より小さい/大きい値を探索するときに『傾き』をコンパスにする手法○ 傾きが正の値=座標を左にズラす必要がある=負の方向を探索方向とする (正負逆でも同様 )

● 曲線に沿ってボールを移動していき、落ち着ける場所を探すイメージ○ ただし極小値/極大値に囚われ、最小値/最大値を見つけられない状況に陥る可能性もある

○ 最小値や極小値のようなボールが落ち着ける位置を『鞍点』、特に最小値でない鞍点にハマること

を『プラトー』と呼ぶ。これを避けるように『学習率』を補正値として調整する。

勾配法(gradient method)とは?

← プラトー

Page 49: SGDによるDeepLearningの学習

● ハイパーパラメータのスライドで説明を先送りにされたアイツ

○ learning_rate = 0.1● ${学習率} * ${勾配(=傾きベクトル)} * (-1) = ${重みの更新差分}

○ 以下のグラフは、例えば横軸が W1、縦軸が損失関数の計算結果 loss(W)○ 次のステップまでにボールの中心軸を動かす度合いに相当する

学習率(learning rate)とは?

数バッチ後……

青の差分が${重みの更新差分 }

Page 50: SGDによるDeepLearningの学習

● つまり学習率は、次のミニバッチに採用するニューラルネットワークの『重み』を決

定する計算に利用されている

● その意味するところは、偏微分の計算結果(=勾配)を『重み』に反映させる割合で

ある○ 学習率を1.0とすると、勾配の各点の傾きを等倍で重みから差っ引くことになる

for key in ('W1', 'b1', 'W2', 'b2'): network.params[key] -= learning_rate * grad[key] # network = 先に解説した2層NLのインスタンス

学習率(learning rate)とは?

Page 51: SGDによるDeepLearningの学習

学習率はボールの『勢い』

● 勾配法をボール転がしに例えると、ボール学習率はボールを転がす『勢い』に相当

する

● 勾配法をミニバッチで繰り返すイメージは以下のような感じ

ボールの位置の傾きを調べる

→ 傾きに沿って一定の勢い(=学習率 * 傾きベクトル)で発射

→ ボールの位置の傾きを調べる

→ 傾きに沿って一定の勢いで発射

→ (繰り返し)

Page 52: SGDによるDeepLearningの学習

● ボールの中心軸は極小値にすら至らず、学習処理が終わってしまう

=勢いが無さ過ぎた……

学習率が小さすぎると?

10,000バッチ後……

Page 53: SGDによるDeepLearningの学習

● プラトーの壁を破った! いけるやん!

学習率が大きすぎると?

1バッチ後……

Page 54: SGDによるDeepLearningの学習

● おや?

学習率が大きすぎると?

さらに1バッチ後……

Page 55: SGDによるDeepLearningの学習

● 学習率が大きすぎると発散してしまう……

=勢いが良すぎた……

学習率が大きすぎると?

さらに1バッチ後……

初期位置より最小値から遠くへ……

Page 56: SGDによるDeepLearningの学習

● 最小値に向けて収束していく学習率=良い学習率○ 理想は傾きゼロの位置=最小値( global minimum)にカップインできること

○ ピッタリな位置に着地させるのは難しいですね ……

適切な学習率

Page 57: SGDによるDeepLearningの学習

● W1、b1、W2、b2にそれぞれ補正を加えている(=学習している)

for key in ('W1', 'b1', 'W2', 'b2'): network.params[key] -= learning_rate * grad[key] # network = 先に解説した2層NLのインスタンス

学習する

Page 58: SGDによるDeepLearningの学習

メイン関数の残りは省略

● グラフ描画とか本質から外れてるコードなんで

Page 59: SGDによるDeepLearningの学習

確率的勾配降下法(Stochastic Gradient Descent/SGD)

● 本章で紹介されたアルゴリズムを確率的勾配降下法(SGD)と呼ぶ○ 確率的?

■ 訓練データ総量からバッチサイズの個数データを無作為に取得したことから

○ 勾配降下法?

■ 最小値を探す勾配法のことを『勾配降下法( Gradient Descent Method)と呼ぶ

○ ゆえに『確率的勾配降下法』と呼ぶ

Page 60: SGDによるDeepLearningの学習

まとめ

1. 学習プログラムの流れa. 下準備

i. 訓練&テストデータを用意する

ii. NLを作る

iii. ハイパーパラメータを宣言する ……)b. ミニバッチを繰り返す

i. 今回バッチ用のデータを選ぶ

ii. (推論処理をする)

iii. (損失関数を計算する)

iv. 勾配を計算する

v. 勾配法で学習する

※グラフ描画関係の処理は省略してます

Page 61: SGDによるDeepLearningの学習

おまけ:ミニバッチの適正サイズについて

● 個人的な疑問

● 機械的にサイズを決める方法はないのだろうか?○ https://goo.gl/ywVEDr