php能开发大型网站wordpress修改后台没反应
news/
2025/9/29 6:02:42/
文章来源:
php能开发大型网站,wordpress修改后台没反应,西安商城网站开发制作,东莞道滘网站建设写在前面 这篇文章提到了绝对位置编码和相对位置编码#xff0c;但是他们都有局限性#xff0c;比如绝对位置编码不能直接表征token的相对位置关系#xff1b;相对位置编码过于复杂#xff0c;影响效率。于是诞生了一种用绝对位置编码的方式实现相对位置编码的编码方式——…写在前面 这篇文章提到了绝对位置编码和相对位置编码但是他们都有局限性比如绝对位置编码不能直接表征token的相对位置关系相对位置编码过于复杂影响效率。于是诞生了一种用绝对位置编码的方式实现相对位置编码的编码方式——旋转位置编码Rotary Position Embedding, RoPE兼顾效率和相对位置关系。 RoPE的核心思想是通过旋转的方式将位置信息编码到每个维度从而使得模型能够捕捉到序列中元素的相对位置信息。现在已经在很多大模型证明了其有效性比如ChatGLM、LLaMA等。
一、RoPE的优点
1.真正的旋转位置编码 Transformer的原版位置编码也使用了三角函数但它生成的是每个位置的绝对编码三角函数的主要用途是生成具有可区分性的周期性模式也没有应用旋转变换的概念因此属于绝对位置编码。同时原版的编码使用加法在多层传递后导致位置信息的稀释如下图 没想到这张图也有被当做反面典型的时候吧 RoPE不是简单的加法而是通过复数乘法实现旋转变换这种旋转是将位置信息融入到token表示中的关键机制。RoPE在实现过程中通过乘法操作融入位置信息与模型中的Q和K深度融合将旋转操作真正植入Attention机制内部强化了位置编码信息的作用。
2.更好的相对位置信息编码 注意力机制通过计算Embedding的内积来确定它们之间的关系强度。 使用RoPE时两个位置的编码通过旋转变换后的内积自然地包含了它们之间的相对位置信息。这是因为旋转操作保持了内积的性质使得内积计算不仅反映了token的内容相似性还反映了它们的位置关系。
3.更适用于多维输入 这点很有意思传统的Transformer位置编码主要针对一维序列如文本序列。然而在某些任务中输入可能是二维或更高维的数据如图像或视频数据。旋转位置编码可以更灵活地应用于多维输入数据通过对不同维度的位置信息进行编码使得模型能够更好地理解多维数据中的位置关系。
4. 更善于处理长序列 RoPE可以减少位置信息的损失。在深层网络中RoPE通过乘法操作融入位置信息乘法操作有助于在深层网络中保持位置信息的完整性。在处理一个长文本时RoPE通过在每一层的自注意力计算中使用旋转变换确保了位置信息能够被有效保留和利用即使是在模型的较深层次。
二、公式 既然旋转的位置编码有这么多优点那怎么实现位置编码的旋转呢其实网上有很多介绍的文章。大概意思就是复数可以通过乘以e的幂来旋转角度其中幂就是角度再结合欧拉公式推出三角函数的表达大致流程如下。 欧拉公式 1 复数旋转角度θ 2 将1带入2 3 这块东西苏剑林老师已经从数学的角度进行过很深入的推导这里的融合式部分我就不班门弄斧了。我今天提供一种朴素的思考过程从代码实现的角度思考如何进行旋转。 众所周知一维向量是不能旋转的那我们就旋转一个[2,d]的二维向量q并且设即 4 要旋转q很容易乘以旋转矩阵就可以了如果我们要旋转角度θ 5 展开之后结果如下 6 上面的很眼熟吧就是沿用了transformer的机制这里有详细的介绍。 而且大家看到字母q也大概能猜到这就是Attention中的Q同样的操作也可以对K使用。经过上述操作其实已经以旋转的方式将位置编码融合到Attention机制内部。 下面就是根据式子6的代码实现了。这里提前说一句ChatGLM的Q和K的形状都是[b,1,32,64]其中b是token_ids的长度32是multi-head的个数64将被拆成两部分每部分32也就是上面的x,y,下面开始代码实现部分。
三、代码实现 我们以ChatGLM的代码为例展示一下RoPE的使用以下代码都在modeling_chatglm.py文件中一条训练数据
{context: 你好, target: 你好,我是大白话}
1.字符串转换成token_ids
[ 5, 74874, 130001, 130004, 5, 74874, 6, 65806, 63850, 95351, 130005]
2.计算position_ids 根据上面的token_ids计算出position_ids
[[0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2],[0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8]] 解释一下position_ids第一行表示序列中每个元素的全局位置第一个“2”表明context结束了target要开始了后面所有的2都是target部分第二行则细化到更具体的局部位置从1开始表征整个target的内容这样用两个维度的编码很优雅的体现了context和target这种层次化处理对于理解上下文非常重要。 代码如下 def get_position_ids(self, input_ids, mask_positions, device, use_gmasksNone):根据token_ids生成position_ids:param input_ids: 这里是[[ 5, 74874, 130001, 130004, 5, 74874, 6, 65806, 63850, 95351, 130005]]:param mask_positions: 2 输出的第1维mask掉几位即这一位及其前面都是0后面是1,2...:param device::param use_gmasks::return: [[0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2],[0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8]]batch_size, seq_length input_ids.shapeif use_gmasks is None:use_gmasks [False] * batch_sizecontext_lengths [seq.tolist().index(self.config.bos_token_id) for seq in input_ids]if self.position_encoding_2d:# 会走这一分支position_ids torch.arange(seq_length, dtypetorch.long, devicedevice).unsqueeze(0).repeat(batch_size, 1)for i, context_length in enumerate(context_lengths):position_ids[i, context_length:] mask_positions[i]block_position_ids [torch.cat((torch.zeros(context_length, dtypetorch.long, devicedevice),torch.arange(seq_length - context_length, dtypetorch.long, devicedevice) 1)) for context_length in context_lengths]block_position_ids torch.stack(block_position_ids, dim0)position_ids torch.stack((position_ids, block_position_ids), dim1)else:position_ids torch.arange(seq_length, dtypetorch.long, devicedevice).unsqueeze(0).repeat(batch_size, 1)for i, context_length in enumerate(context_lengths):if not use_gmasks[i]:position_ids[i, context_length:] mask_positions[i]return position_ids
3.角度序列Embedding 接下来将position_ids转换成角度序列Embedding,下表中每个格的公式为 其中m是position_ids中元素的数值i是编码的索引ChatGLM使用两个0-31拼接d是维度hidden_size // (num_attention_heads * 2)46 第一部分position_ids[0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2]每个值编码成长度64的角度序列
m | i013101310m0, i0m0, i1...m0, i31m0, i0m0, i1...m0, i311m1, i0m1, i1m1, i31m1, i0m1, i1m1, i312m2, i0m2, i1m2, i31m2, i0m2, i1m2, i31...2m2, i0m2, i1m2, i31m2, i0m2, i1m2, i31 第二部分block_position_ids[0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8]
m | i013101310m0, i0m0, i1...m0, i31m0, i0m0, i1...m0, i310m0, i0m0, i1...m0, i31m0, i0m0, i1...m0, i310m0, i0m0, i1...m0, i31m0, i0m0, i1...m0, i311m1, i0m1, i1m1, i31m1, i0m1, i1m1, i31...8m8, i0m8, i1m8, i31m8, i0m8, i1m8, i31
代码如下
class RotaryEmbedding(torch.nn.Module):def _load_from_state_dict(self, state_dict, prefix, local_metadata, strict, missing_keys, unexpected_keys,error_msgs):passdef __init__(self, dim, base10000, precisiontorch.half, learnableFalse):根据position_ids计算旋转角度的Embedding:param dim: 这里hidden_size // (num_attention_heads * 2)46其中hidden_size4096 num_attention_heads32:param base::param precision::param learnable:super().__init__()# 初始化“频率”可以理解为position_id每增加1增加的角度是Embedding形式的。inv_freq 1. / (base ** (torch.arange(0, dim, 2).float() / dim))inv_freq inv_freq.half()self.learnable learnableif learnable:self.inv_freq torch.nn.Parameter(inv_freq)self.max_seq_len_cached Noneelse:self.register_buffer(inv_freq, inv_freq)self.max_seq_len_cached Noneself.cos_cached Noneself.sin_cached Noneself.precision precisiondef forward(self, x, seq_dim1, seq_lenNone):if seq_len is None:seq_len x.shape[seq_dim]if self.max_seq_len_cached is None or (seq_len self.max_seq_len_cached):self.max_seq_len_cached None if self.learnable else seq_len# 1.对position_ids去重并正序排列得到t如[[0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2]] -- t[[0, 1, 2]]t torch.arange(seq_len, devicex.device, dtypeself.inv_freq.dtype)# 2.t与初始化好的“频率”做外积得到每个position_id的角度是Embeddingfreqs torch.einsum(i,j-ij, t, self.inv_freq)# 3.每个Embedding重复叠加一次emb torch.cat((freqs, freqs), dim-1).to(x.device)if self.precision torch.bfloat16:emb emb.float()# 4.算cos和sin并增加维度cos_cached emb.cos()[:, None, :]sin_cached emb.sin()[:, None, :]if self.precision torch.bfloat16:cos_cached cos_cached.bfloat16()sin_cached sin_cached.bfloat16()if self.learnable:return cos_cached, sin_cachedself.cos_cached, self.sin_cached cos_cached, sin_cachedreturn self.cos_cached[:seq_len, ...], self.sin_cached[:seq_len, ...]def apply_rotary_pos_emb_index(q, k, cos, sin, position_id):# position_id: [sq, b], q, k: [sq, b, np, hn], cos: [sq, 1, hn] - [sq, b, 1, hn]# 类似于查表根据每个position_id获取相应的Embeddingcos, sin F.embedding(position_id, cos.squeeze(1)).unsqueeze(2), \F.embedding(position_id, sin.squeeze(1)).unsqueeze(2)......
4.截取拼接Q和K 这一步对Q或者K做截断并将第二段取反拼在第一段的前面拼接成公式第二项的q部分。
上述3、4流程示意图 代码如下
def rotate_half(x):x1, x2 x[..., :x.shape[-1] // 2], x[..., x.shape[-1] // 2:]return torch.cat((-x2, x1), dimx1.ndim - 1)
5.旋转位置编码融合 将旋转位置编码融合到Q和K中计算第一部分的cos(1)和sin(1)并与输入的Q1、K1做乘法融合计算第二部分的cos(1)和sin(1)并与输入的Q1、K1做乘法融合最后将Q和K分别拼接组成融合了旋转位置编码的新Q和K。整体流程图如下其中rotary_pos_emb是上图也就是步骤3、4 代码如下
def apply_rotary_pos_emb_index(q, k, cos, sin, position_id):# position_id: [sq, b], q, k: [sq, b, np, hn], cos: [sq, 1, hn] - [sq, b, 1, hn]# 类似于查表根据每个position_id获取相应的Embeddingcos, sin F.embedding(position_id, cos.squeeze(1)).unsqueeze(2), \F.embedding(position_id, sin.squeeze(1)).unsqueeze(2)# 执行旋转位置编码与QK的融合q, k (q * cos) (rotate_half(q) * sin), (k * cos) (rotate_half(k) * sin)return q, k# 整体流程如下
# 1.拆分出Q1、Q2、K1、K2
q1, q2 query_layer.chunk(2, dim(query_layer.ndim - 1))
k1, k2 key_layer.chunk(2, dim(key_layer.ndim - 1))
# 2.计算旋转Embedding
cos, sin self.rotary_emb(q1, seq_lenposition_ids.max() 1)
position_ids, block_position_ids position_ids[:, 0, :].transpose(0, 1).contiguous(), \position_ids[:, 1, :].transpose(0, 1).contiguous()
# 3.旋转位置编码融合
q1, k1 apply_rotary_pos_emb_index(q1, k1, cos, sin, position_ids)
q2, k2 apply_rotary_pos_emb_index(q2, k2, cos, sin, block_position_ids)
# 4.将拆分出的Q1、Q2、K1、K2合并成新的Q、K
query_layer torch.concat([q1, q2], dim(q1.ndim - 1))
key_layer torch.concat([k1, k2], dim(k1.ndim - 1)) 位置编码对于Transformer的重要性毋庸置疑旋转位置编码也确实解决了一些问题。最有意思的就是它是一个二维编码将旋转信息通过乘法操作融入Attention机制内部强化了位置编码信息现在已经有很多开源大模型都使用了旋转位置编码可见其效果不俗。 旋转位置编码就介绍到这里关注不迷路(#^.^#)
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/921470.shtml
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!