共计 2705 个字符,预计需要花费 7 分钟才能阅读完成。
为什么选择 BP 神经网络做分类?
BP(反向传播)神经网络是入门深度学习最经典的模型之一。它通过多层感知器的结构,能够学习复杂的非线性决策边界。对于分类任务,比如识别手写数字、判断肿瘤良性恶性等场景,BP 神经网络往往能达到比传统机器学习算法更好的效果。

但初学者常会遇到两个头疼的问题:
- 梯度消失 :深层网络中误差反向传播时,梯度越来越小,导致底层参数几乎不更新
- 过拟合 :模型在训练集上表现很好,但遇到新数据就崩溃
用 Python 实现 BP 神经网络
1. 网络初始化
神经网络的骨架由输入层、隐藏层和输出层组成。我们需要随机初始化权重和偏置:
import numpy as np
class NeuralNetwork:
def __init__(self, input_size, hidden_size, output_size):
# 使用 He 初始化,适合 ReLU 激活函数
self.W1 = np.random.randn(input_size, hidden_size) * np.sqrt(2./input_size)
self.b1 = np.zeros((1, hidden_size))
self.W2 = np.random.randn(hidden_size, output_size) * np.sqrt(2./hidden_size)
self.b2 = np.zeros((1, output_size))
2. 前向传播实现
前向传播就是数据从输入层流向输出层的过程:
def forward(self, X):
# 隐藏层计算(使用 ReLU 激活)self.z1 = np.dot(X, self.W1) + self.b1
self.a1 = np.maximum(0, self.z1) # ReLU
# 输出层计算(使用 Softmax 激活)self.z2 = np.dot(self.a1, self.W2) + self.b2
exp_scores = np.exp(self.z2)
self.probs = exp_scores / np.sum(exp_scores, axis=1, keepdims=True)
return self.probs
3. 反向传播核心
反向传播通过链式法则计算梯度:
def backward(self, X, y, learning_rate):
num_examples = X.shape[0]
# 输出层误差
delta3 = self.probs
delta3[range(num_examples), y] -= 1
delta3 /= num_examples
# 隐藏层误差
dW2 = np.dot(self.a1.T, delta3)
db2 = np.sum(delta3, axis=0, keepdims=True)
delta2 = np.dot(delta3, self.W2.T)
delta2[self.a1 <= 0] = 0 # ReLU 的导数
# 更新参数
dW1 = np.dot(X.T, delta2)
db1 = np.sum(delta2, axis=0)
# 加入 L2 正则化
dW2 += reg_lambda * self.W2
dW1 += reg_lambda * self.W1
# 参数更新
self.W1 -= learning_rate * dW1
self.b1 -= learning_rate * db1
self.W2 -= learning_rate * dW2
self.b2 -= learning_rate * db2
实战技巧:让网络更好训练
数据预处理
-
标准化 :将特征缩放到均值为 0,方差为 1
from sklearn.preprocessing import StandardScaler scaler = StandardScaler() X_train = scaler.fit_transform(X_train) X_test = scaler.transform(X_test) # 注意用相同的 scaler -
类别编码 :对于多分类问题使用 one-hot 编码
from keras.utils import to_categorical y_one_hot = to_categorical(y, num_classes=3)
学习率调整
学习率太大容易震荡,太小收敛慢:
# 学习率衰减策略
initial_learning_rate = 0.1
decay_rate = 0.95
epochs = 100
for epoch in range(epochs):
learning_rate = initial_learning_rate * (decay_rate ** epoch)
nn.backward(X_train, y_train, learning_rate)
防止过拟合
- L2 正则化 :在损失函数中加入权重惩罚项
- Dropout:随机让部分神经元失活
- 早停法 :验证集性能不再提升时停止训练
鸢尾花分类实战
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
# 加载数据
iris = load_iris()
X, y = iris.data, iris.target
# 划分训练测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)
# 创建网络
nn = NeuralNetwork(input_size=4, hidden_size=10, output_size=3)
# 训练 500 轮
for epoch in range(500):
# 前向传播
probs = nn.forward(X_train)
# 反向传播
nn.backward(X_train, y_train, learning_rate=0.01)
# 每 50 轮打印一次损失
if epoch % 50 == 0:
loss = -np.log(probs[range(len(y_train)), y_train]).mean()
print(f"Epoch {epoch}, loss: {loss:.4f}")
新手避坑指南
- 忘记数据标准化 :不同量纲的特征会导致梯度下降困难
- 初始化权重全为 0 :会导致所有神经元学习相同的特征
- 学习率设置不当 :建议先用 0.01 尝试,观察损失曲线
- 忽略梯度检查 :复杂网络实现时,先用数值梯度验证反向传播
- 过早停止训练 :监控验证集准确率,而不是只看训练损失
下一步探索方向
- 尝试不同的激活函数(Tanh、LeakyReLU 等)
- 增加网络深度,观察梯度变化
- 加入 Batch Normalization 层
- 用交叉验证选择最佳超参数
神经网络就像乐高积木,理解基础原理后,你可以自由组合各种模块。虽然现在有现成的深度学习框架,但亲手实现一次 BP 算法,会让你对神经网络的工作机制有更深刻的理解。
正文完
