反向传播-图解

梯度和反向传播

我们现在知道如何使用梯度更新我们的权重和偏置,我们还需要知道如何计算所有节点的梯度。对于每个节点,我们需要根据梯度更改代价的值(考虑到该节点的值)。这样,我们做出的梯度下降更新最终会实现最低代价。

gif

我们可以看到这些节点的每个值都向前流动,最终生成代价 C。例如,第二个线性节点l_2的值进入代价节点并确定该节点的值。更改 l_2将导致 C相应地出现更改。我们可以将更改之间的这种关系写成梯度。

image

这就是梯度的含义,是一种斜率,表示给出 l_2 中的更改,你会对代价 C 进行多大幅度的更改。所以,节点的代价梯度更大的话,代价就会改变更大。这样,我们可以对每个节点的代价带来的影响进行分配。节点的梯度越大,就会对最终的代价影响越大。节点的影响越大,我们就会在梯度下降步骤中更新幅度越大。

如果我们想更新某个梯度下降的权重,我们需要知道这些权重对应的代价的梯度。看看我们可以如何使用此框架算出第二层权重 w_2的梯度。我们想要计算 C相对于 w_2 的梯度:

image

我们可以从图表中看出,w_2与 l_2 相关联,所以更改 w_2将导致 l_2 出现更改,从而导致C出现更改。我们可以通过在网络中将代价梯度发送回去,将影响分配给 w_2。首先,你知道 l_2对 C 的影响有多大,然后知道 w_2 对 l_2 的影响有多大。将这些梯度相乘可以得出归为 w_2 的总影响。

image

将这些梯度相乘只是链式法则的一种应用:

image

image

现在,我们再深入一步,计算 w_1 的梯度。和之前用到的方法一样,在图表上一直往回计算。 image image

现在可以看到清晰的规律了。要算出梯度,只需将它前面所有节点(从代价那开始)的梯度相乘。这就是反向传播概念。梯度在网络上向后传播,并使用梯度下降来更新权重和偏置。如果某个节点具有多个向外的节点,则直接将每个节点的梯度相加即可。

每个节点会将代价梯度传递给传入节点,每个节点将从其传出节点那获得代价梯度。然后,对于每个节点,我们需要算出一个梯度,即代价梯度乘以该节点的梯度除以其输出。下面为 Linear 节点写出了这一流程。


# Initialize a partial for each of the inbound_nodes.
self.gradients = {n: np.zeros_like(n.value) for n in self.inbound_nodes}
# Cycle through the outputs. The gradient will change depending
# on each output, so the gradients are summed over all outputs.
for n in self.outbound_nodes:
    # Get the partial of the cost with respect to this node.
    grad_cost = n.gradients[self]
    # Set the partial of the loss with respect to this node's inputs.
    self.gradients[self.inbound_nodes[0]] += np.dot(grad_cost, self.inbound_nodes[1].value.T)
    # Set the partial of the loss with respect to this node's weights.
    self.gradients[self.inbound_nodes[1]] += np.dot(self.inbound_nodes[0].value.T, grad_cost)
    # Set the partial of the loss with respect to this node's bias.
    self.gradients[self.inbound_nodes[2]] += np.sum(grad_cost, axis=0, keepdims=False)