引言:数据质量决定大模型天花板
在大语言模型(LLM)领域,我们往往将目光聚焦在模型架构、参数规模以及精调技巧上。然而,DeepSeekMath 却用实践证明,数据收集策略,尤其是在特定领域(如数学)的数据收集,对于模型性能至关重要。DeepSeekMath-Base 7B 在精心构建的数学语料库上训练后,其在竞赛级别的数学基准测试中的表现甚至超越了参数规模更大的模型。这突显了高质量语料库的重要性,单纯依靠扩大模型参数并非万能钥匙。因此,深入理解和优化数据管道的构建,成为了提升大模型能力的关键一环。本文将深入剖析 DeepSeekMath 的数据收集策略,揭示其构建高质量数学语料库的奥秘,并探讨如何将其经验应用于其他领域。
1. 数据收集的重要性:数学语料库的特殊性
与通用网页相比,互联网上数学相关的内容相对稀缺。更重要的是,数学页面通常包含复杂的公式、LaTeX 代码片段以及领域专业术语,这些内容难以通过简单的关键词过滤(例如,“页面是否包含 ‘integral’ 一词?”)来准确识别。因此,构建一个干净、丰富的数学语料库需要精心设计的数据收集流程。这个流程需要具备以下关键步骤:
- 初始种子页面的构建: 提供一组高质量的初始数学页面,作为分类器学习数学内容的基础。例如,可以选取知名数学网站的首页或者权威数学教材的在线版本。
- 迭代扩展: 持续发现新的数学页面,并不断更新分类器,以提高召回率。这个过程类似于滚雪球,通过不断学习新的模式,逐步扩大数学语料库的规模。
- 领域级推理: 识别整个网站或子路径(例如,mathoverflow.net/questions)中包含大量数学内容的部分。这种方法能够更有效地找到隐藏在网站深处的数学资源。
- 去重和污染过滤: 避免重复抓取相同的内容,并防止将测试集中的问题(例如,GSM8K 问题)意外地包含在训练数据中。这一步至关重要,可以避免模型“作弊”,确保评估结果的真实性。
- Token 预算管理: 由于数学页面通常包含大量的符号、证明和代码片段,因此需要精确控制每个页面抓取的 token 数量,以确保最终的语料库规模符合预算(例如,100B+ token)。
DeepSeekMath 凭借其多阶段、多迭代的数据管道,成功从 3550 万个网页中收集了 1200 亿个数学相关的 token,并严格过滤了其中的非数学内容和测试集问题。
2. DeepSeekMath的数据管道:解构核心步骤
DeepSeekMath 论文中的“数据收集和去污染”部分详细描述了其数据管道的构建过程。下面我们将重点介绍几个关键步骤,为读者提供可借鉴的实践经验:
-
初始种子 (OpenWebMath): DeepSeekMath 首先利用 OpenWebMath(一个精选的高质量数学文本集合,约 136 亿 token)作为初始种子。他们从中随机抽取 50 万个数学页面作为正例。
-
负例选择: 为了训练分类器区分数学页面和非数学页面,DeepSeekMath 从未经过滤的 Common Crawl 数据集中随机抽取 50 万个页面作为负例。
-
FastText 分类器训练: 将上述 100 万个样本(50 万个数学页面 + 50 万个非数学页面)用于训练一个 fastText 分类器,其超参数设置为:
dim=256, lr=0.1, wordNgrams=3, minCount=3, epoch=3
。FastText 以其训练速度快、效果好而著称,非常适合处理大规模文本分类任务。 -
迭代召回数学页面(4 轮): 在种子数据上训练好分类器后,DeepSeekMath 将其应用于一个经过 URL 去重的包含 400 亿个 HTML 页面的 Common Crawl 数据集。任何得分高于特定阈值的页面都被暂时标记为“数学”页面。例如,设定一个阈值为 0.8,只有当 fastText 分类器认为一个页面有 80% 以上的概率是数学页面时,才将其标记为“数学”页面。
-
排序和筛选: 然后,按照分类器得分对页面进行排序,并仅保留得分最高的页面(例如,第一轮中,保留总计 400 亿 token 的页面)。
-
基于领域的扩展: 他们将 Common Crawl 页面按域名分组(例如,mathoverflow.net 中的所有页面),并检查先前迭代中已经“收集”的页面比例。如果一个域名的页面中有超过 10% 被标记为数学页面,则该域名被标记为数学相关。
-
人工标注: 对标记为数学相关的域名,人工标注真正包含数学内容的特定 URL 路径(例如,/questions)。这个步骤非常关键,因为它可以提高分类器的准确率,避免将非数学内容误判为数学内容。
-
添加新种子: 将上述路径下 Common Crawl 中未被选择的 URL 添加到种子集中。这为重新训练分类器提供了更丰富的正例。
-
迭代循环: 将这个过程重复四次。到第四轮结束时,几乎 98% 的新数学页面已经被找到,因此停止迭代。最终得到了包含 3550 万个数学页面,总计 1200 亿 token 的语料库。
-
去重 (URL 级别 & MD5 Sketching): 在分类之前,先进行基于 URL 的去重,以消除简单的重定向或镜像页面。在获取 HTML 后,通过 MD5 对每个页面文本的前 3000 个字符进行近似去重检查。如果两个页面具有相同的 3000 字符 MD5 哈希值,则删除其中一个。
-
HTML → 纯文本: 一旦获取了页面(WARC 段):删除所有标签(正则表达式
<[^>]+>
→ 空格)并折叠空白。这将生成一个粗略的“纯文本”版本,用于输入到分类器。 -
基准测试污染过滤: 删除任何包含出现在 GSM8K、MATH、CMATH 等测试集中的 10-gram 子字符串的页面。对于较短的 n-gram(≥ 3),进行精确匹配。这确保了预训练数据不包含测试集问题。
-
Token 预算选择: 每个候选页面都会产生一个估计的 token 计数(通过其 tokenizer)。他们按分类器置信度对页面进行排序,并不断添加页面,直到达到 1200 亿 token 的预算(当超过预算时停止)。这种“贪婪置信度”方法确保了最高质量的页面首先填充预算,然后再包含置信度较低的页面。
3. 代码实践:构建简易数学 Scraper
文章提供了一段 Python 代码,实现了 DeepSeekMath 数据收集管道的简化版本,使用了 FastText 进行分类。这段代码可以帮助读者了解如何训练 FastText 分类器、抓取网页并根据分类器的预测进行过滤。代码包括以下步骤:
- 安装必要的包: 使用
pip install
命令安装datasets
,cdx-toolkit
,warcio
,fasttext
,tqdm
,tiktoken
等库。 - 加载数据集: 使用 Hugging Face
datasets
库加载一个数学分类数据集(例如kenhktsui/math-classifiers-data
)。 - 准备 FastText 格式的训练和验证文件: 将数据集转换为 FastText 能够识别的格式,每个样本包含一个标签(指示是否为数学内容)和一个文本段。
- 训练 FastText 模型: 使用
fasttext.train_supervised
函数训练模型,并设置与 DeepSeekMath 相同的超参数。 - 评估模型: 使用训练集和验证集评估模型的性能,并测试模型对一些例子的预测结果。
- 构建小型 CDX 索引: 使用
cdx-toolkit
从 Common Crawl 中获取 1000 个 HTML 页面,并构建一个索引文件。 - 抓取 WARC 段,去重,提取文本,使用 FastText 进行分类: 遍历 CDX 索引,抓取对应的 WARC 段,进行 URL 和 MD5 去重,提取 HTML 文本,并使用训练好的 FastText 模型进行分类。
- 统计 token 数量,选择页面直到达到 token 预算: 使用
tiktoken
库统计每个页面的 token 数量,并按照分类器得分排序,选择页面直到达到预定的 token 预算。 - 将选定的页面写入 JSONL 文件: 将最终选择的页面保存到 JSONL 文件中,方便后续使用。
4. 全面升级:打造大规模数学语料库
如果要将语料库规模从 100 万 token 扩展到 1200 亿 token,甚至达到 DeepSeekMath 的水平,需要进行以下改进:
-
预先进行 URL 去重: 维护一个
seen_urls
集合,在请求 WARC 之前跳过任何已存在的 URL。这可以避免重复抓取相同的页面,节省时间和资源。 -
使用 HTML 解析器: 使用 BeautifulSoup 库删除
<script>
、<style>
、<nav>
、<footer>
标签,然后提取<article>
标签或文本长度最长的<div>
标签。这可以更有效地提取页面中的主要内容,减少噪音。 -
基准测试 N-gram 过滤: 构建一个包含 GSM8K/MATH/CMATH 中所有 10-gram 的 Aho-Corasick 自动机。删除任何匹配这些内容的页面。这可以更有效地防止测试集污染。
-
领域特征和手动标注: 跟踪
(domain_name → (#pages_seen, #pages_labelled_math))
。在每次传递后,标记 >10% 的页面被分类为数学的域名。手动标注这些域名的 URL 模式,并将它们添加到下一轮的种子中。这可以更准确地识别数学相关的网站和子路径。 -
迭代分类器重训练: 在每次传递后,将“保留”的页面分成高置信度正例、低置信度候选和负例。标记一个随机的低置信度页面子集,在扩展的种子上重新训练 fastText,然后重新运行。重复直到收益递减。这可以不断提高分类器的准确率。
-
Token 预算收集(两层): 首先估计
approx_tokens = len(text.split())
,以便在接近预算时跳过大型页面。然后仅对边界候选计算精确的len(tiktoken.encode(text))
。这可以更有效地管理 token 预算,避免浪费 token。 -
分片和输出: 在写入
pass1-math.jsonl
时,按hash(url) % N
分成 128 或 256 个分片。还要构建一个index_of_shards.csv
CSV 索引,将每个 URL 映射到其分片和字节偏移量。这可以更方便地存储和管理大规模语料库。
5. 结论:数据为王,高质量语料库是关键
构建一个 1200 亿 token 的高质量数学语料库并非易事。您必须:
- 从一个好的种子开始: 使用 OpenWebMath 或类似精选的数据集。
- 训练一个强大的分类器: 如果速度是首要考虑因素,FastText 是一个不错的选择,确保正例和负例的多样性。
- 迭代: 召回、扩展域、重新训练并再次召回 – 在每次传递时清理受污染的页面。
- 无情地去重: 在 URL 级别和通过近似重复哈希执行去重。
- 预算 token: 目标是 1200 亿个 token,而不是 10 万亿个。优先考虑最值得信赖的页面。
- 对最终输出进行分片: 确保您的预训练管道可以有效地扩展。
DeepSeekMath 证明,一个 70 亿参数的模型解决竞赛级别 MATH(51.7% top-1 准确率)所需的只是好的数据——而不是像 Minerva 那样需要 5400 亿个参数。作为开源社区,我们可以通过为其他领域精心构建特定领域的语料库来复制这种成功。
希望本文能够帮助读者认识到“数据收集”是一个重要的研究方向,也许是每个高性能 LLM 背后的无名英雄。通过构建高质量的数学语料库,我们可以更好地训练数学大模型,并在数学领域取得更大的突破。理解并优化数据管道的构建流程,将成为未来大模型研究的重要趋势。