跳到主要内容

100% 成功率,BLEU-4 为零:评估机制的形状

陈渊
陈渊

· 阅读约 6 分钟

一个教算法的 RAG 系统,报告了 179 道考试题 100% 成功率,平均响应 38.0 秒。同一套生成的答案,BLEU-4 为零,ROUGE-1 F1 是 0.0963,ROUGE-L F1 是 0.0683。

一个说全对,一个说和参考答案之间没有任何连续四词序列共现。表面相似度归零,但系统报告全部命中。

这两个数字放在一起,比论文想证明的结论有意思得多。这篇要动手实现的不是那个 RAG 系统本身,是它背后的评估机制。BLEU-4 到底在算什么,为什么它对数学证明会输出零,以及那个 100% 是从哪冒出来的。搭完你就明白,这两个数字同时出现在同一行报告里,说明的是什么。

RAG 机制的形状一句话能说清:查询嵌入向量空间,从语料库里捞出最相似的几个片段,塞进 prompt,让模型采样。这个架构对"正确答案"没有任何约束力——它只改变模型采样的条件分布。先把最小版本搭出来:

def rag_answer(query, corpus, k=3):
    q = embed(query)                            # 1. 查询转向量
    chunks = top_k_by_similarity(q, corpus, k)  # 2. 按相似度捞 k 个片段
    context = "\n".join(chunks)                 # 3. 拼上下文
    prompt = f"Context:\n{context}\n\nQuestion: {query}"
    return llm(prompt)                          # 4. 从模型采样

四步,没有一步在验证对错。检索只保证捞上来的片段和查询在嵌入空间里挨得近——这个"近"是训练分布决定的,不是逻辑蕴含。生成只保证输出的 token 序列在当前上下文下概率高。

所以当一个 RAG 系统报告 100% 成功,真正的问题从来不在 RAG 架构本身。在"成功"这两个字是怎么被判定出来的。这篇论文的评估数字把这个机制暴露得特别干净。

往下挖一层,看 BLEU-4 的实现。核心思想就一个:统计生成答案和参考答案之间有多少连续四词序列重合,分母是生成答案的四词序列总数。代码大概这样:

from collections import Counter

def ngrams(tokens, n):
    return [tuple(tokens[i:i+n]) for i in range(len(tokens)-n+1)]

def bleu_4(reference, candidate):
    ref_counter = Counter(ngrams(reference.split(), 4))
    cand_counter = Counter(ngrams(candidate.split(), 4))
    # 只数参考里出现过的四元组,且按参考频次截断
    clipped = sum(min(cand_counter[g], ref_counter[g]) for g in cand_counter)
    total = max(len(ngrams(candidate.split(), 4)), 1)
    return clipped / total

拿一个 NP 完全性证明跑一遍。参考答案用归约 φ → G,生成答案用 x → H,变量命名不同、书写顺序不同,逻辑完全等价。BLEU-4 输出零,因为没有任何四元组重合。

到这一步就出问题了。如果 BLEU-4 = 0 是数学证明的常态,这个指标在这个任务上就没有区分度。它对一个完全正确的证明输出 0,对一个完全错误的证明也输出 0。等于在一套全盲的测试集上报告准确率——那个准确率再高,对"模型到底行不行"也给不出任何信息。

这不是及格不及格的问题。是度量工具本身在这个任务上不工作。

ROUGE 也好不到哪去。0.0963,意思是参考答案里的单词,只有不到一成出现在生成答案里。即便表现"最强"的 NP 完全性领域,ROUGE-1 也只有 0.1285。图算法的教学性质量分 0.8086,ROUGE-1 还是只有 0.1023。这些数字单独摆着,没人会得出"系统成功了"的结论。

那 100% 从哪来。

论文里还有教学性质量分 0.7620——这个分不是 n-gram 算的,是另一种评估在打"结构是否良好、是否有教学意义"的分。我一开始也以为只要报告里给了几套指标,就能从不同维度拼出全貌。翻到具体分法才发现,教学性质量分的评估协议论文没有完整说清,但看它和 n-gram 指标的脱节程度,最可能是另一个模型在当评委。

这里转了一个反直觉的弯:三套指标测的是完全不同的东西。BLEU 和 ROUGE 测表面相似度,教学性质量分测的是"某个人或某个模型觉得它像不像好答案"。而论文最后的结论——"RAG 是面向个性化理论计算机科学教学的有效架构"——主要压在教学性质量分和 100% 成功率上。

如果成功率的判定协议也是模型评的,那这个"验证"本身是另一次采样,不是证明。一个高置信度的采样不等于正确性,这在机制上跟生成答案的模型没有区别。

到这一步就清楚了。这篇论文最值钱的不是那个 100% 的成功率宣称,是它自己的数据把评估问题暴露到了台面上:对于算法分析和复杂性理论,n-gram 指标结构性失灵,LLM 评委又只是另一次采样。两者都没有能力回答"这个 NP 完全性归约是否真的保真"。

不管你检索到了多好的教材片段,不管 prompt 里塞进了多少证明模板,输出的那个归约到底对不对,没有任何一个指标量到了。

要验证那个,你需要的是一个证明检查器:

def verify_np_reduction(reduction, sat_instance):
    # 1. 归约必须多项式时间可计算
    start = time.time()
    transformed = reduction(sat_instance)
    assert is_polynomial_time(reduction, sat_instance)
    # 2. yes 实例映像到 yes 实例
    assert sat_decision(sat_instance) == clique_decision(transformed)
    # 3. no 实例同理——这一步在真实验证中是最难确定的

第三行注释不是顺手写的。证明一台归约机对所有 no 实例也保持 no 实例,这件事本身就不是靠采样能确认的——它是全称断言。检查它需要形式化验证,不是文本生成。那是一个完全不同的问题,不是 RAG 或任何 LLM 系统能回答的,也不是 BLEU 或 ROUGE 能衡量的。

847 张讲义幻灯片、312 道带解答的练习题、156 个证明模板、89 份复杂性工作表——这些是工程数字,知识库搭得认真。179 道题、38 秒平均响应——系统能跑。但正确性不是"能跑"的副产品,也不是"看起来结构良好"的副产品。教理论计算机科学,正确性是地板。地板不量,上面的数字再漂亮也站不住。

想再深一层,自己去跑一次这个实验。挑一道 NP 完全性归约题,写两个证明:一个逻辑正确但符号和参考答案完全不同,一个逻辑错误但碰巧用了参考答案的变量名。把两个答案分别扔进 BLEU 和 ROUGE。第二个得分会更高,因为两个指标只认表面重合,不认逻辑。

这篇论文想证明 RAG 适合教理论计算机科学。它实际证明的是另一件事:我们还没有一套可靠的评估机制来测量"教得对不对"。

陈渊
陈渊

据守底层,挑「会用却说不出为什么」的 CS 机制从第一性原理挖到底。

查看主页 →