视觉Transformers中的位置嵌入 - 研究与应用指南

视觉 Transformer 中位置嵌入背后的数学和代码简介。

自从 2017 年推出《Attention is All You Need》以来,Transformer 已成为自然语言处理 (NLP) 领域最先进的技术。 2021 年,An Image is Worth 16x16 Words² 成功地将 Transformer 应用于计算机视觉任务。从那时起,人们提出了许多基于Transformer的计算机视觉架构。

本文[1]研究了为什么位置嵌入是视觉Transformer的必要组成部分,以及不同的论文如何实现位置嵌入。它包括位置嵌入的开源代码以及概念解释。所有代码都使用 PyTorch 包。

为什么使用位置嵌入?

Attention is All You Need 指出,Transformer由于缺乏递归或卷积,无法学习有关一组标记顺序的信息。如果没有位置嵌入,Transformer对于标记的顺序是不变的。对于图像,这意味着可以对图像的补丁进行加扰,而不会影响预测的输出。

让我们看一下 Luis Zuno 的像素艺术《黄昏山》中补丁顺序的示例。原始艺术作品已被裁剪并转换为单通道图像。这意味着每个像素都有一个介于 0 和 1 之间的值。单通道图像通常以灰度显示;但是,我们将以紫色配色显示它,因为它更容易看到。

mountains = np.load(os.path.join(figure_path, 'mountains.npy'))

H = mountains.shape[0]
W = mountains.shape[1]
print('Mountain at Dusk is H =', H, 'and W =', W, 'pixels.')
print('\n')

fig = plt.figure(figsize=(10,6))
plt.imshow(mountains, cmap='Purples_r')
plt.xticks(np.arange(-0.5, W+110), labels=np.arange(0, W+110))
plt.yticks(np.arange(-0.5, H+110), labels=np.arange(0, H+110))
plt.clim([0,1])
cbar_ax = fig.add_axes([0.95.110.050.77])
plt.clim([01])
plt.colorbar(cax=cbar_ax);
#plt.savefig(os.path.join(figure_path, 'mountains.png'), bbox_inches='tight')
alt

我们可以将此图像分割成大小为 20 的块。

P = 20
N = int((H*W)/(P**2))
print('There will be', N, 'patches, each', P, 'by', str(P)+'.')
print('\n')

fig = plt.figure(figsize=(10,6))
plt.imshow(mountains, cmap='Purples_r')
plt.clim([0,1])
plt.hlines(np.arange(P, H, P)-0.5-0.5, W-0.5, color='w')
plt.vlines(np.arange(P, W, P)-0.5-0.5, H-0.5, color='w')
plt.xticks(np.arange(-0.5, W+110), labels=np.arange(0, W+110))
plt.yticks(np.arange(-0.5, H+110), labels=np.arange(0, H+110))
x_text = np.tile(np.arange(9.5, W, P), 3)
y_text = np.repeat(np.arange(9.5, H, P), 5)
for i in range(1, N+1):
    plt.text(x_text[i-1], y_text[i-1], str(i), color='w', fontsize='xx-large', ha='center')
plt.text(x_text[2], y_text[2], str(3), color='k', fontsize='xx-large', ha='center');
#plt.savefig(os.path.join(figure_path, 'mountain_patches.png'), bbox_inches='tight')
alt

据称,视觉Transformer将无法区分原始图像和补丁被打乱的版本。

np.random.seed(21)
scramble_order = np.random.permutation(N)
left_x = np.tile(np.arange(0, W-P+120), 3)
right_x = np.tile(np.arange(P, W+120), 3)
top_y = np.repeat(np.arange(0, H-P+120), 5)
bottom_y = np.repeat(np.arange(P, H+120), 5)

scramble = np.zeros_like(mountains)
for i in range(N):
    t = scramble_order[i]
    scramble[top_y[i]:bottom_y[i], left_x[i]:right_x[i]] = mountains[top_y[t]:bottom_y[t], left_x[t]:right_x[t]]
    
fig = plt.figure(figsize=(10,6))
plt.imshow(scramble, cmap='Purples_r')
plt.clim([0,1])
plt.hlines(np.arange(P, H, P)-0.5-0.5, W-0.5, color='w')
plt.vlines(np.arange(P, W, P)-0.5-0.5, H-0.5, color='w')
plt.xticks(np.arange(-0.5, W+110), labels=np.arange(0, W+110))
plt.yticks(np.arange(-0.5, H+110), labels=np.arange(0, H+110))
x_text = np.tile(np.arange(9.5, W, P), 3)
y_text = np.repeat(np.arange(9.5, H, P), 5)
for i in range(N):
    plt.text(x_text[i], y_text[i], str(scramble_order[i]+1), color='w', fontsize='xx-large', ha='center')
    
i3 = np.where(scramble_order==2)[0][0]
plt.text(x_text[i3], y_text[i3], str(scramble_order[i3]+1), color='k', fontsize='xx-large', ha='center');
#plt.savefig(os.path.join(figure_path, 'mountain_scrambled_patches.png'), bbox_inches='tight')
alt

显然,这是与原始图像非常不同的图像,并且您不希望视觉Transformer将这两个图像视为相同。

排列的注意力不变性

让我们研究一下视觉Transformer对于标记顺序不变的说法。Transformer中对 token 顺序不变的组件是注意力模块。

注意力是根据三个矩阵(查询、键和值)计算得出的,每个矩阵都是通过将token传递到线性层而生成的。生成 Q、K 和 V 矩阵后,将使用以下公式计算注意力。

alt

其中 Q、K、V 分别是查询、键和值; dₖ 是缩放值。为了证明注意力对 token 顺序的不变性,我们将从三个随机生成的矩阵开始来表示 Q、K 和 V。Q、K 和 V 的形状如下:

alt

在此示例中,我们将使用 4 个预计长度为 9 的标记。矩阵将包含整数以避免浮点乘法错误。生成后,我们将交换token 0 和token 2 在所有三个矩阵中的位置。具有交换标记的矩阵将用下标 s 表示。

n_tokens = 4
l_tokens = 9
shape = n_tokens, l_tokens
mx = 20 #max integer for generated matricies

# Generate Normal Matricies
np.random.seed(21)
Q = np.random.randint(1, mx, shape)
K = np.random.randint(1, mx, shape)
V = np.random.randint(1, mx, shape)

# Generate Row-Swapped Matricies
swapQ = copy.deepcopy(Q)
swapQ[[02]] = swapQ[[20]]
swapK = copy.deepcopy(K)
swapK[[02]] = swapK[[20]]
swapV = copy.deepcopy(V)
swapV[[02]] = swapV[[20]]

# Plot Matricies
fig, axs = plt.subplots(nrows=3, ncols=2, figsize=(8,8))
fig.tight_layout(pad=2.0)
plt.subplot(321)
mat_plot(Q, 'Q')
plt.subplot(322)
mat_plot(swapQ, r'$Q_S$')
plt.subplot(323)
mat_plot(K, 'K')
plt.subplot(324)
mat_plot(swapK, r'$K_S$')
plt.subplot(325)
mat_plot(V, 'V')
plt.subplot(326)
mat_plot(swapV, r'$V_S$')
alt

注意力公式中的第一个矩阵乘法是 Q·Kᵀ=A,其中得到的矩阵 A 是一个大小等于 token 数量的正方形。当我们用 Qₛ 和 Kₛ 计算 Aₛ 时,得到的 Aₛ 的行 [0, 2] 和列 [0,2] 都与 A 交换。

A = Q @ K.transpose()
swapA = swapQ @ swapK.transpose()
modA = copy.deepcopy(A)
modA[[0,2]] = modA[[2,0]] #swap rows
modA[:, [20]] = modA[:, [02]] #swap cols

fig, axs = plt.subplots(nrows=1, ncols=3, figsize=(8,3))
fig.tight_layout(pad=1.0)
plt.subplot(131)
mat_plot(A, r'$A = Q*K^T$')
plt.subplot(132)
mat_plot(swapA, r'$A_S = Q_S * K_S^T$')
plt.subplot(133)
mat_plot(modA, 'A\nwith rows [0,2] swaped\n and cols [0,2] swaped')
alt

下一个矩阵乘法是 A·V=A,其中生成的矩阵 A 与初始 Q、K 和 V 矩阵具有相同的形状。当我们用 Aₛ 和 Vₛ 计算 Aₛ 时,得到的 Aₛ 的行 [0,2] 与 A 交换。

A = A @ V
swapA = swapA @ swapV
modA = copy.deepcopy(A)
modA[[0,2]] = modA[[2,0]] #swap rows

fig, axs = plt.subplots(nrows=2, ncols=2, figsize=(127))
fig.tight_layout(pad=1.0)
plt.subplot(221)
mat_plot(A, r'$A = A*V$')
plt.subplot(222)
mat_plot(swapA, r'$A_S = A_S * V_S$')
plt.subplot(224)
mat_plot(modA, 'A\nwith rows [0,2] swaped')
axs[1,0].axis('off')
alt

这表明,更改注意力层输入中标记的顺序会导致输出注意矩阵的相同标记行发生变化。因为注意力是对标记之间关系的计算。如果没有位置信息,更改token顺序不会改变token的关联方式。

示例

定义位置嵌入

现在,我们可以看看正弦位置嵌入的细节。该代码基于 Tokens-to-Token ViT 的公开可用 GitHub 代码。从功能上来说,位置嵌入是一个与 token 形状相同的矩阵。这看起来像:

alt

正弦位置嵌入公式如下所示

alt

其中 PE 是位置嵌入矩阵,i 是沿着标记的数量,j 是沿着标记的长度,d 是标记长度。代码实现:

def get_sinusoid_encoding(num_tokens, token_len):
    """ Make Sinusoid Encoding Table

        Args:
            num_tokens (int): number of tokens
            token_len (int): length of a token
            
        Returns:
            (torch.FloatTensor) sinusoidal position encoding table
    """


    def get_position_angle_vec(i):
        return [i / np.power(100002 * (j // 2) / token_len) for j in range(token_len)]

    sinusoid_table = np.array([get_position_angle_vec(i) for i in range(num_tokens)])
    sinusoid_table[:, 0::2] = np.sin(sinusoid_table[:, 0::2])
    sinusoid_table[:, 1::2] = np.cos(sinusoid_table[:, 1::2]) 

    return torch.FloatTensor(sinusoid_table).unsqueeze(0)

让我们生成一个示例位置嵌入矩阵。我们将使用 176 个tokens。每个token的长度为 768,这是 T2T-ViT代码中的默认长度。一旦生成了矩阵,我们就可以绘制它。

PE = get_sinusoid_encoding(num_tokens=176, token_len=768)

fig = plt.figure(figsize=(108))
plt.imshow(PE[0, :, :], cmap='PuOr_r')
plt.xlabel('Along Length of Token')
plt.ylabel('Individual Tokens');
cbar_ax = fig.add_axes([0.95.360.050.25])
plt.clim([-11])
plt.colorbar(label='Value of Position Encoding', cax=cbar_ax);
#plt.savefig(os.path.join(figure_path, 'fullPE.png'), bbox_inches='tight')
alt

放大标记的开头。

fig = plt.figure()
plt.imshow(PE[0, :, 0:301], cmap='PuOr_r')
plt.xlabel('Along Length of Token')
plt.ylabel('Individual Tokens');
cbar_ax = fig.add_axes([0.95.20.050.6])
plt.clim([-11])
plt.colorbar(label='Value of Position Encoding', cax=cbar_ax);
#plt.savefig(os.path.join(figure_path, 'zoomedinPE.png'), bbox_inches='tight')
alt

具有正弦结构!

将位置嵌入应用于tokens

现在,我们可以将位置嵌入添加到我们的tokens中!我们将使用《Mountain at Dusk》,并具有与上述相同的补丁标记化。这将为我们提供 15 个长度为 20²=400 的token。

fig = plt.figure(figsize=(10,6))
plt.imshow(mountains, cmap='Purples_r')
plt.hlines(np.arange(P, H, P)-0.5-0.5, W-0.5, color='w')
plt.vlines(np.arange(P, W, P)-0.5-0.5, H-0.5, color='w')
plt.xticks(np.arange(-0.5, W+110), labels=np.arange(0, W+110))
plt.yticks(np.arange(-0.5, H+110), labels=np.arange(0, H+110))
x_text = np.tile(np.arange(9.5, W, P), 3)
y_text = np.repeat(np.arange(9.5, H, P), 5)
for i in range(1, N+1):
    plt.text(x_text[i-1], y_text[i-1], str(i), color='w', fontsize='xx-large', ha='center')
plt.text(x_text[2], y_text[2], str(3), color='k', fontsize='xx-large', ha='center')
cbar_ax = fig.add_axes([0.95.110.050.77])
plt.clim([01])
plt.colorbar(cax=cbar_ax);
#plt.savefig(os.path.join(figure_path, 'mountain_patches_w_colorbar.png'), bbox_inches='tight')
alt

当我们将这些补丁转换为token时,它看起来像

tokens = np.zeros((1520**2))
for i in range(15):
    patch = gray_mountains[top_y[i]:bottom_y[i], left_x[i]:right_x[i]]
    tokens[i, :] = patch.reshape(120**2)
tokens = tokens.astype(int)
tokens = tokens/255

fig = plt.figure(figsize=(10,6))
plt.imshow(tokens, aspect=5, cmap='Purples_r')
plt.xlabel('Length of Tokens')
plt.ylabel('Number of Tokens')
cbar_ax = fig.add_axes([0.95.360.050.25])
plt.clim([01])
plt.colorbar(cax=cbar_ax)
alt

现在,我们可以以正确的形状进行位置嵌入:

PE = get_sinusoid_encoding(num_tokens=15, token_len=400).numpy()[0,:,:]
fig = plt.figure(figsize=(10,6))
plt.imshow(PE, aspect=5, cmap='PuOr_r')
plt.xlabel('Length of Tokens')
plt.ylabel('Number of Tokens')
cbar_ax = fig.add_axes([0.95.360.050.25])
plt.clim([01])
plt.colorbar(cax=cbar_ax)
alt

我们现在准备将位置嵌入添加到标记中。位置嵌入中的紫色区域将使令牌变暗,而橙色区域将使它们变亮。

mountainsPE = tokens + PE
resclaed_mtPE = (position_mountains - np.min(position_mountains)) / np.max(position_mountains - np.min(position_mountains))

fig = plt.figure(figsize=(10,6))
plt.imshow(resclaed_mtPE, aspect=5, cmap='Purples_r')
plt.xlabel('Length of Tokens')
plt.ylabel('Number of Tokens')
cbar_ax = fig.add_axes([0.95.360.050.25])
plt.clim([01])
plt.colorbar(cax=cbar_ax)
alt

您可以从原始token中看到结构,以及位置嵌入中的结构!这两条信息都将被转发到Transformer中。

Reference
[1]

Source: https://towardsdatascience.com/position-embeddings-for-vision-transformers-explained-a6f9add341d5

本文由 mdnice 多平台发布

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

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

相关文章

idea中引入新JDK环境

在不同的项目中往往会需要不同的运行环境,那么如何下载一个新的环境并运用到idea中呢? 下面给出的就是oracle官网,以JDK17为例教大家如何下载 Java Archive Downloads - GraalVM for JDK 17https://www.oracle.com/java/technologies/javase…

Python数据可视化库之bashplotlib使用详解

概要 在数据可视化领域,Python拥有许多优秀的库,如Matplotlib、Seaborn等,它们可以创建漂亮而复杂的图形。但是,有时候我们可能需要在终端中绘制简单的图形,这时候Bashplotlib就派上了用场。Bashplotlib是一个Python库,可以在终端中绘制基本的图形,如条形图、散点图等。…

YOLOV9训练集制作+Train+Val记录

一、YOLO数据集格式分布 在YOLO中,数据集的分布如图,在dataset文件夹下有imags(图片)和labels(标签)。在images和labels文件夹下又分别存放三个文件夹,分别对应测试集、训练集、验证集&#xff…

使用postman测试若依其他业务接口API—3

请求方式 如上,使用Get请求来获取练习题库中的所有习题数据。 请求地址 在请求路径栏输入请求地址,以下图为例: 参数体与鉴权 在Parms键入所需参数,其中key为键,value为键的值:如下图所示: 认证成功与失…

持续集成(CICD)- gogs仓库的部署和使用

文章目录 一、gogs的介绍二、部署gog仓库三、首次启动gogs四、登录五、创建一个非空仓库六、从仓库拉取代码到本地七、把本地编辑的代码上传到仓库 一、gogs的介绍 Gogs作为一个轻量级、易于部署和使用的自托管Git服务,为小型团队和个人开发者提供了一个简单而强大…

【AIGC】如何提高Prompt准确度

前言 随着人工智能的迅猛进展,AIGC(通用人工智能聊天工具)已成为多个行业中不可或缺的自然语言处理技术。Prompt作为AIGC系统的一项关键功能,在工具的有效运作中发挥了举足轻重的作用。本篇文章将深入探讨Prompt与AIGC之间的紧密…

python笔记_程序流程控制2

C&#xff0c;循环控制 1&#xff0c;for循环 功能&#xff1a;让代码循环运行 语法&#xff1a; for <变量> in <范围、序列>&#xff1a; <循环操作语句> 例 nums &#xff08;1,2,3,4&#xff09; <class list> for i in nums&#xff1a; print&…

Java中文件的相关知识及文件IO操作

在我们日常生活中&#xff0c;会把许多东西都称之为文件。比如&#xff0c;一份纸质报告&#xff0c;或u盘中的一些文档&#xff0c;都会把它们称为文件。那么&#xff0c;这里说的文件是以操作系统的角度出发的。在操作系统中&#xff0c;会把许多硬件设备和软件资源都抽象成“…

机器学习:主成分分析笔记

主成分分析&#xff08;Principal Component Analysis&#xff0c;PCA&#xff09;是一种无监督的机器学习算法&#xff0c;通常用于高维数据的降维、提取主要特征、数据降噪和可视化。PCA的基本思想是将原始数据的多个变量转换为少数几个相互独立的变量&#xff08;即主成分&a…

shadertoy 游戏《来自星尘》摇杆复刻

正确的做法应该是上 noise 而不是叠加 sin 波&#xff0c;不过如果不想麻烦的话叠波还是一个不错的选择&#xff1a;整体效果如下&#xff0c;已经非常形似 直接上链接&#xff1a;Shader - Shadertoy BETA float radiusScale 0.9; float variation(vec2 v1, vec2 v2, float …

传感器---触摸传感器

一、模块选型概述 芯片型号&#xff1a;TTP223B 供电电压&#xff1a;3-5V 控制接口&#xff1a;共三个引脚&#xff08;GND、VCC、SIG&#xff09;&#xff0c;GND为地&#xff0c;VCC为供电电源&#xff0c;SIG为数字信号输出脚&#xff1b; PCB尺寸&#xff1a;24 x 24 mm 触…

Linux中服务端开发

1 创建socket,返回一个文件描述符lfd---socket(); 2 将lfd和IP&#xff0c;PROT进行绑定---bind(); 3 将lfd由主动变成被动监听---listen(); 4 接收一个新的连接&#xff0c;得到一个的文件描述符cfd--accept() --该文件描述符用于与客户端通信 5 while(1) { 接受数据&a…

网络安全: Kali Linux 使用 docker-compose 部署 openvas

目录 一、实验 1.环境 2.Kali Linux 安装docker与docker-compose 3.Kali Linux 使用docker-compose方式部署 openvas 4. KaliLinux 使用openvas 二、问题 1. 信息安全漏洞库 2.信息安全漏洞共享平台 3.Windows 更新指南与查询 4.CVE 查询 5.docker-compose 如何修改o…

前后端分离项目Docker部署指南(上)

目录 前言 一.搭建局域网 1.搭建net-ry局域网&#xff0c;用于部署若依项目 2.注意点 二.安装redis 创建目录 将容器进行挂载 ​编辑 测试是否安装成功 ​编辑 三. 安装MySQL 创建文件夹 上传配置文件并且修改 .启动MySQL容器服务 充许远程连接 四.部署后端 使用…

LLVM clang_tokenize 的示例

1&#xff0c; 环境搭建 如同上篇 git clong llvm-project git checkout llmvorg-3.4.0 # or llvmorg-18.1.rc /bin/bash ./confiure --prefix....... # or cmake Debug make -j32 make -j install 2&#xff0c; 示例代码 Makefile&#xff1a;如同上篇 LLVM_CON…

NOIP 2010普及组初赛试题及解析

NOIP 2010普及组初赛试题及解析 一. 单项选择题 &#xff08;共20题&#xff0c;每题1.5分&#xff0c;共计30分。每题有且仅有一个正确答案.&#xff09;。二. 问题求解&#xff08;共2题&#xff0c;每题5分&#xff0c;共计10分&#xff09;三. 阅读程序写结果&#xff08;共…

基于YOLOv8深度学习的农作物幼苗与杂草检测系统【python源码+Pyqt5界面+数据集+训练代码】深度学习实战、目标检测

《博主简介》 小伙伴们好&#xff0c;我是阿旭。专注于人工智能、AIGC、python、计算机视觉相关分享研究。 ✌更多学习资源&#xff0c;可关注公-仲-hao:【阿旭算法与机器学习】&#xff0c;共同学习交流~ &#x1f44d;感谢小伙伴们点赞、关注&#xff01; 《------往期经典推…

kafka启动命令、查看topic命令、查看消息内容命令

kafka启动命令 cd /opt/kafka/kafka_2.12-3.5.1/bin ./kafka-server-start.sh ../config/server.properties Windows环境下用kafka Tool 连不上虚拟机的broker报了unable to connect broker 0&#xff0c; 但是zookeeper可以连接上 server.properties的listeners改为listene…

006-CSS-常见问题汇总

常见问题汇总 1、伪元素与伪类2、偏门但好用的样式3、文字溢出三个点展示4、空白折叠问题5、文字的垂直居中6、 Vue项目中 在父组件中修改子组件样式7、BFC 概念7.1、兄弟元素外边距合并7.2、父子元素外边距塌陷 8、box-sizing8.1、box-sizing: border-box8.2、box-sizing: con…

Vue.js 深度解析:nextTick 原理与应用

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…