【フレームワークを使用せず】ゼロから作る物体検出AIモデル
【No.8】ニューラルネットワークの実装(ロス関数)
1. 本記事について
本記事は、これまでの内容(ニューラルネットワークの理解その1〜4)を踏まえ、ニューラルネットワークをpythonプログラムに落とし込んでいきます。
本記事はロス関数の実装を解説していきます。
(前回は、Leaky ReLUについて解説しました。)
ニューラルネットワークの理解その1~4を見てから、本記事を見ることをお勧めします(特にその2とその4)。
注意事項として、本シリーズではpythonの基礎的な解説、numpyの基礎的な解説は割愛させていただきます。特に、numpy配列やshape等頻出なので、その辺の知識が不十分の場合は、そちらの習得を先に行うことをお勧めします。
本シリーズを進めていくにあたり、参考にさせていただく書籍があります。オライリー・ジャパンから出版されている「ゼロから作るDeep Learning ーPythonで学ぶディープラーニングの理論と実装」です。
本記事は、上記書籍を参考にさせていただいております。
2. ロス関数
ロス関数をざっくり説明すると「推論値と正解値の差」のことです。
ロス関数がよくわからない方はこちらで詳しく解説しているので、ご参照いただければと思います。
今回ロス関数をpythonで実装していくのですが、そもそもロス関数がしっかり定まっていないので、まず、実装するロス関数を定義したいと思います。
ロス関数の定義
今回は、ロス関数を以下と定義したいと思います。
式1
式1は2乗和誤差(Sum of Squared Error :SSE)と呼ばれ、ロス関数の基本的な算出方法になります。
このシリーズのゴール(YOLOv1的な物体検出モデルを構築すること)からいうと、YOLOv1で使用されているロス関数をそのまま使った方がいいのですが、それに関してはYOLOv1の全体のネットワーク構造を踏まえた上で進めて行った方が理解しやすいと思うので、もう少し後に解説させていただこうと考えています。
従って今回はシンプルで理解しやすい式1をロス関数として進めていきます。
とはいえ、YOLOv1のロス関数は基本的にSSEで構築されている様なものなので、非常に重要な実装になります。
式1の説明ですが、\(E\)がロス関数、\(y_k\)が出力値(推論値)、\(t_k\)が出力値に対応する正解値です。
今回出力値が複数個ある想定なので、\(y_1-t_1, y_2-t_2...y_n-t_n\)をそれぞれ足し合わせます。
\(Σ\)がその部分です。
\(\frac{1}{2}\)はなくても良いのですが、微分した時に2を消せるので置いておきます。
ロス関数の逆伝播は以下の式で表せます。
式2
ここについてさらっと説明します。詳しくはこちらを参照して頂ければと思います。
式2は式1の\(E\)を\(y_j\)で偏微分した結果です。つまり、以下となります。
なぜ\(E\)を\(y_j\)で偏微分したのかというと、こちら記載の、出力層の\(Δw\)の計算を思い出して欲しいのですが、以下の様になります。
ここで\(\frac{∂E}{∂y_j}\)が出てきました。\(Δw\)を求める為に\(\frac{∂E}{∂y_j}\)が必要になってくるということです。
なので\(E\)を\(y_j\)で偏微分するということになります。
式2を見ると添字がなくなっていますが、数理の際は\(y_1\)から\(y_j\)...\(y_n\)まで一つずつ計算をしますが、pythonで実装する際は\(y\)は配列化されており、配列のまま計算可能ですので、ひとくくりに\(y\)となっています。
ロス関数の実装
式1、式2を元に、pythonで実装していきます。
まず順伝播、というかロス関数の計算方からいきます。式1を以下の様に落とし込みます。
import numpy as np
loss = np.sum((y-t)**2) / 2
シンプルで良いですね。
次に逆伝播を実装します。式2を以下の様に落とし込みます。
import numpy as np
dx = y - t
順伝播時に使用した\(y\)と\(t\)を使用します。
最後に順伝播と逆伝播をまとめます。
import numpy as np
class loss():
# 各値の初期化
def __init__(self):
self.loss = None
self.y = None
self.t = None
# 順伝播
def forward(self, y, t):
self.y = y# 順伝播のyを保持
self.t = t# 教師データを保持
self.loss = np.sum((y-t)**2) / 2
return self.loss
# 逆伝播
def backward(self):
dx = self.y - self.t
return dx
実際に数値を入れて動作を確認します。
import numpy as np
class loss():
# 各値の初期化
def __init__(self):
self.loss = None
self.y = None
self.t = None
# 順伝播
def forward(self, y, t):
self.y = y# 順伝播のyを保持
self.t = t# 教師データを保持
self.loss = np.sum((y-t)**2) / 2
return self.loss
# 逆伝播
def backward(self):
dx = self.y - self.t
return dx
### 実際に数値を入力してみる ###
# 推論値: ノード数3
y = np.array([[-0.232, 0.774, 0.927]])
# 正解値: ノード数3
t = np.array([[0, 1, 2]])
# インスタンス化
Loss = loss()
# 順伝播(ロス関数の算出)
L = Loss.forward(y, t)
print(L)
print()
# 逆伝播
dX = Loss.backward()
print(dX)
0.6281144999999999
[[-0.232 -0.226 -1.073]]
できました。
因みに、これからこの0.628...を小さくしていくために\(w\)の値を更新していきます。これがニューラルネットワークにおける「学習」ということになります。
3. まとめ
今回はニューラルネットワークの実装(ロス関数)について解説しました。
次回も是非みてみてください!
4. 参考文献
斎藤康毅 著 「ゼロから作るDeep Learning ーPythonで学ぶディープラーニングの理論と実装」
Link
Search