大模型的训练语料,已经悄悄进入“实体采购”阶段。最近行业里流传最广的一个做法是:AI 公司批量收购旧书,扫成电子文本之后,再把纸质书销毁。第一反应是可惜,第二反应是好奇:为什么要买纸书再烧掉?
这件事拆开看,本质是一个数据工程问题。当前互联网文本语料的质量正在下降,重复内容、机器生成内容、低质量网文混杂在一起,模型训练需要更干净、更结构化、更长上下文的文本。旧书恰好这个方向提供了一条稳定路径:内容完整、逻辑连贯、知识密度高,而且大量早期出版物已经进入公有版权领域,授权链更清晰。买旧书、扫描、销毁,并不是单纯为了“消灭实体书”,而是为了在合规前提下拿到可用的训练语料,同时避开仓储和复售带来的成本。
这篇文章从训练语料采购、扫描 OCR 流程、数据清洗与去重、版权合规、存储成本、本地 AI 团队落地建议几个角度,把这条链路说清楚。如果你正在做大模型数据建设,或者想为自己部署的模型构建专属高质量文本数据集,这篇文章值得收藏。
1. AI 公司盯上旧书:高质量文本语料为何稀缺
先明确一个背景:大模型训练离不开大量文本数据,但“有数据”和“有干净的高质量数据”是两回事。早期做法是从互联网直接爬取,后来慢慢发现这个方案有不少问题。
1.1 互联网文本的痛点
用爬虫拿网页数据,速度快,成本低,但质量波动非常大。常见的几个问题:
- 重复内容多:同一篇文章被多站点转载,正文、标题、图片说明大量重复。
- 夹杂噪声:导航、广告、评论区、推荐列表混进正文。
- 机器文本膨胀:生成式 AI 普及后,大量站点开始用模型自动生成内容,真实信息密度下降。
- 上下文不完整:网页通常以短文为主,很少有系统性的长文论述。
直接把这些文本丢给模型训练,会提高训练成本,还会让模型学到大量重复和空泛表达。行业里经常讲“数据饥渴”,更准确的描述是“干净长文本稀缺”。
1.2 为什么旧书是高质量文本来源
书籍和网页最大的区别在于结构。一本书有目录、章节、段落、注释,内容经过编辑和校对,错误率低,叙事和论证是连贯的。这种文本正好适合训练模型把长文理解和总结。对于需要处理“长上下文”的场景,书籍语料几乎是天然首选。
旧书还有一个优势是版权状态更明确。出版时间较早、作者去世超过一定年限的书籍,多数情况下已经进入公共领域,企业可以对文本进行数字化和加工。对比那些来路不明的网络爬虫数据,旧书语料的合规风险更低,审计性更强。
这也是“买旧书”这个动作出现的原因:与其在版权状态复杂的电子书库里做吃力不讨好的筛查,不如直接从旧书渠道采购,把所有权和物理控制权抓到手里。
1.3 “买书 + 扫描 + 销毁”模式的出现
整个链路并不复杂:
- 批量采购旧书。
- 对书进行整理、编号、扫描。
- 用 OCR 和清洗流程把扫描件转成高质量文本。
- 文本进入数据仓库,作为模型训练语料。
- 纸质书完成数字化任务后统一销毁。
对很多团队来说,“销毁”不是目标,而是结果。纸质书保存需要仓库、温湿度控制、防虫防潮,这是实打实的存储成本。同时,实体书如果流入二手市场,会出现“同一批内容既在训练集里,又在公开渠道流通”的不可控情况。直接销毁,管理成本最低。
从数据工程角度看,这种模式不是书被“浪费”,而是把纸质书的物理形态转换成了数字形态,并且通过销毁实体副本避免后续被二次传播。
2. 从采购到销毁:旧书语料生产的完整链路
这一节把链路再拆细一点,方便本地团队对照自己的流程。
2.1 采购与物理整理
采购环节需要考虑三件事:来源、范围、状态。
- 来源:旧书店、古籍书店、批量回收渠道、图书馆淘汰批次。
- 范围:优先收公共领域书籍、无版权争议的出版物,避免把仍在版权保护期内的新书大量混入。
- 状态:优先选页面完整、印刷清晰、没有水渍霉斑的书。扫描质量直接影响 OCR 成本。
采购完成后,每本书会分配一个唯一编号,记录书名、出版年份、出版社、ISBN 或馆藏号等元数据。这一步为后面生成训练集元信息打好基础。
2.2 扫描与图像预处理
扫描不是简单“把书拍一遍”。大批量扫描通常要做:
- 裁边与方向校正。
- 去黑边、去底色。
- 矫正页面倾斜。
- 检测空白页和重复页。
- 生成统一分辨率的 TIFF 或 PDF 文件。
图像质量直接决定 OCR 效果。扫描件分辨率过低,小字号容易识别错误;分辨率过高,存储和计算成本又上去了。一般建议在 300-600 DPI 之间做测试,以实际识别效果为准。
2.3 OCR 识别与文本抽取
OCR 是这条链路里最耗算力的环节。对于中文旧书,常见选型是 Tesseract、OCRmyPDF 或商业 OCR 服务。通用流程是:
# 以 OCRmyPDF 为例:先做图像优化,再输出带文本层的 PDF # 实际运行时需要按本机环境安装对应语言包 ocrmypdf --clean --deskew --rotate-pages \ --language chi_sim+eng \ book_scan.pdf book_ocr.pdf # 再从 PDF 抽取文本 pdftotext -layout book_ocr.pdf book_ocr.txt可以用下面这段代码做首轮判断:
import re def clean_ocr_text(text: str) -> str: # 统一换行符 text = text.replace("\r\n", "\n").replace("\r", "\n") # 去掉明显的页眉页脚噪声,具体规则按样本调整 text = re.sub(r"第\s*[0-9一二三四五六七八九十]+\s*页", "", text) # 去掉孤立的页码数字 text = re.sub(r"^\s*\d+\s*$", "", text, flags=re.MULTILINE) # 合并被换行拆散的段落 text = re.sub(r"([^\n])\n([^\n])", r"\1\2", text) return text.strip() sample_text = open("book_ocr.txt", encoding="utf-8").read() print(clean_ocr_text(sample_text)[:500])这里没有固定规则能适配所有书。每类书的版式都不一样,正确做法是先抽样 10-20 页,人工标注一批常见噪声,再写清洗规则。
2.4 文本入库与实体处置
文本抽取完成后,会和元数据一起写入数据仓库。完成归档后,纸质书进入销毁环节。销毁方式包括粉碎、化浆、焚烧,按当地环保要求执行即可。
从成本逻辑看,买旧书、扫描、做文本清洗,前期的固定成本并不低。但如果这批语料能持续用于多轮模型训练、微调、评测,边际成本是摊薄的。销毁节省的是长期仓储和二次传播管理的隐性成本。
3. 从扫描件到训练语料:OCR、清洗与结构化
“扫完就有数据”是错误的。扫描件到可训练文本之间,还有一整条清洗流水线。
3.1 版式分析与结构化
旧书版式很复杂:竖排、繁体、脚注、页眉、目录、索引。直接 OCR 后的纯文本通常是乱的。比较规范的做法是分段处理:
- 页面级分析:识别正文区、页眉页脚区、注释区。
- 段落级重组:把跨页段落拼接起来。
- 章节级拆分:按目录结构把整本书拆成多个逻辑单元。
- 元数据绑定:一本书、一个章节、一个段落都保留来源编号。
输出格式建议用 JSONL,一行一个段落,附带元信息:
{"book_id": "book_000123", "chapter": "第一章 导论", "text": "这里是清洗后的正文段落内容", "source_page": 12, "lang": "zh"}这种格式可以直接被后续的数据加载器读取。
3.2 文本规范化与噪声清理
文本规范化是整个数据管道里最枯燥但最重要的一步。常见动作包括:
- 全角半角统一。
- 去除控制字符和不可见字符。
- 繁体简体转换或保留,按训练任务决定。
- 修正 OCR 常见错误,比如“0/O”“1/l”混用。
- 去掉连续重复段落。
下面给一个通用 Python 清理片段,具体规则需要根据实际语料迭代:
import unicodedata import re def normalize_text(text: str) -> str: # NFC 编码归一化,避免同一个字出现不同码位 text = unicodedata.normalize("NFC", text) # 常见标点替换 text = text.replace("“", "\"").replace("”", "\"") text = text.replace("’", "'").replace("‘", "'") # 去掉零宽字符 text = re.sub(r"[\u200b\u200c\u200d\u2060]", "", text) # 连续空格压缩 text = re.sub(r" {2,}", " ", text) return text.strip()这里没有银弹。清洗效果要以“人工抽样准确率”为判断标准,而不是看脚本跑没跑完。
3.3 去重:训练语料的关键一步
旧书语料的重复来源很多:同一本书的不同版本、不同出版社的重印、再版时内容基本不变、多本书互相引用大段文献。训练时重复语料过多,会让模型对重复模式过拟合,降低泛化能力,也会让评测指标虚高。
常用的去重手段:
- Exact Match 去重:原文完全一致,直接去掉。
- SimHash / MinHash 去重:把文本转成哈希签名,通过汉明距离判断相似度。
- LSH 索引:在海量语料里快速找相似文本。
- 段落级去重:不是整本书去重,而是按段落做去重,避免大段引用造成冗余。
示例:
import hashlib def text_hash(text: str) -> str: return hashlib.sha1(text.strip().encode("utf-8")).hexdigest() seen = set() dedup_lines = [] for line in open("corpus.txt", encoding="utf-8"): h = text_hash(line) if h in seen: continue seen.add(h) dedup_lines.append(line)这个示例只适合精确去重。要处理“同一段话换了几个人名”这类近似重复,需要 MinHash 或更复杂的模糊匹配方案。
4. 版权与合规边界:买书销毁不代表可以随便用
买旧书、销毁纸质书,不意味着数字化后的文字可以随便用。版权问题不会因为“销毁了实体书”就消失。
4.1 公共领域与版权保护期
公共领域作品可以使用,但判断标准要严谨。
- 作者去世超过版权保护期,通常可视为公共领域。
- 有些旧书虽然年代久远,但整理本、注释版、翻译版仍有独立版权。
- 国内整理出版的“古籍今译”“校注本”同样受版权保护,不能因为原著年代久就整体复制。
所以,批量采购后第一件事不是扫描,而是做版权筛查。建议建立“版权状态表”,逐本记录判定依据,保留可审计记录。
4.2 授权链条与使用范围
对于仍在版权保护期内的书,正确做法是获得授权:作者授权、出版社授权或版权代理机构授权。授权书明确数字化、模型训练、商业使用等范围。
绝不要因为“是公开销售的旧书”就默认可自由使用。购买实体书只获得了物理载体所有权,数字化权和复制权是另一套规则。
4.3 销毁环节的风险控制
销毁纸质书可以降低物理传播风险,但不能清除已经形成的数字化副本。所以,数字化副本的权限管理要更严格:
- 训练语料库设置访问白名单。
- 防止内部文本通过复制、下载、外链泄露。
- 模型输出如果可能复现原文片段,需要做输出过滤或内容溯源。
- 涉及人脸图像、个人隐私信息的内容,要遵循个人信息保护要求。
合规不是一次性动作,而是贯穿数据采集、处理、训练、部署全流程的审计体系。
5. 资源占用与性能观察:扫描语料项目的显存、存储与算力
做旧书语料很少只有一个 OCR 脚本跑完就结束,更多时候需要整套批处理流程。下面按资源维度做个梳理。
5.1 存储占用
- 扫描 PDF/TIFF:一本 300 页的书,300 DPI 灰度扫描,原始图像通常在几百 MB 到 1GB 之间。
- OCR 后的文本层 PDF:体积会小很多,几十 MB 到一百 MB 左右。
- 清洗后的 JSONL:一本 30 万字的书,纯文本大约 1-2MB。
所以,真正占空间的是扫描原图。推荐分层存储:原图像文件放冷存储,OCR 中间产物放热存储,最终文本按版本放到训练数据目录。
5.2 CPU 与 GPU 算力
OCR 识别本身可以只依赖 CPU。跑 Tesseract、OCRmyPDF 这类工具,CPU 数量越多,并行处理的吞吐越高。如果采用深度学习 OCR 模型,或者要在亿级文本上做模糊去重,再考虑 GPU。
显存占用取决于具体模型:
- 传统 OCR 工具:基本不占用显存。
- 深度学习 OCR / 文档理解模型:按模型参数规模,需要 6GB-24GB 不等,实际占用需要以本机测试为准。
- 大规模向量化去重:要看嵌入模型和批量大小。
对本地 AI 团队来说,第一步应该先在小数据集上测出稳定的吞吐量,再估算整体工期。不要一上来就堆资源。
5.3 性能观察方法
- CPU 占用:用
htop或任务管理器观察 OCR 进程是否吃满多核。 - 内存占用:批量扫描时,进程会一次性加载多个 PDF,注意内存峰值。
- 磁盘 IO:图像解码和中间文件写入会产生大量 IO,SSD 带来的提升非常明显。
- 端口冲突:如果后续接入 API 服务,注意使用独立端口。
# 查看 OCR 进程资源占用 htop -p $(pgrep -f ocrmypdf | head -n 1)如果批量任务卡住,优先看日志里是单张图片报错还是进程整体阻塞。
6. 接口 API 与批量任务:把旧书语料管道工程化
旧书信数字化不应该停留在“手动跑脚本”阶段。建议把它做成一个可重复的批处理任务系统。
6.1 任务队列设计
一个最小可用的任务队列包含四个环节:
- 扫描任务:输入 PDF 或图片目录,输出 OCR 文本。
- 清洗任务:输入 OCR 文本,输出规范化文本。
- 去重任务:输入规范化文本,输出去重后的语料。
- 入库任务:写 JSONL,绑定元数据,更新索引。
用 Python 写一个最简调度器框架:
import os import subprocess from pathlib import Path INPUT_DIR = Path("./scans") OUTPUT_DIR = Path("./texts") OUTPUT_DIR.mkdir(exist_ok=True) def process_one_pdf(pdf_path: Path) -> Path: output_pdf = OUTPUT_DIR / (pdf_path.stem + "_ocr.pdf") cmd = [ "ocrmypdf", "--language", "chi_sim+eng", "--deskew", "--clean", str(pdf_path), str(output_pdf), ] subprocess.run(cmd, check=True) return output_pdf for pdf_path in sorted(INPUT_DIR.glob("*.pdf")): if pdf_path.name.startswith("_"): continue try: result = process_one_pdf(pdf_path) print(f"[OK] {pdf_path.name} -> {result.name}") except subprocess.CalledProcessError as e: print(f"[FAIL] {pdf_path.name} -> {e}")批量任务最怕“一个文件卡死整个流程”。建议每个任务都加超时和失败重试。
6.2 API 服务化
已经把旧书语料处理流程做稳定的团队,考虑走 API 集成到现有数据平台。常见的服务形态:
- 扫描上传接口。
- OCR 状态查询接口。
- 清洗结果下载接口。
- 去重任务提交接口。
由于本项目没有公开标准 API,下面给一个通用调用模板,具体路径需要按你实际开发的服务调整:
import requests url = "http://127.0.0.1:8000/ocr_task" payload = { "file_id": "book_000123", "language": "chi_sim+eng", "options": { "deskew": True, "clean": True, "output_format": "jsonl" } } response = requests.post(url, json=payload, timeout=30) print(response.status_code) print(response.json())接口服务必须为每个任务返回唯一任务 ID,并提供查询接口,方便异步处理。
6.3 失败重试与日志
批处理任务不能只记录“成功/失败”,要记录失败原因。建议日志字段:
- 输入文件名
- 任务阶段
- 失败原因
- 重试次数
- 当前时间
默认策略:单个文件失败 3 次后跳过,并输出失败清单,由人工检查。
7. 常见问题与排查方法
| 问题现象 | 可能原因 | 排查方式 | 解决方案 |
|---|---|---|---|
| OCR 结果大量乱码 | 扫描分辨率低、页面倾斜、语言包缺失 | 抽查原图和文本层对比 | 调整 DPI、启用 deskew、重新安装语言包 |
| 繁体字/异体字识别率低 | 模型语言包不匹配或未做图像预处理 | 单独测试一页 | 切换更合适的 OCR 引擎或添加字典 |
| 清洗脚本处理后文本丢失段落 | 规则写得太宽,误删正文 | 抽样对比清洗前后文本 | 缩小正则规则范围,先人工标注样本 |
| 去重把不同书籍中有价值的内容误删 | 去重阈值过高 | 查看被删样本的相似度分布 | 降低阈值或改按段落级去重 |
| 批量任务卡住 | 单文件异常导致进程阻塞 | 查看日志和 CPU 占用 | 增加超时机制和任务级隔离 |
| 训练集里出现重复段落 | 数据处理时漏掉去重步骤 | 跑一遍哈希检查 | 在入库阶段强制去重 |
| 版权状态判定困难 | 缺乏纸质出版信息 | 核查版权页和版权登记信息 | 建立人工审核流程,无法确认则不入库 |
| 磁盘空间快速耗尽 | 原图 PDF 没及时归档 | 检查存储占用 | 原图转冷存储,按批次删除中间产物 |
8. 旧书语料生产的工程化建议
这条链路能不能落地,关键在工程化管理。给本地数据团队几点建议。
8.1 先跑最小闭环
不要一开始就采购几千本书。先做小规模样本:10-20 本书,跑完扫描、OCR、清洗、去重、入库。确认质量稳定后再扩大到批量采购。
8.2 维护两份数据清单
一份是“合法入库清单”,记录每本书的采购来源、版权状态、处理版本。一份是“存疑清单”,记录版权不确定、OCR 质量不合格、扫描状态异常的项。
这两份清单要能对应到具体文本文件。做不到“一条数据能追溯到一本书”,后面出问题就很难定位。
8.3 分层存储
建议目录结构:
data/ raw_scan/ # 原始扫描件 ocr_pdf/ # 带文本层的 OCR 结果 cleaned_text/ # 清洗后的文本 final_jsonl/ # 最终训练语料 logs/ # 批次日志原始扫描件不建议直接删除。清洗规则发生变动时,需要能重新生成数据。
8.4 合规先行
所有环节里,合规优先级最高。无法确认版权状态的书,不要进训练集。涉及人脸、个人隐私的旧刊,处理前要检查是不是符合个人信息保护要求。授权合作、商用发布之前,务必完成版权复核。
9. 总结与下一步
“AI 公司买旧书再销毁”这件事,核心不是“旧书值多少钱”,而是“高质量文本语料怎么稳定获得”。互联网数据质量下降,长文本稀缺,旧书数字化正在成为一条不可忽视的训练语料补充路线。
如果你在做大模型数据建设,最先值得验证的是三条链路:旧书版权状态筛查流程、扫描到 OCR 文本的完整管线、批量去重和来源追溯机制。最容易踩的坑是跳过版权审核直接扫描入库,以及清洗规则定得太宽导致有效信息被误删。
下一步可以继续扩展的方向包括:把旧书文本和现有开源数据集迁移到统一的 JSONL 格式,做跨数据源去重;尝试使用更大的开源 OCR 模型提升古籍和繁体文本识别率;以及在数据管道中接入模型输出过滤,确保训练语料既能进得来,也能管得住。建议把这个流程先做成小规模闭环,再逐步扩大采购范围。