跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客我的书AI学习GitHub 精选镜像AI 生图工具UI配色美学关于
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
Python

Transformer 位置编码原理与实现

原理 在之前的编码器 - 解码器介绍中,我们发现模型并没有记录时序相关信息,即没有感知不同词汇的位置顺序。这会导致一个问题,针对'我喜欢你'这句话,经过 Embedding 处理后,再进入编码器 - 解码器处理,最后生成的内容,是和输入'你喜欢我'最后生成的内容是一样的,但我们知道,这两句是含义完全不一样的语句。 加入位置编码,可以解决这个问题。位置编码通过给每个位置添加一个向量,这个向量包含了位…

DevOpsTeam发布于 2026/4/6更新于 2026/9/1056K 浏览

原理

在之前的编码器 - 解码器介绍中,我们发现模型并没有记录时序相关信息,即没有感知不同词汇的位置顺序。这会导致一个问题,针对'我喜欢你'这句话,经过 Embedding 处理后,再进入编码器 - 解码器处理,最后生成的内容,是和输入'你喜欢我'最后生成的内容是一样的,但我们知道,这两句是含义完全不一样的语句。

加入位置编码,可以解决这个问题。位置编码通过给每个位置添加一个向量,这个向量包含了位置信息,然后把这个向量加到词汇向量上。

例如:
位置 1 向量:[0.1,0.2,0.3,...]
位置 2 向量:[0.4,0.5,0.6,...]
位置 3 向量:[0.7,0.8,0.9,...]

'我喜欢你',添加位置编码后:
'我'在位置 1:'我'的词向量 + 位置 1 向量
'喜欢'在位置 2:'喜欢'的词向量 + 位置 2 向量
'你'在位置 3:'你'的词向量 + 位置 3 向量

经过这样处理,Transformer 就可以区分词的位置了。

实现

在 Transformer 中,使用的是正弦位置编码。

正弦位置编码详解

  1. 参数
    • d_model: 8
    • max_len: 10
  2. 位置索引
    • position: [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
  3. 分母项
    • div_term: [1.0, 0.3162, 0.1, 0.0316]
    • 解释:div_term = 10000^(-2i/d_model)
    • i 是维度索引(0, 2, 4, 6, ...),用于控制不同维度的频率
  4. 位置编码矩阵
    • 形状:torch.Size([10, 8])
    • 数据:
      位置 0: [0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0]
      位置 1: 
      位置 2: 
      位置 3: 
      位置 4: 
      ...
      
[0.8415, 0.5403, 0.3129, 0.9499, 0.0998, 0.9950, 0.0316, 0.9995]
[0.9093, -0.4161, 0.5946, 0.8040, 0.1987, 0.9801, 0.0632, 0.9980]
[0.1411, -0.9900, 0.8120, 0.5835, 0.2955, 0.9553, 0.0948, 0.9955]
[-0.7568, -0.6536, 0.9516, 0.3073, 0.3894, 0.9211, 0.1263, 0.9920]
  • 公式与含义
    • 公式:
      PE(pos, 2i) = sin(pos / 10000^(2i/d_model))
      PE(pos, 2i+1) = cos(pos / 10000^(2i/d_model))
      
    • 含义:
      • pos: 位置索引(0, 1, 2, ...)
      • 2i: 偶数维度(0, 2, 4, ...)
      • 2i+1: 奇数维度(1, 3, 5, ...)
    • 特点:
      • 偶数维度用 sin,奇数维度用 cos
      • 不同维度有不同的频率
  • 可视化与作用
    • 位置编码的每个维度:
      • 维度 0: sin(pos * freq_0)
      • 维度 1: cos(pos * freq_0)
      • 维度 2: sin(pos * freq_1)
      • 维度 3: cos(pos * freq_1)
      • ...
    • 不同维度的频率:
      • 低维度:高频率(快速变化),捕捉局部位置
      • 高维度:低频率(慢速变化),捕捉全局位置
  • 位置编码添加过程

    1. 词向量
      • 形状:torch.Size([1, 3, 8])
      • 数据:
        位置 0: [0.1234, -0.5678, 0.9012, -0.3456, 0.7890, -0.1234, 0.5678, -0.9012]
        位置 1: [0.2345, -0.6789, 0.0123, -0.4567, 0.8901, -0.2345, 0.6789, -0.0123]
        位置 2: [0.3456, -0.7890, 0.1234, -0.5678, 0.9012, -0.3456, 0.7890, -0.1234]
        
    2. 位置编码
      • 形状:torch.Size([3, 8])
      • 数据:
        位置 0: [0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0]
        位置 1: [0.8415, 0.5403, 0.3129, 0.9499, 0.0998, 0.9950, 0.0316, 0.9995]
        位置 2: [0.9093, -0.4161, 0.5946, 0.8040, 0.1987, 0.9801, 0.0632, 0.9980]
        
    3. 添加位置编码后
      • 形状:torch.Size([1, 3, 8])
      • 数据:
        位置 0: [0.1234, 0.4322, 0.9012, 0.6544, 0.7890, 0.8766, 0.5678, 0.0988]
        位置 1: [1.0760, -0.1386, 0.3252, 0.4932, 0.9899, 0.7605, 0.7105, 0.9872]
        位置 2: [1.2549, -1.2051, 0.7180, 0.2362, 1.0999, 0.6345, 0.8532, 0.8746]
        
      • 计算:输出 = 词向量 + 位置编码
    4. 总结
      • 步骤:
        1. 获取词向量
        2. 获取位置编码
        3. 词向量 + 位置编码
      • 结果:每个词的向量包含了位置信息,Transformer 可以区分不同位置的词
      • 数据流动:
        输入 (1, 3, 8)
        ↓ Embedding
        词向量 (1, 3, 8)
        ↓ + 位置编码 (1, 3, 8)
        输出 (1, 3, 8)
        
    class PositionalEncoding(nn.Module):
        """位置编码模块"""
        def __init__(self, args):
            super(PositionalEncoding, self).__init__()
            # Dropout 层
            # self.dropout = nn.Dropout(p=args.dropout)
            # block size 是序列的最大长度
            pe = torch.zeros(args.block_size, args.n_embd)
            position = torch.arange(0, args.block_size).unsqueeze(1)
            # 计算 theta
            div_term = torch.exp(torch.arange(0, args.n_embd, 2) * -(math.log(10000.0) / args.n_embd))
            # 分别计算 sin、cos 结果
            pe[:, 0::2] = torch.sin(position * div_term)
            pe[:, 1::2] = torch.cos(position * div_term)
            pe = pe.unsqueeze(0)
            self.register_buffer("pe", pe)
    
        def forward(self, x):
            # 将位置编码加到 Embedding 结果上
            x = x + self.pe[:, :x.size(1)].requires_grad_(False)
            return x
    

    位置编码类型

    类型特点优点缺点使用模型
    正弦位置编码固定公式不需要参数,可外推不能学习Transformer
    可学习的位置编码可以学习效果可能更好需要参数,不能外推BERT、GPT
    旋转位置编码相对位置适合长序列实现复杂LLaMA,GPT-NeoX

    目录

    1. 原理
    2. 实现
    3. 正弦位置编码详解
    4. 位置编码添加过程
    5. 位置编码类型

    更多推荐文章

    查看全部
    • 具身智能机器人运控通讯架构与实现系列
    • 无人机 RGB+ 红外双模态小目标行人检测系统构建与数据集详解
    • AI 产品经理入行必备知识梳理
    • FPGA 是什么?核心原理与优势解析
    • TSPR-WEB-LLM-HIC 四元结构 AI 生成式引擎技术白皮书
    • 2026 年 3 月 AI 领域动态:多模态模型与自主智能体热点梳理
    • WebMCP:浏览器原生 AI 交互新范式
    • 用 FastAPI 零配置构建 SSE MCP 服务器
    • AI 领域必读精选:6 部名家名作
    • Java 反射机制详解:从原理到实战
    • AI 辅助开发在线图书借阅平台设计与实现
    • 为何我想要线性历史与签名提交,GitHub 合并策略却难以满足
    • ChatGPT 记忆功能揭秘:使用与管理指南
    • React 实战:从零构建井字棋游戏
    • Qwen3.5-9B 对比 GPT-oss-120B:混合架构与基准测试全解析
    • 知网 AIGC 检测原理与降低疑似度实战指南
    • 使用 Miniforge3 管理 Python 环境的详细指南
    • GitHub Token 权限配置与安全实践指南
    • Python常用算法解析:从优雅简洁到高效实战
    • Qwen-Image-2512:消费级 GPU 运行 AI 文生图指南

    相关免费在线工具

    • curl 转代码

      解析常见 curl 参数并生成 fetch、axios、PHP curl 或 Python requests 示例代码。 在线工具,curl 转代码在线工具,online

    • Base64 字符串编码/解码

      将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

    • Base64 文件转换器

      将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

    • Markdown转HTML

      将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online

    • HTML转Markdown

      将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online

    • JSON 压缩

      通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online