• Transformer中的位置编码PE(position encoding)


    Transformer中的位置编码PE(position encoding)

    对应视频讲解:

    【代码详解】扩散模型中如何融合时间信息t到图x上(x+t),Transformer中的位置编码PE(position encoding)

    nn.Embedding使用:http://t.csdnimg.cn/m2nKN

    1.提出背景

    transformer模型的attention机制并没有包含位置信息,即一句话中词语在不同的位置时在transformer中是没有区别的

    2.解决背景

    给encoder层和decoder层的输入添加了一个额外的向量Positional Encoding(PE),与embedding维度一致

    embedding维度:

    • 句子:词向量的维度大小,如512
    • 图片:通道数

    实际应用中,会对位置信息向量如[1,2,…,16]先进行位置编码,位置编码的维度可以先保持与句子编码维度一致,如512。再通过一个线性层,将维度降为需要的通道数,最后再进行信息合并。

    3. 创建一个位置编码器PE

    需要的输入设置

    • 序列的最大长度 max_seq_len,如1000
    • 编码向量的维度 d_model,如512或128

    主体逻辑

    1. 计算PE矩阵
    2. 重新定义嵌入层:将嵌入层的权重替换为PE(不可训练)

    PE计算
    PE为二维矩阵,大小跟输入embedding的维度一样,行表示词语,列表示词向量;pos 表示词语在句子中的位置;dmodel表示词向量的维度;i表示词向量的位置。因此,上述公式表示在每个词语的词向量的偶数位置添加sin变量,奇数位置添加cos变量,以此来填满整个PE矩阵,然后加到input embedding中去,这样便完成位置编码的引入了。

    参考:https://blog.csdn.net/qq_34771726/article/details/102918440

    class PositionalEncoding(nn.Module):
    
        def __init__(self, max_seq_len: int, d_model: int):
            super().__init__()
    
            # Assume d_model is an even number for convenience
            assert d_model % 2 == 0   # 为了编码方便
    
            # ---1.计算PE矩阵
            # 位置编码二维矩阵PE的大小: [max_seq_len, d_model]
            pe = torch.zeros(max_seq_len, d_model)  # 初始化为零矩阵
            # 行:i向量 [0,1,2,..., 999]  表示每个时间步t
            i_seq = torch.linspace(0, max_seq_len - 1, max_seq_len)
            # 列:j向量 [0,2,4,6,8]       表示偶数位
            j_seq = torch.linspace(0, d_model - 2, d_model // 2)   #(0, 8, 5)
            # 生成网格数据: 2个矩阵[1000, 5]
            pos, two_i = torch.meshgrid(i_seq, j_seq)
            pe_2i = torch.sin(pos / 10000**(two_i / d_model))    # 偶数位sin
            pe_2i_1 = torch.cos(pos / 10000**(two_i / d_model))  # 奇数位cos
            # stack拼接到第2个维度[0,1,2],在把3维重塑为2维
            pe = torch.stack((pe_2i, pe_2i_1), 2).reshape(max_seq_len, d_model)
    
            # ---2.定义嵌入层
            self.embedding = nn.Embedding(max_seq_len, d_model)  # 定义了一个嵌入层
            self.embedding.weight.data = pe      # 使用位置编码计算好的嵌入矩阵对其进行初始化
            self.embedding.requires_grad_(False) # 将其参数设为不可训练
    
        def forward(self, t):
            # 调用嵌入层方法
            # t表示抽取的时间点向量: [32, 43, 85, 31, 86, 90, 67, 61, 50, 33, 87, 48, 31, 48, 48, 93]
            return self.embedding(t)
    

    4.调用位置编码器PE

    1. 对输入的位置向量,如[1,2,…,16]. 先经过PE编码为词向量长度: [16, 1, 128]
    2. 与原图x拼接,即x+t
    • 先经过一个线性层,将词向量维度转换为与图片通道数一致
    • 与原图相加拼接
    ‘’‘
    对输入时间点的位置编码
    ’‘’
    # 1. 设置位置编码器PE
    max_seq_len = 1000   # 最大序列长度
    d_model = 128        # 编码向量的维度
    pe = PositionalEncoding(max_seq_len, d_model)
    pe
    
    # 2.随机抽取时间点
    n_steps = 100
    batch_size = 16
    t1 = torch.randint(0, n_steps, (batch_size, ))    # 随机抽取16个时间点
    
    # 3.对时间点进行PE编码
    p1 = pe(t1)
    p1    # 得到位置编码结果[1000, 128]
    
    

    将编码后的时间与原图进行拼接

    ‘’‘
    将编码后的时间与原图进行拼接
    ’‘’
    pe_dim = 128   # 词向量维度
    channel = 1    # 图片通道数C
    n = 16   # 图片批量大小(也就是上面时间t的步数)
    
    # 1.先经过一个线性层,将词向量维度转换为与图片通道数一致
    # 设置映射空间层
    pe_linear = nn.Sequential(nn.Linear(pe_dim, channel), 
                               nn.ReLU(),
                               nn.Linear(channel, channel))
    # 将128的词词向量维度转换为1的图片通道数
    pe_v = pe_linear(p1).reshape(n, -1, 1, 1)  # (1, 128) -> (1,1) -> (1,1,1)
    # pe_linear: 降维 128 -> 1 通道数
    # reshape: 整理维度 [n, C, 1, 1]
    
    # 2.将整理后的位置编码与图片连接
    # 原图
    x = torch.randn(1, 28, 28) 
    
    # 拼接
    x + pe_v  
    # [16, 1, 28, 28]  
    # 16: 数据批量大小(时间点的个数 - batch_size)
    # 1: 通道数
    # 28*28: 图片大小
    
    

    对应视频讲解:

    【代码详解】扩散模型中如何融合时间信息t到图x上(x+t),Transformer中的位置编码PE(position encoding)

  • 相关阅读:
    剑指 Offer 03. 数组中重复的数字
    嵌入式知识总结:交叉编译、ARM汇编、驱动开发
    【附源码】计算机毕业设计SSM软考刷题系统
    Day59——Ajax,前后端数据编码格式
    芯片漫游指南(1)-- UVM世界观
    Apache Hive之数据查询
    Flask Web开发实验二:模板(2)
    OpenCV入门2:OpenCV中的图像表示
    2.7 配置Keepalived提高吞吐量
    Redis-数据库和缓存如何保证一致性?
  • 原文地址:https://blog.csdn.net/weixin_43633501/article/details/139380462