以编程方式执行梯度计算

时间:2018-04-01 06:23:07

标签: machine-learning neural-network linear-algebra backpropagation calculus

令y = Relu(Wx)其中W是表示x,矢量的线性变换的2d矩阵。同样,令m = Zy,其中Z是表示y上的线性变换的2d矩阵。如何以编程方式计算相对于W的Loss = sum(m ^ 2)的梯度,其中幂表示得到的向量的元素幂,sum表示将所有元素加在一起?

我可以通过假设,将它全部乘以,然后逐个元素取导数来构造梯度,从数学上缓慢地解决这个问题,但我无法找出编写程序的有效方法一旦神经网络层变为> 1。

说,对于一个图层(m = Zy,采用渐变wrt Z)我可以说

Loss = sum(m^2)
dLoss/dZ  = 2m * y

其中*是向量的外积,我猜这有点像普通的微积分,它有效。现在为2层+激活(渐变wrt W),如果我尝试这样做"正常"微积分并应用我得到的链规则:

dLoss/dW = 2m * Z * dRelu * x

其中dRelu是Relu(Wx)的衍生物,除了在这里我不知道在这种情况下*意味着什么才能使它工作。

有没有一种简单的方法可以数学方式计算这个梯度,而基本上不需要将它全部乘以并导出渐变中的每个单独元素?我真的不熟悉矩阵演算,所以如果有人也可以给出一些数学直觉,如果我的尝试完全错误,那将是值得赞赏的。

2 个答案:

答案 0 :(得分:1)

为了方便起见,我们暂时忽略ReLU。你有一个输入空间X(某个大小[dimX])映射到一个中间空间Y(某种大小[dimY])映射到输出空间m(某种大小[dimM])你有,然后,W:X →Y形状矩阵[dimY,dimX]和Z:Y→形状矩阵[dimM,dimY]。最后,您的损失只是将M空间映射到标量值的函数。

让我们向后走。正如你所说的那样,你想要计算w.r.t W损失的导数,为此你需要一直应用链规则。然后你有:

dL / dW = dL / dm * dm / dY * dY / dW

  • dL / dm的形状[dimm](具有跨dimm维度的导数的标量函数)
  • dm / dY的形状为[dimm,dimY](m维函数,具有衍生的dimY维度)
  • dY / dW的形状[dimY,dimW] = [dimY,dimY,dimX](y维函数,在[dimY,dimX]维度上有衍生物)

修改

  • 为了使最后一位更清晰,Y由dimY不同的值组成,因此Y可以被视为dimY组成函数。我们需要在每个迷你函数上应用梯度算子,所有这些都与W定义的基矢量有关。更具体地说,如果W = [[w11,w12],[w21,w22],[w31,w32]并且x = [x1,x2],则Y = [y1,y2,y3] = [w11x1 + w12x2,w21x1 + w22x2,w31x1 + w32x2]。然后W定义了一个6d空间(3x2),我们需要区分它。我们有dY / dW = [dy1 / dW,dy2 / dW,dy3 / dW],还有dy1 / dW = [[dy1 / dw11,dy1 / dw12],[dy1 / dw21,dy1 / dw22],[dy1 / dw31,dy1 / dw32]] = [[x1,x2],[0,0],[0,0]],3x2矩阵。所以dY / dW是[3,3,2]张量。
  • 乘法部分;这里的操作是张量收缩(基本上是高维空间中的矩阵乘法)。实际上,如果你有一个高阶张量A [[a1,a2,a3 ...],β](即a + 1维,最后一个是大小β)和张量B [β,[b1] ,b2 ...]](即b + 1维,其中第一个是β),它们的张量收缩是矩阵C [[a1,a2 ...],[b1,b2 ...]](即a + b维度,β维度缩小),其中C是通过在共享维度β上的元素方式求和而获得的(参考https://docs.scipy.org/doc/numpy/reference/generated/numpy.tensordot.html#numpy.tensordot)。

然后得到的张量收缩是形状[dimY,dimX]的矩阵,可用于更新W权重。我们之前忽略的ReLU很容易被抛入混合中,因为ReLU:1→1是在Y上逐个元素应用的标量函数。

总结一下,您的代码将是:

W_gradient = 2m * np.dot(Z, x) * np.e**x/(1+np.e**x))

答案 1 :(得分:0)

我刚从C ++开始实现了几个乘法神经网络(MLP)[1],我想我知道你的痛苦是什么。相信我,你甚至不需要任何第三方矩阵/张量/自动微分(AD)库来进行矩阵乘法或梯度计算。你应该注意三件事:

  • 方程式中有两种乘法:矩阵乘法和逐元乘法,如果将它们全部表示为单个*,则会陷入困境。
  • 使用具体示例,尤其是具体数字作为数据/矩阵/向量的维度,以建立直觉。
  • 正确编程的最强大工具是dimension compatibility,不要忘记检查尺寸。

假设您想进行二元分类,神经网络为input -> h1 -> sigmoid -> h2 -> sigmoid -> loss,其中输入层有1个样本,每个样本有2个特征,h1有7个神经元,h2有2个神经元。然后:

前进:

Z1(1, 7) = X(1, 2) * W1(2, 7)
A1(1, 7) = sigmoid(Z1(1, 7))
Z2(1, 2) = A1(1, 7) * W2(7, 2)
A2(1, 2) = sigmoid(Z2(1, 2))
Loss = 1/2(A2 - label)^2

向后传递:

dA2(1, 2) = dL/dA2 = A2 - label
dZ2(1, 2) = dL/dZ2 = dA2 * dsigmoid(A2_i) -- element wise
dW2(7, 2) = A1(1, 7).T * dZ2(1, 2)        -- matrix multiplication

注意最后一个等式,W2的渐变尺寸应与W2匹配,即(7, 2)。获得(7, 2)矩阵的唯一方法是转置输入A1并将A1dZ2相乘,即维度兼容性[2]。

向后传球继续:

dA1(1, 7) = dZ2(1, 2) * A1(2, 7)        -- matrix multiplication
dZ1(1, 7) = dA1(1, 7) * dsigmoid(A1_i)  -- element wise
dW1(2, 7) = X.T(2, 1) * dZ1(1, 7)       -- matrix multiplication

[1]代码为here,你可以看到隐藏的层实现,朴素矩阵实现和那里列出的引用。

[2]我省略了矩阵推导部分,实际上很简单但很难输出方程式。我强烈建议你阅读this paper,本文列出了你应该知道的关于DL中矩阵推导应该知道的每一个细节。

[3]在上面的例子中使用了一个作为输入的样本(作为向量),你可以用任何批号替换1(变成矩阵),并且方程仍然成立。