ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

中文语料库处理实战:从ZIP解压到数据清洗全流程

中文语料库处理实战:从ZIP解压到数据清洗全流程 简介本资源是一份面向中文自然语言处理NLP研究者与AI开发者的基础语料支撑数据集适用于文本分类、情感分析、词性标注、机器翻译等任务的模型训练与算法验证。压缩包共8个文件含5个JSON格式语料样本与标注数据覆盖问答、医疗、新闻等多领域、2个Python脚本含爬虫与预处理工具、1份README说明文档整体大小17.39MB结构清晰、开箱即用。目前已有248人学习下载适合具备基础Python与NLP知识的中初级开发者快速构建实验基线。读者可直接加载JSON语料进行分词、停用词过滤与词性统计调用提供的工具脚本完成Sogou QA等子集的增量采集与清洗并参考文档理解语料组织逻辑与领域划分依据显著降低语料获取与预处理门槛。 前阵子从网盘拖回来一个ChineseCorpus.zip文件名后面还挂着一串“Corpus_of_Chinese”的英文标识压缩包体积不小。当时我手头正好在折腾一个中文文本分类的实验缺的就是这种通用型中文语料库数据集想着解压之后直接切分训练集和验证集就能开工。结果没想到光是把这份 zip 弄到能用的状态就花了我大半天。中途踩了文件校验、编码乱码、重复样本太多、噪声过滤不足等一系列坑最后才摸清楚一套比较顺手的处理流程。这篇就围绕“拿到一个中文语料库数据集压缩包之后怎么把它变成可训练的干净语料”这条主线来写。我不会讲太虚的理论基本都是实际操作过的步骤和排查思路。如果你是做 NLP 的、准备训词向量或者微调大模型的又经常被各种data.zip、corpus.zip折腾到崩溃这篇文章应该能帮你省不少时间。1. 收到 ChineseCorpus.zip 之后先别急着解压做数据检查很多人拿到压缩包的第一反应是双击解压然后直接写代码去读里面的文件报错了再回头找原因。我建议改一下这个顺序先花两分钟做文件本身的检查把“压缩包是否完整”“是不是真的是 zip 格式”这两件事确认清楚。因为后续所有解压报错十有八九都源于这两步没做。1.1 文件完整性与真实格式校验压缩包在网络传输中可能被截断也可能被网盘客户端改名。最稳妥的做法是在命令行里先算一下哈希值和发布方给的 MD5 或 SHA256 做个比对。如果发布方没给哈希那就至少确认文件大小和下载页标注的体积一致。在 Linux 或 macOS 下执行md5sum ChineseCorpus.zip sha256sum ChineseCorpus.zipWindows 下可以用 PowerShellGet-FileHash ChineseCorpus.zip -Algorithm SHA256接着用file命令看它到底是真 zip 还是披着 zip 外壳的别的格式file ChineseCorpus.zip如果输出里包含Zip archive data基本没问题。如果输出是gzip compressed data或者RAR archive data那就说明后缀名和实际格式不一致单纯改后缀名解决不了根本问题得换对应的解压工具。1.2 压缩包内部完整性测试文件哈希没问题不代表里面的每个文件都能正常解压。zip 是支持存储校验信息的我们可以用 Python 的 zipfile 模块快速测一遍所有条目是否完整import zipfile with zipfile.ZipFile(ChineseCorpus.zip, r) as zf: bad_file zf.testzip() if bad_file: print(f压缩包内存在损坏文件: {bad_file}) else: print(压缩包完整性测试通过)这段代码会逐个解压内部文件到内存并校验 CRC。如果输出None就代表所有文件通过校验可以放心解压。如果报BadZipFile错误或者testzip返回了某个文件名那就说明压缩包确实损坏了直接进入第 2 节的处理流程。提示这一小步成本极低但能帮你把“数据文件本身有问题”和“处理代码有 bug”这两类问题彻底隔离开。我见过太多人拿着一个下载不全的 zip 折腾了一下午解析代码最后才发现是压缩包少了几个字节。2. 解压环节容易被低估的三个细节解压 zip 听起来是再基础不过的操作但一旦涉及中文文件名、分卷压缩或者跨平台迁移就会冒出一些平时遇不到的怪问题。2.1 Linux 下处理中文文件名乱码如果这个压缩包是在 Windows 环境下打包的里面的中文文件名很可能用了 GBK 编码。Linux 下默认的 unzip 会按 UTF-8 去解释文件名于是原本的“新闻语料.txt”就变成了“鏂伴椈璇枡.txt”这类乱码。解决办法是让 unzip 使用指定的字符集解码unzip -O GBK ChineseCorpus.zip-O参数在较新的 unzip 版本中才可用。如果你的发行版自带的 unzip 不支持这个参数可以改用 7-Zip7z x ChineseCorpus.zip -mcpGBK或者用 Python 脚本解压手动指定文件名编码import zipfile with zipfile.ZipFile(ChineseCorpus.zip, r) as zf: for info in zf.infolist(): # 尝试修复文件名编码 try: fixed_name info.filename.encode(cp437).decode(gbk) except (UnicodeDecodeError, UnicodeEncodeError): fixed_name info.filename zf.extract(info, output_dir) # 解压后重命名 import os if fixed_name ! info.filename: os.rename( os.path.join(output_dir, info.filename), os.path.join(output_dir, fixed_name), )这里有个细节zipfile 模块在读取文件名时如果遇到未知编码默认会以cp437解码所以把它重新编码回原始字节再用gbk解码就能还原出正确的中文文件名。2.2 分卷 zip 与损坏包的合并修复有些发布方会把大压缩包拆成多个分卷比如ChineseCorpus.z01、ChineseCorpus.z02加上最后的ChineseCorpus.zip。如果你只下载了最后一个 zip 文件解压时一定会报错。分卷文件需要先合并再解压。在 Windows 上用 7-Zip 打开.zip分卷文件它会自动识别同一目录下的.z01分卷在 Linux 下可以手动合并zip -s 0 ChineseCorpus.zip --out merged.zip这句命令会把所有分卷合并成一个完整的merged.zip然后再正常解压。如果压缩包本身没有分卷但解压报错提示文件损坏可以尝试用 zip 的自修复功能zip -FF damaged.zip --out repaired.zip这种修复只对结构损伤有效如果核心数据区已经损坏多数情况下还是得重新下载。2.3 压缩包体积太大时的解压策略有些中文语料库解压后体积可能膨胀到原始 zip 的好几倍。如果你只是想先看看里面有哪些文件不需要马上全量解压可以用一个取巧的办法unzip -l ChineseCorpus.zip | head -50unzip -l只列出压缩包内部的文件清单不解压任何内容。通过这个命令你可以先了解语料库的目录结构、文件格式和数据量级再决定要不要全量解压。3. 语料库内部结构从压缩包到可训练数据的转化路径解压完成只是第一步。压缩包里面通常不是一个规整的 CSV而是一堆散落的 txt、json、jsonl 文件有的还套着多层子目录。这时候需要先摸清内部结构再决定怎么做格式统一。3.1 常见的中文语料库目录形态一份常见的通用中文语料库解压后大概率是下面这几种形态之一类型文件后缀特征纯文本集.txt每个文件是一篇文章或每行是一条句子JSON 结构化.json包含字段名如 title、content、labelJSONL 流式.jsonl每行一条 JSON适合逐行读取CSV/TSV 表格.csv、.tsv适合用 pandas 直接加载看到.txt文件先别高兴txt 内部的编码五花八门。有些是国内爬虫程序直接落盘的 GBK 编码有些是带 BOM 的 UTF-8还有些干脆是 UTF-16。最靠谱的做法是全部统一读成字符串再落盘为标准 UTF-8。用 Python 写一个批量编码探测与转换的小工具可以复用charset-normalizer库pip install charset-normalizerfrom charset_normalizer import from_path def read_text_auto(path): best_match from_path(path).best() return str(best_match) with open(converted.txt, w, encodingutf-8) as f: f.write(read_text_auto(original.txt))这个库比chardet准确率高不少尤其是对中文网页和社交文本这种带大量噪声的语料表现明显更好。3.2 数据量级的判断与抽样验证花了几分钟把编码问题理清之后下一步是评估数据量级。先看总行数、总文件数、总字符数这些指标决定了后续清洗链路用什么工具。如果总体在几百 MB 到几 GB直接用 Python 脚本跑一遍没压力。如果解压后有几十 GB就要考虑分段处理或者用 Dask/Spark 这类分布式工具了。我的建议是先做一个小样本抽样验证清洗逻辑正确之后再全量跑。import random with open(all_text.txt, r, encodingutf-8) as f: lines f.readlines() sample random.sample(lines, min(10000, len(lines))) with open(sample_10k.txt, w, encodingutf-8) as f: f.writelines(sample)抽样之后人工读一遍这 1 万条样本能快速发现语料里混着多少垃圾内容。这一步极其重要它决定了清洗规则要设得多严。3.3 格式统一与字段映射如果语料是 JSON 或 CSV 格式通常还需要把字段名映射到你自己项目需要的字段。比如原始文件里叫正文你的代码里叫text那就要写个字段映射函数。我习惯在这一步同时做三件事统一字段名为英文小写下划线风格过滤掉关键字段为空的行剔除重复的 ID如果原始数据有 ID 字段import json import hashlib def normalize_json_line(line): obj json.loads(line) text obj.get(正文) or obj.get(content) or obj.get(text) if not text: return None return { text: text.strip(), source: obj.get(来源, ), id: hashlib.md5(text.encode(utf-8)).hexdigest(), }这样处理完之后不管原始压缩包里有多少种文件格式你手头都会得到一份统一结构的干净数据。4. 清洗中文语料的核心工序其实就这四步很多初学者把语料清洗理解为“去个标点、转个小写”自然做出来的模型效果也不行。中文语料清洗的关键是处理重复、噪声、短句和敏感信息这四道工序缺一不可。我逐个说清楚怎么做以及为什么要这么做。4.1 文档级与句子级去重两种都要做中文互联网语料里最常见的垃圾就是转载内容同一篇文章可能在多个来源重复出现。如果不去重模型会严重偏向这些高频重复的文本。文档级去重最简单的方式是算 MD5import hashlib def dedup_by_hash(texts): seen set() unique_texts [] for t in texts: h hashlib.md5(t.encode(utf-8)).hexdigest() if h not in seen: seen.add(h) unique_texts.append(t) return unique_texts但完全相同的文章用 MD5 就够了真正麻烦的是“改几个字又发一遍”的近似重复。这种要靠 MinHash 或者 SimHash 做局部敏感哈希计算量会上一个量级。如果只是普通的文本分类、词向量训练任务先做精确去重就够了。做预训练模型数据时再考虑引入近似去重否则工程复杂度会高很多。句子级去重也很重要。中文社交媒体语料中很多句子反复出现在不同上下文里比如“哈哈哈哈”“学到了学到了”“前排围观”这类回复。这类高频共性句子会让模型在生成时变得很油滑。一种简单的句子级去重策略是先统计全量语料里高频句子的出现次数把超过一定阈值比如出现 100 次以上的句子全部加入黑名单过滤掉。也可以用 TF-IDF 的逆文档频率IDF来筛选IDF 过低的词往往是通用废话整句 IDF 偏低就说明信息量不足。4.2 移除 HTML 标签、URL 与无意义符号爬虫类语料里经常会混进去大量 HTML 残留。处理办法是用 BeautifulSoup 的get_text()方法比正则表达式要稳得多from bs4 import BeautifulSoup def clean_html(raw): soup BeautifulSoup(raw, html.parser) return soup.get_text(\n, stripTrue)URL 通常不是我们需要的语义信息建议直接替换为空。邮箱、电话号码也有类似问题。正则表达式可以这样写import re def remove_noise(text): text re.sub(rhttps?://\S, , text) text re.sub(rwww\.\S, , text) text re.sub(r\S\S, , text) # 邮箱 text re.sub(r\d{11}, , text) # 手机号 # 去掉控制字符 text .join(ch for ch in text if ord(ch) 32 or ch in \n\t) return text注意ord(ch) 32这个条件它能过滤掉大部分不可见控制字符同时保留换行符和制表符。4.3 长度过滤与非中文比例过滤中文语料里夹杂太多英文、数字、乱码符号会干扰中文模型的训练。常用策略是设置两个阈值最短长度阈值过滤掉过短的句子或文档。比如文本分类任务里少于 20 个字符的样本往往没有足够的语义信息。最大非中文比例阈值计算非中文字符在总字符中的占比超过一定比例就整段丢弃。def is_qualified(text, min_len20, max_ratio0.5): if len(text) min_len: return False total_chars len(text) non_chinese len(re.findall(r[^\u4e00-\u9fff], text)) ratio non_chinese / total_chars return ratio max_ratio这里\u4e00-\u9fff覆盖了 CJK 统一汉字。遇到混合语言文本时这个比例可以按任务需求调整。做中文词向量时我一般把max_ratio压到 0.3 以下保证语料的“纯度”。4.4 内容合规与个人信息脱敏这里要单独拿出来说因为不是技术问题但比技术问题更重要。凡是会公开传播、重新发布或者用于商业化模型训练的语料都必须做个人信息脱敏。中文语料里最常见的敏感信息是手机号、身份证号、银行卡号和详细地址。脱敏不一定要用很复杂的模型正则表达式可以覆盖大部分场景import re def desensitize(text): text re.sub(r\b1[3-9]\d{9}\b, [手机号], text) # 手机号 text re.sub(r\b\d{17}[\dXx]\b, [身份证], text) # 身份证号 text re.sub(r\b\d{16,19}\b, [银行卡], text) # 银行卡号 return text更复杂的人名、机构名、地名脱敏可以引入命名实体识别模型如 LAC、LTP来做但这是另一个话题了。作为数据从业者的基本职业习惯拿到任何语料后先跑一遍脱敏脚本再考虑下一步。宁可漏掉一些可用文本也要避免把个人信息带进大规模训练数据。提示如果需要对外发布处理后的语料库除了脱敏还要确认原始数据的来源和授权方式。学术开源语料和爬虫采集语料的可再分发性完全不同别等数据被投诉了才想起来查授权。5. 这份语料库能做的事三种落地路线对比清洗干净的中文语料库应用方向非常多。这里按工程难度从低到高列出三条我实际走过的路线供你根据手头资源选择。5.1 路线一训练领域词向量如果语料量级在几千万到几亿字训练一个领域词向量是完全可行的。这个用途非常适合刚接触语料库的开发者因为 word2vec 的训练代码非常简单不需要 GPU 也能几小时内跑完。我用 gensim 跑过一次新闻类语料的 word2vecfrom gensim.models import Word2Vec from gensim.models.word2vec import LineSentence sentences LineSentence(cleaned_corpus.txt) model Word2Vec( sentences, vector_size128, window5, min_count5, workers8, epochs5, ) model.save(word2vec_news.model)注意LineSentence要求输入文件是“一行一句话”的格式。如果你的语料是整段文档需要先做分句处理。这个场景下清洗阶段的长度过滤格外重要太短的句子会产生大量无意义词对。5.2 路线二构建 RAG 知识库近两年大模型应用兴起之后很多中文语料库被用来构建知识库给 LLM 做检索增强生成。这条路对语料质量的要求最高——检索得到的文档必须切得足够规整语义完整否则生成质量会大打折扣。RAG 场景下清洗关注点从“高纯度”转向“高信息密度”。你不需要把非中文全部过滤掉反而要保留日期、数字、专有名词这些关键信息。语料切分时不要按固定字符数硬切而是按段落边界切保留语义完整性。def split_paragraphs(text, max_len800): paragraphs text.split(\n) chunks [] current for p in paragraphs: if len(current) len(p) 1 max_len: chunks.append(current) current p else: current \n p if current: chunks.append(current) return chunks切分之后再对每个 chunk 做嵌入向量化存入向量数据库。这里注意清洗阶段千万别把停顿词全部删掉否则切分出来的 chunk 读起来会很奇怪检索效果反而变差。5.3 路线三微调下游模型的指令数据转换如果语料库带有标签字段比如情感分类、主题分类那么它可以直接作为下游模型微调的原始数据。但要注意普通分类数据不能直接喂给大模型微调需要先转换成“指令-回答”式的对话格式。instruction f请判断下面这段文本的情感倾向是正面还是负面。\n文本{text} response 正面 if label 1 else 负面这种转换成对的样本量不需要特别大几万条高质量数据就足够让模型适应新领域了。反而需要特别注意的是标签噪声——如果原始数据里有 5% 的错误标签模型训完会继承这个错误。我在清洗阶段会额外做一次标签一致性抽样检查。6. 中文语料库使用中的高频报错与排查链路这一节是把最常见的问题单独列出来每条都附上我实际排查过的思路方便你直接对照。6.1 file is not a zip file 与 invalid zip archive: could not find eocd这两个报错本质上是同一个问题解释器不认为当前文件是合法的 zip 结构。could not find eocd中的 EOCDEnd of Central Directory是 zip 文件结尾处的一段固定结构相当于压缩包的“目录索引”。如果文件被截断、下载不完整或者某个分卷没下载就会找不到 EOCD。排查步骤很固定用ls -lh查看文件大小和来源页面比对。用file命令确认真实格式。如果确认是 zip 但存在分卷用zip -s 0合并。如果确认是普通 zip 且大小无误尝试zip -FF修复。以上都不行重新下载。别浪费时间在这类问题上绝大多数情况就是文件没下全。6.2 解压后大量“锟斤拷”乱码“锟斤拷”这三个字是中文编程圈的一道经典伤疤。它的成因是原本用 GBK 编码的文本被程序按 UTF-8 解码又按 GBK 编码存回导致原本的汉字变成无意义的替换字符。修复的前提是你能拿到最原始的字节流如果已经经过多次错误转换基本无法还原。处理思路是在第一步解压时就锁死编码策略用unzip -O GBK解压 Windows 打包的文件或者用 Python 的 zipfile 手动解码前面给过代码解压后立即统一转为 UTF-8后续所有处理环节都基于 UTF-8一个很实用的习惯是在处理文本的任何阶段打开文件时都明确指定encodingutf-8绝不依赖系统默认编码。跨平台脚本尤其要小心这一点Windows 下的默认编码可能不是 UTF-8。6.3 报错信息结尾带“与技术支持部门联系”字样热搜词里有一条很特别的报错“failed to copy spatial iop zip 与技术支持部门联系”。这个报错实际上是 Windows 系统某种设备驱动安装包或软件安装包在解压复制驱动文件时出现的和中文语料库本身无关。它的本质依然是 zip 数据读取失败通常是因为安装包被杀毒软件拦截或者下载过程中出现了文件锁占用。遇到这类报错时的排查顺序暂时关闭杀毒软件和系统防护重新执行安装以管理员身份运行安装程序下载目录路径不要包含中文和空格改成纯英文路径如果还不行重新下载安装包并校验哈希这个报错和语料库处理关系不大但既然搜索时经常一起出现就在这顺手说清楚。6.4 pip 安装依赖时的 zip 损坏报错在 Python 开发中error opening zip file or jar manifest missing这类报错多出现在 Java 环境或者部分 Python 包安装环节。它和压缩包损坏是同一类问题——某个 jar 包或 wheel 包在下载过程中被截断了。排查思路和 6.1 一致删除本地缓存重新下载校验哈希。如果频繁出现可以检查磁盘是否快要写满或者内存是否有故障。我遇到过几次反复下载还是损坏的情况最后发现是硬盘坏道导致的换了路径就解决了。6.5 解压时权限不够导致部分文件丢失在 Linux 服务器上解压别人打包的文件时偶尔会遇到Operation not permitted错误。这通常是因为压缩包里保留了原系统的文件权限而你当前用户没有对应权限。解决办法是解压时忽略权限位unzip -o ChineseCorpus.zip -d output_dir如果unzip因为权限问题解压失败可以用 Python 的 zipfile它默认不保留 Unix 权限位反而不容易出问题import zipfile with zipfile.ZipFile(ChineseCorpus.zip) as zf: zf.extractall(output_dir)6.6 全角半角与中文标点统一中文语料里最容易忽视的是全角半角混用问题。比如英文逗号,和中文逗号并存数字和字母也可能是全角形式。做分词、向量化时这些差异会让同一个词被拆成两个特征。统一规则很简单def normalize_fullwidth(text): # 全角转半角 result [] for char in text: code ord(char) if code 0x3000: # 全角空格 code 0x20 elif 0xFF01 code 0xFF5E: code - 0xFEE0 result.append(chr(code)) return .join(result)全角字符的 Unicode 范围和半角字符相差0xFEE0直接加减换算即可。注意这里只处理 ASCII 对应的全角字符不要动汉字。7. 清洗完成后的质量评估清单语料准备好之后不要急着训练。花点时间做几个快速的质量维度检查能省后面好多调试模型的功夫。我每次都会跑一遍下面这几项检查项目标值参考检查方式总字符数与预期量级一致wc -m或 Python 统计去重率精确去重后的留存率不低于 80%比对原始行数与去重后行数平均文本长度视任务而定一般不低于 30 字statistics.mean空白行占比低于 5%grep -c ^\s*$非中文占比低于 30%正则统计随机抽样人工可读性无明显乱码、无重复段落人工抽查 100 条如果某一项明显偏离目标值建议先修正清洗逻辑再进入模型训练环节。这里的数值只是经验参考不同任务可以大幅调整但随机抽样人工可读性这一项永远不要省——代码检查不出语义层面的垃圾只有人能看出来。我个人在实际操作中的一个习惯是清洗后的语料会保留一份文件名带日期和清洗规则的版本比如clean_v2_minlen20_nonch050.txt。这样过几个月回来自己还能想起来这批数据是用什么规则生成的不会面对一堆不知道哪个是最新版的 txt 发愁。最后分享一个小技巧。在处理超大语料时不要一次性把所有内容读进内存养成逐行迭代的习惯def process_large_file(input_path, output_path): with open(input_path, r, encodingutf-8) as fin, \ open(output_path, w, encodingutf-8) as fout: for line in fin: cleaned clean_line(line) if cleaned: fout.write(cleaned \n)这种写法在几个 GB 的文件上也毫无压力内存占用恒定。你拿到ChineseCorpus.zip之后不管里面装了多少文本这个处理框架都能套用。先把解压和编码这两关过了后面的事情就都是流水线作业了。本文还有配套的精品资源点击获取
返回列表