Python训练营打卡 Day50
预训练模型+CBAM模块
知识点回顾:
- resnet结构解析
- CBAM放置位置的思考
- 针对预训练模型的训练策略
- 差异化学习率
- 三阶段微调
预训练模型 + CBAM 模块
知识点回顾
-
ResNet 结构解析
-
残差块:ResNet 的核心是残差块,它通过残差连接解决了深层网络的梯度消失问题。残差块允许梯度直接传播到后面的层,从而使得网络能够训练得更深。
-
网络结构:ResNet 由多个残差块组成,每个残差块包含两个或三个卷积层,以及一个 shortcut 连接。shortcut 连接将输入直接加到卷积层的输出上。
-
-
CBAM 放置位置的思考
-
CBAM 模块:CBAM(Convolutional Block Attention Module)结合了通道注意力和空间注意力,能够动态调整特征图的重要性。CBAM 通常放置在每个残差块的后面,以增强每个块的特征表达能力。
-
-
针对预训练模型的训练策略
-
微调策略:使用预训练模型作为初始化,然后在目标任务数据上进行微调。微调时可以调整不同的学习率,对不同层进行不同程度的训练。
-
差异化学习率:对不同的层设置不同的学习率,通常对后面的层设置较高的学习率,对前面的层设置较低的学习率,以保护预训练的特征。
-
三阶段微调:分阶段进行微调,逐步调整模型的参数,确保模型能够稳定收敛。
-
用比喻解释
-
ResNet 结构:ResNet 就像是一家连锁餐厅的标准化厨房,每个厨房都有多个工作站(残差块),每个工作站负责特定的烹饪步骤。残差连接就像是每个工作站都有一个直接传递原材料的通道,确保即使某个工作站出现问题,原材料也能顺利传递到下一个工作站。
-
CBAM 模块:CBAM 就像是每个工作站中的“智能烹饪助手”,它能够根据当前的烹饪情况动态调整每个工作站的输出,确保每个工作站的输出都是最优的。
-
预训练模型:预训练模型就像是这家连锁餐厅已经经过长期运营、积累了丰富经验的厨房,可以快速适应新的菜品需求。
-
微调策略:微调策略就像是在新店开业时,对已有菜单进行微调,以适应当地顾客的口味。差异化学习率就像是对不同经验的厨师进行不同程度的培训,确保新厨师能够快速学习,而资深厨师能够稳步提升。
-
三阶段微调:三阶段微调就像是新店开业的三个阶段:试营业、正式营业和优化阶段,逐步调整菜单和厨房流程,确保餐厅能够稳定运营并满足顾客需求。
ps:今日的代码训练时长较长,3080ti大概需要40min的训练时长
作业:
- 好好理解下resnet18的模型结构
- 尝试对vgg16+cbam进行微调策略
-
好好理解下 ResNet-18 的模型结构
-
ResNet-18 是一个较浅的 ResNet 模型,包含 18 层。它的结构相对简单,适合初学者理解 ResNet 的基本原理。通过查阅 PyTorch 的官方文档或使用
torchvision
加载 ResNet-18 模型,你可以查看每个层的定义和作用。
-
ResNet(残差网络)通过引入残差块解决了深层网络的梯度消失问题,使网络可以训练更深。ResNet18 包含 18 层(1 个卷积层 + 17 个卷积层或全连接层),具体结构如下:
输入层:接收 224×224×3 的图像
初始卷积层:7×7 卷积,64 通道,步长 2,随后 BatchNorm 和 ReLU
4 个残差模块组:
每个模块组包含多个残差块(BasicBlock)
残差块结构为:3×3 卷积 → BatchNorm → ReLU → 3×3 卷积 → BatchNorm → 残差连接 → ReLU
全局平均池化:将特征图压缩为 1×1 特征向量
全连接层:输出分类结果
-
尝试对 VGG-16 + CBAM 进行微调策略
-
在 VGG-16 模型中集成 CBAM 模块。你可以在每个卷积块的后面添加 CBAM 模块,以增强模型的特征提取能力。微调时,可以对 VGG-16 的前面几层设置较低的学习率,对后面几层和 CBAM 模块设置较高的学习率,以保护预训练的特征并提高模型性能。
-
CBAM(卷积注意力模块)可以增强特征表达能力,将其应用于 VGG16 时,可以放置在每个卷积组之后。以下是针对 VGG16+CBAM 的三阶段微调策略:
import torch
import torch.nn as nn
import torch.optim as optim
from torchvision import models
from torch.optim.lr_scheduler import StepLR# 定义 CBAM 模块
class ChannelAttention(nn.Module):def __init__(self, in_channels, reduction_ratio=16):super(ChannelAttention, self).__init__()self.avg_pool = nn.AdaptiveAvgPool2d(1)self.max_pool = nn.AdaptiveMaxPool2d(1)self.fc = nn.Sequential(nn.Conv2d(in_channels, in_channels // reduction_ratio, 1, bias=False),nn.ReLU(),nn.Conv2d(in_channels // reduction_ratio, in_channels, 1, bias=False))def forward(self, x):avg_out = self.fc(self.avg_pool(x))max_out = self.fc(self.max_pool(x))out = avg_out + max_outreturn torch.sigmoid(out)class SpatialAttention(nn.Module):def __init__(self, kernel_size=7):super(SpatialAttention, self).__init__()self.conv = nn.Conv2d(2, 1, kernel_size, padding=kernel_size//2, bias=False)def forward(self, x):avg_out = torch.mean(x, dim=1, keepdim=True)max_out, _ = torch.max(x, dim=1, keepdim=True)out = torch.cat([avg_out, max_out], dim=1)out = self.conv(out)return torch.sigmoid(out)class CBAM(nn.Module):def __init__(self, in_channels, reduction_ratio=16, kernel_size=7):super(CBAM, self).__init__()self.channel_att = ChannelAttention(in_channels, reduction_ratio)self.spatial_att = SpatialAttention(kernel_size)def forward(self, x):x = x * self.channel_att(x)x = x * self.spatial_att(x)return x# 修改 VGG16 模型,添加 CBAM
def vgg16_cbam(pretrained=True):model = models.vgg16(pretrained=pretrained)# 在每个 block 后添加 CBAMfeatures = list(model.features)new_features = []# VGG16 的 block 划分点block_end_indices = [4, 9, 16, 23, 30]current_block = 0for i, layer in enumerate(features):new_features.append(layer)if i == block_end_indices[current_block] and current_block < len(block_end_indices) - 1:# 除了最后一个 block,其他都添加 CBAMin_channels = list(filter(lambda x: isinstance(x, nn.Conv2d), features[:i+1]))[-1].out_channelsnew_features.append(CBAM(in_channels))current_block += 1model.features = nn.Sequential(*new_features)return model# 三阶段微调策略
def train_vgg16_cbam():# 阶段 1: 冻结除最后几层外的所有参数model = vgg16_cbam(pretrained=True)# 冻结大部分参数for param in list(model.parameters())[:-20]:param.requires_grad = Falseoptimizer = optim.SGD(filter(lambda p: p.requires_grad, model.parameters()), lr=0.001, momentum=0.9)scheduler = StepLR(optimizer, step_size=5, gamma=0.1)# 训练前 10 个 epochfor epoch in range(10):# 训练代码scheduler.step()# 阶段 2: 解冻更多层,使用差异化学习率for param in list(model.parameters())[-40:]:param.requires_grad = True# 为不同层设置不同学习率params = [{'params': list(model.parameters())[:-40], 'lr': 1e-5},{'params': list(model.parameters())[-40:-20], 'lr': 1e-4},{'params': list(model.parameters())[-20:], 'lr': 1e-3}]optimizer = optim.SGD(params, momentum=0.9)scheduler = StepLR(optimizer, step_size=5, gamma=0.1)# 训练 10-20 个 epochfor epoch in range(10, 20):# 训练代码scheduler.step()# 阶段 3: 解冻所有层,使用更低学习率for param in model.parameters():param.requires_grad = Trueoptimizer = optim.SGD(model.parameters(), lr=1e-4, momentum=0.9)scheduler = StepLR(optimizer, step_size=5, gamma=0.1)# 训练 20-30 个 epochfor epoch in range(20, 30):# 训练代码scheduler.step()return model
策略解释
模型修改:在 VGG16 的每个卷积组后添加 CBAM 模块,增强特征表达能力。
三阶段微调:
阶段 1:冻结大部分预训练参数,仅训练最后几层和 CBAM 模块,防止灾难性遗忘。
阶段 2:解冻更多层,使用差异化学习率(底层小学习率,高层大学习率),微调中间层特征。
阶段 3:解冻所有层,使用低学习率全局微调,进一步优化模型。
学习率调整:使用 StepLR 调度器,每 5 个 epoch 降低学习率,有助于模型收敛。
@浙大疏锦行