本文基于 Neural Networks: Zero to Hero 系列教程整理,系统介绍神经网络从基础到实践的知识点。

神经网络基础

什么是神经网络?

神经网络(Neural Network)是一种受生物神经元启发的计算模型,由大量相互连接的节点(神经元)组成,能够通过学习和训练来识别模式、进行分类和预测。

神经网络的核心思想:

  • 模拟人脑神经元的工作方式
  • 通过大量简单的计算单元组合实现复杂功能
  • 能够从数据中自动学习特征和模式

神经网络的应用:

  • 图像识别和分类
  • 自然语言处理
  • 语音识别
  • 推荐系统
  • 游戏 AI
  • 自动驾驶

神经元模型

生物神经元 vs 人工神经元:

生物神经元:

  • 树突接收信号
  • 细胞体处理信号
  • 轴突传递信号
  • 突触连接其他神经元

人工神经元(感知机):

  • 输入:x₁, x₂, …, xₙ
  • 权重:w₁, w₂, …, wₙ
  • 偏置:b
  • 激活函数:f
  • 输出:y = f(Σwᵢxᵢ + b)

数学表示:

1
y = f(∑(wᵢ × xᵢ) + b)

其中:

  • xᵢ:输入特征
  • wᵢ:权重
  • b:偏置
  • f:激活函数

感知机(Perceptron)

单层感知机

单层感知机是最简单的神经网络,只能解决线性可分问题。

结构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
import numpy as np

class Perceptron:
def __init__(self, learning_rate=0.01, n_iterations=1000):
self.learning_rate = learning_rate
self.n_iterations = n_iterations
self.weights = None
self.bias = None

def fit(self, X, y):
"""训练感知机"""
n_samples, n_features = X.shape

# 初始化权重和偏置
self.weights = np.zeros(n_features)
self.bias = 0

# 训练
for _ in range(self.n_iterations):
for idx, x_i in enumerate(X):
# 计算输出
linear_output = np.dot(x_i, self.weights) + self.bias
y_predicted = self.activation(linear_output)

# 更新权重和偏置
update = self.learning_rate * (y[idx] - y_predicted)
self.weights += update * x_i
self.bias += update

def activation(self, x):
"""阶跃激活函数"""
return 1 if x >= 0 else 0

def predict(self, X):
"""预测"""
linear_output = np.dot(X, self.weights) + self.bias
y_predicted = self.activation(linear_output)
return y_predicted

局限性:

  • 只能解决线性可分问题
  • 无法解决 XOR 问题
  • 需要多层网络才能解决非线性问题

多层感知机(MLP)

多层感知机通过添加隐藏层来解决非线性问题。

结构:

1
输入层 → 隐藏层1 → 隐藏层2 → ... → 输出层

前向传播:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import numpy as np

class MLP:
def __init__(self, layers, activation='sigmoid'):
self.layers = layers # [输入维度, 隐藏层1, 隐藏层2, ..., 输出维度]
self.activation = activation
self.weights = []
self.biases = []

# 初始化权重和偏置
for i in range(len(layers) - 1):
w = np.random.randn(layers[i], layers[i+1]) * 0.1
b = np.zeros((1, layers[i+1]))
self.weights.append(w)
self.biases.append(b)

def sigmoid(self, x):
"""Sigmoid 激活函数"""
return 1 / (1 + np.exp(-np.clip(x, -250, 250)))

def sigmoid_derivative(self, x):
"""Sigmoid 导数"""
s = self.sigmoid(x)
return s * (1 - s)

def forward(self, X):
"""前向传播"""
self.activations = [X]
self.z_values = []

for i in range(len(self.weights)):
z = np.dot(self.activations[-1], self.weights[i]) + self.biases[i]
self.z_values.append(z)
a = self.sigmoid(z)
self.activations.append(a)

return self.activations[-1]

反向传播算法(Backpropagation)

反向传播是训练神经网络的核心算法,通过计算梯度来更新权重。

算法原理

步骤:

  1. 前向传播:计算网络输出
  2. 计算损失:比较输出和真实值
  3. 反向传播:计算梯度
  4. 更新权重:使用梯度下降更新参数

数学推导:

对于输出层:

1
δᴸ = (aᴸ - y) ⊙ f'(zᴸ)

对于隐藏层:

1
δˡ = ((Wˡ⁺¹)ᵀ δˡ⁺¹) ⊙ f'(zˡ)

权重梯度:

1
∂C/∂Wˡ = aˡ⁻¹ (δˡ)ᵀ

偏置梯度:

1
∂C/∂bˡ = δˡ

实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
class MLP:
def backward(self, X, y, output):
"""反向传播"""
m = X.shape[0]

# 输出层误差
self.deltas = []
delta = (output - y) * self.sigmoid_derivative(self.z_values[-1])
self.deltas.insert(0, delta)

# 隐藏层误差
for i in range(len(self.weights) - 2, -1, -1):
delta = np.dot(self.deltas[0], self.weights[i+1].T) * \
self.sigmoid_derivative(self.z_values[i])
self.deltas.insert(0, delta)

# 计算梯度
self.dW = []
self.db = []
for i in range(len(self.weights)):
dW = np.dot(self.activations[i].T, self.deltas[i]) / m
db = np.sum(self.deltas[i], axis=0, keepdims=True) / m
self.dW.append(dW)
self.db.append(db)

def update_weights(self, learning_rate):
"""更新权重"""
for i in range(len(self.weights)):
self.weights[i] -= learning_rate * self.dW[i]
self.biases[i] -= learning_rate * self.db[i]

def train(self, X, y, learning_rate=0.01, epochs=1000):
"""训练网络"""
for epoch in range(epochs):
# 前向传播
output = self.forward(X)

# 计算损失
loss = np.mean((output - y) ** 2)

# 反向传播
self.backward(X, y, output)

# 更新权重
self.update_weights(learning_rate)

if epoch % 100 == 0:
print(f"Epoch {epoch}, Loss: {loss:.4f}")

激活函数

激活函数引入非线性,使神经网络能够学习复杂模式。

常用激活函数

1. Sigmoid

1
2
3
4
5
6
def sigmoid(x):
return 1 / (1 + np.exp(-np.clip(x, -250, 250)))

def sigmoid_derivative(x):
s = sigmoid(x)
return s * (1 - s)

特点:

  • 输出范围:(0, 1)
  • 适合二分类输出层
  • 缺点:梯度消失问题

2. Tanh

1
2
3
4
5
def tanh(x):
return np.tanh(x)

def tanh_derivative(x):
return 1 - np.tanh(x) ** 2

特点:

  • 输出范围:(-1, 1)
  • 零中心化
  • 比 Sigmoid 梯度更大

3. ReLU(Rectified Linear Unit)

1
2
3
4
5
def relu(x):
return np.maximum(0, x)

def relu_derivative(x):
return (x > 0).astype(float)

特点:

  • 计算简单,梯度大
  • 解决梯度消失问题
  • 缺点:死亡 ReLU 问题(负值输出为 0)

4. Leaky ReLU

1
2
3
4
5
def leaky_relu(x, alpha=0.01):
return np.where(x > 0, x, alpha * x)

def leaky_relu_derivative(x, alpha=0.01):
return np.where(x > 0, 1, alpha)

特点:

  • 解决死亡 ReLU 问题
  • 负值有小的梯度

5. Softmax

1
2
3
def softmax(x):
exp_x = np.exp(x - np.max(x, axis=1, keepdims=True))
return exp_x / np.sum(exp_x, axis=1, keepdims=True)

特点:

  • 输出概率分布
  • 适合多分类输出层
  • 所有输出和为 1

激活函数对比:

激活函数 优点 缺点 适用场景
Sigmoid 输出范围固定 梯度消失 输出层(二分类)
Tanh 零中心化 梯度消失 隐藏层
ReLU 计算快,梯度大 死亡 ReLU 隐藏层(最常用)
Leaky ReLU 解决死亡 ReLU - 隐藏层
Softmax 概率分布 - 输出层(多分类)

损失函数

损失函数衡量模型预测与真实值的差距。

常用损失函数

1. 均方误差(MSE)

1
2
3
4
5
def mse_loss(y_pred, y_true):
return np.mean((y_pred - y_true) ** 2)

def mse_derivative(y_pred, y_true):
return 2 * (y_pred - y_true) / len(y_true)

适用场景: 回归问题

2. 交叉熵损失(Cross-Entropy)

1
2
3
4
5
6
7
8
def cross_entropy_loss(y_pred, y_true):
# y_pred 是 softmax 输出
epsilon = 1e-15
y_pred = np.clip(y_pred, epsilon, 1 - epsilon)
return -np.mean(np.sum(y_true * np.log(y_pred), axis=1))

def cross_entropy_derivative(y_pred, y_true):
return y_pred - y_true

适用场景: 分类问题(配合 Softmax)

3. 二元交叉熵(Binary Cross-Entropy)

1
2
3
4
def binary_cross_entropy_loss(y_pred, y_true):
epsilon = 1e-15
y_pred = np.clip(y_pred, epsilon, 1 - epsilon)
return -np.mean(y_true * np.log(y_pred) + (1 - y_true) * np.log(1 - y_pred))

适用场景: 二分类问题(配合 Sigmoid)

优化器

优化器决定如何更新权重来最小化损失函数。

梯度下降

1. 批量梯度下降(BGD)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
def gradient_descent(X, y, learning_rate=0.01, epochs=1000):
weights = np.random.randn(X.shape[1], 1)
bias = 0

for epoch in range(epochs):
# 使用全部数据
predictions = X @ weights + bias
error = predictions - y

# 计算梯度
dw = (1 / len(X)) * X.T @ error
db = (1 / len(X)) * np.sum(error)

# 更新参数
weights -= learning_rate * dw
bias -= learning_rate * db

return weights, bias

特点:

  • 使用全部数据
  • 稳定但慢

2. 随机梯度下降(SGD)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
def stochastic_gradient_descent(X, y, learning_rate=0.01, epochs=1000):
weights = np.random.randn(X.shape[1], 1)
bias = 0

for epoch in range(epochs):
for i in range(len(X)):
# 使用单个样本
x_i = X[i:i+1]
y_i = y[i:i+1]

prediction = x_i @ weights + bias
error = prediction - y_i

dw = x_i.T @ error
db = error

weights -= learning_rate * dw
bias -= learning_rate * db

return weights, bias

特点:

  • 使用单个样本
  • 快但不稳定

3. 小批量梯度下降(Mini-batch GD)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
def mini_batch_gradient_descent(X, y, batch_size=32, learning_rate=0.01, epochs=1000):
weights = np.random.randn(X.shape[1], 1)
bias = 0

for epoch in range(epochs):
# 打乱数据
indices = np.random.permutation(len(X))
X_shuffled = X[indices]
y_shuffled = y[indices]

# 分批处理
for i in range(0, len(X), batch_size):
X_batch = X_shuffled[i:i+batch_size]
y_batch = y_shuffled[i:i+batch_size]

predictions = X_batch @ weights + bias
error = predictions - y_batch

dw = (1 / len(X_batch)) * X_batch.T @ error
db = (1 / len(X_batch)) * np.sum(error)

weights -= learning_rate * dw
bias -= learning_rate * db

return weights, bias

特点:

  • 使用小批量数据
  • 平衡速度和稳定性(最常用)

高级优化器

1. 动量(Momentum)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class MomentumOptimizer:
def __init__(self, learning_rate=0.01, momentum=0.9):
self.learning_rate = learning_rate
self.momentum = momentum
self.v_w = None
self.v_b = None

def update(self, weights, biases, dw, db):
if self.v_w is None:
self.v_w = np.zeros_like(weights)
self.v_b = np.zeros_like(biases)

# 更新速度
self.v_w = self.momentum * self.v_w + self.learning_rate * dw
self.v_b = self.momentum * self.v_b + self.learning_rate * db

# 更新参数
weights -= self.v_w
biases -= self.v_b

return weights, biases

2. Adam(Adaptive Moment Estimation)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
class AdamOptimizer:
def __init__(self, learning_rate=0.001, beta1=0.9, beta2=0.999, epsilon=1e-8):
self.learning_rate = learning_rate
self.beta1 = beta1
self.beta2 = beta2
self.epsilon = epsilon
self.m_w = None
self.v_w = None
self.m_b = None
self.v_b = None
self.t = 0

def update(self, weights, biases, dw, db):
if self.m_w is None:
self.m_w = np.zeros_like(weights)
self.v_w = np.zeros_like(weights)
self.m_b = np.zeros_like(biases)
self.v_b = np.zeros_like(biases)

self.t += 1

# 更新一阶矩估计
self.m_w = self.beta1 * self.m_w + (1 - self.beta1) * dw
self.m_b = self.beta1 * self.m_b + (1 - self.beta1) * db

# 更新二阶矩估计
self.v_w = self.beta2 * self.v_w + (1 - self.beta2) * (dw ** 2)
self.v_b = self.beta2 * self.v_b + (1 - self.beta2) * (db ** 2)

# 偏差修正
m_w_corrected = self.m_w / (1 - self.beta1 ** self.t)
m_b_corrected = self.m_b / (1 - self.beta1 ** self.t)
v_w_corrected = self.v_w / (1 - self.beta2 ** self.t)
v_b_corrected = self.v_b / (1 - self.beta2 ** self.t)

# 更新参数
weights -= self.learning_rate * m_w_corrected / (np.sqrt(v_w_corrected) + self.epsilon)
biases -= self.learning_rate * m_b_corrected / (np.sqrt(v_b_corrected) + self.epsilon)

return weights, biases

优化器对比:

优化器 优点 缺点 适用场景
SGD 简单 收敛慢,震荡 小数据集
Momentum 加速收敛 需要调参 一般场景
Adam 自适应学习率 内存占用大 深度学习(最常用)

完整的神经网络实现

完整的 MLP 类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
import numpy as np

class NeuralNetwork:
def __init__(self, layers, activation='sigmoid', learning_rate=0.01):
self.layers = layers
self.activation = activation
self.learning_rate = learning_rate
self.weights = []
self.biases = []

# 初始化权重和偏置
for i in range(len(layers) - 1):
w = np.random.randn(layers[i], layers[i+1]) * np.sqrt(2.0 / layers[i])
b = np.zeros((1, layers[i+1]))
self.weights.append(w)
self.biases.append(b)

def sigmoid(self, x):
return 1 / (1 + np.exp(-np.clip(x, -250, 250)))

def relu(self, x):
return np.maximum(0, x)

def sigmoid_derivative(self, x):
s = self.sigmoid(x)
return s * (1 - s)

def relu_derivative(self, x):
return (x > 0).astype(float)

def activate(self, x):
if self.activation == 'sigmoid':
return self.sigmoid(x)
elif self.activation == 'relu':
return self.relu(x)

def activate_derivative(self, x):
if self.activation == 'sigmoid':
return self.sigmoid_derivative(x)
elif self.activation == 'relu':
return self.relu_derivative(x)

def forward(self, X):
self.activations = [X]
self.z_values = []

for i in range(len(self.weights)):
z = np.dot(self.activations[-1], self.weights[i]) + self.biases[i]
self.z_values.append(z)
a = self.activate(z)
self.activations.append(a)

return self.activations[-1]

def backward(self, X, y, output):
m = X.shape[0]

# 输出层误差
self.deltas = []
delta = (output - y) * self.activate_derivative(self.z_values[-1])
self.deltas.insert(0, delta)

# 隐藏层误差
for i in range(len(self.weights) - 2, -1, -1):
delta = np.dot(self.deltas[0], self.weights[i+1].T) * \
self.activate_derivative(self.z_values[i])
self.deltas.insert(0, delta)

# 计算梯度
self.dW = []
self.db = []
for i in range(len(self.weights)):
dW = np.dot(self.activations[i].T, self.deltas[i]) / m
db = np.sum(self.deltas[i], axis=0, keepdims=True) / m
self.dW.append(dW)
self.db.append(db)

def update_weights(self):
for i in range(len(self.weights)):
self.weights[i] -= self.learning_rate * self.dW[i]
self.biases[i] -= self.learning_rate * self.db[i]

def train(self, X, y, epochs=1000, batch_size=32, verbose=True):
for epoch in range(epochs):
# 打乱数据
indices = np.random.permutation(len(X))
X_shuffled = X[indices]
y_shuffled = y[indices]

# 分批训练
total_loss = 0
for i in range(0, len(X), batch_size):
X_batch = X_shuffled[i:i+batch_size]
y_batch = y_shuffled[i:i+batch_size]

# 前向传播
output = self.forward(X_batch)

# 计算损失
loss = np.mean((output - y_batch) ** 2)
total_loss += loss

# 反向传播
self.backward(X_batch, y_batch, output)

# 更新权重
self.update_weights()

if verbose and epoch % 100 == 0:
avg_loss = total_loss / (len(X) // batch_size)
print(f"Epoch {epoch}, Loss: {avg_loss:.4f}")

def predict(self, X):
return self.forward(X)

使用示例

1
2
3
4
5
6
7
8
9
10
11
12
# 生成示例数据
X = np.random.randn(1000, 10)
y = np.random.randn(1000, 1)

# 创建网络
nn = NeuralNetwork(layers=[10, 64, 32, 1], activation='relu', learning_rate=0.001)

# 训练
nn.train(X, y, epochs=1000, batch_size=32)

# 预测
predictions = nn.predict(X)

正则化技术

正则化防止过拟合,提高模型泛化能力。

1. L1 和 L2 正则化

1
2
3
4
5
6
7
def l2_regularization(weights, lambda_reg):
"""L2 正则化(权重衰减)"""
return lambda_reg * np.sum([np.sum(w ** 2) for w in weights])

def l1_regularization(weights, lambda_reg):
"""L1 正则化(Lasso)"""
return lambda_reg * np.sum([np.sum(np.abs(w)) for w in weights])

在损失函数中添加:

1
2
3
4
5
6
7
def compute_loss_with_regularization(y_pred, y_true, weights, lambda_reg, reg_type='l2'):
mse = np.mean((y_pred - y_true) ** 2)
if reg_type == 'l2':
reg = l2_regularization(weights, lambda_reg)
else:
reg = l1_regularization(weights, lambda_reg)
return mse + reg

2. Dropout

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Dropout:
def __init__(self, dropout_rate=0.5):
self.dropout_rate = dropout_rate
self.mask = None

def forward(self, x, training=True):
if training:
self.mask = np.random.binomial(1, 1 - self.dropout_rate, size=x.shape) / (1 - self.dropout_rate)
return x * self.mask
else:
return x

def backward(self, dout):
return dout * self.mask

3. 批量归一化(Batch Normalization)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
class BatchNormalization:
def __init__(self, epsilon=1e-8):
self.epsilon = epsilon
self.gamma = None
self.beta = None
self.running_mean = None
self.running_var = None

def forward(self, x, training=True):
if training:
mean = np.mean(x, axis=0, keepdims=True)
var = np.var(x, axis=0, keepdims=True)

# 更新运行统计
if self.running_mean is None:
self.running_mean = mean
self.running_var = var
else:
self.running_mean = 0.9 * self.running_mean + 0.1 * mean
self.running_var = 0.9 * self.running_var + 0.1 * var

# 归一化
x_norm = (x - mean) / np.sqrt(var + self.epsilon)
else:
x_norm = (x - self.running_mean) / np.sqrt(self.running_var + self.epsilon)

# 缩放和偏移
if self.gamma is None:
self.gamma = np.ones((1, x.shape[1]))
self.beta = np.zeros((1, x.shape[1]))

return self.gamma * x_norm + self.beta

实践案例

案例1:手写数字识别(MNIST)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 加载数据(需要先下载 MNIST 数据集)
from sklearn.datasets import fetch_openml
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler

# 加载数据
mnist = fetch_openml('mnist_784', version=1)
X, y = mnist.data, mnist.target.astype(int)

# 二值化(简化问题)
X = (X > 127).astype(float)
y = (y == 0).astype(int).reshape(-1, 1) # 识别是否为 0

# 划分数据集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

# 创建网络
nn = NeuralNetwork(layers=[784, 128, 64, 1], activation='relu', learning_rate=0.001)

# 训练
nn.train(X_train, y_train, epochs=100, batch_size=32)

# 评估
predictions = nn.predict(X_test)
accuracy = np.mean((predictions > 0.5).astype(int) == y_test)
print(f"Accuracy: {accuracy:.4f}")

案例2:XOR 问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# XOR 数据集
X = np.array([[0, 0], [0, 1], [1, 0], [1, 1]])
y = np.array([[0], [1], [1], [0]])

# 创建网络(需要隐藏层才能解决 XOR)
nn = NeuralNetwork(layers=[2, 4, 1], activation='sigmoid', learning_rate=0.5)

# 训练
nn.train(X, y, epochs=10000, batch_size=4, verbose=False)

# 预测
predictions = nn.predict(X)
print("Predictions:")
print(predictions)
print("Expected:")
print(y)

总结

核心要点

  1. 神经网络基础

    • 神经元模型
    • 感知机和多层感知机
    • 前向传播
  2. 反向传播算法

    • 梯度计算
    • 链式法则
    • 权重更新
  3. 激活函数

    • Sigmoid、Tanh、ReLU
    • 选择合适的激活函数
  4. 损失函数和优化器

    • MSE、交叉熵
    • SGD、Adam
  5. 正则化

    • L1/L2 正则化
    • Dropout
    • 批量归一化

学习路径建议

  1. 基础阶段

    • 理解神经元和感知机
    • 掌握前向传播
    • 理解激活函数
  2. 进阶阶段

    • 深入理解反向传播
    • 实现完整的 MLP
    • 学习优化器
  3. 实践阶段

    • 解决实际问题
    • 调参和优化
    • 学习正则化技术

下一步学习

  • 卷积神经网络(CNN):图像处理
  • 循环神经网络(RNN):序列数据
  • 注意力机制:Transformer
  • 生成对抗网络(GAN):生成模型
  • 强化学习:决策问题

常见问题与解决方案

梯度消失和梯度爆炸

问题描述:

在深层网络中,梯度在反向传播过程中可能变得非常小(梯度消失)或非常大(梯度爆炸),导致训练困难。

梯度消失的原因:

  • 使用 Sigmoid 或 Tanh 激活函数时,导数最大值小于 1
  • 深层网络中,多个小于 1 的导数相乘,梯度指数级衰减

梯度爆炸的原因:

  • 权重初始化过大
  • 深层网络中,梯度指数级增长

解决方案:

  1. 使用 ReLU 激活函数

    1
    2
    3
    # ReLU 的导数在正区间为 1,不会导致梯度消失
    def relu(x):
    return np.maximum(0, x)
  2. 梯度裁剪(Gradient Clipping)

    1
    2
    3
    4
    5
    6
    7
    def clip_gradients(gradients, max_norm=1.0):
    """裁剪梯度,防止梯度爆炸"""
    total_norm = np.sqrt(sum(np.sum(g**2) for g in gradients))
    if total_norm > max_norm:
    scale = max_norm / total_norm
    gradients = [g * scale for g in gradients]
    return gradients
  3. 残差连接(Residual Connections)

    1
    2
    3
    4
    5
    class ResidualBlock:
    def forward(self, x):
    # 残差连接:y = f(x) + x
    out = self.layer(x)
    return out + x # 跳跃连接
  4. 批量归一化

    1
    2
    # Batch Normalization 可以稳定梯度
    # 见前面的 BatchNormalization 类

权重初始化

好的初始化方法:

  1. Xavier 初始化(Glorot)

    1
    2
    3
    4
    def xavier_init(fan_in, fan_out):
    """Xavier 初始化,适合 Sigmoid 和 Tanh"""
    limit = np.sqrt(6.0 / (fan_in + fan_out))
    return np.random.uniform(-limit, limit, (fan_in, fan_out))
  2. He 初始化

    1
    2
    3
    4
    def he_init(fan_in, fan_out):
    """He 初始化,适合 ReLU"""
    std = np.sqrt(2.0 / fan_in)
    return np.random.randn(fan_in, fan_out) * std
  3. 改进的初始化

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    def initialize_weights(layers, init_type='he'):
    weights = []
    for i in range(len(layers) - 1):
    fan_in = layers[i]
    fan_out = layers[i+1]

    if init_type == 'xavier':
    limit = np.sqrt(6.0 / (fan_in + fan_out))
    w = np.random.uniform(-limit, limit, (fan_in, fan_out))
    elif init_type == 'he':
    std = np.sqrt(2.0 / fan_in)
    w = np.random.randn(fan_in, fan_out) * std
    else:
    w = np.random.randn(fan_in, fan_out) * 0.1

    weights.append(w)
    return weights

初始化方法对比:

方法 适用激活函数 公式
随机初始化 通用 w ~ N(0, 0.01)
Xavier Sigmoid, Tanh w ~ U(-√6/(n_in+n_out), √6/(n_in+n_out))
He ReLU, Leaky ReLU w ~ N(0, √2/n_in)

超参数调优

重要超参数:

  1. 学习率(Learning Rate)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    # 学习率调度
    class LearningRateScheduler:
    def __init__(self, initial_lr=0.01, decay_rate=0.95, decay_step=100):
    self.initial_lr = initial_lr
    self.decay_rate = decay_rate
    self.decay_step = decay_step
    self.step = 0

    def get_lr(self):
    """指数衰减学习率"""
    lr = self.initial_lr * (self.decay_rate ** (self.step // self.decay_step))
    self.step += 1
    return lr

    def step_decay(self, epoch):
    """阶梯衰减"""
    if epoch < 30:
    return 0.01
    elif epoch < 60:
    return 0.001
    else:
    return 0.0001
  2. 批量大小(Batch Size)

  • 小批量(32-128):更频繁的更新,可能更稳定
  • 大批量(256+):更快的训练,但可能陷入局部最优
  1. 网络深度和宽度
  • 深度:更多层,学习更复杂的特征
  • 宽度:每层更多神经元,增加容量

调优策略:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
def hyperparameter_search():
"""网格搜索超参数"""
learning_rates = [0.001, 0.01, 0.1]
batch_sizes = [32, 64, 128]
hidden_sizes = [64, 128, 256]

best_score = -np.inf
best_params = None

for lr in learning_rates:
for bs in batch_sizes:
for hs in hidden_sizes:
# 训练模型
nn = NeuralNetwork(layers=[10, hs, 1], learning_rate=lr)
nn.train(X_train, y_train, batch_size=bs, epochs=100)

# 评估
score = evaluate(nn, X_val, y_val)

if score > best_score:
best_score = score
best_params = {'lr': lr, 'batch_size': bs, 'hidden_size': hs}

return best_params

调试技巧

1. 检查梯度

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
def check_gradients(network, X, y):
"""检查梯度是否正确"""
# 数值梯度
def numerical_gradient(f, x, h=1e-5):
grad = np.zeros_like(x)
it = np.nditer(x, flags=['multi_index'], op_flags=['readwrite'])
while not it.finished:
idx = it.multi_index
old_value = x[idx]

x[idx] = old_value + h
fxh1 = f(x)

x[idx] = old_value - h
fxh2 = f(x)

grad[idx] = (fxh1 - fxh2) / (2 * h)
x[idx] = old_value

it.iternext()
return grad

# 比较数值梯度和解析梯度
output = network.forward(X)
network.backward(X, y, output)

# 检查第一个权重矩阵的梯度
analytical_grad = network.dW[0]
# 计算数值梯度(简化版)
# ...

2. 监控训练过程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
def train_with_monitoring(network, X_train, y_train, X_val, y_val, epochs=1000):
"""训练并监控过拟合"""
train_losses = []
val_losses = []

for epoch in range(epochs):
# 训练
output_train = network.forward(X_train)
train_loss = np.mean((output_train - y_train) ** 2)
network.backward(X_train, y_train, output_train)
network.update_weights()

# 验证
output_val = network.forward(X_val)
val_loss = np.mean((output_val - y_val) ** 2)

train_losses.append(train_loss)
val_losses.append(val_loss)

# 早停(Early Stopping)
if epoch > 100 and val_loss > min(val_losses[-100:]):
print(f"Early stopping at epoch {epoch}")
break

return train_losses, val_losses

3. 可视化

1
2
3
4
5
6
7
8
9
10
11
12
import matplotlib.pyplot as plt

def plot_training_curves(train_losses, val_losses):
"""绘制训练曲线"""
plt.figure(figsize=(10, 6))
plt.plot(train_losses, label='Train Loss')
plt.plot(val_losses, label='Validation Loss')
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.legend()
plt.title('Training and Validation Loss')
plt.show()

高级主题

自编码器(Autoencoder)

自编码器是一种无监督学习模型,用于学习数据的压缩表示。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
class Autoencoder:
def __init__(self, input_dim, encoding_dim):
# 编码器
self.encoder = NeuralNetwork(
layers=[input_dim, 128, 64, encoding_dim],
activation='relu'
)
# 解码器
self.decoder = NeuralNetwork(
layers=[encoding_dim, 64, 128, input_dim],
activation='sigmoid'
)

def encode(self, X):
"""编码:输入 -> 潜在表示"""
return self.encoder.forward(X)

def decode(self, encoded):
"""解码:潜在表示 -> 重构"""
return self.decoder.forward(encoded)

def forward(self, X):
"""前向传播:编码 -> 解码"""
encoded = self.encode(X)
decoded = self.decode(encoded)
return decoded

def train(self, X, epochs=1000):
"""训练自编码器"""
for epoch in range(epochs):
# 重构
reconstructed = self.forward(X)

# 重构误差
loss = np.mean((X - reconstructed) ** 2)

# 反向传播(需要实现)
# ...

if epoch % 100 == 0:
print(f"Epoch {epoch}, Loss: {loss:.4f}")

变分自编码器(VAE)

VAE 在自编码器基础上,学习数据的概率分布。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
class VAE:
def __init__(self, input_dim, latent_dim):
self.latent_dim = latent_dim
# 编码器:输出均值和方差
self.encoder_mean = NeuralNetwork([input_dim, 128, latent_dim])
self.encoder_logvar = NeuralNetwork([input_dim, 128, latent_dim])
# 解码器
self.decoder = NeuralNetwork([latent_dim, 128, input_dim])

def encode(self, X):
"""编码:输出潜在空间的均值和方差"""
mean = self.encoder_mean.forward(X)
logvar = self.encoder_logvar.forward(X)
return mean, logvar

def reparameterize(self, mean, logvar):
"""重参数化技巧"""
std = np.exp(0.5 * logvar)
epsilon = np.random.randn(*std.shape)
return mean + std * epsilon

def decode(self, z):
"""解码"""
return self.decoder.forward(z)

def forward(self, X):
"""前向传播"""
mean, logvar = self.encode(X)
z = self.reparameterize(mean, logvar)
reconstructed = self.decode(z)
return reconstructed, mean, logvar

def loss(self, X, reconstructed, mean, logvar):
"""VAE 损失:重构损失 + KL 散度"""
# 重构损失
recon_loss = np.mean((X - reconstructed) ** 2)

# KL 散度(正则化项)
kl_loss = -0.5 * np.sum(1 + logvar - mean**2 - np.exp(logvar))

return recon_loss + kl_loss

注意力机制基础

注意力机制允许模型关注输入的不同部分。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Attention:
def __init__(self, hidden_dim):
self.hidden_dim = hidden_dim
self.W_q = np.random.randn(hidden_dim, hidden_dim) * 0.1
self.W_k = np.random.randn(hidden_dim, hidden_dim) * 0.1
self.W_v = np.random.randn(hidden_dim, hidden_dim) * 0.1

def forward(self, query, key, value):
"""计算注意力"""
# 计算 Q, K, V
Q = query @ self.W_q
K = key @ self.W_k
V = value @ self.W_v

# 计算注意力分数
scores = Q @ K.T / np.sqrt(self.hidden_dim)

# Softmax
attention_weights = softmax(scores, axis=1)

# 加权求和
output = attention_weights @ V

return output, attention_weights

性能优化

计算优化

1. 向量化操作

1
2
3
4
5
6
7
8
9
10
11
# 慢:循环
def slow_forward(X, weights, bias):
output = np.zeros((X.shape[0], weights.shape[1]))
for i in range(X.shape[0]):
for j in range(weights.shape[1]):
output[i, j] = np.sum(X[i] * weights[:, j]) + bias[j]
return output

# 快:向量化
def fast_forward(X, weights, bias):
return X @ weights + bias

2. 使用 NumPy 优化

1
2
3
4
# 使用 einsum 进行复杂计算
def einsum_example(A, B, C):
# 等价于 np.sum(A[:, :, None] * B[None, :, :] * C[:, None, :], axis=1)
return np.einsum('ij,jk,ik->i', A, B, C)

内存优化

1. 梯度检查点

1
2
3
4
5
6
7
8
9
10
# 不存储所有中间值,需要时重新计算
class CheckpointedNetwork:
def forward(self, X, checkpoint=False):
if checkpoint:
# 只存储关键节点
# 需要时重新计算
pass
else:
# 正常前向传播
pass

2. 混合精度训练

1
2
3
# 使用 float16 减少内存占用
X_float16 = X.astype(np.float16)
# 注意:需要处理精度问题

实践建议

开发流程

  1. 从简单开始

    • 先实现单层网络
    • 逐步增加复杂度
    • 验证每一步
  2. 使用小数据集测试

    • 先用小数据集验证代码正确性
    • 再扩展到完整数据集
  3. 逐步调试

    • 检查前向传播
    • 检查反向传播
    • 检查梯度更新
  4. 监控训练

    • 绘制损失曲线
    • 监控验证集性能
    • 使用早停防止过拟合

常见错误

  1. 维度不匹配

    1
    2
    3
    4
    5
    6
    # 错误
    output = X @ weights # 如果维度不匹配会报错

    # 正确:检查维度
    assert X.shape[1] == weights.shape[0], "维度不匹配"
    output = X @ weights
  2. 忘记转置

    1
    2
    3
    4
    5
    # 错误
    gradient = X @ delta # 维度可能不对

    # 正确
    gradient = X.T @ delta
  3. 激活函数应用错误

    1
    2
    3
    4
    5
    6
    7
    8
    # 错误:在输出层使用错误的激活函数
    output = sigmoid(linear_output) # 对于回归问题可能不合适

    # 正确:根据任务选择
    if task == 'classification':
    output = sigmoid(linear_output)
    elif task == 'regression':
    output = linear_output

总结

核心要点回顾

  1. 神经网络基础

    • 神经元模型和感知机
    • 多层感知机(MLP)
    • 前向传播
  2. 反向传播算法

    • 链式法则
    • 梯度计算
    • 权重更新
  3. 激活函数

    • Sigmoid、Tanh、ReLU
    • 选择合适的激活函数
  4. 损失函数和优化器

    • MSE、交叉熵
    • SGD、Adam
  5. 正则化技术

    • L1/L2 正则化
    • Dropout
    • 批量归一化
  6. 常见问题

    • 梯度消失/爆炸
    • 权重初始化
    • 超参数调优

学习路径

  1. 基础阶段:理解神经元、感知机、前向传播
  2. 进阶阶段:掌握反向传播、实现完整 MLP
  3. 高级阶段:学习正则化、优化技巧、解决实际问题

下一步学习方向

  • 卷积神经网络(CNN):图像识别和处理
  • 循环神经网络(RNN/LSTM):序列数据和自然语言处理
  • Transformer:注意力机制和现代 NLP
  • 生成模型:GAN、VAE、扩散模型
  • 强化学习:决策和游戏 AI

参考资料: