第58步 深度学习图像识别:Transformer可视化(Pytorch)

2023-10-27

一、写在前面

(1)pytorch_grad_cam库

这一期补上基于基于Transformer框架可视化的教程和代码,使用的是pytorch_grad_cam库,以Bottleneck Transformer模型为例。

(2)算法分类

pytorch_grad_cam库中包含的主要方法有以下几种:

GradCAM: 这是最基本的方法。GradCAM(Gradient-weighted Class Activation Mapping)通过取网络最后一个卷积层的特征图,然后对这些特征图进行加权求和,得到类别激活图。加权的系数是网络最后一个卷积层特征图对应类别的梯度的全局平均池化值。

GradCAMPlusPlus: 这是在GradCAM的基础上的改进。GradCAM++不仅计算了类别相对于特征图的梯度,还计算了二阶和三阶导数。这使得GradCAM++在某些情况下可以获得更细粒度的解释。

ScoreCAM: ScoreCAM采用了不同的策略。它对于每个特征图都生成一个类似的激活图,并将所有这些激活图加权求和。权重是每个特征图对应的类别分数。

AblationCAM: AblationCAM是基于Ablation-based的方法。它首先对每个特征图进行遮挡(或移除),然后看类别得分如何改变。这些改变被用来生成类别激活图。

XGradCAM: 这是GradCAM的另一个扩展。XGradCAM考虑了激活和梯度之间的空间关系,以生成更详细的类别激活图。

EigenCAM: 它基于主成分分析 (PCA) 的方法,利用协方差矩阵的特征向量和特征值来表示激活图。

FullGrad: FullGrad是一个对输入,权重和偏差的特征重要性进行全局分解的方法。

以上方法都在解释深度学习模型的决策,可以帮助理解模型关注的区域和特征。在选择使用哪种方法时,可以根据需求和实验效果进行选择。

二、Transformer可视化实战

继续使用胸片的数据集:肺结核病人和健康人的胸片的识别。其中,肺结核病人700张,健康人900张,分别存入单独的文件夹中。

(a)Bottleneck Transformer建模

######################################导入包###################################
# 导入必要的包
import copy
import torch
import torchvision
import torchvision.transforms as transforms
from torchvision import models
from torch.utils.data import DataLoader
from torch import optim, nn
from torch.optim import lr_scheduler
import os
import matplotlib.pyplot as plt
import warnings
import numpy as np

warnings.filterwarnings("ignore")
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False

# 设置GPU
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")


################################导入数据集#####################################
import torch
from torchvision import datasets, transforms
import os

# 数据集路径
data_dir = "./MTB"

# 图像的大小
img_height = 256
img_width = 256

# 数据预处理
data_transforms = {
    'train': transforms.Compose([
        transforms.RandomResizedCrop(img_height),
        transforms.RandomHorizontalFlip(),
        transforms.RandomVerticalFlip(),
        transforms.RandomRotation(0.2),
        transforms.ToTensor(),
        transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
    ]),
    'val': transforms.Compose([
        transforms.Resize((img_height, img_width)),
        transforms.ToTensor(),
        transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
    ]),
}

# 加载数据集
full_dataset = datasets.ImageFolder(data_dir)

# 获取数据集的大小
full_size = len(full_dataset)
train_size = int(0.7 * full_size)  # 假设训练集占80%
val_size = full_size - train_size  # 验证集的大小

# 随机分割数据集
torch.manual_seed(0)  # 设置随机种子以确保结果可重复
train_dataset, val_dataset = torch.utils.data.random_split(full_dataset, [train_size, val_size])

# 将数据增强应用到训练集
train_dataset.dataset.transform = data_transforms['train']

# 创建数据加载器
batch_size = 32
train_dataloader = torch.utils.data.DataLoader(train_dataset, batch_size=batch_size, shuffle=True, num_workers=4)
val_dataloader = torch.utils.data.DataLoader(val_dataset, batch_size=batch_size, shuffle=True, num_workers=4)

dataloaders = {'train': train_dataloader, 'val': val_dataloader}
dataset_sizes = {'train': len(train_dataset), 'val': len(val_dataset)}
class_names = full_dataset.classes


###############################定义模型################################
# 导入必要的库
import torch.nn as nn
import timm

# 定义Bottleneck Transformer模型
model = timm.create_model('botnet26t_256', pretrained=True)  # 你可以选择适合你需求的BotNet版本
num_ftrs = model.feature_info[-1]['num_chs']

# 根据分类任务修改最后一层
model.head.fc = nn.Linear(num_ftrs, len(class_names))

# 将模型移至指定设备
model = model.to(device)

# 打印模型摘要
print(model)


#############################编译模型#########################################
# 定义损失函数
criterion = nn.CrossEntropyLoss()

# 定义优化器
optimizer = optim.Adam(model.parameters())

# 定义学习率调度器
exp_lr_scheduler = lr_scheduler.StepLR(optimizer, step_size=7, gamma=0.1)

# 开始训练模型
num_epochs = 2

# 初始化记录器
train_loss_history = []
train_acc_history = []
val_loss_history = []
val_acc_history = []

for epoch in range(num_epochs):
    print('Epoch {}/{}'.format(epoch, num_epochs - 1))
    print('-' * 10)

    # 每个epoch都有一个训练和验证阶段
    for phase in ['train', 'val']:
        if phase == 'train':
            model.train()  # 设置模型为训练模式
        else:
            model.eval()   # 设置模型为评估模式

        running_loss = 0.0
        running_corrects = 0

        # 遍历数据
        for inputs, labels in dataloaders[phase]:
            inputs = inputs.to(device)
            labels = labels.to(device)

            # 零参数梯度
            optimizer.zero_grad()

            # 前向
            with torch.set_grad_enabled(phase == 'train'):
                outputs = model(inputs)
                _, preds = torch.max(outputs, 1)
                loss = criterion(outputs, labels)

                # 只在训练模式下进行反向和优化
                if phase == 'train':
                    loss.backward()
                    optimizer.step()

            # 统计
            running_loss += loss.item() * inputs.size(0)
            running_corrects += torch.sum(preds == labels.data)

        epoch_loss = running_loss / dataset_sizes[phase]
        epoch_acc = (running_corrects.double() / dataset_sizes[phase]).item()

        # 记录每个epoch的loss和accuracy
        if phase == 'train':
            train_loss_history.append(epoch_loss)
            train_acc_history.append(epoch_acc)
        else:
            val_loss_history.append(epoch_loss)
            val_acc_history.append(epoch_acc)

        print('{} Loss: {:.4f} Acc: {:.4f}'.format(phase, epoch_loss, epoch_acc))

    print()

# 保存模型
torch.save(model.state_dict(), 'botnet_dit_model.pth')

(b)使用GradCAM可视化

在跑之前,得先安装git;然后用git安装pytorch_grad_cam:

安装git容易,无脑输入:

conda install git

安装pytorch_grad_cam也不难:

git clone https://github.com/jacobgil/pytorch-grad-cam.git
cd pytorch-grad-cam
pip install .

然后码代码:

import numpy as np
from PIL import Image
from matplotlib import pyplot as plt
from pytorch_grad_cam import GradCAM, ScoreCAM, GradCAMPlusPlus, AblationCAM, XGradCAM, EigenCAM, FullGrad
from pytorch_grad_cam.utils.model_targets import ClassifierOutputTarget
from pytorch_grad_cam.utils.image import show_cam_on_image
import timm

# 代码1中的函数
def myimshows(imgs, titles=False, fname="test.jpg", size=6):
    lens = len(imgs)
    fig = plt.figure(figsize=(size * lens,size))
    if titles == False:
        titles="0123456789"
    for i in range(1, lens + 1):
        cols = 100 + lens * 10 + i
        plt.xticks(())
        plt.yticks(())
        plt.subplot(cols)
        if len(imgs[i - 1].shape) == 2:
            plt.imshow(imgs[i - 1], cmap='Reds')
        else:
            plt.imshow(imgs[i - 1])
        plt.title(titles[i - 1])
    plt.xticks(())
    plt.yticks(())
    plt.savefig(fname, bbox_inches='tight')
    plt.show()
    
def tensor2img(tensor,heatmap=False,shape=(256,256)):
    np_arr=tensor.detach().numpy()#[0]
    #对数据进行归一化
    if np_arr.max()>1 or np_arr.min()<0:
        np_arr=np_arr-np_arr.min()
        np_arr=np_arr/np_arr.max()
    #np_arr=(np_arr*255).astype(np.uint8)
    if np_arr.shape[0]==1:
        # 如果是灰度图像,复制三个通道以创建一个RGB图像
        np_arr=np.concatenate([np_arr,np_arr,np_arr],axis=0)
    np_arr=np_arr.transpose((1,2,0))
    return np_arr

# 加载模型
model = timm.create_model('botnet26t_256', pretrained=False)

# 更改全连接层以匹配你的类别数
num_ftrs = model.head.fc.in_features
model.head.fc = nn.Linear(num_ftrs, 2)  # 假设你的类别数为2

model.load_state_dict(torch.load('botnet_dit_model.pth', map_location=device))


# 模型转移到相应设备
model = model.to(device)

# 你的图像路径
image_path = './MTB/Tuberculosis/Tuberculosis-203.png'

# 加载图像
image = Image.open(image_path).convert("RGB")

# 使用代码1中定义的图像转换
input_image = data_transforms['val'](image).unsqueeze(0).to(device)

# 使用GradCAM
target_layer = model.stages[2][0].conv3_1x1.bn.drop
with GradCAM(model=model, target_layers=[target_layer], use_cuda=torch.cuda.is_available()) as cam:
    target = [ClassifierOutputTarget(1)]  # 修改为你的目标类别
    grayscale_cam = cam(input_tensor=input_image, targets=target)
    
    #将热力图结果与原图进行融合
    rgb_img=tensor2img(input_image.cpu().squeeze())
    visualization = show_cam_on_image(rgb_img, grayscale_cam[0], use_rgb=True)
myimshows([rgb_img, grayscale_cam[0], visualization],["image","cam","image + cam"])

结果输出如下:

 

红色区域就是模型认为的“可疑区域”,也就是说模型根据这些区域判断它是Tuberculosis的主要依据。

几个注意事项:

(a)问:代码:‘target = [ClassifierOutputTarget(0)]  # 修改为你的目标类别’,这个怎么解释?此外,0和1分别代表什么呢?

答:第一小问:一般来说,ClassifierOutputTarget(0)中的0代表的是你希望将注意力图(CAM)生成针对的类别标签。例如,如果你的两个类别是猫和狗,且在训练数据集中猫的标签是0,狗的标签是1,那么ClassifierOutputTarget(0)将生成猫的注意力图,而ClassifierOutputTarget(1)将生成狗的注意力图。

第二小问:在 PyTorch 中,使用 ImageFolder 函数或类似的数据加载器加载数据时,类别名称列表(class_names)的顺序将决定了类别标签的分配。这意味着类别名称列表的索引将作为类别的标签。在我们的例子中,class_names = ['Normal', 'Tuberculosis'],"Normal" 的索引是 0,所以它的标签是 0;"Tuberculosis" 的索引是 1,所以它的标签是 1。所以ClassifierOutputTarget(0) 将生成"Normal"类别的注意力图,ClassifierOutputTarget(1) 将生成"Tuberculosis"类别的注意力图。

(b)问:代码:‘target_layer = model.stages[2][0].conv3_1x1.conv’,如何选择输出的层?怎么知道模型中有哪些层?

答:第一小问:一般来说,卷积层或者重复结构的最后一层(如 ResNet 中的每个残差块的最后一层)是可行的目标层,因为这些层能保留空间信息,而全连接层则不行,因为它们不再保留空间信息。

第二小问:通过下面代码打印出模型中所有层次的名称:

#打印出模型中所有层次的名称
for name, module in model.named_modules():
print(name)

输出如下:

 或者打印出模型的顶层子模块:

#打印模型的顶层子模块
for name, module in model.named_children():
    print(name)

输出就四个:

stem
stages
final_conv
head

接下来,展示几个层的写法,大家自行体会:

stem.conv2.conv :target_layer = model.stem.conv2.conv
stages.3.1.conv1_1x1:target_layer = model.stages[3][1].conv1_1x1
final_conv:target_layer = model.final_conv

应该找到规律了吧,不详细解释了。每一层输出是不一样的,例如上面三层输出依次如下:

(c)问:如何改用其他7种方法来替代GradCAM?

答:很简单,来到这个代码段:

with GradCAM(model=model, target_layers=[target_layer], use_cuda=torch.cuda.is_available()) as cam:
    target = [ClassifierOutputTarget(0)]  # 修改为你的目标类别
    grayscale_cam = cam(input_tensor=input_image, targets=target)
    
    #将热力图结果与原图进行融合
    rgb_img=tensor2img(input_image.cpu().squeeze())
    visualization = show_cam_on_image(rgb_img, grayscale_cam[0], use_rgb=True)
myimshows([rgb_img, grayscale_cam[0], visualization],["image","cam","image + cam"])

 只需要把GradCAM分别换成GradCAMPlusPlus、ScoreCAM、AblationCAM、XGradCAM、EigenCAM以及FullGrad即可,简单粗暴。

三、写在后面

除了Transformer,pytorch_grad_cam库也可以用在之前提到的CNN的模型上,大家可自行探索哈。

四、数据

链接:https://pan.baidu.com/s/15vSVhz1rQBtqNkNp2GQyVw?pwd=x3jf

提取码:x3jf

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

第58步 深度学习图像识别:Transformer可视化(Pytorch) 的相关文章

随机推荐

  • 基于Session实现登录流程

    流程一 发送短信验证码 用户在提交手机号后 会校验手机号是否合法 如果不合法 则要求用户重新输入手机号 如果手机号合法 后台此时生成手机号对应的验证码 后台先得到验证码 同时将验证码保存到session中 然后再通过短信的方式 测试中输出到
  • 2020年网络安全国赛解析(仅个人想法)

    A 1任务一登录安全加固 请对服务器Log Web Data 按要求进行相应的设置 提高服务器的安全性 1 密码策略 Log Web Data a 最小密码长度不少于8个字符 windows 打开本地安全策略 开始菜单 管理工具 管理 本地
  • 关于nlohmann::json的简单使用

    nlohmann json的使用非常简单 只需要包含 hpp文件即可 这是它的官网https github com nlohmann json 简单使用 include json hpp include
  • Apache Doris (四) :Doris分布式部署(一) FE部署及启动

    目录 1 Apache Doris下载 2 节点划分 3 节点配置 4 FE部署及启动 进入正文之前 欢迎订阅专题 对博文点赞 评论 收藏 关注IT贫道 获取高质量博客内容 部署Apache Doris时需要分别部署FE BE Broker
  • C# 9 新特性——record的相关总结

    Intro C 9 中引入了 record record 是一个特殊类 用它来实现 model 在有些情况下会非常的好用 Sample record RecordPerson public string Name get init publ
  • AOT

    处理appearance gap很大的图像伪造 比如说复杂的光照条件和肤色 把appearance mapping 定义为一个最优传输问题 在特征空间和像素空间 a relighting generator来模拟最优传输 1通过求特征空间中
  • flvjs播放器事件动态监听并实现flvjs实例销毁(http-flv协议)

    flvjs播放器事件动态监听 http flv协议 1 困扰我的问题 最近在使用flvjs播放器播放http flv协议流的直播视频 业务需求要动态加载非固定数量的视频 不超过6个 使用flv创建flvjs实例简单 但销毁flvjs得想法子
  • LLaMA系列

    文章目录 1 LLaMA 1 1 模型结构 1 2 训练方式 1 3 结论 2 LLaMA 2 2 1 相比LLaMA1的升级 2 3 模型结构 2 3 1 MHA MQA GQA区别与联系 2 4 训练方式 1 LLaMA 纯基座语言模型
  • 1. MongoDB快速实战与基本原理

    分布式技术MongoDB 1 MongoDB介绍 1 1 什么是MongoDB 1 2 MongoDB vs 关系型数据库 1 3 MongoDB的技术优势 1 4 MongoDB的应用场景 2 MongoDB快速开始 2 1 linux安
  • sonarqube启动时报错WrapperSimpleApp: Encountered an error running main: java.nio.file.AccessDeniedExcepti

    sonarqube启动时报错WrapperSimpleApp Encountered an error running main java nio file AccessDeniedException sonarqube的logs目录下so
  • Flex builder 项目运行,页面空白

    项目运行 页面空白的原因 游览器兼容性问题 暂时还未解决 请使用fireFox游览器进行调试查看
  • 计算机磁盘序列号是唯一的吗,磁盘id和硬盘序列号一样吗

    大家好 我是时间财富网智能客服时间君 上述问题将由我为大家进行解答 磁盘ID和硬盘序列号不是一回事 1 硬盘ID是硬盘的密码 2 磁盘序号 在磁盘管理页面显示的磁盘0 1 2等 0 1 2即磁盘序号 硬盘是电脑主要的存储媒介之一 由一个或者
  • Flink SQL 1.11新功能详解:Hive 数仓实时化 & Flink SQL + CDC 实践

    问题导读1 Flink 1 11 有哪些新功能 2 如何使用 flink cdc connectors 捕获 MySQL 和 Postgres 的数据变更 3 怎样利用 Flink SQL 做多流 join 后实时同步到 Elasticse
  • Vue+Element-ui+SpringBoot搭建后端汽车租赁管理系统

    最近在做项目 花了一周的时间搭建了一个十分完备的汽车租赁后端管理系统 页面采用纯Vue2 Element ui搭建 后端采用Springboot Mybatis搭建 数据库采用Mysql 包括了登录验证 根据不同权限进入不同界面 数据增删改
  • Qt标准对话框

    标准对话框 新建桌面应用程序 项目名testStandardDialogs 类名Dialog 基类QDialog 不勾选创建界面文件 1 文件 颜色 字体 对话框 dialog h ifndef DIALOG H define DIALOG
  • java -version 时出现 could not open D:\java\lib\i386\jvm.cfg是什么意思

    简单分几步 删除之前的jdk 删除注册表中相关项 重新安装 注意安装路径不要改变 跟之前可以用的时候一样 配置环境变量 应该是文件缺失导致的问题 JDK在安装的过程当中将会生成如下3个项目 HKEY LOCAL MACHINE SOFTWA
  • what引导的宾语从句

    what引导宾语从句时在从句中作主语 宾语或表语 表示 所 的 1 表示 的东西或事情 相当于 the thing that all that everything that They ve done what they can to he
  • wifi一键配网smartconfig原理及应用

    https blog csdn net flyingcys article details 49283273 智能家居 家电现阶段还处于普及阶段 由于家庭wifi网络的普及 目前普遍采用wifi与路由器完成连接 与手机 云端进行数据交互 智
  • 数据结构与算法 -- 子序列问题

    一 子序列问题 子数组问题是连续的 而子序列问题是不连续的 比如说字符串 I wanna keep a giraffe in my backyard 的一种子序列就可以是 Igbackd 子序列不再要求答案是一个连续的串 一个字符串的子序列
  • 第58步 深度学习图像识别:Transformer可视化(Pytorch)

    一 写在前面 1 pytorch grad cam库 这一期补上基于基于Transformer框架可视化的教程和代码 使用的是pytorch grad cam库 以Bottleneck Transformer模型为例 2 算法分类 pyto