【爆款长文】从文本到向量:文本处理与大模型训练攻略

#代码星辉·七月创作之星挑战赛#

“你以为大模型只会背书?其实它们的‘读书方法’比你还讲究!”

大家好,我是你们的AI技术侦探。今天我们要聊的,是大模型(LLM)最基础、最关键、最容易被忽视、但又最容易踩坑的环节——文本数据的预处理。别急着划走,这一关没搞明白,你的模型再大也只能是个“文盲”!

本篇文章将带你从“文本”到“向量”,从“分词”到“嵌入”,从“滑窗采样”到“批量加载”,一网打尽LLM数据预处理的核心思想。全程少代码,多思路,风趣幽默,绝不枯燥。准备好了吗?让我们开卷!


一、文本预处理的终极目标

先抛个灵魂拷问:为什么LLM不能直接读文本?

因为计算机只认识数字,文本对它来说就是一堆乱码。我们要做的,就是把“人话”变成“机器话”——也就是把文本转成一串数字(token IDs),再进一步变成向量(embeddings),让模型能“理解”并“记忆”这些内容。

整个流程可以拆解为:

  1. 分词(Tokenization):把文本切成小块(token),比如单词、标点、子词等。

  2. 映射(Mapping):每个token分配一个唯一编号(token ID)。

  3. 嵌入(Embedding):把token ID变成高维向量,供模型“咀嚼”。

  4. 批量采样(Batch Sampling):把长文本切成小段,方便模型训练。

  5. 位置编码(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经典做法


八、全流程串联:一图胜千言

  1. 原始文本 → 分词 → token序列

  2. token序列 → 映射 → token ID序列

  3. token ID序列 → 滑窗采样 → 输入/目标对

  4. 输入token ID → 嵌入层 → token嵌入

  5. 位置ID → 位置嵌入层 → 位置嵌入

  6. 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技术

RAG技术全解:从原理到实战的简明指南

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

许泽宇的技术分享

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值