LLM Attention and Rotary Position Embedding(旋转位置编码)

旋转位置编码(Rotary Position Embedding,RoPE)是一种能够将相对位置信息依赖集成Attention计算里的方法。就是在做词表映射的时候不是单一的进行一个embedding计算,还考虑位置信息。

一些资料

[1] https://arxiv.org/pdf/2104.09864

[2] https://arxiv.org/pdf/1706.03762

https://colab.research.google.com/drive/1rPk3ohrmVclqhH7uQ7qys4oznDdAhpzF

LLM Visualization

neural networks - What exactly are keys, queries, and values in attention mechanisms? - Cross Validated

Attention

从论文里: An attention function can be described as mapping a query and a set of key-value pairs to an output. Attention即把查询向量映射成一个输出的操作。在看之前先看下embedding和qkv等几个基本概念。

Embedding

embdding的作用是词表到特征向量的映射,即把一个int的index索引映射到一个向量空间表示。比如看llama的token: https://huggingface.co/meta-llama/Llama-2-7b-hf/blob/main/tokenizer.json

截图:

vocab里面是当前模型的所有词表,比如"<unk>"对应的索引是0。通过tokenizer.endocer的编码操

作就可以将一个字段编译成一组index vector。比如:

from transformers import LlamaForCausalLM, LlamaTokenizertokenizer = LlamaTokenizer.from_pretrained("/model/Llama-2-7b-chat-hf")encode = tokenizer.encode("who")
print(encode)# [1, 1058]
# "" -> 1
# "who" -> 1058

然后得到的[1, 1058]就是embedding的输入了。embeding的计算:

>>> import torch
>>> a = torch.tensor([1])   # 输入index是1就是前面encode后的数值>>> import torch.nn as nn
>>> embedding = nn.Embedding(3, 3)
>>> embedding.weight
Parameter containing:
tensor([[ 0.0198, -0.5562,  0.8156],[-0.3192, -1.2203, -0.8307],[-0.1649, -0.2753, -0.9075]], requires_grad=True)
>>> o = embedding(a) # 从weight里取出来对应的第1行的数值。
>>> o
tensor([[-0.3192, -1.2203, -0.8307]], grad_fn=<EmbeddingBackward0>)

embeeding的操作是根据输入的index的数值(比如这里1)然后从对应的权重(embedding.weight, 3x3)里取出对应index行索引对应的权重vector(第一行,embedding.weight[1])。即一个简单的索引操作。所以这里就有一个限制,即输入的index的大小必须要小于embedding.weight的shape(0),超出了就会挂掉。另外embedding.weight的shape(1)的大小被称为hidden size,即feature的大小。后面的linear等操作都是根据这个feature来运算的。实际上,embedding将index变成了一个高维空间的表示,可以用于模型训练推理。正常下embedding的权重第一个维度大小是vocab_size,可以查看config.json配置文件。

query、keys and values

query: 希望取查询的文本对应的查找向量,即LLM的输入经过embedding映射后的向量就是一个query vector。

keys: 和query,是对输入的文本的一个向量映射。keys里存储了所有的之前LLM推理后的context的vector数值。The key vectors are used to compute how relevant each element in the input sequence is to the query.

values: values是和query一一对应的,一个key对应一个values, These values are weighted by the attention scores (computed from the query-key interaction) to determine how much each element contributes to the final output.

也就是根据key和value,来判断当前的query和哪个key的关联性最高,分数最高。那么是怎么计算的呢?参考qkv比较清楚计算方式了。

从图中可以看到对一个query([1, 0, 2])会分别和三组key/value计算,得到三组vactor。然后再把三组vector相加得到[2, 7, 1.5],这个vector在经过重复的mlp和attention和最后的logits处理得到llm模型的输出index, 比如:

第一次输入How to predict, 经过llm根据最后的softmax后vector选取最优的输出。每个有个score. (prefill的时候全部是query,没有key value的)decode的时候,query长度都是1,然后之前的context存储在所有的key value里。

Attention is all you need

An attention function can be described as mapping a query and a set of key-value pairs to an output, where the query, keys, values, and output are all vectors. attention表示:

左图是最简单的attention操作,右边是multi head attention的图. 先看左图,输入QKV,经过计算Scaled Dot Product Attention,得到一组特征向量,然后每一组query和key/value得到一个vector(如上面qkv章节的图),然后将vector相加。计算公式 :

scaled dot product attention:

query和key的维度是d_k。如qkv章节的图,q和k点乘,然后经过softmax之后得到一个score分数。在将这个score和V做乘法,即得到一个基于当前组k/v的输出特征向量。

一个scaled dot product的pytorch实现:

class ScaledDotProductAttention(nn.Module):''' Scaled Dot-Product Attention '''def __init__(self, temperature, attn_dropout=0.1):super().__init__()self.temperature = temperatureself.dropout = nn.Dropout(attn_dropout)def forward(self, q, k, v, mask=None):attn = torch.matmul(q / self.temperature, k.transpose(2, 3))if mask is not None:attn = attn.masked_fill(mask == 0, -1e9)attn = self.dropout(F.softmax(attn, dim=-1))output = torch.matmul(attn, v)return output, attn

Multi-Head Attention(MHA)

多头注意力机制和单个scaled dot product attention的区别是先将输入的QKV分别进行多个linear操作,然后在最后的输出vector也经过多个linear映射。这样可以将qkv的向量映射到更高维度的空间上。每个linear的输出大小可以表示为head_dim, 有多少和linear可以用head_num来表示。多头(head)即指这里head_num有几个。一般在实现的时候由于linear是线性的,所以可以将多个head合并成一个linear算子来实现,这个linear的权重大小就是head_num * head_dim(这样也自动实现了图里面的concat操作)。 一个MHA的实现:

class MultiHeadAttention(nn.Module):''' Multi-Head Attention module '''def __init__(self, n_head, d_model, d_k, d_v, dropout=0.1):super().__init__()self.n_head = n_headself.d_k = d_kself.d_v = d_vself.w_qs = nn.Linear(d_model, n_head * d_k, bias=False)self.w_ks = nn.Linear(d_model, n_head * d_k, bias=False)self.w_vs = nn.Linear(d_model, n_head * d_v, bias=False)self.fc = nn.Linear(n_head * d_v, d_model, bias=False)self.attention = ScaledDotProductAttention(temperature=d_k ** 0.5)self.dropout = nn.Dropout(dropout)self.layer_norm = nn.LayerNorm(d_model, eps=1e-6)def forward(self, q, k, v, mask=None):d_k, d_v, n_head = self.d_k, self.d_v, self.n_headsz_b, len_q, len_k, len_v = q.size(0), q.size(1), k.size(1), v.size(1)residual = q# Pass through the pre-attention projection: b x lq x (n*dv)# Separate different heads: b x lq x n x dvq = self.w_qs(q).view(sz_b, len_q, n_head, d_k)k = self.w_ks(k).view(sz_b, len_k, n_head, d_k)v = self.w_vs(v).view(sz_b, len_v, n_head, d_v)# Transpose for attention dot product: b x n x lq x dvq, k, v = q.transpose(1, 2), k.transpose(1, 2), v.transpose(1, 2)if mask is not None:mask = mask.unsqueeze(1)   # For head axis broadcasting.q, attn = self.attention(q, k, v, mask=mask)# Transpose to move the head dimension back: b x lq x n x dv# Combine the last two dimensions to concatenate all the heads together: b x lq x (n*dv)q = q.transpose(1, 2).contiguous().view(sz_b, len_q, -1)q = self.dropout(self.fc(q))q += residualq = self.layer_norm(q)return q, attn

multi head的体现就体现在了qkv三个linear上,一般的权重大小是:

x: [bs, seq_len, hidden_size]  # hidden size有embedding的输出决定

Q: [hidden_size, num_hididden_heads * head_dim]  # Q有num_hiddden_heads个头,每个head大小是head dim.

K/V: [hidden_size, num_key_value_heads * head_dim]  # KV的head num一样

查看每个LLM模型的head_num等参数可以看对应权重目录下的config.json

{"_name_or_path": "meta-llama/Llama-2-7b-hf","architectures": ["LlamaForCausalLM"],"bos_token_id": 1,"eos_token_id": 2,"hidden_act": "silu","hidden_size": 4096,"initializer_range": 0.02,"intermediate_size": 11008,"max_position_embeddings": 4096,"model_type": "llama","num_attention_heads": 32,"num_hidden_layers": 32,"num_key_value_heads": 32,"pretraining_tp": 1,"rms_norm_eps": 1e-05,"rope_scaling": null,"tie_word_embeddings": false,"torch_dtype": "float16","transformers_version": "4.31.0.dev0","use_cache": true,"vocab_size": 32000
}

RoPE

Positional Encoding

位置编码主要是将位置信息加入到输入vector里。啥意思,为啥呢?还是看qkv章节里的图,attention的原理是计算q和每个kv的关系向量,假设三个kv则得到三个vector,然后将三个vector做加法。这个操作就导致,如果将三组kv的顺序换掉的话还是得到一样的三个vector, 做加法后输出还是一样的, 自注意机制无法捕捉位置信息!比如输入是"I am a"和"a I am"的attention特征向量一样了,那结果肯定有问题的。所以希望在计算attention的时候除了vector之外还可以带上当前vector的位置信息。这个工作就用positionl encoding来完成。

最简单的想法是用绝对位置编码,即对每一个embedding后的vector加上一个独立的position encoding向量(比如one hot的向量,1表示位置信息,其他为0)或者根据论文里的实现通过下面方式正弦编码(Sinusoidal):

import torch
import torch.nn as nn
import numpy as npclass PositionalEncoding(nn.Module):def __init__(self, d_hid, n_position=5):super(PositionalEncoding, self).__init__()# Not a parameterself.register_buffer('pos_table', self._get_sinusoid_encoding_table(n_position, d_hid))def _get_sinusoid_encoding_table(self, n_position, d_hid):''' Sinusoid position encoding table '''# TODO: make it with torch instead of numpydef get_position_angle_vec(position):return [position / np.power(10000, 2 * (hid_j // 2) / d_hid) for hid_j in range(d_hid)]sinusoid_table = np.array([get_position_angle_vec(pos_i) for pos_i in range(n_position)])sinusoid_table[:, 0::2] = np.sin(sinusoid_table[:, 0::2])  # dim 2isinusoid_table[:, 1::2] = np.cos(sinusoid_table[:, 1::2])  # dim 2i+1return torch.FloatTensor(sinusoid_table).unsqueeze(0)def forward(self, x):return x + self.pos_table[:, :x.size(1)].clone().detach()model = PositionalEncoding(10)
x = torch.randn([1, 10])model(x)

输出一下sinusoid_table:

0 10 [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
1 10 [1.0, 1.0, 0.15848931924611134, 0.15848931924611134, 0.025118864315095794, 0.025118864315095794, 0.003981071705534973, 0.003981071705534973, 0.000630957344480193, 0.000630957344480193]
2 10 [2.0, 2.0, 0.3169786384922227, 0.3169786384922227, 0.05023772863019159, 0.05023772863019159, 0.007962143411069947, 0.007962143411069947, 0.001261914688960386, 0.001261914688960386]
3 10 [3.0, 3.0, 0.47546795773833406, 0.47546795773833406, 0.07535659294528739, 0.07535659294528739, 0.011943215116604919, 0.011943215116604919, 0.0018928720334405792, 0.0018928720334405792]
4 10 [4.0, 4.0, 0.6339572769844454, 0.6339572769844454, 0.10047545726038318, 0.10047545726038318, 0.015924286822139894, 0.015924286822139894, 0.002523829377920772, 0.002523829377920772]

论文里的公式:

每一行position是不一样的,然后对每个0~hidden size上,偶数位置用sin计算pe,奇数位置用cos计算pe值。这种计算的好处是不仅将position值考虑进计算,而且相邻的区域使用sin/cos进行了区分,而且由于sin/cos是有边界的函数这样也导致pe的值不会太大。

位置编码的主要目的就是将位置信息引入到embedding后的特征向量表示中,将每个kv的vector在空间表示的时候可以完全的分开。

但是相对位置编码有个问题就是和训练时候的vocab size强绑定,当推理词表长度和训练不一样,那相对位置编码就没有这么强的泛化性了。这个被称为外推性,外推性是指大模型在训练时和预测时的输入长度不一致,导致模型的泛化能力下降的问题。而且绝对位置编码还是只和当前的位置有关系,无法感知相对位置信息。

RoPE

Rotary Position Embedding(RoPE)选择位置编码考虑了相对位置信息,使用旋转矩阵来实现。什么是旋转矩阵, 在计算机视觉里旋转矩阵常用来变换图像像素:

R是一个旋转矩阵,可以将像素点(x, y)进行一定的旋转变换。在rope论文里就使用了旋转矩阵来变换embedding词向量:

然后根据2维度的旋转矩阵就得到了rope层的大的旋转矩阵。然后经过计算和推导后..(todo...)

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

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

相关文章

2024全国大学省数学建模竞赛A题-原创参考论文(部分+第一问代码)

一问题重述 1.1 问题背景 "板凳龙"&#xff0c;又称"盘龙"&#xff0c;是浙闽地区的传统地方民俗文化活动。这种独特的表演艺术形式融合了中国传统龙舞的精髓和地方特色&#xff0c;展现了人们对美好生活的向往和对传统文化的传承。 在板凳龙表演中&am…

版本控制系统Git/Gitlab/GitHub

版本控制系统 git和svn:公司内部的代码仓库&#xff0c;用于存放项目代码&#xff0c;方便整合开发过程 公共代码仓库&#xff1a;github全球 gitee国内 git 分布式 ---没有中心代码库&#xff0c;所有机器之间的地位同等&#xff08;每台机器上都有相同的代码&#xff09; …

18055 主对角线上的元素之和

### 思路 1. 输入一个3行4列的整数矩阵。 2. 计算主对角线上的元素之和。 3. 输出主对角线上的元素之和。 ### 伪代码 1. 初始化一个3行4列的矩阵 matrix。 2. 输入矩阵的元素。 3. 初始化一个变量 sum 为0&#xff0c;用于存储主对角线元素之和。 4. 遍历矩阵的行&#xff0c…

AI产品经理:ai产品经理从零基础到精通,非常详细收藏我这一篇就够了

在互联网的浪潮中&#xff0c;AI人工智能领域无疑是最引人注目的风口。AI产品经理&#xff0c;作为这一领域的新兴岗位&#xff0c;以其高薪、低压力、无年龄限制等优势&#xff0c;吸引了众多互联网从业者的目光。随着GPT等AIGC工具的兴起&#xff0c;AI产品经理的市场需求日益…

企业网银登录提示请确认您已插入工商银行U盾证书的解决方法

昨天受人之托帮小企业财务解决上网银的问题 因为不是专业做这个的&#xff0c;所以只能安装“常识”行事&#xff0c;但结果实在是令人意想不到。 排错的步骤&#xff1a; 同一台电脑上尝试不同浏览器&#xff0c;发现360浏览器的接受度相当普遍&#xff1b;给U盾换不同的连接…

【408 数据结构】第1章绪论

文章目录 绪论考纲DS 基本概念1. 基本概念2. 数据结构三要素 算法&#xff08;时/空间复杂度计算&#xff09;1. 算法概念2. 算法效率的度量时间复杂度&#xff1a;空间复杂度&#xff1a; 小结 绪论 考纲 计算时间复杂度和空间复杂度&#xff08;重点难点&#xff09; DS …

如何使用AI来免费提升你的图片质量

学习如何使用AI免费放大您的图像&#xff0c;可以将那些恼人的低分辨率图像转变为高分辨率的杰作——至少在某种程度上是这样。虽然使用我们用于此任务的应用程序Upscayl需要稍微调整一下不同的模型&#xff0c;但您至少应该能够将图像转换成视觉上更令人愉悦的效果。 Upscayl…

Python教程(二十) : 十分钟入门【PyQt6】

文章目录 专栏列表环境准备1 安装 Python2 安装 PyQt6 创建 PyQt6 项目1 创建项目目录2 创建主 Python 文件 代码书写测试流程1 导入 PyQt6 模块2 创建主窗口类3 创建应用程序实例并运行 核心解析&#xff1a;PyQt6 中的模块示例代码&#xff1a; PyQt6 常用的控件1. QPushButt…

【Linux网络编程八】实现最简单Http服务器(基于Tcp套接字)

基于TCP套接字实现一个最简单的Http服务器 Ⅰ.Http请求和响应格式1.请求格式2.响应格式3.http中请求格式中细节字段4.http中响应格式中细节字段 Ⅱ.域名ip与URLⅢ.web根目录Ⅳ.Http服务器是如何工作的&#xff1f;一.获取请求二.分析请求2.1反序列化2.2解析url 三.构建响应3.1构…

java重点学习-mysql

2.1 如何定位慢查询? 1:介绍一下当时产生问题的场景(我们当时的一个接口测试的时候非常的慢&#xff0c;压测的结果大概5秒钟) 2.我们系统中当时采用了运维工具(Skywalking)&#xff0c;可以监测出哪个接口&#xff0c;最终因为是sql的问题 3.在mysql中开启了慢日志查询&#…

【LeetCode】14.最长公共前缀

题目要求 解题思路 这道题我们可以通过一列一列的比较是否相等来解决 代码实现 class Solution { public:string longestCommonPrefix(vector<string>& strs) {string ret;//以第一个字符串为标准for(int i0;i<strs[0].size();i){//保存第一个字符串的第i个位…

前端---对MVC MVP MVVM的理解

就需要从前端这些年的从无到有、从有到优的变迁过程讲一下。 1. Web1.0时代 在web1.0时代并没有前端的概念&#xff0c;开发一个web应用多数采用ASP.NET/Java/PHP编写&#xff0c;项目通常用多个aspx/jsp/php文件构成&#xff0c;每个文件中同时包含了HTML、CSS、JavaScript、…

微信小程序手写签名

微信小程序手写签名组件 该组件基于signature_pad封装&#xff0c;signature_pad本身是web端的插件&#xff0c;此处将插件代码修改为小程序端可用。 signature_pad.js /*!* Signature Pad v5.0.3 | https://github.com/szimek/signature_pad* (c) 2024 Szymon Nowak | Releas…

[数据集][目标检测]轮胎检测数据集VOC+YOLO格式4629张1类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;4629 标注数量(xml文件个数)&#xff1a;4629 标注数量(txt文件个数)&#xff1a;4629 标注…

Spring扩展点系列-InstantiationAwareBeanPostProcessor

文章目录 简介测试一1、配置文件Bean注册2、单元测试方法3、测试类4、输出结果结论 测试二1、测试类2、输出结果结论 源码解析postProcessPropertiesCommonAnnotationBeanPostProcessorAnnotationInjectedBeanPostProcessor 总结 简介 spring容器中Bean的生命周期内所有可扩展…

【重构获得模式 Refactoring to Patterns】

重构获得模式 Refactoring to Patterns 面向对象设计模式是“好的面向对象设计”&#xff0c;所谓“好的面向对象设计”指的是那些可以满足“应对变化&#xff0c;提高复用”的设计。 现代软件设计的特征是“需求的频繁变化”。设计模式的要点是“寻找变化点&#xff0c;然后…

Kafka 实战演练:创建、配置与测试 Kafka全面教程

文章目录 1.配置文件2.消费者1.注解方式2.KafkaConsumer 3.依赖1.注解依赖2.KafkaConsumer依赖 本文档只是为了留档方便以后工作运维&#xff0c;或者给同事分享文档内容比较简陋命令也不是特别全&#xff0c;不适合小白观看&#xff0c;如有不懂可以私信&#xff0c;上班期间都…

5G前传-介绍

1. 引用 知识分享系列一&#xff1a;5G基础知识-CSDN博客 5G前传的最新进展-CSDN博客 灰光和彩光_通信行业5G招标系列点评之二&#xff1a;一文读懂5G前传-光纤、灰光、彩光、CWDM、LWDM、MWDM...-CSDN博客 术语&#xff1a; 英文缩写描述‌BBU&#xff1a;Building Baseba…

review——C++中的右值引用

目录 前言 一、什么是左值、什么是右值 二、右值引用 1.右值引用与右值引用的一些性质 2.解释一下左值引用与右值应用于程序员之间的关系 3.右值引用与移动语义 4.右值引用右值后变成左值的必要性与完美转发 1.右值引用引用右值后变为左值属性的必要性 2.完美转发 Ⅰ…

【docker】docker 镜像仓库的管理

Docker 仓库&#xff08; Docker Registry &#xff09; 是用于存储和分发 Docker 镜像的集中式存储库。 它就像是一个大型的镜像仓库&#xff0c;开发者可以将自己创建的 Docker 镜像推送到仓库中&#xff0c;也可以从仓库中拉取所需的镜像。 Docker 仓库可以分为公共仓…