层2/U3: SFTCollator 放开 prompt-only 模式(供 on-policy 生成)
data.py(对应 docs/03 §5 U3): - 加 prompt_only 开关:True 时输出 prompts/prompt_attention_mask(不产 labels, 由 U4 生成后重建);False 时 SFT 双预算路径逐字不变 - max_length 改可选:prompt-only 无总预算;SFT 模式缺它构造即报错 - 兑现 T3 为 on-policy 生成预留的口子;生成用左 padding(右边界对齐) test_data.py: - 新增 prompt_only 模式:返回 prompt 张量/不报错、左 padding、截断、剥末轮 assistant - 回归守卫:SFT 模式仍拒绝 prompt-only 行("SFT 路径行为不变") Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
+59
-1
@@ -233,11 +233,20 @@ def test_nothink模板注入空思考块():
|
||||
assert text.endswith("<T></T>")
|
||||
|
||||
|
||||
def test_prompt_only行显式报错():
|
||||
def test_sft模式仍拒绝prompt_only行():
|
||||
# 回归守卫(docs/03 §5 U3:"SFT 路径行为不变"):SFT 模式下 prompt-only 行
|
||||
# 仍是静默空训练闸门,必须报错——放开只发生在显式 prompt_only=True 模式
|
||||
with pytest.raises(ValueError, match="prompt-only"):
|
||||
make_collator()([row("没有答案的题")])
|
||||
|
||||
|
||||
def test_sft模式缺max_length构造即报错():
|
||||
with pytest.raises(ValueError, match="max_length"):
|
||||
SFTCollator(
|
||||
ToyTokenizer(), max_prompt_length=50
|
||||
) # 非 prompt_only 却无 max_length
|
||||
|
||||
|
||||
def test_左padding对齐():
|
||||
collator = make_collator()
|
||||
batch = collator([row("ab", "cd"), row("a", "c")])
|
||||
@@ -258,3 +267,52 @@ def test_pad回退到eos():
|
||||
max_prompt_length=50,
|
||||
)
|
||||
assert collator.pad_token_id == 7
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# SFTCollator:prompt_only 模式(层 2 / U3)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def make_prompt_collator(**kw):
|
||||
defaults = dict(max_prompt_length=100, enable_thinking=False, prompt_only=True)
|
||||
defaults.update(kw)
|
||||
return SFTCollator(ToyTokenizer(), **defaults)
|
||||
|
||||
|
||||
def test_prompt_only模式返回prompt张量且不报错():
|
||||
# 层 2:prompt-only 行是常态,不再报错;渲染带生成引导符供 generate 续写
|
||||
collator = make_prompt_collator()
|
||||
batch = collator([row("ab")])
|
||||
expected = "[user]ab[assistant]<T></T>"
|
||||
|
||||
assert set(batch.keys()) == {"prompts", "prompt_attention_mask"}
|
||||
assert batch["prompts"][0].tolist() == ids_of(expected)
|
||||
assert batch["prompt_attention_mask"][0].tolist() == [1] * len(expected)
|
||||
|
||||
|
||||
def test_prompt_only模式左padding对齐():
|
||||
# 生成要求左 padding:短 prompt 在左侧补 pad,右边界对齐
|
||||
collator = make_prompt_collator()
|
||||
batch = collator([row("abc"), row("a")])
|
||||
t = batch["prompts"].shape[1]
|
||||
short_mask = batch["prompt_attention_mask"][1].tolist()
|
||||
n_pad = t - short_mask.count(1)
|
||||
|
||||
assert n_pad > 0
|
||||
assert short_mask[:n_pad] == [0] * n_pad # padding 在左
|
||||
assert batch["prompts"][1].tolist()[:n_pad] == [0] * n_pad # pad_token_id=0
|
||||
|
||||
|
||||
def test_prompt_only模式截断到prompt预算():
|
||||
collator = make_prompt_collator(max_prompt_length=8)
|
||||
batch = collator([row("很长的题目" * 20)])
|
||||
assert batch["prompts"].shape[1] == 8 # 恰截到 max_prompt_length
|
||||
|
||||
|
||||
def test_prompt_only模式剥掉末轮assistant():
|
||||
# 若数据碰巧带了 assistant 轮,取生成前上下文(剥掉它再加生成引导符)
|
||||
collator = make_prompt_collator()
|
||||
batch = collator([row("q", "已有答案")])
|
||||
expected = "[user]q[assistant]<T></T>" # 不含"已有答案"
|
||||
assert batch["prompts"][0].tolist() == ids_of(expected)
|
||||
|
||||
Reference in New Issue
Block a user