文章目录一、问题模型看不懂文字二、分词为什么不按字/词切三、嵌入让数字携带语义四、位置编码告诉模型我是第几个词五、思考题参考《图解大模型》第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最高频对 er → er l o w er l o w n e w er 合并2最高频对 lo → lo lo w er lo w n e w er ...最终常见词变成整体 token罕见词被拆成子词用 tiktoken 直接感受importtiktoken enctiktoken.get_encoding(cl100k_base)# GPT-4 使用的分词器# 普通英文通常整词就是一个 tokenprint(enc.encode(hello))# [15339] — 1个tokenprint(enc.encode(Hello))# [9906] — 大小写是不同 token# 罕见词被拆开print(enc.encode(Unbelievable))# [1844, 14110, 481] — 拆成3个子词print(enc.encode(未知词语))# 中文每个字甚至被拆成多个字节token# encode decode 是互逆的idsenc.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 (ID9906) → [0.23, -0.15, 0.88, ..., 0.05] (768维) Hi (ID15338) → [0.21, -0.13, 0.85, ..., 0.04] (768维) Dog (ID39) → [-0.8, 0.44, -0.12, ..., 0.77] (768维) 余弦相似度 sim(Hello, Hi) ≈ 0.97 ← 很接近 sim(Hello, Dog) ≈ 0.12 ← 很远这个嵌入矩阵是可学习的参数在预训练过程中不断更新使得语义相近的词向量自然聚拢。用 transformers 感受一下fromtransformersimportAutoTokenizer,AutoModelimporttorch tokenizerAutoTokenizer.from_pretrained(bert-base-uncased)modelAutoModel.from_pretrained(bert-base-uncased)# 只看嵌入层不经过 Transformerinputstokenizer([hello,hi,dog],return_tensorspt,paddingTrue)withtorch.no_grad():embeddingsmodel.embeddings.word_embeddings(inputs[input_ids])# 计算相似度fromtorch.nn.functionalimportcosine_similarity e_helloembeddings[0,1]# hello 的嵌入向量e_hiembeddings[1,1]# hie_dogembeddings[2,1]# dogprint(cosine_similarity(e_hello.unsqueeze(0),e_hi.unsqueeze(0)))# 接近 1print(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(posk) 可以从 PE(pos) 变换得到RoPE旋转位置编码更进一步把位置信息编码成旋转角度点积自然体现相对距离。简单说不是加了位置向量就自动有位置信息而是位置编码的设计让注意力计算能感知相对位置。两种主流方案方案代表模型特点绝对位置编码可学习GPT-2、BERT简单但长度固定训练时定死旋转位置编码RoPELLaMA、Qwen泛化性好支持更长的上下文直觉位置编码不是单独记我是第5个词而是把位置信息编码进向量方向让注意力计算时自然感知到相对位置关系。五、思考题“tokenization” 被拆成 3 个 token“the” 是 1 个 token。这意味着处理中文时 token 消耗会更多吗用 tiktoken 验证一下。嵌入矩阵在预训练前是随机初始化的为什么训练后语义相近的词向量会自动聚拢提示想想预测下一个词的任务如何迫使模型学出这个结构。如果去掉位置编码猫追狗和狗追猫的模型输出会完全一样吗提示注意力机制本身能感知顺序吗检验能用 tiktoken 对任意文本分词并解码能解释为什么词嵌入能表示语义相似度。
阅读完成 · 觉得有帮助?