【大模型基础(2)】文本如何变成数字 — 分词、嵌入与位置信息
参考:《图解大模型》第2章
核心问题:LLM 只能处理数字,文本怎么变成数字?这些数字怎么携带语义?
一、问题:模型看不懂文字
神经网络本质上是矩阵乘法,只能处理数字。所以文本进入模型之前,需要经历两步转换:
"Hello world"
↓ 分词(Tokenization)
[9906, 1917] ← 整数 ID
↓ 嵌入(Embedding)
[[0.23, -0.15, ...], ← 每个 ID 变成一个浮点向量
[0.11, 0.88, ...]]
二、分词:为什么不按字/词切?
直觉上有三种方案:
| 方案 | 问题 |
|---|---|
| 按整词切("hello " = 1个token) | 词汇表爆炸(几十万词),遇到新词/变形词无法处理 |
| 按字符切(“h”,“e”,“l”,“l”,“o”) | 序列极长,模型难以学习词语级语义 |
| 按子词切(BPE) | 平衡词汇量和覆盖率,主流方案 |
BPE(字节对编码)的直觉:从字符开始,反复把最高频的相邻对合并成一个新 token,直到词汇表达到预设大小。
初始: "l o w e r" "l o w" "n e w e r"
合并1:最高频对 e+r → er
"l o w er" "l o w" "n e w er"
合并2:最高频对 l+o → lo
"lo w er" "lo w" "n e w er"
...最终:常见词变成整体 token,罕见词被拆成子词
用 tiktoken 直接感受:
import tiktoken
enc = tiktoken.get_encoding("cl100k_base") # GPT-4 使用的分词器
# 普通英文:通常整词就是一个 token
print(enc.encode("hello")) # [15339] — 1个token
print(enc.encode("Hello")) # [9906] — 大小写是不同 token!
# 罕见词被拆开
print(enc.encode("Unbelievable")) # [1844, 14110, 481] — 拆成3个子词
print(enc.encode("未知词语")) # 中文每个字甚至被拆成多个字节token
# encode + decode 是互逆的
ids = enc.encode("Large Language Models")
print(ids) # [35, 4876, 16688, 27992]
print(enc.decode(ids)) # "Large Language Models"
关键直觉:token 不等于词,也不等于字符。"tokenization"可能是 1 个 token,也可能是 3 个,取决于它在训练数据里出现的频率。
运行 python tokenizer.py 可以交互式探索任意文本的分词结果。
三、嵌入:让数字携带语义
分词只是给每个 token 分配了一个整数 ID(像字典的页码),ID 本身没有语义。嵌入层做的事是:把 ID 映射到一个高维空间里的向量,让语义相近的词向量也相近。
为什么训练后语义相近的词向量会聚拢?
关键在于预训练任务:预测下一个词。
"The cat sat on the [mat]"
↓ 预测 "mat"
"The dog sat on the [mat]" ← "cat" 和 "dog" 出现在几乎相同的上下文中
↓ 预测 "mat"
"The animal sat on the [mat]" ← "dog" 和 "animal" 也能互相替换
训练过程中:
- “cat” 和 “dog” 经常出现在相似的上下文里(能互相替换预测出后面的词)
- 模型发现:把 “cat” 和 “dog” 的向量靠在一起,对预测更有利
- 因为:这样 attention 层可以从对方的"经验"中获益
结果是:经常能互相替换的词,向量自然聚拢——这就是语义相似度的本质。
Token ID → 嵌入矩阵查表 → 浮点向量
"Hello" (ID=9906) → [0.23, -0.15, 0.88, ..., 0.05] (768维)
"Hi" (ID=15338) → [0.21, -0.13, 0.85, ..., 0.04] (768维)
"Dog" (ID=39) → [-0.8, 0.44, -0.12, ..., 0.77] (768维)
余弦相似度:
sim("Hello", "Hi") ≈ 0.97 ← 很接近
sim("Hello", "Dog") ≈ 0.12 ← 很远
这个嵌入矩阵是可学习的参数,在预训练过程中不断更新,使得语义相近的词向量自然聚拢。
用 transformers 感受一下:
from transformers import AutoTokenizer, AutoModel
import torch
tokenizer = AutoTokenizer.from_pretrained("bert-base-uncased")
model = AutoModel.from_pretrained("bert-base-uncased")
# 只看嵌入层(不经过 Transformer)
inputs = tokenizer(["hello", "hi", "dog"], return_tensors="pt", padding=True)
with torch.no_grad():
embeddings = model.embeddings.word_embeddings(inputs["input_ids"])
# 计算相似度
from torch.nn.functional import cosine_similarity
e_hello = embeddings[0, 1] # "hello" 的嵌入向量
e_hi = embeddings[1, 1] # "hi"
e_dog = embeddings[2, 1] # "dog"
print(cosine_similarity(e_hello.unsqueeze(0), e_hi.unsqueeze(0))) # 接近 1
print(cosine_similarity(e_hello.unsqueeze(0), e_dog.unsqueeze(0))) # 较低
四、位置编码:告诉模型"我是第几个词"
Transformer 的自注意力是全局并行的——它同时看所有词,本身不知道词的顺序。"猫追狗"和"狗追猫"在没有位置信息时对模型来说是一样的。
实现方式:逐元素相加(不是拼接,是两个向量每个维度相加):
"猫"的词向量: [0.2, -0.1, 0.8]
位置0的编码: [0.1, 0.5, -0.3]
相加: [0.3, 0.4, 0.5] ← 最终输入
维度必须相同(都是 d_model 维)
模型如何"理解"这个位置信息?
关键在注意力计算。注意力用点积衡量两个词的相关性,而点积对向量方向敏感:
"猫"(位置0): [0.3, 0.4, 0.5]
"追"(位置1): [0.4, 0.5, 0.4] ← 位置编码不同,向量方向不同
点积("猫", "追") = 0.3×0.4 + 0.4×0.5 + 0.5×0.4 = 0.54
如果"猫"在位置1、"追"在位置0,由于位置编码变了,点积结果也会不同。模型在训练中学会:同样的语义关系,在不同相对位置会有不同的注意力分数模式。
不是让模型记住"这是第5个词",而是让注意力机制能感知"这两个词相距多远"。
关键:位置编码不是任意向量,而是精心设计的
原始 Transformer 用正弦/余弦函数生成位置编码:
位置 pos 的第 i 维 = sin(pos / 10000^(2i/d_model))
这种设计的妙处:
- 不同位置的编码有固定模式,模型能泛化到训练时没见过的长度
- 相对位置可以通过线性变换推导出来(PE(pos+k) 可以从 PE(pos) 变换得到)
RoPE(旋转位置编码)更进一步:把位置信息编码成旋转角度,点积自然体现相对距离。
简单说:不是"加了位置向量就自动有位置信息",而是位置编码的设计让注意力计算能感知相对位置。
两种主流方案:
| 方案 | 代表模型 | 特点 |
|---|---|---|
| 绝对位置编码(可学习) | GPT-2、BERT | 简单,但长度固定(训练时定死) |
| 旋转位置编码(RoPE) | LLaMA、Qwen | 泛化性好,支持更长的上下文 |
直觉:位置编码不是单独记"我是第5个词",而是把位置信息编码进向量方向,让注意力计算时自然感知到相对位置关系。
五、思考题
- “tokenization” 被拆成 3 个 token,“the” 是 1 个 token。这意味着处理中文时 token 消耗会更多吗? 用 tiktoken 验证一下。
- 嵌入矩阵在预训练前是随机初始化的,为什么训练后语义相近的词向量会自动聚拢? 提示:想想预测下一个词的任务如何迫使模型学出这个结构。
- 如果去掉位置编码,"猫追狗"和"狗追猫"的模型输出会完全一样吗? 提示:注意力机制本身能感知顺序吗?
检验:能用 tiktoken 对任意文本分词并解码;能解释为什么词嵌入能表示语义相似度。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)