【问题标题】:Programmatically performing gradient calculation以编程方式执行梯度计算
【发布时间】:2018-04-01 06:23:06
【问题描述】:

令 y = Relu(Wx) 其中 W 是一个二维矩阵,表示对 x 的线性变换,一个向量。同样,设 m = Zy,其中 Z 是表示 y 上的线性变换的二维矩阵。如何以编程方式计算 Loss = sum(m^2) 相对于 W 的梯度,其中幂表示结果向量的元素幂,而 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) 的导数,除了这里我不知道 * 在这种情况下意味着什么才能使其工作。

有没有一种简单的方法可以在数学上计算这个梯度,而无需将其全部相乘并推导出梯度中的每个单独元素?我真的不熟悉矩阵微积分,所以如果有人也可以给出一些数学直觉,如果我的尝试完全错误,那将不胜感激。

【问题讨论】:

  • 所以您特别希望手动完成而不是使用自动微分库?
  • 我建议您按照完整的教程进行演示和作业,以便完全理解该主题。线性代数在深度学习中是强制性的。见Stanfor's tutorial and unsupervised feature learning
  • @mr_mo 如果您对学习如何进行这种矩阵演算的资源有任何想法,我将不胜感激。我学过线性代数,问题是我找不到好的资源来教我如何做矩阵微积分。
  • @KonstantinosKokos - 是的,我想了解这是如何在数学上完成的,尽管使用库会非常简单。
  • @Matt 可能是wikipedia

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


【解决方案1】:

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

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

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

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

编辑

  • 为了让最后一点更清楚,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))

【讨论】:

  • 谢谢 - 我在这里没有遵循几个想法。首先,如果它们是不同的维度矩阵,这里的“*”是什么意思?其次,如果 Y = Wx,我不明白你说的 dY/dW 是什么意思,然后变成 3D 矩阵 [dimY,dimY,dimX] - 你是如何计算的?如果不是太难,您能否使用我提供的变量来解决这个问题,以便我更好地了解如何计算结构?
  • 更改了原始答案。如果您仍然有问题,请随时询问。尝试将方程式写在纸上,并证明在代码块中使用 dot over tensordot 是合理的。
【解决方案2】:

我刚刚在 C++[1] 中从头开始实现了几个乘法器神经网络 (MLP),我​​想我知道你的痛点是什么。相信我,您甚至不需要任何第三方矩阵/张量/自动微分 (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,本文列出了你应该知道的关于深度学习矩阵推导的每一个细节。

[3] 上例中使用了一个样本作为输入(作为向量),您可以将 1 替换为任意批次号(成为矩阵),等式仍然成立。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-10-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多