预训练语言模型数据集工程:从GPT-1到GPT-2的语料处理与复现
简介这份PDF文档系统梳理了2018年至2022年初GPT-1、GPT-2、GPT-3、GPT-NeoX-20B、Megatron-11B、MT-NLG与Gopher等大规模预训练语言模型所使用数据集的组成情况面向自然语言处理研究人员、机器学习工程师与数据科学家帮助读者理解各模型训练数据的来源、规模与令牌数量差异并直面数据集透明度不足这一核心问题。资源包内含1个PDF文件大小约2.36MB结构上依次覆盖Wikipedia、Books、Journals、Reddit links、Common Crawl等常见数据源分析以及各模型数据集摘要、The Pile v1分组数据、MT-NLG与Gopher的专项讨论并附结论、延伸阅读与Top 50资源附录。已有112人学习关注。读者可借此获得横向对比多款主流大模型数据构成的参考框架识别Books1、Books2及Wikipedia等数据在披露与统计上的争议点为后续数据集构建、模型训练优化与透明度标准研究提供可引用的分析依据。1. 大规模预训练语言模型数据集从 GPT-1 到 GPT-2 的语料账本很多人第一次接触预训练语言模型注意力全在模型结构上Transformer 堆了多少层、注意力头怎么切、参数量多大。但真正决定模型下限的往往是那堆看不见的语料。GPT-1 用了约 7000 本书规模的 BooksCorpusGPT-2 则把 WebText 做到约 40GB、800 万篇文档这个量级变化直接改变了模型在零样本任务上的表现。换句话说预训练语言模型的数据集不是「喂进去就行」的原料而是一套需要清洗、去重、配比、采样、评估的工程系统。这篇笔记面向正在做预训练数据管线、或者准备复现小规模 GPT 训练的人把数据集从采集到可训练格式的完整链路拆开讲重点放在可复现的步骤和参数上而不是停留在「数据很重要」这种废话层面。2. 预训练语料从哪来GPT-1 与 GPT-2 的数据源拆解2.1 GPT-1 的 BooksCorpus 路线与适用边界GPT-1 的数据集核心是 BooksCorpus约 7000 本未出版书籍总量在 5GB 左右。选书籍而不是网页原因很直接书籍文本长、语法完整、段落连贯模型能学到长距离依赖。但这条路线有明显边界——领域单一偏文学和叙事对代码、数学、多语言覆盖几乎为零。如果你现在做的是垂直领域预训练比如法律或医疗直接照搬 BooksCorpus 思路去爬电子书会遇到版权和领域偏差两个问题。常见做法是用公开书籍数据集做基础语料再按领域配比混入专业文本比例一般控制在 7:3 到 8:2 之间专业文本太多会让模型通用能力下降。实际操作上BooksCorpus 类数据的清洗重点是章节切分和页眉页脚去除。下面是一个最小清洗脚本处理纯文本格式的书籍语料import re def clean_book_text(raw: str) - str: # 去除页眉页脚常见模式如纯数字行、页码标记 lines raw.split(\n) cleaned [] for line in lines: s line.strip() if not s: continue # 跳过纯页码行 if re.fullmatch(r\d{1,4}, s): continue # 跳过常见页眉关键词行 if re.search(r(Chapter|CHAPTER)\s\d, s) and len(s) 20: continue cleaned.append(s) text \n.join(cleaned) # 合并被硬换行拆断的句子 text re.sub(r([a-z,])\n([a-z]), r\1 \2, text) return text这段代码的逻辑是先按行过滤噪声再合并断句。参数上页码正则\d{1,4}覆盖 1 到 4 位数字避免误删正文中的年份断句合并只处理小写字母后接换行再小写字母的情况防止把正常段落边界也合掉。清洗完的文本建议按 0.5GB 一个分片存储方便后续并行 tokenize。2.2 GPT-2 的 WebText 思路与可复现替代方案GPT-2 的 WebText 没有直接公开完整数据但论文里给了构造逻辑从 Reddit 上抓取获得至少 3 个 karma 的外链页面最终约 800 万文档、40GB 文本。这个思路的本质是用社交信号做质量过滤karma 阈值相当于一个人工弱标注的质量分。你要复现类似规模直接爬 Reddit 不现实常见替代是用 Common Crawl 的一个子集配合质量分类器过滤。具体步骤分三步。第一步从 Common Crawl 的 WET 文件里抽取纯文本每个 dump 大约 2 到 3 亿网页。第二步用启发式规则做初筛去重、去模板、去低信息密度页面。第三步用一个小型分类器打分保留高分文档。下面是一个基于 MinHash 的去重示例from datasketch import MinHash, MinHashLSH def build_minhash(text: str, num_perm: int 128) - MinHash: m MinHash(num_permnum_perm) for token in set(text.split()): m.update(token.encode(utf8)) return m def dedup_documents(docs, threshold: float 0.8): lsh MinHashLSH(thresholdthreshold, num_perm128) keep [] for i, doc in enumerate(docs): m build_minhash(doc) if not lsh.query(m): lsh.insert(str(i), m) keep.append(doc) return keep参数说明num_perm128是精度和内存的折中再高收益递减threshold0.8表示 Jaccard 相似度超过 0.8 视为重复。这个阈值在网页去重里比较常用低于 0.7 会误删同主题不同文章高于 0.9 会漏掉改写内容。去重后一般能砍掉 30% 到 50% 的原始网页具体取决于爬取源。2.3 数据配比为什么不能只堆一种来源GPT-2 之后的大量实验都指向一个结论单一来源语料会让模型在特定任务上过拟合。比如纯网页数据训练出来的模型在需要长逻辑链的任务上表现差纯书籍数据训练的模型对实时事实和代码几乎无感。常见做法是按来源分层采样网页、书籍、代码、学术文本各占一定比例。一个可用的初始配比是网页 60%、书籍 20%、代码 10%、学术 10%。这个比例不是金科玉律但作为起点比随机混合稳定得多。调整时每次只动一个来源的比例观察验证集 loss 变化避免同时改多个变量导致无法归因。3. 从原始文本到训练样本tokenize 与打包的工程细节3.1 训练 tokenizer 的样本量与参数选择预训练语言模型的 tokenizer 通常用 BPE 或 Unigram训练样本量建议在 1GB 到 10GB 纯文本之间。样本太少会导致词表覆盖不足太多则边际收益很低。词表大小方面GPT-2 用的是 50257这个数字来自 256 个字节级 base token 加上 50000 个合并 token 再加一个结束符。你自己训练时如果语料以中文为主词表可以设到 30000 到 50000如果多语言混合建议 50000 以上。下面是一个用 Hugging Face tokenizers 训练 BPE 的最小示例from tokenizers import Tokenizer, models, trainers, pre_tokenizers tokenizer Tokenizer(models.BPE()) tokenizer.pre_tokenizer pre_tokenizers.ByteLevel(add_prefix_spaceTrue) trainer trainers.BpeTrainer( vocab_size32000, special_tokens[|endoftext|, pad, unk], min_frequency2, show_progressTrue ) tokenizer.train(files[corpus_part_*.txt], trainertrainer) tokenizer.save(tokenizer.json)逻辑说明ByteLevel预处理保证任何字符都能被编码不会出现 OOVmin_frequency2表示出现少于 2 次的字符对不合并防止词表被噪声撑大。训练完成后用tokenizer.encode检查几个典型样本看中文是否被切成合理子词英文单词是否保留完整。如果中文被切成单字说明词表偏小或语料中中文占比不够。3.2 打包成固定长度序列的两种策略tokenize 之后每条文本变成长度不一的 id 序列。训练时需要固定长度常见两种做法一是 padding 到最大长度二是拼接后按固定长度切块。预训练阶段几乎都用第二种因为 padding 会浪费大量算力在填充 token 上。具体做法是把所有 token 拼成一条长流然后按block_size切分最后一块不足的丢弃或补齐。def pack_sequences(token_ids, block_size1024): # 拼接所有 token total sum(len(ids) for ids in token_ids) buffer [] for ids in token_ids: buffer.extend(ids) # 按 block_size 切块 blocks [] for i in range(0, len(buffer) - block_size 1, block_size): blocks.append(buffer[i:i block_size]) return blocks参数上block_size取 1024 或 2048 比较常见取决于你的显存和模型位置编码上限。注意拼接时要在每条文档末尾加结束符否则模型学不到文档边界。切块后建议打乱顺序再写入二进制文件避免同一来源的块连续出现导致训练时 batch 内相关性过高。3.3 数据加载内存映射与流式读取打包后的数据通常存成 numpy memmap 或二进制文件训练时用内存映射读取避免一次性加载几十 GB 到内存。下面是一个 PyTorch Dataset 的骨架import numpy as np import torch from torch.utils.data import Dataset class PackedDataset(Dataset): def __init__(self, path, block_size1024): self.data np.memmap(path, dtypenp.uint16, moder) self.block_size block_size def __len__(self): return len(self.data) // self.block_size def __getitem__(self, idx): start idx * self.block_size chunk self.data[start:start self.block_size].astype(np.int64) x torch.from_numpy(chunk[:-1]) y torch.from_numpy(chunk[1:]) return x, y这里dtypenp.uint16是因为词表 32000 以内用 16 位足够能省一半存储。x和y错一位是标准自回归语言模型做法。注意 memmap 文件在训练前要确认没有损坏可以用np.memmap读前 100 个值检查是否在词表范围内。4. 数据质量评估怎么判断一份预训练语料能不能用4.1 困惑度与压缩率作为快速体检指标拿到一份语料先别急着训练。用一个小型参考模型算困惑度或者用 gzip 算压缩率都能快速判断文本是否过于重复或噪声过多。压缩率特别低说明文本重复度高困惑度异常高说明文本和参考模型分布差距大。具体操作随机抽 1000 条文档每条算 gzip 压缩比取中位数。正常网页文本压缩比在 2.5 到 4 之间低于 2 说明重复严重高于 5 说明文本可能太短或太碎。4.2 去重后的重复率检查去重不是一次性的。清洗、tokenize、打包之后还要再查一次重复。常见做法是用 MinHash 对打包后的块做近似去重或者直接统计 n-gram 重复率。下面是一个 5-gram 重复率统计from collections import Counter def ngram_repeat_rate(token_ids, n5): ngrams [tuple(token_ids[i:in]) for i in range(len(token_ids)-n1)] counter Counter(ngrams) total len(ngrams) repeated sum(c for c in counter.values() if c 1) return repeated / total if total else 0如果 5-gram 重复率超过 5%说明去重不彻底需要回到 MinHash 阶段调低阈值。这个指标在书籍和网页混合语料里尤其重要因为网页模板会带来大量重复片段。4.3 领域覆盖与词频分布检查最后一步是看词频分布。把语料 tokenize 后统计词频和通用词频表对比。如果某些领域词比如代码关键词、医学术语频率异常高说明配比偏了。常见做法是画一张词频对数图看长尾是否完整。长尾被截断通常意味着语料量不够或者清洗时误删了低频内容。5. 避坑与排查预训练数据管线里最容易翻车的五件事5.1 现象训练 loss 突然变成 NaN原因打包数据里混入了超出词表范围的 token id或者 memmap 文件末尾有未初始化区域。 解决在 Dataset 的__getitem__里加断言assert chunk.max() vocab_size并在打包脚本最后显式截断不足一个 block 的尾部数据。5.2 现象模型生成重复内容像卡带原因语料去重不彻底或者打包时同一文档的块连续排列导致模型学到局部重复模式。 解决打包后全局打乱块顺序并在去重阶段把 MinHash 阈值从 0.8 降到 0.7 重跑一遍。5.3 现象中文生成全是单字不成词原因tokenizer 训练时中文语料占比太低BPE 合并主要发生在英文上。 解决重新训练 tokenizer把中文语料比例提到至少 30%并把vocab_size增大到 50000。5.4 现象验证集 loss 比训练集低很多原因训练集和验证集来源不同比如训练集以网页为主验证集以书籍为主分布不一致。 解决从同一份清洗后语料里按比例切分训练和验证不要用不同来源分别做训练和验证。5.5 现象训练速度远低于预期原因数据加载是瓶颈memmap 随机读取导致磁盘 IO 过高。 解决把打包数据按块顺序预读或者转成 WebDataset 格式做流式加载减少随机寻道。6. 小规模复现时的数据采样技巧1B token 怎么选如果你不打算从头训练一个 GPT-2 规模模型而是想做 1B token 级别的小规模复现数据采样策略比数据总量更关键。我的习惯是先从各来源各抽 10GB 原始文本清洗去重后大概剩 6 到 7GBtokenize 后约 1.5B token再按配比采样到 1B。这样比直接拿 1B token 的混合语料更有控制感。具体操作上用分层采样网页、书籍、代码、学术各维护一个 token 池按目标比例从每个池里随机抽。抽完后合并打乱再切块打包。验证集从每个池里各留 1%确保验证集分布和训练集一致。下面是一个分层采样的骨架import random def stratified_sample(pools: dict, target_ratio: dict, total_tokens: int): sampled [] for source, ratio in target_ratio.items(): need int(total_tokens * ratio) pool pools[source] if len(pool) need: sampled.extend(pool) else: sampled.extend(random.sample(pool, need)) random.shuffle(sampled) return sampled参数说明target_ratio就是前面说的配比total_tokens是目标总量。注意每个池的 token 要提前 tokenize 好否则采样单位不统一。采样后建议再跑一次 5-gram 重复率检查因为不同来源的文本可能在采样后出现新的重复组合。这套流程我前后跑过几次最深的教训是数据管线的 bug 比模型代码的 bug 更难发现因为 loss 曲线不会直接告诉你数据哪里错了。我的习惯是每完成一个清洗步骤就存一份中间产物出问题时能快速回滚到上一步而不是从头重跑。希望帮到你。本文还有配套的精品资源点击获取

相关新闻

React面试进阶指南:从组件通信到Fiber并发渲染

React面试进阶指南:从组件通信到Fiber并发渲染

1. React面试到底在考什么:先想清楚面试官的出题逻辑说实话,我面过不少候选人,也被面过。站在面试官的角度看,React题目的核心从来不是"你有没有背过八股文",而是要在半小时到一个小时内,判断出你…

2026/10/14 11:58:05 阅读更多 →
基于Docker的DeepSeek Harness局域网AI Agent部署实战

基于Docker的DeepSeek Harness局域网AI Agent部署实战

最近在帮团队搭建内部知识问答和任务自动处理系统时,折腾了一套基于 Docker 的 DeepSeek Harness 部署方案。所谓 Harness,说白了就是给 DeepSeek 模型套上一层“编排缰绳”,让它可以按你的指令拆解任务、调用工具、多轮推理,最终…

2026/10/14 11:57:03 阅读更多 →
Docker部署DeepSeek Harness:局域网AI Agent平台搭建实战

Docker部署DeepSeek Harness:局域网AI Agent平台搭建实战

1. 为什么要在局域网里自建 AI Agent 平台1.1 从“调用云端 API”到“把模型搬进机房”的转变过去一年,我身边不少做企业内部工具的朋友都经历过同一个阶段:一开始大家都是直接调用云端大模型 API,写个脚本、搭个前端,跑得挺欢。但…

2026/10/14 11:57:03 阅读更多 →

最新新闻

子网划分原理与实战:从课后题1理解网络分段本质

子网划分原理与实战:从课后题1理解网络分段本质

1. 这道题不是“做作业”,而是打开网络世界的第一把钥匙“计算机网络原理 课后题1”——看到这行字,很多人第一反应是翻教材、查答案、抄个IP地址配个子网掩码就完事。但我在某高校连续带了七年《计算机网络原理》实验课,带过三百多名学生&am…

2026/10/15 23:46:00 阅读更多 →
Qt5范例开发大全:从目录索引到可运行工程的最小闭环

Qt5范例开发大全:从目录索引到可运行工程的最小闭环

简介:《Qt5范例开发大全(完整目录版)》面向具备一定C基础、希望系统掌握Qt5跨平台界面与功能开发的程序员及在校学生。全书以实例驱动,从开发环境搭建与Hello World起步,依次覆盖窗体应用、控件应用、组件应用、文件操…

2026/10/15 23:46:00 阅读更多 →
Claude Code官方插件机制深度解析:从接入规范到自定义开发实践

Claude Code官方插件机制深度解析:从接入规范到自定义开发实践

1. 从"官方插件"这个词说起:它到底解决了什么问题很多人第一次看到claude-plugins-official这个仓库名,第一反应是"官方插件合集,装就完事了"。但真去翻一遍目录结构,会发现它跟传统意义上的"插件市场&q…

2026/10/15 23:46:00 阅读更多 →
SpringBoot+Vue汽车资讯网站管理系统实战开发全流程

SpringBoot+Vue汽车资讯网站管理系统实战开发全流程

做JavaWeb项目这些年,SpringBootVue这对组合算是绕不开的搭配。前段时间刚完成一个汽车资讯网站管理系统,从需求梳理、数据库设计到前后端联调,最后打包部署,整个流程走下来收获不少。这个项目用JavaMySQLMyBatis做后端数据层&…

2026/10/15 23:46:00 阅读更多 →
AnyPS5:将DualSense手柄打造成跨平台万能输入设备

AnyPS5:将DualSense手柄打造成跨平台万能输入设备

1. 项目缘起与核心定位AnyPS5 这个名字第一次出现在我视野里的时候,我正被一堆跨平台输入设备的适配问题折腾得够呛。简单来说,AnyPS5 是一个把 PlayStation 5 的 DualSense 手柄变成“万能输入设备”的软件方案——它能让这块原本只服务于游戏主机的手柄…

2026/10/15 23:46:00 阅读更多 →
Python工具链Rust化:五个提升开发效率的新库实战指南

Python工具链Rust化:五个提升开发效率的新库实战指南

这两年 Python 生态里的话题,绕不开一个词:Rust 化。从包管理到 lint 再到数据验证,一堆新库选择直接用 Rust 重写底层,把之前 Python 工具链里让人头疼的慢、复杂、配置繁琐的问题挨个收拾了一遍。我最近在几个项目里把工具链切到…

2026/10/15 23:45:00 阅读更多 →

日新闻

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

1. 为什么选择容器化方式部署 LoRa 网络服务LoRa 物联网项目落地时,网络服务器是整个链路的核心枢纽。它负责接收网关上报的数据包、完成去重与解密、再把有效载荷分发给上层应用。早期大家习惯把网络服务器直接装在物理机上,依赖一堆系统库和特定版本的…

2026/10/15 0:00:46 阅读更多 →
共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

1. 共享推理服务里,扩缩容到底难在哪做过大模型推理服务部署的人,大概都经历过这样的场景:一个团队共用一套推理集群,A 业务跑的是 7B 的小模型,B 业务跑的是 72B 的大模型,C 业务白天流量高、晚上几乎归零…

2026/10/15 0:00:46 阅读更多 →
Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

1. 引子:一次内核恐慌引发的追查先从一个真实场景说起。某次我给一台老旧的嵌入式设备折腾系统裁减,为了把启动时间压进两秒以内,我手贱地把一串自认为“没用”的 systemd 服务给 disable 了,其中就包括systemd-remount-fs.servic…

2026/10/15 0:00:46 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/15 16:18:54 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/15 19:41:51 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/15 13:48:15 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/15 0:14:36 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/14 22:45:47 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/15 9:49:14 阅读更多 →