神经网络框架代码详细讲解
本文分部分详细讲解这个神经网络框架的代码,方便理解每个组件的原理和作用。
完整代码如下:
python
import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import make_moons, make_circles, make_blobs
import tkinter as tk
from tkinter import ttk
import matplotlib
matplotlib.use('TkAgg')
from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import matplotlib
matplotlib.rcParams['font.sans-serif'] = ['SimHei', 'DejaVu Sans', 'Arial Unicode MS', 'Microsoft YaHei']
# matplotlib.rcParams['axes.unicode_minus'] = False # 解决负号显示问题
class NeuralNetwork:
"""一个简单但完整的神经网络框架"""
def __init__(self, layers, learning_rate=0.1):
"""
初始化神经网络
参数:
layers: 列表,如[2, 4, 4, 1]表示2个输入,两个4个神经元的隐藏层,1个输出
learning_rate: 学习率
"""
self.layers = layers
self.lr = learning_rate
self.params = {}
self.history = {'loss': [], 'accuracy': []}
# 初始化权重和偏置
np.random.seed(42)
for i in range(1, len(layers)):
# Xavier/Glorot初始化
scale = np.sqrt(2.0 / layers[i - 1])
self.params[f'W{i}'] = np.random.randn(layers[i - 1], layers[i]) * scale
self.params[f'b{i}'] = np.zeros((1, layers[i]))
def relu(self, x):
"""ReLU激活函数"""
return np.maximum(0, x)
def relu_derivative(self, x):
"""ReLU的导数"""
return (x > 0).astype(float)
def sigmoid(self, x):
"""Sigmoid激活函数"""
return 1 / (1 + np.exp(-np.clip(x, -500, 500)))
def sigmoid_derivative(self, x):
"""Sigmoid的导数"""
sig = self.sigmoid(x)
return sig * (1 - sig)
def tanh(self, x):
"""Tanh激活函数"""
return np.tanh(x)
def tanh_derivative(self, x):
"""Tanh的导数"""
return 1 - np.tanh(x) ** 2
def softmax(self, x):
"""Softmax激活函数"""
exp_x = np.exp(x - np.max(x, axis=1, keepdims=True))
return exp_x / np.sum(exp_x, axis=1, keepdims=True)
def forward(self, X, activation='relu'):
"""前向传播"""
cache = {'A0': X}
L = len(self.layers) - 1
# 隐藏层的前向传播
for i in range(1, L):
W = self.params[f'W{i}']
b = self.params[f'b{i}']
Z = np.dot(cache[f'A{i - 1}'], W) + b
# 选择激活函数
if activation == 'relu':
cache[f'Z{i}'] = Z
cache[f'A{i}'] = self.relu(Z)
elif activation == 'sigmoid':
cache[f'Z{i}'] = Z
cache[f'A{i}'] = self.sigmoid(Z)
elif activation == 'tanh':
cache[f'Z{i}'] = Z
cache[f'A{i}'] = self.tanh(Z)
# 输出层(使用sigmoid进行二分类)
W_out = self.params[f'W{L}']
b_out = self.params[f'b{L}']
Z_out = np.dot(cache[f'A{L - 1}'], W_out) + b_out
cache[f'Z{L}'] = Z_out
cache[f'A{L}'] = self.sigmoid(Z_out)
return cache
def compute_loss(self, y_pred, y_true):
"""计算二元交叉熵损失"""
m = y_true.shape[0]
# 添加小值防止log(0)
loss = -np.mean(y_true * np.log(y_pred + 1e-8) +
(1 - y_true) * np.log(1 - y_pred + 1e-8))
return loss
def backward(self, cache, y, activation='relu'):
"""反向传播"""
grads = {}
m = y.shape[0]
L = len(self.layers) - 1
# 输出层的梯度
dA = cache[f'A{L}'] - y
grads[f'dW{L}'] = np.dot(cache[f'A{L - 1}'].T, dA) / m
grads[f'db{L}'] = np.sum(dA, axis=0, keepdims=True) / m
# 隐藏层的梯度(反向传播)
for i in reversed(range(1, L)):
# 计算dZ
if activation == 'relu':
dZ = np.dot(dA, self.params[f'W{i + 1}'].T) * self.relu_derivative(cache[f'Z{i}'])
elif activation == 'sigmoid':
dZ = np.dot(dA, self.params[f'W{i + 1}'].T) * self.sigmoid_derivative(cache[f'Z{i}'])
elif activation == 'tanh':
dZ = np.dot(dA, self.params[f'W{i + 1}'].T) * self.tanh_derivative(cache[f'Z{i}'])
# 计算dW和db
grads[f'dW{i}'] = np.dot(cache[f'A{i - 1}'].T, dZ) / m
grads[f'db{i}'] = np.sum(dZ, axis=0, keepdims=True) / m
dA = dZ
return grads
def update_params(self, grads):
"""更新参数(梯度下降)"""
L = len(self.layers) - 1
for i in range(1, L + 1):
self.params[f'W{i}'] -= self.lr * grads[f'dW{i}']
self.params[f'b{i}'] -= self.lr * grads[f'db{i}']
def train(self, X, y, epochs=1000, activation='relu', batch_size=32):
"""训练神经网络"""
n_samples = X.shape[0]
L = len(self.layers) - 1 # 输出层的索引
for epoch in range(epochs):
# 随机打乱数据
indices = np.random.permutation(n_samples)
X_shuffled = X[indices]
y_shuffled = y[indices]
# 小批量训练
for start in range(0, n_samples, batch_size):
end = min(start + batch_size, n_samples)
X_batch = X_shuffled[start:end]
y_batch = y_shuffled[start:end]
# 前向传播
cache = self.forward(X_batch, activation)
# 反向传播
grads = self.backward(cache, y_batch, activation)
# 更新参数
self.update_params(grads)
# 每100个epoch记录一次损失和准确率
if epoch % 100 == 0 or epoch == epochs - 1:
cache = self.forward(X, activation)
y_pred = cache[f'A{L}'] # 修复这里:使用正确的键名
loss = self.compute_loss(y_pred, y)
accuracy = np.mean((y_pred > 0.5) == y)
self.history['loss'].append(loss)
self.history['accuracy'].append(accuracy)
if epoch % 500 == 0:
print(f"Epoch {epoch}: Loss = {loss:.4f}, Accuracy = {accuracy:.4f}")
print(f"训练完成!最终准确率: {self.history['accuracy'][-1]:.4f}")
def predict(self, X, activation='relu'):
"""预测"""
cache = self.forward(X, activation)
L = len(self.layers) - 1
return (cache[f'A{L}'] > 0.5).astype(int), cache[f'A{L}']
def visualize_decision_boundary(self, X, y, resolution=100):
"""可视化决策边界"""
# 创建网格
x_min, x_max = X[:, 0].min() - 0.5, X[:, 0].max() + 0.5
y_min, y_max = X[:, 1].min() - 0.5, X[:, 1].max() + 0.5
xx, yy = np.meshgrid(np.linspace(x_min, x_max, resolution),
np.linspace(y_min, y_max, resolution))
# 预测整个网格
grid_points = np.c_[xx.ravel(), yy.ravel()]
_, probs = self.predict(grid_points)
# 创建图形
fig, axes = plt.subplots(2, 2, figsize=(12, 10))
# 1. 决策边界
ax = axes[0, 0]
probs_grid = probs.reshape(xx.shape)
contour = ax.contourf(xx, yy, probs_grid, levels=20, cmap='RdBu_r', alpha=0.8)
ax.scatter(X[:, 0], X[:, 1], c=y.flatten(), cmap='bwr', edgecolors='k', s=50)
ax.set_xlabel('特征1')
ax.set_ylabel('特征2')
ax.set_title('神经网络决策边界')
plt.colorbar(contour, ax=ax)
# 2. 损失曲线
ax = axes[0, 1]
ax.plot(self.history['loss'], 'b-', linewidth=2)
ax.set_xlabel('Epoch (每100个)')
ax.set_ylabel('损失')
ax.set_title('训练损失曲线')
ax.grid(True, alpha=0.3)
# 3. 准确率曲线
ax = axes[1, 0]
ax.plot(self.history['accuracy'], 'g-', linewidth=2)
ax.set_xlabel('Epoch (每100个)')
ax.set_ylabel('准确率')
ax.set_title('训练准确率曲线')
ax.grid(True, alpha=0.3)
ax.set_ylim([0, 1.1])
# 4. 网络结构示意图
ax = axes[1, 1]
ax.axis('off')
# 绘制网络结构
layer_sizes = self.layers
v_spacing = 1.0 / max(layer_sizes)
h_spacing = 1.0 / len(layer_sizes)
# 绘制神经元
for i, layer_size in enumerate(layer_sizes):
layer_top = v_spacing * (layer_size - 1) / 2.0
for j in range(layer_size):
x = i * h_spacing + 0.1
y = layer_top - j * v_spacing + 0.5
circle = plt.Circle((x, y), radius=0.03, color='blue', alpha=0.6)
ax.add_patch(circle)
# 添加层标签
if j == 0:
layer_name = ['输入层', '隐藏层', '输出层'][min(i, 2)]
if i == len(layer_sizes) - 1 and len(layer_sizes) > 3:
layer_name = '输出层'
ax.text(x, 0.9, f'{layer_name}\n{layer_size}个神经元',
ha='center', fontsize=10)
# 绘制连接线(示例)
for i in range(len(layer_sizes) - 1):
for j in range(layer_sizes[i]):
for k in range(layer_sizes[i + 1]):
x1 = i * h_spacing + 0.1 + 0.03
y1 = (v_spacing * (layer_sizes[i] - 1) / 2.0) - j * v_spacing + 0.5
x2 = (i + 1) * h_spacing + 0.1 - 0.03
y2 = (v_spacing * (layer_sizes[i + 1] - 1) / 2.0) - k * v_spacing + 0.5
ax.plot([x1, x2], [y1, y2], 'gray', alpha=0.2)
ax.set_xlim(0, 1)
ax.set_ylim(0, 1)
ax.set_title('网络结构示意图')
plt.tight_layout()
plt.show()
def get_weights_visualization(self):
"""可视化权重分布"""
num_layers = len(self.layers) - 1
if num_layers == 0:
return
if num_layers == 1:
fig, ax = plt.subplots(figsize=(8, 4))
axes = [ax]
else:
fig, axes = plt.subplots(1, num_layers, figsize=(15, 4))
for i in range(1, num_layers + 1):
ax = axes[i - 1] if num_layers > 1 else axes
weights = self.params[f'W{i}']
# 绘制权重热力图
im = ax.imshow(weights, cmap='coolwarm', aspect='auto')
ax.set_xlabel(f'层{i}输出')
ax.set_ylabel(f'层{i - 1}输入')
ax.set_title(f'权重矩阵 W{i} ({weights.shape[0]}×{weights.shape[1]})')
plt.colorbar(im, ax=ax)
plt.tight_layout()
plt.show()
def simple_demo():
"""简单的演示示例"""
print("=" * 50)
print("神经网络框架演示")
print("=" * 50)
# 1. 创建数据集
print("\n1. 创建月亮数据集...")
X, y = make_moons(n_samples=300, noise=0.2, random_state=42)
y = y.reshape(-1, 1) # 重塑为列向量
# 2. 创建神经网络
print("2. 初始化神经网络...")
nn = NeuralNetwork(layers=[2, 8, 8, 1], learning_rate=0.1)
print(f" 网络结构: {nn.layers}")
print(
f" 总参数数量: {sum([np.prod(nn.params[f'W{i}'].shape) + np.prod(nn.params[f'b{i}'].shape) for i in range(1, len(nn.layers))])}")
# 3. 训练网络
print("\n3. 训练神经网络...")
nn.train(X, y, epochs=2000, activation='relu')
# 4. 预测和评估
predictions, probs = nn.predict(X)
accuracy = np.mean(predictions.flatten() == y.flatten())
print(f"\n4. 模型性能:")
print(f" 训练准确率: {accuracy:.2%}")
# 5. 可视化
print("\n5. 生成可视化...")
nn.visualize_decision_boundary(X, y)
# 6. 显示权重
print("6. 显示权重分布...")
nn.get_weights_visualization()
# 7. 演示前向传播过程
print("\n7. 前向传播示例:")
sample = X[:5] # 前5个样本
print(f" 输入样本形状: {sample.shape}")
cache = nn.forward(sample)
L = len(nn.layers) - 1
print(f" 输出预测值: {cache[f'A{L}'][:5].flatten()}")
print(f" 真实标签: {y[:5].flatten()}")
return nn
# 运行演示
if __name__ == "__main__":
# 方式1: 运行简单演示
print("运行简单演示...")
model = simple_demo()
# 方式2: 运行GUI应用 (取消注释以下行)
# print("\n\n启动GUI应用...")
# app = NeuralNetworkGUI()
# app.run()
一、整体架构概览
这个神经网络框架实现了:
- 完整的前向传播
- 完整的反向传播(自动微分)
- 多种激活函数
- 梯度下降优化
- 可视化工具
框架的核心是 通过数学公式(矩阵运算)实现数据变换,通过梯度下降最小化损失函数。
二、核心数学原理
2.1 神经网络基本公式
神经网络的核心是线性变换 + 非线性激活:
Z[l] = W[l] · A[l-1] + b[l] # 线性变换
A[l] = g(Z[l]) # 非线性激活
其中:
W是权重矩阵b是偏置向量g()是激活函数
2.2 损失函数(Loss Function)
我们使用二元交叉熵损失,用于二分类问题:
Loss = -1/m * Σ[y*log(ŷ) + (1-y)*log(1-ŷ)]
y:真实标签(0或1)ŷ:预测概率(0到1之间)m:样本数量
三、代码逐部分详解
3.1 神经网络类初始化
python
class NeuralNetwork:
def __init__(self, layers, learning_rate=0.1):
self.layers = layers # 网络结构,如[2, 8, 8, 1]
self.lr = learning_rate # 学习率
self.params = {} # 存储权重和偏置
self.history = {'loss': [], 'accuracy': []} # 训练历史
关键点:
layers定义了网络的架构- 输入层:
layers[0]个神经元(对应特征数量) - 输出层:
layers[-1]个神经元(二分类问题为1) - 隐藏层:中间的数字表示神经元数量
3.2 权重初始化(Xavier初始化)
python
# Xavier/Glorot初始化
scale = np.sqrt(2.0 / layers[i-1])
self.params[f'W{i}'] = np.random.randn(layers[i-1], layers[i]) * scale
self.params[f'b{i}'] = np.zeros((1, layers[i]))
数学原理:
- 权重不能全为0,也不能太大或太小
- Xavier初始化:从均值为0、方差为
sqrt(2/n_input)的正态分布中采样 - 这有助于避免梯度消失/爆炸问题
为什么需要随机初始化:
- 如果所有权重相同,所有神经元会学到相同的特征
- 随机性打破对称性,让不同神经元学习不同的特征
3.3 激活函数
ReLU(修正线性单元)
python
def relu(self, x):
return np.maximum(0, x) # 小于0的部分变为0,大于0的部分保持不变
特点:
- 计算简单,速度快
- 解决梯度消失问题(在正区间梯度为1)
- 缺点:负数部分完全失活("死亡ReLU"问题)
Sigmoid
python
def sigmoid(self, x):
return 1 / (1 + np.exp(-np.clip(x, -500, 500)))
数学公式 :σ(x) = 1 / (1 + e^(-x))
特点:
- 将输出压缩到(0, 1)之间
- 常用于二分类问题的输出层
- 缺点:容易导致梯度消失(两端梯度接近0)
Tanh(双曲正切)
python
def tanh(self, x):
return np.tanh(x) # (e^x - e^(-x)) / (e^x + e^(-x))
特点:
- 输出范围(-1, 1),均值为0
- 相比sigmoid,收敛更快
- 同样存在梯度消失问题
3.4 前向传播(Forward Propagation)
python
def forward(self, X, activation='relu'):
cache = {'A0': X} # 缓存每一层的输出
L = len(self.layers) - 1 # 总层数
# 隐藏层的前向传播
for i in range(1, L):
W = self.params[f'W{i}']
b = self.params[f'b{i}']
Z = np.dot(cache[f'A{i-1}'], W) + b # 线性变换
# 激活函数
if activation == 'relu':
cache[f'Z{i}'] = Z
cache[f'A{i}'] = self.relu(Z)
# 输出层(使用sigmoid)
W_out = self.params[f'W{L}']
b_out = self.params[f'b{L}']
Z_out = np.dot(cache[f'A{L-1}'], W_out) + b_out
cache[f'Z{L}'] = Z_out
cache[f'A{L}'] = self.sigmoid(Z_out) # 输出概率
return cache
前向传播流程:
输入X → 线性变换 → 激活 → 线性变换 → 激活 → ... → 输出ŷ
为什么需要缓存:
- 反向传播需要用到前向传播的中间结果
cache存储了每一层的Z(线性输出)和A(激活输出)
3.5 损失计算
python
def compute_loss(self, y_pred, y_true):
m = y_true.shape[0]
# 添加小值防止log(0)
loss = -np.mean(y_true * np.log(y_pred + 1e-8) +
(1 - y_true) * np.log(1 - y_pred + 1e-8))
return loss
为什么加 1e-8:
- 防止
log(0)导致数值错误(无穷大) - 这是一个数值稳定性的技巧
3.6 反向传播(Backpropagation) - 核心!
反向传播是神经网络学习的核心机制 ,它使用链式法则计算梯度。
python
def backward(self, cache, y, activation='relu'):
grads = {}
m = y.shape[0]
L = len(self.layers) - 1
# 1. 输出层的梯度(最关键的一步!)
dA = cache[f'A{L}'] - y # 损失对输出的导数
# 权重和偏置的梯度
grads[f'dW{L}'] = np.dot(cache[f'A{L-1}'].T, dA) / m
grads[f'db{L}'] = np.sum(dA, axis=0, keepdims=True) / m
# 2. 隐藏层的梯度(反向传播)
for i in reversed(range(1, L)):
# 计算dZ(链式法则的关键)
if activation == 'relu':
dZ = np.dot(dA, self.params[f'W{i+1}'].T) * self.relu_derivative(cache[f'Z{i}'])
# 计算dW和db
grads[f'dW{i}'] = np.dot(cache[f'A{i-1}'].T, dZ) / m
grads[f'db{i}'] = np.sum(dZ, axis=0, keepdims=True) / m
dA = dZ # 传递给下一层
return grads
反向传播的数学原理:
对于输出层:
dL/dW[L] = (A[L] - y) · A[L-1].T / m
dL/db[L] = sum(A[L] - y) / m
对于隐藏层(链式法则):
dL/dZ[l] = (dL/dA[l]) * g'(Z[l]) # g'是激活函数的导数
dL/dW[l] = dL/dZ[l] · A[l-1].T / m
dL/db[l] = sum(dL/dZ[l]) / m
链式法则的直观理解:
- 将误差从输出层"反向传播"到输入层
- 每层的梯度 = 后一层的梯度 × 当前层的激活函数导数
3.7 参数更新(梯度下降)
python
def update_params(self, grads):
L = len(self.layers) - 1
for i in range(1, L + 1):
self.params[f'W{i}'] -= self.lr * grads[f'dW{i}']
self.params[f'b{i}'] -= self.lr * grads[f'db{i}']
梯度下降公式:
W = W - η * ∂L/∂W
b = b - η * ∂L/∂b
其中 η 是学习率,控制每次更新的步长。
3.8 训练循环
python
def train(self, X, y, epochs=1000, activation='relu', batch_size=32):
n_samples = X.shape[0]
L = len(self.layers) - 1
for epoch in range(epochs):
# 随机打乱数据
indices = np.random.permutation(n_samples)
X_shuffled = X[indices]
y_shuffled = y[indices]
# 小批量训练(Mini-batch)
for start in range(0, n_samples, batch_size):
end = min(start + batch_size, n_samples)
X_batch = X_shuffled[start:end]
y_batch = y_shuffled[start:end]
# 前向传播
cache = self.forward(X_batch, activation)
# 反向传播
grads = self.backward(cache, y_batch, activation)
# 更新参数
self.update_params(grads)
训练的关键要素:
-
随机打乱(Shuffling):
- 防止模型学习到数据的顺序信息
- 提高训练稳定性
-
小批量梯度下降(Mini-batch Gradient Descent):
- 批量大小(batch_size)是超参数
- 太大:内存需求高,收敛慢但稳定
- 太小:噪声大,收敛快但不稳定
- 常用值:32, 64, 128
-
学习率(Learning Rate):
- 控制参数更新的步长
- 太大:可能跳过最优解,甚至发散
- 太小:收敛太慢,可能陷入局部最优
3.9 预测函数
python
def predict(self, X, activation='relu'):
cache = self.forward(X, activation)
L = len(self.layers) - 1
return (cache[f'A{L}'] > 0.5).astype(int), cache[f'A{L}']
为什么阈值是0.5:
- 对于二分类问题,sigmoid输出在0-1之间
- 大于0.5预测为正类(1),否则为负类(0)
3.10 可视化工具
决策边界可视化
python
def visualize_decision_boundary(self, X, y, resolution=100):
# 创建网格点
xx, yy = np.meshgrid(np.linspace(x_min, x_max, resolution),
np.linspace(y_min, y_max, resolution))
# 预测整个网格
grid_points = np.c_[xx.ravel(), yy.ravel()]
_, probs = self.predict(grid_points)
原理:
- 在特征空间创建密集的网格点
- 用训练好的模型预测每个点的类别
- 用颜色表示预测概率,形成决策边界
网络结构可视化
python
# 绘制神经元连接
for i in range(len(layer_sizes) - 1):
for j in range(layer_sizes[i]):
for k in range(layer_sizes[i + 1]):
ax.plot([x1, x2], [y1, y2], 'gray', alpha=0.2)
这展示了神经网络实际上是一个复杂的加权连接图。
四、神经网络学习过程的直观解释
4.1 训练过程的三个阶段
-
初始化阶段:
python# 随机初始化权重 np.random.randn(layers[i-1], layers[i]) * scale- 决策边界是随机的
- 准确率接近50%(随机猜测)
-
学习阶段:
Epoch 0: Loss = 0.6931, Accuracy = 0.5000 Epoch 500: Loss = 0.4521, Accuracy = 0.7867 Epoch 1000: Loss = 0.2103, Accuracy = 0.9100- 损失逐渐下降
- 准确率逐渐上升
- 决策边界逐渐拟合数据分布
-
收敛阶段:
Epoch 2000: Loss = 0.1205, Accuracy = 0.9533- 损失基本不再下降
- 准确率趋于稳定
- 决策边界基本确定
4.2 神经网络如何"学习"
- 前向传播:计算预测值
- 计算损失:比较预测值和真实值
- 反向传播:计算每个参数的梯度(误差贡献)
- 梯度下降:沿着梯度反方向更新参数
类比:
- 像一个人蒙着眼睛爬山(寻找最高点)
- 每次用小棍子探周围的地面(计算梯度)
- 向最陡的方向走一步(梯度下降)
- 重复直到山顶(最小化损失)
五、关键概念总结
5.1 神经网络的核心思想
python
# 整个神经网络可以简化为:
def neural_network(X):
# 多层变换
for layer in layers:
X = activation(dot(X, W) + b)
return X
本质:通过多层非线性变换,将输入数据映射到目标输出。
5.2 为什么需要深度?
-
特征层次结构:
- 浅层学习简单特征(边缘、颜色)
- 深层组合简单特征为复杂特征(形状、物体)
-
表示能力:
- 理论证明:深层网络比浅层网络有更强的表示能力
- 可以用更少的参数表示更复杂的函数
5.3 超参数的选择
python
# 常见的超参数
layers = [2, 16, 16, 1] # 网络结构
learning_rate = 0.1 # 学习率
epochs = 2000 # 训练轮数
batch_size = 32 # 批量大小
activation = 'relu' # 激活函数
调参经验:
- 先从简单网络开始
- 学习率通常尝试0.1, 0.01, 0.001
- 增加网络深度/宽度可以提高性能,但也可能过拟合
- 使用验证集选择最佳超参数
六、与Transformer的联系
虽然这个框架比Transformer简单得多,但核心思想相通:
- 都是参数化函数:通过权重矩阵实现数据变换
- 都使用梯度下降:通过反向传播优化参数
- 都有前向传播:输入→变换→输出
- 目标都是最小化损失函数
主要区别:
- 这个框架使用全连接层,Transformer使用自注意力层
- 这个框架用于简单分类,Transformer用于序列任务
- Transformer有更复杂的架构(编码器-解码器、多头注意力等)
七、扩展思考
7.1 如何改进这个框架?
-
添加优化器:
python# 目前使用普通梯度下降 # 可以添加Adam、RMSprop等 -
添加正则化:
python# L2正则化:防止过拟合 loss += lambda * sum(W**2) -
添加批归一化:
python# 加速训练,提高稳定性 X_normalized = (X - mean) / sqrt(var + epsilon)
7.2 从框架到深度学习库
现代深度学习库(PyTorch、TensorFlow)的核心组件:
- 张量(Tensor):多维数组,支持GPU加速
- 自动微分(Autograd):自动计算梯度
- 计算图(Computation Graph):记录运算过程
- 优化器(Optimizer):各种梯度下降变体
- 层(Layer):预定义的网络组件
我们这个简单框架包含了所有这些核心概念的简化版本。
八、实践建议
-
自己动手修改:
- 尝试不同的激活函数
- 调整网络结构
- 可视化权重变化
-
观察训练过程:
- 注意损失曲线的变化
- 观察决策边界的形成过程
- 理解梯度下降的实际效果
-
思考深度学习本质:
- 神经网络只是复杂的数学函数
- 训练就是寻找函数的最佳参数
- 所有深度学习模型都遵循这个基本范式
这个框架虽然简单,但包含了深度学习的核心思想。通过理解和修改这个代码,你可以真正掌握神经网络的工作原理,而不仅仅是调用库函数。