📚 课程目录

第6课:正则化:Dropout与BatchNorm

📖 本课概述

过拟合是深度学习的头号敌人。正则化技术帮助模型在训练集和测试集上都表现良好。本课深入分析Dropout和BatchNorm的原理、实现和效果。

🎯 一、过拟合问题

训练误差 << 测试误差 → 过拟合

原因:模型容量过大,记住训练数据的噪声

解决方案:①更多数据 ②正则化 ③早停 ④模型简化

💧 二、Dropout

2.1 原理

训练时以概率p随机"丢弃"神经元,测试时使用所有神经元但输出乘以(1-p)。

训练:h̃ = m ⊙ h,其中 m ~ Bernoulli(1-p)

测试:h̃ = (1-p) · h

或使用倒置Dropout:训练时除以(1-p),测试时不变

Dropout的效果:①隐式集成多个子网络②减少神经元共适应③提供模型平均效果

2.2 PyTorch验证


import torch
import torch.nn as nn

# Dropout效果验证
torch.manual_seed(42)
X = torch.randn(1000, 50)
y = (X[:, 0] + X[:, 1] > 0).float().unsqueeze(1)

# 有Dropout的模型
class ModelWithDropout(nn.Module):
    def __init__(self, use_dropout=False, p=0.5):
        super().__init__()
        self.use_dropout = use_dropout
        self.net = nn.Sequential(
            nn.Linear(50, 200),
            nn.ReLU(),
            nn.Dropout(p) if use_dropout else nn.Identity(),
            nn.Linear(200, 100),
            nn.ReLU(),
            nn.Dropout(p) if use_dropout else nn.Identity(),
            nn.Linear(100, 1),
            nn.Sigmoid()
        )
    def forward(self, x):
        return self.net(x)

# 训练对比
for use_drop, name in [(False, "无Dropout"), (True, "有Dropout(p=0.5)")]:
    torch.manual_seed(42)
    model = ModelWithDropout(use_dropout=use_drop)
    optimizer = torch.optim.Adam(model.parameters(), lr=0.001)
    loss_fn = nn.BCELoss()
    
    train_losses = []
    test_losses = []
    # Split train/test
    X_train, X_test = X[:800], X[800:]
    y_train, y_test = y[:800], y[800:]
    
    for epoch in range(200):
        model.train()
        optimizer.zero_grad()
        output = model(X_train)
        loss = loss_fn(output, y_train)
        loss.backward()
        optimizer.step()
        
        model.eval()
        with torch.no_grad():
            test_loss = loss_fn(model(X_test), y_test)
        
        if epoch % 50 == 0 or epoch == 199:
            train_losses.append(loss.item())
            test_losses.append(test_loss.item())
    
    gap = [t - tr for tr, t in zip(train_losses, test_losses)]
    print(f"\n{name}:")
    print(f"  训练loss: {' → '.join([f'{l:.4f}' for l in train_losses])}")
    print(f"  测试loss: {' → '.join([f'{l:.4f}' for l in test_losses])}")
    print(f"  过拟合差距: {' → '.join([f'{g:.4f}' for g in gap])}")

# BatchNorm验证
print("\n=== BatchNorm效果 ===")
class ModelWithBN(nn.Module):
    def __init__(self, use_bn=False):
        super().__init__()
        self.net = nn.Sequential(
            nn.Linear(50, 200),
            nn.BatchNorm1d(200) if use_bn else nn.Identity(),
            nn.ReLU(),
            nn.Linear(200, 100),
            nn.BatchNorm1d(100) if use_bn else nn.Identity(),
            nn.ReLU(),
            nn.Linear(100, 1),
        )
    def forward(self, x):
        return self.net(x)

for use_bn, name in [(False, "无BN"), (True, "有BN")]:
    torch.manual_seed(42)
    model = ModelWithBN(use_bn=use_bn)
    optimizer = torch.optim.SGD(model.parameters(), lr=0.01)
    loss_fn = nn.MSELoss()
    y_reg = X[:, :1] * 2 + 1
    
    for epoch in range(100):
        optimizer.zero_grad()
        loss = loss_fn(model(X), y_reg)
        loss.backward()
        optimizer.step()
        if epoch % 25 == 0:
            print(f"  {name} Epoch {epoch}: loss={loss.item():.4f}")
🟢 运行结果 ✅验证通过 无Dropout: 训练loss: 0.6959 → 0.0303 → 0.0021 → 0.0010 → 0.0006 测试loss: 0.6810 → 0.2269 → 0.2454 → 0.2541 → 0.2608 过拟合差距: -0.0149 → 0.1966 → 0.2433 → 0.2531 → 0.2602 有Dropout(p=0.5): 训练loss: 0.7011 → 0.2042 → 0.0545 → 0.0241 → 0.0191 测试loss: 0.6832 → 0.2467 → 0.1870 → 0.1947 → 0.1955 过拟合差距: -0.0178 → 0.0425 → 0.1325 → 0.1705 → 0.1764 === BatchNorm效果 === 无BN Epoch 0: loss=5.5533 无BN Epoch 25: loss=3.7553 无BN Epoch 50: loss=2.4264 无BN Epoch 75: loss=0.8604 有BN Epoch 0: loss=6.2695 有BN Epoch 25: loss=0.5910 有BN Epoch 50: loss=0.2172 有BN Epoch 75: loss=0.1365

🔬 三、Dropout深入分析

3.1 Dropout的理论解释

Dropout可以从多个角度理解:

3.2 Dropout率选择指南

层类型推荐Dropout率说明
输入层0.1-0.2不要丢弃太多输入信息
隐层(小)0.2-0.3小网络不宜过多丢弃
隐层(大)0.3-0.5大网络可以更激进
全连接层0.5标准选择
卷积层很少用卷积参数少,不易过拟合
注意力层0.1Transformer中常用

3.3 Spatial Dropout与DropPath

标准Dropout:随机丢弃单个神经元

Spatial Dropout(nn.Dropout2d):随机丢弃整个通道

→ 适合CNN,因为相邻像素高度相关

DropPath:随机丢弃整个残差块

→ 适合深层网络(如ResNet, ViT)

📖 四、延伸阅读

💡 推荐资源:

📝 练习

练习1:调整Dropout率

分别用p=0.1, 0.3, 0.5, 0.7训练,画出验证准确率随p的变化曲线。

练习2:Spatial Dropout

实现nn.Dropout2d,理解为什么对CNN要用整个通道的Dropout。

练习3:BN的running_mean

打印BatchNorm的running_mean和running_var,理解训练和推理时BN的行为差异。

💡 五、BatchNorm深入理解

5.1 BatchNorm的工作原理

训练时:μ_B = mean(x_B), σ²_B = var(x_B)

标准化:x̂ = (x - μ_B) / √(σ²_B + ε)

缩放平移:y = γx̂ + β (γ和β可学习)

推理时:使用训练时的running_mean和running_var

5.2 BatchNorm为什么有效

三种解释:

5.3 BatchNorm的替代方案

方法归一化维度适用场景对小batch敏感?
BatchNormbatch维度CV(大batch)
LayerNorm特征维度NLP/Transformer
InstanceNorm通道维度风格迁移
GroupNorm通道组CV(小batch)
RMSNorm特征维度LLaMA

🔬 深度拓展:从理论到实践

核心概念网络

理解本课内容需要将其置于更大的知识网络中。每个核心概念都不是孤立的,它们相互关联、相互支撑:

关键洞察与直觉

建立正确的直觉比记住公式更重要:

1. 维度直觉:高维空间中,数据分布与低维直觉截然不同(维度诅咒)

2. 信息瓶颈:神经网络逐层压缩信息,保留任务相关的、丢弃无关的

3. 流形假设:高维数据实际分布在低维流形上,学习即发现流形结构

4. 偏差-方差权衡:简单模型欠拟合(高偏差),复杂模型过拟合(高方差)

与其他课程的关联

本课作为神经网络基础阶段的核心内容,与前后课程紧密关联:

关联课程关联点协同效应
前序课程提供了本课的基础知识循序渐进的理解
后续课程本课内容是后续的基础逐步构建能力
平行课程同一阶段的互补知识多角度深入理解
实战项目综合运用所有知识理论与实践结合

面试常见问题

💡 准备面试时,确保能回答以下问题:

  1. 用简单语言解释本课核心概念
  2. 画出关键算法/结构的流程图
  3. 比较不同方法的优缺点
  4. 举出实际应用场景
  5. 讨论常见误解和注意事项

进阶学习路径

掌握本课内容后,可以通过以下方式继续深入:

🛡️

成就解锁:防过拟合卫士

掌握Dropout和BatchNorm,你已经有了对抗过拟合的利器!