数据从 10 万涨到 100 万,模型为什么反而更差?Embedding 数据治理实战
训练句子嵌入模型时,“再加一些数据”是最自然的优化方向。但数据量增长并不等于有效信息增长:同一条点击日志复制十次,会放大热门样本;把同一文档的相邻 chunk 随机分到训练集和测试集,会让指标虚高;把真正相关的文本挖成 hard negative,则会直接制造冲突训练信号。
因此,数据工作的目标不是把文件洗得整齐,而是回答四个问题:
- 样本表达的相关性是否符合业务任务?
- 训练、验证和测试之间是否真正隔离?
- 数据分布是否覆盖线上核心与长尾场景?
- 每次数据变化是否可追踪、可复现、可回滚?
本文以检索型 embedding 为主,给出样本设计、清洗、去重、分组切分、评估集构建和自动审计的完整做法。
一、先定义样本,否则所有统计都可能失真
“我们有 100 万条数据”这句话缺少单位。100 万次点击、100 万个唯一 query、100 万个 query-document 对,价值完全不同。
常见任务的样本单位如下:
| 任务 | 基本样本 | 关系含义 |
|---|---|---|
| 语义相似度 | sentence_a, sentence_b, score |
两段文本语义有多接近 |
| 二分类匹配 | query, document, label |
文档是否与查询相关 |
| 对比检索 | query, positive |
文档能否满足查询 |
| 三元组检索 | query, positive, negative |
正文档应比负文档更匹配 |
| 向量建库 | document_id, chunk_id, text, metadata |
可检索证据及其来源 |
推荐先把复杂来源统一成可审计的 pair 形式:
1 | { |
三元组可展开为一条 label=1 的 positive pair 和一条 label=0 的 negative pair。统一表示便于检查重复、标签冲突和来源分布;训练前再按 loss 所需格式组装。
leakage_group 为什么不能省
query_id 不一定能识别同义改写,document_id 也不一定能识别同一原文的多个 chunk。leakage_group 表示不能跨数据集拆开的业务实体,例如:
- 同一个搜索意图的所有改写;
- 同一原始文档及其所有 chunk;
- 同一用户会话或工单;
- 同一商品族的标题变体;
- 同一个时间窗口内高度关联的事件。
这个字段通常需要在上游构造,而不是靠文本哈希临时猜测。
二、相关不是“关键词相似”,而是符合标注问题
对 query“缓存穿透怎样处理”,以下关系不同:
1 | 强相关:使用布隆过滤器或缓存空值。 |
如果任务是问答召回,“定义”可能部分相关但不足以回答;如果任务是主题聚类,它又可能是正确正例。标注规范必须明确任务,而不能只写“相似/不相似”。
一个实用的三级规范是:
2:可以直接支撑回答或完成目标;1:提供相关背景,但不能独立满足 query;0:不能满足 query,包括看似相关的干扰项。
训练时可按 loss 映射标签,评估时保留分级相关性以计算 NDCG。不要过早把所有非 2 样本压成同一种负例,否则会丢失业务信息。
三、正样本为什么会有噪声
点击不是天然正样本
用户点击可能因为:
- 结果排在第一位;
- 标题吸引人但内容不相关;
- 用户误触;
- 没有更好的结果;
- 点击后立即返回。
点击日志适合弱监督,但应结合曝光位置、停留、后续改写、转化或满意度信号。没有曝光的文档也不能直接标成负样本,因为用户根本没看到它。
结构化关系也会过期
FAQ 问答、商品类目和文档目录通常质量较高,但映射可能已经失效。样本必须保留来源和时间,才能在规则变化时定位并撤回。
生成式改写会放大模板
LLM 能快速生成 query 改写和候选负例,但容易出现语义漂移、事实错误或相似句式重复。生成数据必须抽样复核,并与真实 query 分布分别统计,不能因为便宜就占据训练集大多数。
四、负样本不是越难越好,首先要“真负”
负样本通常分三层:
- 随机负样本:帮助模型学习基本主题边界,但很快变得过于简单;
- 同域负样本:属于同一主题或类目,但不能满足 query;
- hard negative:被 BM25、旧 embedding 或 reranker 排得很高,人工确认后仍不相关。
例如:
1 | { |
这个 hard negative 共享 json 和相近函数名,却没有回答输入区别。
false negative 比简单负样本更危险
语料中可能存在多个正确文档。如果只把已点击文档标正,其他未点击但能回答 query 的文档就可能被当作 hard negative。挖难例时应:
- 排除已知正样本和同源近重复;
- 使用较强模型或规则初筛;
- 对高分候选人工复核;
- 允许一个 query 对应多个 relevant document;
- 记录负例生成器及版本。
模型越强,挖到的候选越“像正例”,误标风险也越高。
五、清洗不是越彻底越好
比较安全的基础清洗包括:
- Unicode 规范化;
- 合并无语义的连续空白;
- 去除明确的页眉、页脚和模板噪声;
- 过滤空文本、解析失败和不可用编码;
- 在合规要求下脱敏敏感信息。
以下操作不能默认执行:
- 全部转小写:可能破坏类名、基因名或大小写敏感标识;
- 删除标点:可能破坏
C++、::、版本号和错误码; - 统一所有数字:
HTTP 404与HTTP 500不能变成同一句; - 删除 URL:路径、域名和参数可能是检索关键信号;
- 简繁转换:应与业务语种、模型和展示需求一起决定。
清洗前后都应保留可追踪标识。若偏移、页码或引用需要回到原文,还要保存规范化文本与原文的映射。
六、完全重复、近重复与语义重复不是一回事
完全重复
规范化后文本完全相同,可用哈希高效发现。重复会让高频样本获得额外权重,也可能跨集合泄漏。
近重复
只差页脚、日期或少量字符,可用 n-gram Jaccard、MinHash、SimHash 或专门去重模型筛查。阈值必须抽样评估:过严会漏掉模板变体,过松会误删真正不同的条款。
语义重复
不同措辞表达相同内容。embedding 可用于发现候选,但不能按一个全局相似度阈值自动删除,因为“相似”和“重复”并非同义。
去重前先定义保留策略:保留最新、权威、信息最完整或具有有效业务反馈的样本。否则删除哪一条会变成不可复现的偶然行为。
七、可运行的数据审计脚本
下面的 Python 3.9+ 程序只使用标准库。它演示:
- 可解释的文本规范化;
- 稳定、按组的数据切分;
- 完全重复和标签冲突检测;
- 来源、标签、集合和长度分布统计;
- 质量门禁断言。
它是审计 baseline,不包含近重复模型、PII 检测或特定业务规则。
1 | from __future__ import annotations |
代码中的三个设计点
第一,使用 SHA-256 而不是 Python 内置 hash() 分组。内置哈希默认可能在不同进程间随机化,不适合生成长期稳定的数据集合。
第二,按 leakage_group 切分,而不是按行随机。以后新增同组样本时,它仍会进入相同集合。
第三,冲突键使用规范化后的 (query, document),能发现同一 pair 同时被标正负。真实项目还应结合 canonical query ID、原始文档 ID 和近重复簇检查,因为改写文本不一定完全相同。
为什么示例没有自动删除坏样本
冲突不代表某一条必然错误。人工标注、点击和挖掘器可能反映了不同相关性定义。审计器应先报告来源,再由规则或人工决定:修标签、降权、保留多级相关度,还是删除样本。自动“保留最新一条”会把数据问题藏起来。
八、切分比例不如切分边界重要
8:1:1、9:0.5:0.5 都可以作为容量起点,但无法保证测试可信。以下对象一旦跨集合,就可能泄漏:
- 同一 query 的改写;
- 同一文档的不同 chunk;
- 同一 FAQ 的标题与正文;
- 同一用户会话;
- 同一模板生成的近重复文本;
- 时间上紧邻且共享结果的事件。
随机切分还是时间切分
随机分组切分适合评估同分布泛化;时间切分更接近“用过去预测未来”,能暴露新术语和分布变化。搜索与知识库项目可同时保留:
- 稳定分组测试集:横向比较模型;
- 滚动时间测试集:观察最新分布;
- bad case 回归集:保证高价值错误不复发。
测试集应尽量冻结。若反复根据测试结果调参,它就逐渐变成验证集,需要另留最终盲测集。
九、数据分布至少要按五个维度观察
1. 长度
分别统计 query 与 document 的 token 长度 P50、P95、P99 和超长截断率。字符长度只能做快速诊断,最终应使用与模型相同的 tokenizer。
2. 标签与每个 query 的候选数
不仅看全局正负比例,还要看每个 query 有多少 positive、negative。某些 query 有上百条负例而另一些只有一条,会改变训练权重。
3. 来源
人工、点击、结构化映射、LLM 生成、BM25 挖掘和旧模型挖掘应分别统计。不同来源的噪声不能用一个平均准确率掩盖。
4. 热门与长尾
按 query 频次分桶,检查训练样本和线上流量的占比。热门 query 重复采样过多,可能让模型记住少数表达;完全均匀采样又可能忽视真实业务权重。
5. 场景切片
至少区分 FAQ、长文档、代码、精确术语、否定表达、数字/版本敏感和多语言等场景。模型总分只有结合切片才可解释。
十、怎样设计真正有用的评估集
检索评估集应包含:
1 | query_id -> query text |
只保存孤立的正负 pair 无法完整模拟在真实 corpus 中的排名。负例也不应只来自人工挑选的几条,因为线上文档库里的其他文本同样是候选。
Sentence Transformers 的 InformationRetrievalEvaluator 接受 queries、corpus 和 relevant_docs,可计算 Recall、MRR、NDCG 等指标。接口与指标用法见句子嵌入模型开发与上线。
标准集、时间集和回归集各司其职
| 数据集 | 主要目的 | 更新策略 |
|---|---|---|
| 标准评估集 | 稳定比较不同实验 | 严格版本化,少更新 |
| 时间外测试集 | 评估新分布泛化 | 按周期滚动 |
| bad case 回归集 | 防止关键错误复发 | 持续追加,保留来源 |
| 最终盲测集 | 做阶段性决策 | 限制访问和调参次数 |
bad case 集不应替代标准集。若只训练和评估困难样本,模型可能修好投诉场景,却损害普通流量。
十一、标注质量怎样量化
人工抽检要覆盖随机样本,也要对高风险切片加权:
- 来源首次接入的数据;
- 模型与标签严重冲突的数据;
- 高相似 hard negative;
- 高流量 query;
- 新术语和长尾场景。
多人标注时应记录一致率,并定期复盘分歧。简单准确率可能被类别不平衡误导,可同时看每类混淆矩阵或适合任务的标注一致性指标。数字不能代替规则讨论:两位标注员若对“部分相关”定义不同,一致率下降只是症状。
黄金样本应带标注规范版本。规则变化后,不要悄悄覆盖旧标签,而应迁移或重新标注并生成新数据版本。
十二、数据质量门禁应该检查什么
每次生成训练快照时,至少自动输出并与上一版本比较:
- 总行数、唯一 query 和唯一 document 数;
- 空值、解析失败和非有限标签;
- 完全重复率、近重复候选率;
- 同 pair 标签冲突率;
- 跨 split 的 group、文档和近重复泄漏;
- 各标签、来源、语言、场景和时间占比;
- query/document token 长度分位数与截断率;
- 每个 query 的正负样本数量;
- hard negative 的人工抽检通过率。
门禁阈值不应凭空设定。例如“重复率必须为 0”未必合理,因为同一 query 可合法对应多个来源相同的正例;更稳妥的是先建立上一稳定版本的分布,再对异常变化报警,并对必须为零的硬错误单独失败。
十三、数据版本要能复现一场训练
“使用最新数据”不是版本。一次训练至少要绑定:
1 | { |
还应保存代码提交、配置、统计报告和不含敏感信息的样本谱系。原始日志可能受隐私和保留期限约束,版本管理不等于无限复制敏感数据;应遵守项目的数据治理策略。
十四、指标变差时,按什么顺序排查
训练 loss 很低,测试指标异常高
先查跨集合重复、同文档 chunk、同义 query 和模板泄漏。不要急着庆祝模型能力提升。
新数据加入后 Recall 下降
对比新旧快照的来源、场景、长度和标签分布;抽查新增 hard negative 是否含假负例;检查热门 query 是否被重复放大。
热门 query 好,长尾很差
按频次分桶查看训练占比和测试 Recall。调整采样或补充长尾并不意味着把线上真实流量权重完全抹平,应通过业务目标决定权衡。
离线好,线上差
检查评估 query 是否来自真实分布、时间是否过旧、线上预处理与 corpus 快照是否一致,以及点击指标是否受展示和排序影响。
hard negative 越加越差
抽样复核“最难”的负例。它们很可能是未标注正例、部分相关文本或缺少上下文的 chunk。
每次训练波动很大
固定数据快照、切分 seed 和评估集;统计每个 query 的候选数;再检查训练随机性。没有可复现数据,模型侧的 seed 也救不了实验。
十五、一条务实的落地路线
- 写清任务、样本单位和分级相关性规范;
- 保留 query/document 原始 ID、来源、时间和 leakage group;
- 做克制、可追踪的文本规范化;
- 先建立完全重复、冲突和分组泄漏门禁;
- 统计长度、标签、来源、热门/长尾和场景切片;
- 用随机负例建立基线,再逐步引入经复核的同域与 hard negative;
- 建立稳定标准集、滚动时间集和 bad case 回归集;
- 固定快照和版本清单后再训练;
- 模型失败时将 bad case 回流为可复现标注;
- 每次数据变更都与上一稳定版本做差异报告。
这套流程不要求一开始就部署复杂数据平台。一个可重复运行的脚本、一份明确 schema、一组质量断言和不可变快照,已经比没有边界的“最新 CSV”可靠得多。
总结
Embedding 数据治理不是删空行、去乱码这么简单。它要保证正例真能满足 query,负例足够有区分度且确实为负,相关实体不会跨集合泄漏,评估集能代表真实业务,每次变化还能被追溯。
数据规模决定模型看过多少样本,数据关系决定模型学到什么,评估设计决定你能否看清它学得对不对。
可以用一句话记住:
先让数据可解释、可审计、可复现,再讨论把模型训练得更大或更久。