共计 3018 个字符,预计需要花费 8 分钟才能阅读完成。
3D 结构示意图入门
先来看一个典型的三层 BP 网络 3D 示意图(想象一下立体结构):
- 输入层:假设是 3 个神经元,对应矩阵维度 [3×1]
- 隐藏层:设定为 4 个神经元,第一层权重矩阵维度 [4×3]
- 输出层:2 个神经元输出,第二层权重矩阵维度 [2×4]
箭头连接线粗细代表权重大小,红色表示正向传播,蓝色表示反向传播。这种可视化能帮我们直观理解 ”W 的维度 = 下一层神经元数×当前层神经元数 ” 的黄金法则。
数学原理拆解
1. 前向传播的向量化计算
前向传播就是数据流过网络的过程:
- 输入数据 X(形状[batch_size, input_dim])
- 隐藏层计算:Z1 = W1·X + b1(注意矩阵乘法的行列对应关系)
- 激活函数:A1 = relu(Z1)(ReLU 比 sigmoid 更常用,能缓解梯度消失)
- 输出层计算:Z2 = W2·A1 + b2
- 最终输出:A2 = softmax(Z2)(分类任务用 softmax,回归用线性输出)
关键技巧:始终用 np.dot() 做矩阵乘法,避免用 * 导致逐元素相乘的错误。
2. 反向传播梯度推导
损失函数对 W2 的偏导(以交叉熵损失为例):
∂L/∂W2 = (A2 - Y) · A1.T # Y 是真实标签的 one-hot 编码
对 W1 的偏导(链式法则的经典应用):
∂L/∂W1 = (W2.T · (A2-Y)) * relu_derivative(Z1) · X.T

(示意图:展示∂L/∂W 的分步推导过程)
3. 学习率与权重更新
实现权重更新的核心代码:
learning_rate = 0.01
W1 -= learning_rate * dW1 # 注意是 -= 不是 +=
b1 -= learning_rate * db1
Python 完整实现
import numpy as np
import matplotlib.pyplot as plt
class NeuralNetwork:
def __init__(self, input_size, hidden_size, output_size):
self.W1 = np.random.randn(hidden_size, input_size) * 0.01
self.b1 = np.zeros((hidden_size, 1))
self.W2 = np.random.randn(output_size, hidden_size) * 0.01
self.b2 = np.zeros((output_size, 1))
def forward(self, X):
self.Z1 = np.dot(self.W1, X) + self.b1
self.A1 = np.maximum(0, self.Z1) # ReLU
self.Z2 = np.dot(self.W2, self.A1) + self.b2
exp_scores = np.exp(self.Z2 - np.max(self.Z2))
self.A2 = exp_scores / np.sum(exp_scores, axis=0)
return self.A2
def backward(self, X, Y):
m = X.shape[1]
dZ2 = self.A2 - Y
dW2 = np.dot(dZ2, self.A1.T) / m
db2 = np.sum(dZ2, axis=1, keepdims=True) / m
dZ1 = np.dot(self.W2.T, dZ2) * (self.Z1 > 0)
dW1 = np.dot(dZ1, X.T) / m
db1 = np.sum(dZ1, axis=1, keepdims=True) / m
return dW1, db1, dW2, db2
def train(self, X, Y, epochs, lr):
losses = []
for i in range(epochs):
A2 = self.forward(X)
loss = -np.mean(Y * np.log(A2 + 1e-8))
losses.append(loss)
dW1, db1, dW2, db2 = self.backward(X, Y)
self.W1 -= lr * dW1
self.b1 -= lr * db1
self.W2 -= lr * dW2
self.b2 -= lr * db2
if i % 100 == 0:
print(f"Epoch {i}, loss: {loss:.4f}")
plt.plot(losses)
plt.title("Training Loss Curve")
plt.show()
# 梯度检查实现
def gradient_check(nn, X, Y, epsilon=1e-7):
params = nn.W1.flatten()
grad_approx = np.zeros_like(params)
for i in range(len(params)):
theta_plus = params.copy()
theta_plus[i] += epsilon
theta_minus = params.copy()
theta_minus[i] -= epsilon
loss_plus = -np.mean(Y * np.log(nn.forward(X)))
loss_minus = -np.mean(Y * np.log(nn.forward(X)))
grad_approx[i] = (loss_plus - loss_minus) / (2*epsilon)
grad = nn.backward(X, Y)[0].flatten()
difference = np.linalg.norm(grad_approx - grad) / np.linalg.norm(grad_approx + grad)
print(f"Gradient check difference: {difference}")
五大避坑指南
- 隐藏层神经元数量:
- 起始公式:
hidden_size = (input_size + output_size) // 2 -
根据验证集表现调整,通常 2 - 3 倍输入维度效果较好
-
ReLU 死亡神经元:
- 使用 LeakyReLU:
np.where(Z1>0, Z1, 0.01*Z1) -
初始化权重时减小方差:
W1 = np.random.randn(...) * np.sqrt(2/n_input) -
批量归一化实战:
# 在前向传播中加入 self.gamma = np.ones((hidden_size, 1)) self.beta = np.zeros((hidden_size, 1)) Z1_normalized = (Z1 - np.mean(Z1, axis=0)) / np.std(Z1, axis=0) A1 = np.maximum(0, self.gamma * Z1_normalized + self.beta) -
学习率选择:
- 先用 0.001 测试,观察损失曲线
-
如果震荡太大降低 10 倍,下降太慢增大 5 倍
-
维度检查技巧:
- 在每步计算后添加
assert W1.shape == (hidden_size, input_size) - 使用
np.sum(axis=1, keepdims=True)保持维度一致
思考题与扩展
如何实现带动量 (Momentum) 的优化器?核心思路:
# 在类初始化中添加
self.v_dW1 = np.zeros_like(self.W1)
self.v_db1 = np.zeros_like(self.b1)
# 修改权重更新部分
beta = 0.9 # 动量系数
self.v_dW1 = beta * self.v_dW1 + (1-beta) * dW1
self.W1 -= lr * self.v_dW1
通过记录历史梯度方向,能加速收敛并减少震荡。完整实现还需要考虑偏差校正(特别是训练初期)。这个改进能让你的神经网络在复杂任务上训练速度提升 3 - 5 倍!
正文完
