Natural Memory NM2.1: 记忆路由器分叉、数据集缺陷修复与全轴评测证据
- 引入 MemoryRouterXL 与 v5/v6 流式多线程训练/编码管线 - 修复 prepare_memory_router_dataset 候选池重建缺陷(mega 家族 3568x 加速,输出逐字节相同) - 修复 v5 被破坏的拒答与多跳标签(train 未知样本 319 -> 16319,multi_hop 平均正例 1.00 -> 2.00) - 同存储预算下 V2-128 v6 逐轴 22/22 通过:Top-1 41.12% -> 94.62%,未知拒答 0.00% -> 100.00% - 记录三条被实测推翻的显然优化(logits_to_keep=1 反而慢 55%、XL 容量未带来收益) - 记忆手术跨架构可移植性 14/14,读写关闭时与原生模型逐位相同
This commit is contained in:
@@ -0,0 +1,70 @@
|
||||
"""Audit the unknown-refusal axis: are the 0/25 refusals real, or misdetected?
|
||||
|
||||
`ABSTENTION_MARKERS` is a fixed keyword list ("不知道", "没有记录", "无法确认",
|
||||
"未找到", "不清楚", "没有相关信息"). A reply that says
|
||||
"当前长期记忆中未包含相关信息,无法回答。" is a refusal in plain Chinese but
|
||||
contains none of those exact strings, so it is scored as a confident wrong answer.
|
||||
|
||||
This prints every unknown_category reply so the true refusal rate can be counted
|
||||
by hand, and groups them by which abstention signal (if any) they carry.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import re
|
||||
import sys
|
||||
from collections import Counter
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
|
||||
|
||||
from V2_dpskw.eval_scoring import ABSTENTION_MARKERS
|
||||
from V2_dpskw.rescore_e2e import load_corpus, load_run
|
||||
|
||||
# Refusal shapes actually observed in this project's replies, expressed as
|
||||
# patterns rather than exact strings.
|
||||
REFUSAL_PATTERNS = [
|
||||
r"不知道",
|
||||
r"没有(相关)?(记录|信息)",
|
||||
r"未(包含|找到|记录|提及|提供)",
|
||||
r"无法(回答|确认|确定|提供)",
|
||||
r"不清楚",
|
||||
r"缺乏(具体)?(上下文|信息)",
|
||||
r"未(曾)?(在)?(长期)?记忆中(包含|出现|找到|记录)",
|
||||
r"证据不足",
|
||||
r"无法(从|根据)(记忆|证据)",
|
||||
r"没有(足够)?(的)?证据",
|
||||
]
|
||||
REFUSAL_RE = re.compile("|".join(REFUSAL_PATTERNS))
|
||||
|
||||
|
||||
def looks_like_refusal(reply: str) -> bool:
|
||||
return bool(REFUSAL_RE.search(reply))
|
||||
|
||||
|
||||
def main(corpus, run, category="unknown_attribute"):
|
||||
cases = load_corpus(Path(corpus), 25)
|
||||
rows = load_run(Path(run))
|
||||
n = 0
|
||||
markers_hit = 0
|
||||
pattern_hit = 0
|
||||
for case, row in zip(cases, rows):
|
||||
if case["category"] != category:
|
||||
continue
|
||||
n += 1
|
||||
reply = row.get("reply", "")
|
||||
m = [x for x in ABSTENTION_MARKERS if x in reply]
|
||||
p = bool(looks_like_refusal(reply))
|
||||
markers_hit += int(bool(m))
|
||||
pattern_hit += int(p)
|
||||
print(f"[{n:02d}] markers={m or '-'} refusal_pattern={p}")
|
||||
print(f" Q: {case['query']}")
|
||||
print(f" R: {reply}")
|
||||
print(f"\n{category}: {n} cases")
|
||||
print(f" detected by keyword list : {markers_hit}/{n}")
|
||||
print(f" detected by pattern set : {pattern_hit}/{n}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main(sys.argv[1], sys.argv[2])
|
||||
Reference in New Issue
Block a user