Backpropagationの実装
メニューを表示するにはスワイプしてください
一般的なアプローチ
順伝播では、各層 l が前の層の出力 al−1 を入力として受け取り、自身の出力を計算します。そのため、forward() クラスの Layer メソッドは、前の出力ベクトルのみをパラメータとして受け取り、その他の必要な情報はクラス内に保持されます。
逆伝播では、各層 l は dal のみを使って勾配を計算し、dal−1 を返します。そのため、backward() メソッドは dal ベクトルをパラメータとして受け取ります。その他の必要な情報はすでに Layer クラス内に保存されています。
活性化関数の導関数
逆伝播には活性化関数の導関数が必要となるため、ReLU や sigmoid などの活性化関数は、単独の関数ではなくクラスとして実装する必要があります。この構造により、両方の要素を明確に定義できます:
- 活性化関数本体 —
__call__()メソッドで実装し、Layerクラス内でself.activation(z)のように直接適用可能; - その導関数 —
derivative()メソッドで実装し、逆伝播時にself.activation.derivative(z)で効率的に計算可能。
活性化関数をオブジェクトとして表現することで、さまざまな層に簡単に渡して、順伝播・逆伝播の両方で動的に適用できます。
ReLu
ReLU活性化関数の導関数は以下の通りです。ここで zi は事前活性化ベクトル z の要素です:
f′(zi)={1,zi>00,zi≤0class ReLU:
def __call__(self, z):
return np.maximum(0, z)
def derivative(self, z):
return (z > 0).astype(float)
シグモイド
シグモイド活性化関数の導関数は次の通り:
f′(zi)=f(zi)⋅(1−f(zi))class Sigmoid:
def __call__(self, x):
return 1 / (1 + np.exp(-z))
def derivative(self, z):
sig = self(z)
return sig * (1 - sig)
どちらの活性化関数も、演算はベクトル全体zおよびその導関数に適用される。NumPyは自動的に要素ごとに計算を行い、ベクトルの各要素が独立して処理される。
例えば、ベクトルzが3つの要素を含む場合、導関数は次のように計算される:
f′(z)=f′z1z2z3=f′(z1)f′(z2)f′(z3)backward() メソッド
backward() メソッドは、以下の式を用いて勾配を計算する役割。
a^{l-1} および zl は、それぞれ inputs クラスの outputs 属性と Layer 属性として保存。活性化関数 f は activation 属性として保存。
すべての必要な勾配が計算された後、重みとバイアスは、これ以上の計算に必要ないため更新可能:
Wlbl=Wl−α⋅dWl=bl−α⋅dblしたがって、learning_rate(α)はこのメソッドのもう一つのパラメータ。
def backward(self, da, learning_rate):
dz = ...
d_weights = ...
d_biases = ...
da_prev = ...
self.weights -= learning_rate * d_weights
self.biases -= learning_rate * d_biases
return da_prev
* 演算子は要素ごとの乗算を行い、np.dot() 関数は NumPy でドット積を実行します。.T 属性は配列を転置します。
フィードバックありがとうございます!
AIに質問する
AIに質問する
何でも質問するか、提案された質問の1つを試してチャットを始めてください