反向传播 - 神经网络的核心
反向传播的目标是优化权重,以便神经网络可以学习如何正确地将任意输入映射到输出。
每一层都有自己的一组权重,必须调整这些权重,以便能够准确预测给定输入的正确输出。
反向传播的高级概述如下:
- 正向传递 - 输入转换为某些输出。在每一层,使用输入和权重之间的点积计算激活,然后将结果与偏差相加。最后,该值通过激活函数传递,以激活该层,该层将成为下一层的输入。
- 在最后一层中,将输出与对应于该输入的实际标签进行比较,并计算错误。通常,它是均方误差。
- 向后传递 - 将步骤 2 中计算的误差传播回内层,并调整所有层的权重以解决此错误。
1.权重初始化
权重初始化的简化示例如下所示:
layers = [784, 64, 10]
weights = np.array([(np.random.randn(y, x) * np.sqrt(2.0 / (x + y))) for x, y in zip(layers[:-1], layers[1:])])
biases = np.array([np.zeros((y, 1)) for y in layers[1:]])
-
隐藏层 1 具有尺寸[64,784]的重量和尺寸 64 的偏差。
-
输出层具有维度[10,64]的权重和维度的偏差
你可能想知道在上面的代码中初始化权重时发生了什么。这称为 Xavier 初始化,它比随机初始化权重矩阵更好一步。是的,初始化确实很重要。根据你的初始化,你可能能够在梯度下降期间找到更好的局部最小值(反向传播是梯度下降的美化版本)。
2.前往通行证
activation = x
hidden_activations = [np.reshape(x, (len(x), 1))]
z_list = []
for w, b in zip(self.weights, self.biases):
z = np.dot(w, np.reshape(activation, (len(activation), 1))) + b
z_list.append(z)
activation = relu(z)
hidden_activations.append(activation)
t = hidden_activations[-1]
hidden_activations[-1] = np.exp(t) / np.sum(np.exp(t))
该代码执行上述转换。hidden_activations[-1]
包含 softmax 概率 - 所有类的预测,其总和为 1.如果我们预测数字,则输出将是维度 10 的概率向量,其总和为 1。
3.后退通行证
weight_gradients = [np.zeros(w.shape) for w in self.weights]
bias_gradients = [np.zeros(b.shape) for b in self.biases]
delta = (hidden_activations[-1] - y) * (z_list[-1] > 0) # relu derivative
weight_gradients[-1] = np.dot(delta, hidden_activations[-2].T)
bias_gradients[-1] = delta
for l in range(2, self.num_layers):
z = z_list[-l]
delta = np.dot(self.weights[-l + 1].T, delta) * (z > 0) # relu derivative
weight_gradients[-l] = np.dot(delta, hidden_activations[-l - 1].T)
bias_gradients[-l] = delta
前两行初始化渐变。计算这些梯度,并将用于稍后更新权重和偏差。
接下来的 3 行通过从目标中减去预测来计算误差。然后将错误反馈传播到内层。
现在,仔细跟踪循环的工作。第 2 行和第 3 行将错误从 layer[i]
转换为 layer[i - 1]
。跟踪乘以矩阵的形状以理解。
4.重量/参数更新
for i in xrange(len(self.weights)):
self.weights[i] += -self.learning_rate * weight_gradients[i]
self.biases[i] += -self.learning_rate * bias_gradients[i]
self.learning_rate
指定网络学习的速率。你不希望它学得太快,因为它可能不会收敛。平稳下降有利于找到一个好的最小值。通常,0.01
和 0.1
之间的比率被认为是好的。