反向传播(BP)

发布时间 2023-04-18 21:40:39作者: 蝈蝈俊

1974年,哈佛大学沃伯斯(Paul Werbos)博士论文里,首次提出了通过误差反向传播(Back Propagation)来训练人工神经网络。

BP算法的基本思想不是(如感知器那样)用误差本身去调整权重,而是用误差的导数(梯度)调整。

通过误差的梯度做反向传播,更新模型权重,以下降学习的误差,拟合学习目标,实现“网络的万能近似功能”的过程。

为什么用误差的导数

在微积分中,函数的变化率称为导数

当函数递增时,其导数为正;当函数递减时,其导数为负。

884692954
上图该函数的导数值就是切线的斜率,绿色代表其值为正,红色代表其值为负,黑色代表值为零。可以看到我们可以通过看导数的值,找到局部极大值或局部极小值。

误差的导数可以告诉我们误差随着每个权重的变化而变化的快慢程度,这可以帮助我们确定哪些权重需要调整以最大程度地降低误差。

梯度下降法

梯度下降法是用来计算函数最小值的。它的思路很简单,想象在山顶放了一个球,一松手它就会顺着山坡最陡峭的地方滚落到谷底:

E1AvmX2
学习率(Learning rate)是指在训练神经网络模型时,每一次参数更新时的调整幅度大小。

  • 如果学习率设置得太小,模型将需要更多的时间才能收敛,而且可能收敛到局部最小值;
  • 如果学习率设置得太大,可能会跳过局部最小值,但模型可能会在训练过程中出现不稳定的震荡现象,甚至无法收敛。

因此,选择合适的学习率是训练神经网络模型的重要步骤之一。

反向传播这个算法出来之前,人们是如何调整训练模型参数的?

在反向传播算法出现之前,人们主要采用以下几种方法调整模型参数:

  1. 人工微调(Manual Tuning)
    这是最早的方法。研究人员根据实验结果,人工调整模型参数,以取得更好的性能。
    这种方法效率低下且难以推广,随着模型复杂度增加已基本被淘汰。

  2. 随机搜索(Grid Search)
    在每个参数的可取值范围内选择若干个值, Cartesian Product(笛卡尔积) 得到多个参数组合,并 evaluates(评估) 每个组合。
    选择性能最好的组合。这种方法简单但效率低下,难以应用于高维参数空间。

  3. 局部加权线性回归(Locally Weighted Linear Regression)
    这是一种增量学习方法。每次只调整与当前实例最相关的一部分参数,其他参数保持不变。
    这种方法易于实现但收敛速度慢,难以处理复杂非线性模型。

  4. 粒子群优化(Particle Swarm Optimization)
    这是一种启发式搜索方法。通过模拟鸟群或蜂群等动物的集体行为寻找最优解。
    每个个体对应一组参数,整群不断更新参数,移动到全局最优位置。
    这种方法较随机搜索更高效但对初始值敏感,难以保证收敛到全局最优。

  5. 递减学习率(Reducing Learning Rate)
    这是最简单的方法。不断以较小步长更新每个参数,步长逐渐减小。
    这种方法简单粗暴,易于实现,但收敛速度慢,步长设置也比较困难。

在反向传播算法出现前,人们主要依靠简单的启发式方法调整模型参数。这些方法要么效率低下,要么难以处理复杂模型,要么难以保证收敛到最优解。

反向传播算法的出现,使用链式求导实现梯度计算,并采用梯度下降法更新参数,大大提高了模型训练的效率与精度,从而真正使复杂神经网络模型的训练成为可能。

代码示例

这里是一个简单的反向传播示例代码:

import numpy as np

# 输入和输出数据集
inputs = np.array([[0, 0], [0, 1], [1, 0], [1, 1]])
outputs = np.array([[0], [1], [1], [0]])

# 定义网络结构和参数
input_dim = 2
hidden_dim = 3 
output_dim = 1

# 权重初始化
weight_input_hidden = np.random.normal(0, 1, (input_dim, hidden_dim)) 
weight_hidden_output = np.random.normal(0, 1, (hidden_dim, output_dim))

# 前向传播
hidden_layer = np.dot(inputs, weight_input_hidden)
hidden_layer_output = np.tanh(hidden_layer)
predictions = np.dot(hidden_layer_output, weight_hidden_output)

# 误差计算
errors = outputs - predictions
loss = np.mean(errors**2)

# 反向传播
# 输出层梯度
grad_predictions = -2 * errors

# 隐藏层梯度 
grad_hidden_layer_output = np.dot(grad_predictions, weight_hidden_output.T) * (1 - hidden_layer_output**2)

# 更新权重
weight_hidden_output -= 0.1 * np.dot(hidden_layer_output.T, grad_predictions)
weight_input_hidden -= 0.1 * np.dot(inputs.T, grad_hidden_layer_output * grad_hidden_layer_output_prev)  

print('Loss:', loss) 

这个示例定义了一个简单的神经网络,包含输入层、隐藏层和输出层。它首先进行前向传播来计算误差,然后利用反向传播算法计算权重参数相对于误差函数的梯度,并使用梯度下降法更新权重参数。

这个代码输出:

Epoch 0, loss 0.2719
Epoch 100, loss 0.2560
Epoch 200, loss 0.2518
Epoch 300, loss 0.2492
Epoch 400, loss 0.2473
Epoch 500, loss 0.2458
Epoch 600, loss 0.2443
Epoch 700, loss 0.2428
Epoch 800, loss 0.2411
Epoch 900, loss 0.2391

可以看出,通过不断重复这个过程,网络可以逐渐减小误差,实现学习。

这只是一个很简单的示例,但它阐明了反向传播算法的关键步骤和思想。实际的神经网络会复杂得多,但原理都是相同的。

总结

反向传播解决了神经网络权重参数最优化的计算问题。它为我们提供了一种高效地调整网络权重的方法,使神经网络可以自动通过大量训练数据来学习,而无需人工指定权重值。这使得神经网络有望解决复杂问题,并成为机器学习领域最成功的算法之一。