transformer-PositionalEncoding (对数空间计算实现)
PositionalEncoding (对数空间计算实现)
-
参考论文:
https://arxiv.org/pdf/1706.03762
https://arxiv.org/pdf/1706.03762
位置编码的正常实现链接如下
https://blog.csdn.net/hbkybkzw/article/details/147431820
这次说的是位置编码的另一种实现方式,在计算分母的幂次时使用对数变换技巧,在数值上更稳定,两者只是在计算上不同,实际是等价的
数学公式
-
原公式
P E ( p o s , 2 i ) = sin ( p o s 10000 2 i d m o d e l ) P E ( p o s , 2 i + 1 ) = cos ( p o s 10000 2 i d m o d e l ) PE_{(pos,2i)} = \sin ( \frac{pos}{10000^{ \frac{2i}{d_{model}} } } ) \\ \quad \\ PE_{(pos,2i+1)} = \cos ( \frac{pos}{10000^{ \frac{2i}{d_{model}} } } ) \\ PE(pos,2i)=sin(10000dmodel2ipos)PE(pos,2i+1)=cos(10000dmodel2ipos)
其中, p o s pos pos 表示位置, i i i 是维度索引, d m o d e l d_{model} dmodel 是模型的维度 (即embedding的维度)。
-
分母的变换
已知
x = e log ( x ) x = e^{\log_(x)} x=elog(x)
则
$$
\begin{aligned}
\frac{1}{10000^{ \frac{2i}{d_{model}}}} &= 10000^{-\frac{2i}{d_{model} }} \
&=e^{\log 10000^{-\frac{2i}{d_{model} }} } \
&=e^{{-\frac{2i}{d_{model} }} \log 10000} \
&=e^{-2i \log{10000} / d_{model} } \\end{aligned}
$$ -
所以原来的实现代码为
item = 1 / 10000 ** (torch.arange(0, d_model, 2) / d_model)
更改为对数计算后是
div_item = torch.exp(torch.arange(0, d_model, 2) * -(math.log(10000.0)) / d_model)
代码实现 (对数空间计算)
-
根据上述公式,
PositionalEncoding
的一种实现方式如下,其中div_term
的计算在对数空间进行,以提高数值稳定性:import torch from torch import nn import mathclass PositionalEncodingExponent(nn.Module):"Implement the PE function."def __init__(self, d_model, max_seq_len=512):super(PositionalEncodingExponent, self).__init__()# shape of position : [max_seq_len, 1]position = torch.arange(0, max_seq_len).unsqueeze(1)# Compute the positional encodings once in log space.div_item = torch.exp(torch.arange(0, d_model, 2) * -(math.log(10000.0)) / d_model)tmp_pos = position * div_itempe = torch.zeros(max_seq_len, d_model)pe[:, 0::2] = torch.sin(tmp_pos)pe[:, 1::2] = torch.cos(tmp_pos)pe = pe.unsqueeze(0)self.register_buffer("pe", pe,persistent=False)def forward(self, x):batch,seq_len,d_model = x.shape # 注意,这里的x已经经过embeddingpe = self.pereturn x + pe[:, :seq_len, :]
使用示例
-
测试代码
# 假设我们有一个大小为 (batch_size=32, seq_len=20, d_model=512) 的输入 batch_size = 32 seq_len = 20 d_model = 512# 创建位置编码的实例 pos_encoding_layer = PositionalEncodingExponent(d_model=d_model)# 生成随机输入数据 input_data = torch.randn(batch_size, seq_len, d_model)# 获取带有位置编码的数据 output_data = pos_encoding_layer(input_data)print(output_data.shape) # 输出应为 (32, 20, 512)
此示例创建了一个
PositionalEncoding
类的实例,并将其应用于一个随机生成的输入张量,模拟了在Transformer模型中将位置信息添加到词嵌入的过程。输出张量的形状与输入张量保持一致。