大模型训练语料实战:从PDF杂源到干净数据集完整流水线

文章目录

  • [1 前言:数据采回来,只算完成了一半](#1 前言:数据采回来,只算完成了一半)
  • [2 文档解析:先把杂货铺改成标准间](#2 文档解析:先把杂货铺改成标准间)
    • [2.1 MinerU:挖矿界的清道夫](#2.1 MinerU:挖矿界的清道夫)
    • [2.2 图片内容处理:让大模型看图说话](#2.2 图片内容处理:让大模型看图说话)
      • [2.2.1 单张图片转成描述](#2.2.1 单张图片转成描述)
      • [2.2.2 批量处理:一张张来,手会先报废](#2.2.2 批量处理:一张张来,手会先报废)
      • [2.2.3 别忘了图文一致性检验](#2.2.3 别忘了图文一致性检验)
  • [3 数据清洗流水线:半成品到成品的三道工序](#3 数据清洗流水线:半成品到成品的三道工序)
    • [3.1 去重:同一个通告,十家网站转](#3.1 去重:同一个通告,十家网站转)
      • [3.1.1 精确去重:MD5 一锤子买卖](#3.1.1 精确去重:MD5 一锤子买卖)
      • [3.1.2 近似去重:MinHash + LSH 认亲大会](#3.1.2 近似去重:MinHash + LSH 认亲大会)
    • [3.2 质量过滤:把噪声挡在训练集门外](#3.2 质量过滤:把噪声挡在训练集门外)
    • [3.3 隐私脱敏:安全领域的安全课](#3.3 隐私脱敏:安全领域的安全课)
  • [4 总结:两大阶段,五道工序](#4 总结:两大阶段,五道工序)


P.S. 无意间发现了一个巨牛的人工智能教程,非常通俗易懂,对AI感兴趣的朋友强烈推荐去看看, 传送门https://blog.csdn.net/qq_34419312

1 前言:数据采回来,只算完成了一半

上回我们把数据从各个犄角旮旯捞了回来------论文、电子书、漏洞库、开源数据集,一个都没落下。当时觉得自己富可敌国。

现在冷静下来看一眼手里的东西:论文有双栏有单栏,电子书图文混排,漏洞库清一色 JSON,文章里还夹着截图。这哪是数据,这是杂货铺刚进完货。

你要敢把这堆东西直接喂给模型,模型还没学会说话,先学会了看相------因为它得先猜你这份 PDF 里哪一行才是正文。

所以中间的加工环节省不了。流程不复杂,就三步:先统一格式,再清洗,再切块导出。今天咱们把这条流水线从头跑到尾。

2 文档解析:先把杂货铺改成标准间

异构文档怎么统一?我的答案是 Markdown。标题层级天然带结构,分块和清洗都能顺着章节语义来,属于中间格式里的万金油。

2.1 MinerU:挖矿界的清道夫

之前聊 OCR 的时候提过 DeepSeek-OCR,今天主角换一个:上海人工智能实验室开源的 MinerU。名字里带个"矿",干的就是挖数据的活。

它对学术论文、固定版式 PDF、docx、网页数据都挺能打,公式和表格还原得比我手打还完整,而且部分模式纯 CPU 也能跑------这个对穷人非常友好。

安装就三条命令,先给它单独开个 Conda 环境。理由很简单:环境隔离做好,将来炸了也不连累别人。

bash 复制代码
conda create -n mineru python=3.13 #创建名为mineru的conda虚拟环境
conda activate mineru #激活mineru环境
pip install -U "mineru[all]" #安装mineru依赖

装完直接用。假设原始文档都躺在 raw_docs 里,一行命令开工:

bash 复制代码
mineru -p ./raw_docs -o ./raw_output

-p 指定输入,-o 指定输出。跑完之后 MinerU 会自动做版面分析、文本提取、公式识别,最后吐给你一份结构化的 Markdown。

GPU 不够用?换 pipeline 模式,它调用更小的模型来干版式识别和文本提取。道理和点外卖一样:预算不够就换个小份的,饭照样能吃饱。

bash 复制代码
mineru -p ./raw_docs -o ./raw_output -b pipeline

解析完你会发现,每个 PDF 都生成一个同名目录,里面躺着一堆文件:.md 是最终成果,_layout.pdf 是版面还原,几个 JSON 是中间过程,还有一个 images 文件夹。咱们重点盯两个:Markdown 文件和图片文件夹。

打开 .md 一看,双栏论文的阅读顺序、章节标题、参考文献全给还原了,公式也变成了 LaTeX。说实话,它还原双栏论文的阅读顺序,比我当年看论文时翻页的顺序靠谱多了。

但这里有个问题:纯文本模型读不了图。Markdown 里的 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KLSDpxXD-1789106617242)(images/xxx.jpg) 只是立了个"此地有图"的牌子,图里的信息------攻击链、网络拓扑、流量截图------全藏在牌子后面。

2.2 图片内容处理:让大模型看图说话

思路其实之前就埋过伏笔:找多模态大模型给每张图生成一段语义描述,把图片"翻译"成文字。图没了,知识还在,这叫精神续作。

2.2.1 单张图片转成描述

我这次用的是阿里云百炼平台的 qwen3.8-flash。别问我为什么选它,问就是便宜大碗。

先用起来:登录百炼平台,在 API Key 管理页面创建一把 key。记住,key 只在创建那一刻给你看一次,之后想找回?没门。比前女友还绝情。

然后通过 OpenAI 兼容接口访问。注意,图片要先转成 base64 才能发,代码如下:

python 复制代码
import base64
from openai import OpenAI

client = OpenAI(
    api_key='',  # 百炼平台申请的 API Key
    base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
)

def describe_image(image_path: str) -> str:
    with open(image_path, "rb") as f:
        b64 = base64.b64encode(f.read()).decode()
    completion = client.chat.completions.create(
        model="qwen3.8-flash",
        messages=[{"role": "user", "content": [
            {"type": "image_url",
             "image_url": {"url": f"data:image/png;base64,{b64}"}},
            {"type": "text",
             "text": "你是一名网络安全专家。请用一段话详细描述这张图片的内容,"
                     "若图中包含文字标注,请一并准确转录。"}
        ]}],
    )
    return completion.choices[0].message.content

if __name__ == "__main__":
    print(describe_image("./images/f9534223ea3d6c1cd740555708cab1a64f3286c4005d78fae12b11709187d4ac.jpg"))

跑一下,模型会给你一段像模像样的描述,连图里的文字标注都能转录出来。那一刻你会觉得,它比我认识的大多数同事会写周报。

2.2.2 批量处理:一张张来,手会先报废

单张能跑通,但现实里动辄成百上千张图,一张张手动调?那模型还没训练完,先废掉的是我的手。

常规做法三步走:先用正则把 Markdown 里所有图片引用扒出来,再批量调模型生成描述,最后用描述文本把原引用替换掉。代码如下:

python 复制代码
# ---------- 批量处理逻辑 ----------
MD_PATH = "./面向大模型分布式微调的选择性聚合轻量化后门防御方法.md"
IMAGE_DIR = "./images"
OUTPUT_PATH = "./面向大模型分布式微调的选择性聚合轻量化后门防御方法_new.md"  # 直接覆盖原文件

# 第一步:用正则提取 Markdown 中实际引用的图片
IMG_PATTERN = re.compile(r"!\[([^\]]*)\]\(images/([^)]+)\)")
with open(MD_PATH, encoding="utf-8") as f:
    md_text = f.read()
referenced = [(m.group(0), m.group(2)) for m in IMG_PATTERN.finditer(md_text)]
print(f"Markdown 中引用的图片数量: {len(referenced)}")

# 第二步:逐张生成描述并直接替换
for i, (full_match, fname) in enumerate(referenced, 1):
    path = os.path.join(IMAGE_DIR, fname)
    if not os.path.exists(path):
        print(f"[{i}/{len(referenced)}] {fname} 文件不存在,跳过")
        continue
    try:
        desc = describe_image(path)
        # 用【图片内容:...】包裹描述,替换原图片引用
        replacement = f"【图片内容:{desc}】"
        md_text = md_text.replace(full_match, replacement, 1)
        print(f"[{i}/{len(referenced)}] {fname} 完成")
    except Exception as e:
        print(f"[{i}/{len(referenced)}] {fname} 失败: {e}")

    time.sleep(0.5)  # 简单限速,避免触发 API 限流

# 第三步:写回文件
with open(OUTPUT_PATH, "w", encoding="utf-8") as f:
    f.write(md_text)
print("全部完成,结果已写回", OUTPUT_PATH)

跑完一看,图片引用全变成了【图片内容:......】,图没了,但信息都在,读起来居然毫无违和感。像极了把前任的照片换成文字备注------人走了,备注还在。

2.2.3 别忘了图文一致性检验

这里有个坑,我替你们踩过了:多模态模型偶尔会"看图说话说过头",编造图里根本不存在的细节。这毛病我外婆也有,看个背影能给你脑补出一整部连续剧。

所以批量生成完之后,按 5%~10% 的比例抽检,重点看描述跟上下文逻辑对不对得上;要是关键报告(比如威胁分析里的攻击链图),那就全量人工过一遍。

记住一句话:宁可少几张图的描述,也别让错误信息混进训练集。模型学错东西,比你教错孩子还难掰回来。

3 数据清洗流水线:半成品到成品的三道工序

图片描述替换完,这份 Markdown 就能直接拿去训练了吗?想得美。

刚才处理的是论文,素质本身就高。换成爬来的社区数据试试?转载重复、广告导流、真实 IP 和邮箱,比小区门口的小广告还全。

从半成品到能下锅的饭,中间隔着三道工序:去重、质量过滤、隐私脱敏。咱们一道一道来。

3.1 去重:同一个通告,十家网站转

同一个漏洞通告,经常被十几个网站转载,措辞还略有不同。你以为是看到了十份新知识,其实是同一个笑话换了十个封面。

不去重的后果很实在:模型把这类内容反复学好多遍,挤占了其他知识的"学习配额",还容易过拟合。说得直白点,模型把"重复"当成了"重要"。

实践里通常上两级策略:先精确去重,再近似去重。

3.1.1 精确去重:MD5 一锤子买卖

完全一样的文档------转载时原样复制的很常见------直接用哈希判定,快到你还没反应过来就结束了:

python 复制代码
import hashlib

def exact_dedup(texts: dict) -> dict:
    """texts: {文件名: 文本},返回去重后的字典"""
    seen, result = set(), {}
    for name, text in texts.items():
        h = hashlib.md5(text.encode("utf-8")).hexdigest()
        if h not in seen:
            seen.add(h)
            result[name] = text
    return result

但是!更多转载不是原样复制,人家改个标题、换两段导语,MD5 就失效了。这帮人别的本事没有,规避检测的直觉比安全工程师还敏锐。

3.1.2 近似去重:MinHash + LSH 认亲大会

这时候就要近似去重登场:把文本切成 n-gram 词集合,用 MinHash 生成签名,再用 LSH 快速找出签名高度相似的文档对。

原理我用大白话讲:MinHash 把每篇文章的分词集合压成一串固定长度的整数签名,压缩完还能估算两篇文章的相似度,属于"压缩饼干"级别的技术;LSH 再把签名切成好几块分别哈希,保证相似文章至少在一块上撞车------像极了相亲软件,总有一个共同好友能把你们凑到一起。

工具不用自己造,datasketch 库拿来就用。先装依赖:

bash 复制代码
pip install jieba datasketch

完整代码长这样,注意看并查集那部分,那是处理"重复的传染性"的关键:

python 复制代码
import jieba
from datasketch import MinHash, MinHashLSH

# 1. 模拟文章数据(ID -> 正文)
articles = {
    1: "人工智能是计算机科学的一个分支,致力于开发能够执行人类智能任务的系统。",
    2: "人工智能是计算机科学的分支,专注于创建能完成人类智能活动的系统。",  # 近似重复
    3: "今天天气很好,适合出门散步和运动。"
}

# 2. 分词与预处理
def tokenize(text):
    return [w for w in jieba.lcut(text.lower()) if len(w) > 1]  # 过滤单字

# 3. 生成 MinHash 签名(perm=128)
def get_minhash(text, num_perm=128):
    mh = MinHash(num_perm=num_perm)
    for w in tokenize(text):
        mh.update(w.encode('utf-8'))
    return mh

# 4. 构建 LSH 索引(相似度阈值 0.8)
lsh = MinHashLSH(threshold=0.8, num_perm=128)
for aid, txt in articles.items():
    lsh.insert(aid, get_minhash(txt))

# 5. 查询相似并标记重复(使用并查集处理传递性)
parent = {aid: aid for aid in articles}

def find(x):
    while parent[x] != x:
        parent[x] = parent[parent[x]]
        x = parent[x]
    return x

def union(x, y):
    rx, ry = find(x), find(y)
    if rx != ry:
        parent[ry] = rx

for aid, txt in articles.items():
    mh = get_minhash(txt)
    candidates = list(lsh.query(mh))  # 0.4.6+ 返回生成器
    for cid in candidates:
        if cid != aid:
            # 可选:二次精确验证(提高准确率)
            # if mh.jaccard(get_minhash(articles[cid])) >= 0.8:
            union(aid, cid)

# 6. 提取去重后的文章(每个并查集簇保留一个代表)
unique_ids = []
seen_roots = set()
for aid in articles:
    root = find(aid)
    if root not in seen_roots:
        seen_roots.add(root)
        unique_ids.append(aid)

print("保留的文章ID:", unique_ids)  # 输出 [1, 3](2 被去重)

为什么必须用并查集?因为重复会传染:A 和 B 像,B 和 C 像,那 A 和 C 虽然长得不直接像,也得归到一家。不处理传递性,你会在半个月后惊讶地发现,同一篇文章在你的数据集里出现了三个版本。

参数上提两嘴:num_perm 是签名长度,越大精度越高但内存吃紧;threshold 控制灵敏度,建议先在小样本上试,0.7~0.9 是常见区间。百万级数据就换 MinHashLSHForest 提速。还有,LSH 是近似算法,召回候选后最好用 mh.jaccard() 精确算一遍相似度兜底,别全信相亲软件的"灵魂契合度"。

中文分词质量直接决定去重效果,停用词表和自定义词典该上就上。这套组合拳打下来,洗稿、局部改写统统逃不掉------它俩在我们这行,相当于学术界的降重。

3.2 质量过滤:把噪声挡在训练集门外

社区爬来的数据里,广告、导航残留、标题党是标配。这些低质数据模型是真消化不了,训练集里混进一条广告,模型就能学会在正经回答里给你安利公众号。

质量过滤的目标很简单粗暴:宁可错杀,不可放过。先上规则过滤,覆盖绝大多数明显噪声:

python 复制代码
AD_KEYWORDS = ["扫码关注", "点击阅读全文", "加微信", "领取资料"]

def quality_filter(text: str) -> bool:
    """返回 True 表示文本通过过滤(保留)"""
    # 1. 有效字数:去掉 Markdown 符号和空白后统计
    plain = re.sub(r"[#>\\-|`!()\\[\\]]", "", text)
    plain = re.sub(r"\\s", "", plain)
    if len(plain) < 100:
        return False
    # 2. 特殊字符占比:乱码、OCR 残留的典型特征
    special_ratio = len(re.findall(r"[^\\w\\s\u4e00-\u9fa5]", text)) / max(len(text), 1)
    if special_ratio > 0.3:
        return False
    # 3. 广告关键词命中
    if any(kw in text for kw in AD_KEYWORDS):
        return False
    return True

三条规则各管一片天:

规则 针对的噪声 示例
有效字数下限 导航页、目录页、摘要碎片 "首页 / 安全资讯 / 正文"
特殊字符占比 OCR 乱码、编码损坏文本 大段的 锟斤拷 或符号串
广告关键词 导流话术 "扫码关注公众号领取资料"

字数都凑不满 100 的,别叫碎片,叫渣渣,直接扔。特殊字符占比超 30% 的,基本是 OCR 把字认成了符号------这文本拿去训练,模型会以为世界的通用语是乱码。广告关键词更不用说了,它比你自己还懂读者想要什么。

实际工程里规则可不止这三条,我见过最多的公司光过滤规则就上百种。规则嘛,跟袜子一样,多多益善,就是别穿反。

那规则拿不准怎么办?老祖宗的智慧:万事不绝问大模型。如果公司有本地部署的大模型,写个脚本调 API,用提示词让模型判断文本有没有干货:

python 复制代码
from openai import OpenAI

client = OpenAI(
    api_key=os.environ["DASHSCOPE_API_KEY"],
    base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
)

def model_quality_filter(text: str) -> bool:
    """调用大模型判断文本是否包含有效安全知识"""
    prompt = f"""你是一名网络安全数据质量评估专家。请判断以下文本是否包含有效的网络安全知识。

判断标准:
1. 文本是否包含实质性的安全技术内容(如漏洞分析、攻防技术、安全规范等)
2. 文本是否有明确的技术价值,而非广告、导航、无意义内容
3. 文本是否适合作为大模型预训练语料

请仅返回一个数字:1表示通过(有有效知识),0表示不通过(无有效知识)。

待评估文本:
---
{text}
---
请判断(仅返回0或1):"""

    completion = client.chat.completions.create(
        model="qwen-plus",
        messages=[{"role": "user", "content": prompt}],
    )
    result = completion.choices[0].message.content.strip()
    return result.startswith("1")

注意,模型过滤又慢又贵,别全量硬上。正确姿势是:规则先过滤一轮,剩下拿不准的才交给模型二判。规则当保安,模型当法官,各司其职。

3.3 隐私脱敏:安全领域的安全课

脱敏不是网络安全语料的专属需求,但安全文章里这个需求最扎眼:真实 IP、域名、邮箱、内网主机名,尤其是事件复盘和应急响应报告,受害单位的家底全在里面。

你要敢原样喂给模型,模型将来回答问题时能直接给你"背"出别人公司的内网地址。安全模型自己先泄密,这说出去,甲方爸爸的脸往哪搁。

日常脱敏也是两段式。先用正则做基础脱敏,覆盖三类最常见的敏感信息:

python 复制代码
def desensitize(text: str) -> str:
    # IP 地址 -> 占位符
    text = re.sub(r"\b(?:\d{1,3}\.){3}\d{1,3}\b", "[IP已脱敏]", text)
    # 邮箱地址 -> 占位符
    text = re.sub(r"[\w.-]+@[\w.-]+\.\w+", "[邮箱已脱敏]", text)
    # 口令类字段(password=xxx 等)
    text = re.sub(r"(password|passwd|pwd)\s*[=:]\s*\S+",
                  r"\1=[已脱敏]", text, flags=re.I)
    return text

但正则不是万能的。叙述句里的"运维人员账号 admin_zhang"、内部系统名,正则根本抓不到------人家藏得比你对象藏私房钱还深。对高敏感的报告类语料,还得让大模型再过一遍:

python 复制代码
from openai import OpenAI

client = OpenAI(
    api_key=os.environ["DASHSCOPE_API_KEY"],
    base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
)

def model_desensitize(text: str) -> str:
    """调用大模型识别并替换敏感实体"""
    prompt = f"""你是一名数据安全脱敏专家。请识别并替换以下文本中的敏感信息。

需要脱敏的敏感信息类型:
1. 人名(如:张三、admin_zhang)-> 替换为 [人名已脱敏]
2. 账号/用户名(如:admin、root、user123)-> 替换为 [账号已脱敏]
3. 内部系统名/主机名(如:server-01、db-master)-> 替换为 [主机名已脱敏]
4. 手机号(如:13800138000)-> 替换为 [手机号已脱敏]
5. 身份证号 -> 替换为 [身份证号已脱敏]
6. 银行卡号 -> 替换为 [银行卡号已脱敏]

注意:
- 保留 CVE 编号、版本号、技术术语等非敏感技术信息
- 仅替换明确的敏感实体,不要过度脱敏
- 直接返回脱敏后的文本,不要添加额外说明

待脱敏文本:
---
{text}
---
请返回脱敏后的文本:"""

    completion = client.chat.completions.create(
        model="qwen3.7-plus",
        messages=[{"role": "user", "content": prompt}],
    )
    return completion.choices[0].message.content.strip()

脱敏完照样要抽检 5%~10%,确认没有漏网之鱼,同时别误伤了正常技术内容。别问为什么又是 5%~10%,问就是经验,就是玄学,就是工程界的黄金比例。

到这儿,三道工序走完:去重、质量过滤、隐私脱敏。Markdown 文档也完成了从"原料"到"有效数据集"的蜕变------就像菜从带泥的萝卜,变成了码得整整齐齐的净菜。

4 总结:两大阶段,五道工序

来,把今天的流水线复盘一遍。全过程两大阶段:

**阶段一:结构化解析。**先用 MinerU 把 PDF、docx 这类异构文档统一解析成 Markdown,再调 Qwen-VL 多模态模型给图片批量生成语义描述、替换掉原始引用。从"异构原料"到"结构化半成品"。

**阶段二:清洗与脱敏。**按"去重 → 质量过滤 → 隐私脱敏"的顺序跑三道工序,去掉重复、滤掉噪声、替换敏感实体。半成品正式转正。

数一数:两大阶段,五道工序。听起来不多?等你真去洗一千份安全报告的时候,你会回来点赞的。

手里有了这份干净的结构化语料库,下一步就是把它转成预训练格式、丢进 LLaMAFactory 做增量预训练。咱们下篇接着唠。

P.S. 无意间发现了一个巨牛的人工智能教程,非常通俗易懂,对AI感兴趣的朋友强烈推荐去看看,传送门https://blog.csdn.net/qq_34419312

相关推荐
ADAI_Bowen2 小时前
2026 年 8 月建筑设计 AI 工具分析:基于几何保真、可控与编辑能力
人工智能·机器学习·计算机视觉
风合星语2 小时前
2026 机器人行业观察(一):现在入局机器人还来得及吗?——机会、门槛与技术人的切入点
网络·人工智能·机器人
jimmyleeee2 小时前
大模型安全之十一:拆解现代 AI 系统的“骨架”:一份安全架构的完整蓝图
人工智能·安全·安全架构
一切皆是因缘际会2 小时前
事件驱动计算
人工智能
程序员无隅2 小时前
Codex 实战:用 AI 写运维脚本
运维·人工智能
lauo2 小时前
FDE的终极形态:从“一个人”到“一个网络”
人工智能·分布式
zandy10112 小时前
2026 隐私保护 AI 搜索工具选型指南:从个人防护到 Agent 级基础设施的完整梳理
大数据·人工智能
Token掘金室2 小时前
Function Calling完整调用教程
大数据·数据库·人工智能
Dawson Zhu3 小时前
GraphRAG 原理、适用场景与工程落地:从关系建模视角做一次客观拆解
人工智能·语言模型·架构·aigc·agi