“你以为大模型只会背书?其实它们的‘读书方法’比你还讲究!”
大家好,我是你们的AI技术侦探。今天我们要聊的,是大模型(LLM)最基础、最关键、最容易被忽视、但又最容易踩坑的环节——文本数据的预处理。别急着划走,这一关没搞明白,你的模型再大也只能是个“文盲”!
本篇文章将带你从“文本”到“向量”,从“分词”到“嵌入”,从“滑窗采样”到“批量加载”,一网打尽LLM数据预处理的核心思想。全程少代码,多思路,风趣幽默,绝不枯燥。准备好了吗?让我们开卷!
一、文本预处理的终极目标
先抛个灵魂拷问:为什么LLM不能直接读文本?
因为计算机只认识数字,文本对它来说就是一堆乱码。我们要做的,就是把“人话”变成“机器话”——也就是把文本转成一串数字(token IDs),再进一步变成向量(embeddings),让模型能“理解”并“记忆”这些内容。
整个流程可以拆解为:
-
分词(Tokenization):把文本切成小块(token),比如单词、标点、子词等。
-
映射(Mapping):每个token分配一个唯一编号(token ID)。
-
嵌入(Embedding):把token ID变成高维向量,供模型“咀嚼”。
-
批量采样(Batch Sampling):把长文本切成小段,方便模型训练。
-
位置编码(Positional Encoding):告诉模型“谁在前谁在后”,避免“乱炖”。
下面我们逐步拆解每一步的门道。
二、分词:让文本“碎碎平安”
2.1 什么是分词?
分词,就是把一段文本拆成小块。比如:
“Hello, world. This is a test.”
分词后可能变成:
['Hello', ',', 'world', '.', 'This', 'is', 'a', 'test', '.']
为什么要分词?因为模型的“胃口”有限,一口吞下整本书会噎死。分词让模型能逐块消化,既高效又安全。
2.2 分词的“江湖流派”
-
按空格分:最简单,适合英文,不适合中文。
-
按标点分:更细致,能区分“Hello,”和“Hello”。
-
正则表达式:万能瑞士军刀,能按需拆分各种符号。
-
子词分割(BPE等):高级玩法,后面详解。
伪代码思路
# 伪代码:正则分词
tokens = regex_split(text, pattern="([,.:;?_!\"()']|--|\\s)")
tokens = [t.strip() for t in tokens if t.strip()]
2.3 分词的“坑”
-
空字符串:分完后一堆空格,记得过滤。
-
未知词:新词、错别字、外语,模型没见过咋办?加个“”兜底。
-
特殊符号:比如“--”,别被拆成两个“-”。
三、映射:给每个Token发身份证
分完词,下一步就是给每个token分配一个唯一编号(token ID)。这就是词表(Vocabulary)的作用。
3.1 构建词表
-
去重:所有出现过的token,去重后排序。
-
编号:每个token分配一个整数ID。
伪代码思路
vocab = {token: idx for idx, token in enumerate(sorted(set(tokens)))}
3.2 特殊Token的“身份证”
-
**[UNK]**:未知词
-
**[EOS]**:文本结尾
-
**[PAD]**:补齐用
-
**[BOS]**:文本开头
GPT-2只用<|endoftext|>
,极简主义,其他模型可能会用更多。
3.3 Tokenizer的“变形金刚”
一个好的Tokenizer,既能把文本转成ID(encode),也能把ID还原成文本(decode)。还要能优雅处理未知词和特殊符号。
伪代码思路
class Tokenizer:
def __init__(self, vocab):
self.str2id = vocab
self.id2str = {v: k for k, v in vocab.items()}
def encode(self, text):
tokens = split(text)
ids = [self.str2id.get(t, self.str2id["<unk>"]) for t in tokens]
return ids
def decode(self, ids):
tokens = [self.id2str[i] for i in ids]
return join(tokens)
四、BPE:让分词更“智能”
4.1 为什么要BPE?
传统分词遇到新词就懵了,比如“someunknownPlace”。BPE(Byte Pair Encoding)能把生词拆成更小的“子词”甚至字符,模型就不会“词穷”了。
比如:
“unfamiliarword” → [“un”, “familiar”, “word”]
4.2 BPE的原理
-
统计所有token对出现频率
-
频率最高的合并成新token
-
反复迭代,直到词表大小满足要求
GPT-2用的就是BPE,词表高达50257个token,能覆盖绝大多数英文、符号、甚至emoji。
4.3 BPE的优势
-
抗生词:新词也能拆开理解
-
节省空间:词表不用太大
-
灵活性高:多语言、多领域通吃
五、滑窗采样:让长文本“分批进食”
5.1 为什么要滑窗?
模型一次只能处理有限长度的文本(比如512、1024 token),长文本要切成小段。滑窗采样就是用一个“窗口”在文本上滑动,每次取一段,窗口可以重叠也可以不重叠。
5.2 滑窗采样的套路
-
窗口大小(context size):每次取多少token
-
步长(stride):每次滑动多少token
-
输入/目标对:输入是窗口内的token,目标是下一个token(即“预测下一个词”)
伪代码思路
for i in range(0, len(token_ids) - context_size, stride):
input_chunk = token_ids[i:i+context_size]
target_chunk = token_ids[i+1:i+context_size+1]
yield input_chunk, target_chunk
5.3 批量加载(Batching)
-
批量大小(batch size):一次处理多少窗口
-
打乱顺序(shuffle):防止模型“死记硬背”
-
补齐(padding):不同长度的文本补齐到同一长度
六、嵌入:让Token“长出肌肉”
6.1 什么是嵌入(Embedding)?
Token ID只是个数字,模型看不懂。嵌入层(Embedding Layer)把每个ID映射成一个高维向量(比如256维),让模型能“感知”token之间的关系。
6.2 嵌入的本质
-
查表操作:每个token ID查一行向量
-
可训练:嵌入向量会随着模型训练不断优化
-
比One-hot高效:不用存一大堆0,只存有用的信息
伪代码思路
embedding_matrix = random_matrix(vocab_size, embedding_dim)
embedding_vector = embedding_matrix[token_id]
6.3 批量嵌入
-
输入是(batch_size, context_size)的token ID矩阵
-
输出是(batch_size, context_size, embedding_dim)的嵌入张量
七、位置编码:让模型“记住顺序”
7.1 为什么要位置编码?
嵌入层只看token,不看顺序。模型要知道“谁在前谁在后”,否则“我爱你”和“你爱我”就傻傻分不清。
7.2 绝对位置编码
-
每个位置分配一个向量
-
和token嵌入相加,形成最终输入
伪代码思路
pos_embedding_matrix = random_matrix(context_size, embedding_dim)
input_embeddings = token_embeddings + pos_embeddings
7.3 其他位置编码
-
相对位置编码:更灵活,适合长文本
-
正弦位置编码:Transformer经典做法
八、全流程串联:一图胜千言
-
原始文本 → 分词 → token序列
-
token序列 → 映射 → token ID序列
-
token ID序列 → 滑窗采样 → 输入/目标对
-
输入token ID → 嵌入层 → token嵌入
-
位置ID → 位置嵌入层 → 位置嵌入
-
token嵌入 + 位置嵌入 → 输入模型
最终,模型就能“读懂”文本,预测下一个词,生成新内容!
九、总结与思考
-
分词不是越细越好,要结合实际场景和模型需求。
-
词表设计要兼顾覆盖率和效率,BPE是主流选择。
-
特殊token是模型的“安全气囊”,别忘了加。
-
滑窗采样和批量加载是大规模训练的基础设施。
-
嵌入层和位置编码是模型理解文本的“眼镜”和“指南针”。
“数据预处理不是搬砖,是炼丹。炼得好,模型飞升;炼得差,模型扑街。”
十、彩蛋:常见问题答疑
Q1:中文分词怎么办? A:中文没有空格,常用jieba、BERT分词器或BPE等子词分割。
Q2:词表太大怎么办? A:用BPE或Unigram等子词分割,控制词表规模。
Q3:遇到新词怎么办? A:BPE能拆分新词,实在不行用兜底。
Q4:如何选择窗口大小和步长? A:窗口越大,模型能看得更远,但显存压力大。步长越小,样本重叠多,训练更充分但慢。
Q5:嵌入维度怎么选? A:常见128、256、512,越大表达力越强,但计算量也大。
十一、结语
数据预处理是LLM的“开胃菜”,也是“地基工程”。别小看这一步,打好了地基,模型才能高楼平地起。希望本文能让你对LLM的数据预处理有一个全局、深刻、风趣的理解。
如果你觉得有用,欢迎点赞、转发、在评论区聊聊你踩过的“分词坑”!下期我们将揭秘Attention机制,敬请期待!
关注我,带你用最通俗的语言,掌握最硬核的AI技术