ADVANCE Day44

@浙大疏锦行

📘 Day 44 实战作业 (极速版):ResNet 与 迁移学习

1. 作业综述

核心目标

  1. 迁移学习:学会调用 ImageNet 预训练的 ResNet18 模型,将其知识迁移到 CIFAR-10 任务上。
  2. 策略对比 :亲手实验 冻结骨干 (Linear Probing)解冻微调 (Fine-tuning) 两种策略的效果差异。
  3. 工业级加速 :掌握 混合精度训练 (AMP)分辨率适配 技巧,在保持高精度的同时极大提升训练速度。

涉及知识点

  • ResNet18: 工业界最常用的基准模型。
  • Transfer Learning : pretrained=True, freeze weights.
  • AMP (Automatic Mixed Precision) : torch.cuda.amp.
  • Data Resize: 适配模型输入尺寸。

场景类比

  • 从头训练: 像小学生写作文,词汇量有限,写得慢且水平一般。
  • 迁移学习 : 像大学教授写作文,知识渊博。
    • 冻结: 教授套模板写(只练最后一层),速度极快。
    • 微调: 教授认真推敲每一句话(全网微调),水平最高。

步骤 1:数据准备 (极速配置)

优化策略

  • 分辨率 112x112:比标准的 224x224 少了 75% 的像素,速度提升显著。
  • Pin Memory: 锁页内存,加快数据传输。

任务

  1. 定义预处理管道 (Resize -> 112)。
  2. 加载 CIFAR-10 数据集。
py 复制代码
import torch
import torch.nn as nn
import torch.optim as optim
from torchvision import datasets, transforms, models
from torch.utils.data import DataLoader
from torch.cuda.amp import autocast, GradScaler # 混合精度神器
import time

# --- 1. 极速配置 ---
IMG_SIZE = 112      # 降级分辨率加速 (原版 224)
BATCH_SIZE = 128    # 因为图小了,Batch 可以开大点 (原版 32)
NUM_WORKERS = 2     # 数据加载进程数

# 2. 定义预处理
norm_mean = [0.485, 0.456, 0.406]
norm_std = [0.229, 0.224, 0.225]

train_transform = transforms.Compose([
    transforms.Resize(IMG_SIZE),
    transforms.RandomHorizontalFlip(), # 数据增强
    transforms.ToTensor(),
    transforms.Normalize(norm_mean, norm_std)
])

test_transform = transforms.Compose([
    transforms.Resize(IMG_SIZE),
    transforms.ToTensor(),
    transforms.Normalize(norm_mean, norm_std)
])

# 3. 加载数据 (开启 pin_memory)
print("📥 正在加载数据...")
train_dataset = datasets.CIFAR10(root='./data', train=True, download=True, transform=train_transform)
test_dataset = datasets.CIFAR10(root='./data', train=False, download=True, transform=test_transform)

train_loader = DataLoader(train_dataset, batch_size=BATCH_SIZE, shuffle=True, 
                          num_workers=NUM_WORKERS, pin_memory=True)
test_loader = DataLoader(test_dataset, batch_size=BATCH_SIZE, shuffle=False, 
                         num_workers=NUM_WORKERS, pin_memory=True)

device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
print(f"✅ 数据准备就绪 | 设备: {device} | 输入尺寸: {IMG_SIZE}x{IMG_SIZE}")
复制代码
📥 正在加载数据...
✅ 数据准备就绪 | 设备: cuda | 输入尺寸: 112x112

步骤 2:模型构建工厂

任务

编写一个函数 create_resnet18,灵活控制是否加载预训练权重、是否冻结骨干。

  • Backbone: ResNet18 (去掉最后的全连接层)。
  • Head: 新的全连接层 (输出 10 类)。
py 复制代码
def create_resnet18(pretrained=True, freeze=False):
    """
    创建一个适配 CIFAR-10 的 ResNet18
    """
    # 1. 加载模型
    if pretrained:
        # weights='DEFAULT' 自动下载最新权重
        model = models.resnet18(weights=models.ResNet18_Weights.DEFAULT)
        print(f"🔧 [模型创建] 加载 ImageNet 预训练权重 (Freeze={freeze})...")
    else:
        model = models.resnet18(weights=None)
        print(f"✨ [模型创建] 随机初始化权重...")

    # 2. 冻结骨干 (Backbone)
    if freeze:
        for param in model.parameters():
            param.requires_grad = False
            
    # 3. 替换 Head (这一层默认是可训练的)
    # ResNet18 的 fc 输入特征数是 512
    in_features = model.fc.in_features
    model.fc = nn.Linear(in_features, 10) 
    
    return model

步骤 3:混合精度训练引擎

优化策略

  • 使用 torch.cuda.amp 进行半精度 (FP16) 训练。
  • 显存占用减半,计算速度翻倍,非常适合 RTX 30 系列显卡。

任务

封装 train_one_epochevaluate 函数,集成 AMP 逻辑。

py 复制代码
# 初始化梯度缩放器 (AMP 必备)
scaler = GradScaler()

def train_one_epoch(model, loader, criterion, optimizer):
    model.train()
    running_loss = 0.0
    correct = 0
    total = 0
    
    for inputs, labels in loader:
        inputs, labels = inputs.to(device), labels.to(device)
        
        optimizer.zero_grad()
        
        # --- 核心优化:混合精度上下文 ---
        with autocast():
            outputs = model(inputs)
            loss = criterion(outputs, labels)
        
        # --- 核心优化:缩放梯度反向传播 ---
        scaler.scale(loss).backward()
        scaler.step(optimizer)
        scaler.update()
        
        # 统计
        running_loss += loss.item()
        _, predicted = outputs.max(1)
        total += labels.size(0)
        correct += predicted.eq(labels).sum().item()
        
    return running_loss / len(loader), 100. * correct / total

def evaluate(model, loader, criterion):
    model.eval()
    running_loss = 0.0
    correct = 0
    total = 0
    
    with torch.no_grad():
        for inputs, labels in loader:
            inputs, labels = inputs.to(device), labels.to(device)
            # 测试时不需要 scaler,但可以用 autocast 加速推理
            with autocast():
                outputs = model(inputs)
                loss = criterion(outputs, labels)
            
            running_loss += loss.item()
            _, predicted = outputs.max(1)
            total += labels.size(0)
            correct += predicted.eq(labels).sum().item()
            
    return running_loss / len(loader), 100. * correct / total
复制代码
C:\Users\ADVANCE\AppData\Local\Temp\ipykernel_4824\1229741273.py:2: FutureWarning: `torch.cuda.amp.GradScaler(args...)` is deprecated. Please use `torch.amp.GradScaler('cuda', args...)` instead.
  scaler = GradScaler()

步骤 4:实验 A - 极速冻结训练 (Linear Probing)

场景

只训练最后一层分类器 (Head)。

因为骨干网络参数被锁死,无需计算梯度,速度快到飞起

预期

3 个 Epoch 内,准确率应该能达到 80% - 85%

py 复制代码
print("\n=== 实验 A: 冻结骨干 (只练 Head) ===")
start_time = time.time()

# 1. 创建冻结模型
model_frozen = create_resnet18(pretrained=True, freeze=True).to(device)
criterion = nn.CrossEntropyLoss()

# 2. 优化器 (只优化 fc 层,lr 可以大一点)
optimizer = optim.Adam(model_frozen.fc.parameters(), lr=0.001)

# 3. 训练 3 轮
for epoch in range(3):
    train_loss, train_acc = train_one_epoch(model_frozen, train_loader, criterion, optimizer)
    test_loss, test_acc = evaluate(model_frozen, test_loader, criterion)
    print(f"Epoch {epoch+1}: Train Acc: {train_acc:.2f}% | Test Acc: {test_acc:.2f}%")

print(f"⏱️ 实验 A 耗时: {time.time() - start_time:.2f} 秒")
复制代码
=== 实验 A: 冻结骨干 (只练 Head) ===
🔧 [模型创建] 加载 ImageNet 预训练权重 (Freeze=True)...


C:\Users\ADVANCE\AppData\Local\Temp\ipykernel_4824\1229741273.py:16: FutureWarning: `torch.cuda.amp.autocast(args...)` is deprecated. Please use `torch.amp.autocast('cuda', args...)` instead.
  with autocast():
C:\Users\ADVANCE\AppData\Local\Temp\ipykernel_4824\1229741273.py:43: FutureWarning: `torch.cuda.amp.autocast(args...)` is deprecated. Please use `torch.amp.autocast('cuda', args...)` instead.
  with autocast():


Epoch 1: Train Acc: 70.42% | Test Acc: 75.63%
Epoch 2: Train Acc: 76.52% | Test Acc: 76.82%
Epoch 3: Train Acc: 77.49% | Test Acc: 76.55%
⏱️ 实验 A 耗时: 111.37 秒

步骤 5:实验 B - 全网微调 (Fine-tuning)

场景

解冻所有层,让 ResNet 针对 CIFAR-10 进行自我调整。

这通常能达到最高精度,但计算量大。得益于我们的 AMP 和 Resize 优化,这里也能跑得很快。

关键技巧
分层学习率:骨干网络 (Backbone) 用小火慢炖 (1e-4),分类头 (Head) 用大火爆炒 (1e-3)。

预期

3 个 Epoch 内,准确率有望突破 90%

py 复制代码
print("\n=== 实验 B: 解冻微调 (追求极致精度) ===")
start_time = time.time()

# 1. 创建解冻模型
model_finetune = create_resnet18(pretrained=True, freeze=False).to(device)

# 2. 分层学习率设置
optimizer = optim.Adam([
    {'params': model_finetune.conv1.parameters(), 'lr': 1e-4},
    {'params': model_finetune.layer1.parameters(), 'lr': 1e-4},
    {'params': model_finetune.layer2.parameters(), 'lr': 1e-4},
    {'params': model_finetune.layer3.parameters(), 'lr': 1e-4},
    {'params': model_finetune.layer4.parameters(), 'lr': 1e-4},
    {'params': model_finetune.fc.parameters(), 'lr': 1e-3} # Head 用大一点的 LR
])

# 3. 训练 3 轮
for epoch in range(3):
    train_loss, train_acc = train_one_epoch(model_finetune, train_loader, criterion, optimizer)
    test_loss, test_acc = evaluate(model_finetune, test_loader, criterion)
    print(f"Epoch {epoch+1}: Train Acc: {train_acc:.2f}% | Test Acc: {test_acc:.2f}%")

print(f"⏱️ 实验 B 耗时: {time.time() - start_time:.2f} 秒")
复制代码
=== 实验 B: 解冻微调 (追求极致精度) ===
🔧 [模型创建] 加载 ImageNet 预训练权重 (Freeze=False)...


C:\Users\ADVANCE\AppData\Local\Temp\ipykernel_4824\1229741273.py:16: FutureWarning: `torch.cuda.amp.autocast(args...)` is deprecated. Please use `torch.amp.autocast('cuda', args...)` instead.
  with autocast():
C:\Users\ADVANCE\AppData\Local\Temp\ipykernel_4824\1229741273.py:43: FutureWarning: `torch.cuda.amp.autocast(args...)` is deprecated. Please use `torch.amp.autocast('cuda', args...)` instead.
  with autocast():


Epoch 1: Train Acc: 88.53% | Test Acc: 92.06%
Epoch 2: Train Acc: 95.27% | Test Acc: 92.97%
Epoch 3: Train Acc: 97.24% | Test Acc: 93.48%
⏱️ 实验 B 耗时: 173.20 秒

🎓 Day 44 总结:速度与激情的平衡

今天我们不仅学习了迁移学习,还掌握了显卡榨干技巧

对比结果

  1. 从头训练 (Day 41): 20 轮才跑 75%,还慢。
  2. 冻结训练 (实验 A): 几秒钟跑完,直接 80%+。适合快速验证想法。
  3. 微调训练 (实验 B): 精度之王,轻松 90%+。配合 AMP 和 Resize 优化,速度也完全可以接受。

工程化经验

  • 遇到训练慢,先问自己:图是不是太大了?是不是没开混合精度?
  • 遇到显存爆,先问自己:Batch Size 是不是太大了?能不能减小图片尺寸?

Next Level :

明天(Day 45),我们将走出分类任务的舒适区,挑战计算机视觉皇冠上的明珠 ------ 目标检测 (Object Detection)。我们将学习如何不仅识别出"猫",还能框出"猫在哪里"!🚀

相关推荐
AI科技星17 小时前
电场起源的几何革命:变化的引力场产生电场方程的第一性原理推导、验证与统一性意义
开发语言·人工智能·线性代数·算法·机器学习·数学建模
沫儿笙17 小时前
库卡机器人气保焊智能节气阀
人工智能·机器人
changlianzhifu117 小时前
中小型企业如何接入数字人民币跨境支付系统:条件、步骤与实战指南
人工智能
童话名剑17 小时前
目标检测指标与优化(吴恩达深度学习笔记)
深度学习·目标检测·非极大值抑制·交并比
九河云17 小时前
华为云SDRS跨Region双活:筑牢证券核心系统零中断防线
大数据·人工智能·安全·机器学习·华为云
中國龍在廣州17 小时前
“物理AI”吹响号角
大数据·人工智能·深度学习·算法·机器人·机器人学习
大力财经17 小时前
贾跃亭:FX Super One二季度交付,FF机器人目标美国头部AI机器人公司
人工智能
好好学操作系统17 小时前
flash_attn ImportError undefined symbol:
开发语言·python
CCPC不拿奖不改名17 小时前
面向对象编程:继承与多态+面试习题
开发语言·数据结构·python·学习·面试·职场和发展