共计 3158 个字符,预计需要花费 8 分钟才能阅读完成。
BP 神经网络基础认知
BP(Back Propagation)神经网络是一种典型的多层前馈神经网络,其核心是通过误差反向传播算法调整网络权重。一个标准的三层结构包括:

- 输入层:接收原始特征向量
- 隐含层:进行非线性特征变换(可有多层)
- 输出层:产生最终预测结果
网络训练的本质是通过梯度下降最小化损失函数,这个过程需要反复执行:
- 前向传播计算预测值
- 反向传播调整各层参数
数学推导全流程
前向传播计算
设网络有 $L$ 层,第 $l$ 层的权重矩阵为 $W^l$,偏置为 $b^l$,激活函数为 $\sigma(\cdot)$,则:
$$
\begin{aligned}
z^l &= W^l a^{l-1} + b^l \
a^l &= \sigma(z^l)
\end{aligned}
$$
其中 $a^0$ 即为输入数据,$a^L$ 为最终输出。
损失函数定义
对于分类任务常用交叉熵损失,回归任务常用均方误差(MSE):
$$
J = \frac{1}{2m}\sum_{i=1}^m (y_i – a_i^L)^2
$$
反向传播推导(核心部分)
输出层误差计算
首先计算输出层误差 $\delta^L$:
$$
\delta^L = \frac{\partial J}{\partial z^L} = (a^L – y) \odot \sigma'(z^L)
$$
隐含层误差传播
通过链式法则逐层回传误差:
$$
\delta^l = ((W^{l+1})^T \delta^{l+1}) \odot \sigma'(z^l)
$$
参数梯度计算
最终得到权重和偏置的梯度:
$$
\begin{aligned}
\frac{\partial J}{\partial W^l} &= \delta^l (a^{l-1})^T \
\frac{\partial J}{\partial b^l} &= \delta^l
\end{aligned}
$$
参数更新
使用梯度下降法更新参数:
$$
\begin{aligned}
W^l &:= W^l – \eta \frac{\partial J}{\partial W^l} \
b^l &:= b^l – \eta \frac{\partial J}{\partial b^l}
\end{aligned}
$$
Python 实现详解
import numpy as np
class BPNeuralNetwork:
def __init__(self, layers):
"""
初始化网络结构
:param layers: 各层神经元数量,如 [4,5,1] 表示两层网络
"""
self.L = len(layers) - 1
self.weights = [np.random.randn(layers[i+1], layers[i])*0.1
for i in range(self.L)]
self.biases = [np.zeros((layers[i+1], 1))
for i in range(self.L)]
def sigmoid(self, z):
return 1/(1+np.exp(-z))
def sigmoid_prime(self, z):
return self.sigmoid(z)*(1-self.sigmoid(z))
def forward(self, x):
"""前向传播"""
a = x.T if x.ndim > 1 else x.reshape(-1,1)
activations = [a]
zs = []
for w,b in zip(self.weights, self.biases):
z = np.dot(w, a) + b
a = self.sigmoid(z)
zs.append(z)
activations.append(a)
return activations[-1], activations, zs
def backward(self, x, y, activations, zs):
"""反向传播"""
nabla_w = [np.zeros(w.shape) for w in self.weights]
nabla_b = [np.zeros(b.shape) for b in self.biases]
# 输出层误差
delta = (activations[-1] - y) * self.sigmoid_prime(zs[-1])
nabla_b[-1] = delta
nabla_w[-1] = np.dot(delta, activations[-2].T)
# 反向传播误差
for l in range(2, self.L+1):
delta = np.dot(self.weights[-l+1].T, delta) * \
self.sigmoid_prime(zs[-l])
nabla_b[-l] = delta
nabla_w[-l] = np.dot(delta, activations[-l-1].T)
return nabla_w, nabla_b
def train(self, X, y, epochs=1000, lr=0.1):
"""训练过程"""
for epoch in range(epochs):
# 小批量梯度下降
output, activations, zs = self.forward(X)
nabla_w, nabla_b = self.backward(X, y, activations, zs)
# 参数更新
self.weights = [w - lr*nw
for w, nw in zip(self.weights, nabla_w)]
self.biases = [b - lr*nb
for b, nb in zip(self.biases, nabla_b)]
if epoch % 100 == 0:
loss = np.mean((output - y)**2)
print(f"Epoch {epoch}, Loss: {loss:.4f}")
# 使用示例
if __name__ == "__main__":
# XOR 问题数据集
X = np.array([[0,0],[0,1],[1,0],[1,1]])
y = np.array([[0],[1],[1],[0]])
# 创建 2 -4- 1 结构的网络
nn = BPNeuralNetwork([2,4,1])
nn.train(X, y, epochs=2000, lr=0.5)
工程实践关键点
学习率选择
- 常用范围:0.001~0.1
- 可采用学习率衰减策略:
lr = initial_lr / (1 + decay_rate * epoch)
权重初始化技巧
- 避免全零初始化
- Xavier 初始化:
w = np.random.randn(fan_in, fan_out) / np.sqrt(fan_in)
梯度检查方法
通过数值梯度验证解析梯度的正确性:
def numerical_gradient(f, x, eps=1e-4):
grad = np.zeros_like(x)
for i in range(x.size):
tmp = x.flat[i]
x.flat[i] = tmp + eps
f1 = f(x)
x.flat[i] = tmp - eps
f2 = f(x)
grad.flat[i] = (f1 - f2)/(2*eps)
x.flat[i] = tmp
return grad
常见问题解决方案
- 梯度消失:使用 ReLU 等激活函数
- 梯度爆炸:梯度裁剪
- 过拟合:添加 L2 正则化项
loss = original_loss + 0.5*lambda_*(np.sum(weights**2))
性能优化方向
- 计算加速:
- 使用 GPU 加速矩阵运算
-
采用 mini-batch 训练
-
算法改进:
- 引入动量(Momentum)
-
自适应学习率(Adam 等)
-
结构优化:
- 批量归一化(BatchNorm)
- 残差连接
总结思考
通过手动推导 BP 算法,我们可以深入理解神经网络的学习机制。虽然现代深度学习框架已经实现了自动微分,但掌握底层原理对于:
- 调试网络训练问题
- 设计新型网络结构
- 优化训练过程
都具有重要意义。建议读者可以进一步探索:
- 不同激活函数对梯度传播的影响
- 二阶优化方法(如 Hessian 矩阵)的应用
- 与其他优化算法(SGD、Adam 等)的对比实验
