第十三篇:Token 与嵌入空间:AI如何“阅读”人类的语言?
Ai如何识别人类语言
- 前言:AI眼中的“巴别塔”——语言的数字化难题
- 第一章:Tokenization —— 将文本“打碎”成AI能懂的“积木”
- 1.1为什么不能直接用“词”?—— OOV问题的噩梦
- 1.2 主流的分词魔法:BPE (字节对编码) 算法简介
- 1.3 亲眼看看不同模型如何“切分”同一个句子
- 第二章:Embedding —— 赋予“积木”灵魂的“魔法坐标”
- 2.1 告别One-Hot编码:从“稀疏”到“稠密”的革命
- 2.2 终极比喻:嵌入空间是一张“语义地图”
- 2.3 神奇的向量运算:国王 - 男性 + 女性 ≈ 女王
- 第三章:两大核心组件:Embedding层 与 Tokenizer
- 3.1 nn.Embedding:PyTorch中的“查字典”大师
- 3.2 从文本到最终嵌入向量的全流程代码演示
- 4 “位置编码” (Positional Encoding) 的必要性:为什么AI需要“GPS”?
- 尾声:你已掌握AI语言理解的“基石”
前言:AI眼中的“巴别塔”——语言的数字化难题
我们人类,通过眼睛看文字,大脑理解其含义。但对于计算机来说,一个汉字、一个单词,本质上和一张图片、一段声音一样,都只是无法直接计算的“符号”。
AI要处理语言,首先必须解决一个根本问题:如何将这些千变万化的、离散的文字符号,转化成它能理解和计算的、连续的数学形式?
这就好比要建造一座通天塔,我们必须先发明一种所有工匠(神经元)都能理解的通用“建筑语言”。
今天,我们将深入这个“转换引擎”的内部,学习它的两大核心工艺:Tokenization (分词) 和 Embedding (嵌入)。掌握了它们,你就掌握了所有自然语言处理(NLP)模型的“起点”。
第一章:Tokenization —— 将文本“打碎”成AI能懂的“积木”
解释为什么需要分词,并介绍主流的BPE算法,让你明白AI处理文本的最小单位是什么。
1.1为什么不能直接用“词”?—— OOV问题的噩梦
一个最自然的想法是:把每个“单词”作为基本单位,给它一个编号不就行了?
但这样会遇到一个巨大问题:词汇表爆炸和未登录词 (Out-of-Vocabulary, OOV) 问题。
词汇表爆炸:语言中的词语几乎是无限的(各种时态、复数、合成词、专有名词…)。要为所有词都创建一个编号,词汇表会大到无法管理。
OOV问题:如果模型在训练时没见过一个词(比如一个新的网络流行语“i人”),它就完全不知道该如何处理。
1.2 主流的分词魔法:BPE (字节对编码) 算法简介
为了解决这个问题,现代大模型普遍采用一种更聪明的**“子词 (Subword)”分词策略,其中最著名的就是BPE (Byte-Pair Encoding)**。
BPE的核心思想:
初始单位:把所有单词拆成最基本的字符。
迭代合并:不断地在语料库中,寻找出现频率最高的“相邻字符对”,并将它们合并成一个新的、更长的“子词”。
最终词表:重复这个过程,直到达到预设的词汇表大小(比如5万)。
举个例子:
对于单词 deeper,如果er是高频组合,它就先被合并。然后如果deep也是高频组合,它也被合并。最终,deeper 可能会被切分成 [‘deep’, ‘er’] 这两个子词。
BPE的好处:
完美平衡:既能保留高频的完整单词,又能将低频或未知的单词,拆解成已知的、有意义的子词组合(比如transformer -> [‘transform’, ‘er’])。
告别OOV:理论上,只要基础字符集完备,就不存在不认识的“词”,任何新词都能被拆解。
1.3 亲眼看看不同模型如何“切分”同一个句子
本节Tokenizer。它们的“切词”习惯,深刻地影响着模型的性能和行为。让我们来对比一下BERT和GPT-2概括:通过实际代码,让你直观感受不同“流派”的模型(如BERT和GPT-这两个经典模型。
from transformers
不同的 import AutoTokenizer
from transformers import AutoTokenizer# 加载两个经典模型的Tokenizer
# bert-base-uncased 是一个不区分大小写的英语BERT模型
tokenizer_bert = AutoTokenizer.from_pretrained('bert-base-uncased')# gpt2 是OpenAI的经典生成模型
tokenizer_gpt2 = AutoTokenizer.from_pretrained('gpt2')text = "Tokenization is awesome for Large Language Models!"# 使用两个Tokenizer分别进行分词
tokens_bert = tokenizer_bert.tokenize(text)
tokens_gpt2 = tokenizer_gpt2.tokenize(text)print(f"原始句子: {text}")
print("-" * 50)
print(f"🤖 BERT 的“世界观” (切分结果):")
print(tokens_bert)
print(f"Token数量: {len(tokens_bert)}")
print("-" * 50)
print(f"🧠 GPT-2 的“世界观” (切分结果):")
print(tokens_gpt2)
print(f"Token数量: {len(tokens_gpt2)}")
预期输出及深度解析:
原始句子: Tokenization is awesome for Large Language Models!
--------------------------------------------------
🤖 BERT 的“世界观” (切分结果):
['token', '##ization', 'is', 'awesome', 'for', 'large', 'language', 'models', '!']
Token数量: 9
--------------------------------------------------
🧠 GPT-2 的“世界观” (切分结果):
['Token', 'ization', 'Ġis', 'Ġawesome', 'Ġfor', 'ĠLarge', 'ĠLanguage', 'ĠModels', '!']
Token数量: 9
第二章:Embedding —— 赋予“积木”灵魂的“魔法坐标”
解释什么是词嵌入和嵌入空间,让你理解AI是如何“理解”词与词之间的关系的。
Tokenization之后,我们得到了一串数字ID(比如 [101, 7592, 1010, 2022, …])。但这些ID本身是孤立的,没有任何语义信息。7592和2022这两个数字,本身看不出任何关系。
Embedding(嵌入) 的任务,就是将这些离散的、无意义的ID,映射成稠密的、有意义的连续向量。
2.1 告别One-Hot编码:从“稀疏”到“稠密”的革命
早期的一种方法叫One-Hot编码,如果词汇表有5万个词,“猫”的ID是888,那么它的向量就是一个5万维的、只有第888位是1、其余全是0的向量。
缺点:维度灾难、向量稀疏、无法表达词与词的相似性(任意两个One-Hot向量的点积都是0)。
2.2 终极比喻:嵌入空间是一张“语义地图”
词嵌入(Word Embedding)则聪明得多。它将每个Token,都映射到一个相对低维(比如768维)的、稠密的浮点数向量中。
你可以把这个768维的空间,想象成一张巨大的、包含了人类所有概念的**“语义地图”**。
位置代表意义:在这张地图上,意思相近的词,它们的“坐标”(嵌入向量)也互相靠近。比如,“猫”、“狗”、“宠物”这几个点的坐标会离得很近。
方向代表关系:从一个点指向另一个点的“方向”(向量的差),可以编码出某种“关系”。
2.3 神奇的向量运算:国王 - 男性 + 女性 ≈ 女王
这正是嵌入空间最神奇的地方。因为方向代表关系,所以我们可以进行向量运算:
vector(‘国王’) - vector(‘男性’) + vector(‘女性’)
这个计算的结果,会得到一个在“语义地图”上,与vector(‘女王’)的坐标极其接近的向量!
这证明了,Embedding不仅存储了词的“意义”,更存储了词与词之间复杂的**“关系”**。
第三章:两大核心组件:Embedding层 与 Tokenizer
从代码层面,介绍PyTorch中的nn.Embedding层,并展示从文本到最终嵌入向量的全流程。
3.1 nn.Embedding:PyTorch中的“查字典”大师
在PyTorch中,nn.Embedding层就是实现这个“映射”的工具。
你可以把它想象成一个巨大的查询表(lookup table)。
embedding_layer = nn.Embedding(vocab_size, embedding_dim)
vocab_size:词汇表大小(比如50257)。
embedding_dim:你希望每个词向量的维度(比如768)。
这个层内部,就存着一个50257 x 768的巨大权重矩阵。当我们输入一个ID(比如888)时,它就去这个矩阵里,把第888行那一整行的768个浮点数“取”出来,作为这个ID的嵌入向量。
关键是:这个巨大的权重矩阵,是可以学习的!在模型训练的过程中,这些向量的“坐标”会被不断微调,让它们能更好地表达语义关系。
3.2 从文本到最终嵌入向量的全流程代码演示
import torch
import torch.nn as nn
from transformers import AutoTokenizer, AutoConfig# --- 准备工作 ---
# 我们以GPT-2为例,因为它最经典
model_name = 'gpt2'
text = "AI reads the world as vectors."# --- 第一步:初始化Tokenizer和获取配置信息 ---
tokenizer = AutoTokenizer.from_pretrained(model_name)
config = AutoConfig.from_pretrained(model_name)# 从配置中获取词汇表大小和嵌入维度
vocab_size = config.vocab_size
embedding_dim = config.hidden_size # 在Transformer模型中,嵌入维度通常等于隐藏层维度print(f"--- 模型 '{model_name}' 配置 ---")
print(f"词汇表大小 (Vocab Size): {vocab_size}")
print(f"嵌入维度 (Embedding Dimension): {embedding_dim}")
print("-" * 30)# --- 第二步:Tokenization (文本 -> 数字ID) ---
print(f"原始文本: '{text}'")
# return_tensors="pt" 让它直接返回PyTorch Tensor
encoded_input = tokenizer(text, return_tensors="pt")
input_ids = encoded_input['input_ids']# 解码回来看看具体是什么tokens,验证一下
tokens = tokenizer.convert_ids_to_tokens(input_ids[0])
print(f"分词结果 (Tokens): {tokens}")
print(f"对应的Token IDs: {input_ids.tolist()}")
print(f"ID张量的形状: {input_ids.shape}")
print("-" * 30)# --- 第三步:Embedding (数字ID -> 语义向量) ---
# 创建一个与GPT-2配置完全相同的Embedding层
# 这就像创建了一个巨大的、可学习的“字典”,有vocab_size行,每行embedding_dim列
embedding_layer = nn.Embedding(vocab_size, embedding_dim)# 将Token IDs“喂”给Embedding层,进行“查字典”操作
final_embeddings = embedding_layer(input_ids)print("--- 嵌入层输出 ---")
print(f"最终嵌入向量 (Final Embeddings) 的形状: {final_embeddings.shape}")
print("\n✅ 成功!我们已经将一句话转换成了AI能“理解”的、携带语义的向量!")
print("这个形状为 [1, 7, 768] 的Tensor,就是进入Transformer Attention层之前的最终形态。")
预期输出及深度解析:
--- 模型 'gpt2' 配置 ---
词汇表大小 (Vocab Size): 50257
嵌入维度 (Embedding Dimension): 768
------------------------------
原始文本: 'AI reads the world as vectors.'
分词结果 (Tokens): ['AI', 'Ġreads', 'Ġthe', 'Ġworld', 'Ġas', 'Ġvectors', '.']
对应的Token IDs: [[13443, 2 reads, 1 the, 1 world, 1 as, 1 vectors, 1 .]] # 注:具体ID可能略有变化
ID张量的形状: torch.Size([1, 7])
------------------------------
--- 嵌入层输出 ---
最终嵌入向量 (Final Embeddings) 的形状: torch.Size([1, 7, 768])✅ 成功!我们已经将一句话转换成了AI能“理解”的、携带语义的向量!
这个形状为 [1, 7, 768] 的Tensor,就是进入Transformer Attention层之前的最终形态。
4 “位置编码” (Positional Encoding) 的必要性:为什么AI需要“GPS”?
我们知道,注意力机制是并行计算的,它同时“看”到所有词。这带来一个问题:它丢失了单词的顺序信息!
“我打他” 和 “他打我”,在Attention看来,可能是一样的,因为包含的词完全相同。
为了解决这个问题,Transformer的作者发明了位置编码 (Positional Encoding)。它是一个与词嵌入维度相同的、根据单词位置(第1个,第2个…)计算出来的特殊向量。
在将词嵌入向量送入Transformer Block之前,我们会把**“词嵌入”和“位置编码”这两个向量相加**。
最终输入 = 词嵌入 (语义信息) + 位置编码 (位置信息)
这就相当于给每个“积木”,都打上了一个独一无二的“GPS坐标”,让模型在处理它时,既知道**“它是什么”,也知道“它在哪里”**。
尾声:你已掌握AI语言理解的“基石”
恭喜你!今天你已经彻底打通了AI模型处理语言的“任督二脉”。
✨ 本章惊喜概括 ✨
你掌握了什么? | 对应的技能/工具 |
---|---|
理解了AI的“最小单位” | ✅ Tokenization与BPE子词算法 |
洞悉了“语义”的数学表示 | ✅ Embedding与神奇的“语义地图” |
掌握了核心代码实现 | ✅ PyTorch中的nn.Embedding层 |
补全了最后一块拼图 | ✅ 位置编码(Positional Encoding)的意义 |
现在,你已经完全理解,当一句话进入AI模型时,它所经历的从“文字”到“携带位置信息的语义向量”的完整旅程。这是后续所有复杂计算(比如注意力)的唯一输入和基础。 |