【フレームワークを使用せず】ゼロから作る物体検出AIモデル
【No.8】ニューラルネットワークの実装(ロス関数)

1. 本記事について

本記事は、これまでの内容(ニューラルネットワークの理解その1〜4)を踏まえ、ニューラルネットワークをpythonプログラムに落とし込んでいきます。
本記事はロス関数の実装を解説していきます。

前回は、Leaky ReLUについて解説しました。)

ニューラルネットワークの理解その1~4を見てから、本記事を見ることをお勧めします(特にその2とその4)。

注意事項として、本シリーズではpythonの基礎的な解説、numpyの基礎的な解説は割愛させていただきます。特に、numpy配列やshape等頻出なので、その辺の知識が不十分の場合は、そちらの習得を先に行うことをお勧めします。

本シリーズを進めていくにあたり、参考にさせていただく書籍があります。オライリー・ジャパンから出版されている「ゼロから作るDeep Learning ーPythonで学ぶディープラーニングの理論と実装」です。

本記事は、上記書籍を参考にさせていただいております。

2. ロス関数

ロス関数をざっくり説明すると「推論値と正解値の差」のことです。

ロス関数がよくわからない方はこちらで詳しく解説しているので、ご参照いただければと思います。

今回ロス関数をpythonで実装していくのですが、そもそもロス関数がしっかり定まっていないので、まず、実装するロス関数を定義したいと思います。

ロス関数の定義

今回は、ロス関数を以下と定義したいと思います。

式1

\( E = \frac{1}{2}\sum_{k=1}^{n}{(y_k - t_k)^2} \)

式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

\( E' = y - t \)

ここについてさらっと説明します。詳しくはこちらを参照して頂ければと思います。

式2は式1の\(E\)を\(y_j\)で偏微分した結果です。つまり、以下となります。

\( E' = \frac{∂E}{∂y_j} = y - t \)

なぜ\(E\)を\(y_j\)で偏微分したのかというと、こちら記載の、出力層の\(Δw\)の計算を思い出して欲しいのですが、以下の様になります。

\( Δw^L_{ij} = -η \frac{∂E}{∂w^L_{ij}} \)
\( \frac{∂E}{∂w^L_{ij}} = \frac{∂E}{∂u^L_j} \times \frac{∂u^L_j}{∂w^L_{ij}} \)
\( = \frac{∂E}{∂y_j} \times \frac{∂z^L_j}{∂u^L_j} \times z^{L-1}_i \)

ここで\(\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で学ぶディープラーニングの理論と実装」

・物体検出AIの導入
・アノテーションサービス
・手書き計算サイト ZONE++ の運営
・技術ブログ LAB++の運営
   上記をメインにおこなっております

詳しくはこちら

Category

Search