day50/60

@浙大疏锦行

DAY 50 预训练模型+CBAM模块

知识点回顾:

  1. resnet结构解析
  2. CBAM放置位置的思考
  3. 针对预训练模型的训练策略
    1. 差异化学习率
    2. 三阶段微调

ps:今日的代码训练时长较长,3080ti大概需要40min的训练时长

作业:

  1. 好好理解下resnet18的模型结构
  2. 尝试对vgg16+cbam进行微调策略

我会用 “汽车生产线”“医院分诊系统”“游戏难度调整” 等生活化比喻,帮你理解这些深度学习概念,再整理成结构化的学习笔记。

一、知识点通俗解释 + 趣味比喻

1. ResNet 结构解析

含义:ResNet(残差网络)通过 “跳跃连接” 解决深层网络训练时的梯度消失问题,允许网络学习残差映射而非直接映射。
比喻:把 ResNet 想象成 “汽车生产线” :

 
  • 传统网络:每道工序(网络层)必须完全加工上一道工序的半成品,一旦某工序出错,后续全乱(梯度消失)。
  • ResNet:在工序间增加 “捷径传送带”(跳跃连接),如果某工序加工效果不好,直接通过捷径将半成品送到下工序,保证生产线畅通。
    • 残差块:相当于 “可选择加工的工作站”,既能对半成品加工(卷积操作),也能直接放行(跳跃连接)。
    • 优势:像灵活的生产线,即使部分工序效率低,整体仍能正常运转,网络可以训练得更深。
2. CBAM 放置位置的思考

含义:CBAM(注意力模块)可插入网络不同位置,放置位置影响模型性能。
比喻:把 CBAM 想象成 “医院分诊系统” :

 
  • 放置在浅层:如急诊室入口,快速筛选危急病人(提取基础特征),但可能忽略病情复杂的患者(深层语义)。
  • 放置在深层:如专科诊室,针对特定疾病(语义特征)深入分析,但可能遗漏早期症状(浅层特征)。
  • 均匀放置:如在各科室都设分诊台,全面优化诊疗流程,但增加系统复杂度。
  • 最佳实践:通常在中间层放置(如 ResNet 的 Block3 之后),平衡浅层细节与深层语义。
3. 针对预训练模型的训练策略
a. 差异化学习率

含义:对预训练模型的不同层设置不同学习率,底层(特征提取)设小学习率,顶层(任务适应)设大学习率。
比喻:把模型训练想象成 “游戏难度调整” :

 
  • 底层:像游戏新手教程,已训练好基础技能(如识别边缘、纹理),只需微调(小学习率)。
  • 顶层:像游戏最终关卡,需针对新任务(如猫狗分类)大幅调整策略(大学习率)。
  • 优势:避免破坏底层已学习的通用特征,同时快速适应新任务。
b. 三阶段微调

含义:分阶段解冻模型层,逐步调整训练强度:

 
  1. 冻结所有层:只训练分类器(如全连接层)。
  2. 解冻部分层:如解冻最后几个残差块。
  3. 解冻全部层:整体微调。
    比喻:把模型微调想象成 “装修老房子” :
  4. 第一阶段:只换家具(调整分类器),不改动墙体(冻结底层)。
  5. 第二阶段:翻新部分房间(解冻部分层),如厨房、卫生间。
  6. 第三阶段:全面改造(解冻全部层),包括水电线路。
 
  • 优势:从易到难,避免灾难性遗忘,提高训练稳定性。

二、学习笔记

1. 核心概念对比表
知识点核心定义比喻场景关键操作 / 作用
ResNet 结构通过跳跃连接解决梯度消失问题汽车生产线捷径传送带允许网络深度增加,提升性能
CBAM 放置位置影响模型对不同层次特征的关注医院分诊系统中间层放置平衡细节与语义
差异化学习率不同层设置不同学习率游戏难度调整保护底层特征,加速顶层适应
三阶段微调分阶段解冻模型层装修老房子逐步优化,提高训练稳定性
2. 代码框架(简化版)

python

运行

import torch
import torch.nn as nn
from torchvision.models import resnet50# 1. ResNet结构(关键部分)
class ResidualBlock(nn.Module):def forward(self, x):identity = x  # 保存原始输入out = self.conv1(x)  # 卷积操作out = self.bn1(out)out = self.relu(out)out = self.conv2(out)out = self.bn2(out)out += identity  # 跳跃连接:残差相加return self.relu(out)# 2. 在ResNet中插入CBAM
model = resnet50(pretrained=True)
# 在layer3后插入CBAM
model.layer3.add_module('cbam', CBAM(in_channels=1024))# 3. 差异化学习率
optimizer = torch.optim.Adam([{'params': model.conv1.parameters(), 'lr': 1e-5},  # 底层小学习率{'params': model.layer4.parameters(), 'lr': 1e-4},  # 中层中等学习率{'params': model.fc.parameters(), 'lr': 1e-3},  # 顶层大学习率
])# 4. 三阶段微调(伪代码)
def three_stage_finetune(model):# 阶段1:冻结所有层,只训练分类器for param in model.parameters():param.requires_grad = Falsemodel.fc.requires_grad = Truetrain(model, epochs=5)# 阶段2:解冻layer4for param in model.layer4.parameters():param.requires_grad = Truetrain(model, epochs=10)# 阶段3:解冻全部层for param in model.parameters():param.requires_grad = Truetrain(model, epochs=20)
3. 记忆口诀

ResNet
深层训练梯度难,跳跃连接把路开,
残差块里做选择,信息传递更顺畅。

 

CBAM 放置
注意力放哪里好?浅层深层要权衡,
中间位置最常见,细节语义两不误。

 

差异化学习率
底层小步慢慢走,顶层大步向前冲,
预训练权保护好,新任务中快速调。

 

三阶段微调
一冻分类器先训,二解部分再优化,
三全解冻细打磨,步步为营效果佳。

三、总结

通过这些比喻,可以把复杂的网络结构和训练策略想象成生活中的生产、管理和优化流程,帮助你理解它们的设计思路和作用。这些技术都是为了让模型训练更高效、性能更优,在实际应用中可根据任务需求灵活组合使用。



下面我将通过详细注释和可视化方式帮助你理解 ResNet18 的结构,并实现 VGG16+CBAM 的三阶段微调策略。

一、ResNet18 模型结构解析

1. 整体架构可视化

plaintext

ResNet18
├── Conv1: 3x3, 64 channels, stride=2
├── MaxPool: 3x3, stride=2
├── Layer1: 2个残差块 (每个块包含2个3x3卷积)
│   ├── Block1: 64→64
│   └── Block2: 64→64
├── Layer2: 2个残差块 (降维+通道翻倍)
│   ├── Block1: 64→128 (stride=2, 带维度调整)
│   └── Block2: 128→128
├── Layer3: 2个残差块 (通道翻倍)
│   ├── Block1: 128→256 (stride=2)
│   └── Block2: 256→256
├── Layer4: 2个残差块 (通道翻倍)
│   ├── Block1: 256→512 (stride=2)
│   └── Block2: 512→512
├── AvgPool: 7x7
└── FC: 512→1000 (ImageNet分类)
2. 关键代码理解

python

运行

import torch
import torch.nn as nn
from torchvision.models import resnet18# 加载预训练模型
model = resnet18(pretrained=True)# 打印模型结构(简化版)
print("ResNet18核心组件:")
print(f"输入层: {model.conv1}")
print(f"第一层残差块: {model.layer1}")
print(f"最后一层残差块: {model.layer4}")
print(f"分类器: {model.fc}")# 残差块内部结构
first_block = model.layer1[0]
print("\n第一个残差块结构:")
print(f"卷积1: {first_block.conv1}")  # 3x3卷积, 64→64
print(f"卷积2: {first_block.conv2}")  # 3x3卷积, 64→64
print(f"跳跃连接: {first_block.downsample}")  # None,因为输入输出维度相同
3. 残差连接的作用

残差块通过out += identity实现跳跃连接,允许网络学习 “残差映射”:

 

python

运行

def forward(self, x):identity = x  # 保存原始输入out = self.conv1(x)out = self.bn1(out)out = self.relu(out)out = self.conv2(out)out = self.bn2(out)if self.downsample is not None:  # 当需要调整维度时identity = self.downsample(x)out += identity  # 核心: 残差连接out = self.relu(out)return out

二、VGG16+CBAM 的三阶段微调策略

1. 模型定义与 CBAM 插入

python

运行

import torch
import torch.nn as nn
from torchvision.models import vgg16# 定义CBAM模块(简化版)
class ChannelAttention(nn.Module):def __init__(self, in_channels, reduction=16):super().__init__()self.avg_pool = nn.AdaptiveAvgPool2d(1)self.mlp = nn.Sequential(nn.Linear(in_channels, in_channels // reduction, bias=False),nn.ReLU(),nn.Linear(in_channels // reduction, in_channels, bias=False),nn.Sigmoid())def forward(self, x):b, c, _, _ = x.size()avg_out = self.mlp(self.avg_pool(x).view(b, c))return x * avg_out.view(b, c, 1, 1)class SpatialAttention(nn.Module):def __init__(self, kernel_size=7):super().__init__()self.conv = nn.Conv2d(2, 1, kernel_size, padding=kernel_size//2, bias=False)self.sigmoid = nn.Sigmoid()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 x * self.sigmoid(out)class CBAM(nn.Module):def __init__(self, in_channels, reduction=16, kernel_size=7):super().__init__()self.channel_att = ChannelAttention(in_channels, reduction)self.spatial_att = SpatialAttention(kernel_size)def forward(self, x):x = self.channel_att(x)x = self.spatial_att(x)return x# 修改VGG16结构,插入CBAM
def vgg16_with_cbam(pretrained=True):model = vgg16(pretrained=pretrained)# 在每个MaxPooling后插入CBAMfeatures = list(model.features)new_features = []cbam_idx = 0for layer in features:new_features.append(layer)# 在特定层后插入CBAMif isinstance(layer, nn.MaxPool2d):in_channels = new_features[-2].out_channels  # 获取前一层的输出通道数new_features.append(CBAM(in_channels))cbam_idx += 1model.features = nn.Sequential(*new_features)return model
2. 三阶段微调策略实现

python

运行

def three_stage_finetune(model, train_loader, test_loader, device):model.to(device)criterion = nn.CrossEntropyLoss()# 阶段1: 只训练分类器print("===== 阶段1: 冻结所有特征层 =====")for param in model.features.parameters():param.requires_grad = Falseoptimizer = torch.optim.Adam(model.classifier.parameters(), lr=0.001)train(model, train_loader, test_loader, criterion, optimizer, device, epochs=5)# 阶段2: 解冻最后两个block + CBAMprint("===== 阶段2: 解冻最后两个block =====")# VGG16的features分为5个block,每个block以MaxPool结束# 解冻最后两个block (从第24层到最后)for i, param in enumerate(model.features.parameters()):if i >= 24:  # VGG16的第24层之后是最后两个blockparam.requires_grad = True# 对不同部分设置不同学习率optimizer = torch.optim.Adam([{'params': model.features.parameters(), 'lr': 1e-5},  # 低学习率{'params': model.classifier.parameters(), 'lr': 1e-4}  # 较高学习率])train(model, train_loader, test_loader, criterion, optimizer, device, epochs=10)# 阶段3: 解冻全部层,小学习率微调print("===== 阶段3: 解冻全部层 =====")for param in model.parameters():param.requires_grad = Trueoptimizer = torch.optim.Adam(model.parameters(), lr=1e-6)  # 非常小的学习率train(model, train_loader, test_loader, criterion, optimizer, device, epochs=15)return model# 训练函数(简化版)
def train(model, train_loader, test_loader, criterion, optimizer, device, epochs):model.train()for epoch in range(epochs):running_loss = 0.0for inputs, labels in train_loader:inputs, labels = inputs.to(device), labels.to(device)optimizer.zero_grad()outputs = model(inputs)loss = criterion(outputs, labels)loss.backward()optimizer.step()running_loss += loss.item()# 每5个epoch评估一次if (epoch + 1) % 5 == 0:val_acc = evaluate(model, test_loader, device)print(f"Epoch {epoch+1}/{epochs}, Loss: {running_loss/len(train_loader):.4f}, Val Acc: {val_acc:.2f}%")# 评估函数
def evaluate(model, test_loader, device):model.eval()correct = 0total = 0with torch.no_grad():for inputs, labels in test_loader:inputs, labels = inputs.to(device), labels.to(device)outputs = model(inputs)_, predicted = torch.max(outputs, 1)total += labels.size(0)correct += (predicted == labels).sum().item()return 100 * correct / total
3. 主函数调用示例

python

运行

# 初始化模型
model = vgg16_with_cbam(pretrained=True)# 准备数据(示例,需根据实际情况修改)
from torchvision import datasets, transforms
from torch.utils.data import DataLoadertransform = transforms.Compose([transforms.Resize(224),transforms.ToTensor(),transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
])train_dataset = datasets.CIFAR10(root='./data', train=True, transform=transform, download=True)
test_dataset = datasets.CIFAR10(root='./data', train=False, transform=transform)train_loader = DataLoader(train_dataset, batch_size=32, shuffle=True)
test_loader = DataLoader(test_dataset, batch_size=32)# 训练模型
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model = three_stage_finetune(model, train_loader, test_loader, device)

三、总结与优化建议

  1. ResNet18 关键点

    • 残差连接通过out += identity实现,解决深层网络梯度消失问题
    • 共有 8 个残差块,分为 4 组,每组通道数翻倍
    • 最后通过全局平均池化和全连接层进行分类
  2. VGG16+CBAM 微调策略

    • 阶段 1 冻结所有特征层,只训练分类器(快速适应新任务)
    • 阶段 2 解冻最后两个卷积块 + CBAM,使用差异化学习率
    • 阶段 3 解冻全部层,用极小学习率微调(防止过拟合)
  3. 优化建议

    • 根据具体任务调整 CBAM 的插入位置和数量
    • 可使用学习率调度器(如 ReduceLROnPlateau)自动调整学习率
    • 在每个阶段保存模型,方便回滚和比较
 

这样的微调策略可以在保持预训练模型优势的同时,高效地适应新任务,通常能显著提升训练效率和最终性能。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/diannao/89599.shtml
繁体地址,请注明出处:http://hk.pswp.cn/diannao/89599.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Vue3 之vite.config.js配置

一、示例 import { defineConfig } from vite import vue from vitejs/plugin-vue import path from path // https://vitejs.dev/config/ export default defineConfig({plugins: [vue()],base: ./,build: {assetsDir: static, //指定静态资源目录rollupOptions: {input: {mai…

利用Gpu训练

方法一: 分别对网络模型,数据(输入,标注),损失函数调用.cuda() 网络模型: if torch.cuda.is_available():netnet.cuda() 数据(训练和测试): if torch.cud…

使用excel中的MATCH函数进行匹配数据

一、背景 在平日处理数据时,经常需要将给定数据按照制定的数据进行排序,数量比较大时,逐个处理有点费事费力且容易出错,这时可借助excel表格中match函数进行精确匹配。 二、使用match函数–精确排序操作步骤 主要工作步骤&#xf…

SpringCloud系列(41)--SpringCloud Config分布式配置中心简介

前言:微服务意味着要将单体应用中的业务拆分成一个个子服务,每个服务的粒度相对较小,因此系统中会出现大量的服务,但由于每个服务都需要必要的配置信息才能运行,所以—套集中式的、动态的配置管理设施是必不可少的&…

wireshark介绍和使用

Wireshark 介绍 Wireshark 是一款开源的 网络协议分析工具(Packet Sniffer),用于捕获和分析网络数据包。它支持多种协议解析,适用于网络调试、安全分析、网络教学等场景。 官网:https://www.wireshark.org/ 特点&#…

【甲方安全建设】敏感数据检测工具 Earlybird 安装使用详细教程

文章目录 背景工具介绍安装方法一、Linux 与 macOS 安装流程二、Windows 系统安装流程(一)三、Windows 系统安装流程(二)四、错误处理使用说明模块与规则机制集成与运维建议结语背景 随着源代码泄露、配置误提交、密码硬编码等风险频发,企业源代码库中潜在的敏感信息泄漏…

异步Websocket构建聊天室

目录 Websocket技术背景 Websockec简介 实现websocket通信程序 实验环境: 服务端(阿里云ESC,VPC网络): 客户端1(本机): 通信模型: 实现功能逻辑: 源代码: 服务…

OpenCV CUDA模块设备层-----反向二值化阈值处理函数thresh_binary_inv_func()

操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 算法描述 OpenCV CUDA 模块(cudev) 中的一个仿函数(functor)生成器,用于创建一个反向二值化阈值…

【实现一个时间MCP完整技术解析】

🕒 MCP Time Server 完整技术解析:从核心实现到文件架构的深度剖析 目前已上传npm库,chan-mcp-time-server,有兴趣的可以下载试试 创建时间: 2025年7月2日 🎯 项目概述与架构设计 核心问题定义 AI助手在处理时间相关…

类成员方法命名风格解析:动宾、纯动词与纯名词的选择之道

在软件开发的浩瀚代码海洋中,类成员方法的命名犹如指引开发者的灯塔,其重要性不言而喻。合理的命名不仅能让代码 “自我言说”,降低理解成本,还能提升开发效率,促进团队协作。常见的类成员方法命名风格可归纳为动宾结构…

自己电脑搭建本地服务器并实现公网访问,内网也能提供互联网连接使用

如何在本地自己计算机上自建服务器并开启公网地址提供互联网服务的详细教学,一步步操作流程,从本地部署到配置公网IP,最后并附无公网IP内网穿透公网访问的nat123方案。 要在自用的电脑上搭建本地服务器并实现公网地址的访问,需要…

如何使用AI改进论文写作 ---- 引言篇(2)

写在前面 本篇作为1.0版本的补充优化,记录本人的研究过程。 在分析了多本论文写作的相关的书籍之后,我明白了一点,关于论文写作,永远是一个熟能生巧的过程,对于人来说,必须多写才能够变得熟练,对…

【Java21】在spring boot中使用ScopedValue

文章目录 0.环境说明1.基础知识1.1 ScopedValue的特点 2.应用场景2.1 spring web项目中,使用ScopedValue传递上下文(全局不可变量)2.2 spring grpc项目中,使用ScopedValue传递上下文(全局不可变量) 3.Scope…

第10篇 图像语义分割和目标检测介绍

语义分割(Semantic Segmentation)是图像处理和机器视觉一个重要分支,其目标是精确理解图像场景与内容。语义分割是在像素级别上的分类,属于同一类的像素都要被归为一类,因此语义分割是从像素级别来理解图像的。如下如所示的照片,属…

微算法科技(NASDAQ MLGO)基于量子图像处理的边缘检测算法:开拓图像分析新视野

在当今数字化时代,图像数据海量增长,边缘检测作为图像处理的关键环节,在机器视觉、医学成像、安防监控等众多领域有着至关重要的作用。传统边缘检测算法在处理复杂图像时,面临计算效率低、精度不足等问题。量子计算的兴起&#xf…

SM4密码算法的C语言实现(带测试)

一、SM4算法原理 SM4是中国国家密码管理局于2012年发布的国家商用密码算法标准,也称为GB/T 32907-2016。它是一种分组对称加密算法,采用32轮非线性迭代结构,分组长度和密钥长度均为128位。SM4算法的设计充分考虑了安全性、高效性和实现简便性…

【React Native原生项目不能运行npx react-native run-android项目】

运行命令报错,帮我修复X:\jetbrains-workspace\theme-wallpaper>npx react-native run-android error Android project not found. Are you sure this is a React Native project? If your Android files are located in a non-standard location (e.g. not inside ‘andro…

SPLADE 在稀疏向量搜索中的原理与应用详解

今天看到Sentence Transformers v5.0 集成了许多稀疏嵌入模型。为了搞清楚什么稀疏嵌入模型以及应用,查到了SPLADE,比较巧合的是在paper reading分享的时候看到有同学分享了一片ACL 2025的工作也是基于SPLADE去做的。下面结合一些资料分享关于SPLADE 在稀…

wpf的Binding之UpdateSourceTrigger

前言 在wpf界面开发中,Binding的源和目标之间可以通过Mode来决定数据的传递方向,同时数据传递时的触发条件也是可以有多种情况,多种情况由UpdateSourceTrigger属性来控制,该属性有Default、Explicit、LostFocus、PropertyChanged…

突破性进展:超短等离子体脉冲实现单电子量子干涉,为飞行量子比特奠定基础

关键词:量子计算、电子干涉测量、等离子体脉冲、马赫-曾德尔干涉仪、非绝热量子操控 研究背景 在量子计算领域,飞行量子比特(flying qubits)因其动态传播特性和通过库仑相互作用直接纠缠的能力,成为替代光子量子比特的…