位置编码在Transformer模型中是用于注入序列中位置信息的,因为Transformer的自注意力机制本身不考虑单词的顺序。为了计算位置编码,通常使用不同频率的正弦和余弦函数。
import torch
seq_len = 10 # 序列长度
d_model = 512 # 模型的维度
# 初始化位置编码张量
position_encoding = torch.zeros(seq_len, d_model)
# 生成位置索引并增加一个维度以便广播
position = torch.arange(0, seq_len, dtype=torch.float).unsqueeze(1)
'''
torch.arange(0, seq_len, dtype=torch.float)生成了一个从0到seq_len - 1的一维张量,并将其数据类型设置为float。这个张量代表了序列中的位置索引。
.unsqueeze(1)用于增加张量的维度。在索引为1的维度上增加了一个大小为1的维度。如果原始张量的形状是(seq_len,),那么unsqueeze(1)之后的张量形状将变为(seq_len, 1)。这样做是为了后续能够与位置编码的其他维度进行广播操作。
'''
# 计算用于调节频率的分母项
div_term = torch.exp(torch.arange(0, d_model, 2).float() * -(torch.log(torch.tensor(10000.0)) / d_model))
'''
div_term用于计算一个随着维度索引增加而减小的值,这个值用于调节正弦和余弦函数的频率。通过这种方式,每个维度上的位置编码都会有一个不同的频率,从而允许模型捕捉到更丰富的位置信息。
'''
# 使用正弦函数填充偶数索引的位置编码
position_encoding[:, 0::2] = torch.sin(position * div_term)
# 使用余弦函数填充奇数索引的位置编码
# 与sin函数形成互补,共同表示位置信息
position_encoding[:, 1::2] = torch.cos(position * div_term)
# 现在position_encoding包含了每个位置的位置编码
为什么引入正余弦函数:
-
周期性:正弦和余弦函数是周期函数,这意味着它们会在一定的范围内重复相同的模式。通过选择不同的频率,可以为不同的位置生成不同的编码,而这些编码在序列长度上是有规律的。
-
可学习性的平衡:位置编码需要被模型学习并用来理解位置信息,但又不能是完全随机的或者太过复杂。正弦和余弦函数生成的编码既有足够的差异性以供模型学习,又不会过于复杂而导致模型难以训练。
-
长度外推:正弦和余弦函数的另一个优点是它们可以平滑地处理比训练期间遇到的序列更长的序列。因为它们是连续的周期函数,所以即使对于超出训练数据长度的位置,也可以生成合理的位置编码。
-
计算效率:正弦和余弦函数是数学上简单且计算效率高的函数。这意味着在生成位置编码时,不会对模型的训练或推理速度产生显著影响。
-
相对位置信息:通过使用不同频率的正弦和余弦函数,位置编码可以隐含地包含相对位置的信息。这是因为正弦和余弦函数在不同位置上的值具有可预测的相互关系,这种关系可以帮助模型理解位置的相对顺序。

2692

被折叠的 条评论
为什么被折叠?



