
很多人对数据准备的理解是:拉数据、去空值、格式化、喂给模型。实际项目里真正出问题的是这些:
所以正确姿势不是“清洗一次”,而是把数据当软件资产来管:
数据契约是字段级的约定,写清了,管道和校验才有依据。
数据集:客服对话样本 v1
字段:
sample_id: string, 唯一
text: string, 1-2000 字, 不含手机号/身份证
label: enum[退款, 物流, 账号, 其他]
source: enum[app, web, phone]
created_at: date, 不晚于今天
规则:
- sample_id 不可重复
- text 去首尾空白后非空
- label 缺失率 < 1%,缺失样本进隔离区而非丢弃
- 每批次记录行数、来源分布、label 分布契约里最有价值的两条是缺失率阈值和隔离区。坏数据不要直接删,删了就永远不知道坏在哪;应该隔离并统计。
数据校验的第一原则:能自动拦住的,绝不留到训练后
from dataclasses import dataclass
@dataclass
class Row:
sample_id: str
text: str
label: str
source: str
LABELS = {"退款", "物流", "账号", "其他"}
SOURCES = {"app", "web", "phone"}
def validate(rows: list[Row]) -> tuple[list[Row], list[dict]]:
ok, bad, seen = [], [], set()
for r in rows:
errs = []
if not r.sample_id or r.sample_id in seen:
errs.append("id缺失或重复")
if not r.text.strip() or len(r.text) > 2000:
errs.append("text为空或超长")
if r.label not in LABELS:
errs.append("label非法")
if r.source not in SOURCES:
errs.append("source非法")
seen.add(r.sample_id)
(bad.append({"row": r, "errors": errs}) if errs else ok.append(r))
return ok, bad这段代码不长,但它把“什么算合格”从口头约定变成了可执行规则。质量规则应该是版本化的代码,而不是某个人的经验。
训练集和测试集重叠,是让指标虚高最常见的原因。而重叠往往来自近重复样本:同一段话改了几个字、加了标点、换了空格。
最小可行做法是归一化 + 哈希去重:
import hashlib, re
def normalize(text: str) -> str:
t = re.sub(r"\s+", "", text.lower())
t = re.sub(r"[^\w\u4e00-\u9fff]", "", t)
return t
def dedup(rows: list[Row]) -> list[Row]:
seen, out = set(), []
for r in rows:
h = hashlib.md5(normalize(r.text).encode()).hexdigest()
if h not in seen:
seen.add(h)
out.append(r)
return out严格去重之后再切分数据集,顺序不能反。反了就会泄漏:先去重再切分,测试集里就不会有训练集的近重复。
“这个模型是用哪版数据训的”必须能回答。最小方案是给每个数据集生成一个清单文件,记录哈希、行数、来源和生成时间。
import json, hashlib
from datetime import datetime, timezone
def make_manifest(paths: list[str], out: str = "dataset_manifest.json"):
files = []
for p in paths:
with open(p, "rb") as f:
files.append({"path": p, "sha256": hashlib.sha256(f.read()).hexdigest()})
manifest = {
"created_at": datetime.now(timezone.utc).isoformat(),
"files": files,
"total_rows": sum(1 for _ in files), # 示例占位,实际按行统计
}
with open(out, "w") as f:
json.dump(manifest, f, ensure_ascii=False, indent=2)清单不用复杂,关键是每次训练都绑定一个 manifest。出了问题时,你能回答“当时用的到底是哪批数据、有没有被改过”。
数据管道最常见的运维事故是:中途失败后重跑,数据被写了两遍。
解决原则很朴素:每一步都可重跑,输出由输入决定,重复执行结果不变。
具体做法:
丢弃原因统计是被严重低估的指标。它突然升高,通常意味着上游数据源变了。
如果是需要人工或模型标注的数据,规则和指南必须版本化:
AI 可以辅助预标注、生成候选,但指南的边界定义必须人定。因为“这句话算不算退款诉求”,是业务判断,不是模型判断。
数据工程的合规不是上线前补的,是采集时就要设计的:
最后一条最容易被忽略。如果数据没有血缘,用户要求删除时你根本找不到它散落在哪。
AI 很适合帮你做这些:
一个实用的审查提示词:
请审查下面数据管道代码,只输出问题表格:
问题类型 | 位置 | 风险说明 | 修改建议
重点检查:是否幂等、失败重跑是否重复写入、是否有数据泄漏、
是否记录丢弃行数和原因、是否有未脱敏字段。
不要重写代码。但契约怎么定、缺失率阈值多少、什么算边界样本,这些必须人来做。AI 没有你的业务判断。
AI 数据工程的正确姿势,不是“清洗一下丢给模型”,而是把数据当软件资产:契约先行、校验可执行、去重先于切分、版本与血缘可追、管道幂等可重跑、标注规则版本化、合规前置设计。
代码可以很少,但校验不能省,版本不能丢,幂等不能破。记住一句话:模型决定下限,数据决定上限;没有可复现的数据管道,就没有可信的模型结论。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。