大模型只懂数字。这一篇拆「文字 -> 一串向量」的全过程:分词、词嵌入、位置编码。 本文档为教学性模拟--所有向量与热图均为确定性可视化,用以理解原理,非真实模型输出。
模型不认字 · 只认数。第一关就是把字变成数,再变成向量。
同样这行字,模型眼里是一串整数 id。每个 id 对应词表里的一个 token。整条链路从这里开始。
先决定「按什么粒度切」。三种切法的取舍,点 tab 看。
按单个字符切。词表极小(几十~几百),但序列拉得特别长,模型得自己学会「c-a-t 拼成 cat」--负担重。
按空格整词切。序列短,但词表会爆炸(每个变形都算新词),遇到没见过的词直接抓瞎--OOV(词外)问题。
折中:高频整词保留,低频/生词切成可复用的小块(子词)。unhappiness = un + happiness,happiness 又能拆成 happ+iness。词表适中、几乎无 OOV--GPT/Llama 都用这路子。
字符级词表小但序列太长,算力吃不消;词级序列短但词表巨大且怕生词。BPE 用「高频整词 + 低频拆碎」拿到两端好处:词表可控(通常 3 万~15 万),生词也能用已知碎片拼出来。BPE = Byte Pair Encoding,本质是「按出现频率不断合并最高频的字符对」训练出的切分表。
每个 token id 查表变成一个 d_model 维向量。这就是「字 -> 向量」的那一下。
每根柱子是一个维度的值。这串数本身没意义,但训练让意思相近的词向量也靠近--「猫」和「狗」的向量会比「猫」和「汽车」更像。语义就藏在这片向量空间里。
注意力本身没有「先后」概念。得把位置信息额外塞进去。
没有 Position,Attention 把「猫吃鱼」和「鱼吃猫」看成同一件事--因为它只看「谁和谁相关」,不看顺序。位置编码就是把「第几个」这层信息编码进向量。
用一组正弦/余弦波当每个位置的「条形码」。不同维度用不同频率的波,位置不同 -> 条形码不同。优点:不用学,公式直接算,能外推到训练时没见过的长度。
横轴=位置(0~31),纵轴=16 个维度。颜色深浅=该维度的值。能看到不同维度是不同频率的波。
干脆给每个位置开一排可训练的参数,让模型自己学出最优的「位置向量」。简单直接,效果也好--代价是训练多长就只能用多长,超过就抓瞎(外推性差)。
每个位置一串随机但固定的向量(示意可学习参数)。没有波形的规律,全靠学。
不单独加位置向量,而是按位置旋转 Q 和 K。位置越远,旋转角差越大,注意力得分自然衰减。一句话:相对位置直接写进「谁关注谁」的打分里,外推性好、长度好扩展。
每个 token 是一根向量,按其位置旋转一个角度。位置 0 不转,越往后转得越多。两根向量的夹角就编码了它们的相对距离。
把上面三步串起来:文本 -> tokens -> (embedding + position) -> 输入序列。
最终输入 = token_embedding + position_embedding(逐维相加)。这一串向量就是后面所有 Transformer 层的输入。零件备齐,下一篇进入注意力。
这一篇出现的术语 -> 一句话含义。
| 术语 | 一句话 |
|---|---|
token | 分词切出来的最小单位;模型的基本单位 |
vocab / 词表 | 所有 token 的集合,每个有一个整数 id |
BPE | 按频率合并字符对训出的子词分词法,词表适中、几乎无 OOV |
d_model | embedding / 整个模型内部向量的维度(如 512、4096) |
embedding | token id 查表得到的稠密向量;语义藏在向量空间里 |
position encoding | 把「第几个」这层顺序信息编码进向量 |
RoPE | 按位置旋转 Q/K 来注入相对位置,现代 LLM 主流 |
OOV | Out-Of-Vocabulary,词表里没有的词;BPE 几乎消除了它 |
向量有了,下一步让它们「互相看一眼」--Q/K/V 与 softmax 打分。