大模型知识库建设前置之数据清洗

发布时间:2026/7/11 9:33:46
大模型知识库建设前置之数据清洗 Day09大模型知识库建设前置之数据清洗——数据是 AI 的粮食垃圾进则垃圾出前言教程里的语料是干净 txt企业里是一锅粥先甩一个可能反直觉的数字。多年业界调研都指向同一件事数据团队 / 数据科学家60%~80% 的时间花在找数据、洗数据上而不是建模。AI 项目失败根因也大多落在数据质量而不是模型不够强。这个数字我第一次看到时是不信的。直到我第一次给公司搭企业 RAG自信满满上了线——结果用户一问AI 一本正经地引用了一份 2018 年的过期表格而那份表格藏在一个扫描件 PDF 的第三页。从那以后我学会一件事先洗数据再谈模型。教程里的语料永远是干干净净的txt。企业里的语料呢是Word 批注、Excel 合并单元格、双栏 PDF、PPT 截图、扫描件 jpg一锅粥。十几种格式每种脏法都不一样。这就引出 AI 领域最古老也最狠的一条铁律——Garbage In, Garbage Out垃圾进垃圾出。很多人觉得大模型这么强数据脏一点无所谓吧恰恰相反。模型越强对数据越敏感。弱模型遇到脏数据输出平庸强模型遇到脏数据输出的是自信的胡说——危害大十倍。Day06 我讲过 RAG 检索。今天补上拼图里缺的那一块为什么你 RAG 效果差八成不是检索算法的锅是语料没洗干净。这篇文章按企业真实场景走一遍先建诊断框架——脏数据的五宗罪按文件类型分类实战——纯文本、办公文档、PDF 与图片各怎么洗治本——如何从源头避免脏数据而不是事后擦屁股PART 01脏数据的五宗罪——先学会体检再动刀动手洗之前先搞清楚脏有哪些种类。我把企业里最常见的脏数据归成五宗罪① 缺失与空值字段为空、只有标题没有正文、只有模板没有内容② 重复数据同一份文档被爬 10 次严重污染检索结果和向量库③ 格式混乱PDF / 网页提取后的换行错乱、表格散架、段落粘连④ 编码与噪音乱码、HTML 标签残留、不可见字符、全角半角混用⑤ 隐私与合规手机号、身份证、邮箱、地址直接进语料——最容易忽略也最致命记住这五条。后面不管你处理什么文件类型都是在跟这五种脏打交道只是每种格式的重灾区不一样。一个心法清洗前先体检别上来就套模板。统计一下缺失率、重复率随机抽几条看看长什么样再决定怎么洗。PART 02纯文本与结构化数据txt / markdown / html / json / csv——别以为最简单就最干净从最简单的说起。但最简单不等于最干净。txt第一杀手是编码txt 的头号问题是编码。同一份文件可能是 GBK可能是 UTF-8还可能是带 BOM 的 UTF-8。读错编码就是满屏乱码。import re import unicodedata from charset_normalizer import from_path def read_txt_clean(path: str) - str: # 1. 自动探测编码统一读成 utf-8 raw from_path(path).best().bytes.decode(utf-8, errorsignore) # 2. NFKC 规范化全角转半角、干掉零宽字符、统一兼容字符 text unicodedata.normalize(NFKC, raw) # 3. 折叠多余空白和连续空行 text re.sub(r[ \t], , text) text re.sub(r\n{3,}, \n\n, text) return text.strip()这里有个坑我踩过无数次全角逗号和半角逗号,看起来一样其实是两个字符。它们会让去重完全失效——你以为去重了结果同一句话因为一个标点存了两份。NFKC规范化就是救场的。它会把全角字符统一转成半角让看起来一样的东西真的变一样。markdown / html导航栏和广告是噪音重灾区网页抓取下来的内容正文往往混着导航栏、页脚、广告、JS 代码。直接喂给模型等于让 AI 读一份贴满小广告的报纸。别自己写正则去抠。用trafilatura专门做网页正文提取一行搞定import trafilatura def extract_main_content(html: str) - str: # 一行干掉导航栏、广告、页脚只留正文 text trafilatura.extract( html, include_commentsFalse, include_tablesTrue ) return text or json / csvschema 不一致是慢性毒药结构化数据看似规整真正的杀手是字段对不上这一批title是字符串下一批变成了 null这行的date是2024-01-01下一行是2024年1月1日。别等数据进了向量库才发现问题。入库前就用 schema 校验卡住它像代码 CI 一样import pandas as pd import pandera as pa from pandera import Column, DataFrameSchema, Check schema DataFrameSchema({ title: Column(str, checksCheck.str_length(min_value1)), # 非空 content: Column(str, checksCheck.str_length(min_value10)), # 正文至少10字 date: Column(pa.DateTime, nullableTrue), }) df pd.read_csv(articles.csv) try: schema.validate(df, lazyTrue) print(✅ 数据合规) except pa.errors.SchemaErrors as err: print(f❌ 拦截 {len(err.failure_cases)} 行脏数据)脏数据在门口就被拦下总好过它溜进模型后再让你 debug 一整夜。PART 03办公文档三兄弟Word / Excel / PPT——企业语料的主战场企业里的知识八成躺在办公文档里。这一节是真正的实战。Word批注和修订会复活脏内容Word 文档最坑的地方是修订和批注。你以为删掉的内容其实还躺在文档里只是被标记成删除态。如果你的提取代码不处理已经删掉的脏内容会复活重新进语料。from docx import Document def extract_docx_text(path: str) - str: doc Document(path) paragraphs [] for p in doc.paragraphs: text p.text.strip() if text: # python-docx 的 .text 默认不含已删除修订但仍建议先接受所有修订 paragraphs.append(text) return \n\n.join(paragraphs)实操建议批量处理前先用脚本或 Word 自带功能接受所有修订再提取。别带着满篇红线就上。Excel合并单元格和公式是两个地雷Excel 有两个经典地雷地雷一公式 vs 公式结果。一个单元格存的是A1B1这个公式字符串还是它算出来的42读错就是灾难——下游拿到的全是公式根本不是数据。地雷二合并单元格。看起来一个值占了好几格实际只有左上角有值其它是空。直接读会丢一大片数据。import openpyxl def read_excel_values(path: str): # data_onlyTrue取公式计算结果不取公式字符串 wb openpyxl.load_workbook(path, data_onlyTrue) rows [] for ws in wb.worksheets: for row in ws.iter_rows(values_onlyTrue): # 过滤整行全空的废行 if any(c is not None and str(c).strip() for c in row): rows.append(row) return rowsdata_onlyTrue这一个参数能帮你省掉无数个为什么我读出来全是 None的深夜。PPT每页都碎备注里藏着金子PPT 的特点是碎片化——每页就那么几个字单独看几乎没有信息量。但有个地方常被忽略备注栏。演讲者把真正想说的、详细的解释都写在备注里。丢掉备注等于丢掉一半内容。from pptx import Presentation def extract_pptx(path: str): prs Presentation(path) pages [] for i, slide in enumerate(prs.slides, 1): texts [] for shape in slide.shapes: if shape.has_text_frame: texts.append(shape.text_frame.text) note slide.notes_slide.notes_text_frame.text if slide.has_notes_slide else pages.append({page: i, content: \n.join(texts), note: note}) return pages办公文档的脏往往不在字面藏在格式结构里。所以你必须按结构去解析而不是当纯文本一把梭。PART 04最硬的两块骨头——PDF 与图片pdf / png / jpg / 扫描件终于来到企业最痛的地方。前面那些都算好洗的PDF 和图片才是真正费时费力的。PDF第一步永远先判断它有没有文本层PDF 分两种清洗路线完全不同文本层 PDF数字原版导出的可以直接提取文字扫描件 PDF纸质扫描 / 拍照转的没有文本层必须走 OCR怎么判断提一下试试提不出字就是扫描件import pdfplumber def is_scanned_pdf(path: str) - bool: with pdfplumber.open(path) as pdf: text pdf.pages[0].extract_text() or return len(text.strip()) 20 # 提不出字 → 大概率扫描件文本层 PDF用pdfplumber还能把表格还原成结构化数据def extract_pdf(path: str): all_text, tables [], [] with pdfplumber.open(path) as pdf: for page in pdf.pages: all_text.append(page.extract_text() or ) tables.extend(page.extract_tables()) # 表格单独还原保留行列结构 return \n.join(all_text), tablesPDF 的坑还有一堆双栏排版按行读会跨栏串台、跨页断句一句话被切成两半、页眉页脚噪音。这些没有银弹得针对你的文档类型调参。复杂 PDF 别硬啃——上 MinerUpdfplumber 提文本、自己再接 OCR是拼积木。简单 PDF 够用但企业里的 PDF 常常是双栏论文、嵌套表格、数学公式、图文混排——每个坑都得自己填填到怀疑人生。这种场景别硬拼直接上MinerU——上海人工智能实验室开源的文档解析工具一个工具把 PDF也支持 Word / PPT变成结构化 Markdown专治各种硬骨头。它一个工具干完四件事版面分析自动识别标题、段落、图、表、页眉页脚恢复正确阅读顺序——双栏不再串台表格还原复杂表格直接输出 Markdown 表格行列结构完整公式识别数学公式转成 LaTeX——这是 pdfplumber 想都别想的能力OCR扫描件、图片也能解析不用你手动分流文本层输出是干干净净的 Markdown直接喂 RAG。前面文本层还是扫描件的判断MinerU 内部自动处理你根本不用操心。用法也很省心参数以官方仓库opendatalab/MinerU为准pip install -U mineru[core] mineru -p report.pdf -o output_dir/那什么时候用 pdfplumber什么时候上 MinerU一句话简单文本 PDF 用 pdfplumber轻、快复杂或扫描件 PDF 用 MinerU重但省心。当你手里有一堆合同、报告、论文 PDF 要清洗进 RAGMinerU 基本是当下绕不开的选择。图片 / 扫描件OCR 的老路和多模态大模型的新路图片png / jpg和扫描件本质都得把图变成字。有两条路老路OCR。pytesseract是通用选择中文场景PaddleOCR更准。新路2026 主流直接丢给多模态大模型读图。GPT-4o、Claude 这类模型能直接看懂图片连图表、公式、手写都能读。省掉一整套 OCR 管线import base64 from anthropic import Anthropic client Anthropic() def image_to_text(image_path: str) - str: with open(image_path, rb) as f: b64 base64.b64encode(f.read()).decode() resp client.messages.create( modelclaude-opus-4-8, # 替换成你在用的多模态模型 max_tokens2000, messages[{ role: user, content: [ {type: image, source: {type: base64, media_type: image/png, data: b64}}, {type: text, text: 把这张图里的文字、表格、公式结构化提取出来保留原始顺序。} ] }] ) return resp.content[0].text这条路还有一个隐形好处多模态模型理解内容而 OCR 只是识别字符。遇到模糊图、复杂表格差距立现。最后提一个隐私坑图片的 EXIF 信息可能带 GPS 定位、拍摄设备、时间。上传图片给模型前先把 EXIF 剥掉别让你的训练数据泄漏公司坐标。PART 05治本——如何从源头避免脏数据到这里你可能会想清洗这么麻烦能不能别让它脏能。清洗是治标规范是治本。源头少制造一份脏数据下游就少洗十份。我总结成五件事① 采集就要立规矩。统一文档模板、字段必填、定好 schema。别让每个人按自己习惯填那是制造脏数据的流水线。② 入库即校验数据 CI。上一节的Pandera就是干这个的。让脏数据在进库那一刻就被拦下像代码过 CI 一样——进不了库的脏数据永远不会污染你的模型。③ 隐私前置别事后补救。采集 / 上传那一刻就脱敏而不是等数据堆成山了再全库扫一遍import re PATTERNS { phone: (re.compile(r1[3-9]\d{9}), [手机号]), id_card: (re.compile(r\d{17}[\dXx]), [身份证]), email: (re.compile(r[\w.-][\w.-]\.\w), [邮箱]), } def mask_pii(text: str) - str: for name, (pat, rep) in PATTERNS.items(): text pat.sub(rep, text) return text # mask_pii(联系我13800138000邮箱 abcx.com) # → 联系我[手机号]邮箱 [邮箱]④ 数据血缘与版本管理。每份数据能追溯来源、能回滚版本工具如DVC、LakeFS。哪天模型突然变蠢你能定位到是哪一批脏数据混进来的。⑤ 组织层面定 SLO、定 owner。工具再全没人对数据质量负责也白搭。脏数据有没有人兜底比有没有工具更关键。记住这句话最好的清洗是让数据从一开始就不脏。结尾模型决定上限数据决定下限回头盘点这篇的核心脏数据有五宗罪缺失、重复、格式、编码噪音、隐私泄漏按文件类型对症下药纯文本治编码、办公文档治结构、PDF 与图片先判文本层清洗是治标规范是治本——采集规范、入库校验、隐私前置、血缘版本、质量 SLO我见过太多团队模型换了三代效果还是上不去。一查语料还是三年前那堆没洗过的扫描件。模型决定上限数据决定下限——而大多数人卡在下限上。你喂给 AI 什么它就还你什么。粮食干净了AI 才吃得香。互动时间你做 AI 项目时被哪种格式的脏数据坑得最惨是合并单元格、双栏 PDF还是别的来评论区聊聊我挨个帮你拆。下一篇 Day10 预告大模型微调入门——数据洗干净了Day09下一步就是拿这批干净数据训练一个属于你自己的模型。LoRA 和全参微调怎么选、什么场景才该微调。关注小刘檀木不错过每一篇。— END —小刘檀木 · 帮普通人把 AI 学进简历