def precompute_freqs_cis(dim: int, end: int, theta: float = 10000.0):
# 计算频率:将 [0, dim) 按步长为 2 切片,并将每个值除以 dim。
# 然后用 theta 的幂次倒数进行缩放
freqs = 1.0 / (theta ** (torch.arange(0, dim, 2)[: (dim // 2)].float() / dim))
# 创建从 0 到 end-1 的序列,并确保它们与频率张量位于相同设备上
t = torch.arange(end, device=freqs.device, dtype=torch.float32)
# 计算时间序列和频率张量的外积,得到 shape 为 (end, dim // 2) 的张量
freqs = torch.outer(t, freqs)
# 使用极坐标表示将频率转换为复数张量
# torch.polar 用于创建复数,其中实部为1,虚部为频率值
freqs_cis = torch.polar(torch.ones_like(freqs), freqs) # complex64
return freqs_cis
t对应于论文中的pos
关于外积
特别说明一下翻译的相关问题。由于历史原因,数学学科和物理学科关于「inner product」和「outer product」两个词汇有着五花八门的翻译。
在物理学科,一般翻译成「标积」和「矢积」,表示运算的结果为标量和矢量。高中数学课本上「数量积」和「向量积」也采用了这种意译的办法。
在数学学科,通常也可以翻译成「内积」和「外积」,是两个名词的直译。「点乘」和「叉乘」是根据运算符号得来的俗称,这种俗称也很常见。
这里的外积的例子:
>>> v1 = torch.arange(1., 5.)
>>> v2 = torch.arange(1., 4.)
>>> torch.outer(v1, v2)
tensor([[ 1., 2., 3.],
[ 2., 4., 6.],
[ 3., 6., 9.],
[ 4., 8., 12.]])
频率计算中为什么要加入dim,我们不是只需要token的位置信息吗
这种频率编码方式允许模型在不同的频率上捕捉位置信息,使得模型可以对序列中的不同位置进行细粒度的区分。较低频率的编码捕捉全局位置信息,而较高频率的编码捕捉局部细节。