指令跟随难度评分(IFD)
更新时间:2026-09-14
简介
指令跟随难度(Instruction Following Difficulty,论文 https://arxiv.org/abs/2308.12032)打分算子。对每条样本做两次因果 LM 前向,输出 IFD = loss(response | query) / loss(response):比值越小说明 query 对生成 response 帮助越大。算子只产出分数,不按上下界过滤样本。
功能描述
- 双输入列(query 指令 + response 回答),逐条计算,每条两次前向:带 query 前缀的条件 loss 与不带前缀的无条件 loss
- loss 只在 response 段上统计(前缀位置的 label 置 -100)
- 拼接方式为
f"{prefix} {response}".strip(),前缀为空时直接用response.strip();两侧可分别套query_template/response_template - response 的 token 数按单独分词的结果扣掉 BOS 计算,必须 >0 且不超过拼接后长度,否则该行按异常处理
- 拼接后不足 2 个 token 时因果 LM 没有有效 label 位(loss 为 nan),该行返回 None;无 query 的 loss 为 0 也返回 None
- 任一列为空/纯空白/非字符串返回 None;单条异常只影响该行,其余行照算
- 输出保留 6 位小数
- tokenizer 的
pad_token缺失时用eos_token顶上,padding_side与truncation_side都取 left aihc_udf的num_gpus > 0且 CUDA 可用时按cuda:{rank % 卡数}选卡,否则跑 CPUdtype非法抛ValueError,权重目录不存在抛FileNotFoundError,都发生在初始化阶段
算子参数
输入
| 输入 | 含义 |
|---|---|
| queries | 指令/问题文本列 |
| responses | 回答文本列,IFD 的 loss 只在这一段上算 |
输出
| 输出 | 含义 |
|---|---|
| ifd_score | float64,条件 loss 与无条件 loss 的比值;空值、response 过短或计算异常返回 None |
参数
| 参数名称 | 类型 | 默认值 | 描述 |
|---|---|---|---|
| model_path | str | "/opt/aihc/model" | 权重根目录 |
| model_name | str | "Qwen/Qwen2.5-0.5B" | 相对 model_path 的权重子目录 |
| dtype | str | "float32" | 权重精度,可选 float16 / float32 / bfloat16;float32 数值更稳,故为默认 |
| max_length | int 或 None | None | tokenizer 截断长度,None 表示不截断 |
| query_template | str | "{query}" | query 侧模板,占位符 {query} |
| response_template | str | "{response}" | response 侧模板,占位符 {response} |
| rank | int | 0 | 多卡场景 worker 序号,实际设备取 cuda:{rank % 卡数} |
注意事项
- 与存量「困惑度计算」(
PerplexityCalculator,kenlm + sentencepiece 的语言模型困惑度)语义不同,两者分值不可互相比较,也不能复用阈值。 - 逐条两次前向,吞吐显著低于批量文本算子;长文本可用
max_length截断控制耗时。 - 换
model_name或dtype都会改变分值,阈值需重新校准。 - 算子不联网下载权重,权重缺失时在初始化阶段直接抛
FileNotFoundError。
调用示例
Python
1from __future__ import annotations
2
3import os
4
5import daft
6from daft import col
7
8from daft.aihc.common.udf import aihc_udf
9from daft.aihc.functions.text.instruction_following_difficulty_scorer import (
10 InstructionFollowingDifficultyScorer,
11)
12
13if __name__ == "__main__":
14 if os.getenv("DAFT_RUNNER", "native") == "ray":
15 import ray
16 ray.init(dashboard_host="0.0.0.0", ignore_reinit_error=True)
17 daft.set_runner_ray()
18 daft.set_execution_config(actor_udf_ready_timeout=6000, min_cpu_per_task=0)
19
20samples = {
21 "query": [
22 "What is the capital of France?",
23 "Explain what a neural network is in one sentence.",
24 "",
25 "Translate to French: hello",
26 ],
27 "response": [
28 "The capital of France is Paris.",
29 "A neural network is a model made of layers of weighted connections.",
30 "some response without query",
31 "Bonjour",
32 ],
33 }
34 ds = daft.from_pydict(samples)
35 ds = ds.with_column(
36 "ifd_score",
37 aihc_udf(
38 InstructionFollowingDifficultyScorer,
39 construct_args={
40 "model_path": "/path/to/models",
41 "model_name": "Qwen/Qwen2.5-0.5B",
42 "dtype": "float32",
43 },
44 num_cpus=1,
45 num_gpus=1,
46 concurrency=1,
47 batch_size=4,
48 )(col("query"), col("response")),
49 )
50 ds.show()
评价此篇文章
