实现通用的反向传播算法

3
我正在尝试使用任意激活函数实现全连接层的反向传播方法。我理解算法背后的一般思想和数学,但是对于向量化形式有困难...
我需要帮助理解元素的预期维度。
已知大小:
- 输入 - self.X 的大小为 (N,128) - 权重 - self.W 的大小为 (128,10) - 偏置 - self.b 的大小为 (128,10) - 输出 - self.y 的大小为 (N,10) - 线性输出(激活之前)- self.z 的大小为 (N,10)
未知大小:
当 N=1 时(示例数量)
- dy - 下一层的梯度 - 应该是什么大小? - dz - 激活函数的导数 - 应该是什么大小? - self.d - 当前层的梯度 - 应该是什么大小?
以下是我的代码:
def backward(self, dy):
    if self.activator == 'relu':
        dz = np.zeros((self.z.shape[0], self.z.shape[1]))
        dz[self.z>0] = 1
    elif self.activator == 'sigmoid':
        dz = self.z * (1 - self.z)
    elif self.activator == 'soft-max':
        s = self.z.reshape(-1, 1)
        dz = np.diagflat(s) - np.dot(s, s.T)
    elif self.activator == 'none':
        dz = 1

    self.d = np.dot((dz * dy), self.W.T) # the error of the layer
    self.W_grad = np.dot(self.X.T, dy) # The weight gradient of the layer
    self.b_grad = np.sum(dy, axis=0).reshape(1, -1) # The bias gradient of the layer
2个回答

1
我认为,您的代码有些混淆:您写道self.z是激活之前的线性输出,但出于某种原因,您使用它来计算称为dz的激活导数。应该使用激活值。然后,假设您计算出了该值(我将其称为prime以避免与其他dz混淆),请尝试以下操作:
dz = dy * prime
dW = np.dot(dz, self.z.T)
db = np.sum(dz, axis=1, keepdims=True)
d = np.dot(self.W.T, dz)

你对我的代码错误的指出是正确的,但你写的第二行不应该是:dW = np.dot(dz, self.X.T)吗?线性输出的导数是输入。 - Mark.F
为什么是错的?他所称的dz实际上是f'(z)(您所称之为prime). - Peter
@Mark.F 关于 dW 中的顺序:它完全取决于前向传递。我通常会像这样将其乘到左边:self.W * ...。如果你将其乘到右边,你可以翻转它。 - Maxim

1

有几个错误:

  • self.b 的大小应该为 self.b is size (10, ) 而不是 (128, 10) (因为偏置是每个单元的,而不是每个单元对)。
  • self.W_grad 应该是 np.dot(self.X.T, (dz * dy)),而不是 np.dot(self.X.T, dy)。同样地,self.b_grad 应该是 np.sum(dz * dy, axis=0)

至于剩下的部分

dy := dL/dy 应该是 (N, 10),因为它包含了损失相对于 y 中每个元素的梯度。

dz := df(z)/d(z) 对于逐元素激活函数应该是 (N, 10),因为 dz[i] 包含了 df(z[i])/dz[i]

self.d := dL/dX 应该是 (N, 128),因为它包含了损失相对于 X 中每个元素的梯度。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接