首页 / 文章 / 实用笔记:小模型蒸馏——第二部分:离策略软标签KD算法

实用笔记:小模型蒸馏——第二部分:离策略软标签KD算法

《实用笔记》操作指南:小模型蒸馏——第二部分:离策略软标签KD方法:为采用该方法的团队提供的契约、校验规则及可直接插入的代码片段。

3831 词

可将此内容视为《小模型蒸馏——第二部分:用于0.8B SQL智能体的离线软标签KD方法》中理念面向操作员的重构版本:清晰的阶段划分、有序的代码模块以及可在交接时保留的恢复说明。

简述

将“简述”阶段视为可度量的界面使用效果最佳。在扩大范围之前,先记录一份理想案例、一个失败案例以及回滚说明。 将配置置于应用代码之外。环境文件、密钥存储和功能标志应集中存放于一个位置,以便操作员无需查看整个架构即可进行审计。 为每轮对话和每次会话设定token预算。智能体工具会大量消耗上下文资源;设置上限可避免演示过程变成意外的费用账单。

你想测试的内容

将“你想要实现的功能”视为可度量的对象来处理,其效果最佳。在扩大范围之前,先记录一个成功的案例、一个失败案例以及回滚说明。同时记录正常流程和恢复流程。重试机制、人工审核环节以及死信处理都是产品本身的一部分,而非后续需要补充的内容。为每轮对话和每次会话设定预算额度。智能代理工具会大量消耗上下文资源,设置上限可避免演示过程变成意外的费用账单。

SQL-Agent问题

将“SQL-Agent问题”阶段视为可测量的对象来处理效果最佳。在扩大范围之前,先记录一份理想的执行日志、一个失败案例以及回滚说明。 优先选择小型且可测试的单元,而非庞大的脚本。当某一步骤失败时,故障应能指向单一责任主体,而非复杂的流程链。 为每轮对话和每次会话设定token预算。智能代理工具会大量消耗上下文资源,设置上限可避免演示过程变成意外的费用账单。

WITH vars AS (SELECT COUNT(*) AS vars_id FROM track)
SELECT * FROM track WHERE track_id = vars_id
{"draft":"Need schema first.","output":{"action":"inspect_schema"}}
{"draft":"Test a candidate query.","output":{"action":"run_sql_query","sql":"SELECT ..."}}
{"draft":"Submit corrected SQL.","output":{"action":"submit_sql","sql":["SELECT ..."]}}

训练思路

将“训练思路”阶段视为可度量的框架最为有效。在扩大范围之前,先记录一份优秀的示例、一个失败案例以及回滚说明。 把这一阶段视为输入与经过验证的输出之间的契约。为相关成果命名,明确成功标准,绝不允许默默地仅完成部分工作。 需为每轮及每次会话设定token预算。智能工具往往会过度扩展上下文;设置上限可避免演示过程变成意外的费用账单。

一个简短的教育类代码示例解析

将“A Small Educational Code”阶段视为可度量的对象来使用效果最佳。在扩大范围之前,先记录一份理想的操作流程、一个故障案例以及回滚说明。 在功能结果旁同时记录执行时间以及令牌或查询成本。提前了解成本情况,就能避免从演示环境过渡到共享环境时出现意外账单。 为每轮操作和每次会话设定令牌预算。智能工具往往会大量消耗上下文资源,设置上限可防止演示环境变成令人意外的费用来源。 将“A Small Educational Code”阶段视为可度量的对象来使用效果最佳。在扩大范围之前,先记录一份理想的操作流程、一个故障案例以及回滚说明。 需同时记录正常流程与异常恢复流程。重试机制、人工审核环节以及错误处理措施都是产品本身的一部分,而非后续需要补充的内容。

row = {
    "messages": conversation_before_teacher_decision + [
        {"role": "assistant", "content": canonical_decision_json(teacher_decision)}
    ],
    "teacher_draft": teacher_decision.draft,
    "teacher_action": teacher_decision.output,
}
row = {
    "messages": [..., {"role": "assistant", "content": target_decision_json}],
    "distillation": {
        "probability_model": "Qwen3.5-35B-A3B 8-bit",
        "top_k": 20,
        "row_weight": 1.18,
        "token_scores": [
            {
                "position": 1661,              # full-sequence token position
                "target_token_id": 412,
                "target_logprob": -0.21,
                "top_token_ids": [412, 879, 91],
                "top_logprobs": [-0.21, -2.81, -3.51],
                "top_mass": 0.90,
                "tail_mass": 0.10,
            }
        ],
    },
}
prompt = chat_template(messages[:-1], add_generation_prompt=True)
full = chat_template(messages, add_generation_prompt=False)
prompt_ids = tokenize(prompt)
full_ids = tokenize(full)
target_ids = full_ids[len(prompt_ids):]
prefill_teacher_cache(prompt_ids[:-1])
driver_ids = [prompt_ids[-1]] + target_ids[:-1]
for i, target_id in enumerate(target_ids):
    logits = qwen_35b(driver_ids[i]).logits
    logprobs = log_softmax(logits)
    top_ids, top_logprobs = top_k(logprobs, k=20)
    save_token_score(
        position=len(prompt_ids) + i,
        target_token_id=target_id,
        target_logprob=logprobs[target_id],
        top_token_ids=top_ids,
        top_logprobs=top_logprobs,
        tail_mass=1.0 - sum(exp(top_logprobs)),
    )
labels = full_ids.copy()
labels[: len(prompt_ids)] = -100
hard_ce = cross_entropy(student_logits, labels)
batch = {
    "input_ids": pad(input_ids, pad_id),
    "attention_mask": pad(attention_mask, 0),
    "labels": pad(labels, -100),
    "position_weights": pad(position_weights, 0.0),
    "topk_token_ids": pad(topk_token_ids, [0] * top_k),
    "topk_logprobs": pad(topk_logprobs, [0.0] * top_k),
    "topk_mask": pad(topk_mask, [False] * top_k),
    "tail_probs": pad(tail_probs, 0.0),
}
z = (mean_target_logprob - dataset_mean_logprob) / dataset_logprob_std
row_weight = clip(2 / (1 + exp(-z)), 0.25, 1.75)
loss = row_weight * hard_ce
teacher_top_probs = exp(top_logprobs) * topk_mask
teacher_top_probs *= (1.0 - tail_mass) / sum(teacher_top_probs)
student_logprobs = log_softmax(student_logits_at_target_positions)
student_top_logprobs = gather(student_logprobs, topk_token_ids)
student_tail_prob = 1.0 - sum(exp(student_top_logprobs))
topk_kl = sum(teacher_top_probs * (log(teacher_top_probs) - student_top_logprobs))
tail_kl = tail_mass * (log(tail_mass) - log(student_tail_prob))
loss = hard_ce + topk_kl + tail_kl
student = load_student("unsloth/Qwen3.5-0.8B", max_seq_length=4096)
student = add_lora_adapters(
    student,
    rank=32,
    alpha=32,
    target_modules=[
        "q_proj", "k_proj", "v_proj", "o_proj",
        "gate_proj", "up_proj", "down_proj",
    ],
)
trainer_class = make_trainer_class(Trainer, soft_label_args)
trainer = trainer_class(
    model=student,
    train_dataset=scored_train_examples,
    eval_dataset=scored_validation_examples,
    data_collator=distillation_collator,
    batch_size=1,
    gradient_accumulation_steps=8,
    learning_rate=5e-5,
    epochs=3,
)
trainer.train()
adapter = save_lora_adapter(student)
for task in held_out_eval_tasks:
    state = start_sql_agent_task(task)
    for turn in range(8):
        messages = render_baml_messages(state)
        decision = model_with_adapter.generate_action(
            messages,
            max_new_tokens=512,
            temperature=0.0,
        )
        state = execute_action_and_append_observation(state, decision)
        if state.solved or state.stopped:
            break
    results.append(score_final_submission(state))

实验的运行方式

在“实验的运行方式”阶段,应在修改代码之前明确输入参数、该步骤的负责人以及终止标准。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。 建议使用小型、可测试的单元而非冗长的脚本。当某个步骤失败时,故障应指向单一的责任主体,而非复杂的流程链。 如果后续步骤是代码或工具调用,建议使用具有结构化格式且经过模式验证的输出,而非自由形式的文字描述。

数据集与过滤

在数据集与过滤阶段,应在修改代码之前明确输入内容、该步骤的负责人以及终止标准。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。 将此阶段视为输入与经过验证的输出之间的契约。为相关成果命名,定义成功判定标准,并拒绝默许的半完成状态。 当后续步骤为代码或工具调用时,优先采用具有模式验证的结构化输出,而非自由形式的文本。

训练设置

在训练设置阶段,应在修改代码之前明确输入参数、该步骤的负责人以及终止标准。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。除了功能结果外,还需记录执行时间以及令牌或查询成本。提前了解成本情况,可避免在从演示环境过渡到共享环境时出现意外费用。当下一步操作为编写代码或调用工具时,应优先使用具有结构化格式且经过模式验证的输出,而非自由形式的文字描述。在训练设置阶段,应在修改代码之前明确输入参数、该步骤的负责人以及终止标准。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。需同时记录正常流程和异常恢复流程。重试机制、人工审核环节以及错误处理措施都是产品本身的一部分,而非后续需要补充的内容。

将结果转化为研究问题

在“将结果转化为研究问题”这一阶段,首先需明确相关约定:所需的输入参数、成功标志以及部分失败时的处理方式。这样的清单能确保后续的代码修改保持一致性。 建议采用小型、可测试的单元而非庞大的脚本。当某个步骤出现故障时,故障应指向单一责任模块,而非复杂的流程链。 应对稳定的系统指令和工具结构进行缓存。重复发送相同的开头信息是导致资源浪费的常见原因。

图表背后的精确数字

在处理“舞台背后的精确数值”这一阶段时,首先需写下合同条款:所需的输入参数、成功信号以及部分失败时的处理方式。这样的清单能确保后续的代码修改保持一致性。 将这一阶段视为输入与经过验证的输出之间的契约。为相关成果命名,明确成功判定标准,并杜绝无声的半完成状态。 缓存稳定的系统指令和工具架构。重复发送相同的开头信息是导致资源浪费的常见原因。

故障分析

在进行故障分析阶段时,首先需写下接口规范:所需的输入参数、成功信号以及部分故障时的处理方式。这份清单能确保后续的代码修改保持一致性。 在功能结果旁记录执行时间以及令牌或查询成本。提前明确成本信息,可避免在系统从演示环境过渡到共享环境时出现意外费用。 缓存稳定的系统指令和工具架构。重复发送相同的开头信息是导致资源浪费的常见原因。 在进行故障分析阶段时,首先需写下接口规范:所需的输入参数、成功信号以及部分故障时的处理方式。这份清单能确保后续的代码修改保持一致性。 同时记录正常流程和故障恢复流程。重试机制、人工审核环节以及错误处理方式都是产品本身的组成部分,而非后续需要补充的功能。

硬件与基础设施的经验教训

将“硬件与基础设施经验”阶段视为可衡量的对象来处理效果最佳。在扩大范围之前,先记录一份优秀的操作文档、一个故障案例以及回滚说明。 优先选择小型且可测试的单元,而非庞大的脚本。当某个步骤出现故障时,故障应指向单一责任主体,而非复杂的流程链。 为每轮及每次会话设定预算额度。智能工具会大量消耗上下文资源,设置上限可避免演示变成意外的费用账单。

你学到了什么

“所学内容”阶段若被视为可量化的指标,效果会更好。在扩大范围之前,先记录一份优秀的成果案例、一个失败案例以及回滚说明。 将此阶段视为输入与已验证输出之间的契约。为相关成果命名,明确成功标准,绝不允许出现悄无声息的半完成状态。 需为每轮及每次会话设定token预算。智能工具往往会过度扩展上下文,设置上限可避免演示过程变成意外的费用账单。

结论

将“结论阶段”视为可度量的对象来处理效果最佳。在扩大范围之前,需记录一份理想的操作流程、一个故障案例以及回滚说明。 在功能结果旁同时记录执行时间以及令牌或查询成本。提前了解成本情况,可避免从演示环境过渡到共享环境时出现意外账单。 为每轮操作和每次会话设定令牌预算。智能工具往往会大量消耗上下文资源,设置上限能防止演示过程变成意外收费的源头。 将“结论阶段”视为可度量的对象来处理效果最佳。在扩大范围之前,需记录一份理想的操作流程、一个故障案例以及回滚说明。 需同时记录正常流程与异常恢复流程。重试机制、人工审核环节以及错误处理措施都是产品本身的一部分,而非后续需要补充的内容。

操作检查清单

在处理操作检查清单阶段时,首先写下合同细节:所需输入、成功信号以及部分失败时的处理方式。这样的检查清单能确保后续的代码修改保持一致性。

将配置信息与应用程序代码分开。环境文件、密钥存储和功能标志应集中存放于一个位置,以便操作人员无需查看整个系统结构即可进行审计。

缓存系统中稳定的指令和工具架构。重复发送相同的开头信息是导致资源浪费的常见原因。

保持系统状态结构简洁且具有类型定义。嵌套的数据结构会掩盖哪个节点修改了哪个字段,还会在中断后导致程序无法继续运行。

只要预算允许,就在持续集成过程中使用测试数据而非真实的付费 API 来执行针对关键路径的冒烟测试。

将此阶段视为输入与经过验证的输出之间的契约。为相关成果命名,明确成功标准,并拒绝默许的半完成状态。

在升级整个系统之前,先冻结各版本,为关键流程记录标准输出文本,并确认回滚步骤。共享环境需要设置访问速率限制、租户身份验证机制,以及明确的密钥轮换负责人。与其追求花哨的一次性演示,不如注重扎实的可靠性。

关于284950c427d0的批注:请将服务提供商密钥存放在仓库之外,设定单次会话的令牌上限,并将输出文本与评估用文件一起存储,以便后续更换模型时仍能保持对比性。

在实施强化措施的第0阶段,应在修改代码之前明确输入参数、该步骤的负责人以及结束标准。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。 配置信息应置于应用程序代码之外。环境文件、密钥存储以及功能标志应集中存放于一个位置,这样操作人员无需查看整个系统结构即可进行审计。

强化措施细节0/720:需统计该措施的运行时间、错误类型以及令牌消耗情况,然后根据固定的评估标准而非主观判断来决定是否保留该变更。

在处理强化措施的第一阶段时,首先写下相关约定:所需的输入参数、成功信号以及部分失败时的处理方式。这样的检查清单能确保后续的代码修改始终符合既定标准。 相比冗长的脚本,更应采用小型且可测试的单元。当某个步骤失败时,故障应能指向单一责任点,而非复杂的流程链。

强化措施细节 1/720:需测量该步骤的运行时间、错误类型以及代币消耗情况,然后依据固定的评估标准而非主观判断来决定是否保留该修改。

将强化措施的第二阶段视为可度量的对象来处理效果最佳。在扩大范围之前,先记录一份理想的操作流程、一个失败案例以及回滚说明。 在功能结果旁同时记录时间消耗及代币或查询成本。提前明确成本情况,可避免在从演示环境过渡到共享环境时出现意外支出。

强化措施细节2/720:为该记录测量运行时间、错误类型以及令牌消耗情况,然后依据固定的问题集而非个别案例来决定是否保留该变更。

对于强化措施的第3阶段,在修改代码之前需明确输入参数、该步骤的负责人以及完成标准。操作人员应能够从已知的检查点重新执行该步骤,而无需猜测隐藏状态。同时需将正常流程与故障恢复流程一并记录下来。重试机制、人工审核环节以及错误处理方式都是产品本身的组成部分,而非后续需要补充的内容。

强化措施细节3/720:为该记录测量运行时间、错误类型以及令牌消耗情况,然后依据固定的问题集而非个别案例来决定是否保留该变更。

在处理强化措施的第4阶段时,首先写下相关契约:所需的输入参数、成功信号以及部分失败时的处理方式。这样的检查清单能确保后续的代码修改始终符合约定。 将这一阶段视为输入与验证后输出之间的契约。为相关产物命名,明确成功判定标准,杜绝默许的部分完成情况。

强化措施细节4/720:需记录该环节的耗时、错误类型以及令牌消耗情况,然后依据固定的评估标准而非个人经验来决定是否保留该变更。

将强化措施的第5阶段视为可度量的对象来处理效果最佳。在扩大范围之前,先记录一份标准操作示例、一个失败案例以及回滚说明。 应将配置信息与应用程序代码分开。环境文件、密钥存储和功能开关应集中存放于一处,以便操作人员无需查看全部代码结构即可进行审计。

强化措施细节5/720:为该记录测量运行时间、错误类型以及令牌消耗情况,然后依据固定的问题集而非个人经验来判断是否保留该变更。

在强化措施的第6阶段,应在修改代码之前明确输入参数、该步骤的负责人以及完成标准。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。相比复杂的脚本,更应采用小型且可测试的单元。当某一步骤失败时,故障原因应能指向单一责任方,而非混乱的整个流程。

强化措施细节6/720:为该记录测量运行时间、错误类型以及令牌消耗情况,然后依据固定的问题集而非个人经验来判断是否保留该变更。

在处理强化措施的第7阶段时,首先写下相关约定:所需的输入参数、成功信号以及部分失败时的处理方式。这样的检查清单能确保后续的代码修改始终符合初始要求。

在功能结果旁记录执行时间以及令牌或查询成本。提前了解这些成本信息,可避免在系统从演示环境过渡到共享环境时出现意外费用。

强化措施细节7/720:为该阶段测量实际执行时间、错误类型以及令牌消耗情况,然后依据固定的评估标准而非个人经验来决定是否保留相关修改。

将强化措施的第8阶段视为可度量的对象来处理效果最佳。在扩大范围之前,先记录一个理想运行案例、一个失败案例以及回滚说明。

应同时记录正常流程和故障恢复流程。重试机制、人工审核环节以及错误处理方式都是产品本身的组成部分,而非后续需要补充的内容。

强化措施细节8/720:记录该任务的执行时间、错误类型以及代币消耗情况,然后依据固定的评估标准而非个人经验来决定是否保留该变更。

在强化措施的第9阶段,应在修改代码之前明确输入参数、该步骤的负责人以及完成标准。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。应将此阶段视为输入参数与验证后输出结果之间的契约,为相关成果命名、定义成功判定条件,并拒绝默许部分完成的情况。

强化措施细节9/720:记录该任务的执行时间、错误类型以及代币消耗情况,然后依据固定的评估标准而非个人经验来决定是否保留该变更。

在处理强化措施的第10阶段时,首先列出相关约定:所需输入、成功信号以及部分失败时的处理方式。这样的检查清单能确保后续的代码修改保持一致性。 应将配置信息与应用程序代码分开存放。环境文件、密钥存储以及功能开关应集中于一个位置,这样操作人员无需查看整个系统结构即可进行审计。

强化措施细节10/720:需测量该措施的执行时间、错误类型以及令牌消耗情况,然后依据固定的评估标准而非个人经验来决定是否保留该变更。

将强化措施的第11阶段视为可度量的对象来处理效果最佳。在扩大范围之前,先记录一份标准操作流程、一个失败案例以及回滚说明。 相比复杂的脚本,更应采用小型且可测试的单元。当某个步骤出现故障时,故障原因应能明确指向某个具体职责,而非整个复杂的流程链。

强化措施细节11/720:为该记录测量运行时间、错误类型以及代币消耗情况,然后依据固定的问题集而非个人经验来判断是否保留该变更。

在强化措施的第12阶段,应在修改代码之前明确输入参数、该步骤的负责人以及完成标准。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。应在功能结果旁记录运行时间以及代币或查询成本。提前了解成本情况,可避免在从演示环境过渡到共享环境时出现意外费用。

强化措施细节12/720:为该记录测量运行时间、错误类型以及代币消耗情况,然后依据固定的问题集而非个人经验来判断是否保留该变更。

在处理强化措施第13阶段时,首先写下相关契约:所需的输入参数、成功信号以及部分失败时的处理方式。这样的检查清单能确保后续的代码修改始终符合要求。

同时记录正常流程与故障恢复流程。重试机制、人工审核环节以及错误处理方式都是产品本身的组成部分,而非后续才添加的完善措施。

强化措施细节13/720:需测量该阶段的执行时间、错误类型以及令牌消耗情况,然后依据固定的评估标准而非个人经验来决定是否保留该变更。

将强化措施第14阶段视为一个可量化的目标面,效果最佳。在扩大范围之前,先记录一份理想的操作日志、一个故障案例以及回滚说明。

要把这一阶段视为输入参数与验证后输出结果之间的契约。为相关文档命名,明确成功判定标准,杜绝无声的半完成状态。

强化措施细节14/720:为该记录测量运行时间、错误类型以及令牌消耗情况,然后依据固定的问题集而非个人经验来判断是否保留该变更。

对于强化措施记录的第15阶段,在修改代码之前需明确输入参数、该步骤的负责人以及完成标准。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。配置应置于应用程序代码之外,环境文件、密钥存储以及功能标志应集中存放于一个操作人员可以审核的位置,无需查看整个系统结构。

强化措施细节15/720:为该记录测量运行时间、错误类型以及令牌消耗情况,然后依据固定的问题集而非个人经验来判断是否保留该变更。

在处理强化措施第16阶段时,首先列出相关约定:所需输入、成功信号以及部分失败时的处理方式。这样的检查清单能确保后续的代码修改保持一致性。 建议使用小型、可测试的单元,而非冗长的脚本。当某个步骤失败时,故障应能指向单一责任点,而非复杂的流程链。

强化措施细节16/720:需测量该步骤的运行时间、错误类型以及代币消耗情况,然后根据固定的评估标准而非主观判断来决定是否保留该修改。

将强化措施第17阶段视为可测量的对象来处理效果最佳。在扩大范围之前,先记录一份理想的操作流程、一个失败案例以及回滚说明。 在功能结果旁同时记录时间消耗及代币或查询成本。提前明确成本情况,可避免在从演示环境过渡到共享环境时出现意外费用。

强化措施细节17/720:为该记录测量运行时间、错误类型以及令牌消耗情况,然后依据固定的问题集而非个人经验来判断是否保留该变更。

在强化措施记录的第18阶段,应在修改代码之前明确输入参数、该步骤的负责人以及完成标准。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。需同时记录正常流程和故障恢复流程。重试机制、人工审核环节以及错误处理方式都是产品本身的组成部分,而非后续的优化工作。

强化措施细节18/720:为该记录测量运行时间、错误类型以及令牌消耗情况,然后依据固定的问题集而非个人经验来判断是否保留该变更。

在处理强化建议第19阶段时,首先写下相关契约:所需的输入参数、成功信号以及部分失败时的处理方式。这样的检查清单能确保后续的代码修改保持一致性。 将这一阶段视为输入与验证后输出之间的契约。为相关产物命名,明确成功判定标准,杜绝无声的半完成状态。

强化建议细节19/720:需测量该建议的执行时间、错误类型以及令牌消耗情况,然后依据固定的评估标准而非个人经验来决定是否保留该修改。

在处理强化建议第0阶段时,首先写下相关契约:所需的输入参数、成功信号以及部分失败时的处理方式。这样的检查清单能确保后续的代码修改保持一致性。 将这一阶段视为输入与验证后输出之间的契约。为相关产物命名,明确成功判定标准,杜绝无声的半完成状态。

强化措施细节 0/739:为该记录测量墙钟时间、错误类型以及令牌消耗情况,然后依据固定的问题集而非个人经验来判断是否保留该变更。

将强化措施的第一阶段视为可测量的对象最为有效。在扩大范围之前,先收集一份最佳案例记录、一个故障实例以及回滚说明。 应将配置置于应用程序代码之外。环境文件、密钥存储和功能标志应集中存放于一个位置,以便操作人员无需查看整个系统结构即可进行审计。

强化措施细节 1/739:为该记录测量墙钟时间、错误类型以及令牌消耗情况,然后依据固定的问题集而非个人经验来判断是否保留该变更。