[神经网络这次真的搞懂了!] (8) 使用神经网络识别手写数字
英文原文:http://neuralnetworksanddeeplearning.com/
对原文的表达有部分改动
反向传播方程为我们提供了一种计算代价函数梯度的方法。让我们以算法的形式总结一下:
- 输入 :设置输入层 。
- 前馈:对于每个 计算 和 。
- 输出误差 :计算向量
- 反向传播误差:对于每个 计算 。
- 输出:代价函数的梯度由 和 给出。
了解上述的算法,您会明白为什么它被称为反向传播。我们从最后一层开始往回计算误差向量 。逐步了解了代价如何随早期权重和偏差而变化,通过往前追溯以获得可用的表达式。
正如我上面所描述的,反向传播算法计算单个训练示例的代价函数的梯度,。在实践中,通常将反向传播与随机梯度下降等学习算法相结合,在这种算法中,我们为许多训练示例计算梯度。特别是,给定 mini-batch 训练示例,以下算法应用基于该 mini-batch 的梯度下降学习步骤:
- 输入训练样本中的一组
- 对于每个训练样例 :设置对应的输入激活 ,执行以下步骤:
- 前馈:对于每个 计算 和 。
- 输出误差:计算向量。
- 反向传播误差:对于每个 计算 。
- 梯度下降:对于每个 根据规则更新权重 ,以及根据规则 更新偏差 的。
反向传播的代码
在抽象地理解了反向传播之后,我们现在可以理解上一章中用于实现反向传播的代码。回忆那一章,代码包含在 Network 类的 update_mini_batch 和 backprop 方法中。这些方法的代码是上述算法的直接翻译。特别是,update_mini_batch 方法通过计算当前 mini_batch 训练示例的梯度来更新网络的权重和偏差:
class Network(object):
def update_mini_batch(self, mini_batch, eta):
"""Update the network's weights and biases by applying
gradient descent using backpropagation to a single mini batch.
The ``mini_batch`` is a list of tuples ``(x, y)``, and ``eta``
is the learning rate."""
nabla_b = [np.zeros(b.shape) for b in self.biases]
nabla_w = [np.zeros(w.shape) for w in self.weights]
for x, y in mini_batch:
delta_nabla_b, delta_nabla_w = self.backprop(x, y)
nabla_b = [nb+dnb for nb, dnb in zip(nabla_b, delta_nabla_b)]
nabla_w = [nw+dnw for nw, dnw in zip(nabla_w, delta_nabla_w)]
self.weights = [w-(eta/len(mini_batch))*nw
for w, nw in zip(self.weights, nabla_w)]
self.biases = [b-(eta/len(mini_batch))*nb
for b, nb in zip(self.biases, nabla_b)]
反向传播的大部分工作由 delta_nabla_b, delta_nabla_w = self.backprop(x, y)
完成,它使用反向传播方法计算偏导数 和。反向传播方法紧密遵循上一节中的算法。有一个小的变化 - 我们使用稍微不同的方法来索引层。进行此更改是为了利用 Python 的一个特性,即使用列表负数索引从列表的末尾往前计数,例如,l[-3]
是列表 l
中的倒数第三个条目。反向传播的代码如下,以及一些辅助函数,用于计算 函数、导数 和代价函数的导数。有了这些内容,您应该能够以一种自包含的方式理解代码。
class Network(object):
def backprop(self, x, y):
"""Return a tuple ``(nabla_b, nabla_w)`` representing the
gradient for the cost function C_x. ``nabla_b`` and
``nabla_w`` are layer-by-layer lists of numpy arrays, similar
to ``self.biases`` and ``self.weights``."""
nabla_b = [np.zeros(b.shape) for b in self.biases]
nabla_w = [np.zeros(w.shape) for w in self.weights]
# feedforward
activation = x
activations = [x] # list to store all the activations, layer by layer
zs = [] # list to store all the z vectors, layer by layer
for b, w in zip(self.biases, self.weights):
z = np.dot(w, activation)+b
zs.append(z)
activation = sigmoid(z)
activations.append(activation)
# backward pass
# BP1
delta = self.cost_derivative(activations[-1], y) * \
sigmoid_prime(zs[-1])
# BP3
nabla_b[-1] = delta
# BP4
nabla_w[-1] = np.dot(delta, activations[-2].transpose())
# Note that the variable l in the loop below is used a little
# differently to the notation in Chapter 2 of the book. Here,
# l = 1 means the last layer of neurons, l = 2 is the
# second-last layer, and so on. It's a renumbering of the
# scheme in the book, used here to take advantage of the fact
# that Python can use negative indices in lists.
for l in range(2, self.num_layers):
z = zs[-l]
sp = sigmoid_prime(z)
# BP2
delta = np.dot(self.weights[-l+1].transpose(), delta) * sp
nabla_b[-l] = delta
nabla_w[-l] = np.dot(delta, activations[-l-1].transpose())
return (nabla_b, nabla_w)
def cost_derivative(self, output_activations, y):
"""Return the vector of partial derivatives \partial C_x /
\partial a for the output activations."""
return (output_activations-y)
#### Miscellaneous functions
def sigmoid(z):
"""The sigmoid function."""
return 1.0/(1.0+np.exp(-z))
def sigmoid_prime(z):
"""Derivative of the sigmoid function."""
return sigmoid(z)*(1-sigmoid(z))
-
Network.cost_derivative
是代价函数的代数形式,因为代价函数为 ,其导数形式为 -
sigmoid
是激活函数的代码实现,可以支持向量参数 -
sigmoid_prime
是 sigmoid 函数的导数,