二、Transformer之位置编码


在这里插入图片描述

前言

在Transformer中使用位置编码表示 , 语义前后关联关系

一、Transformer 原文的位置编码公式

P E ( p o s , 2 i ) = sin ⁡ ( p o s 10000 2 i / d model ) { \begin{aligned} PE(pos, 2i) &= \sin\left(\frac{pos}{10000^{2i / d_{\text{model}}}}\right) \\ \end{aligned} } PE(pos,2i)=sin(100002i/dmodelpos)

P E ( p o s , 2 i + 1 ) = cos ⁡ ( p o s 10000 2 i / d model ) {\begin{aligned}PE(pos, 2i+1) &= \cos\left(\frac{pos}{10000^{2i / d_{\text{model}}}}\right)\end{aligned}} PE(pos,2i+1)=cos(100002i/dmodelpos)

其中 ( i ) 表示维度索引(从 0 开始),( d_{\text{model}} ) 是词嵌入的维度。

为了让计算更高效,通常预先计算好一个除数向量:

div_term [ i ] = 1 10000 2 i / d model \text{div\_term}[i] = \frac{1}{10000^{2i / d_{\text{model}}}} div_term[i]=100002i/dmodel1

这样在编码每个位置 ( pos ) 时,只需要计算 ( pos \times \text{div_term}[i] )。

二、代码实现

"""
Transformer1的位置嵌入项链的

"""


import  torch
import torch.nn as nn
import math
import math
import copy
from torch.autograd import Variable

import torch
import torch.nn as nn
import torch.nn.functional as F
import math
import torch
import torch.nn as nn
from torch.autograd import Variable

g_device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')

class PositionalEncoding(nn.Module):
    def __init__(self, d_model, dropout=0.1, max_len=5000):
        super(PositionalEncoding, self).__init__()
        self.dropout = nn.Dropout(p=dropout)

        # 初始化 一个size 为max_len(设定的最大长度)* embedding维度 的全部
        # 来存放所有小于这个长度位置对应的positional embedding
        pe = torch.zeros(max_len, d_model, device=g_device)
        print("pe shape:", pe.shape)  # pe shape: torch.Size([5000, 512])
        print("pe:", pe);
        # 生成一个位置下标的tensor矩阵(每一行都是一个位置下标)
        # torch.arange(start=0, end, step=1, *, out=None, dtype=None, layout=torch.strided, device=None, requires_grad=False)
        position = torch.arange(0., max_len, device=g_device).unsqueeze(1)
        print("PositionalEncoding: d_model={}, max_len={}".format(d_model, max_len));
        print("position shape:", position.shape)  # position shape: torch.Size([5000, 1])
        # 这里幂运算太多,我们使用exp和log来转换实现公式中pos下面要除以的分母(由于是分母,要注意带负号)
        div_term = torch.exp(torch.arange(0., d_model, 2, device=g_device) * -(math.log(10000.0) / d_model))

        # 根据公式,计算各个位置在各embedding维度上的位置纹理值,存放到pe矩阵中
        pe[:, 0::2] = torch.sin(position * div_term)
        print("pe after even:", pe);
        pe[:, 1::2] = torch.cos(position * div_term)
        print("pe after odd:", pe);
        # 加1个维度,使得pe维度变为:1×max_len×embedding维度
        # (方便后续与一个batch的句子所有词的embedding批量相加)
        pe = pe.unsqueeze(0)
        # 将pe矩阵以持久的buffer状态存下(不会作为要训练的参数)
        self.register_buffer('pe', pe)

    def forward(self, x):
        # 将一个batch的句子所有词的embedding与已构建好的positional embeding相加
        # (这里按照该批次数据的最大句子长度来取对应需要的那些positional embedding值)
        x = x + Variable(self.pe[:, :x.size(1)], requires_grad=False )
        # return self.dropout(x)
        return self.dropout(x)



if __name__ == "__main__":
    # ------------------------------------------------------------
    # 批大小 (batch_size)
    # ------------------------------------------------------------
    # 含义:一次前向传播/反向传播中,同时处理的独立序列样本数量。
    # 作用:
    #   - 影响训练速度:更大的 batch 能更好利用 GPU 并行计算,但会消耗更多显存。
    #   - 影响梯度稳定性:适中的 batch 可使梯度估计更稳定,训练收敛更平滑。
    #   - 这里是 batch_size = 2,意味着每次迭代会同时输入 2 条独立的序列。
    # 示例:若你有 1000 条句子,batch_size=2,则需要 500 次迭代完成一个 epoch。
    batch_size = 2

    # ------------------------------------------------------------
    # 序列长度 (seq_len)
    # ------------------------------------------------------------
    # 含义:每个序列中包含的 token(词/子词/时间步)的数量。
    # 作用:
    #   - 决定注意力机制的计算范围:Transformer 自注意力的复杂度是 O(seq_len^2 * d_model)。
    #   - 影响模型能够捕捉的长期依赖关系:更长的序列需要更多位置编码信息。
    #   - 这里是 seq_len = 10,意味着每条样本有 10 个 token(例如 10 个单词或 10 个时间步)。
    # 注意:实际应用中通常需要统一序列长度,通过截断或补零(padding)来实现。
    seq_len = 10

    # ------------------------------------------------------------
    # 模型维度 (d_model)
    # ------------------------------------------------------------
    # 含义:词嵌入向量以及 Transformer 各子层输出/输入的维度(即隐藏层的宽度)。
    # 作用:
    #   - 决定模型的表示能力:更大的 d_model 可以学习更复杂的特征,但参数量和计算量也会显著增加。
    #   - 影响多头注意力的头数:通常 d_model 需要能被 num_heads 整除(例如 d_model=512,num_heads=8)。
    #   - 这里是 d_model = 512,这是 Transformer 原文(“Attention Is All You Need”)中 base model 使用的标准维度。
    #   - 常见取值范围:256、512、768(BERT-base)、1024(BERT-large)等。
    d_model = 512

    # 模拟Embedding输出
    x = torch.randn(
        batch_size,
        seq_len,
        d_model
    ).to(g_device)

    pe = PositionalEncoding(
        d_model=d_model,
        dropout=0.1,
        max_len=5000
    )

    output = pe(x)

    print(f"x shape: {x.shape}, x:{x}")
    print("Input Shape :", x.shape)
    print("Output Shape:", output.shape)

三、代码逐行解释

div_term = torch.exp(torch.arange(0., d_model, 2, device=DEVICE) * -(math.log(10000.0) / d_model))
  1. torch.arange(0., d_model, 2, device=DEVICE)
    生成一维张量:[0, 2, 4, ..., d_model-2],对应公式中的 ( 2i )。

  2. -(math.log(10000.0) / d_model)
    ( − ln ⁡ ( 10000 ) d model ) (-\frac{\ln(10000)}{d_{\text{model}}}) (dmodelln(10000)),是一个常数。

  3. torch.arange(...) * ...
    得到向量: ( − ln ⁡ ( 10000 ) d model × 2 i ) ( -\frac{\ln(10000)}{d_{\text{model}}} \times 2i ) (dmodelln(10000)×2i)

  4. torch.exp(...)
    对上述向量逐元素取指数:
    exp ⁡ ( − ln ⁡ ( 10000 ) ⋅ 2 i d model ) = 10000 − 2 i / d model \exp\left(-\frac{\ln(10000) \cdot 2i}{d_{\text{model}}}\right) = 10000^{-2i / d_{\text{model}}} exp(dmodelln(10000)2i)=100002i/dmodel
    这正是上面所说的 div_term(分母部分)。

总结

Transformer原理分析:https://chensongpoixs.github.io/artificial_intelligence/Transfomer

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值