@浙大疏锦行
📘 Day 44 实战作业 (极速版):ResNet 与 迁移学习
1. 作业综述
核心目标:
- 迁移学习:学会调用 ImageNet 预训练的 ResNet18 模型,将其知识迁移到 CIFAR-10 任务上。
- 策略对比:亲手实验冻结骨干 (Linear Probing)和解冻微调 (Fine-tuning)两种策略的效果差异。
- 工业级加速:掌握混合精度训练 (AMP)和分辨率适配技巧,在保持高精度的同时极大提升训练速度。
涉及知识点:
- ResNet18: 工业界最常用的基准模型。
- Transfer Learning:
pretrained=True,freeze weights. - AMP (Automatic Mixed Precision):
torch.cuda.amp. - Data Resize: 适配模型输入尺寸。
场景类比:
- 从头训练: 像小学生写作文,词汇量有限,写得慢且水平一般。
- 迁移学习: 像大学教授写作文,知识渊博。
- 冻结: 教授套模板写(只练最后一层),速度极快。
- 微调: 教授认真推敲每一句话(全网微调),水平最高。
步骤 1:数据准备 (极速配置)
优化策略:
- 分辨率 112x112:比标准的 224x224 少了 75% 的像素,速度提升显著。
- Pin Memory: 锁页内存,加快数据传输。
任务:
- 定义预处理管道 (Resize -> 112)。
- 加载 CIFAR-10 数据集。
importtorchimporttorch.nnasnnimporttorch.optimasoptimfromtorchvisionimportdatasets,transforms,modelsfromtorch.utils.dataimportDataLoaderfromtorch.cuda.ampimportautocast,GradScaler# 混合精度神器importtime# --- 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"iftorch.cuda.is_available()else"cpu")print(f"✅ 数据准备就绪 | 设备:{device}| 输入尺寸:{IMG_SIZE}x{IMG_SIZE}")📥 正在加载数据... ✅ 数据准备就绪 | 设备: cuda | 输入尺寸: 112x112步骤 2:模型构建工厂
任务:
编写一个函数create_resnet18,灵活控制是否加载预训练权重、是否冻结骨干。
- Backbone: ResNet18 (去掉最后的全连接层)。
- Head: 新的全连接层 (输出 10 类)。
defcreate_resnet18(pretrained=True,freeze=False):""" 创建一个适配 CIFAR-10 的 ResNet18 """# 1. 加载模型ifpretrained:# 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)iffreeze:forparaminmodel.parameters():param.requires_grad=False# 3. 替换 Head (这一层默认是可训练的)# ResNet18 的 fc 输入特征数是 512in_features=model.fc.in_features model.fc=nn.Linear(in_features,10)returnmodel步骤 3:混合精度训练引擎
优化策略:
- 使用
torch.cuda.amp进行半精度 (FP16) 训练。 - 显存占用减半,计算速度翻倍,非常适合 RTX 30 系列显卡。
任务:
封装train_one_epoch和evaluate函数,集成 AMP 逻辑。
# 初始化梯度缩放器 (AMP 必备)scaler=GradScaler()deftrain_one_epoch(model,loader,criterion,optimizer):model.train()running_loss=0.0correct=0total=0forinputs,labelsinloader:inputs,labels=inputs.to(device),labels.to(device)optimizer.zero_grad()# --- 核心优化:混合精度上下文 ---withautocast():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()returnrunning_loss/len(loader),100.*correct/totaldefevaluate(model,loader,criterion):model.eval()running_loss=0.0correct=0total=0withtorch.no_grad():forinputs,labelsinloader:inputs,labels=inputs.to(device),labels.to(device)# 测试时不需要 scaler,但可以用 autocast 加速推理withautocast():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()returnrunning_loss/len(loader),100.*correct/totalC:\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%。
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 轮forepochinrange(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%。
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 轮forepochinrange(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 总结:速度与激情的平衡
今天我们不仅学习了迁移学习,还掌握了显卡榨干技巧。
对比结果:
- 从头训练 (Day 41): 20 轮才跑 75%,还慢。
- 冻结训练 (实验 A): 几秒钟跑完,直接 80%+。适合快速验证想法。
- 微调训练 (实验 B): 精度之王,轻松 90%+。配合 AMP 和 Resize 优化,速度也完全可以接受。
工程化经验:
- 遇到训练慢,先问自己:图是不是太大了?是不是没开混合精度?
- 遇到显存爆,先问自己:Batch Size 是不是太大了?能不能减小图片尺寸?
Next Level:
明天(Day 45),我们将走出分类任务的舒适区,挑战计算机视觉皇冠上的明珠 ——目标检测 (Object Detection)。我们将学习如何不仅识别出“猫”,还能框出“猫在哪里”!🚀