Transformer系列-6丨LRNet和Stand-Alone Self-Attention网络解析

news2024/11/28 8:33:59

0. 前言

本次主要分享之前看的两篇将自注意力机制self-attention应用在视觉感知任务的文章,分别为LRNetStand-alone self-attention。为了深化读者的理解,本文提供了较为详细的中文注释的代码。

首先了解一下这两篇文章的背景,其都是在Vision Transformer(ViT)提出之前将transformer应用在感知任务上的尝试。尽管这些方法没有取得像ViT那么大的关注度,其后续的影响也较为深刻。

之前的内容中,我们就ViT模型也做了较为详细的分享。具体链接如下:

  • 【Transformer系列】Vision Transformer (ViT)模型解析

感兴趣的小伙伴们可以重温一下!ViT的设计可谓大道至简,而本文将介绍的两篇工作的网络设计较ViT可能更复杂一些。

论文名《Local Relation Networks for Image Recognition》

发表:ICCV 2019
所属领域(关键词):视觉transformer,local relation

针对所有自学遇到困难的同学们,我帮大家系统梳理大模型学习脉络,将这份 LLM大模型资料 分享出来:包括LLM大模型书籍、640套大模型行业报告、LLM大模型学习视频、LLM大模型学习路线、开源大模型学习教程等, 😝有需要的小伙伴,可以 扫描下方二维码领取🆓↓↓↓

👉[CSDN大礼包🎁:全网最全《LLM大模型入门+进阶学习资源包》免费分享(安全链接,放心点击)]()👈

### 1.动机和创新

人类具有通过有限的手段“看到无限世界”的非凡能力。这是通过从一组有限的低级视觉原语出发,创造性地组合出无限的高级视觉概念来实现的,进而形成对观察场景的理解。

在计算机视觉中,这种组合行为可以通过构建卷积神经网络中的层次化表示来近似实现,不同层次代表不同级别的视觉元素。低层提取基本元素如边缘中间层组合这些基本元素形成物体部分高层则表示整个物体

尽管卷积层可以构建一个层次化的表示,但其将低级元素组合成高级实体的机制在概念推理方面被视为高度低效。卷积层更像是模板,通过卷积滤波器权重在空间上聚合输入特征,而不是识别元素如何有意义地结合在一起。

卷积在训练之后,应用固定的权重,而不管视觉输入的任何改变。

如图,在鸟的眼睛和嘴之间空间是多变的,用固定卷积核的卷积来表示需要3个channel,而如果可以在局部区域内的视觉元素之间自适应地推断出有意义的组合结构,并基于局部像素对的可组合性调整聚合权重,则只需要一个channel即可。

为了实现这一点,本文提出了local relation layer(局部关系层),与使用固定聚合权重的卷积层不同,这个新层基于局部像素对的可组合性(Composability)调整聚合权重。通过学习如何在局部区域内自适应地组合像素,从而构建一个更有效和高效的组合层次结构。

由于这种局部关系层可以直接替换深度网络中的卷积层,几乎不增加额外开销。作者开发了一种名为局部关系网络(Local Relation Network, LR-Net)的网络架构,该架构在ResNet的基础上通过堆叠残差块来优化非常深的网络。

需要提到的是,上面提到的可组合性(composability)是指在视觉元素(如像素)之间根据它们的特征在一个学习到的嵌入空间中的相似性动态确定它们是否可以组合在一起的能力。简单来说,可组合性是衡量两个视觉元素(比如像素)之间能否根据它们的特征被有效组合的一种度量。这种度量不是静态的,而是通过学习得到的,能够根据元素特征的相似性自适应地调整。

本文是VIT(Vision Transformer)提出前,研究者将transformer和视觉任务融合的一次尝试。

2.思路和方法

和我们知道的self-attention不同的是(key的选取不再是全图所有像素/cell,而是以query为中心的一个区域中所有的像素,大小为)。local relation layer(局部关系层)具体的实现如下:

  • 输入Input Feature,分别通过一个1×1大小的卷积核,得到Key Map和Query Map,通道数为,这里的可以视为注意力头的个数。

  • 对于Query Map中的每一个位置,在Key Map中提取出一个以该点为中心,的区域,然后对应像素点相乘,得到Appearance Composability,其大小为。

  • 构建几何先验值Gemotry Prior为Appearance Composability提供位置/几何信息。首先构建的相对位置。这里的2可以认为其中一层表示坐标,另一层表示坐标。然后将这个的Position经过两个1×1大小的卷积层,得到Geometry Prior,其大小为。中间使用ReLU激活函数。作者发现,使用该小型网络来间接计算几何先验值直接学习这些值更有效,特别是当邻域大小较大时。这可能是因为小型网络将相对位置视为度量空间中的向量,而直接学习方法则将不同的相对位置视为独立的实体。这样的向量表示允许模型更好地理解和利用空间关系。

  • 将Geometry Prior和Appearance Composability相加,然后经过Softmax,得到某query位置特定的参数Aggregation Weights,其大小为。

  • 对Input Feature提取出对应的一个的区域(value),然后与Aggregation Weights进行参数聚合,即对范围进行加权平均操作,最终得到该点的结果,大小为。

  • 遍历所有的query的空间尺度(大小为),聚合成Aggregation Feature,其大小为。

  • 最后将其经过一个1×1的卷积层,得到Output Feature,其大小为C×W×H

local relation layer(局部关系层)具体的实现代码如下:

import torch

# 几何先验模块
class GeometryPrior(torch.nn.Module):
  def __init__(self, k, channels, multiplier=0.5):
      super(GeometryPrior, self).__init__()
      # 初始化参数
      self.channels = channels  # 通道数
      self.k = k  # 局部区域的大小
      # 初始化位置信息,范围在-1到1之间
      self.position = 2 * torch.rand(1, 2, k, k, requires_grad=True) - 1
      # 两个1x1卷积层用于处理位置信息
      self.l1 = torch.nn.Conv2d(2, int(multiplier * channels), 1)
      self.l2 = torch.nn.Conv2d(int(multiplier * channels), channels, 1)
      
  def forward(self, x):
      # 通过两个卷积层处理位置信息,使用ReLU激活函数
      x = self.l2(torch.nn.functional.relu(self.l1(self.position)))
      # 调整输出形状
      return x.view(1, self.channels, 1, self.k ** 2)

# Key和Query映射模块
class KeyQueryMap(torch.nn.Module):
  def __init__(self, channels, m):
      super(KeyQueryMap, self).__init__()
      # 1x1卷积层用于生成Key和Query映射
      self.l = torch.nn.Conv2d(channels, channels // m, 1)
  
  def forward(self, x):
      # 应用卷积层
      return self.l(x)

# 外观可组合性模块
class AppearanceComposability(torch.nn.Module):
  def __init__(self, k, padding, stride):
      super(AppearanceComposability, self).__init__()
      # 初始化参数
      self.k = k  # 局部区域的大小
      # Unfold操作用于提取局部区域
      self.unfold = torch.nn.Unfold(k, 1, padding, stride)
  
  def forward(self, x):
      key_map, query_map = x
      # 提取Key和Query的局部区域
      key_map_unfold = self.unfold(key_map)
      query_map_unfold = self.unfold(query_map)
      # 调整形状,进行相乘操作,这里的self.k**2//2:self.k**2//2+1为选择区域的中心点
      return key_map_unfold * query_map_unfold[:, :, :, self.k**2//2:self.k**2//2+1]

# 结合几何先验和外观可组合性
def combine_prior(appearance_kernel, geometry_kernel):
  # 使用Softmax结合两者
  return torch.nn.functional.softmax(appearance_kernel + geometry_kernel, dim=-1)

# 局部关系层
class LocalRelationalLayer(torch.nn.Module):
  def __init__(self, channels, k, stride=1, m=None, padding=0):
      super(LocalRelationalLayer, self).__init__()
      # 初始化参数
      self.channels = channels  # 通道数
      self.k = k  # 局部区域的大小
      self.stride = stride  # 步长
      self.m = m or 8  # 注意力头的数量,默认为8
      self.padding = padding  # 填充
      # 初始化各个子模块
      self.kmap = KeyQueryMap(channels, k)
      self.qmap = KeyQueryMap(channels, k)
      self.ac = AppearanceComposability(k, padding, stride)
      self.gp = GeometryPrior(k, channels//m)
      self.unfold = torch.nn.Unfold(k, 1, padding, stride)
      self.final1x1 = torch.nn.Conv2d(channels, channels, 1)
      
  def forward(self, x):
      # 计算几何先验
      gpk = self.gp(0)
      # 生成Key和Query映射
      km = self.kmap(x)
      qm = self.qmap(x)
      # 计算外观可组合性
      ak = self.ac((km, qm))
      # 结合几何先验和外观可组合性
      ck = combine_prior(ak, gpk)[:, None, :, :, :]
      # 提取输入特征的局部区域
      x_unfold = self.unfold(x)
      # 调整形状并进行加权平均
      pre_output = (ck * x_unfold).view(x.shape[0], x.shape[1], -1, x_unfold.shape[-2] // x.shape[1])
      # 计算输出特征图的高和宽
      h_out = (x.shape[2] + 2 * self.padding - 1 * (self.k - 1) - 1) // self.stride + 1
      w_out = (x.shape[3] + 2 * self.padding - 1 * (self.k - 1) - 1) // self.stride + 1                               
      # 聚合特征并调整形状
      pre_output = torch.sum(pre_output, axis=-1).view(x.shape[0], x.shape[1], h_out, w_out)
      # 应用最后的1x1卷积层并返回结果
      return self.final1x1(pre_output)

论文名《Stand-Alone Self-Attention in Vision Models》

发表:NeurIPS 2019
所属领域(关键词):视觉transformer,全注意力视觉模型

1.动机和创新

这一篇和上一篇很接近,连方法论都很相似。动机的话,主要是考虑到卷积神经网络在捕获长范围交互时的挑战性,而自注意力恰好可以弥补这一点不足。

现有一些方法已经结合CNN+Self-attention已经实现了较好的结果,其中Self-attention主要用来实现基于内容(content)的交互的。

本文作者是想构建一个完全基于注意力的视觉模型,这也是Stand-Alone一词的由来,这样的好处是能够直接捕获长距离交互

当然,这篇论文也是通过在value上构建大小来实现局部关联(local correlation)。为什么选择局部,是因为如果选择全局注意力实现全局关联,那么运算量会很大

2.思路和方法

如下图所示为本文提出方法的大致示意图:

可见和上面提到的Local Relation Layer很像。

由于这篇论文没有给出详细的网络结构图,这里我给出CoTNet中关于这篇论文的网络图,具体如下:

这个流程其实和Local Relation Layer很像,但是这个图画的远没有Local Relation Layer的直观(因为他省略了很多步骤,比如取的区域,而且他也不是以一个query位置为例的,而是以整体实现过程中的张量运算为例)。

这里我以一个query位置为例,拆开来说一下它的运算流程,如下:

  • 输入Input Feature(通道数为),分别通过一个1×1大小的卷积核,得到Key Map和Query Map,通道数为,其中,视为注意力头的数量

  • 对于每个注意力头中Query Map的每一个位置,在Key Map中提取出一个以该点为中心,的区域,然后对应像素点相乘,得到一个关联图,其大小为。图中的****其实是遍历了所有query和其对应局部key的结果

  • 为区域构建相对位置位置编码,其中query位置为原点,那么具体相对位置编码如下图所示:

  • 位置编码与query相乘后与关联图相加。这等同于赋予了关联图(所在维度)相对位置信息。随后,输入到softmax,输出注意力图

  • 对Input Feature提取出对应的一个的区域(value),然后与注意力图进行乘法运算,即对范围进行加权平均操作,最终得到该点的结果,大小为。

  • 遍历所有的query的空间尺度(大小为),以及堆叠所有的注意力头,最后聚合成输出Y,其大小为。上述过程其实与Local Relation Layer过程非常接近,稍微有点不同的可能是这个是相对位置编码的差异。具体实现代码如下:

class AttentionConv(nn.Module):
    def __init__(self, in_channels, out_channels, kernel_size, stride=1, padding=0, groups=1, bias=False):
        super(AttentionConv, self).__init__()
        # 初始化输入参数
        self.out_channels = out_channels  # 输出通道数
        self.kernel_size = kernel_size  # 卷积核大小
        self.stride = stride  # 步长
        self.padding = padding  # 填充
        self.groups = groups  # 分组卷积的组数

        # 确保输出通道数可以被组数整除
        assert self.out_channels % self.groups == 0, "out_channels should be divided by groups. (example: out_channels: 40, groups: 4)"

        # 初始化相对位置编码的参数
        self.rel_h = nn.Parameter(torch.randn(out_channels // 2, 1, 1, kernel_size, 1), requires_grad=True)  # 针对高度方向的相对位置编码
        self.rel_w = nn.Parameter(torch.randn(out_channels // 2, 1, 1, 1, kernel_size), requires_grad=True)  # 针对宽度方向的相对位置编码

        # 定义key, query, value的1x1卷积层
        self.key_conv = nn.Conv2d(in_channels, out_channels, kernel_size=1, bias=bias)  # Key卷积层
        self.query_conv = nn.Conv2d(in_channels, out_channels, kernel_size=1, bias=bias)  # Query卷积层
        self.value_conv = nn.Conv2d(in_channels, out_channels, kernel_size=1, bias=bias)  # Value卷积层

        # 初始化参数
        self.reset_parameters()

    def forward(self, x):
        # 获取输入特征图的尺寸
        batch, channels, height, width = x.size()

        # 对输入特征图进行填充
        padded_x = F.pad(x, [self.padding, self.padding, self.padding, self.padding])
        # 通过1x1卷积生成query, key, value特征图
        q_out = self.query_conv(x)
        k_out = self.key_conv(padded_x)
        v_out = self.value_conv(padded_x)

        # 将key和value特征图展开,形成局部窗口
        k_out = k_out.unfold(2, self.kernel_size, self.stride).unfold(3, self.kernel_size, self.stride)
        v_out = v_out.unfold(2, self.kernel_size, self.stride).unfold(3, self.kernel_size, self.stride)

        # 将key特征图分为两部分,并加上相对位置编码
        k_out_h, k_out_w = k_out.split(self.out_channels // 2, dim=1)
        k_out = torch.cat((k_out_h + self.rel_h, k_out_w + self.rel_w), dim=1)

        # 重塑key和value特征图以适应后续计算
        k_out = k_out.contiguous().view(batch, self.groups, self.out_channels // self.groups, height, width, -1)
        v_out = v_out.contiguous().view(batch, self.groups, self.out_channels // self.groups, height, width, -1)

        # 重塑query特征图
        q_out = q_out.view(batch, self.groups, self.out_channels // self.groups, height, width, 1)

        # 计算注意力权重,并通过加权求和得到输出特征图
        out = q_out * k_out
        out = F.softmax(out, dim=-1)
        out = torch.einsum('bnchwk,bnchwk -> bnchw', out, v_out).view(batch, -1, height, width)

        return out

这里我看了一下相对位置编码的实现其实是设置成可学习的随机参数了,而不是论文中的相对位置,这一点有点奇怪,我在github的issue中也看到别人问了,貌似作者没有正面回答。

如何系统的去学习大模型LLM ?

大模型时代,火爆出圈的LLM大模型让程序员们开始重新评估自己的本领。 “AI会取代那些行业?”“谁的饭碗又将不保了?”等问题热议不断。

不如成为「掌握AI工具的技术人」,毕竟AI时代,谁先尝试,谁就能占得先机!

但是LLM相关的内容很多,现在网上的老课程老教材关于LLM又太少。所以现在小白入门就只能靠自学,学习成本和门槛很高

针对所有自学遇到困难的同学们,我帮大家系统梳理大模型学习脉络,将这份 LLM大模型资料 分享出来:包括LLM大模型书籍、640套大模型行业报告、LLM大模型学习视频、LLM大模型学习路线、开源大模型学习教程等, 😝有需要的小伙伴,可以 扫描下方二维码领取🆓↓↓↓

👉[CSDN大礼包🎁:全网最全《LLM大模型入门+进阶学习资源包》免费分享(安全链接,放心点击)]()👈

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

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

相关文章

【CSP:202309-1】坐标变换(其一)(Java)

题目链接 202309-1 坐标变换(其一) 题目描述 求解思路 直接暴力模拟即可。 实现代码 import java.util.Scanner;public class Main {public static void main(String[] args) {Scanner in new Scanner(System.in);int n, m;n in.nextInt();m in.…

Python 实现自定义异常

在Python编程中,异常处理是保证程序健壮性的重要机制。Python提供了一些内置的异常类,如ValueError、TypeError、IndexError等,开发者可以直接使用这些类来捕获和处理程序运行中出现的各种错误。然而,某些场景下,内置的…

破晓之战:物流“三剑客”扬帆东南亚

【潮汐商业评论/原创】 70公里的曼谷至春武里省之旅,在日常或许只是一趟轻松的自驾。但对于“美美”服装的员工来说,这却成了一场午夜时分的紧急挑战。 忙碌了一整天的她,直到凌晨才猛然想起,VIP客户的货物必须在清晨前送达春武…

Android.bp和Android.mk文件有的区别

文章目录 1. 构建系统2. 语法和格式3. 可维护性和扩展性4. 编译效率5. 未来趋势 在Android的构建系统中, Android.mk和 android.bp是用于定义如何编译项目文件的两种文件类型,它们有一些显著的区别。 1. 构建系统 Android.mk:使用于基于GN…

软考高级考完了,怎么评职称?

首先祝贺您成功获得了软考高级证书,拥有了高级证书意味着具备了申请高级职称的资格!这样就不会因为缺乏资格而错失申请空缺岗位评职称的机会。接下来,我将帮助您处理申请高级职称的事宜!如果对您有帮助,请不要忘记点赞…

本地生活服务商系统如何利用本地推获得更多曝光?

随着本地生活赛道中的竞争愈演愈烈,越来越多的本地生活服务商和本地生活商家开始计划着通过在本地推等平台投放相关信息,以提高品牌店铺的曝光量和知名度。不过,就目前的情况来看,绝大多数人都陷入了一种“投入多,转化…

创客匠人标杆对话(上):她如何通过“特长+赛道”实现财富升级

老蒋创客圈第64期对话标杆直播连麦,本期我们邀请到【iAMU蒙特梭利翻转星球】平台创始人申晓慧老师。 为我们揭秘“如何挖掘人生首个百万,实现财富升级?”,深度分享如何提炼用户痛点,高效引流新用户?如何通…

构建基于LLM的应用程序——为您的应用程序选择合适的LLM

。 在本章中,将引导您完成为应用程序选择合适LLM的过程。我们将涵盖以下几个主题: 市场上最具前景的LLM概览比较LLM时应使用的主要标准和工具规模与性能之间的权衡 在本章结束时,您应该能够清楚地理解如何为您的应用程序选择合适的LLM&…

18款各具特色的项目管理系统软件,项目经理用过都说好!

项目管理是指在项目活动中运用专门的知识、技能、工具和方法,使项目能够在有限资源限定条件下,实现或超过设定的需求和期望的过程。 简单来说,项目管理系统就像是交响音乐会指挥手中的乐谱,能为项目经理清晰地呈现出项目的各个音符…

第十二章 迁移学习-实战宝可梦精灵

文章目录 一、Pokemon数据集1.1 数据集收集1.2 数据集划分1.3 数据集加载1.4 数据预处理1.5 pytorch自定义数据库实现 二、ResNet网络搭建三、训练与测试四、迁移学习4.1 pytorch实现迁移学习 一、Pokemon数据集 1.1 数据集收集 # git下载 git lfs install git clone https://…

【大数据】什么是数据中台?

随着企业规模不断扩大、业务多元化——中台服务架构的应运而生。“中台”早期是由美军的作战体系演化而来的,技术上说的“中台”主要是指学习这种高效、灵活和强大的指挥作战体系。阿里在今年发布“双中台ET”数字化转型方法论,“双中台”指的是数字中台…

学生和青年研究人员该如何撰写高质量论文?教授支招:3个关键点解锁一篇成功发表的稿件

我是娜姐 迪娜学姐 ,一个SCI医学期刊编辑,探索用AI工具提效论文写作和发表。 本文通讯作者为中国科学院大学唐智勇教授,博士生导师,科技部纳米重大研究计划首席科学家。 对于学生和青年研究人员该如何撰写高质量稿件并成功发表…

揭秘Semantic Kernel:用AI自动规划和执行用户请求

在我们日益高效的开发世界中,将任务自动化并智能规划变得越来越必要。今天,我要给大家介绍一个强大的概念——Semantic Kernel中的planner功能。通过这篇文章,我们会学习到planner的工作原理以及如何实现智能任务规划。 什么是planner&#x…

vue3项目中无法实现cpolar内网穿透解决方案

运行vue3,打开cpolar启动内网穿透,结果却发现 在vue.config.js中修改为如下代码: const { defineConfig } require(vue/cli-service);module.exports defineConfig({transpileDependencies: true,devServer: {allowedHosts: all,host: 0.0…

day06-MySQL学习笔记01

2024.08.17 day06-MySQL学习笔记 前言 前面说过,三层架构,其中dao层用于操作数据。在上面的项目中,数据放在了xml文件中。在企业开发中,数据一般存储在数据库中,我们直接对数据库操作。今天就学习如何操作数据库。 首…

赛氪网技术支持第八届集创赛全国总决赛:共绘集成电路创新蓝图

赛氪网技术支持第八届集创赛全国总决赛:共绘集成电路创新蓝图 山东,2024年8月19日至21日 —— 全国瞩目的第八届全国大学生集成电路创新创业大赛(以下简称“集创赛”)全国总决赛在美丽的海滨城市山东省烟台市隆重举行。本次大赛由…

架桥机液压站比例阀放大器

架桥机液压站是专为公路桥梁建设而设计的一种重要设备,它通过先进的液压系统来实现桥梁的快速、安全架设。液压系统包括三套独立的子系统,分别服务于1号柱、2号柱以及0号柱和3号柱。每套系统均由液压泵站、液压缸、比例电磁控制阀等核心部件构成。液压泵…

IaaS,PaaS,aPaaS,SaaS,FaaS,如何区分?

​IaaS, PaaS,SaaS,aPaaS 还有一种 FaaS ,这几个都是云服务中常见的 5 大类型: IaaS:基础架构即服务,Infrastructure as a Service PaaS:平台即服务,Platform as a Service aPaaS&…

Linux_rwx权限,修改权限,修改所有者和所在组

目录 权限的基本介绍 rwx作用到文件 rwx作用到目录 权限说明案例 修改权限 修改文件所有者-chown 修改文件/目录所在组-chgrp 权限的基本介绍 第0位是文件类型,然后是所有者的权限,所属组的权限,其他用户的权限。 -代表它是一个普通…

使用VS Code开发.NET 8 环境搭建

1. sdk环境确认 -- 查看.net 版本 PS C:\Users\a> dotnet --version 8.0.303 -- 查看已安装的.net sdk 列表 PS C:\Users\a> dotnet --list-sdks 3.0.100 [C:\Program Files\dotnet\sdk] 5.0.301 [C:\Program Files\dotnet\sdk] 6.0.417 [C:\Program Files\dotnet\sdk] …