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)。我们将学习如何不仅识别出"猫",还能框出"猫在哪里"!🚀

相关推荐
CaracalTiger8 小时前
什么是Clawdbot?Clawdbot下载、安装、配置教程(最新版Moltbot)
python·编辑器·aigc·idea·ai编程·intellij idea·agi
企业老板ai培训8 小时前
从九尾狐AI案例拆解企业AI培训的技术实现与降本增效架构
人工智能
WJX_KOI12 小时前
Open Notebook 一个开源的结合AI的记笔记软件
python
0思必得013 小时前
[Web自动化] 反爬虫
前端·爬虫·python·selenium·自动化
Elastic 中国社区官方博客13 小时前
使用 Discord 和 Elastic Agent Builder A2A 构建游戏社区支持机器人
人工智能·elasticsearch·游戏·搜索引擎·ai·机器人·全文检索
2301_8223827613 小时前
Python上下文管理器(with语句)的原理与实践
jvm·数据库·python
喵手14 小时前
Python爬虫实战:从零搭建字体库爬虫 - requests+lxml 实战采集字体网字体信息数据(附 CSV 导出)!
爬虫·python·爬虫实战·零基础python爬虫教学·csv导出·采集字体库数据·字体库字体信息采集
2501_9333295514 小时前
企业级AI舆情中台架构实践:Infoseek系统如何实现亿级数据实时监测与智能处置?
人工智能·架构
阿杰学AI14 小时前
AI核心知识70——大语言模型之Context Engineering(简洁且通俗易懂版)
人工智能·ai·语言模型·自然语言处理·aigc·数据处理·上下文工程
赛博鲁迅14 小时前
物理AI元年:AI走出屏幕进入现实,88API为机器人装上“最强大脑“
人工智能·机器人