Affineレイヤ

Affineレイヤを実装。

class Affine:
    def __init__(self, W, b):
        self.W = W
        self.b = b
        self.x = None

    def forward(self, x):
        self.x = x
        return np.dot(self.x, self.W) + self.b

    def backward(self, dy):
        dx = np.dot(dy, self.W.T)
        self.dW = np.dot(self.x.T, dy)
        self.db = np.sum(dy, axis=0)
        return dx

Affine変換とかあるじゃないですか。Affineで変な響きだし、Affineてなによ、と思ったら、一次関数のようです。

Ax+b

線形変換かよ。
ただ、ニューラルネットワークでは良く使うようです。
Wはウエイト(傾き)、xは入力、bはバイアス(切片)に対応します。
全て多次元配列ですので要素数の一致が大事ですね。

クラスAffineの中に、forward関数を定義します。
xを引き数にして、xとWの内積にbを足しますので、xの列数、Wの列数、xとWの内積がbの行列数と一致している必要があります。

同様にbackward関数は誤差逆伝播法で使うので定義します。

 dL/dx = dL/dy dot W.T
 dL/dW = X.T dot dL/dy
 dL/db = sum(dL/dy)

ですね。

早速試してみます。

x = np.array([[1, 2]])
W = np.array([[4, 5, 6],
              [7, 8, 9]])
b = np.array([[10, 11, 12]])

affine = Affine(W, b)
print("forward=", affine.forward(x))

dy = np.array([[1, 2, 3]])
dx = affine.backward(dy)
print("dx=", dx)

forward= [[28 32 36]]
dx= [[32 50]]

forwardはx dot W + bなので、
(1×4+2×7+10, 1×5+2×8+11, 1×6+2×9+12) = (28, 32, 36)
でOK。

backwardはdyとして(1, 2, 3)が返ってきた場合、
(1×4+2×5+3×6, 1×7+2×8+3×9) = (32, 50)
なのでOKですかね。