深度学习|误差逆传播:梯度速解

文章目录

    • 引言
    • 链式法则
    • 误差逆传播
      • 加法的逆传播
      • 乘法的逆传播
      • 逆传播求梯度
    • SoftmaxWithLoss 层
      • 正向传播
      • 逆传播
      • 代码实现参考
    • 结语

引言

我们知道训练神经网络模型的核心是以损失函数为基准来调整优化网络参数,使得网络的输出尽可能接近真实标签。在神经网络中,优化网络参数需要计算每个权重参数的梯度,不同的网络结构,计算梯度的方式和复杂度往往大不相同,有没有一种算法,即可以有效囊括所有类型的网络结构的梯度计算,又足以保证梯度计算的高效性?答案就是我们今天要讲的误差逆传播算法。

在这里插入图片描述

链式法则

要理解误差逆传播算法,需要先了解微分中链式法则的概念。链式法则是微分中的基本法则,可用于求解复合函数的导数。

如果某个函数由复合函数表示,则该复合函数的导数可以用构成该复合函数的各个函数的导数的乘积表示。

以式 1 所示的复合函数为例:

z = t 2 t = x + y (1) z = t^2 \\ t = x + y \tag{1} z=t2t=x+y(1)

通过链式法则求解 ∂ z ∂ x \frac{\partial{z}}{\partial{x}} xz

∂ z ∂ x = ∂ z ∂ t ∂ t ∂ x = 2 t × 1 = 2 ( x + y ) \frac{\partial{z}}{\partial{x}} = \frac{\partial{z}}{\partial{t}} \frac{\partial{t}}{\partial{x}} = 2t \times 1 = 2(x + y) xz=tzxt=2t×1=2(x+y)

可见一个复杂函数的求导问题可以分解为组成该复杂函数的局部函数的求导问题,我们完全可以将复杂函数的导数等价的表示为其所有局部函数的导数的乘积。

在神经网络中,误差逆传播算法就是利用链式法则来计算网络中每个参数的梯度。

误差逆传播

在前文「深度学习|模型训练:手写 SimpleNet」中,我们演示了使用数值微分方式求梯度的过程,数值微分的方式求梯度简单、易于理解与实现,但它的问题是计算效率很低。在 SimpleNet 的示例中,我们使用数值微分法训练所需时间长达 27.7 小时,几乎是不可用的状态。

那么有没有更高效的替代方式呢?终于轮到神经网络的主角算法误差逆传播出场了!

参考上文求复合函数(式 1)关于 x 的导数 ∂ z ∂ x \frac{\partial{z}}{\partial{x}} xz 的求解过程,给定 x 与 y,按照函数式求解 z 的正向计算的过程,就好比神经网络的前向传播Forward Propagation),而沿着函数正向计算的链路,从最末端逆向计算每个局部函数的导数,最终相乘从而得到该复杂函数的导数,就好比神经网络的逆传播Backward Propagation)。

前向传播(Forward Propagation):将输入数据通过网络进行运算,得到网络的输出、输出与目标值之间的误差
逆传播(Backward Propagation):从输出层开始,将误差逆传播到隐藏层,直到输入层。逆传播过程可以计算每个权重的梯度,即误差相对于每个权重的偏导数。

误差逆传播error BackPropagation,简称 BP)就是基于数学推导的解析性(相对于数值微分的数值性)梯度计算方法(符号微分Symbolic Differentiation),按照数学中求导的链式法则,局部导数会按正向传播的反方向传递。

以求解 ∂ z ∂ x \frac{\partial{z}}{\partial{x}} xz 为例,我们可以用如下图 1 中红色箭头所指过程表示该求导过程:

在这里插入图片描述

图 1 所示从左到右是复合函数的正向传播过程,表示的是 t = x + y t = x + y t=x+y z = t 2 z = t^2 z=t2 的正向计算过程。

从右往左是复合函数的逆传播过程,通过逆传播求函数关于 x 的导数 ∂ z ∂ x \frac{\partial{z}}{\partial{x}} xz,只需要沿着正向计算的链路逆向计算每个局部函数的导数,例如从输出 zz 本身的导数是 ∂ z ∂ z \frac{\partial{z}}{\partial{z}} zz,从 zt 的导数是 ∂ z ∂ t \frac{\partial{z}}{\partial{t}} tz,从 tx 的导数是 ∂ t ∂ x \frac{\partial{t}}{\partial{x}} xt,最终将每个环节的导数相乘即是该复合函数的导数 ∂ z ∂ z ∂ z ∂ t ∂ t ∂ x \frac{\partial{z}}{\partial{z}} \frac{\partial{z}}{\partial{t}} \frac{\partial{t}}{\partial{x}} zztzxt(其中 ∂ z ∂ z \frac{\partial{z}}{\partial{z}} zz 可忽略)。这就是 BP 算法的基本思想。

不难发现,神经网络中的前向传播都是由一些简单的加法、乘法等常用的运算复合而成,而神经网络的逆传播就是求解网络整个“复合函数”关于网络各层中权重参数梯度

我们在了解了 BP 算法的基本思路后,不难得出这些梯度的求解方式:沿着网络的正向运算过程,反向从输出层开始,往前计算每层运算的局部梯度,然后将求解目标参数梯度的完整链路上的所有局部梯度相乘,得到的就是目标参数的梯度。

接下来我们可以找到在逆传播过程中,使用 BP 算法求解加法、乘法等常用运算的梯度的规律。应用这些规律,我们可以在神经网络的逆传播运算过程中高效地计算梯度。

加法的逆传播

z = x + y z = x + y z=x+y 为例,其梯度 ( ∂ z ∂ x \frac{\partial{z}}{\partial{x}} xz ∂ z ∂ y \frac{\partial{z}}{\partial{y}} yz) 永远为 (1, 1)。

因此加法运算在逆传播时,总是将下游梯度乘以 1,即原封不动传递给上游。

我们可以使用 AddLayer 类实现加法运算的前向传播与逆传播:

class AddLayer:"""加法运算的前向传播与逆传播"""def __init__(self):self.x = Noneself.y = Nonedef forward(self, x, y):"""前向传播Args:x: 输入 xy: 输入 yReturns:out: 输出"""out = x + yreturn outdef backward(self, dout):"""逆传播Args:dout: 上游梯度Returns:dx: x 的梯度dy: y 的梯度"""dx = dout * 1dy = dout * 1return dx, dy

这里采用了标准层封装的方式来实现加法运算,将加法运算封装成了一个可以被任意结构的神经网络直接复用的小组件,其他如乘法运算激活函数损失函数等我们都将采用这样的实现方式。采用这样的封装方式,我们就可以在组装我们想要的网络时随意选择我们想要的组件(基本运算单元)。在实际的生产级机器学习框架(如 Scikit-learn、TensorFlow、PyTorch 等)中,这些底层运算封装也正是采用了这样的方式实现。

乘法的逆传播

z = x y z = xy z=xy 为例,其梯度 ( ∂ z ∂ x \frac{\partial{z}}{\partial{x}} xz ∂ z ∂ y \frac{\partial{z}}{\partial{y}} yz) = (y, x)。

因此乘法运算的逆传播时,总是将下游梯度乘以上游相乘参数的值(翻转值)。比如 x 与 y 相乘,求关于 x 的偏导数时,y 是 x 的翻转值;求关于 y 的偏导数时,x 是 y 的翻转值。

同上,我们用 MulLayer 类实现乘法运算的前向传播与逆传播:

class MulLayer:"""乘法运算的前向传播与逆传播"""def __init__(self):self.x = Noneself.y = Nonedef forward(self, x, y):"""前向传播Args:x: 输入 xy: 输入 yReturns:out: 输出"""self.x = xself.y = yout = x * yreturn outdef backward(self, dout):"""逆传播Args:dout: 上游梯度Returns:dx: x 的梯度dy: y 的梯度"""dx = dout * self.ydy = dout * self.xreturn dx, dy

逆传播求梯度

y = x 1 x 2 + x 3 y = x_1x_2 + x_3 y=x1x2+x3 为例,求 ( x 1 , x 2 , x 3 ) (x_1, x_2, x_3) (x1,x2,x3)= (100, 2, 300) 处的梯度。

在这里插入图片描述

y = x 1 x 2 + x 3 y = x_1x_2 + x_3 y=x1x2+x3 的计算链路如图 2,我们可以直接通过上文的 MulLayer 和 AddLayer 进行前向传播求 y,以及逆传播求关于 ( x 1 , x 2 , x 3 ) (x_1, x_2, x_3) (x1,x2,x3) 的梯度。

x1, x2, x3 = 100, 2, 300
mul_layer = MulLayer()
add_layer = AddLayer()# forward
a = mul_layer.forward(x1, x2)
y = add_layer.forward(a, x3)
print(y)                # 500# backward
da, dx3 = add_layer.backward(1)
dx1, dx2 = mul_layer.backward(da)
print(dx1, dx2, dx3)    # (x2, x1, 1) = (2, 100, 1)

运行结果与图 2 中所示 ( x 2 , x 1 , 1 ) (x_2, x_1, 1) (x2,x1,1)(输入 x 1 , x 2 , x 3 x_1, x_2, x_3 x1,x2,x3 各自的反向红色箭头是它们各自的梯度)一致,可见逆传播求梯度的结果是符合预期的。

以上逆传播求梯度过程可以直接应用在神经网络中对数组和矩阵的运算上:

x1, x2, x3 = np.array([100, 101, 102]), np.array([2, 3, 4]), np.array([300, 301, 302])
mul_layer = MulLayer()
add_layer = AddLayer()# forward
a = mul_layer.forward(x1, x2)
y = add_layer.forward(a, x3)
print(y)                # [500 604 710]# backward
da, dx3 = add_layer.backward(1)
dx1, dx2 = mul_layer.backward(da)
print(dx1, dx2, dx3)    # (x2, x1, 1) = [2 3 4] [100 101 102] 1

SoftmaxWithLoss 层

我们知道神经网络模型的训练过程就是根据损失函数关于权重参数的梯度优化权重参数的过程,其中求解损失函数关于权重参数的梯度是运算的核心。而损失函数往往是神经网络正向传播中的最后一个环节(训练过程的最后一个过程是损失函数,推理过程则一般不需要计算损失),根据 BP 算法的思路,在逆传播过程中,求解损失函数的“局部梯度”就成了求解权重参数梯度的第一步。

由于在多分类任务中,神经网络模型经常使用 Softmax 函数来对最终输出做归一化处理,我们在封装损失函数时,通常会将 Softmax 函数与损失函数结合在一起,这样的结构我们称之为SoftmaxWithLoss层。

下面我们以交叉熵误差为例,通过实现一个 SoftmaxWithLoss 层来演示 BP 算法及其“局部梯度”的求解过程。

假定网络的输出层有 n 个神经元(n 个分类类别),则 SoftmaxWithLoss 层的计算过程如图 3 所示:

在这里插入图片描述

从前面的层输入的是 ( a 1 , a 2 , . . . , a n ) (a_1, a_2, ..., a_n) (a1,a2,...,an),Softmax 层输出的是 ( y 1 , y 2 , . . . , y n ) (y_1, y_2, ..., y_n) (y1,y2,...,yn),实际结果分别是 ( t 1 , t 2 , . . . , t n ) (t_1, t_2, ..., t_n) (t1,t2,...,tn),Cross Entropy Error 输出的损失是 L。

正向传播

Softmax 往往作为网络输出层的激活函数,对网络的输出做最后的归一化处理;而在模型训练时,Softmax 的输出与实际结果作为损失函数的输入,可以计算出模型训练所需的损失值。可见 SoftmaxWithLoss 层实际就是经过了 Softmax 计算和 Loss Function 计算两个过程。

其中 Softmax 计算过程如式 2:

y k = e a k ∑ i = 1 n e a i (2) y_k = \frac{e^{a_k}}{\sum_{i=1}^{n} e^{a_i}} \tag{2} yk=i=1neaieak(2)

Loss Function(交叉熵误差)计算过程如式 3:

L = − ∑ k t k log ⁡ y k (3) L = -\sum_{k} t_k \log{y_k} \tag{3} L=ktklogyk(3)

合并式 2 和式 3,SoftmaxWithLoss 层的正向传播总计算式 4:

L = cross_entropy_error ( y , t ) = cross_entropy_error ( softmax ( a ) , t ) = − ∑ k t k log ⁡ e a k ∑ i = 1 n e a i (4) \begin{split} L = \text{cross\_entropy\_error}(y, t) \\ = \text{cross\_entropy\_error}(\text{softmax}(a), t) \\ = - \sum_{k} t_k \log{\frac{e^{a_k}}{\sum_{i=1}^{n} e^{a_i}}} \end{split} \tag{4} L=cross_entropy_error(y,t)=cross_entropy_error(softmax(a),t)=ktklogi=1neaieak(4)

SoftmaxWithLoss 层的正向传播分布计算过程:

  1. 指数运算:计算每个输入 a k a_k ak 的自然指数 e a k e^{a_k} eak
  2. 加法运算:计算所有输入的自然指数之和 S = ∑ i = 1 n e a i S = \sum_{i=1}^{n} e^{a_i} S=i=1neai
  3. 除法运算:计算所有输入的自然指数之和的倒数 1 S = 1 ∑ i = 1 n e a i \frac{1}{S} = \frac{1}{\sum_{i=1}^{n} e^{a_i}} S1=i=1neai1
  4. 乘法运算:计算每个输入 a k a_k ak 的自然指数与所有输入的自然指数之和的倒数的乘积 y k = e a k ∑ i = 1 n e a i y_k = \frac{e^{a_k}}{\sum_{i=1}^{n} e^{a_i}} yk=i=1neaieak
  5. 对数运算:计算每个输入 a k a_k ak 的自然指数与所有输入的自然指数之和的倒数的乘积的对数 log ⁡ y k = log ⁡ e a k ∑ i = 1 n e a i \log{y_k} = \log{\frac{e^{a_k}}{\sum_{i=1}^{n} e^{a_i}}} logyk=logi=1neaieak
  6. 乘法运算:计算每个输入 a k a_k ak 的自然指数与所有输入的自然指数之和的倒数的乘积的对数与实际结果 t k t_k tk 的乘积 t k log ⁡ y k = t k log ⁡ e a k ∑ i = 1 n e a i t_k \log{y_k} = t_k \log{\frac{e^{a_k}}{\sum_{i=1}^{n} e^{a_i}}} tklogyk=tklogi=1neaieak
  7. 加法运算:计算所有输入的自然指数与所有输入的自然指数之和的倒数的乘积的对数与实际结果的乘积之和 ∑ k t k log ⁡ y k = ∑ k t k log ⁡ e a k ∑ i = 1 n e a i \sum_{k} t_k \log{y_k} = \sum_{k} t_k \log{\frac{e^{a_k}}{\sum_{i=1}^{n} e^{a_i}}} ktklogyk=ktklogi=1neaieak
  8. 乘法运算:计算所有输入的自然指数与所有输入的自然指数之和的倒数的乘积的对数与实际结果的乘积之和的相反数 L = − ∑ k t k log ⁡ y k = − ∑ k t k log ⁡ e a k ∑ i = 1 n e a i L = - \sum_{k} t_k \log{y_k} = - \sum_{k} t_k \log{\frac{e^{a_k}}{\sum_{i=1}^{n} e^{a_i}}} L=ktklogyk=ktklogi=1neaieak

正向传播计算的是 SoftmaxWithLoss 根据输入 a 和真实结果 t 计算误差的过程;相对的,我们再看如何使用逆传播迅速计算 SoftmaxWithLoss 层的损失梯度(损失函数关于输入 a 的梯度)。

逆传播

逆传播计算过程是沿着正向传播过程的反方向进行的,首先误差梯度的初始值永远是 ∂ L ∂ L \frac{\partial{L}}{\partial{L}} LL = 1,逆传播的计算过程如下:

  1. 乘法求导(8)

− ∑ k t k log ⁡ y k = ∑ k t k log ⁡ y k × ( − 1 ) - \sum_{k} t_k \log{y_k} = \sum_{k} t_k \log{y_k} \times (-1) ktklogyk=ktklogyk×(1)

该步是 ∑ k t k log ⁡ y k \sum_{k} t_k \log{y_k} ktklogyk ( − 1 ) (-1) (1) 做乘法运算,逆传播计算 ∑ k t k log ⁡ y k \sum_{k} t_k \log{y_k} ktklogyk 的局部梯度取乘数 -1,此时梯度为 − 1 × 1 = − 1 -1 \times 1 = -1 1×1=1

  1. 加法求导(7)

∑ k t k log ⁡ y k = t 1 log ⁡ y 1 + t 2 log ⁡ y 2 + . . . + t n log ⁡ y n \sum_{k} t_k \log{y_k} = t_1 \log{y_1} + t_2 \log{y_2} + ... + t_n \log{y_n} ktklogyk=t1logy1+t2logy2+...+tnlogyn

该步是对各项 t k log ⁡ y k t_k \log{y_k} tklogyk 做加法运算,逆传播计算各项 t k log ⁡ y k t_k \log{y_k} tklogyk 的局部梯度取原值 -1,此时各项梯度为 -1;

  1. 乘法求导(6)

t k log ⁡ y k t_k \log{y_k} tklogyk

该步是 t k t_k tk log ⁡ y k \log{y_k} logyk 做乘法运算,逆传播计算 log ⁡ y k \log{y_k} logyk 的局部梯度取乘数 t k t_k tk,此时梯度为 t k × ( − 1 ) = − t k t_k \times (-1) = -t_k tk×(1)=tk

  1. 对数求导(5)

log ⁡ y k \log{y_k} logyk

该步是对 y k y_k yk 做对数运算,逆传播计算 y k y_k yk 的局部梯度取自变量的倒数 1 y k \frac{1}{y_k} yk1,此时梯度为 − t k × 1 y k = − t k y k - t_k \times \frac{1}{y_k} = - \frac{t_k}{y_k} tk×yk1=yktk

  1. 乘法求导(4)

y k = e a k ∑ i = 1 n e a i y_k = \frac{e^{a_k}}{\sum_{i=1}^{n} e^{a_i}} yk=i=1neaieak

该步是 e a k e^{a_k} eak 1 ∑ i = 1 n e a i \frac{1}{\sum_{i=1}^{n} e^{a_i}} i=1neai1 做乘法运算,逆传播计算 1 ∑ i = 1 n e a i \frac{1}{\sum_{i=1}^{n} e^{a_i}} i=1neai1 的局部梯度取乘数 e a k e^{a_k} eak,此时梯度为:

− t k y k × e a k = − t k × e a k e a k ∑ i = 1 n e a i = − t k ∑ i = 1 n e a i - \frac{t_k}{y_k} \times e^{a_k} = - \frac{t_k \times e^{a_k}}{\frac{e^{a_k}}{\sum_{i=1}^{n} e^{a_i}}} = - t_k \sum_{i=1}^{n} e^{a_i} yktk×eak=i=1neaieaktk×eak=tki=1neai

因为 1 ∑ i = 1 n e a i \frac{1}{\sum_{i=1}^{n} e^{a_i}} i=1neai1 分别与 n 项 e a i e^{a_i} eai 做乘法运算,逆传播计算局部梯度求所有分支的和 - t k ∑ i = 1 n e a i t_k \sum_{i=1}^{n} e^{a_i} tki=1neai,此时梯度为:

∑ k ( − t k ∑ i = 1 n e a i ) \sum_k{(- t_k \sum_{i=1}^{n} e^{a_i})} k(tki=1neai)

逆传播计算 e a k e^{a_k} eak 的局部梯度取乘数 1 ∑ i = 1 n e a i \frac{1}{\sum_{i=1}^{n} e^{a_i}} i=1neai1,此时梯度为:

− t k y k × 1 ∑ i = 1 n e a i = − t k e a k ∑ i = 1 n e a i 1 ∑ i = 1 n e a i = − t k e a k (temp-1) \begin{split} - \frac{t_k}{y_k} \times \frac{1}{\sum_{i=1}^{n} e^{a_i}} \\ = - \frac{t_k}{\frac{e^{a_k}}{\sum_{i=1}^{n} e^{a_i}}} \frac{1}{\sum_{i=1}^{n} e^{a_i}} \\ = - \frac{t_k}{e^{a_k}} \tag{temp-1} \end{split} yktk×i=1neai1=i=1neaieaktki=1neai1=eaktk(temp-1)

  1. 除法求导(3)

1 S = 1 ∑ i = 1 n e a i \frac{1}{S} = \frac{1}{\sum_{i=1}^{n} e^{a_i}} S1=i=1neai1

该步是用 ∑ i = 1 n e a i \sum_{i=1}^{n} e^{a_i} i=1neai 除 1,逆传播计算 ∑ i = 1 n e a i \sum_{i=1}^{n} e^{a_i} i=1neai 的局部梯度为: − 1 S 2 = − 1 ( ∑ i = 1 n e a i ) 2 - \frac{1}{S^{2}} = - \frac{1}{(\sum_{i=1}^{n} e^{a_i})^{2}} S21=(i=1neai)21,此时梯度为:

∑ k ( − t k ∑ i = 1 n e a i ) × ( − 1 ( ∑ i = 1 n e a i ) 2 ) = ∑ k ( t k ) 1 ( ∑ i = 1 n e a i ) 2 ∑ i = 1 n e a i = ∑ k ( t k ) 1 ∑ i = 1 n e a i \begin{split} \sum_k(- t_k \sum_{i=1}^{n} e^{a_i}) \times (- \frac{1}{(\sum_{i=1}^{n} e^{a_i})^{2}}) \\ = \sum_k(t_k) \frac{1}{(\sum_{i=1}^{n} e^{a_i})^{2}} \sum_{i=1}^{n} e^{a_i} \\ = \sum_k(t_k) \frac{1}{\sum_{i=1}^{n} e^{a_i}} \end{split} k(tki=1neai)×((i=1neai)21)=k(tk)(i=1neai)21i=1neai=k(tk)i=1neai1

此处因为 t k t_k tk 是 one-hot 编码,有且仅有一个 k 令 t k t_k tk 为 1,其他 t 全是 0,即 ∑ k ( t k ) = 1 \sum_k(t_k) = 1 k(tk)=1,因此此时梯度为:

∑ k ( t k ) 1 ∑ i = 1 n e a i = 1 ∑ i = 1 n e a i \sum_k(t_k) \frac{1}{\sum_{i=1}^{n} e^{a_i}} = \frac{1}{\sum_{i=1}^{n} e^{a_i}} k(tk)i=1neai1=i=1neai1

  1. 加法求导(2)

S = ∑ i = 1 n e a i S = \sum_{i=1}^{n} e^{a_i} S=i=1neai

该步是对各项 e a i e^{a_i} eai 做加法运算,逆传播计算各项 e a i e^{a_i} eai 的局部梯度取原值为:

1 ∑ i = 1 n e a i (temp-2) \frac{1}{\sum_{i=1}^{n} e^{a_i}} \tag{temp-2} i=1neai1(temp-2)

  1. 指数求导(1)

e a k e^{a_k} eak

该步是自然指数运算,逆传播求 e a k e^{a_k} eak 的局部梯度取自身为: e a k e^{a_k} eak

因为 e a i e^{a_i} eai 被分别使用于正向传播 2正向转播 4 的运算,逆传播求 e a i e^{a_i} eai 的梯度前,需要将正向传播 2正向转播 4处的梯度(即 temp-2 与 temp-1)求和作为 e a i e^{a_i} eai 的逆传播输入值,即:

1 ∑ i = 1 n e a i + ( − t k e a k ) = 1 ∑ i = 1 n e a i − t k e a k \frac{1}{\sum_{i=1}^{n} e^{a_i}} + (- \frac{t_k}{e^{a_k}}) = \frac{1}{\sum_{i=1}^{n} e^{a_i}} - \frac{t_k}{e^{a_k}} i=1neai1+(eaktk)=i=1neai1eaktk

因此此处的梯度为:

e a k × ( 1 ∑ i = 1 n e a i − t k e a k ) = e a k 1 ∑ i = 1 n e a i − t k = y k − t k \begin{split} e^{a_k} \times (\frac{1}{\sum_{i=1}^{n} e^{a_i}} - \frac{t_k}{e^{a_k}}) \\ = \frac{e^{a_k}}{\frac{1}{\sum_{i=1}^{n} e^{a_i}}} - t_k \\ = y_k - t_k \end{split} eak×(i=1neai1eaktk)=i=1neai1eaktk=yktk

即通过 SoftmaxWithLoss 层的逆传播过程推演计算,我们得到了损失函数 L 关于输入 a k a_k ak 的梯度为式 5:

∂ L ∂ a k = y k − t k (5) \frac{\partial{L}}{\partial{a_k}} = y_k - t_k \tag{5} akL=yktk(5)

相比数值微分法求梯度需要大量计算,我们可以直接利用公式 5 求损失函数 L 关于输入 a k a_k ak 的梯度,这将极大提高计算效率。

代码实现参考

根据上文 SoftmaxWithLoss 层的正向传播算式 4 与逆传播算式 5,我们可以轻易实现 SoftmaxWithLoss 层:

class SoftmaxWithLoss:def __init__(self):self.loss = Noneself.y = None  # softmax的输出self.t = None  # 监督数据def forward(self, x, t):"""前向传播Args:x: 输入数据t: 监督数据Returns:float: 损失"""self.t = tself.y = softmax(x)self.loss = cross_entropy_error(self.y, self.t)return self.lossdef backward(self, dout=1):"""逆传播Args:dout: 上游梯度Returns:np.ndarray: 损失关于输入 x 的梯度"""batch_size = self.t.shape[0]if self.t.size == self.y.size:  # 监督数据是one-hot-vector的情况dx = (self.y - self.t) / batch_sizeelse:dx = self.y.copy()dx[np.arange(batch_size), self.t] -= 1dx = dx / batch_sizereturn dx

测试 SoftmaxWithLoss 层的正向传播与逆传播:

softmax_with_loss = SoftmaxWithLoss()# 示例数据
a = np.array([[1.0, 2.0, 0.5], [0.0, 1.0, 1.0]])  # 未归一化输出
t = np.array([[1, 0, 0], [0, 1, 0]])  # one-hot 编码标签# 前向传播
loss = softmax_with_loss.forward(a, t)# 逆传播
da = softmax_with_loss.backward()print("Softmax Result:\n", softmax_with_loss.y)
print("Cross Entropy Loss:", loss)
print("Gradient:\n", da)
# Softmax Result:
#  [[0.2312239  0.62853172 0.14024438]
#  [0.1553624  0.4223188  0.4223188 ]]
# Cross Entropy Loss: 1.1631814594485623
# Gradient:
#  [[-0.38438805  0.31426586  0.07012219]
#  [ 0.0776812  -0.2888406   0.2111594 ]]

将以上逆传播求梯度的过程套用在神经网络损失函数关于权重参数的梯度求解上,我们就实现一个高效的神经网络学习算法,这就是 BP 算法。

交叉熵误差概念回顾

以多分类任务为例,我们知道其交叉熵误差的计算公式为:

L ( y , t ) = − ∑ i t i log ⁡ ( y i ) L(y, t) = -\sum_{i} t_i \log(y_i) L(y,t)=itilog(yi)

其中:

  • y y y 是模型的输出,通常是经过 softmax 函数处理得到的预测概率分布。
  • t t t 是真实标签,通常是 one-hot 编码表示的实际结果。

使用 Python 代码实现的交叉熵误差函数:

import numpy as npdef cross_entropy_error(y, t):"""交叉熵误差函数Args:y: 神经网络的输出t: 监督数据Returns:float: 交叉熵误差"""# 监督数据是 one-hot-vector 的情况下,转换为正确解标签的索引if t.size == y.size:t = t.argmax(axis=1)batch_size = y.shape[0]return -np.sum(np.log(y[np.arange(batch_size), t] + 1e-7)) / batch_size

Softmax 函数实现回顾

def softmax(x):"""归一化指数函数"""if x.ndim == 2:x = x.Tx = x - np.max(x, axis=0)y = np.exp(x) / np.sum(np.exp(x), axis=0)return y.Tx = x - np.max(x)  # 溢出对策return np.exp(x) / np.sum(np.exp(x))

结语

我们通过 BP 算法的正向传播与逆传播过程,演示了如何高效计算神经网络的梯度。通过 BP 算法,我们可以直接计算损失函数关于权重参数的梯度,而不需要通过数值微分法进行梯度计算,这将大大提高神经网络的训练效率。

BP 算法是迄今最成功的神经网络学习算法,通常神经网络(不限于前馈神经网络)都使用 BP 算法进行训练。“BP 网络”特指使用 BP 算法训练的多层前馈神经网络。

BP 算法实质是 LMS(Least Mean Square)算法的推广。LMS 试图使网络的输出均方误差最小化,用于神经元激活函数可微的感知机学习,LMS 推广到由非线性可微神经元组成的多层前馈网络,就是 BP 算法。


PS:感谢每一位志同道合者的阅读,欢迎关注、点赞、评论!


  • 上一篇:深度学习|损失函数:网络参数优化基准
  • 专栏:「数智通识」 | 「机器学习」

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/431184.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Vue使用qrcodejs2-fix生成网页二维码

安装qrcodejs2-fix npm install qrcodejs2-fix核心代码 在指定父view中生成一个二维码通过id找到父布局 //通过id找到父布局let codeView document.getElementById("qrcode")new QRCode(codeView, {text: "测试",width: 128,height: 128,colorDark: #00…

Fyne ( go跨平台GUI )中文文档-小部件 (五)

本文档注意参考官网(developer.fyne.io/) 编写, 只保留基本用法 go代码展示为Go 1.16 及更高版本, ide为goland2021.2 这是一个系列文章: Fyne ( go跨平台GUI )中文文档-入门(一)-CSDN博客 Fyne ( go跨平台GUI )中文文档-Fyne总览(二)-CSDN博客 Fyne ( go跨平台GUI…

LeetcodeTop100 刷题总结(二)

LeetCode 热题 100:https://leetcode.cn/studyplan/top-100-liked/ 文章目录 八、二叉树94. 二叉树的中序遍历(递归与非递归)补充:144. 二叉树的前序遍历(递归与非递归)补充:145. 二叉树的后序遍…

移动数组中数字的方法(c语言)

1.移动一维数组中的内容;若数组中有n个整数,要求把下标从0到p(含p,p小于等于n-1)的数组元素平移到数组的最后。 例如,一维数组中的原始内容为:1,2,3,4,5,6,7,8,9,10;p的值为3。 移动后,一维数组中的内容应为:5,6,7,8…

qm 命令:管理PVE虚拟机

一、命令简介 ​qm​ 是 Proxmox Virtual Environment (PVE) 中用于管理虚拟机的命令行工具。它允许用户创建、启动、停止、删除虚拟机,以及管理虚拟机的配置和状态。 ‍ 介绍 PVE Proxmox Virtual Environment (PVE) 是一个开源的虚拟化管理平台,专…

设计模式 享元模式(Flyweight Pattern)

享元模式 简绍 享元模式(Flyweight Pattern)是一种结构型设计模式,它的目的是通过共享技术来有效地支持大量细粒度的对象。享元模式可以极大地减少内存的使用,从而提高程序的性能。它特别适用于需要创建大量相似对象的场景&#…

QT 数据加密

一.使用环境 应该是通用的,此测试版本为如图 二.使用代码 1. 运行代码 QString data = "123abcAbc.-+";qDebug() << "加密:" << QAESEncryption::encodedText(data, "填入自己秘钥");qDebug() << "解密:" <…

C++STL的Stack的使用:STL栈和队列的使用介绍、leecode---最小栈、nowcoder---栈的压入、弹出序列等的介绍

文章目录 前言一、STL栈和队列的使用二、leetcode---最小栈三、nowcoder---栈的压入、弹出序列四、逆波兰表达式求值总结 前言 CSTL的Stack的使用&#xff1a;STL栈和队列的使用介绍、leecode—最小栈、nowcoder—栈的压入、弹出序列等的介绍 一、STL栈和队列的使用 #include …

服务器安装pytorch_geometric torch_scatter踩坑记录

conda create -n pyg python3.8.12 pip install torch1.13.0安装的版本如下 pip install torch-scatter pip install torch-sparse pip install torch-cluster pip install torch-spline-conv pip install torch-geometric2.2.0 pip install ipykernel python -m ipykernel i…

Adobe Illustrator吸管工具提取的颜色与原色之间存在色差

问题原因&#xff1a; 被提取颜色的对象是外部链接图片&#xff0c;对其提取的颜色会与AI中看到的颜色不同 如下图所示&#xff0c;中间的矩形与外部矩形的内部颜色存在色差 解决办法&#xff1a; 方法一&#xff1a;将该外部图片利用屏幕截图的形式&#xff0c;粘贴到AI中。…

2.以太网

局域网 局域网: Local Area Networks (LAN) 网络大小分类 局域网园区网(可以理解为企业网)城域网 广域网是一个网络连接的技术&#xff0c;并非多大范围的网络 网关 为局域网内的用户提供了一扇门&#xff0c;通过网关可以访问到别的网络。这个门&#xff0c;就叫网关 以…

部标(JT/T1078)流媒体对接说明

1.前言 最近在配合客户开发流媒体相关的服务的时候&#xff0c;整理了一些对接过程资料&#xff0c;这里做个分享与记录。流媒体的对接主要牵扯到4个方面&#xff1a; &#xff08;1&#xff09;平台端&#xff1a;业务端系统&#xff0c;包含前端呈现界面。 &#xff08;2&a…

物联网实践教程:微信小程序结合OneNET平台MQTT实现STM32单片机远程智能控制 远程上报和接收数据——STM32代码实现篇

STM32代码实现 开启本章节需要完成下方的前置任务&#xff1a; 点击跳转&#xff1a; 物联网实践教程&#xff1a;微信小程序结合OneNET平台MQTT实现STM32单片机远程智能控制 远程上报和接收数据——汇总 目标 1.连接OneNET&#xff1a;STM32使用串口与ESP8266/01s连接发送…

Linux网络命令:用于请求和配置网络地址的命令dhclient详解

目录 一、概述 二、功能描述 三、基本使用 1. 命令格式 2. 常用选项 3. 获取帮助 ​编辑 4. 基本操作 四、工作原理 1. 发送DHCP请求 2. 接收DHCP响应 3. 请求IP地址 4. 确认IP地址 5. 配置网络接口 五、功能特点 六、配置文件 七、常用命令和示例 1、启动…

QT客户端发送HTTP请求此时服务器到底收到了哪些数据?

一个Http请求包括 请求行 请求头 空行 请求体 下面是示例&#xff1a; 1,2,3,4分别代表上面的四个部分&#xff0c;我只是做了一些解析&#xff0c;具体可以结合代码 1. post / HTTP/1.1 2.GET请求头包括Host(主机名),user-agent&#xff08;客户端标识符&#xff09;&am…

SiMBA:基于FFT变换和Mamba的图像分类模型算法解析

目录 1 算法原理1 EinFFT&#xff1a;1.1 频谱变换1.2 频域通道混频 & 频谱门控网络1.3 逆频谱变换 2 整体计算公式3 SiMBA&#xff1a;4 小结 2 代码大致逻辑EinFFT模块MambaBlock模块MambaLayer模块PVT2FFN和FFN模块ClassBlock和Block_mamba模块DownSamples模块Stem模块定…

统信服务器操作系统【targetcli部署】

targetcli部署方案 文章目录 功能概述功能介绍1.安装targetcli2.targetcli语法及参数说明3.示例1. 配置2.访问功能概述 SCSI 即小型计算机系统接口(Small Computer System Interface;简写:SCSI) iSCSI,internet SCSI 网络磁盘 ,提供一对一的网络存储, 主机A 提供xx存储设…

gitlab 的CI/CD (二)

前言 上文完成了gitlab-runner的基础配置及将gitlab的制品上传至软件包库&#xff08;产品库&#xff09;的脚本编写&#xff1b; 本文实现gitlab的ci/cd对远程服务器的操作&#xff1b; 介绍 要让Gitlab Runner部署到远程机器&#xff0c;远程机器必须信任gitlab runner账…

当大语言模型应用到教育领域时会有什么火花出现?

当大语言模型应用到教育领域时会有什么火花出现&#xff1f; LLM Education会出现哪些机遇与挑战? 今天笔者分享一篇来自New York University大学的研究论文&#xff0c;另外一篇则是来自Michigan State University与浙江师范大学的研究论文&#xff0c;希望对这个话题感兴趣…

构建高效企业客户管理系统:SpringBoot应用

1 绪论 1.1研究背景 随着网络不断的普及发展&#xff0c;企业客户管理系统依靠网络技术的支持得到了快速的发展&#xff0c;首先要从员工的实际需求出发&#xff0c;通过了解员工的需求开发出具有针对性的首页、个人中心、员工管理、客户信息管理、行业类型管理、项目信息管理、…