深層学習day1
0.ニューラルネットワークの構成
ディープラーニングの目的は重みとバイアスを最適化して、誤差を最小化するパラメータを発見すること。そのk本的な構成がニューラルネットワークである。
1.入力層~中間層
入力層
説明変数(特徴量)の値が入力される個所。
具体例:犬や猫など動物を分類するとき、説明変数は体長、体重、ひげの本数、平均寿命、などになり、それぞれの値(体長=40cm、体重=10kg、ひげの本数=300本、平均寿命=15年、…)が入力され、中間層へ伝達される。
2.活性化関数
ニューラルネットワークにおいて、次の層への出力の大きさを決める非線形の関数。入力値の値によって、次の層への信号のON/OFFや強弱を定める働きをもつ。
※非線形、線形の違い
線形とは、相互間の関係が比例関係にある状態。 グラフを描くと真っ直ぐな線になる。
非線形とは、相互間の関係が比例関係で表せない状態。
ステップ関数は、 で表現される。しきい値を超えたら発火し、出力は常に1か0である。線形分離である、簡単なものにしか適用できない。
シグモイド関数は、 で表現される。0 ~ 1の間を緩やかに変化する関数で、ステップ関数ではON/OFFしかない状態に対し、信号の強弱を伝えられる。その反面、大きな値では出力の変化が微小なため、勾配消失問題を引き起こす事が課題である。
RELU関数は、 で表現される。今最も使われている活性化関数勾配消失問題の回避とスパース化に貢献することで良い成果をもたらしている。
入力層から中間層、活性化関数の整理として、順伝播で入力層:4ノード1層、中間層:3ノード1層、中間層活性化関数:シグモイド関数を適用、の事例を計算した。
# 順伝播(単層・複数ユニット) # 重み W = np.array([ [0.1, 0.2, 0.3], [0.2, 0.3, 0.4], [0.3, 0.4, 0.5], [0.4, 0.5, 0.6] ]) print_vec("重み", W) # バイアス b = np.array([0.1, 0.2, 0.3]) print_vec("バイアス", b) # 入力値 x = np.array([1.0, 5.0, 2.0, -1.0]) print_vec("入力", x) # 総入力 u = np.dot(x, W) + b print_vec("総入力", u) # 中間層出力 z = functions.sigmoid(u) print_vec("中間層出力", z)
3.出力層
推定結果(構成図中の)が得られる層。真の出力値(教師データともいう、構成図中の)との誤差を確認する。誤差の大きさ、特徴に応じて各層の重みをのちに調整する。中間層と同様に前の層の値と重みより内積演算を実施する。活性化関数も適用するが、使用する関数が中間層と異なる。
a.恒等写像
で表現される。回帰問題によく使用される。
b.シグモイド関数
で表現される。前述の通り、0~1の値を取るため、二値分類問題に適用される。
c.ソフトマックス関数
で表現される。3つ以上の多クラス分類問題に適用される。
誤差関数については、回帰問題では二乗誤差、分類問題では交差エントロピーが一般的に使用される。
a.二乗誤差
で表現される。ここで差の値のままでなく二乗するのは、誤差の向きが正でも負でも評価値を大きくする(誤差が大きいと表示する)ためである。また、1/2を掛けているのは微分するときに二乗の「2」と打ち消すためである。
4.勾配降下法
深層学習の目的は、学習を通して誤差を最小にするネットワークを作成すること
→ 誤差 を最小化するパラメータ を発見すること
a.勾配降下法
全サンプルの平均誤差から勾配を取り、学習率を掛けて、重みを修正する。
b.確率的勾配降下法
ランダムに抽出したサンプルの誤差を取る。メリットとしては、データが冗⻑な場合の計算コストの軽減、望まない局所極小解に収束するリスクの軽減、オンライン学習(学習データが入ってくるたびにその都度、新たに入ってきたデータのみを使って学習を行うこと)ができる、が挙げられる。
c.ミニバッチ勾配降下法
ランダムに分割したデータの集合(ミニバッチ)に属するサンプルの平均誤差を取る。メリットとしては、確率的勾配降下法のメリットを損なわず、計算機の計算資源を有効利用できることである。
5.誤差逆伝播法
誤差勾配を数値微分で計算すると、各パラメータ それぞれについて や を計算するために、順伝播の計算を繰り返し行う必要があり負荷が大きい。
誤差逆伝播法は、算出された誤差を出力層側から順に微分し、前の層へと伝播することにより、不要な再帰的計算を避けて各パラメータの微分値を計算する手法である。
最後に演習で入力層:2ノード1層、中間層:3ノード1層、出力層:2ノード1層の2値分類ネットワークで逆誤差伝播を確認した。ポイントになる部分にはコメントを追記した。いかに効率よく勾配および重みの修正しているか、イメージをつかむことができた。
# ウェイトとバイアスを設定 # ネートワークを作成 def init_network(): print("##### ネットワークの初期化 #####") 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.2, 0.5], [0.3, 0.6] ]) network['b1'] = np.array([0.1, 0.2, 0.3]) network['b2'] = np.array([0.1, 0.2]) print_vec("重み1", network['W1']) print_vec("重み2", network['W2']) print_vec("バイアス1", network['b1']) print_vec("バイアス2", network['b2']) return network # 順伝播 def forward(network, x): print("##### 順伝播開始 #####") 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) print_vec("総入力1", u1) print_vec("中間層出力1", z1) print_vec("総入力2", u2) print_vec("出力1", y) print("出力合計: " + str(np.sum(y))) return y, z1 # 誤差逆伝播 def backward(x, d, z1, y): print("\n##### 誤差逆伝播開始 #####") grad = {} W1, W2 = network['W1'], network['W2'] b1, b2 = network['b1'], network['b2'] # 出力層でのデルタ(yに対するu2の偏微分) delta2 = functions.d_sigmoid_with_loss(d, y) print(delta2) # b2の勾配→u2に対するb2の偏微分は1、よってdelta2を保持 grad['b2'] = np.sum(delta2, axis=0) print(grad['b2']) # W2の勾配→u2に対するw2の偏微分はz1、よって転置したものにdelta2と内積を取る grad['W2'] = np.dot(z1.T, delta2) # 中間層でのデルタ(yに対するu1の偏微分→u2に対するz1の偏微分はw2、z1に対するu1の偏微分は*の後) delta1 = np.dot(delta2, W2.T) * functions.d_relu(z1) # b1の勾配→ u1に対するb1の偏微分は1、よってdelta1を保持 grad['b1'] = np.sum(delta1, axis=0) # W1の勾配 →u1に対するw1の偏微分はx、よって転置したものにdelta1と内積を取る grad['W1'] = np.dot(x.T, delta1) print_vec("偏微分_dE/du2", delta2) print_vec("偏微分_dE/du1", delta1) print_vec("偏微分_重み1", grad["W1"]) print_vec("偏微分_重み2", grad["W2"]) print_vec("偏微分_バイアス1", grad["b1"]) print_vec("偏微分_バイアス2", grad["b2"]) return grad # 訓練データ x = np.array([[1.0, 5.0]]) # 目標出力 d = np.array([[0, 1]]) # 学習率 learning_rate = 0.01 network = init_network() 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("##### 結果表示 #####") print("##### 更新後パラメータ #####") print_vec("重み1", network['W1']) print_vec("重み2", network['W2']) print_vec("バイアス1", network['b1']) print_vec("バイアス2", network['b2'])