【深度学习】Attention、Self-Attention、Multi-Head Attention

一、Attention

在CV领域,注意力机制通常分为通道注意力和空间注意力或者两者结合。

一张图像经backbone得到的特征通常包括多个通道,每个通道是一个像素矩阵,每个通道对任务的贡献不尽相同,单个通道的特征图中每个像素对任务的贡献也不尽相同。注意力机制就是希望通过加权的方式凸显其中重要的通道或像素。

1. SE

1.1 概念

Squeeze-and-Excitation Networks (SENet)是通道注意力的代表性工作,它为每个通道分配一个权重,其结构图如下:

如图, x \boldsymbol{x} x F t r ( ⋅ , θ ) F_{tr}(\cdot,\theta) Ftr(,θ)提取得到维度为 [ c 2 , h , w ] [c_2,h,w] [c2,h,w]的特征,之后按照如下步骤获取各通道权重并完成加权:
(1) 对特征进行全局平均池化,每个通道都被池化为一个特征点,形成长度为 c 2 c_2 c2的特征向量。
(2) 经过两次全连接(两层的MLP),第一层神经元个数较少,第二层神经元个数为 c 2 c_2 c2
(3) 第二层全连接输出经Sigmoid将值固定到0-1之间,即可得到分配给每个通道的权重。
(4) 将权重乘以对应通道的特征图。

1.2 实现
import torch
from torch import nnclass se_block(nn.Module):def __init__(self, channel, ratio=16):super().__init__()self.avg_pool = nn.AdaptiveAvgPool2d((1, 1))self.fc1 = nn.Linear(channel, channel // ratio, bias=False)self.relu = nn.ReLU(inplace=True)self.fc2 = nn.Linear(channel // ratio, channel, bias=False)self.sigmoid = nn.Sigmoid()def forward(self, x):w = self.avg_pool(x).squeeze(-1).squeeze(-1)  # [8, 512, 24, 24] -> [8, 512, 1, 1] -> [8, 512, 1] -> [8, 512]w = self.relu(self.fc1(w))  # [8, 512] -> [8, 32]w = self.sigmoid(self.fc2(w)).unsqueeze(2).unsqueeze(3)  # [8, 32] -> [8, 512] -> [8, 512, 1] -> [8, 512, 1, 1]return w * xif __name__ == "__main__":x = torch.randn((8, 512, 24, 24))se = se_block(512)x_se = se(x)

2. ECA

2.1 概念

ECA-Net: Efficient Channel Attention for Deep Convolutional Neural Networks也是一种通道注意力方法,对SENet做了改进。ECA认为通过全连接捕捉所有通道的信息是没有必要的,并且CNN具有良好的跨通道信息捕捉能力,于是ECA将两层全连接替换成了一层1D卷积。如下图,左图是SE模块,右图是ECA模块。

可以看出,将全连接替换为1D卷积后多了一个关键参数,即卷积核大小 k k k。ECA通过下式确定 k k k

其中, C C C为通道数 , γ ,\gamma γ b b b是超参数,在ECA中被分别设为2和1, ∣ ⋅ ∣ o d d |\cdot|_{odd} odd表示取绝对值后再取最近的奇数。

2.2 实现
import math
import torch
from torch import nnclass eca_block(nn.Module):def __init__(self, channel, gamma=2, b=1):super().__init__()self.avg_pool = nn.AdaptiveAvgPool2d((1, 1))kernel_size = int(abs(math.log(channel, 2) / gamma + b / gamma))kernel_size = kernel_size if kernel_size % 2 else kernel_size + 1  # 是奇数不变,是偶数+1self.conv = nn.Conv1d(1, 1, kernel_size=kernel_size, padding=kernel_size // 2, bias=False)  # 输入和输出通道数均为1,需要padding才能保证输出特征长度为channelself.sigmoid = nn.Sigmoid()def forward(self, x):w = self.avg_pool(x).squeeze(-1).transpose(1, 2)  # [8, 512, 24, 24] -> [8, 512, 1, 1] -> [8, 512, 1] -> [8, 1, 512]w = self.sigmoid(self.conv(w)).transpose(1, 2).unsqueeze(-1)  # [8, 1, 512] -> [8, 512, 1] -> [8, 512, 1, 1]return w * xif __name__ == "__main__":x = torch.randn((8, 512, 24, 24))eca = eca_block(512)x_eca = eca(x)

3. CBAM

3.1 概念

CBAM: Convolutional Block Attention Module 结合了通道空间注意力。

如图所示,CBAM先进行通道注意力然后再进行空间注意力。

对于通道注意力,其结构图如下:

可见,在通道注意力方面,CBAM与SE的差别为在池化时前者多了最大池化,于是在Sigmoid前两个池化向量经MLP的输出要先合并。

对于空间注意力,其结构图如下:

同样地,空间注意力部分也进行两种池化最大和平均,不过它不在通道尺度上进行,而是在特征图像素尺度上求所有通道的最大和平均。所以,池化后得到两个特征图(与输入的高宽一致),经过一个2D卷积核然后经Sigmoid即可得到在所有像素点上的权重。

3.2 实现
import torch
from torch import nnclass ChannelAttention(nn.Module):def __init__(self, channel, ratio=16):super(ChannelAttention, self).__init__()self.avg_pool = nn.AdaptiveAvgPool2d((1, 1))self.max_pool = nn.AdaptiveMaxPool2d((1, 1))self.fc1 = nn.Conv2d(channel, channel // ratio, kernel_size=1, bias=False)  # 使用1*1卷积代替全连接,参数量相同,但是输入和输出维度不需要转换了self.relu = nn.ReLU(inplace=True)self.fc2 = nn.Conv2d(channel // ratio, channel, kernel_size=1, bias=False)self.sigmoid = nn.Sigmoid()def forward(self, x):w_m = self.max_pool(x)w_a = self.avg_pool(x)w_m = self.relu(self.fc1(w_m))w_a = self.relu(self.fc1(w_a))w = self.sigmoid(self.fc2(w_m) + self.fc2(w_a))return w * xclass SpatialAttention(nn.Module):def __init__(self, kernel_size=7):super(SpatialAttention, self).__init__()self.conv = nn.Conv2d(2, 1, kernel_size=kernel_size, padding=kernel_size // 2, bias=False)  # 原文kernel_size=7self.sigmoid = nn.Sigmoid()def forward(self, x):w_m, _ = torch.max(x, dim=1, keepdim=True)w_a = torch.mean(x, dim=1, keepdim=True)w = torch.cat([w_m, w_a], dim=1)  # 两个[8, 1, 24, 24]拼接为一个[8, 2, 24, 24]w = self.sigmoid(self.conv(w))return w * xclass cbam(nn.Module):def __init__(self, channel):super(cbam, self).__init__()self.channel_attention = ChannelAttention(channel)self.spatial_attention = SpatialAttention()def forward(self, x):x = self.channel_attention(x)x = self.spatial_attention(x)return xif __name__ == "__main__":x = torch.randn((8, 512, 24, 24))cbam = cbam(512)x_cbam = cbam(x)

二、Self-Attention

自注意力最先在NLP领域被提出,后来被用到CV领域。自注意力也是注意力的一种,本质上也是对原特征进行加权。“自”体现在它的权重不是像上面的方法通过网络学习出来,而是根据自身特征计算而来。

1. 概念

首先,我们以NLP中的数据形式理解Self-Attention,NLP中一个批次的数据包括多个句子,每个句子有多个单词,每个单词通过某种转换可以形成固定长度的特征向量,又称embedding。于是一个句子就被转换为大小为 [ L , C ] [L,C] [L,C]的特征矩阵 X X X L L L为该句子中单词的个数, C C C是embedding的长度。

对于一个句子的特征矩阵,自注意力的关键公式如下:

其中, Q , K , V Q,K,V Q,K,V X X X分别经过线性变化得到的特征矩阵, d k d_k dk K K K中特征向量的长度(一般与embedding的长度一致)。

但是,直接从 Q , K , V Q,K,V Q,K,V讲起理解较为困难,为进一步体现“自”的含义,我们简化上式,从下式开始讲起:

其中, X X T XX^T XXT X X X中embedding两两之间的点积,从几何上理解点积是一个向量在另一个向量上的投影,能够衡量两个向量在方向长度上的相似度。

所以 X X T XX^T XXT是一个相似度矩阵,经过SoftMax之后,相似度被归一化至 [ 0 , 1 ] [0,1] [0,1],变得更适合用来加权。可以理解为 S o f t M a x ( X X T ) SoftMax(XX^T) SoftMax(XXT)是一个权重矩阵,用来加权 X X X S o f t M a x ( X X T ) X SoftMax(XX^T)X SoftMax(XXT)X X X X的尺寸是一致的,它的每一行都是 X X X中各行的加权和,对应的权重是 X X X中当前行与各行的点积相似度。

可以看出,上述操作是通过自身获取权重再加权自身,这就是“自”的含义。

然后,我们对比上面两个图像中的公式,可以发现,区别如下有两个部分:
(1)
区别: 前者是 Q , K , V Q,K,V Q,K,V,后者是 X X X
解释: Q , K , V Q,K,V Q,K,V X X X分别经过线性变化得到的特征,通常是通过三个全连接层分别进行变换,这样做能够从一定程度上提升Self-Attention的效果,毕竟多了三个可学习的参数矩阵。
(2)
区别: 前者多了一个 1 d k \frac{1}{\sqrt{d_k}} dk 1 d k d_k dk K K K中特征向量的长度。
解释: 如果 Q Q Q K K K的特征长度很长( Q Q Q K K K的长度可以与 X X X不同,但一般是相同的),就会导致 Q K T QK^T QKT的值很大,使得 S o f t M a x SoftMax SoftMax的梯度消失,除以 d k \sqrt{d_k} dk 能够避免该问题。

2. 实现

import torch.nn as nn
import torch
import mathclass SelfAttention(nn.Module):def __init__(self, dim_in, dim_k, dim_v):super(SelfAttention, self).__init__()self.dim_in = dim_inself.dim_k = dim_k  # K与Q的特征长度相同,可以与V的特征长度不同self.dim_v = dim_v  # V可以与输入特征长度不同,但为了即插即用,通常dim_in=dim_vself.linear_q = nn.Linear(dim_in, dim_k, bias=False)self.linear_k = nn.Linear(dim_in, dim_k, bias=False)self.linear_v = nn.Linear(dim_in, dim_v, bias=False)self.scale = 1 / math.sqrt(dim_k)def forward(self, x):q = self.linear_q(x)  # [batch_size, n, dim_in] -> [batch_size, n, dim_k]k = self.linear_k(x)  # [batch_size, n, dim_in] -> [batch_size, n, dim_k]v = self.linear_v(x)  # [batch_size, n, dim_in] -> [batch_size, n, dim_v]w = torch.bmm(q, k.transpose(1, 2)) * self.scale  # (QK^T)/sqrt(dim_k)w = torch.softmax(w, dim=-1)  # SoftMax[(QK^T)/sqrt(dim_k)]att = torch.bmm(w, v)  # SoftMax[(QK^T)/sqrt(dim_k)]Vreturn attif __name__ == "__main__":x = torch.randn((8, 6, 512))self_attention = SelfAttention(512, 128, 512)o = self_attention(x)

三、Multi-Head Attention

上述只进行一次自注意力计算的情况称为单头自注意力,与之相对的,多头自注意力会将 Q , K , V Q,K,V Q,K,V进行拆分(拆成几份就是几个头),然后分别进行自注意力计算,最后将各部分计算结果拼接并通过一次仿射变换得到新的输出。

使用多个头能从不同层面(representation subspace)考虑相似性,提供模型表达能力。

1. 概念

如上图,我们将图中与 Q , K , V Q,K,V Q,K,V相连的 L i n e a r Linear Linear视为拆分操作,假设 Q , K , V Q,K,V Q,K,V大小均为 [ 6 , 512 ] [6,512] [6,512],头的数量为4(必须是能整除embedding长度512的),那么拆分后就有4组大小均为 [ 6 , 128 ] [6,128] [6,128] q , k , v q,k,v q,k,v。对同组的 q , k , v q,k,v q,k,v进行Self-Attention计算(对应图中Scaled Dot-Product Attention)会得到4组大小均为 [ 6 , 128 ] [6,128] [6,128]的输出,拼接起来就得到大小为 [ 6 , 512 ] [6,512] [6,512]的输出。最后经过一层全连接进行仿射变换即可得到Multi-Head Attention的输出。

2. 实现

import math
import torch
import torch.nn as nnclass MultiHeadAttention(nn.Module):def __init__(self, dim_in, dim_k, dim_v, num_heads=4):super(MultiHeadAttention, self).__init__()assert dim_k % num_heads == 0 and dim_v % num_heads == 0, "dim_k and dim_v must be multiple of num_heads"self.num_heads = num_headsself.dim_in = dim_inself.dim_k = dim_kself.dim_v = dim_vself.scale = 1 / math.sqrt(dim_k / num_heads)self.linear_q = nn.Linear(dim_in, dim_k)self.linear_k = nn.Linear(dim_in, dim_k)self.linear_v = nn.Linear(dim_in, dim_v)self.fc = nn.Linear(dim_v, dim_v)def forward(self, x):batch_size, n, dim_in = x.shapeQ = self.linear_q(x)  # [batch_size, n, dim_in] -> [batch_size, n, dim_k]K = self.linear_k(x)  # [batch_size, n, dim_in] -> [batch_size, n, dim_k]V = self.linear_v(x)  # [batch_size, n, dim_in] -> [batch_size, n, dim_v]nh = self.num_headsdk = self.dim_k // nh  # 每个头的k的长度,使用 // 可以确保得到的是整数dv = self.dim_v // nh  # 每个头的v的长度# 将头的位置放在第2个,这样后面计算点积时就是每个头单独计算q = Q.reshape(batch_size, n, nh, dk).transpose(1, 2)  # [batch_size, n, dim_k] -> [batch_size, n, num_heads, dk] -> [batch_size, num_heads, n, dk]k = K.reshape(batch_size, n, nh, dk).transpose(1, 2)  # [batch_size, n, dim_k] -> [batch_size, n, num_heads, dk] -> [batch_size, num_heads, n, dk]v = V.reshape(batch_size, n, nh, dv).transpose(1, 2)  # [batch_size, n, dim_v] -> [batch_size, n, num_heads, dv] -> [batch_size, num_heads, n, dv]w = torch.matmul(q, k.transpose(2, 3)) * self.scale  # (qk^T)/sqrt(dk)w = torch.softmax(w, dim=-1)  # SoftMax[(qk^T)/sqrt(dk)]att = torch.matmul(w, v)  # SoftMax[(qk^T)/sqrt(dk)]vatt = att.transpose(1, 2).reshape(batch_size, n, self.dim_v)  # [batch_size, num_heads, n, dv] -> [batch_size, n, num_heads, dv] -> [batch_size, n, dim_v]att = self.fc(att)  # [batch_size, n, dim_v] -> [batch_size, n, dim_v]return attif __name__ == '__main__':x = torch.randn((8, 6, 512))mh_attention = MultiHeadAttention(512, 512, 512)o = mh_attention(x)

致谢:

本博客仅做记录使用,无任何商业用途,参考内容如下:
神经网络学习小记录64——Pytorch 图像处理中注意力机制的解析与代码详解
self-Attention|自注意力机制 |位置编码 | 理论 + 代码
SE、CBAM、ECA注意力机制

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

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

相关文章

2W 3KVDC 隔离双输出 DC/DC 电源模块——TPD-2W 系列

TPD-2W系列提供双独立输出电压,并且两组电压可以不同,这样就节省一个电源模块,特别适合一块板上有多个不同电压要求的设计,而外形尺寸和TPA一样,工作温度范围广-40℃到 105℃。

LabVIEW连接postgre sql

一、安装ODBC 下载对应postgreSQL版本的ODBC 下载网址:http://ftp.postgresql.org/pub/odbc/versions/msi/ 下载好后默认安装就行,这样在ODBC数据源中才能找到。 二、配置系统DSN 实现要新建好要用的数据库,这里的用户名:postg…

new[]与delete[]

(要理解之前关于new,delete的一些概念,看​​​​​​ CSDN) 引子: 相比new,new[]不仅仅是个数的增加,还有int大小记录空间的创建, 下图中错误的用模拟多个new来替代new[],释放步…

9.MMD 基础内容总结及制作成品流程

前期准备 1. 导入场景和模型 在左上角菜单栏,显示里将编辑模型时保持相机和光照勾选上,有助于后期调色 将抗锯齿和各向异性过滤勾掉,可以节省资源,避免bug 在分辨率设定窗口,可以调整分辨率 3840x2160 4k分辨率 1…

【python】图形用户界面学习之tkinter

认识tkinter Tkinter是Python中内置的图形用户界面(GUI)库。它是Tk GUI工具包的接口,可以创建和管理窗口、按钮、标签、文本框等各种GUI组件,并与用户交互。 使用Tkinter,可以创建各种GUI应用程序,如桌面…

QT Webengine开发过程报错qml: Render process exited with code 159 (killed)

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言一、解决方法二、补充说明总结 前言 基于QT的Webengine开发过程中,QT的官方示例quicknanobrowser也无法成功运行,程序运行后&#xff0…

git的学习

设置用户(目的在于可以在远端看到是谁提交了内容,更方便公司的管理) 1、设置全局用户(在家路径下创建用户,每个项目都用这一个用户)>: git config --global user.name manba >: git config --global …

使用Go语言和chromedp库下载Instagram图片:简易指南

摘要/导言 本文将介绍如何使用Go语言配合chromedp库来下载Instagram上的图片。我们将通过一个简单的示例来展示整个过程,包括如何设置爬虫代理IP以绕过网络限制。 背景/引言 在数据采集和自动化测试领域,Go语言以其出色的执行效率、简洁的语法和卓越的…

【科学研究】那些考进精英大学的农家子弟们

::: block-1 “时问桫椤”是一个致力于为本科生到研究生教育阶段提供帮助的不太正式的公众号。我们旨在在大家感到困惑、痛苦或面临困难时伸出援手。通过总结广大研究生的经验,帮助大家尽早适应研究生生活,尽快了解科研的本质。祝一切顺利!—…

智能化转型的得力助手:山海鲸智慧工厂解决方案详解

在数字化浪潮席卷全球的今天,工业领域正迎来一场前所未有的智能化变革。作为这一变革的领军者,山海鲸智慧工厂解决方案以其前瞻性的技术理念和创新的解决方案,为工业发展注入了强大的动力。 山海鲸智慧工厂解决方案的核心在于其高度的集成性…

Windows Server 2012 R2 中 IIS 8.5 安装证书

文章目录 前言一、获取服务器证书二、证书格式转换二、IIS8安装证书1.Win R 键打开运行窗口 → 输入【inetmgr】→ 点击【确定】2.打开【IIS管理器】→ 点击计算机名称 → 双击打开【服务器证书】3.点击【导入】4.选择证书文件 → 输入密码 → 点击【确定】5.选择要使用证书的…

14. Spring AOP(二)实现原理

源码位置:spring_aop 上一篇文章中我们主要学习了AOP的思想和Spring AOP使用,本文讲的是Spring是如何实现AOP的,Spring AOP是基于动态代理来实现AOP的,在将动态代理之前先来了解一下什么是代理模式。 1. 代理模式 在现实中就有许…

深度学习transformer架构详细详解

一、transformer的贡献 transformer架构的贡献:该架构只使用自注意力机制,没有使用RNN或卷积网络。且可以实现并行计算,加快模型训练速度。 (将所有的循环层全部换成:multi-headed self-attention) 二、t…

VisualStudio2019和2022开发Winform项目用到Devexpress组件报错不能正确加载的解决办法

1.报错1 问题简单描述:DevExpress.Utils.ImageCollectionStreamer 无法强制转换为 DevExpress.Utils.ImageCollectionStreamer。 原因分析:原项目某个组件使用的是 DevExpresss.XtraBars.v15.1版本,直接引用扩展控件改成引用v20.2。 解决办法…

负载均衡集群——Nginx

目录 1.Nginx反向代理实战 2.Nginx 反向代理和负载均衡实践 实验操作步骤 步骤 1 Nginx1 和 Nginx2 配置 步骤2 测试资源是否可用 步骤 3 安装和配置 Nginx 代理 步骤 4 代理服务器配置检测 步骤 5 在 Nginx1 和 Nginx2 配置虚拟主机 步骤 6 将虚拟主机添加入后端主机组…

BMR:基于Boostrapping多视图的虚假新闻检测

一、概述 文章提出了三种视图信息来表示一篇新闻:文本、图像结构、图像语义。然后设计了改进的多门混合专家系统(iMMoE)来进行信息融合。保留单模态信息来保证特征对新闻的保真性,增加的多模态信息能保证不同模态的一致性&#xf…

【KingSCADA】通过地址引用和弹窗模板实现设备控制

当相同的设备过多时,要做很多相同的弹窗,这种情况下可以通过地址引用和弹窗模板实现设备控制。 1.变量创建 2.画面开发 以阀门控制为例,只需要做一个阀门控制界面模板 3.地址引用 # 4.实现效果

每日算法4/21

LCR 073. 爱吃香蕉的狒狒 题目 狒狒喜欢吃香蕉。这里有 N 堆香蕉,第 i 堆中有 piles[i] 根香蕉。警卫已经离开了,将在 H 小时后回来。 狒狒可以决定她吃香蕉的速度 K (单位:根/小时)。每个小时,她将会选…

web自动化系列-selenium的3种弹框操作(十二)

在进行功能测试时 ,经常会遇到出现各种的弹出的提示 ,比如删除数据给出提示 、做某个操作时也会弹框给出一些友好提示 ,因为这些弹框都是做web操作时的一些常用组件 ,所以,selenium就不得不支持这些组件 。 1.弹框介绍…

随机森林计算指标重要性—从决策树到随机森林Python实现

文章目录 前言一、节点二、决策树2.1 案例分析——优良的水稻2.2 案例分析——家庭财富水平 三、随机森林三、Python代码实现3.1 关键问题3.1.1 节点的表示3.1.2 决策树的表示** 根节点划分左右子树的依据 **3.1.3 随机森林的构造与重要性的表示 3.2 节点类3.2 决策树类3.2.1 初…