标签: #深度学习 #激活函数 #梯度下降 #反向传播 #过拟合
学习周期:2 天 | 核心目标:理解深度学习核心概念,掌握激活函数、梯度下降原理及过拟合应对方法
6.2 深度学习基础概念
深度学习是机器学习的一个子集,基于多层神经网络自动学习数据的层次化特征表示。本章解释神经网络为什么能训练、为什么能收敛、为什么会出错的核心理论,是看懂所有模型的基础。
6.2.1 激活函数
激活函数引入非线性,使神经网络能够拟合复杂函数。没有激活函数,多层线性层等价于单层线性变换。
1. Sigmoid
- 公式:\sigma(x) = \frac{1}{1+e^{-x}}
- 输出范围:(0, 1)
- 特点:平滑、可导,但存在梯度饱和(两端梯度接近0),导致梯度消失
- 适用:二分类输出层(配合 BCELoss)
2. Tanh(双曲正切)
- 公式:\tanh(x) = \frac{e^x - e^{-x}}{e^x + e^{-x}}
- 输出范围:(-1, 1)
- 特点:零中心化(优于 Sigmoid),但仍有梯度饱和问题
3. ReLU(Rectified Linear Unit)——隐藏层默认首选
- 公式:\text{ReLU}(x) = \max(0, x)
- 优点:计算简单、非饱和(正区间梯度恒为1),缓解梯度消失,收敛快
- 缺点:负区间梯度为0,可能导致“神经元死亡”(Dead ReLU)
- 变种:Leaky ReLU、ELU
4. Softmax
- 公式:\text{Softmax}(z_i) = \frac{e^{z_i}}{\sum_{j} e^{z_j}}
- 特点:将 logits 转换为概率分布(总和为1),用于多分类输出层
- 常与
CrossEntropyLoss搭配(该损失内部包含 Softmax)
代码示例(绘制激活函数曲线)
import torch import torch.nn.functional as F import matplotlib.pyplot as plt x = torch.linspace(-5, 5, 100) y_sigmoid = torch.sigmoid(x) y_tanh = torch.tanh(x) y_relu = F.relu(x) plt.plot(x, y_sigmoid, label='Sigmoid') plt.plot(x, y_tanh, label='Tanh') plt.plot(x, y_relu, label='ReLU') plt.legend() plt.grid(True) plt.title('常用激活函数') plt.show() # Softmax 示例 logits = torch.tensor([2.0, 1.0, 0.1]) probs = F.softmax(logits, dim=0) print(probs) # tensor([0.6590, 0.2424, 0.0986])6.2.2 梯度下降与反向传播
梯度下降(Gradient Descent)
- 目标:最小化损失函数 L(\theta)
- 更新规则:\theta \leftarrow \theta - \eta \cdot \nabla_\theta L(\theta)
- \eta:学习率(learning rate)
# 手动演示梯度下降(一元函数) def f(x): return x**2 + 2*x + 1 def grad_f(x): return 2*x + 2 x = 5.0 lr = 0.1 for _ in range(20): x = x - lr * grad_f(x) print(f"x={x:.4f}, f(x)={f(x):.4f}") # 最终 x 趋近于 -1(函数最小值点)反向传播(Backpropagation)
- 利用链式法则从输出层向输入层逐层计算梯度
- PyTorch 自动微分机制封装了反向传播:
loss.backward()
训练流程直观理解:
- 前向传播:计算预测值 → 计算 loss
- 反向传播:计算每个参数对 loss 的影响程度(梯度)
- 优化器:按梯度更新参数
# PyTorch 自动反向传播 x = torch.tensor([2.0], requires_grad=True) y = x ** 2 + 3*x + 1 y.backward() print(x.grad) # 7.06.2.3 批量大小(Batch)、迭代周期(Epoch)、学习率
| 概念 | 定义 | 典型值 | 影响 |
|---|---|---|---|
| Batch | 一次前向/后向传播使用的样本数 | 32, 64, 128, 256 | 大 batch 训练稳定但内存大;小 batch 噪声大但泛化好 |
| Epoch | 遍历完整个训练集一次 | 10, 50, 100 | 欠拟合需增加 epoch;过拟合需减少或加正则化 |
| 学习率(LR) | 参数更新的步长 | 0.001, 0.01 | 过大震荡不收敛;过小收敛慢 |
from torch.utils.data import DataLoader, TensorDataset # 模拟数据 X = torch.randn(1000, 10) y = torch.randint(0, 2, (1000,)) dataset = TensorDataset(X, y) # 设置 batch_size batch_size = 64 dataloader = DataLoader(dataset, batch_size=batch_size, shuffle=True) # 一个 epoch 的迭代次数 = ceil(样本数 / batch_size) print(f"每个 epoch 有 {len(dataloader)} 个 batch") # 学习率调整策略(学习率衰减) optimizer = torch.optim.SGD(model.parameters(), lr=0.1) scheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size=30, gamma=0.1)6.2.4 过拟合应对方法
过拟合:模型在训练集上表现很好,但在验证/测试集上表现差。原因:模型过于复杂、数据不足、噪声过多。
1. Dropout
- 原理:训练时随机丢弃一部分神经元(输出置0),迫使网络学习冗余表示,防止共适应。
- 实现:
nn.Dropout(p),其中p是丢弃概率(常用 0.5) - 注意:训练时开启(
model.train()),测试时自动关闭(model.eval())
class NetWithDropout(nn.Module): def __init__(self): super().__init__() self.fc1 = nn.Linear(784, 256) self.dropout = nn.Dropout(0.5) self.fc2 = nn.Linear(256, 10) def forward(self, x): x = F.relu(self.fc1(x)) x = self.dropout(x) # 训练时随机丢弃 x = self.fc2(x) return x2. Batch Normalization(批归一化)
- 原理:对每个 mini-batch 的输入进行标准化(均值0,方差1),加速收敛、允许更大学习率、有一定正则化效果
- 位置:通常放在全连接层或卷积层之后,激活函数之前
- 实现:
nn.BatchNorm1d(num_features)(全连接) /nn.BatchNorm2d(num_features)(卷积)
class ConvBNNet(nn.Module): def __init__(self): super().__init__() self.conv1 = nn.Conv2d(3, 64, 3) self.bn1 = nn.BatchNorm2d(64) self.conv2 = nn.Conv2d(64, 128, 3) self.bn2 = nn.BatchNorm2d(128) def forward(self, x): x = F.relu(self.bn1(self.conv1(x))) x = F.relu(self.bn2(self.conv2(x))) return x3. 早停(Early Stopping)
- 原理:监控验证集损失,若连续若干 epoch 不再下降则停止训练,防止过拟合
- 实现:保存最佳模型,当
patience次没有改善时停止
best_val_loss = float('inf') patience = 5 counter = 0 for epoch in range(epochs): train_loss = train_one_epoch() val_loss = validate() if val_loss < best_val_loss: best_val_loss = val_loss counter = 0 torch.save(model.state_dict(), 'best_model.pth') else: counter += 1 if counter >= patience: print("Early stopping triggered") break4. 其他正则化方法
- L1/L2 正则化:在损失函数中加入权重的 L1/L2 范数(PyTorch 优化器中
weight_decay即 L2 正则化) - 数据增强:对输入数据做随机变换(旋转、裁剪、翻转等),增加样本多样性
- 降低模型复杂度:减少层数、神经元数量
# L2 正则化(weight decay) optimizer = torch.optim.Adam(model.parameters(), lr=0.001, weight_decay=1e-5) # 数据增强示例(torchvision) from torchvision import transforms transform = transforms.Compose([ transforms.RandomHorizontalFlip(), transforms.RandomRotation(10), transforms.ToTensor(), ])🔥 完整示例:带激活函数 + Dropout + BN 的神经网络
# 导入 PyTorch 核心库 import torch # 导入神经网络模块(用于定义层、模型等) import torch.nn as nn # 导入函数式接口(包含激活函数、损失函数等,这里未直接使用,但通常和 nn 配合) import torch.nn.functional as F from sklearn.datasets import load_iris from sklearn.model_selection import train_test_split from sklearn.preprocessing import StandardScaler # 定义一个更高级的神经网络类,继承自 nn.Module class AdvancedNet(nn.Module): # 构造函数,input_dim: 输入特征数,num_classes: 输出类别数 def __init__(self, input_dim=4, num_classes=3): # 调用父类 nn.Module 的构造函数 super().__init__() # 使用 Sequential 容器按顺序封装网络层 self.layers = nn.Sequential( # 第一层:全连接层,将输入维度 input_dim 映射到 32 个神经元 nn.Linear(input_dim, 32), # 批归一化层(Batch Normalization):对 32 个神经元的输出做标准化,加速收敛并稳定训练 nn.BatchNorm1d(32), # ReLU 激活函数:引入非线性,将所有负值置为 0 nn.ReLU(), # Dropout 层:以 30% 的概率随机将神经元输出置为 0,防止过拟合 nn.Dropout(0.3), # 第二层:全连接层,从 32 维映射到 16 维 nn.Linear(32, 16), # 批归一化层(特征维度为 16) nn.BatchNorm1d(16), # ReLU 激活函数 nn.ReLU(), # Dropout 层,同样 30% 丢弃率 nn.Dropout(0.3), # 输出层:全连接层,将 16 维特征映射到类别数 num_classes(3 个类别,输出原始分数 logits) nn.Linear(16, num_classes) ) # 定义前向传播函数 def forward(self, x): # 将输入 x 依次经过 Sequential 中的各层,并返回最终输出 return self.layers(x) # 加载数据 iris = load_iris() X, y = iris.data, iris.target # 划分训练集和测试集 X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42) # 标准化 scaler = StandardScaler() X_train = scaler.fit_transform(X_train) X_test = scaler.transform(X_test) # 转换为 PyTorch 张量 X_train = torch.tensor(X_train, dtype=torch.float32) y_train = torch.tensor(y_train, dtype=torch.long) # 实例化模型(使用默认参数:输入维度 4,输出类别 3) model = AdvancedNet() # 定义损失函数:交叉熵损失(适用于多分类,内部会执行 Softmax) criterion = nn.CrossEntropyLoss() # 定义优化器:Adam 优化器,学习率为 1e-3,更新模型所有参数 optimizer = torch.optim.Adam(model.parameters(), lr=1e-3) # 训练循环:共训练 100 个 epoch(假设 X_train, y_train 已经事先定义好) for epoch in range(100): # 将模型设置为训练模式(影响 Dropout 和 BatchNorm 的行为) model.train() # 清除之前累积的梯度 optimizer.zero_grad() # 前向传播:将训练数据 X_train 输入模型,得到预测输出 outputs = model(X_train) # 计算损失:比较预测 outputs 和真实标签 y_train loss = criterion(outputs, y_train) # 反向传播:自动计算损失相对于所有可训练参数的梯度 loss.backward() # 参数更新:根据梯度执行一步优化(Adam 算法) optimizer.step() print(f"Epoch {epoch+1}, Loss: {loss.item():.4f}") print("训练完成")📚 学习资料(Obsidian 可直接收藏)
官方文档
PyTorch 激活函数
PyTorch 正则化层
Batch Normalization 原论文中文教程
深度学习中的激活函数
反向传播算法详解
Dropout 与 BatchNorm 对比视频推荐
3Blue1Brown - 反向传播
李宏毅 - 梯度下降优化
🎯 学习建议(2 天计划)
- 第 1 天:理解激活函数(手动绘制曲线),掌握梯度下降与反向传播的数学原理,用 PyTorch 验证自动微分。
- 第 2 天:实践 batch、epoch、learning rate 对训练的影响;在简单模型上添加 Dropout 和 BatchNorm,观察过拟合改善情况。
✅ 核心要点总结
- 激活函数:隐藏层用 ReLU(默认首选),多分类输出层用 Softmax。
- 训练流程:前向传播 → 计算 loss → 反向传播 → 更新参数(梯度下降)。
- 关键超参数:学习率(步长)、Epoch(轮数)、Batch(批量大小)。
- 过拟合应对:Dropout、Batch Normalization、早停、数据增强、正则化。
- 所有深度学习模型都遵循这套原理,理解本章是看懂一切模型的基础。
练习题(自测)
- 手动实现 Sigmoid 和 ReLU 函数(不使用 torch 内置),并绘制它们的导数曲线。
- 使用 PyTorch 构建一个带有两个隐藏层(每层 128 个神经元)的 MLP,分别使用 ReLU、Sigmoid 和 Tanh 激活函数,在 MNIST 上对比收敛速度和准确率。
- 在训练循环中实现早停机制,并保存验证集最佳模型。
- 对比有无 Dropout(p=0.5)时模型在测试集上的表现,分析过拟合程度。
- 尝试不同 batch size(16, 64, 256),观察训练时间与收敛效果的差异。