【深度学习】总目录

SE论文:《Squeeze-and-Excitation Networks》将重点放在了通道(channel)关系上,并提出了一种新的结构单元SE block。将SE block堆叠在一起,就形成了SENet。SE块略微增加计算成本但显著地提升了性能,并且即插即用。SENet获得了最后一届ImageNet 2017竞赛分类任务的冠军。

CBAM论文:《Convolutional Block Attention Module》论文(2018年)提出了一种轻量的注意力模块CBAM,可以在通道和空间维度上进行 Attention 。

1 SE模块

  卷积神经网络建立在卷积运算的基础上,通过融合局部感受野内的空间信息和通道信息来提取信息特征。为了提高网络的表示能力,许多现有的工作通过在空间维度上对网络的性能进行了提升,比如Inception模块中嵌入多尺度处理来提高准确度,Spatial Transformer Networks(STN)结合空间注意力机制。本文与这些方法相反,专注于通道,并提出了一种新颖的架构单元“Squeeze-and-Excitation”(SE)模块,通过显式地建模通道之间的相互依赖关系,自适应地重新校准通道式的特征响应。具体来说,就是通过学习的方式来自动获取每个通道的重要程度,然后依照这个重要程度去提升有用的特征并抑制对当前任务用处不大的特征。

Squeeze-and-Excitation Module

给定一个输入x,其特征通道数为c1,通过一系列卷积等一般变换后得到一个特征通道数为c2的特征。接下来通过SE模块的三个操作来重标定前面得到的特征:

1)Squeeze(压缩)。沿着空间维度压缩特征,将每个通道的二维的特征变成一个实数,可以理解为对每个 channel 的特征信息的进行融合,这个实数某种程度上具有全局的感受野,并且输出的维度和输入的特征通道数相匹配。它表征着在特征通道上响应的全局分布,而且使得靠近输入的层也可以获得全局的感受野,这一点在很多任务中都是非常有用。采用全局平局池化来实现,输出维度为1x1xC。

2)Excitation(激励)。得到Squeeze的1x1xC全局特征后,通过全连接层对每个通道的重要性进行预测。它是一个类似于循环神经网络中门的机制。通过参数来为每个特征通道生成权重,其中参数被学习用来显式地建模特征通道间的相关性。
为了减少通道个数从而降低计算量,设置了缩放参数SERatio。 第一个全连接层有C*SERatio个神经元,输入为1×1×C,输出1×1×C×SERadio,起到降维作用。 第二个全连接层有C个神经元,输入为1×1×C×SERadio,输出为1×1×C。

3)Scale(缩放)。将Excitation的输出的权重看做是经过特征选择后的每个特征通道的重要性,然后通过乘法逐通道加权到先前的特征上,完成在通道维度上的对原始特征的重标定。

SE模块的代码实现(pytorch)

from torch import nn
class SELayer(nn.Module):
def __init__(self, channel, reduction=16):
super(SELayer, self).__init__()
self.avg_pool = nn.AdaptiveAvgPool2d(1)
self.fc = nn.Sequential(
nn.Linear(channel, channel // reduction, bias=False),
nn.ReLU(inplace=True),
nn.Linear(channel // reduction, channel, bias=False),
nn.Sigmoid()
) def forward(self, x):
b, c, _, _ = x.size()
y = self.avg_pool(x).view(b, c)
y = self.fc(y).view(b, c, 1, 1)
return x * y.expand_as(x)

2 CBAM模块

CBAM(Convolutional Block Attention Module)是一种轻量的注意力模块,给定一个中间特征图,我们的模块会沿着两个独立的维度(通道和空间)依次推断注意力图,然后将注意力图乘以输入特征图以进行自适应特征修饰。 由于CBAM是轻量级的通用模块,因此可以以可忽略的开销将其无缝集成到任何CNN架构中,并且可以与基础CNN一起进行端到端训练。 论文在 ResNet 和 MobileNet 等经典结构上添加了 CBAM 模块并进行对比分析,同时也进行了可视化,发现 CBAM 更关注识别目标物体,这也使得 CBAM 具有更好的解释性。

2.1 Channel Attention Module(CAM)

通道上的Attention模块以及具体计算如下图所示:

将输入的特征图F(H×W×C)分别经过global max pooling(全局最大池化)和global average pooling(全局平均池化),得到两个1×1×C的特征图,接着,再将它们分别送入一个两层的神经网络(MLP),第一层神经元个数为 C/r(r为减少率),激活函数为 Relu,第二层神经元个数为 C,这个两层的神经网络是共享的。而后,将MLP输出的特征进行基于element-wise的加和操作,再经过sigmoid激活操作,生成最终的channel attention feature,即M_c。最后,将M_c和输入特征图F做element-wise乘法操作,生成Spatial attention模块需要的输入特征。

实验:AvgPool & MaxPool

在channel attention中,表1对于pooling的使用进行了实验对比,发现avg & max的并行池化的效果要更好。这里也有可能是池化丢失的信息太多,avg&max的并行连接方式比单一的池化丢失的信息更少,所以效果会更好一点。

2.2 Spatial Attention Module(SAM)

空间上的Attention模块以及具体计算如下图所示:

将Channel attention模块输出的特征图F’作为本模块的输入特征图。首先做一个基于channel的global max pooling 和global average pooling,得到两个H×W×1 的特征图,然后将这2个特征图基于channel做concat操作(通道拼接)。然后经过一个7×7卷积(7×7比3×3效果要好)操作,降维为1个channel,即H×W×1。再经过sigmoid生成spatial attention feature,即M_s。最后将该feature和该模块的输入feature做乘法,得到最终生成的特征。

实验:Comparison of different spatial attention methods

使用channel-pooling(沿着通道维度进行平均和最大池化)的同时,使用大kernel size=7效果最好。

2.3 CAM和SAM的组合形式

通道注意力和空间注意力这两个模块能够以并行或者串行顺序的方式组合在一块儿,关于通道和空间上的串行顺序和并行作者进行了实验对比,发现先通道再空间的结果会稍微好一点。具体实验结果如下:

从表中可以看出,在ResNet50的基准架构上,两个attetnion子模块的连接顺序里面的确是channel + spatial的要更好一些,也要好于标准的SENet的通道attention。

3 Grad-CAM可视化

论文:Grad-CAM: Visual Explanations from Deep Networks via Gradient-based Localization
博客:Grad-CAM简介

利用 Grad-CAM 对不一样的网络进行可视化后,能够发现,引入 CBAM 后,特征覆盖到了待识别物体的更多部位,而且最终判别物体的几率也更高,这代表注意力机制的确让网络学会了关注重点信息。

3.1 分类网络使用grad-cam

代码:https://github.com/jacobgil/pytorch-grad-cam

首先安装库 pip install grad-cam,然后运行以下文件

import os
import numpy as np
import torch
from PIL import Image
import matplotlib.pyplot as plt
from pytorch_grad_cam.utils.model_targets import ClassifierOutputTarget
from torchvision import models
from torchvision import transforms
from pytorch_grad_cam import GradCAMPlusPlus, GradCAM, XGradCAM
from pytorch_grad_cam.utils.image import show_cam_on_image def main():
model = models.mobilenet_v3_large(pretrained=True)
print(model)
target_layers = [model.features[-1]] # model = models.vgg16(pretrained=True)
# target_layers = [model.features] # model = models.resnet34(pretrained=True)
# target_layers = [model.layer4] # model = models.regnet_y_800mf(pretrained=True)
# target_layers = [model.trunk_output] # model = models.efficientnet_b0(pretrained=True)
# target_layers = [model.features] data_transform = transforms.Compose([transforms.ToTensor(),
transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])])
# load image
img_path = "both.png"
assert os.path.exists(img_path), "file: '{}' dose not exist.".format(img_path)
img = Image.open(img_path).convert('RGB')
img = np.array(img, dtype=np.uint8)
# img = center_crop_img(img, 224) # [C, H, W]
img_tensor = data_transform(img)
# expand batch dimension
# [C, H, W] -> [N, C, H, W]
input_tensor = torch.unsqueeze(img_tensor, dim=0) cam = GradCAM(model=model, target_layers=target_layers, use_cuda=False) targets = [ClassifierOutputTarget(281)] # tabby, tabby cat
# targets = [ClassifierOutputTarget(254)] # pug, pug-dog # You can also pass aug_smooth=True and eigen_smooth=True, to apply smoothing.
grayscale_cam = cam(input_tensor=input_tensor, targets=targets)
grayscale_cam = grayscale_cam[0, :]
visualization = show_cam_on_image(img.astype(dtype=np.float32) / 255.,
grayscale_cam,
use_rgb=True)
plt.imshow(visualization)
plt.show() if __name__ == '__main__':
main()

3.2 Yolov5-7.0使用grad-cam

代码:https://github.com/z1069614715/objectdetection_script/tree/master/yolo-gradcam

首先安装库 pip install grad-cam,然后运行以下文件

import warnings

warnings.filterwarnings('ignore')
warnings.simplefilter('ignore')
import torch, yaml, cv2, os, shutil
import numpy as np np.random.seed(0)
import matplotlib.pyplot as plt
from tqdm import trange
from PIL import Image
from models.yolo import Model
from utils.general import intersect_dicts
from utils.augmentations import letterbox
from utils.general import xywh2xyxy
from pytorch_grad_cam import GradCAMPlusPlus, GradCAM, XGradCAM
from pytorch_grad_cam.utils.image import show_cam_on_image
from pytorch_grad_cam.activations_and_gradients import ActivationsAndGradients class yolov5_heatmap:
def __init__(self, weight, cfg, device, method, layer, backward_type, conf_threshold, ratio):
device = torch.device(device)
ckpt = torch.load(weight) # 包含模型权重、epoch、data等
model_names = ckpt['model'].names # 80个类别名
csd = ckpt['model'].float().state_dict() # checkpoint state_dict as FP32
model = Model(cfg, ch=3, nc=len(model_names)).to(device) # create model
csd = intersect_dicts(csd, model.state_dict(), exclude=['anchor']) # 筛选预训练权重中的键值对,不加载anchor
model.load_state_dict(csd, strict=False) # load
model.eval()
print(f'Transferred {len(csd)}/{len(model.state_dict())} items') target_layers = [eval(layer)]
method = eval(method) colors = np.random.uniform(0, 255, size=(len(model_names), 3)).astype(np.int_) # 不同类别的颜色
self.__dict__.update(locals()) def post_process(self, result):
logits_ = result[..., 4:]
boxes_ = result[..., :4]
sorted, indices = torch.sort(logits_[..., 0], descending=True)
return logits_[0][indices[0]], xywh2xyxy(boxes_[0][indices[0]]).cpu().detach().numpy() def draw_detections(self, box, color, name, img):
xmin, ymin, xmax, ymax = list(map(int, list(box)))
cv2.rectangle(img, (xmin, ymin), (xmax, ymax), tuple(int(x) for x in color), 2)
cv2.putText(img, str(name), (xmin, ymin - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.8, tuple(int(x) for x in color), 2,
lineType=cv2.LINE_AA)
return img def __call__(self, img_path, save_path):
# remove dir if exist
if os.path.exists(save_path):
shutil.rmtree(save_path)
# make dir if not exist
os.makedirs(save_path, exist_ok=True) # img process
img = cv2.imread(img_path)
img = letterbox(img)[0]
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
img = np.float32(img) / 255.0
tensor = torch.from_numpy(np.transpose(img, axes=[2, 0, 1])).unsqueeze(0).to(self.device) # init ActivationsAndGradients
grads = ActivationsAndGradients(self.model, self.target_layers, reshape_transform=None) # get ActivationsAndResult
result = grads(tensor) # 从指定中间层提取激活值和梯度
activations = grads.activations[0].cpu().detach().numpy() # postprocess to yolo output
post_result, post_boxes = self.post_process(result[0])
for i in trange(int(post_result.size(0) * self.ratio)):
if post_result[i][0] < self.conf_threshold: # 没有非极大值抑制,只用置信度筛选,所以输出的热力图结果比较多
break self.model.zero_grad()
if self.backward_type == 'conf':
post_result[i, 0].backward(retain_graph=True)
else:
# get max probability for this prediction
score = post_result[i, 1:].max()
score.backward(retain_graph=True) # process heatmap
gradients = grads.gradients[0]
b, k, u, v = gradients.size()
weights = self.method.get_cam_weights(self.method, None, None, None, activations,
gradients.detach().numpy())
weights = weights.reshape((b, k, 1, 1))
saliency_map = np.sum(weights * activations, axis=1)
saliency_map = np.squeeze(np.maximum(saliency_map, 0))
saliency_map = cv2.resize(saliency_map, (tensor.size(3), tensor.size(2))) # resize到原图大小
saliency_map_min, saliency_map_max = saliency_map.min(), saliency_map.max()
if (saliency_map_max - saliency_map_min) == 0:
continue
saliency_map = (saliency_map - saliency_map_min) / (saliency_map_max - saliency_map_min) # scale到0-255之间 # add heatmap and box to image
cam_image = show_cam_on_image(img.copy(), saliency_map, use_rgb=True)
cam_image = self.draw_detections(post_boxes[i], self.colors[int(post_result[i, 1:].argmax())],
f'{self.model_names[int(post_result[i, 1:].argmax())]} {post_result[i][0]:.2f}',
cam_image)
cam_image = Image.fromarray(cam_image)
cam_image.save(f'{save_path}/{i}.png') def get_params():
params = {
'weight': 'yolov5s.pt',
'cfg': 'models/yolov5s.yaml',
'device': 'cuda:0',
'method': 'XGradCAM', # GradCAMPlusPlus, GradCAM, XGradCAM
'layer': 'model.model[-2]',
'backward_type': 'conf', # class or conf
'conf_threshold': 0.6, # 0.6
'ratio': 0.02 # 0.02-0.1
}
return params if __name__ == '__main__':
model = yolov5_heatmap(**get_params())
model(r'data/images/bus.jpg', 'result')

参考

1. SENet概览

2. SE (Squeeze Excitation)模块

3. 经典论文-SeNet论文及实践

4. 注意机制(CBAM)理解

5. Grad-CAM简介

常用的注意力机制模块(SE、CBAM)的更多相关文章

  1. CVPR2021| 继SE,CBAM后的一种新的注意力机制Coordinate Attention

    前言: 最近几年,注意力机制用来提升模型性能有比较好的表现,大家都用得很舒服.本文将介绍一种新提出的坐标注意力机制,这种机制解决了SE,CBAM上存在的一些问题,产生了更好的效果,而使用与SE,CBA ...

  2. Non-local Neural Networks 原理详解及自注意力机制思考

    Paper:https://arxiv.org/abs/1711.07971v1 Author:Xiaolong Wang, Ross Girshick, Abhinav Gupta, Kaiming ...

  3. (转)注意力机制(Attention Mechanism)在自然语言处理中的应用

    注意力机制(Attention Mechanism)在自然语言处理中的应用 本文转自:http://www.cnblogs.com/robert-dlut/p/5952032.html  近年来,深度 ...

  4. 注意力机制(Attention Mechanism)在自然语言处理中的应用

    注意力机制(Attention Mechanism)在自然语言处理中的应用 近年来,深度学习的研究越来越深入,在各个领域也都获得了不少突破性的进展.基于注意力(attention)机制的神经网络成为了 ...

  5. 自然语言处理中的自注意力机制(Self-attention Mechanism)

    自然语言处理中的自注意力机制(Self-attention Mechanism) 近年来,注意力(Attention)机制被广泛应用到基于深度学习的自然语言处理(NLP)各个任务中,之前我对早期注意力 ...

  6. 深度学习之注意力机制(Attention Mechanism)和Seq2Seq

    这篇文章整理有关注意力机制(Attention Mechanism )的知识,主要涉及以下几点内容: 1.注意力机制是为了解决什么问题而提出来的? 2.软性注意力机制的数学原理: 3.软性注意力机制. ...

  7. DeepLearning.ai学习笔记(五)序列模型 -- week2 序列模型和注意力机制

    一.基础模型 假设要翻译下面这句话: "简将要在9月访问中国" 正确的翻译结果应该是: "Jane is visiting China in September" ...

  8. TensorFlow从1到2(十)带注意力机制的神经网络机器翻译

    基本概念 机器翻译和语音识别是最早开展的两项人工智能研究.今天也取得了最显著的商业成果. 早先的机器翻译实际脱胎于电子词典,能力更擅长于词或者短语的翻译.那时候的翻译通常会将一句话打断为一系列的片段, ...

  9. 注意力机制(Attention Mechanism)应用——自然语言处理(NLP)

    近年来,深度学习的研究越来越深入,在各个领域也都获得了不少突破性的进展.基于注意力(attention)机制的神经网络成为了最近神经网络研究的一个热点,下面是一些基于attention机制的神经网络在 ...

  10. 自注意力机制(Self-attention Mechanism)——自然语言处理(NLP)

    近年来,注意力(Attention)机制被广泛应用到基于深度学习的自然语言处理(NLP)各个任务中.随着注意力机制的深入研究,各式各样的attention被研究者们提出.在2017年6月google机 ...

随机推荐

  1. sweetviz工具避坑和简单使用

    网上关于sweetviz的文章比较少,有些坑这里说一下给大家避坑. 使用sweetviz遇到的错误如下: KeyError: "None of ['index'] are in the co ...

  2. springboot多模块项目启动经历

    springboot多模块使用 @ 目录 springboot多模块使用 前言 大佬把项目权限给我了,我就先下下来看看学习一下 一.识别 二.maven配置 1.安装maven 三.加载刷新 总结 前 ...

  3. Web前端 -- NPM包管理器

    初始化: #建立一个空文件夹,在命令提示符进入该文件夹 执行命令初始化 npm init #按照提示输入相关信息,如果是用默认值则直接回车即可. #name: 项目名称 #version: 项目版本号 ...

  4. Dapr Outbox 执行流程

    Dapr Outbox 是1.12中的功能. 本文只介绍Dapr Outbox 执行流程,Dapr Outbox基本用法请阅读官方文档 .本文中appID=order-processor,topic= ...

  5. OpenYurt:延伸原生 Kubernetes 到边缘场景下的落地实践

    简介: 随着云原生技术的逐步成熟,阿里云容器服务团队在具体落地实践过程中不断探索云原生技术的应用边界.同时随着物联网和 5G 的迅猛发展,传统的边缘计算架构已经不能满足业务发展的需要. 如何基于云原生 ...

  6. 逸仙电商Seata企业级落地实践

    简介: 本文将会以逸仙电商的业务作为背景, 先介绍一下seata的原理, 并给大家进行线上演示, 由浅入深去介绍这款中间件, 以便读者更加容易去理解 Seata 这个中间件. 作者 | 张嘉伟(Git ...

  7. Kubernetes 已经成为云原生时代的安卓,这就够了吗?

    ​简介:本文将介绍如何在 Kubernetes 上构建新的应用管理平台,提供一层抽象以封装底层逻辑,只呈现用户关心的接口,使用户可以只关注自己的业务逻辑,管理应用更快更安全. 作者:司徒放 导语:云原 ...

  8. 拒绝双写:巧用Lindorm数据订阅

    ​简介: 本文介绍了双写场景的一致性问题,详细介绍了三种解决方案,并针对DB->Binlog->Kafka方案给出了Lindorm数据订阅的最佳实践 双写问题介绍 双写问题(Dual Wr ...

  9. IIS相关发布错误解决记录

    HRESULT 代码 0x80070021 错误消息: 应用程序"应用程序名称"中的服务器错误HTTP 错误 500.19 - 内部服务器错误HRESULT:0x80070021对 ...

  10. Process-与操作系统中的进程进行交互

    1.Process介绍 在Java中,Process类是一个抽象类,它提供了与操作系统中的进程进行交互的方法.当你在Java程序中启动一个新的进程(例如,运行一个外部程序或脚本)时,JVM会创建一个P ...