大模型预训练数据通常需要经过哪些清洗、过滤和去重步骤?
大模型预训练数据的清洗、过滤和去重,是决定模型性能和训练效率的核心环节。这通常不是一个简单的线性流程,而是一个多层级、多工具组合的工程系统。
数据清洗:从原始网页到可用文本
目标是把混乱的原始数据(如Common Crawl的WARC文件)转化为干净、连贯的自然语言文本。
HTML解析与正文提取
工具:
trafilatura、jusText、readability-lxml、selectolax。操作:剥离HTML标签、CSS、JavaScript,保留正文文本。需要处理嵌套结构,识别并去掉导航栏、广告、页脚等“锅炉板”内容。
特殊处理:将代码块、表格、数学公式用特殊标记保留或转换为文本,而非简单删除。
文本规范化
Unicode归一化:统一用
NFKC等标准处理全角/半角、特殊空格(如\xa0)。控制字符与乱码清理:移除零宽字符、无效字节序列、私人使用区(PUA)字符。保留关键的换行符和标点,但删除过多连续空行。
句子与段落切分:修复被错误截断的行内换行,重构连贯段落。
语言识别与筛选
工具:
fastText、langdetect、cld3或pycld2。操作:以段落甚至文档级判断主语言。通常设定置信度阈值(如>0.7),丢弃多语言混杂严重的文档。对于多语模型,需要分语种筛选后再按比例混合。
启发式与规则过滤
在语义质量过滤前,先用低成本规则剔除明显的低质数据。
文档级统计特征过滤
长度截断:丢弃字符数极少(<100)或过长(>100,000)的文档。
词汇与符号特征:
平均词长、句子长度的分布异常。
大写词占比过高(可能为广告或垃圾)。
特殊符号(如“•”、“|”)占比过高。
停用词占比过低(暗示非自然语言,如日志、词表)。
重复密集度:同一文档内,词/ngram的重复比率(如每行重复的日志)。
内容黑名单与敏感词过滤
维护一套脏词、攻击性语言、涉政敏感词列表,用于排除文档或标注风险。
使用URL、域名的黑名单,筛除已知的垃圾站、色情网站等。
“困惑度”轻量过滤
用一个已经训练好的小语言模型(如KenLM 5-gram)计算文档的轻量困惑度(Perplexity),丢弃极高困惑度的乱码文本。这一步介于规则和模型过滤之间。
基于模型的深度质量过滤
这是现代高质量预训练数据的核心,利用训好的分类器或更复杂指标筛选高质量文本。
文本质量分类器
训练数据:通常人工标注一批“wiki-like”高质量正例,以及随机、混有垃圾的负例。
常用方法:在低成本的n-gram特征上训练线性分类器(如CCNet的做法),或用轻量级Transformer(如基于DistilBERT)做文档级评分。
输出:每个文档的质量得分,只保留得分大于预设阈值的部分。例如,Falcon利用分类器对RefinedWeb进行多层过滤。
教育价值或毒性过滤
教育价值过滤:类似Gopher的做法,利用分类器评估内容是否富有知识性、逻辑性,筛除低信息量的闲聊或无意义枚举。
毒性/有害内容过滤:用预训练的毒性分类器(如Detoxify)或OpenAI的Moderation API风格模型,剔除高度有毒、仇恨言论。
综合信号融合
结合链接来源的PageRank权重、网站权威性等外部信号,提升高质量源(如百科、学术库)的权重,抑制论坛水贴。
去重:精确与近似结合的多阶段漏斗
去重是防止训练数据记忆、污染测试集、浪费计算的关键。通常分三到四层。
第一层:URL/来源级去重
对同一URL,只保留最新或首次抓取的版本。
基于URL规范化后的MD5等指纹,快速剔除完全重复的页面。
第二层:文档级精确去重
方法:对文档全文做哈希(如SHA-256),或对文档内所有行排序后哈希。
效果:移除完全一样的文档(数据集中常见镜像拷贝)。
第三层:模糊去重(近似重复检测)
核心算法:MinHash + LSH(局部敏感哈希)。
操作流程:
将文档切分为n-gram(如5-gram或13-gram)的集合。
计算该集合的MinHash签名(如128个哈希值)。
用LSH进行桶分,将签名相似的文档分入同一桶。
在桶内比较Jaccard相似度,超过阈值(如0.8)的文档视为近似重复,保留最长或最新的那个。
工具:
datasketch,text-dedup,或自建Spark/Marquez流水线。规模:需要在文档簇内进行,常跨文档比较(inter-document dedup),如Falcon/RefinedWeb就是大规模应用MinHash去除同源转载、轻微修改的文章。
第四层:段落/句子级去重(可选但关键)
目的:去除文档内部或跨文档中高频重复的模板化语句(如法律条文、网站版权声明、代码头注释)。
方法:对每个句子(或固定长度片段)哈希,若某片段在全量数据中出现次数超过阈值(如10次),则将该片段标记或移除。或者使用后缀数组找出所有重复子串并替换。
注意:避免移除代码或诗歌中的正常重复结构,需谨慎设定阈值。
去污染与隐私脱敏
基准数据集去污染
查找并移除与任何已知评测基准(如MMLU、HellaSwag、HumanEval等)高度相似的文本。
采用n-gram重叠搜索(如13-gram),一旦发现长串匹配就从训练集中剔除该文档,防止数据泄露,保证评测公正。
个人身份信息脱敏
PII识别:利用正则表达式和命名实体识别,检测邮箱、身份证号、电话号码、IP地址等。
处理策略:直接移除文档,或将敏感实体替换为特殊占位符(如
[EMAIL]、[PHONE])。需要平衡隐私保护与文本连贯性。
什么是数据污染?如何检查训练数据是否包含评测集?
数据污染,简单说,就是训练集中混入了本应在测试阶段才使用的评估基准数据。这是预训练数据清洗中一项专门的去污染工作,因为它会直接破坏模型评估的公正性。
什么是数据污染
它主要有两种形式:
直接重叠:训练文本直接包含评测集的题目、选项和答案。例如,网上有人发布了“MMLU题库及解析”,模型训练时“背”下了答案。
间接重叠:训练数据包含评测集的源文本,但可能没有直接的问题。比如,训练集包含了小说原文,而评测集是基于该小说的阅读理解题。这会让模型因“读过”原文而获得不公平的上下文优势。
核心危害是导致基准性能虚高,我们无法判断模型是真理解了,还是单纯记性好。这会阻碍技术迭代和模型间的公平对比。
如何检查训练数据是否包含评测集
检查的核心就是在大海(训练集)里捞针(评测集中的特定文本片段)。这是一个计算密集型任务,常用以下由严到松的方法组合:
1. 核心方法:n-gram 重叠检测
这是目前最主流、最被认可的方法,也是 GPT-3、LLaMA、PaLM 等模型论文中的标准做法。
原理:如果训练数据和评测集中的某个样本,连续重合了足够长的一段文本,就可以判定为污染。
操作流程:
构建“针”的索引:从每个评测样本(如问题和每个选项拼接的文本)中,提取所有连续的 n-gram(通常 n=13,即连续 13 个词,这个长度足以唯一标识一段文本)。将这些 n-gram 存入一个高效检索结构中。
在“大海”中搜索:将整个训练语料也切分成 13-gram,逐一检查它们是否出现在上一步构建的索引中。
判定污染:一旦发现匹配,就扩展匹配长度,获得最长公共子序列。如果训练文档与评测样本的最长公共子序列(不一定是 13-gram 的整倍数)占评测样本本身的长度比例超过预设阈值(如 50%),则标记该训练文档为“污染文档”。
例子:一篇训练文章里恰好有“光合作用的原料是水和二氧化碳,产物是有机物和氧气”这句话,它和一个生物评测题目的文本有超过80%的连续重合,就会触发污染标记。
为什么是 13-gram?这是一个经验值,能在召回率(避免遗漏)和精确率(避免误伤)之间取得良好平衡。太短(如 5-gram)会有大量巧合匹配,太长(如 20-gram)则可能漏掉被轻微编辑过的污染。
2. 补充方法:模糊匹配与语义检测
上述精确匹配法可以发现直接复制,但无法应对他人刻意“改写”后的数据(例如同义替换、翻译回译、总结摘要等)。为此,需要更高级的补充手段:
MinHash + LSH(局部敏感哈希):这正是前文提到的模糊去重技术在去污染场景下的应用。它对改写、小幅增删有很强的鲁棒性。将训练文档和评测样本都转换成 MinHash 签名,在桶内比对 Jaccard 相似度,就能发现“撞衫”的数据。
基于嵌入向量的语义检索:用文本嵌入模型(如 BGE、E5)将所有评测样本编码成向量。然后,将可疑的高风险训练文档段也编码,通过向量相似度搜索找出语义高度相近的对。这能发现翻译成其他语言又翻回来的污染。
倒排索引与模型困惑度:针对常识、数学等评测,也可直接搜索关键词,然后让一个小语言模型计算该段落与评测题的困惑度。如果模型对这段训练文本接上评测问题的语言感到极其“不意外”(低困惑度),说明很可能训练时已见过。
工程实现与处理
在实际项目中,这个检查过程通常是:
分离评测集:将所有用于评估的基准数据(MMLU, HellaSwag, HumanEval 等)统一处理成(id, 文本, 源)的格式。
建立全量索引:在 Spark 或 Ray 集群上,对经过初步清洗的训练集建立 13-gram 的倒排索引或后缀数组。
批量碰撞检测:用每个评测样本的所有 13-gram,去索引里快速检索碰撞的文档 ID。
后验过滤与移除:对碰撞的文档进行精确比对,根据最长公共子序列长度/比例阈值,最终确定污染文档列表。
执行移除:将这些文档从最终训练集中完全剔除,而不是打标签。
一个Token经过Decoder-only模型得到下一个Token概率分布,需要经过哪些主要计算步骤?
当一个 Token 进入 Decoder-only 模型(如 GPT 系列),最终得到下一个 Token 的概率分布,需要经历以下几个核心计算步骤。这个过程本质就是模型的一次前向传播。
输入表示:从 Token 到向量
Token 嵌入输入 Token 的 ID 通过嵌入矩阵 We映射为一个稠密向量 h0(维度为 d_model)。 例如,词汇表大小 V,模型维度 d,嵌入矩阵就是 V×d的查找表。
位置编码由于自注意力自身不包含顺序信息,需要给 h0加上一个位置编码向量。 GPT 类模型通常使用可学习的位置嵌入,直接根据当前 Token 的绝对位置查表得到,然后与 Token 嵌入相加:
堆叠的 Decoder 层处理
这是模型的主体,由多个结构相同但参数独立的层堆叠而成(如 GPT-3 有 96 层)。每层依次执行:
步骤 A:带因果掩码的多头自注意力
生成 Q、K、V:输入 hin通过三个不同的线性层,产生 Query、Key、Value 矩阵。
计算注意力分数:
得到一个注意力分数矩阵。
应用因果掩码:这是 Decoder 的核心。使用一个上三角矩阵(多为 -∞)遮盖未来位置,确保当前 Token 只能看到自身及之前的 Token,防止信息泄露。
计算注意力权重:对遮盖后的分数做 Softmax,得到归一化的注意力权重。
加权求和:权重矩阵乘以 V,得到注意力输出。
多头拼接与线性投影:将多个头的输出拼接,通过一个线性层 WO映射回原维度,得到自注意力的最终输出 attn_out。
步骤 B:残差连接与层归一化 (Add & Norm)将 attn_out和该层的输入 hin相加(残差连接),再进行层归一化(Layer Normalization,现在多用 Pre-LN,即归一化在子层之前;但原理上计算流程仍是残差相加后接归一化,或先将输入归一化再进子层然后残差相加。主流实现如 GPT-2 使用 Post-LN,但 GPT-3 及以后很多用 Pre-LN。具体顺序不影响抽象步骤)。
步骤 C:前馈神经网络 (FFN)归一化后的结果通过一个全连接网络,通常由两个线性变换和一个激活函数组成(如 ReLU、GeLU 或 SwiGLU 变体):
这个网络对每个位置独立计算,扩展并压缩维度,增强非线性表达。
步骤 D:再次残差连接与层归一化将 FFN 的输出与输入它的向量相加(残差连接),再做层归一化。 此时得到该层的最终输出,并作为下一层的输入。
输出概率分布
经过所有 Decoder 层后,得到最终的隐藏状态向量 hfinal(仅取当前 Token 位置对应的向量)。
线性层 (LM Head)将 hfinal乘以一个线性变换矩阵 Wlm(维度为 d×V,通常与 Token 嵌入矩阵共享权重),得到每个词的原始分数:
(或加上偏置)。
Softmax 函数将 logits 转换成概率分布:
此时,每个位置的值代表词汇表中对应 Token 成为下一个 Token 的概率。
MHA、MQA和GQA在KV头数量、效果、显存占用及推理速度上有什么区别?
在 Decoder-only 模型中,多头注意力(MHA)、多查询注意力(MQA)和分组查询注意力(GQA)的核心区别在于KV 头的共享方式。这直接影响模型效果、推理显存和速度。以总头数 H,KV 头数 Hkv为例,三者的对比如下:
| 特性 | MHA (标准多头注意力) | MQA (多查询注意力) | GQA (分组查询注意力) |
|---|---|---|---|
| KV 头数量 | Hkv=H(每个 Q 头对应独立的 K、V 头) | Hkv=1(所有 Q 头共享同一组 K、V) | Hkv=G,其中 1<G<H(Q 头分成 G组,组内共享 K、V) |
| 模型效果 | 最好表达能力最强,基准最高。 | 略有下降因 KV 表示受限,可能轻微影响生成质量和训练收敛。 | 接近 MHA通过分组共享,平衡了表达力与效率,实际效果几乎无损。 |
| 推理显存占用 (KV Cache) | 最大需缓存所有 H个头的 K、V,大小为 2×L×H×dk。 | 最小仅缓存 1 组 K、V,显存为 MHA 的 1/H。 | 居中缓存 G组,显存为 MHA 的 G/H倍。 例如 H=32,G=4,则节省约 8 倍显存。 |
| 推理速度 | 最慢KV Cache 庞大,显存带宽成为瓶颈,数据搬运开销大。 | 最快极小的 KV Cache 大幅减少了显存读写,解码速度显著提升。 | 居中速度介于两者之间,通过减少 KV Cache 大小获得明显加速。 |
关键细节说明
为什么影响显存和速度?自回归生成时,每个 token 都要将过去所有 token 的 K、V 矩阵缓存在显存中(KV Cache),避免重复计算。KV 头数量直接决定了这个缓存的大小。缓存越小,推理时显存占用越低,从显存读取数据的速度瓶颈也越小,推理就越快。
为什么 GQA 成为主流?MQA 将效率推向极致,但效果损失可能难以接受。GQA 则在两者间找到了平衡。LLaMA 2、LLaMA 3、Mistral 等主流模型均采用 GQA。通常设置 G=8或 G=4,这样几乎可以保持与 MHA 相同的质量,同时获得数倍的推理效率和显存节省。
参数量与训练MQA 和 GQA 减少了 K、V 投影矩阵的参数量,训练速度可能稍快,但主要增益仍在推理阶段。
RoPE相比绝对位置编码有哪些优势?外推到更长上下文时可能出现什么问题?
RoPE(旋转位置编码)在处理长序列和捕捉相对关系上,确实比绝对位置编码有显著优势,但在外推到更长上下文时,也并非完美无缺。
相比绝对位置编码的优势
绝对位置编码(如可学习的嵌入或固定的正弦编码)通常是在词嵌入上相加一个位置向量,而RoPE的核心创新是通过旋转矩阵来实现位置信息的注入。这带来了几个关键优势:
内在地捕捉相对位置这是RoPE最本质的设计优势。经过RoPE编码后,两个token的Query和Key向量做内积时,结果天然只依赖于它们之间的相对距离,而不依赖于各自的绝对位置。这让模型能更直接、高效地学习“两个词距离多远”这样的关系,而非从绝对坐标中间接推导。
更好的序列长度外推能力绝对位置编码遇到训练时没见过的长度,会因找不到对应的位置向量而性能崩溃。RoPE则不同,它通过旋转角度来编码位置,而角度可以连续变化。因此,即使测试序列长于训练序列,模型也能计算出相应的旋转矩阵,这为外推提供了可能,性能下降也相对平缓。
具备远程衰减特性RoPE天然带有一个符合语言直觉的属性:随着两个token相对距离的增加,它们注意力分数的上限会逐渐降低。这意味着模型会自然地更关注邻近的上下文,有助于聚焦局部信息。
参数效率与灵活性RoPE是一种固定的数学变换,不引入任何可学习参数。这避免了为最大序列长度预留嵌入空间的问题,让模型结构更简洁灵活。
外推到更长上下文时的问题
尽管RoPE具备外推基础,但直接将模型应用到远超训练长度的文本(例如从2K外推到32K),效果通常还是会显著下降,这主要源于模型在推理时遇到了训练阶段从未见过的“旋转角度”。
具体表现为以下问题:
高频维度的“角度混叠”这是最关键的问题。RoPE会在不同维度上设置不同频率,低维度(高频)旋转很快,即使微小的位置变化也会带来大的角度变化。当位置ID变得非常大时,这些高频维度的旋转角度会远超训练时的分布,出现周期性重复。模型会因此将不同的远距离位置混淆为相同的“位置指纹”,导致注意力混乱。
注意力分数的“分布失真”外推时,由于旋转角度异常,Query和Key的内积值可能进入一个全新的范围,导致Softmax后的注意力权重分布异常。模型可能会突然对某些无关的token分配极高的注意力,或让注意力分布变得极度平坦,无法有效提取信息,直接表现为困惑度(PPL)急剧升高。
低频维度的“周期未完成”负责长程依赖的低频维度,其旋转周期非常长。当外推长度远小于其完整周期时,模型就没法见到一个完整的位置周期变化。这意味着模型在处理长程依赖时,缺少了足够的训练信号,难以准确利用低频维度的位置信息。
为了解决这些问题,研究者们提出了位置插值(PI)、NTK-aware RoPE和YaRN等方法。它们的核心思路都是通过对旋转频率或位置索引进行缩放,把长文本的位置压缩回模型训练时熟悉的范围内,从而在不重新预训练的情况下,显著扩展模型的上下文窗口。
推理阶段KV Cache缓存了什么?其显存占用与哪些因素有关?
推理阶段的KV Cache,本质上是用空间换时间的经典做法,它缓存的是每一层中,历史Token在注意力计算时生成的Key和Value向量。
KV Cache到底缓存了什么
可以把它理解为模型对历史上下文的“压缩记忆”。具体来说:
它是一组堆叠的矩阵:假设模型有
L层,每层有多头注意力。推理时,每层都需要重新计算注意力,KV Cache就为每一层、每个注意力头都维护了独立的K和V缓存。缓存的是计算结果而非原始输入:存储的是原始嵌入序列
[x_1, x_2, ..., x_t]分别乘以本层的W_K和W_V权重矩阵后,得到的Key和Value向量。模型不会缓存Query,因为Query只来自当前最新的Token。它是逐步填充的:初始化时为空。生成第一个Token时,整个输入序列的K、V被计算并存入缓存。之后每生成一个新Token,只需计算这个新Token的K、V,并将其追加到缓存尾部。这样就不用对过去所有Token做重复的矩阵乘法了。
显存占用的决定性公式
KV Cache的显存占用可以通过这个公式精确计算:
显存占用 = 2 × 层数 × 缓存序列长度 × KV头数 × 每个头的维度 × 精度字节数
其中每个因素都直接影响最终大小:
2(常数):代表需要同时缓存Key和Value两组向量。
层数:更深(参数大)的模型,缓存层数也多,占用线性增加。例如34层的模型就比12层的模型占用更多。
缓存序列长度:这是最关键、最动态的因素。它随时间线性增长。对话轮次越多、输出越长,这个值就越大,显存占用也线性增长。它是推理后期显存紧张的主要原因。
KV头数与每头维度:这取决于注意力架构(MHA、MQA、GQA)。KV头数越少,缓存越小。例如,在总头数相同的情况下,GQA的KV缓存就比MHA小得多。每头维度
d_k是模型隐藏层维度除以总头数。精度字节数:FP16占2字节,量化到INT8或INT4则只占1或0.5字节。量化能直接且成倍地减少显存占用。