-
Notifications
You must be signed in to change notification settings - Fork 0
DNET_NeuralNetworkTraining
- 戻る(深層学習(deep learning))
- パーセプトロン
-
ニューラルネットワーク
- ニューラルネットワーク(推論)
- ニューラルネットワーク(学習)
・深層学習の誤差逆伝播法
・深層学習のテクニック
ニューラルネットワークにおいては、
特徴量を機械が選択し重みを学習(自動獲得)する。
機械学習については機械学習(machine learning)を参照。
-
構造化データ
- 特徴量を表から選択する。
- 場合によっては次元圧縮したりする。
-
非構造化データ
-
画像
入力画像データから、本質的なデータを抽出できるように設計された変換器を指す。- 画像データの特徴量は通常、ベクトルを使用して表される。
- SIFT, SURF, HOGなど、人が設計した変換器によって画像データをベクトル化する。
-
-
構造化データ
機械学習の様々なアルゴリズムで学習させる。 -
非構造化データ
- 画像
ベクトル化された画像データを機械学習の
SVMやKNNなどの識別器(機械学習(machine learning)の該当節を参照)で
学習させる。
- 画像
深層学習については深層学習(deep learning)を参照。
学習フェーズでは、信号は逆方向に伝播する。
-
学習とは、重みパラメタを自動獲得するためのもの。
-
実際のニューラルネットワークの重みパラメタは、
- 数千、数万になるため、手動での設定は不可能。
- 更に層を深めた深層学習(deep learning)では数億にも登る。
-
重みパラメタの自動獲得のため、
- MNISTデータセットの様な画像の分類処理を行うアルゴリズムを考え出すのは困難。
しかし、機械にデータを学習させる機械学習を用いれば分類処理が実現可能。
人間が(暗黙的な学習によって、)これらを判別することはできる。
従って、人間の脳も、一部は、このようなデータ駆動で動いているのかもしれない。
-
損失関数は、誤差関数とほぼ同じか、より包括的な概念。
-
予想データと正解データの出力の間に、
どのくらい誤差があるのかを評価する関数。 -
作成した予測モデルの精度を評価する際に使われ、
-
値(誤差)が小さければ小さいほど正確なモデルと言える。
- モデル関数とデータ群の誤差という文脈では誤差関数と呼ばれる。
- 学習の文脈では損失関数と呼ばれ、誤差関数以外にも例えば
KL情報量(機械学習(machine learning)の該当節を参照)などを含む。
-
-
深層学習(deep learning)では一つの指標を手がかりに最適なパラメタを探索する。
-
この指標を損失関数と呼び、パラメタに対して連続的に変化する関数を用いる。
-
これは学習時に、微分によって
傾きが0になってパラメタの更新できなくなることを防ぐため。
-
ほとんどの最適化アルゴリズムは、正確な勾配やヘッセ行列を前提としている。
-
勾配の悪条件
-
鞍点
・ある方向から見ると極小値であるが、別の方向から見ると極大値になる点。
・鞍点の数はパラメタの数に対して指数関数的に増加するので
ディープラーニングの場合は非常に多く存在する。 -
平地:ほぼ水平な領域
・勾配が0となるような平地領域ではSGDで学習が進まなくなる。
・MomentumSGDなどの慣性を用いた最適化アルゴリズムで解決を図る。 -
崖:ほぼ垂直な領域
・勾配が急すぎて(勾配爆発を起こし)、パラメタが遠くに吹っ飛ぶ。
・同じ値の掛け算が繰り返し行われる場合に発生
・RNNなどで多く現れ、長期依存性が現れるのもこの地形が原因。
・勾配の大きさに上限を設ける勾配クリッピングが有効。 -
ノイズやバイアス
・ノイズやバイアスが加わった場合、勾配が滑らかにならずギザギザした形になる。
・これを回避するためには、損失関数をより平坦な代理損失関数で代用する。
-
-
ヘッセ行列の悪条件
-
ヘッセ行列
関数の二階偏導関数全体が作る正方行列で対称行列
・固有値の符号をみることにより極小点や凸性の判定を行える。
・固有値が全て正であれば、凸関数になるので大域解が求まることを保証できる。 -
悪条件
ヘッセ行列の特異値が非常に大きいまたは非常に小さい場合、その逆行列を求めることが困難になる。
・極端な比率の変数:ある変数が他の変数に比べて数桁大きいまたは小さい値を取る場合
・局所的な不連続性:関数が局所的な不連続性や非線形性を持つ場合
-
-
-
- SGD (MomentumSGD, NAG)
- AdaGrad
- Rmsprop
- AdaDelta
- Adam
-
勾配降下のプログラム実装
- 重み更新は勾配の引き算:Θ = Θ - 勾配
- ミニバッチでは損失をΣしたものをサイズで割って平均を取る。
- この値に学習率(0.01などの値)を掛ける。
- 個別のアルゴリズムの実装は数式より想像できる。
回帰の場合。ニューラルネットワークの出力と
正解となる教師データの各要素の差の二乗の総和の2分の一。
- 式
2
E = 1/2 Σ (yk-tk)
k
-
説明
- k:データの次元数
- yk:ニューラルネットワークの出力
- tk:教師データ
-
例(k=10)
-
yk:ニューラルネットワークの出力
=[0.1, 0.05, 0.6, 0.0, 0.05, 0.1, 0.0, 0.1, 0.0, 0.0]
Softmax関数(ニューラルネットワーク(推論)の該当節を参照)の出力
(全て足して1.0になる。) -
tk:教師データ
=[0, 0, 1, 0, 0, 0, 0, 0, 0, 0]
※ 正解ラベルを1、ソレ以外を0とする、one-hot表現
-
-
Python
- 実装
"""This is a test program."""
import numpy as np
def mean_squared_error(yk, tk):
"""損失関数(2乗和誤差)"""
return 0.5 * np.sum((yk - tk)**2)
# 教師データ
tk = np.array([0, 0, 1, 0, 0, 0, 0, 0, 0, 0])
# 推定データ1の誤差
yk = np.array([0.1, 0.05, 0.6, 0.0, 0.05, 0.1, 0.0, 0.1, 0.0, 0.0])
print(mean_squared_error(yk, tk))
# 推定データ2の誤差
yk = np.array([0.1, 0.05, 0.1, 0.0, 0.05, 0.1, 0.0, 0.6, 0.0, 0.0])
print(mean_squared_error(yk, tk))-
出力
正解(tk)に対応する確率(yk)が-
高ければ高いほど、0に近いデータになる。
tkで2が正解の場合、ykで2の確率が一番高いとしたデータの場合のE。0.0975 -
低ければ低いほど、1に近いデータになる。
tkで2が正解の場合、ykで7の確率が一番高いとしたデータの場合のE。0.5975
-
-
損失関数(2乗和誤差)の出力が小さい前者のykが、より適合していることを示している。
多値分類の場合。正解ラベルに対応する yk の、底が e の自然対数 log e を計算する。
-
式
- 通常版
E = - Σ tk log yk
k
- ミニバッチ対応版
1
E = - ─ Σ Σ tnk log ynk
N n k
-
説明
- log:logは底がeの自然対数 log e
- k:データの次元数
- yk:ニューラルネットワークの出力
- tk:教師データ
-
例(k=10)
同上 -
Python
- 実装
"""This is a test program."""
import numpy as np
def cross_entropy_error(yk, tk):
"""損失関数(交差エントロピー誤差)"""
delta = 1e-7 # log(0)はマイナス∞になるのを微小な値を足して防止。
return - np.sum(tk * np.log(yk + delta))
tk = np.array([0, 0, 1, 0, 0, 0, 0, 0, 0, 0])
yk = np.array([0.1, 0.05, 0.6, 0.0, 0.05, 0.1, 0.0, 0.1, 0.0, 0.0])
print(cross_entropy_error(yk, tk))
yk = np.array([0.1, 0.05, 0.1, 0.0, 0.05, 0.1, 0.0, 0.6, 0.0, 0.0])
print(cross_entropy_error(yk, tk))-
出力
正解(tk)に対応する確率(yk)が-
高ければ高いほど、0に近いデータになる。
tkで2が正解の場合、ykで2の確率が一番高いとしたデータの場合のE。0.510825457099 -
低ければ低いほど、大きいデータになる。
tkで2が正解の場合、ykで7の確率が一番高いとしたデータの場合のE。2.30258409299
-
-
ミニバッチ対応版
上記を平均にしている。
return - np.sum(tk * np.log(yk + delta)) / yk.shape[0]- 損失関数(交差エントロピー誤差)の出力が小さい前者のykが、より適合していることを示している。
ミニバッチ対応版
return np.mean(- y * np.log(p) - (1-y) * np.log(1-p))微分(偏微分)で求めた勾配の示す方向は、
各場所で関数の値を最も減らす方向(である可能性が高い)。
-
勾配法では、広大なパラメタ空間から、複雑な損失関数が、
最小(最大)値を出力するパラメタを、勾配を使用して探す。- 勾配降下法 : (誤差などが)最小になるパラメタ値を探す。
- 勾配上昇法 : (確率などが)最大になるパラメタ値を探す。
-
全てのデータを選択し、損失の平均値を使用する場合、バッチ勾配降下法(最急降下法)
-
ランダムにデータを選択し、損失の平均値を使用する場合、確率的勾配降下法(SGD)
(深層学習のテクニックの該当節を参照)- 1つのデータを選択する場合、オンライン学習
- n個のデータを選択する場合、ミニバッチ学習
-
注意点
- 勾配の指す方向に最小(最大)値があることは保証されない。
- 複雑な関数の場合は、勾配の指す方向に最小(最大)値が無い可能性が高い。
-
ポイント
学習を如何に-
効率良く進めるか。
-
上手く最適解に収束させるか。
- 局所最適解 ✕
- 大域最適解 ○
-
上手く鞍点を脱するか。
-
- 式
df(x0, x1) df(x0, x1)
───── , ─────
dx0 dx1
-
説明
- すべての変数の偏微分を
ベクトルとしてまとめたものを勾配と呼ぶ。 - f(x0, x1) = x0² + x1² の例なら、x0=3, x1=4の場合、勾配は、(6, 8)となる。
- すべての変数の偏微分を
-
Python
- 実装
"""This is a test program."""
import numpy as np
def numerical_gradient(f, x01):
"""偏微分"""
h = 1e-4 # 微小な値hとして1の-4乗を用いる
grad = np.zeros_like(x01) # x01と同じ形状で要素が0。
for idx in range(x01.size):
tmp_val = x01[idx]
# 前方差分から中心差分にして誤差減
# f(x + h)
fxh1 = f(tmp_val + h)
# f(x - h)
fxh2 = f(tmp_val - h)
# (f(x + h) - f(x - h)) / 2 * h
grad[idx] = (fxh1 - fxh2) / (2 * h)
return grad
def function_2(x):
return np.sum(x**2)
print(numerical_gradient(function_2, np.array([3.0, 4.0])))
print(numerical_gradient(function_2, np.array([0.0, 2.0])))
print(numerical_gradient(function_2, np.array([3.0, 0.0])))- 出力
[ 6. 8.]
[ 0. 4.]
[ 6. 0.]
-
参考
https://github.com/oreilly-japan/deep-learning-from-scratch/blob/master/ch04/gradient_2d.py- x = -2.0 - 2.5, y = -2.0 - 2.5 の範囲の0.25刻みのメッシュグリッドを生成
- メッシュグリッドはm行n列の配列なので、これをflatten()メソッドで m * n にベクトル化。
- np.array([X, Y])で x0, x1 の 2 行 m * n 列の配列にする。
- バッチ関数ではforで 1 行ずつ取り出し、m * n 列のベクトルとして処理する。
- 各場所(x0, x1)での、勾配をplt.quiver使用してベクトル(矢印)として描画する。
- 式
df(x0, x1)
x0 = x0 - η ─────
dx0
df(x0, x1)
x1 = x1 - η ─────
dx1
-
説明
- 上記は、一回の更新式で、勾配法では、このステップを繰り返す。
- ηは学習率で、一回の学習でどれだけ学習すべきか?=どれだけパラメタ更新するか?
- 学習率は、0.01、0.001など前もって値を決める、
それから正しく学習できているか確認しながら調整する。
-
Python
以下の式の最小値を勾配法で求める。
2 2
f(x0, x1) = x0 + x1
- 実装
import numpy as np
def numerical_gradient(f, x01):
"""偏微分"""
h = 1e-4 # 微小な値hとして1の-4乗を用いる
grad = np.zeros_like(x01) # x01と同じ形状で要素が0。
# print("x01:" + str(x01));
for idx in range(x01.size):
tmp_val = x01[idx]
# 前方差分から中心差分にして誤差減
# f(x + h)
fxh1 = f(tmp_val + h)
# f(x - h)
fxh2 = f(tmp_val - h)
# (f(x + h) - f(x - h)) / 2 * h
grad[idx] = (fxh1 - fxh2) / (2 * h)
return grad
def gradient_descent(f, init_x01, lr, step_num):
print("ln : step_num = " + str(lr) + " : " + str(step_num))
x01 = init_x01
for i in range(step_num):
grad = numerical_gradient(f, x01)
x01 -= lr * grad
return x01
def function_2(x):
return np.sum(x**2)
init_x = np.array([-3.0, 4.0])
print(gradient_descent(function_2, init_x, 0.1, 100))
# 学習率が大きすぎる。
init_x = np.array([-3.0, 4.0])
print(gradient_descent(function_2, init_x, 10.0, 100))
# 学習率が小さすぎる。
init_x = np.array([-3.0, 4.0])
print(gradient_descent(function_2, init_x, 1e-10, 100))- 出力
ln : step_num = 0.1 : 100
[ -6.11110793e-10 8.14814391e-10]
ln : step_num = 10.0 : 100
[ -1.91613251e+13 -1.26893162e+12]
ln : step_num = 1e-10 : 100
[-2.99999994 3.99999992]
ニューラルネットワークの学習における勾配は、
重みパラメタに関する損失関数の勾配となる。
- 式
┌ w11 w21 w31 ┐
W = │ │
└ w12 w22 w32 ┘
┌ dL dL dL ┐
│ ── ── ── │
dL │ dw11 dw21 dw31 │
── = │ │
dW │ dL dL dL │
│ ── ── ── │
└ dw12 dw22 dw32 ┘
-
Python
-
gradient_simplenet.py
https://github.com/oreilly-japan/deep-learning-from-scratch/blob/master/ch04/gradient_simplenet.py-
重みパラメタをインスタンス変数に持つ。
2 行 3 列の行列で、標準正規分布関数に従う乱数を生成 -
x = 入力データ, t = 正解ラベル
-
predict(x)メソッド : dot product(ドット積)メソッドで推論する。
-
loss(x, t)メソッド : 損失関数(の値を求める)
predict ---> softmax ---> cross_entropy_error- predict(推論する)
- softmax(Softmax関数
(ニューラルネットワーク(推論)の該当節を参照)) - cross_entropy_error(交差エントロピー誤差)
-
nditerで多次元配列に対応した勾配の計算メソッド : common.gradient.numerical_gradient
https://github.com/oreilly-japan/deep-learning-from-scratch/blob/master/common/gradient.py#L34
-
-
実行
フォルダ構成を維持した状態でch04に定義した以下のファイルを実行。C:\deep-learning-from-scratch-master\ch04>python Untitled-1.py -
simpleNetを使って推論/学習
- 実装
-
import sys, os
sys.path.append(os.pardir) # 親ディレクトリのファイルをインポートするための設定
import numpy as np
from common.functions import softmax, cross_entropy_error
from common.gradient import numerical_gradient
from gradient_simplenet import simpleNet
print("==================================================")
print("==================================================")
net = simpleNet()
print("W:" + str(net.W))
x = np.array([0.6, 0.9])
p = net.predict(x)
print("p:" + str(p))
t = np.array([0, 0, 1])
l = net.loss(x, t)
print("loss:" + str(l))- 出力
W:[[ 1.92087598 0.63971089 -0.26820797]
[ 0.58411529 -0.04610929 -0.88999594]]
p:[ 1.67822935 0.34232817 -0.96192113]
loss:2.92853604814
-
simpleNetを使って勾配を求める
- 実装
import sys, os
sys.path.append(os.pardir) # 親ディレクトリのファイルをインポートするための設定
import numpy as np
from common.functions import softmax, cross_entropy_error
from common.gradient import numerical_gradient
from gradient_simplenet import simpleNet
print("==================================================")
print("==================================================")
net = simpleNet()
print("W:" + str(net.W))
x = np.array([0.6, 0.9])
t = np.array([0, 0, 1])
f = lambda w: net.loss(x, t)
dW = numerical_gradient(f, net.W)
print("dW:" + str(dW))- 出力
==================================================
==================================================
W:[[ 1.83160192 0.4900981 -0.94188042]
[-0.24946104 2.91410946 -0.00695892]]
dW:[[ 0.06708443 0.51711391 -0.58419835]
[ 0.10062665 0.77567087 -0.87629752]]
- Wは、
W : [[ w11 = 1.83160192 w21 = 0.4900981 w31 = -0.94188042]
[ w12 = -0.24946104 w22 = 2.91410946 w32 = -0.00695892]]
dL/dW : [[ dL/dw11 = 0.06708443 dL/dw21 = 0.51711391 dL/dw31 = -0.58419835]
[ dL/dw12 = 0.10062665 dL/dw22 = 0.77567087 dL/dw32 = -0.87629752]]
- w11を h 増やすと、0.06708443 h 増加する。
- w12を h 増やすと、0.10062665 h 増加する。
- w21を h 増やすと、0.51711391 h 増加する。
- w22を h 増やすと、0.77567087 h 増加する。
- w31を h 増やすと、-0.58419835 h 増加する( = 減少する)。
- w32を h 増やすと、-0.87629752 h 増加する( = 減少する)。
- 勾配がプラスならhをマイナス方向に、マイナスならプラス方向に動かす。
-
停留点は、極大点、極小点、鞍点のいずれかになりうる。
- 鞍点は、ある次元では最小だが、別の次元では最大(最小ではない)など。
- 最小化問題において、極小点は局所最小点または大域的最小点のいずれか。
- (最大化問題において、極大点は局所最大点または大域的最大点のいずれか。)
-
学習率を大きく設定し、適切なタイミングで値を小さくしていくなどのアルゴリズムを適用する。
SGD (MomentumSGD, NAG)、AdaGrad、Rmsprop、AdaDelta、Adam
(深層学習のテクニックの該当節を参照)
- 平均を取って誤差逆伝播する。
- ミニバッチがランダムにサンプリングされている限り、
ミニバッチから平均として算出された損失の勾配が真の勾配に従う。
-
バッチ学習(一括学習)
- 全てのサンプルを一度に用いて勾配を求める手法
- 機械学習のアルゴリズムでは基本的にバッチ学習が採用される。
- ただし、深層学習では、データが多過ぎるため、現実的でない。
-
オンライン学習(逐次学習)
- ひとつのサンプルのみを用いて勾配を求める手法
- ノイズや外れ値の影響を受け易く、解が適切な値に収束し難い。
-
ミニバッチ学習
データを幾つかの塊に小分けにして、その塊毎に勾配を求める手法。-
勾配計算に損失関数の平均値を使用しても問題はない。
-
バッチ学習とオンライン学習の折衷案で、多くの場合において現実的な方法。
- バッチ学習ほど学習時間がかからず、
- オンライン学習ほど解の収束が不安定にならない。
-
| バッチ学習(一括学習) | ミニバッチ学習 | オンライン学習(逐次学習) | |
|---|---|---|---|
| 効率 | ✕(処理完了までが遅い) | ○(中間) | ◎(処理完了までが早い) |
| 安定性 | ◎(ノイズや外れ値の影響が小) | ○(中間) | ✕(ノイズや外れ値の影響が大) |
| 勾配降下法 | バッチ勾配降下法(最急降下法) | 確率的勾配降下法(SGD) | 〃 |
-
ミニバッチ学習では、上記の損失関数を訓練用データセットに対して適用する。
-
ここでは、訓練用データセットに対する損失関数の総和を指標とする。
-
式
E = -1/N ΣΣ tnk log ynk
n k
-
説明
-
交差エントロピー誤差の式を
N個のデータを含む訓練用データセット用に拡張する。 - 最後に、Nで割って正規化する(データ1個あたりの平均の損失関数を求める)。
-
交差エントロピー誤差の式を
-
例(MNISTデータセット(ニューラルネットワーク(推論)の該当節を参照)を使用する)
-
Python
-
実装
- one-hot表現の場合
-
"""This is a test program."""
import numpy as np
def mean_squared_error(ynk, tnk):
"""損失関数(交差エントロピー誤差)"""
print("tnk:" + str(tnk))
print("ynk:" + str(ynk))
batch_size = ynk.shape[0]
print("batch_size:" + str(batch_size))
delta = 1e-7 # log(0)はマイナス∞になるのを微小な値を足して防止。
return - 1 / batch_size * (np.sum(tnk * np.log(ynk + delta)))
TNK = np.array([[0, 0, 1, 0, 0, 0, 0, 0, 0, 0], \
[0, 0, 1, 0, 0, 0, 0, 0, 0, 0]])
YNK = np.array([[0.1, 0.05, 0.6, 0.0, 0.05, 0.1, 0.0, 0.1, 0.0, 0.0], \
[0.1, 0.05, 0.6, 0.0, 0.05, 0.1, 0.0, 0.1, 0.0, 0.0]])
print("mean_squared_error:" + str(mean_squared_error(YNK, TNK)))
YNK = np.array([[0.1, 0.05, 0.1, 0.0, 0.05, 0.1, 0.0, 0.6, 0.0, 0.0], \
[0.1, 0.05, 0.1, 0.0, 0.05, 0.1, 0.0, 0.6, 0.0, 0.0]])
print("mean_squared_error:" + str(mean_squared_error(YNK, TNK)))- one-hot表現でない場合
"""This is a test program."""
import numpy as np
def mean_squared_error(ynk, tnk):
"""損失関数(交差エントロピー誤差)"""
print("tnk:" + str(tnk))
print("ynk:" + str(ynk))
batch_size = ynk.shape[0]
print("batch_size:" + str(batch_size))
delta = 1e-7 # log(0)はマイナス∞になるのを微小な値を足して防止。
ynk = ynk + delta
print("arange:" + str(ynk[np.arange(batch_size), tnk]))
return - 1 / batch_size * (np.sum(np.log(ynk[np.arange(batch_size), tnk])))
TNK = np.array([2, 2])
YNK = np.array([[0.1, 0.05, 0.6, 0.0, 0.05, 0.1, 0.0, 0.1, 0.0, 0.0], \
[0.1, 0.05, 0.6, 0.0, 0.05, 0.1, 0.0, 0.1, 0.0, 0.0]])
print("mean_squared_error:" + str(mean_squared_error(YNK, TNK)))
YNK = np.array([[0.1, 0.05, 0.1, 0.0, 0.05, 0.1, 0.0, 0.6, 0.0, 0.0], \
[0.1, 0.05, 0.1, 0.0, 0.05, 0.1, 0.0, 0.6, 0.0, 0.0]])
print("mean_squared_error:" + str(mean_squared_error(YNK, TNK)))- 出力
同上
-
バッチサイズ
-
ミニバッチ勾配降下法のサブセットのサイズ
-
1,000件の訓練データでバッチサイズを200にすると、
サブセットのサイズは200件でバッチの回数は5回になる。
-
-
イテレーション数
-
前述の訓練データ中のバッチ回数がイテレーション数
-
訓練データサイズをバッチサイズで割った値
-
訓練データサイズとバッチサイズが決まれば自動的に決まる。
-
-
エポック数
- 一つの訓練データを何回繰り返して学習させるか?
- 深層学習ではパラメタが収束するまで同じ訓練データで繰り返し学習。
- 学習回数(エポック数)を一定以上増加させると、過学習が発生
-
参考
機械学習/ディープラーニングにおける
バッチサイズ、イテレーション数、エポック数の決め方 - Qiita
https://qiita.com/kenta1984/items/bad75a37d552510e4682
-
適切なバッチサイズを選ぶ事が、学習を上手く行うために必要になる。
-
大きくした場合
- 大まかな特徴を捉えるため精度は下がる。
- パラメタの更新が少ないため計算コストは下がる。
-
小さくした場合
- 精度は上がる。小さすぎると学習が終了しない可能性がある。
- 1件毎に誤差逆伝播処理するため計算回数=コストは上がる。
-
その他注意点
- GPU を用いて計算する際は、2 のべき乗にバッチサイズを設定する。
- バイアスがかからないようにミニバッチはランダムに選ぶ。
-
-
CNNでの学習例
バッチサイズ500のままエポック数を増やせば
より良い精度が出ると考えられる。-
バッチサイズ5
- そもそも学習が上手くできていない。
- 1つ1つのデータに敏感に反応し過ぎて学習が難しい。
-
バッチサイズ50
- 初めは順調に学習が進む。
- 途中から過学習を起こす。
-
バッチサイズ500
- 過学習を起こすことなく順調に学習が進むが、
- パラメタ更新回数が少ないため、学習は途中
-
※loss(損失)とacc(正解率)の進捗を確認する。
- ニューラルネットワークには適応可能な重みとバイアスがある。
- 学習により、この重みとバイアスを訓練データに適応するように調整する。
- ニューラルネットワークの学習は、以下の4つのステップで行われる。
- 訓練データから、ランダムにデータを選択する。
- ここでは、損失関数の値を減らすことを目的とする。
重みパラメタを勾配の方向に微小量だけ更新する。
TwoLayerNetクラス
https://github.com/oreilly-japan/deep-learning-from-scratch/blob/master/ch04/two_layer_net.py
| # | 変数 | 説明 |
|---|---|---|
| 1 | params | ニューラルネットワークの重みパラメタのディクショナリ変数 |
| 1-1 | params['W1'] | 第1層の重み |
| 1-2 | params['b1'] | 第1層のバイアス |
| 1-3 | params['W2'] | 第2層の重み |
| 1-4 | params['b2'] | 第2層のバイアス |
| 2 | grads | numerical_gradientで計算された勾配を保持するディクショナリ変数 |
| 2-1 | grads['W1'] | 第1層の重みの勾配 |
| 2-2 | grads['b1'] | 第1層のバイアスの勾配 |
| 2-3 | grads['W2'] | 第2層の重みの勾配 |
| 2-4 | grads['b2'] | 第2層のバイアスの勾配 |
| # | メソッド シグネチャ / 引数 | 説明 |
|---|---|---|
| 1 | __init__(self, input_size, hidden_size, output_size, weight_init_std=0.01): |
コンストラクタ |
| 1-1 | self | インスタンス |
| 1-2 | input_size | 入力層のニューロンの数 |
| 1-3 | hidden_size | 隠れニューロンの数 |
| 1-4 | output_size | 出力層のニューロンの数 |
| 1-5 | weight_init_std | ・・・ |
| 2 | predict(self, x): |
推論を行う |
| 2-1 | self | インスタンス |
| 2-2 | x | 画像データ |
| 3 | loss(self, x, t): |
損失関数の値を求める |
| 3-1 | self | インスタンス |
| 3-2 | x | 画像データ |
| 3-3 | t | 正解ラベル |
| 4 | accuracy(self, x, t): |
推論の精度を求める |
| 4-1 | self | インスタンス |
| 4-2 | x | 画像データ |
| 4-3 | t | 正解ラベル |
| 5 | numerical_gradient(self, x, t): |
lossを使用し各重みパラメタの勾配を求める |
| 5-1 | self | インスタンス |
| 5-2 | x | 画像データ |
| 5-3 | t | 正解ラベル |
| 6 | gradient(self, x, t): |
numerical_gradientの高速版 |
| 6-1 | self | インスタンス |
| 6-2 | x | 画像データ |
| 6-3 | t | 正解ラベル |
-
式
行列の積による実装と、
バッチ化の各節を参考に、以下のようになる。-
第1層 (100個のバッチ)
-
x(1) =
(100, 784 = 28 * 28 pixel)
-
-
重みパラメタ
-
W(1) =
(784, x = 100) -
b(1) =
(1, x = 100)
-
-
隠れ層 (x = 100)
-
A(1) =
(100, x = 100) -
= x(2) =
(100, x = 100)
-
-
重みパラメタ
-
W(2) =
(x = 100, 10) -
b(2) =
(1, 10)
-
-
第2層
-
A(2) =
(100, 10) # 0-9の数字
-
-
-
Python
https://github.com/oreilly-japan/deep-learning-from-scratch/blob/master/ch04/train_neuralnet.py- 実装
C:\deep-learning-from-scratch-master\ch04\train_neuralnetを以下のように編集
- 実装
# coding: utf-8
import sys, os
sys.path.append(os.pardir) # 親ディレクトリのファイルをインポートするための設定
import numpy as np
import matplotlib.pyplot as plt
from dataset.mnist import load_mnist
from two_layer_net import TwoLayerNet
# データの読み込み
(x_train, t_train), (x_test, t_test) = load_mnist(normalize=True, one_hot_label=True)
network = TwoLayerNet(input_size=784, hidden_size=50, output_size=10)
iters_num = 10000 # 繰り返しの回数を適宜設定する
train_size = x_train.shape[0]
batch_size = 100
learning_rate = 0.1
train_loss_list = []
for i in range(iters_num):
print(str(i) + " / " + str(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)
plt.xlabel("Iteration")
plt.ylabel("loss")
plt.plot(range(iters_num), train_loss_list)
plt.show()-
実行
フォルダ構成を維持した状態でch04に定義した上記ファイルを実行。C:\deep-learning-from-scratch-master\ch04>python train_neuralnet -
出力
-
CMD
0 / 10000 ... 9999 / 10000 -
グラフ
-

学習結果を使用して、学習データとテストデータを推論する。
-
Python
https://github.com/oreilly-japan/deep-learning-from-scratch/blob/master/ch04/train_neuralnet.py-
実装
C:\deep-learning-from-scratch-master\ch04\train_neuralnetのオリジナルを実行 -
実行
フォルダ構成を維持した状態でch04に定義した上記ファイルを実行。C:\deep-learning-from-scratch-master\ch04>python train_neuralnet -
出力
1バッチ・サイクル毎に学習した重みパラメタを使用した推論結果が表示される。- CMD
-
train acc, test acc | 0.102183333333, 0.101
train acc, test acc | 0.783416666667, 0.7894
train acc, test acc | 0.874916666667, 0.8791
train acc, test acc | 0.8964, 0.8991
train acc, test acc | 0.907433333333, 0.9092
train acc, test acc | 0.913616666667, 0.9147
train acc, test acc | 0.9184, 0.9185
train acc, test acc | 0.923366666667, 0.9238
train acc, test acc | 0.926316666667, 0.9275
train acc, test acc | 0.9294, 0.9298
train acc, test acc | 0.932666666667, 0.9318
train acc, test acc | 0.9341, 0.9341
train acc, test acc | 0.936816666667, 0.9367
train acc, test acc | 0.940133333333, 0.9382
train acc, test acc | 0.94175, 0.9397
train acc, test acc | 0.943566666667, 0.9417
train acc, test acc | 0.945233333333, 0.9435
- グラフ

-
人間が手動で設定するパラメタ
-
確率的勾配降下法(SGD)(深層学習のテクニックの該当節を参照)の繰り返し回数
iters_num = 10000 -
1回のミニバッチのサイズ
batch_size = 100 -
学習率
learning_rate = 0.1
-
-
NNの構造に組み込まれているハイパーパラメタ
-
層のサイズ
-
層の深さ
-
移行メモ
「バッチ、ミニバッチ、オンライン学習」の表は、元の PukiWiki で 最終行の後ろ2列が横結合(
|>|)されていたため、 ミニバッチ学習の欄に値を置き、オンライン学習の欄を 〃 とした。「2層NNのクラス」の変数・メソッドの表は、元の PukiWiki で セルの横結合を用いた2階層の表だったため、GitHub Wiki では 単一のヘッダ行に整理した。あわせて、
gradsの子項目の番号が1-1〜1-4、キーがparams[...]となっていたものを2-1〜2-4、grads[...]に正した。「曲線の勾配計算」の「上記の例」のリンクは、元の PukiWiki の アンカが本ページ内に存在しない(削除された節を指す)ため、 参照先の式 f(x0, x1) = x0² + x1² を直接記述した。
「勾配降下法」の冒頭は、元の PukiWiki では 「最小(最大)値を出力する鞍点(saddle point)を、勾配を使用して探す。」 となっていたが、本ページ自身が「上手く鞍点を脱するか」と述べている通り 鞍点は脱するべき停留点であるため、「最小(最大)値を出力するパラメタ」に正した。
「バッチサイズ、イテレーション数、エポック数」の 「訓練データサイズをバッチサイズで割った値」は イテレーション数の説明であるため、その項へ移した。
Tags: 移行, 人工知能, 深層学習, ニューラルネットワーク, 学習, 損失関数, 勾配降下法, ミニバッチ, Python
このWikiは「Open棟梁Project」,「OSSコンソーシアム 開発基盤部会」によって運営されています。