【フレームワークを使用せず】ゼロから作る物体検出AIモデル
【No.7】ニューラルネットワークの実装(Leaky ReLU)
1. 本記事について
本記事は、これまでの内容(ニューラルネットワークの理解その1〜4)を踏まえ、ニューラルネットワークをpythonプログラムに落とし込んでいきます。
本記事は活性化関数(Leaky ReLU)の実装を解説していきます。
ニューラルネットワークの理解その1~4を見てから、本記事を見ることをお勧めします(特にその4)。
注意事項として、本シリーズではpythonの基礎的な解説、numpyの基礎的な解説は割愛させていただきます。特に、numpy配列やshape等頻出なので、その辺の知識が不十分の場合は、そちらの習得を先に行うことをお勧めします。
本シリーズを進めていくにあたり、参考にさせていただく書籍があります。オライリー・ジャパンから出版されている「ゼロから作るDeep Learning ーPythonで学ぶディープラーニングの理論と実装」です。
本記事は、上記書籍を参考にさせていただいております。
2. Leaky ReLU層
活性化関数の処理をpythonで実装していきます。YOLOv1は活性化関数にLeaky ReLUを採用しているので、Leaky ReLUを実装していきます(Leaky ReLUについてはこちらをみてみてください)。
Affineと同様に順伝播の処理と逆伝播の処理に分けて実装します。
順伝播
Leaky ReLUはReLUの派生系で、以下の式で表せるのでした。
式1
\(x \gt 0\)の時は\(f(x) = x\)(恒等関数)となり、\(x \leq 0\)の時は\(f(x) = αx\)(\(α\)は定数)となります。
式1をpythonで書くと以下となります。
入力をx、αをalphaとします。
import numpy as np
self.x = x# 順伝播の入力を保持
out = np.where(x > 0, x, alpha*x)
メインの処理はout = の部分で、np.whereを用いて条件分岐しています。xが0超の時そのままxを返し、xがそれ以外の時alpha*xを返します。
self.x = xがありますが、これは順伝播の入力において、0以下の要素の情報を保存しておく為のものです。逆伝播の時に使用しますので、いったんここではスルーします。
具体的に、xとalphaをそれぞれ以下とし、順伝播の計算をしてみます。
import numpy as np
# 入力: ノード数3
x = np.array([[-0.8157, 1.3761, -1.555 ]])
alpha = 0.1
out = np.where(x > 0, x, alpha*x)
print(out)
[[-0.08157 1.3761 -0.1555 ]]
順伝播はこれで完了です。続いて逆伝播を解説します。
逆伝播
Leaky ReLUの逆伝播は以下の式(式1を微分したもの)を使用します。
式2
\(f(x)\)を\(x\)で微分する形です。
因みに、これは前回Affineの実装時に除外したところになります(詳しくはこちら)。
今回(=活性化関数の逆伝播処理)の実装で、逆伝播してきた\(δ\)に活性関数の微分結果を掛けてあげます。そしてその結果を次の逆伝播として渡してあげます。
式2をpythonで表すと以下の様になります。
逆伝播の入力をdout、αをalpha、順伝播の時の入力をxとします。
import numpy as np
dx = np.where(self.x > 0, dout*1, dout*alpha)
ここは結構誤解が発生するので、詳しくみていきます。
np.whereで条件分岐されているものを言葉でいうと以下になります。
\(x \leq 0\)の時 dx = dout * alpha
\(x \gt 0\)の時 dx = dout * 1
ここのポイント1つ目に「\(x \leq 0\)のxが、順伝播の際の入力値を指している」ということです。「逆伝播のdoutで条件分岐しているわけではない」ということになります。
ここのポイント2つ目に「dx = dout * 1 のように、doutを掛けることを忘れない」ということです。忘れるとdx = 1若しくはdx = alphaになってしまうので、注意しましょうということです。
以下がたまにみる実装です。
import numpy as np
dx = np.where(dout > 0, 1, alpha)
これだと、せっかく出力層から逆伝播してきた情報が、全て1かalphaに置換されてしまいます。
その後でdoutを掛けて逆伝播する処理があれば良いのですが、そうでなければ注意が必要です(Leaky ReLUではみかけませんが、ReLUの実装でたまに見る気がします)。
逆伝播に関して、具体的に数値を入れて流してみます。
doutとalphaとxをそれぞれ以下とし、逆伝播の計算をします。
import numpy as np
# δ: ノード数3
dout = np.array([[-0.71, 0.57, -0.35]])
alpha = 0.1
# 順伝播の入力: ノード数3
x = np.array([[-0.8157, 1.3761, -1.555 ]])
dx = np.where(x > 0, dout*1, dout*alpha)
print(dx)
[[-0.071 0.57 -0.035]]
Leaky ReLUまとめ
順伝播・逆伝播をそれぞれ1つのクラスにまとめます。
class LeakyReLU:
# 各値の初期化
def __init__(self, alpha):
self.x = None# 逆伝播の時に使う
self.alpha = alpha
# 順伝播
def forward(self, x):
self.x = x# 順伝播の入力を保持
out = np.where(x > 0, x, self.alpha*x)
return out
# 逆伝播
def backward(self, dout):
dx = np.where(self.x > 0, dout*1, dout*self.alpha)# δの計算
return dx
これでLeaky ReLU層をpythonで実装完了しました。最後に、実際に数値を入れて動作を確認します。
class LeakyReLU:
# 各値の初期化
def __init__(self, alpha):
self.x = None# 逆伝播の時に使う
self.alpha = alpha
# 順伝播
def forward(self, x):
self.x = x# 順伝播の入力を保持
out = np.where(x > 0, x, self.alpha*x)
return out
# 逆伝播
def backward(self, dout):
dx = np.where(self.x > 0, dout*1, dout*self.alpha)# δの計算
return dx
### 実際に数値を入力してみる ###
# 入力: ノード数3
x = np.array([[-0.8157, 1.3761, -1.555 ]])
# δ: ノード数3
dout = np.array([[-0.71, 0.57, -0.35]])
alpha = 0.1
# インスタンス化
f = LeakyReLU(alpha)
# 順伝播
Y = f.forward(x)
print(Y)
print()
# 逆伝播
dX = f.backward(dout)
print(dX)
[[-0.08157 1.3761 -0.1555 ]]
[[-0.071 0.57 -0.035]]
大丈夫そうです!
3. まとめ
今回はニューラルネットワークの実装(Leaky ReLU層)について解説しました。
次回も是非みてみてください!
4. 参考文献
斎藤康毅 著 「ゼロから作るDeep Learning ーPythonで学ぶディープラーニングの理論と実装」
Link
Search