筛除 AI 训练样本时,先给每条记录标出拒绝原因和来源位置,再生成可用表与拒绝表,能避免“少了几行却不知道为什么”。这里用 Hugging Face Datasets 实现空文本、缺失文本和越界标签的筛选,同时保留原始 ID。
先固定有效样本规则
本例是二分类文本数据:text 必须为非空文本,label 只能取 0 或 1。空格组成的文本、None 文本、标签 2 和缺失标签分别进入拒绝表。这里的标签含义只是演示;真实任务的许可、标注准确性、隐私和重复样本需要另行判断。

示例于 2026-10-01 在 Windows、Python 3.11.15、Datasets 4.8.3 环境实际执行通过;数据全部人为构造,只验证数据处理接口,没有训练模型或测量业务效果。安装可参考 Datasets 官方安装说明。先在新的练习目录建立环境:
python -m venv .venv
# Windows 使用 .venv\Scripts\python.exe;macOS/Linux 使用 .venv/bin/python
.venv\Scripts\python.exe -m pip install "datasets==4.8.3"
下文运行命令按 Windows 写法给出。使用 macOS 或 Linux 时,把解释器路径换为 .venv/bin/python;后续版本请先用同一份玩具数据复核,再替换生产数据。
给记录附上原因,再筛选
官方处理文档说明 map() 用来添加或修改字段,filter() 返回符合条件的记录。接口参考说明 with_indices=True 可把当前位置传给映射函数。将下面代码保存为 filter_demo.py,再运行 .venv\Scripts\python.exe filter_demo.py。
from collections import Counter
from pathlib import Path
from datasets import Dataset
rows = [
{'id': 101, 'text': '回复清楚', 'label': 1},
{'id': 102, 'text': ' ', 'label': 0},
{'id': 103, 'text': None, 'label': 0},
{'id': 104, 'text': '标签越界', 'label': 2},
{'id': 105, 'text': '标签缺失', 'label': None},
{'id': 106, 'text': '等待较长', 'label': 0},
]
ds = Dataset.from_list(rows)
def audit(row, index):
if row['text'] is None:
reason = 'missing_text'
elif not row['text'].strip():
reason = 'blank_text'
elif row['label'] not in (0, 1):
reason = 'invalid_label'
else:
reason = ''
return {'source_row': index + 1, 'reject_reason': reason}
audited = ds.map(audit, with_indices=True)
accepted = audited.filter(lambda row: row['reject_reason'] == '')
rejected = audited.filter(lambda row: row['reject_reason'] != '')
assert list(accepted['id']) == [101, 106]
assert list(rejected['source_row']) == [2, 3, 4, 5]
assert len(accepted) + len(rejected) == len(ds)
counts = dict(Counter(rejected['reject_reason']))
assert counts == {'blank_text': 1, 'missing_text': 1, 'invalid_label': 2}
target = Path('rejected-demo.jsonl')
if target.exists():
raise FileExistsError('请换到新的练习目录,避免覆盖已有拒绝表')
rejected.to_json(str(target), force_ascii=False)
print('accepted_ids:', list(accepted['id']))
print('rejected_rows:', list(rejected['source_row']))
print('reasons:', counts)
audit() 按顺序检查缺失文本、空白文本和无效标签。同一条记录若同时触发多个规则,reject_reason 只记录最先命中的原因。因此原因统计表示主拒绝原因,不能把它解释成所有问题出现次数;需要多原因统计时应返回原因列表,并按列表分别计数。
source_row 是当前 Dataset 中从 1 开始的记录位置,不一定等于原 CSV 的物理行号。CSV 表头、多行字段、先前筛选都会改变两者关系;生产流程应在首次读取时保留源文件名、原始行标识或业务 ID,不能处理到一半才补来源。
核对行数与拒绝清单
这六条演示记录在实测中保留 ID 101、106,拒绝位置为 2、3、4、5。拒绝原因计数为 blank_text: 1、missing_text: 1、invalid_label: 2。代码里的断言会核对保留表与拒绝表行数之和等于输入行数。
打开 rejected-demo.jsonl,逐条检查 source_row、id 和 reject_reason 是否指向原始输入。实际数据还应抽查接受表,确认标签 0 没有被误当成 False 删除。这里明确使用 label in (0, 1),允许 0。
先在自己的脱敏样本上保留这三项检查:输入行数是否守恒、各拒绝原因是否符合预先约定、接受表是否仍有空文本或非法标签。确认后才把接受表送入后续去重、切分或训练。
遇到这些情况先停止
- 字段名不存在:先打印 ds.column_names,再按真实列名改函数;不要给缺失列随意补值。
- 源文件解析失败或列里混杂不可兼容类型:错误可能发生在 Dataset 创建前,先修复 JSON、CSV 或字段类型,filter() 不能替代文件解析。
- 接受表为空或拒绝比例异常:检查标签编码、单位和过滤规则;不能为了让训练继续而静默放宽条件。
- 拒绝文件已存在:示例主动报 FileExistsError,换一个新目录再运行,避免覆盖之前的审计记录。
map() 和 filter() 的结果需要接住返回值;本例保留 ds 与 audited,既没有原地删掉输入,也没有替读者判断拒绝记录该怎样修复。
Ai菜鸟网。发布者:AI小管家,转载请注明出处:https://www.alyyhw.com/30279.html