共计 2681 个字符,预计需要花费 7 分钟才能阅读完成。
神经网络基础回顾
BP 神经网络(Back Propagation Neural Network)是一种多层前馈神经网络,通过反向传播算法来调整网络权重。它由输入层、隐藏层和输出层组成,每一层包含若干神经元。神经元之间的连接权重决定了信号的传递强度。

神经网络的训练过程分为两个阶段:正向传播和反向传播。正向传播用于计算网络的输出,反向传播用于根据误差调整权重。
正向传播的数学推导与实现
正向传播是指输入信号从输入层经过隐藏层最终到达输出层的过程。每一层的输出都是前一层输出的加权和经过激活函数的结果。
数学表达式如下:
-
输入层到第一隐藏层的计算:
$$Z^1 = W^1 \cdot X + b^1$$
$$A^1 = f(Z^1)$$ -
第 l 层到第 l + 1 层的计算:
$$Z^{l+1} = W^{l+1} \cdot A^l + b^{l+1}$$
$$A^{l+1} = f(Z^{l+1})$$
其中,$W$ 是权重矩阵,$b$ 是偏置向量,$f$ 是激活函数(如 sigmoid、ReLU 等)。
反向传播的梯度计算详解
反向传播是根据输出误差来调整网络权重的过程。它通过链式法则计算各层权重和偏置的梯度。
-
输出层的误差:
$$\delta^L = \frac{\partial J}{\partial Z^L} = (A^L – Y) \odot f'(Z^L)$$ -
隐藏层的误差传播:
$$\delta^l = (W^{l+1})^T \cdot \delta^{l+1} \odot f'(Z^l)$$ -
权重和偏置的梯度:
$$\frac{\partial J}{\partial W^l} = \delta^l \cdot (A^{l-1})^T$$
$$\frac{\partial J}{\partial b^l} = \delta^l$$
其中,$J$ 是损失函数,$Y$ 是真实标签,$\odot$ 表示元素乘法。
完整 Python 代码实现
以下是使用 NumPy 实现的 BP 神经网络代码:
import numpy as np
class BPNeuralNetwork:
def __init__(self, layers, learning_rate=0.1):
self.layers = layers
self.learning_rate = learning_rate
self.weights = []
self.biases = []
# 初始化权重和偏置
for i in range(len(layers)-1):
w = np.random.randn(layers[i+1], layers[i]) * 0.01
b = np.zeros((layers[i+1], 1))
self.weights.append(w)
self.biases.append(b)
def sigmoid(self, z):
return 1 / (1 + np.exp(-z))
def sigmoid_derivative(self, z):
s = self.sigmoid(z)
return s * (1 - s)
def forward_propagation(self, X):
A = X
caches = []
for i in range(len(self.weights)):
Z = np.dot(self.weights[i], A) + self.biases[i]
A = self.sigmoid(Z)
caches.append((Z, A))
return A, caches
def backward_propagation(self, X, Y, caches):
m = X.shape[1]
grads = {}
A_final = caches[-1][1]
# 计算输出层误差
dZ = A_final - Y
for l in reversed(range(len(self.weights))):
A_prev = X if l == 0 else caches[l-1][1]
dW = np.dot(dZ, A_prev.T) / m
db = np.sum(dZ, axis=1, keepdims=True) / m
grads[f'dW{l+1}'] = dW
grads[f'db{l+1}'] = db
if l > 0:
dZ = np.dot(self.weights[l].T, dZ) * self.sigmoid_derivative(caches[l-1][0])
return grads
def update_parameters(self, grads):
for l in range(len(self.weights)):
self.weights[l] -= self.learning_rate * grads[f'dW{l+1}']
self.biases[l] -= self.learning_rate * grads[f'db{l+1}']
def train(self, X, Y, epochs=1000):
for i in range(epochs):
# 正向传播
A, caches = self.forward_propagation(X)
# 计算损失
loss = -np.mean(Y * np.log(A) + (1-Y) * np.log(1-A))
# 反向传播
grads = self.backward_propagation(X, Y, caches)
# 更新参数
self.update_parameters(grads)
if i % 100 == 0:
print(f'Epoch {i}, Loss: {loss}')
超参数调优建议
- 学习率:
- 过大可能导致震荡甚至发散
- 过小会导致收敛速度慢
-
建议从 0.01 开始尝试,根据训练效果调整
-
批量大小:
- 批量梯度下降(Batch GD):使用全部样本
- 小批量梯度下降(Mini-batch GD):常用 32/64/128
-
随机梯度下降(SGD):每次一个样本
-
隐藏层数量和神经元数量:
- 过于复杂的网络容易过拟合
- 建议从简单网络开始,逐步增加复杂度
常见错误与解决方案
- 梯度消失 / 爆炸问题:
- 使用 ReLU 等非饱和激活函数
- 使用 Batch Normalization
-
使用梯度裁剪
-
参数初始化问题:
- 避免全零初始化
-
使用 Xavier 或 He 初始化
-
过拟合问题:
- 使用 L1/L2 正则化
- 使用 Dropout
- 增加训练数据
延伸思考题
- 除了 sigmoid 函数,还有哪些激活函数可以使用?它们各有什么优缺点?
- 如何修改代码实现批量归一化(Batch Normalization)?
- 除了均方误差和交叉熵,还有哪些损失函数适合分类问题?
- 如何实现早停(Early Stopping)来防止过拟合?
- 如何可视化训练过程中权重和偏差的变化?
通过本文的学习,你应该已经掌握了 BP 神经网络的基本原理和实现方法。接下来可以尝试将这些知识应用到实际项目中,比如手写数字识别、房价预测等问题。在实践中不断调整和优化网络结构,积累经验。
