共计 2703 个字符,预计需要花费 7 分钟才能阅读完成。
神经网络已成为计算机视觉、自然语言处理等领域的核心工具,能够自动学习数据特征并实现端到端的复杂映射。其核心价值在于:1) 通过多层非线性变换提取高阶特征;2) 对图像 / 文本等非结构化数据具有强大表征能力;3) 在 GPU 加速下可处理海量数据。下面我们从最基础的人工神经元开始,逐步解析神经网络的实现原理。

人工神经元模型
单个神经元 (Neuron) 是神经网络的基本单元,其数学表示为:
$$
z = \sum_{i=1}^n w_i x_i + b
$$
$$
a = \sigma(z)
$$
其中:
– $x_i$: 输入特征(input features)
– $w_i$: 对应权重(weights)
– $b$: 偏置项(bias)
– $\sigma$: 激活函数(activation function)
图示说明:
x₁ ──── w₁ ────\
x₂ ──── w₂ ───── (Σ + b) ── σ ── a
... /
xₙ ──── wₙ ────
前向传播与反向传播
前向传播(Forward Propagation)
对于 L 层神经网络,前向计算流程为:
- 输入层:$a^{[0]} = X$
- 隐藏层:$z^{[l]} = W^{[l]}a^{[l-1]} + b^{[l]}$
- 激活层:$a^{[l]} = \sigma(z^{[l]})$
- 输出层:$\hat{y} = a^{[L]}$
反向传播(Backpropagation)
通过链式法则计算梯度:
- 输出层误差:$\delta^{[L]} = \frac{\partial \mathcal{L}}{\partial a^{[L]}} \odot \sigma'(z^{[L]})$
- 隐藏层误差:$\delta^{[l]} = (W^{[l+1]T}\delta^{[l+1]}) \odot \sigma'(z^{[l]})$
- 参数梯度:
$\frac{\partial \mathcal{L}}{\partial W^{[l]}} = \delta^{[l]}a^{[l-1]T}$
$\frac{\partial \mathcal{L}}{\partial b^{[l]}} = \delta^{[l]}}$
Python 实现
import numpy as np
class NeuralNetwork:
def __init__(self, layer_dims, activation='relu'):
"""
初始化网络参数
:param layer_dims: 各层神经元数量,如[784, 128, 10]
:param activation: 激活函数类型
"""
self.params = {}
self.activation = activation
# He 初始化
for l in range(1, len(layer_dims)):
self.params[f'W{l}'] = np.random.randn(layer_dims[l], layer_dims[l-1]) * np.sqrt(2/layer_dims[l-1])
self.params[f'b{l}'] = np.zeros((layer_dims[l], 1))
def _activate(self, z):
"""激活函数实现"""
if self.activation == 'sigmoid':
return 1 / (1 + np.exp(-z))
elif self.activation == 'relu':
return np.maximum(0, z)
def _activate_derivative(self, a):
"""激活函数导数"""
if self.activation == 'sigmoid':
return a * (1 - a)
elif self.activation == 'relu':
return (a > 0).astype(float)
def forward(self, X):
"""前向传播"""
self.cache = {'A0': X}
L = len(self.params) // 2
for l in range(1, L+1):
W = self.params[f'W{l}']
b = self.params[f'b{l}']
Z = W.dot(self.cache[f'A{l-1}']) + b
self.cache[f'Z{l}'] = Z
self.cache[f'A{l}'] = self._activate(Z)
return self.cache[f'A{L}']
def backward(self, Y, learning_rate=0.01):
"""反向传播"""
L = len(self.params) // 2
m = Y.shape[1]
grads = {}
# 输出层误差
dZ = self.cache[f'A{L}'] - Y
grads[f'dW{L}'] = dZ.dot(self.cache[f'A{L-1}'].T) / m
grads[f'db{L}'] = np.sum(dZ, axis=1, keepdims=True) / m
# 隐藏层误差
for l in reversed(range(1, L)):
dA = self.params[f'W{l+1}'].T.dot(dZ)
dZ = dA * self._activate_derivative(self.cache[f'A{l}'])
grads[f'dW{l}'] = dZ.dot(self.cache[f'A{l-1}'].T) / m
grads[f'db{l}'] = np.sum(dZ, axis=1, keepdims=True) / m
# 参数更新
for l in range(1, L+1):
self.params[f'W{l}'] -= learning_rate * grads[f'dW{l}']
self.params[f'b{l}'] -= learning_rate * grads[f'db{l}']
实践建议
梯度消失问题
当使用 Sigmoid 激活函数时,其导数最大值为 0.25,多层连乘会导致梯度指数级减小。解决方案:
- 使用 ReLU 及其变体(LeakyReLU, PReLU)
- 添加 Batch Normalization 层
- 残差连接(ResNet)
优化算法选择
| 算法 | 优点 | 缺点 |
|---|---|---|
| SGD | 简单 | 容易陷入局部最优 |
| Momentum | 加速收敛 | 需调整动量参数 |
| Adam | 自适应学习率 | 内存占用较大 |
推荐初始学习率设置:
– SGD: 0.01-0.1
– Adam: 0.001-0.0001
思考题
- 如何利用 NumPy 的广播机制实现批量样本的并行计算?
- Xavier 初始化与 Kaiming 初始化分别适用于什么场景?
- Dropout 和 L2 正则化哪种更适合处理过拟合问题?为什么?
通过本文的实践,读者应能掌握神经网络的核心原理与实现方法。建议在 MNIST 或 CIFAR-10 等标准数据集上测试代码效果,逐步调整网络深度和超参数以获得更好性能。
正文完
发表至: 未分类
近三天内
