训练 JSONL 不能只检查“文件能被 json.loads 打开”。每行要是一个独立对象,还要检查字段、类型、标签范围、编号重复和重复键。把错误行单独保存,能让数据修正有依据,也能防止少量坏数据悄悄改变训练输入。
下面约定 sample_id、text、label 三个字段,label 只允许整数 0 或 1。这是演示分类协议,不代表所有训练框架的格式;聊天微调、工具调用或其他任务应先采用其真实官方数据协议。

明确“合法 JSON”之外的契约
- 每条记录必须是对象,字段恰好是 sample_id、text、label。
- 编号和文本为非空字符串,合格记录之间编号唯一。
- label 为整数 0 或 1;布尔值 true/false 不能因为 Python 类型关系而被当作整数标签。
- 重复对象键与非标准常量必须拒绝,避免后一个值静默覆盖前一个值。
Python 官方 json 说明指出默认解析会接受重复名称并保留最后一个值,也提供 object_pairs_hook 和 parse_constant 配置。JSONL 则要按行分别调用解析器,不能把整份多对象文件当成一个普通 JSON 对象加载。
逐行校验并保留错误原文
保存为 jsonl_check.py,执行 python jsonl_check.py。示例会覆盖练习目录内三个 demo_ 文件;实际语料请只替换读取路径,不覆盖原文件。
import json
from pathlib import Path
source = Path("demo_records.jsonl")
source.write_text(
'{"sample_id":"s1","text":"示例文本","label":1}\n'
'{"sample_id":"s2","text":"另一个示例","label":true}\n'
'{"sample_id":"s1","text":"重复编号","label":0}\n'
'{"sample_id":"s3","text":"字段被覆盖","label":0,"label":1}\n'
'not-json\n', encoding="utf-8"
)
def unique_keys(pairs):
obj = {}
for key, value in pairs:
if key in obj:
raise ValueError(f"重复键:{key}")
obj[key] = value
return obj
def reject_constant(value):
raise ValueError(f"不允许的 JSON 常量:{value}")
seen = set()
passed = failed = 0
with source.open(encoding="utf-8") as incoming, \
Path("demo_valid.jsonl").open("w", encoding="utf-8") as good, \
Path("demo_errors.jsonl").open("w", encoding="utf-8") as bad:
for line_no, raw in enumerate(incoming, start=1):
try:
obj = json.loads(raw, object_pairs_hook=unique_keys,
parse_constant=reject_constant)
if not isinstance(obj, dict) or set(obj) != {"sample_id", "text", "label"}:
raise ValueError("对象字段不符合协议")
if not isinstance(obj["sample_id"], str) or not obj["sample_id"].strip():
raise ValueError("编号必须为非空字符串")
if not isinstance(obj["text"], str) or not obj["text"].strip():
raise ValueError("文本必须为非空字符串")
if type(obj["label"]) is not int or obj["label"] not in {0, 1}:
raise ValueError("标签必须为整数 0 或 1")
if obj["sample_id"] in seen:
raise ValueError("编号与已通过记录重复")
seen.add(obj["sample_id"])
good.write(json.dumps(obj, ensure_ascii=False, allow_nan=False) + "\n")
passed += 1
except (ValueError, TypeError) as error:
bad.write(json.dumps({"line_no": line_no, "reason": str(error),
"raw": raw.rstrip("\r\n")}, ensure_ascii=False) + "\n")
failed += 1
print({"passed": passed, "failed": failed})
assert passed == 1 and failed == 4
核对合格文件和四条错误
固定示例应得到 1 条合格记录和 4 条错误:布尔标签、与合格记录重复的编号、重复 label 键、非 JSON 文本。错误文件应保留原始行号、原因与原文,不能只输出“校验失败”后丢掉位置。
编号重复检查只针对已通过的记录,失败行仍全部保存在错误文件。如果业务要求“任何原始行出现相同编号都要整组复核”,应另建原始编号审计表;本文规则不能替代这个更严格的冲突策略。
从校验结果进入训练准备
- 逐条修正 demo_errors 所示类型的问题,再重新生成合格集;保留修正前后与来源映射。
- 核对类别分布与文本来源。结构合法不代表标签标对,也不代表数据覆盖真实应用。
- 按实体、时间或任务需要划分训练与评测数据,不能因为合格 JSONL 生成了就认为没有数据泄漏。
- 合格记录数为零时停止后续训练;有错误时按自己的验收规则处理,不静默把失败部分从报告中省略。
适用范围与规模限制
示例流式读取文本行,但 seen 集合会随合格编号增大。百万级编号可改为数据库唯一约束或外部审计方案,并保持同一判定规则。本文没有训练模型,也没有用这些虚构样本评估业务效果。
实际 JSONL 可能含个人或内部资料,错误文件同样保留原文,应遵守与来源数据一致的访问范围。读者下一步是先用固定五行样本核对错误原因,再改成自己的真实任务协议。
Ai菜鸟网。发布者:AI小管家,转载请注明出处:https://www.alyyhw.com/31987.html