用Python标准库按提示词分组划分数据:让同组样本留在同一个集合

举报
yd_243677465 发表于 2026/11/01 22:21:02 2026/11/01
【摘要】 用Python标准库按instruction与input生成稳定分组,比较固定哈希划分与逐行随机划分,验证同组同侧、顺序无关和边界比例,并说明近重复、关联样本与比例偏差的限制。

从格式检查走到数据划分

JSONL能够正常解析,说明数据过了格式这一关;准备训练集和验证集时,还需要考虑样本之间的关系。例如同一个问题有多个答案版本,如果直接逐行随机划分,它们可能分别进入训练集和验证集。这样评估时就可能遇到训练中见过的问题,结果不容易解释。

本文给出一个小型分组划分实现:用instruction和input组成提示词分组键,再通过固定规则将整组分配给train或validation。代码只依赖Python标准库。本次在本地Python 3.11中用8条自建记录运行验证,没有上传业务数据,也没有据此声称模型效果提升。

先把分组规则讲清楚

这里把instruction和input相同的记录归为一组,output不参与分组。原因是同一道问题的不同答案仍应保持在同一侧。缺少input时按空字符串处理;显式传入null则报错,避免把缺失与错误值混为一谈。

为了处理常见的文本表示差异,示例统一CRLF、CR换行为LF,执行Unicode NFC规范化,并去掉字段首尾空白。它不会合并内部连续空格、改变大小写、翻译文字或识别语义近似问题。这些选择都是本工具的规则,不是任何云平台的数据规范。对于代码、诗歌、缩进敏感文本,首尾空白本身也可能有意义,此时应调整normalize函数,重新生成整个划分版本。

组键不能简单写成instruction + input。否则“a”和“bc”与“ab”和“c”会拼接出相同字符串。代码使用JSON数组序列化保留两个字段的边界,再把固定版本标记salt和组键一起编码后计算SHA-256。

完整实现

保存为group_split.py。plan返回分配结果和原始记录,不写入文件、不修改输入记录,也不调用任何外部服务。

"""Deterministic prompt grouping; no network and no file mutation."""
import hashlib
import json
import unicodedata


def normalize(value):
    if not isinstance(value, str):
        raise TypeError("instruction and input must be strings")
    value = value.replace("\r\n", "\n").replace("\r", "\n")
    return unicodedata.normalize("NFC", value).strip()


def group_key(record):
    if not isinstance(record, dict):
        raise TypeError("record must be an object")
    instruction = normalize(record["instruction"])
    if not instruction:
        raise ValueError("instruction must not be empty")
    return json.dumps([instruction, normalize(record.get("input", ""))],
                      ensure_ascii=False, separators=(",", ":"))


def assign(record, validation_percent=20, salt="split-v1"):
    if type(validation_percent) is not int or not 0 <= validation_percent <= 100:
        raise ValueError("validation_percent must be an integer in 0..100")
    payload = json.dumps([salt, group_key(record)], ensure_ascii=False,
                         separators=(",", ":")).encode("utf-8")
    digest = hashlib.sha256(payload).digest()
    bucket = int.from_bytes(digest[:8], "big") % 100
    return "validation" if bucket < validation_percent else "train"


def plan(records, validation_percent=20, salt="split-v1"):
    return [(assign(r, validation_percent, salt), r) for r in records]

validation_percent为0到100之间的整数。把摘要前8字节转换为整数后映射到100个桶,小于阈值的组进入validation。salt在这里是公开的划分版本标记,不是密码。为了复现结果,应记录salt、分组字段、规范化规则、代码版本及输入数据版本。不要使用Python内置hash作为跨进程稳定分配的依据。

同一规则下,记录重新排序不会改变其所属集合;新增其他组也不会改变已有组的归属。改变比例、salt或规范化规则则可能改变已有分配,因此这些参数应和实验记录一起保存。

最小用法与检查方法

from group_split import plan, group_key

rows = [
    {"instruction": "Summarize", "input": "A", "output": "one"},
    {"instruction": "Summarize", "input": "A", "output": "another"},
    {"instruction": "Classify", "input": "B", "output": "six"},
]
assigned = plan(rows, validation_percent=20, salt="split-v1")
train_keys = {group_key(r) for label, r in assigned if label == "train"}
valid_keys = {group_key(r) for label, r in assigned if label == "validation"}
assert train_keys.isdisjoint(valid_keys)
for label, record in assigned:
    print(label, record["instruction"])

如果要处理JSONL,可以先逐行解析并完成字段校验,再把记录交给assign。本文的plan为了便于演示会返回一个列表,空间开销随记录数增加;大文件可逐条调用assign,分别写到新建的输出文件。正式导出时还应考虑出错后的临时文件清理和原子发布,不能把只写到一半的文件当作完整训练输入。

本次自建样例验证了什么

8条样例包含:同一提示词的两个不同答案、首尾空白与CRLF差异、NFC等价的两种重音字符表示,以及三个不同提示词。按上述规则形成5个分组。实际输出标签按输入顺序为:

validation, validation, validation, train,
train, train, train, validation

断言确认前3条同组且同侧,第4、5条同组且同侧;倒序输入后每个组的归属不变;新增另一组后原有组归属不变。比例设为0时全部为train,设为100时全部为validation;负数、超过100、浮点数和布尔值均被拒绝。测试还检查了字段拼接歧义、空instruction、非字符串input和输入对象未被修改。

这里4条进入验证集,并不等于程序承诺20%的记录数。阈值作用于组的摘要桶,有限样本和大小不均的分组会让实际记录比例偏离配置值。如果业务必须严格满足数量或标签分布,需要在整组约束下设计额外的分配策略,并保存最终清单。

适用边界

相同提示词分组只能解决明确规则下的一类交叉问题。来自同一篇文档、同一会话或同一对象的不同问题,仍可能被分到两侧;若这些关联是评估关注点,应优先用文档ID、会话ID或对象ID作为分组依据。近重复内容需要另外检测;按时间评估未来表现时,也不适合直接用这种无时间约束的划分代替时间切分。

本示例不检查output是否为有效标注,不处理重复JSON字段,也不替代数据格式预检和人工抽查。计算哈希也不意味着匿名化,尤其是提示词来自一个很小的可猜测集合时。分享实验记录时不要顺带公开原始问题、答案或敏感标识。

可以先做格式校验,再确定业务上的独立分组单位,最后检查两侧组键是否相交,并统计实际条数、标签分布和长短文本分布。这样的记录比只保存一句“按8:2切分”更容易复现。

参考:Python官方文档《hashlib — Secure hashes and message digests》与《json — JSON encoder and decoder》。本文代码与文字由AI辅助编写,示例使用自建数据实测,未包含真实账号、密钥或训练样本。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。