机器学习之(2)——反向传播算法如何工作+python代码实例

原文链接 https://blog.csdn.net/qq_38688564/article/details/79440381    在这里首先感谢作者 

  1. 简介 
    在上一章,我们看到了使用梯度下降算法来学习他们自身的权重和偏置。但是我们并没有讨论如何计算代价函数的梯度。本章我们来解释一下计算这些梯度的快速算法,也就是反向传播 
    (backpropagation)。 
    反向传播的核心是一个对代价函数C关于任何权重w(或者偏置b)的偏导数∂C/∂w的表达式。这个表达式告诉我们在改变权重和偏置时,代价函数变化的快慢。尽管表达式会有点复杂,不过⾥⾯也包含⼀种美感,就是每个元素其实是拥有⼀种⾃然的直觉上的解释。所以反向传播不仅仅是⼀种学习的快速算法。实际上它让我们细致领悟如何通过改变权重和偏置来改变整个⽹络的⾏为。因此,这也是学习反向传播细节的重要价值所在。
  2. 热身:神经网络中使用矩阵快速计算输出的方法 
    在讨论反向传播之前,我们先熟悉基于矩阵的算法来计算网络的输出。(ps这里上下标不好表示,所以就截图显示了,- ^ -) 
    这里写图片描述 
    如下图实例:这样的表⽰粗看⽐较奇怪,需要花⼀点时间消化。但是,后⾯你会发现这样的表⽰会⽐较⽅便也很⾃然。奇怪的⼀点其实是下标 j 和 k 的顺序。你可能觉得反过来更加合理。但我接下来会告诉你为什么要这样做。 
    这里写图片描述

这里写图片描述 
其中求和是在(l-1)层的所有k个神经元上进行的。为了用矩阵的形式重写这个表达式,我们对每一层l都定义一个权重矩阵这里写图片描述 权重矩阵w^l的元素正是连接到l层神经元的权重,更确切的说,在第j行第k列的元素是这里写图片描述类似的,对每⼀层 L,定义⼀个偏置向量,这里写图片描述。 
你已经猜到这些如何⼯作了 —— 偏置向量的每个元素其实就是前⾯给出的 这里写图片描述,每个元素对应于lth 层的每个神经元。最后,我们定义激活向量 这里写图片描述,其元素是那些激活值 这里写图片描述 
最后我们可以使用向量形式简洁而美妙的写出上面的(23)表达式: 
这里写图片描述 
这里写图片描述 
3. 关于代价函数的两个假设 
为了应用反向传播,我们需要对代价函数做两个前提假设。 
3. 1代价函数可以被写成一个在训练样本x上的代价函数Cx的均值这里写图片描述这是关于⼆次代价函数的例⼦,其中对每个独⽴的训练样本其代价是这里写图片描述 
需要这个假设的原因是反向传播实际是对一个独立的训练样本计算了 ∂Cx/∂w 和 ∂Cx/∂b。然后我们通过在所有训练样本上进⾏平均化获得 ∂C/∂w 和 ∂C/∂b。实际上,有了这个假设,我们会认为训练样本 x 已经被固定住了,丢掉了其下标,将代价函数 Cx 看做 C。最终我们会把下标加上,现在为了简化表⽰其实没有这个必要。 
3. 2第二个假设就是代价可以写成神经网络输出的函数: 
这里写图片描述 
例如,⼆次代价函数满⾜这个要求,因为对于⼀个单独的训练样本 x 其⼆次代价函数可以写作:这里写图片描述 
这是输出的激活值的函数。当然,这个代价函数同样还依赖于⽬标输出 y,你可能奇怪为什么我们不把代价也看作⼀个 y 的函数。记住,输⼊的训练样本 x 是固定的,所以输出 y 同样是⼀个固定的参数。尤其是它并不是可以随意通过改变权重和偏置来改变的,也就是说,这不是神经⽹络学习的对象。所以,将 C 看成仅有输出激活值这里写图片描述 的函数才是合理的,⽽ y 仅仅是帮助定义函数的参数⽽已。 
4. Hadamard乘积,这里写图片描述 
反向传播算法基于常规的线性代数运算 —— 诸如向量加法,向量矩阵乘法等。但是有⼀个运算不⼤常⻅。特别地,假设 s 和 t 是两个同样维度的向量。那么我们使⽤ s ⊙ t 来表⽰按元素的乘积。所以 s ⊙ t 的元素就是 (s ⊙ t)j = sj * tj。给个例⼦, 
这里写图片描述 
这种类型的按元素乘法有时候被称为 Hadamard 乘积,或者 Schur 乘积。我们这⾥取前者。好的矩阵库通常会提供Hadamard 乘积的快速实现,在实现反向传播的时候⽤起来很⽅便。 
5. 反向传播的四个基本方程 
5. 1反向传播其实是对权重和偏置变化影响代价函数过程的理解。最终极的含义其实就是计算偏导数这里写图片描述 
但是为了计算这些值,我们需要引入一个中间量,这里写图片描述,这个我们称为在 l层第 j 个神经元上的误差。 
反向传播将给出计算误差的流程,然后关联到计算偏导数上。 
为了理解误差是如何定义的,假设在神经⽹络上有⼀个调⽪鬼: 
这里写图片描述 
这里写图片描述 
解决方案:反向传播基于四个基本方程。这些方程给了我们计算误差和代价函数的方法。我列出这四个⽅程。但是需要注意:你不需要⼀下⼦能够同时理解这些公式。希望越⼤失望越⼤。实际上,反向传播⽅程内容很多,完全理解这些需要花费充分的时间和耐⼼,需要⼀步⼀步地深⼊理解。⽽好的消息是,这样的付出回报巨⼤。所以本节对这些内容的讨论仅仅是⼀个帮助你正确掌握这些⽅程的起步。 
- 1)输出层误差的方程,这里写图片描述每个元素定义如下: 
这里写图片描述 
这里写图片描述 
⽅程 (BP1) 对 δL 来说是个按分量构成的表达式。这是⼀个⾮常好的表达式,但不是我们期望的⽤矩阵表⽰的形式。但是,以矩阵形式重写⽅程其实很简单,这里写图片描述这⾥ ∇aC 被定义成⼀个向量,其元素是偏导数这里写图片描述 。你可以将 ∇aC 看成是 C 关于输出激活值的改变速度。⽅程 (BP1) 和⽅程 (BP1a) 的等价也是显⽽易⻅的,所以现在开始,我们会⽤ (BP1) 表⽰这两个⽅程。如你所见,这个⽅程中的每个项都有⼀个很好的向量形式,所以也可以很⽅便地使⽤像 
Numpy 这样的矩阵库进⾏计算了。 
- 2)使用下一层的误差δ^(l+1 )来表示当前层的误差δ^l:这里写图片描述 
其中 [w^(l+1)]^T 是 (l + 1)层权重矩阵 w^(l+1) 的转置。这个公式看上去有些复杂,但每⼀个元素有很好的解释。假设我们知道 l + 1 层的误差 δ^(l+1)。当我们应⽤转置的权重矩阵 [w^(l+1)]^T ,我们可以凭直觉地把它看作是在沿着⽹络反向移动误差,给了我们度量在 l 层输出的误差⽅法。然后,我们进⾏ Hadamard 乘积运算 ⊙σ’(z^l)。这会让误差通过 l 层的激活函数反向传递回来并给 
出在第 l 层的带权输⼊的误差 δ。通过组合 (BP1) 和 (BP2),我们可以计算任何层的误差 δ^l。⾸先使⽤ (BP1) 计算 δL,然后应⽤⽅程 (BP2) 来计算 δ^(L−1),然后再次⽤⽅程 (BP2) 来计算 δ^(L−2),如此⼀步⼀步地反向传播完整个⽹络。 
- 3)代价函数关于网络中任意偏置的改变率:就是这里写图片描述 
这里写图片描述 
- 4)代价函数关于任何一个权重的改变率:特别的, 
这里写图片描述 
这里写图片描述 
其中 a in 是输⼊给权重 w 的神经元的激活值,δout 是输出⾃权重 w 的神经元的误差。放⼤看看权重 w,还有两个由这个权重相连的神经元,我们给出⼀幅图如下:这里写图片描述 
⽅程 (32) 的⼀个好的结果就是当激活值 ain 很⼩,ain ≈ 0,梯度 ∂C/∂w 也会趋向很⼩。这样,我们就说权重缓慢学习,表⽰在梯度下降的时候,这个权重不会改变太多。换⾔之,(BP4)的⼀个结果就是来⾃低激活值神经元的权重学习会⾮常缓慢。 
这四个公式 (BP1)–(BP4) 同样还有其它可以理解的⽅⾯。让我们从输出层开始,先看看 (BP1)中的项这里写图片描述。回忆⼀下上⼀章中 S 型函数的图形,当 这里写图片描述近似为 0 或者 1 的时候 σ 函数变得⾮常平。这时 这里写图片描述≈ 0。所以如果输出神经元处于或者低激活(≈ 0)或者⾼激活值(≈ 1)时,最终层的权重学习缓慢。这样的情形,我们常常称输出神经元已经饱和了,并且,权重学习也会终⽌(或者学习⾮常缓慢)。类似的结果对于输出神经元的偏置也是成⽴的。 
针对前⾯的层,我们也有类似的观点。特别地,注意在 (BP2) 中的项 σ′(z^l)。这表⽰如果神经元已经接近饱和,这里写图片描述 很可能变⼩。这就导致任何输⼊进⼀个饱和的神经元的权重学习缓慢。 
四个基本⽅程也其实对任何的激活函数都是成⽴的(证明中也可以看到,其实推断本⾝不依赖于任何具体的代价函数)所以,我们可以使⽤这些⽅程来设计有特定学习属性的激活函数。我们这⾥给个例⼦,假设我们准备选择⼀个(⾮ S 型)激活函数 σ 使得 σ′ 总是正数,并且不会趋近 0。这会防⽌在原始的 S 型神经元饱和时学习速度下降的情况出现。在本书的后⾯,我们会⻅到这种类型的对激活函数的改变。时时回顾这四个⽅程 (BP1)–(BP4) 可以帮助解释为何需要有这些尝试,以及尝试带来的影响。 
6. 反向传播算法 
反向传播方程给出了一种计算代价函数梯度的方法。让我显示的用算法描述出来: 
6. 1.输入x:为输入层设置对应的激活值a^l; 
6. 2前项传播:对每一个l=2,3,..L计算对应的z^l=w^l*a^(l-1)+b^l和a^l=σ(z^l) 
6. 3输出层误差δ^L: 计算向量 δ^L = ∇aC ⊙ σ′(z^L) 
6. 4反向误差传播:对每个l=L-1,L-2,…2,计算这里写图片描述 
6. 5输出:代价函数的梯度由这里写图片描述这里写图片描述 
检视这个算法,你可以看到为何它被称作反向传播。我们从最后⼀层开始向后计算误差向量δ^l。这看起来有点奇怪,为何要从后⾯开始。但是如果你认真思考反向传播的证明,这种反向移动其实是代价函数是⽹络输出的函数的结果。为了理解代价随前⾯层的权重和偏置变化的规律,我们需要重复作⽤链式法则,反向地获得需要的表达式. 
7. 代码

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
    delta = self.cost_derivative(activations[-1], y) * \
    sigmoid_prime(zs[-1])
    nabla_b[-1] = delta
    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 xrange(2, self.num_layers):
    z = zs[-l]
    sp = sigmoid_prime(z)
    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)
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))

原文链接 https://blog.csdn.net/qq_38688564/article/details/79440381

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值