多轮评测比单轮多两个维度:指代能不能解出、前后会不会自相矛盾。 这两个维度在单轮评测里根本量不到。 这一节用十二道题、两种设置量一次,重点是看「给更多历史」到底治好了什么、又弄坏了什么。
上一节讲了六种提升手段,这一节判断它们有没有用。 单轮评测的口径,第 3 章 3.4 节讲过; 这一节补的是多轮特有的那两个维度,以及「错题怎么变成动作」。
一、三个指标,两种设置
事实正确:答案对不对。指代解出:「它」「那个」指的是不是那个东西。 前后一致:同一个字段两次问,答案一样吗。三项分开量,别合成一个总分。 还要分两种设置:不带历史、带全部历史。两者之差,就是「历史值多少」。 演示里的数:事实 2/6 涨到 5/6,指代 0/6 涨到 4/6——历史把这两类治好了。
但「两个数取最新」那一栏两遍都是零。历史里同时有二十万和二十五万时,模型取的是最早出现的那个。 给更多历史,不一定更好。
二、题库怎么建
三类题不要:能从规范直接答出来的(测不出模型)、答案本身有争议的(测出来也吵不清)、 只考记忆的(换个说法就废)。 题库要固定、要版本化、要分类。分类不是为了好看,是为了「错哪一类」能直接读出「改哪里」。 题库只增不改。改题库等于换尺子,历史分数全部作废,你就再也说不清「这一版比上一版好在哪」。
三、错题归类:从分数到动作
演示里十二道题:不带历史错十道,带全部历史错三道。但错的类别整个换了一批。 指代悬空:补多轮数据。上下文缺失:接槽位。前后矛盾:提示里写「以最新为准」。格式不符:加格式约束。 每一类对应一个具体动作。 没有动作的评测报告,等于没写。「准确率 78%」这种句子,读完不知道该干什么。
还有一层:错题要留档。同一道题连着三个版本都错,说明它不是数据问题,是设计问题。
四、持续改进的闭环
抽样、归错、补数据、重训、重跑同一套题、看哪一列动了。六步,一步不能省。 一轮下来只应该有一两列动。 全都在动,说明你改的东西太多,归因不了——这跟第 3 章「一次只改一处」是同一个道理。 演示里的提升是脚本补了三条规则换来的,不是模型学会了。 但流程一模一样,只是真机上「补数据」之后要重训一轮,慢得多。
闭环一周转一圈就够了。转得太快,每轮的数据量太少,噪声会盖过信号。
五、动手 Demo:十二道多轮题,三种指标,带不带历史差多少
十二道题:六道事实、四道指代、两道「两个数打架」。两种设置各跑一遍,三个指标分开算。 作答的是一个规则替身,不是真模型。 它把「有上下文就答得出、没上下文就答不出」写成代码,好让差别能被数出来。 要看的是评测方法,不是这个替身的智商。
def answer(qid, ctx):
kw = KEY[qid]
if qid in CONFLICT and ctx.count(u'二十万') + ctx.count(u'三千') >= 2:
return u'结论:二十五万。(取了最早出现的那个数)'
if qid in NEED_CTX and kw not in ctx:
return u'结论:不确定您指哪一项。' if qid in PRON else u'结论:需要前文才能确认。'
return u'结论:%s。理由:按前文。' % kw
def score(res):
f = sum(1 for qid, a in res if qid in FACT and KEY[qid] in a and u'理由:' in a)
p = sum(1 for qid, a in res if qid in PRON and KEY[qid] in a)
c = sum(1 for qid, a in res if qid in CONFLICT and KEY[qid] in a)
return f, p, c
实跑输出:
--------------------------------------------------------------------
十二道多轮题,三种指标,带不带历史差多少
--------------------------------------------------------------------
① 题目分四类
--------------------------------------------------------------------
| 编号 | 第几轮 | 类型 | 题目 |
|:---:|---|:---:|---|
| q01 | 1 | 事实 | 外立面要改哪一面 |
| q02 | 2 | 事实 | 预算够不够 |
| q03 | 3 | 指代 | 那它还能挪多少 |
| q04 | 4 | 指代 | 这个色温合适吗 |
| q05 | 5 | 指代 | 那面墙要不要加洗墙灯 |
| q06 | 6 | 事实 | 工期压到十天行不行 |
| q07 | 7 | 矛盾 | 刚才说的预算能改吗 |
| q08 | 8 | 指代 | 防护等级按那个来 |
| q09 | 9 | 事实 | 验收要几天 |
| q10 | 10 | 事实 | 下雨顺延吗 |
| q11 | 11 | 矛盾 | 前面定的色温是多少 |
| q12 | 12 | 事实 | 最后确认一下预算 |
十二道题:六道事实、四道指代、两道「两个数打架」。
! 带代词的共六道(含两道「两个数打架」的),没有历史就一定答不出——这不是模型的问题,是输入不全。
把它们放进题库,就是为了单独量出「历史到底值多少」。
② 两种设置,三种指标
--------------------------------------------------------------------
| 设置 | 事实正确 | 指代解出 | 两数取新 |
|---|:---:|:---:|:---:|
| 不带历史 | 2 / 6 | 0 / 6 | 0 / 2 |
| 带全部历史 | 5 / 6 | 4 / 6 | 0 / 2 |
事实题 2/6 涨到 5/6,指代 0/6 涨到 4/6——历史把这两类治好了。
! 但「两数取新」那一栏两次都是 0。
历史里同时有「二十万」和「二十五万」时,模型取的是最早出现的那个。给更多历史,不一定更好。
③ 错题归成四类
--------------------------------------------------------------------
| 类别 | 不带历史 | 带历史 | 例子 |
|---|:---:|:---:|---|
| 指代悬空 | 6 | 0 | q03、q04 |
| 上下文缺失 | 3 | 0 | q01、q02 |
| 前后矛盾 | 0 | 2 | q07、q11 |
| 格式不符 | 1 | 1 | q10 |
十二道题:不带历史错 10 道,带全部历史错 3 道。错的类别整个换了一批。
! 归类的意义是决定改哪里:指代悬空要补多轮数据,上下文缺失要接槽位,
前后矛盾要在提示里写「以最新为准」。如果只记「错了 3 道」,你没有任何下一步。
④ 补三条,重跑同一套题
--------------------------------------------------------------------
| 指标 | 改进前 | 改进后 |
|---|:---:|:---:|
| 事实正确 | 5 / 6 | 6 / 6 |
| 指代解出 | 4 / 6 | 6 / 6 |
| 两数取新 | 0 / 2 | 2 / 2 |
! 这里的提升是脚本里补了三条规则换来的,不是模型学会了。
演示的是闭环:错题 → 归类 → 补数据(或补规则)→ 重跑同一套题 → 看哪一列动了。
真机上这一步是重新训一轮,慢得多,但流程一模一样。
⑤ 持续改进要落成什么
--------------------------------------------------------------------
[流程约定 · 不是代码]
| 1. 线上抽样:每天抽 N 条真实对话
| 2. 按四类归错:指代 / 漏取 / 矛盾 / 格式
| 3. 每类补 5~20 条,进训练集或进提示
| 4. 重跑固定题库,看哪一列动了
| 5. 题库只增不改:改题库等于改尺子
这一段是流程,不是代码,所以不涉及能不能跑。
最后一条最要紧:题库一改,历史分数全部作废,你就再也说不清「这版比上版好在哪」。
! 所以题库要版本化,跟代码一起提交。「上个月多少分」这个问题,只有题库没动过才答得出。
①十二道题分四类:事实、指代、矛盾(还是事实题,只是历史里有两个数)。 带代词的共六道,没有历史就一定答不出——这不是模型的问题,是输入不全。 ②两张设置的对比很有信息量:事实 2/6 到 5/6,指代 0/6 到 4/6。但「两数取新」两遍都是零。 这一栏全是零,恰恰是最有价值的发现:「历史给得不够」和「历史给得太多」是两个不同的问题, 前者靠补历史解决,后者补历史只会更糟。 ③错题归类是这一节的方法核心。 不带历史错十道(指代六、缺上下文三、格式一),带历史错三道(矛盾二、格式一)——数量从十降到三, 但错的类别换了。 如果只记「错三道」,你没有任何下一步。归了类之后,三类对应三个动作,每一类都能当天开工。 ④补三条规则后重跑:事实 6/6、指代 6/6、两数取新 2/2。 这个提升是脚本补规则换来的,不是模型学会了——要诚实地说这一点,否则读者会以为「补数据」是魔法。 真机上这一步是重新训一轮。慢,但流程一样:错的样本进训练集,重训,重跑同一套题,看哪一列动了。 ⑤最后那张流程表里,第五条最要紧:题库只增不改。改了题库, 你就没法回答「这个月比上个月好多少」——那是最常被问到的一个问题。
30 秒实验
- 加一道题:问「刚才说的防护等级是多少」,但历史里 IP65 出现两次且一致。 看它会不会被误判成「两数打架」。
- 把「两数取新」的口径改成「两个都答出来算对」,重跑。分数会变吗?这说明口径怎么影响结论。
- 把 FACT 的定义改成「关键词对且不超过六十字」,重跑。哪一版的标准更接近业务要求?
常见误区
- 只报一个总分。 多轮评测里总分最没用——它把「指代悬空」和「格式不符」平均成一个数,而这两类的修法完全不同。
- 用同一段历史当训练数据和测试题。 这类污染在多轮数据里特别容易发生,因为对话都是同事写的,主题高度重合。
- 把改口径当改进。放宽字数、放宽格式,分数立刻好看,模型一点没变。 口径改动要单独记账,否则三个月后没人说得清分数是怎么涨的。
自测三问(点开对答案)
1. 为什么「两数取新」这一栏带历史反而更差? —— 因为历史里同时存在两个数,模型取到了最早的那个。 历史越长,这种「有两个候选」的情况越多,所以补历史解决不了它——得在提示里明确「以最新为准」, 或者直接查槽位。 2. 错题归类的意义是什么? —— 把分数变成动作。 指代悬空要补多轮数据,上下文缺失要接槽位,矛盾要改提示——三类三个动作,都能当天开工。 只记「错了几道」,读完不知道该干什么。 3. 为什么题库不许改? —— 因为题库是尺子。尺子一换,历史分数全部作废,你就无法回答「这版比上版好在哪」。 新题只能加,不能改也不能删。评测说完了,这一章还剩最后一节:把上面所有结论落到一个具体的字符串上——prompt 怎么拼。
- ☐ (本篇完,下一篇 4.6 适合 Llama 2 大模型多轮对话的 prompt 构建)
相关笔记
- 评估与优化:分维度看丢分,一次只改一处
- 应对能力:错题怎么归类
- 40 课 图解机器学习:评测指标的盲区
- 🦙 Llama 大模型实践 · 索引