共计 1683 个字符,预计需要花费 5 分钟才能阅读完成。
从单层感知机到神经网络
在机器学习中,单层感知机是最简单的神经网络模型。它只能处理线性可分的问题,对于复杂的非线性问题无能为力。这就是为什么我们需要多层神经网络,而多层网络就需要反向传播算法来训练。

前向计算过程详解
-
加权求和 :每个神经元的输入是前一层所有神经元输出的加权和
$$z_j = \sum_{i} w_{ji}x_i + b_j$$ -
激活函数 :通过非线性激活函数转换
$$a_j = \sigma(z_j)$$
常用的激活函数包括 Sigmoid、ReLU 和 Tanh 等。
误差反向传播机制
反向传播的核心是链式法则。我们通过计算损失函数对权重的梯度来更新权重:
-
输出层误差:
$$\delta_k = (y_k – t_k)\sigma'(z_k)$$ -
隐藏层误差:
$$\delta_j = \sigma'(z_j)\sum_k w_{kj}\delta_k$$ -
权重更新:
$$\Delta w_{ji} = -\eta \delta_j x_i$$
Python 实现代码
import numpy as np
class NeuralNetwork:
def __init__(self, layers):
# 网络初始化
self.weights = []
self.biases = []
for i in range(len(layers)-1):
# 使用 Xavier 初始化
w = np.random.randn(layers[i+1], layers[i]) * np.sqrt(1/layers[i])
b = np.zeros((layers[i+1], 1))
self.weights.append(w)
self.biases.append(b)
def forward(self, x):
# 前向传播
a = x
for w, b in zip(self.weights, self.biases):
z = np.dot(w, a) + b
a = self.sigmoid(z)
return a
def backward(self, x, y, learning_rate):
# 反向传播
# 保存各层激活值
activations = [x]
zs = []
# 前向传播
a = x
for w, b in zip(self.weights, self.biases):
z = np.dot(w, a) + b
zs.append(z)
a = self.sigmoid(z)
activations.append(a)
# 计算输出层误差
delta = (activations[-1] - y) * self.sigmoid_derivative(zs[-1])
# 更新权重和偏置
for l in range(len(self.weights)-1, -1, -1):
# 计算梯度
dw = np.dot(delta, activations[l].T)
db = delta
# 更新参数
self.weights[l] -= learning_rate * dw
self.biases[l] -= learning_rate * db
# 如果不是第一层,计算前一层的误差
if l > 0:
delta = np.dot(self.weights[l].T, delta) * self.sigmoid_derivative(zs[l-1])
def sigmoid(self, z):
return 1/(1+np.exp(-z))
def sigmoid_derivative(self, z):
s = self.sigmoid(z)
return s * (1-s)
实际应用注意事项
- 学习率选择 :
- 太大可能导致震荡
- 太小收敛缓慢
-
可以使用学习率衰减策略
-
梯度消失 / 爆炸 :
- 使用 ReLU 等激活函数缓解
- 采用批标准化
-
使用残差连接
-
激活函数选择 :
- Sigmoid 容易导致梯度消失
- ReLU 计算简单但可能有 ” 死亡神经元 ” 问题
- Leaky ReLU 是较好的折中方案
思考题
- 如何改进基础 BP 算法来加速收敛?
- 引入动量项
-
使用自适应学习率方法 (如 Adam)
-
批量训练与在线训练的利弊分析
- 批量训练更稳定但计算量大
- 在线训练可以实时更新但波动较大
- 小批量训练是常用折中方案
通过这篇文章,相信你已经对 BP 神经网络的工作原理有了深入理解。在实际应用中,建议从简单的网络结构开始,逐步增加复杂度,并注意监控训练过程中的损失和准确率变化。
正文完
