バックプロパゲーション実装
メニューを表示するにはスワイプしてください
一般的なアプローチ
順伝播では、各層 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つを試してチャットを始めてください