1. 大模型基础:分词与嵌入的技术解析
在自然语言处理领域,分词(Tokenization)和嵌入(Embedding)是构建大型语言模型(LLM)的两大基石技术。它们共同完成了从人类语言到机器可理解表示的转换过程,就像翻译官将一种语言转换为另一种语言那样重要。
1.1 为什么需要分词和嵌入?
想象一下,你正在教一个完全不懂中文的外国人学习汉语。你会怎么做?通常会从最基础的汉字和词语开始教起,然后逐步过渡到句子和段落。分词和嵌入在LLM中的作用就类似于这个过程:
- 分词:将连续的文本切分成有意义的单元(类似于教外国人认字)
- 嵌入:为这些单元赋予数学表示(类似于解释每个字词的含义)
这种转换之所以必要,是因为计算机本质上只能处理数字。我们需要将"我爱你"这样的情感表达转化为计算机能够理解和处理的数值形式。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 分词技术详解
2.1 分词的基本概念
分词是将原始文本分解为模型可以处理的最小单元(token)的过程。这个过程看似简单,实则充满挑战和设计考量。
2.1.1 分词的三种主要方法
-
字符级分词(Character-level)
- 示例:"apple" → ['a', 'p', 'p', 'l', 'e']
- 优点:词表极小(通常不超过256个字符),几乎不会遇到未知字符
- 缺点:序列过长,单个字符缺乏语义信息,模型处理效率低
-
词级分词(Word-level)
- 示例:"I love apples" → ['I', 'love', 'apples']
- 优点:语义单元完整,序列较短
- 缺点:词表庞大(英语约100万词),无法处理新词和变形词
-
子词分词(Subword-level)
- 示例:"unbelievable" → ['un', 'believ', 'able']
- 优点:平衡词表大小和处理能力,能有效应对新词
- 缺点:需要训练分词器,实现复杂度较高
2.2 主流分词算法
现代LLM主要采用以下三种分词算法或其变种:
2.2.1 BPE(Byte Pair Encoding)
BPE是一种基于统计的分词算法,其核心思想是通过不断合并高频字符对来构建词表。
算法步骤:
- 初始化:将所有单词拆分为字符
- 统计相邻字符对的频率
- 合并频率最高的字符对
- 重复步骤2-3,直到达到预设的词表大小
实际案例:
假设我们有如下语料:
code复制hug: 10次
pug: 5次
pun: 12次
bun: 4次
hugs: 5次
第一次合并会找到最高频的字符对(u,g),合并为"ug"。经过多次合并后,最终可能得到包含"hug"、"pug"、"pun"等子词的词表。
BPE的优势在于它能够自动发现语言中的常见词缀和词根,如英语中的"un-"、"-ing"等。
2.2.2 WordPiece
WordPiece是BPE的改进版本,被用于BERT等模型。它与BPE的主要区别在于合并策略:
- BPE:基于字符对的绝对频率
- WordPiece:基于字符对的相对频率(似然值)
WordPiece的合并评分公式为:
score = freq(x,y) / (freq(x) × freq(y))
这种策略避免了仅因高频而合并不相关的字符对,使分词结果更具语言学意义。
2.2.3 SentencePiece
SentencePiece采用了与BPE/WordPiece不同的自顶向下策略,特别适合处理没有明确词边界的语言(如中文、日文)。
核心特点:
- 直接处理原始文本,不需要预分词
- 支持多种采样策略,可以生成不同的分词结果
- 能够完美还原原始文本(包括空格等特殊字符)
SentencePiece的Unigram算法版本从一个大的候选词表开始,通过逐步删除最不重要的token来优化词表。
2.3 分词的实际应用问题
2.3.1 OOV(Out-of-Vocabulary)问题
OOV问题是指遇到词表中不存在的词汇时的处理方式。传统词级分词会使用特殊标记(如
示例对比:
| 输入文本 | 词级分词 | 子词分词 |
|---|---|---|
| ChatGPT | ['Chat', 'G', 'PT'] | |
| DeepSeek-V3 | ['Deep', 'Seek', '-', 'V', '3'] | |
| 超级计算机 | ['超级', '计算', '机'] |
2.3.2 分词粒度的权衡
分词粒度需要在多个因素间取得平衡:
| 粒度 | Token数量 | 语义完整性 | 适用场景 |
|---|---|---|---|
| 粗粒度 | 少 | 高 | 通用文本、对话 |
| 细粒度 | 多 | 低 | 代码、专业术语 |
| 字符级 | 最多 | 最低 | 极端情况 |
现代LLM通常采用中等粒度的分词方案,词表大小在30k-150k之间。例如:
- GPT-4:约50,000词表
- Llama 3:128,000词表
- DeepSeek-V3:100,000+词表(特别优化中文)
3. 嵌入技术详解
3.1 从Token到向量的转换过程
分词得到的token ID只是离散的索引,嵌入层负责将这些索引转换为具有语义意义的连续向量。
3.1.1 Token ID(离散表示)
分词后,每个token被赋予一个唯一的整数ID:
code复制"I love AI" → [40, 3021, 15592]
这些ID本身没有数学意义,40和41之间没有语义上的关联。
3.1.2 One-Hot编码(理论中间步骤)
One-Hot编码将每个token表示为词表大小的稀疏向量:
code复制假设词表大小V=50000
"love"(ID=3021) → [0,0,...,1,...,0](第3021位为1)
这种表示的问题:
- 极度稀疏(99.998%的元素为0)
- 所有向量互相正交,无法表达语义关系
- 存储和计算效率极低
3.1.3 Dense Embedding(稠密向量)
嵌入层实际上是一个可学习的查找表:
code复制Embedding(x) = W_E[x]
其中W_E ∈ R^(V×d)是嵌入矩阵:
- V:词表大小(如150,000)
- d:嵌入维度(如4096,远小于V)
实际过程:
python复制vocab_size = 150000
embed_dim = 4096
# 初始化嵌入矩阵(训练前随机,训练后包含语义)
embedding_matrix = torch.randn(vocab_size, embed_dim)
# 查表操作
token_id = 3021 # "love"
embedding_vector = embedding_matrix[token_id] # 形状: [4096]
3.2 嵌入空间的特性
3.2.1 语义几何
嵌入空间最神奇的特性是它能够捕捉词语之间的语义关系。经典的例子是:
code复制king - man + woman ≈ queen
这表明模型不仅学习了单个词的含义,还理解了词与词之间的关系(如性别、时态、整体-部分等)。
3.2.2 静态嵌入 vs 上下文嵌入
-
静态嵌入(Word2Vec, GloVe):
- 每个词有固定向量表示
- 无法处理一词多义
- 示例:"bank"在"river bank"和"bank account"中向量相同
-
上下文嵌入(Transformer模型):
- 同一个词在不同上下文中向量不同
- 通过模型动态调整
- 示例:"bank"在不同句子中有不同表示
4. 实战应用
4.1 使用tiktoken进行分词
tiktoken是OpenAI开源的高性能分词库,比HuggingFace的tokenizer快3-6倍。
python复制import tiktoken
def analyze_tokens(text: str, model: str = "gpt-4"):
print(f"--- Model: {model} ---")
# 获取编码器
try:
encoding = tiktoken.encoding_for_model(model)
except KeyError:
print(f"Warning: Model {model} using default cl100k_base")
encoding = tiktoken.get_encoding("cl100k_base")
# 编码 (Text -> Token IDs)
token_ids = encoding.encode(text)
print(f"Text: {text}")
print(f"Token IDs: {token_ids}")
print(f"Count: {len(token_ids)}")
# 解码查看每个token
print("Token Breakdown:")
for tid in token_ids:
token_bytes = encoding.decode_single_token_bytes(tid)
try:
print(f" {tid}: {token_bytes.decode('utf-8')}")
except:
print(f" {tid}: {token_bytes}")
# 测试
text_sample = "Hello, world! 我爱大模型 2025."
analyze_tokens(text_sample, "gpt-4")
4.2 可视化嵌入空间
python复制import numpy as np
import matplotlib.pyplot as plt
from sklearn.manifold import TSNE
from sentence_transformers import SentenceTransformer
# 准备测试词汇
words = [
"king", "queen", "prince", "princess",
"man", "woman", "boy", "girl",
"cat", "dog", "puppy", "kitten",
"car", "bus", "train", "plane"
]
# 加载预训练模型
model = SentenceTransformer('all-MiniLM-L6-v2')
# 获取嵌入向量
embeddings = model.encode(words)
# 使用t-SNE降维
tsne = TSNE(n_components=2, random_state=42)
vis_data = tsne.fit_transform(embeddings)
# 可视化
plt.figure(figsize=(12, 10))
colors = ['red']*4 + ['blue']*4 + ['green']*4 + ['purple']*4
plt.scatter(vis_data[:, 0], vis_data[:, 1], c=colors, s=100, alpha=0.6)
for i, word in enumerate(words):
plt.annotate(word, xy=(vis_data[i, 0], vis_data[i, 1]),
xytext=(5, 2), textcoords='offset points',
ha='right', va='bottom', fontsize=12)
plt.title("Word Embeddings Visualization", fontsize=16)
plt.xlabel("Dimension 1")
plt.ylabel("Dimension 2")
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
4.3 构建语义搜索引擎
python复制from sentence_transformers import SentenceTransformer, util
# 准备语料
corpus = [
"A man is eating food.",
"A man is eating a piece of bread.",
"The girl is carrying a baby.",
"A man is riding a horse.",
"A woman is playing violin."
]
query = "A man is eating pasta."
# 加载模型
model = SentenceTransformer('all-MiniLM-L6-v2')
# 编码
corpus_embeddings = model.encode(corpus, convert_to_tensor=True)
query_embedding = model.encode(query, convert_to_tensor=True)
# 计算相似度
scores = util.cos_sim(query_embedding, corpus_embeddings)[0]
# 排序结果
results = [(scores[i].item(), corpus[i]) for i in range(len(corpus))]
results.sort(key=lambda x: x[0], reverse=True)
# 输出
print(f"Query: {query}\n")
for score, text in results:
print(f"Score: {score:.4f} | {text}")
5. 工程实践建议
5.1 分词器选择指南
- 通用文本:使用模型对应的原生分词器
- 中文处理:优先选择专门优化中文的模型(如Qwen、DeepSeek)
- 代码处理:确保分词器对空格和缩进敏感
5.2 处理长文本的策略
- 截断(Truncation):直接丢弃超出部分
- 滑动窗口(Sliding Window):将文本分块处理
- 检索增强生成(RAG):先检索相关片段再处理
5.3 嵌入模型选择
- OpenAI text-embedding-3-small:性价比高,通用场景
- GTE-Qwen/BGE-M3:中文和多语言效果优秀
- Matryoshka Embeddings:可灵活截断,节省存储
6. 技术发展趋势
- 词表扩大:现代模型词表普遍增至100k-150k,提高压缩率
- 数字处理优化:为数字分配独立token,增强数学能力
- 多语言支持:原生支持更多语言,减少分词偏差
- 动态分词:根据上下文调整分词策略
分词和嵌入技术仍在快速发展,理解这些基础原理对于有效使用和优化LLM至关重要。在实际应用中,需要根据具体任务和语言特点选择合适的分词策略和嵌入方法。
