构建中文版的 nanoGPT - 位置嵌入( Position Embedding)
构建中文版的 nanoGPT - 位置嵌入( Position Embedding)
flyfish
参考
https://github.com/shaoshengsong/nanoGPT-cn
Input_Embedding=Token_Embedding+Position_Embedding Input\_Embedding = Token\_Embedding + Position\_Embedding Input_Embedding=Token_Embedding+Position_Embedding
位置编码
先说位置
- 文本是有序序列,每一个字词、Token 在句子里都有固定先后顺序、排布顺位
- 注意力数学计算本身没有时序、顺序概念,模型分不清字词谁在前、谁在后、距离远近
- 位置 = 每个 Token 在序列里的顺序编号、先后次序、相对远近关系
- 位置信息,就是给每一个 Token 附加专属时序标识,让模型看懂文本语序
再说位置编码的样子
要分情况
单点位置:向量
连续序列整体、编码总表:矩阵
批量输入模型:三维张量
1 基础单元
单个序列位置,对应一维向量
维度等于模型嵌入维度 dmodeld_{model}dmodel。
2 连续序列整体
一段长度为 TTT 的文本序列,全部位置编码组合,构成二维矩阵
形状:[序列长度T, 嵌入维度dmodel][序列长度T,\ 嵌入维度d_{model}][序列长度T, 嵌入维度dmodel]
3 全局编码存储表
提前预定义、存储全部可用位置编码,本身是大尺寸二维参数矩阵
- 原生三角函数位置编码:预计算固定大矩阵,上限为最大支持长度
- nanoGPT 可学习位置编码:
wpe本身就是可训练参数矩阵,尺寸 [block_size, n_embd][block\_size,\ n\_embd][block_size, n_embd]
4 批量训练形态
送入模型批量数据,拓展为三维张量
形状:[批次数量B, 序列长度T, 嵌入维度dmodel][批次数量B,\ 序列长度T,\ 嵌入维度d_{model}][批次数量B, 序列长度T, 嵌入维度dmodel]
原始的Transformer架构图有Postional Encoding,nanoGPT的是Position_Embedding
标准Transformer的固定位置编码不可训练的原因
没有使用 nn.Parameter()
可训练的参数必须用 nn.Parameter() 包裹
这里从头到尾没有出现一次 nn.Parameter
对比 nanoGPT:wpe = nn.Embedding(...) 内部就是 Parameter,所以可训练
使用了 register_buffer('pe', pe)
buffer 的定义:模型的固定常量/预计算数据
不会被优化器更新
不会参与反向传播梯度计算
保存模型时会跟着保存,但永远不会变
所有值都是数学公式一次性算死的
位置向量是:sin / cos 公式 → 直接算出结果 → 锁死不变
和训练数据、loss、梯度完全无关
self.dropout
self.dropout = nn.Dropout(dropout)
Dropout 本身没有可训练参数!
它只是一个随机失活的运算规则,不参与学习,不影响位置编码。
nanoGPT 的位置嵌入流程 =
随机初始化位置嵌入矩阵
→ 输入序列生成位置索引
→ 通过嵌入层查找对应向量
→ 与词嵌入相加
→ 训练中通过反向传播优化位置向量
→ 最终学习到能捕捉语序信息的位置表示。



- 先有:标准Transformer的固定位置编码(不可训练)
- 后来演化:GPT/nanoGPT把它替换成了可学习的嵌入层(位置嵌入也就是 Position_Embedding)
叫位置编码还是叫位置嵌入
固定位置编码
最早原版 Transformer 采用这种方案
没有专门的嵌入参数层,依靠正弦、余弦数学公式直接计算出位置对应的向量。
向量数值一经算出就固定不变,不会跟着训练更新,这就是最早形态的位置编码。
位置嵌入
后续 GPT、nanoGPT 这类纯解码器模型,放弃公式计算
改用嵌入层存放位置对应的向量,这些向量是模型参数,能在训练中不断学习调整优化,这种形式就被称作位置嵌入。
广义上:位置编码是统称
不管是公式计算,还是参数学习得到的位置向量,只要用来标识序列先后顺序,都可以叫做位置编码。
狭义上:
公式算出、不可训练 → 习惯直接叫位置编码
嵌入层存储、可训练更新 → 习惯叫做位置嵌入
位置嵌入是位置编码的一种实现形式。
怎么叫都可以没毛病
nanoGPT的位置嵌入
self.transformer = nn.ModuleDict(dict(
wte = nn.Embedding(config.vocab_size, config.n_embd), # 词嵌入
wpe = nn.Embedding(config.block_size, config.n_embd), # 位置嵌入(重点!)
drop = nn.Dropout(config.dropout),
h = nn.ModuleList([Block(config) for _ in range(config.n_layer)]),
ln_f = LayerNorm(config.n_embd, bias=config.bias),
))
这一行:
wpe = nn.Embedding(config.block_size, config.n_embd)
-
wpe = Word Position Embedding
= 位置嵌入层
专门存每个位置应该长什么样的向量。 -
nn.Embedding(…)
PyTorch 里的查表层。 = 一个二维矩阵
可以把它理解成:一本字典,每一行对应一个位置的向量 -
config.block_size
模型能接受的最长句子长度
比如 1024
→ 位置编号:0,1,2,3,…,1023
→ 字典一共 1024 行 -
config.n_embd
每个位置向量的维度 比如 768
→ 每一行有 768 个数字
最终得到一张表:
形状:[1024 个位置,768 维向量]
这张表是模型参数,会跟着训练一起变。
更多推荐

所有评论(0)