共计 2248 个字符,预计需要花费 6 分钟才能阅读完成。
为什么需要神经网络?
传统机器学习方法依赖手工设计特征(如 SIFT、HOG),但面对图像、语音等高维数据时存在明显局限:

- 特征工程成本高,需要领域专业知识
- 难以捕捉数据的层次化特征(如从边缘到纹理再到物体的视觉特征)
- 对非线性关系的表达能力有限
神经网络通过多层非线性变换自动学习特征表示。以一个 3 层全连接网络为例,其前向传播过程可表示为:
$$
\begin{aligned}
z^{[1]} &= W^{[1]}x + b^{[1]} \
a^{[1]} &= \sigma(z^{[1]}) \
z^{[2]} &= W^{[2]}a^{[1]} + b^{[2]} \
a^{[2]} &= \text{softmax}(z^{[2]})
\end{aligned}
$$
反向传播的数学推导
以交叉熵损失函数为例,对第 2 层权重 $W^{[2]}$ 的梯度计算过程如下:
$$
\frac{\partial L}{\partial W^{[2]}} = \frac{\partial L}{\partial a^{[2]}} \cdot \frac{\partial a^{[2]}}{\partial z^{[2]}} \cdot \frac{\partial z^{[2]}}{\partial W^{[2]}} = (a^{[2]} – y) \cdot a^{[1]T}
$$
对第 1 层权重 $W^{[1]}$ 的梯度需要通过链式法则回溯:
$$
\frac{\partial L}{\partial W^{[1]}} = \frac{\partial L}{\partial a^{[2]}} \cdot \frac{\partial a^{[2]}}{\partial z^{[2]}} \cdot \frac{\partial z^{[2]}}{\partial a^{[1]}} \cdot \frac{\partial a^{[1]}}{\partial z^{[1]}} \cdot \frac{\partial z^{[1]}}{\partial W^{[1]}}
$$
Python 实现详解
网络初始化
import numpy as np
class NeuralNetwork:
def __init__(self, input_size, hidden_size, output_size):
# Xavier 初始化
self.W1 = np.random.randn(hidden_size, input_size) * np.sqrt(1/input_size)
self.b1 = np.zeros((hidden_size, 1))
self.W2 = np.random.randn(output_size, hidden_size) * np.sqrt(1/hidden_size)
self.b2 = np.zeros((output_size, 1))
前向传播实现
def forward(self, x):
self.z1 = np.dot(self.W1, x) + self.b1
self.a1 = sigmoid(self.z1)
self.z2 = np.dot(self.W2, self.a1) + self.b2
self.a2 = softmax(self.z2)
return self.a2
反向传播核心
def backward(self, x, y, learning_rate, lambda_l2=0.01):
m = x.shape[1] # 样本数量
# 输出层梯度
dz2 = self.a2 - y
dW2 = (1/m) * np.dot(dz2, self.a1.T) + (lambda_l2/m)*self.W2
db2 = (1/m) * np.sum(dz2, axis=1, keepdims=True)
# 隐藏层梯度
dz1 = np.dot(self.W2.T, dz2) * sigmoid_derivative(self.a1)
dW1 = (1/m) * np.dot(dz1, x.T) + (lambda_l2/m)*self.W1
db1 = (1/m) * np.sum(dz1, axis=1, keepdims=True)
# 参数更新
self.W2 -= learning_rate * dW2
self.b2 -= learning_rate * db2
self.W1 -= learning_rate * dW1
self.b1 -= learning_rate * db1
关键优化技巧
解决梯度消失问题
- 权重初始化 :Xavier 初始化根据每层神经元数量调整初始权重范围
W = np.random.randn(fan_out, fan_in) * sqrt(2/(fan_in + fan_out))
- 激活函数选择 :ReLU 及其变体(LeakyReLU, ELU)能有效缓解梯度消失
def relu(x):
return np.maximum(0, x)
学习率调整策略
- 初始学习率设为 0.1,每 10 个 epoch 衰减 10%
- 使用动量加速(momentum)或自适应方法(Adam)
learning_rate = initial_lr * (0.9 ** (epoch // 10))
MNIST 实验对比
在 60000 张 28×28 手写数字图像上测试:
| 激活函数 | 测试准确率 | 训练时间 |
|---|---|---|
| Sigmoid | 92.3% | 38s/epoch |
| ReLU | 97.1% | 22s/epoch |
扩展思考
尝试实现卷积层的反向传播:
- 将卷积操作转换为 Toeplitz 矩阵乘法
- 对输入数据求导时需进行 full 模式的卷积
- 对卷积核求导时需执行 valid 模式的卷积
完整代码已开源在 GitHub 仓库(示例链接),包含数据预处理、训练循环和可视化模块。建议读者尝试添加 BatchNorm 层并观察对训练速度的影响。
