问题与目标
SFT 学到的是训练序列中的 Token 关系。原始 JSON 看起来正确,并不代表模型看到的模板、截断位置和 Label 正确。本篇用自创设备数据完成校验、去重、稳定划分,并解释怎样只训练助手回答。
核心概念

常见数据契约有三种:
json
{"instruction":"判断设备风险","input":"AX-3 离线 12 分钟","output":"高风险"}
{"prompt":"判断设备风险:AX-3 离线 12 分钟","completion":"高风险"}
{"messages":[{"role":"user","content":"AX-3 离线 12 分钟"},{"role":"assistant","content":"高风险"}]}
训练前还要决定:系统消息是否固定、多轮历史是否都参与 Loss、最大长度如何截断、重复设备是否跨集合泄漏。对话模型应使用其原生 Chat Template,不要手写看似相似的特殊 Token。
可运行实现
python
import hashlib
import json
REQUIRED_ROLES = ("user", "assistant")
def validate(sample: dict) -> None:
messages = sample.get("messages")
if not isinstance(messages, list) or len(messages) < 2:
raise ValueError("messages 至少包含一轮用户与助手")
roles = tuple(item.get("role") for item in messages[-2:])
if roles != REQUIRED_ROLES:
raise ValueError(f"最后两条消息角色错误:{roles}")
if not all(isinstance(item.get("content"), str) and item["content"].strip() for item in messages):
raise ValueError("content 必须是非空字符串")
def fingerprint(sample: dict) -> str:
normalized = json.dumps(sample["messages"], ensure_ascii=False, sort_keys=True)
return hashlib.sha256(normalized.encode()).hexdigest()
def split(samples: list[dict]) -> dict[str, list[dict]]:
unique = {}
for sample in samples:
validate(sample)
unique[fingerprint(sample)] = sample
result = {"train": [], "validation": [], "test": []}
for key, sample in sorted(unique.items()):
bucket = int(key[:8], 16) % 10
name = "test" if bucket == 0 else "validation" if bucket == 1 else "train"
result[name].append(sample)
return result
samples = [
{"messages": [{"role": "user", "content": "AX-3 离线 12 分钟"},
{"role": "assistant", "content": '{"risk":"high"}'}]},
{"messages": [{"role": "user", "content": "AX-5 在线,温度 42℃"},
{"role": "assistant", "content": '{"risk":"low"}'}]},
]
print({name: len(rows) for name, rows in split(samples).items()})
真实划分应按用户、设备或文档来源分组,而不是只按行随机拆分,避免同一事件的改写同时出现在训练集与测试集。
在当前 TRL 中,对话数据可通过 SFTConfig(assistant_only_loss=True) 只计算助手消息的 Loss,但 Chat Template 必须能生成助手区域掩码。训练前抽查渲染文本、input_ids、labels,确认用户 Token 对应 -100,而不是盲信配置名。
常见问题与排查
训练集和测试集问题不同,但答案来自同一记录
这仍然是数据泄漏。按来源实体或时间窗口分组划分,并对近似文本做相似去重。
截断后只剩问题,没有答案
统计渲染后的 Token 长度与截断比例。保留答案尾部或过滤超长样本,不要让大量样本产生零有效 Label。
所有消息都参与 Loss
模型会学习复述系统和用户文本。检查 Collator 或 Trainer 的 Label Mask,并用一个样本打印 Token—Label 对齐。
直接复制公开数据集训练
检查许可、隐私、内容质量和目标任务一致性。公开数据可以做格式参考,不应未经审计混入业务模型。
小结
SFT 的第一份可交付物不是训练脚本,而是经过验证的数据契约、划分清单和 Token—Label 样例。数据怎样进入 Loss,决定模型最终学习什么。
许可协议:CC BY-NC 4.0
更新于 1 小时前
觉得文章有帮助?点个赞吧!
0 条评论


