AIって結局何なのかよく分からないので、とりあえず100日間勉強してみた Day55
経緯についてはこちらをご参照ください。
■本日の進捗
- 勾配降下法を理解
■はじめに
今回も「ゼロから作るDeep Learning Pythonで学ぶディープラーニングの理論と実装(オライリー・ジャパン)」で、深層学習を学んでいきます。
今回は、ついに損失関数を最小化して自動で学習させる方法を学びます。
■勾配降下法
勾配降下法(gradient descent method)とは、損失関数の値を徐々に減らしていくことでニューラルネットワークのパラメータ(重みやバイアス)を最適化するアルゴリズムです。
これまでは損失関数の値を算出するところまで実装してきましたが、これを上手く最小化することで初めて誤差を減らして良い学習モデルを構築することができます。
$$ x = x – \eta \frac{ \partial f }{ \partial x } $$
ここで、ηは学習率(learning rate)と呼ばれ、パラメータの更新の大きさを制御する定数です。この値は人間が事前に決めることになります。偏微分項は損失関数fの勾配で、現在の値から最も大きく増加する方向を示します。この逆方向を取ることで、損失を減らせるというものです。
交差エントロピー誤差を勾配降下法で最小化する手法でニューラルネットワークに実装してみます。
import sys import numpy as np import matplotlib.pyplot as plt sys.path.append("./") from work.mnist import load_mnist def relu(x): return np.maximum(0, x) def softmax(z): c = np.max(z, axis=1, keepdims=True) exp_z = np.exp(z - c) sum_exp_z = np.sum(exp_z, axis=1, keepdims=True) y = exp_z / sum_exp_z return y def a(x, W, b): return np.dot(x, W) + b def init_network(): network = {} network['W1'] = np.random.randn(784, 50) * 0.1 network['b1'] = np.zeros(50) network['W2'] = np.random.randn(50, 100) * 0.1 network['b2'] = np.zeros(100) network['W3'] = np.random.randn(100, 10) * 0.1 network['b3'] = np.zeros(10) return network def forward(network, x): W1, W2, W3 = network['W1'], network['W2'], network['W3'] b1, b2, b3 = network['b1'], network['b2'], network['b3'] a1 = a(x, W1, b1) z1 = relu(a1) a2 = a(z1, W2, b2) z2 = relu(a2) a3 = a(z2, W3, b3) y = softmax(a3) return y def cross_entropy_error(y, t): delta = 1e-7 return -np.sum(t * np.log(y + delta)) / y.shape[0] def numerical_gradient(f, x): h = 1e-4 grad = np.zeros_like(x) it = np.nditer(x, flags=['multi_index'], op_flags=['readwrite']) while not it.finished: idx = it.multi_index tmp_val = x[idx] x[idx] = tmp_val + h fxh1 = f(x) # f(x + h) x[idx] = tmp_val - h fxh2 = f(x) # f(x - h) grad[idx] = (fxh1 - fxh2) / (2 * h) x[idx] = tmp_val it.iternext() return grad def numerical_gradient_network(network, x, t): grads = {} def loss_W(W): y = forward(network, x) return cross_entropy_error(y, t) grads['W1'] = numerical_gradient(loss_W, network['W1']) grads['b1'] = numerical_gradient(loss_W, network['b1']) grads['W2'] = numerical_gradient(loss_W, network['W2']) grads['b2'] = numerical_gradient(loss_W, network['b2']) grads['W3'] = numerical_gradient(loss_W, network['W3']) grads['b3'] = numerical_gradient(loss_W, network['b3']) return grads (X_train, y_train), (X_test, y_test) = load_mnist(normalize=True, one_hot_label=True) print("X_train.shape :{}".format(X_train.shape)) train_size = X_train.shape[0] batch_size = 10 np.random.seed(8) batch_mask = np.random.choice(train_size, batch_size) X_batch = X_train[batch_mask] y_batch = y_train[batch_mask] lr = 0.01 step_num = 100 network = init_network() for i in range(step_num): grads = numerical_gradient_network(network, X_batch, y_batch) for key in ('W1', 'b1', 'W2', 'b2', 'W3', 'b3'): network[key] -= lr * grads[key] y = forward(network, X_batch) loss = cross_entropy_error(y, y_batch) print(f"Step {i+1}, Loss: {loss}")
Step 1, Loss: 2.3530156333205685
Step 2, Loss: 2.325261005193958
Step 3, Loss: 2.2982270663146633
Step 4, Loss: 2.2718446680229425
Step 5, Loss: 2.245982306687302
Step 6, Loss: 2.2203704040415024
Step 7, Loss: 2.1951046881513148
Step 8, Loss: 2.1700576041384236
Step 9, Loss: 2.1457384153511097
Step 10, Loss: 2.1218802466749453
Step 11, Loss: 2.0982691083940646
Step 12, Loss: 2.0746644148340083
Step 13, Loss: 2.0513818823846792
Step 14, Loss: 2.028097539058615
Step 15, Loss: 2.005042328202429
Step 16, Loss: 1.9825131143053167
Step 17, Loss: 1.9603358726933038
Step 18, Loss: 1.9386429504545848
Step 19, Loss: 1.917034715062457
Step 20, Loss: 1.8956181131574072
Step 21, Loss: 1.8744659241018315
Step 22, Loss: 1.8533287832310568
Step 23, Loss: 1.8324847390001253
Step 24, Loss: 1.812044654157803
Step 25, Loss: 1.7919155973622072
Step 26, Loss: 1.7720145141160155
Step 27, Loss: 1.7524266810015867
Step 28, Loss: 1.7330353585197014
Step 29, Loss: 1.7138846389196172
Step 30, Loss: 1.6945811058455937
Step 31, Loss: 1.6756153296572234
Step 32, Loss: 1.656902586602947
Step 33, Loss: 1.638519001194976
Step 34, Loss: 1.620444628760125
Step 35, Loss: 1.602415618796212
Step 36, Loss: 1.5847799012025918
Step 37, Loss: 1.5673874306930162
Step 38, Loss: 1.5502280458721316
Step 39, Loss: 1.5336133941935133
Step 40, Loss: 1.5170960889252239
Step 41, Loss: 1.5007028045330204
Step 42, Loss: 1.4845611932491414
Step 43, Loss: 1.4686180989480682
Step 44, Loss: 1.452834501306468
Step 45, Loss: 1.4372407971213945
Step 46, Loss: 1.4219125418510803
Step 47, Loss: 1.4066670353834225
Step 48, Loss: 1.3916983753624295
Step 49, Loss: 1.3768805947569889
Step 50, Loss: 1.3623180271213127
Step 51, Loss: 1.3478338535513559
Step 52, Loss: 1.333454405537196
Step 53, Loss: 1.3194615037529938
Step 54, Loss: 1.3052834552958057
Step 55, Loss: 1.2915686515305078
Step 56, Loss: 1.277845536203047
Step 57, Loss: 1.2645485590154086
Step 58, Loss: 1.2510652958925859
Step 59, Loss: 1.2377845386908675
Step 60, Loss: 1.224727789288155
Step 61, Loss: 1.21173506204716
Step 62, Loss: 1.1990360846961035
Step 63, Loss: 1.1863646306352877
Step 64, Loss: 1.173561112931067
Step 65, Loss: 1.1609369837205332
Step 66, Loss: 1.1482412809033022
Step 67, Loss: 1.1359210411596927
Step 68, Loss: 1.1234418325212847
Step 69, Loss: 1.1112795918075742
Step 70, Loss: 1.099338383539
Step 71, Loss: 1.0870185622072719
Step 72, Loss: 1.0752167407862063
Step 73, Loss: 1.063099029580949
Step 74, Loss: 1.0515294682541956
Step 75, Loss: 1.0396564717867418
Step 76, Loss: 1.0281457405928245
Step 77, Loss: 1.016624316454742
Step 78, Loss: 1.0051413667407751
Step 79, Loss: 0.9936664912647787
Step 80, Loss: 0.9824608121974592
Step 81, Loss: 0.9710574097352485
Step 82, Loss: 0.9598640988969553
Step 83, Loss: 0.9486097051933996
Step 84, Loss: 0.9373587229321755
Step 85, Loss: 0.9262468884653554
Step 86, Loss: 0.9151520649891906
Step 87, Loss: 0.9040658115068799
Step 88, Loss: 0.8929642499878157
Step 89, Loss: 0.8821770073861659
Step 90, Loss: 0.8712495387022952
Step 91, Loss: 0.8602125181587927
Step 92, Loss: 0.8495685128492871
Step 93, Loss: 0.838777474364473
Step 94, Loss: 0.8282802839373046
Step 95, Loss: 0.8178319558445126
Step 96, Loss: 0.8074593231670439
Step 97, Loss: 0.797232678625498
Step 98, Loss: 0.7870099240805443
Step 99, Loss: 0.7768182337259985
Step 100, Loss: 0.766848703816795
MNISTデータセットを用いたニューラルネットワークの損失関数を算出し、勾配降下法で100ステップ学習させ、損失関数の変化を見てみました。
ステップごとに徐々に交差エントロピー誤差の値が下がっていて、ニューラルネットワークが上手く学習できている様子が確認できました。
■おわりに
遂に機械学習ライブラリを用いずにニューラルネットワークがデータを学習するところまで実装することができました。
ちなみに前回学んだミニバッチ化の要素を少し入れていますが、バッチ(全データ)で学習させてみたところ、爆裂に重かった(Ryzen9をもってしても1ステップも動かない…)のでこのように書いてみました。並列化やGPU化ができれば恐らくそれほど問題ではなかったのかもしれませんが、その辺もやっぱり既存ライブラリのメリットだと実感できます。しかし、自分で実装してみるというのは何にも代えがたい達成感と理解が深まる実感をずっしりと感じます。この記事に巡り合ってくださった方がもしいらっしゃったら是非参考文献(と本稿も入れていただければ光栄です)を参考に書いてみてください。
■参考文献
- Andreas C. Muller, Sarah Guido. Pythonではじめる機械学習. 中田 秀基 訳. オライリー・ジャパン. 2017. 392p.
- 斎藤 康毅. ゼロから作るDeep Learning Pythonで学ぶディープラーニングの理論と実装. オライリー・ジャパン. 2016. 320p.
- ChatGPT. 4o mini. OpenAI. 2024. https://chatgpt.com/
- API Reference. scikit-learn.org. https://scikit-learn.org/stable/api/index.html