块 100 时单跳题 5/5、多跳题只有 2/4;块 400 才双双满分。 同一份文档、同一个检索算法、同一批问题,只有切块变了,多跳能力差一倍。 切块是天花板。
上一节把参数定了,这一节把资料真传进去,然后验收——重点是分清「单跳」和「多跳」这两类问题。
一、上传不等于能用
传完看到「已完成」只是说文档存进去了、向量算了,不等于检索能找对,更不等于模型答得对。 验收要拿真实问题去问,而且问题得从业务里来, 不能是「文档第一段讲了什么」这种——那种问题怎么切都能找对。 问题要分两类,它们的难点完全不同: 单跳题:一句话里就有完整答案。「一等座谁能坐」——答在第三章某一句话里。 多跳题:要两个地方的原文拼起来才答得出。 「二线城市住超标了谁承担」——一半在第四章(标准四百元),一半在第九章(超额部分自理)。 单跳考的是「切块细不细」,多跳考的是「能不能拼齐」。这两件事的修法完全相反,所以必须分开量。
二、实测:块越小,单跳越稳、多跳越差
Demo 用同一份 979 字的制度文档,切四档块大小(都带 10% 重叠),跑 5 道单跳加 4 道多跳。 块 100(11 块):单跳 5/5,多跳只有 2/4。 块 200(6 块):单跳掉到 4/5,多跳升到 3/4。块 400(3 块):两个都满分。 块 800(2 块):也是满分。 注意 200 那一行:单跳反而掉了一道。这不是笔误——块变了,切点跟着变, 某道题的答案句被切到边界上,准确率不是单调的,会抖。 所以调这个参数不能只看一次结果,得多试几档、多看几个问题。单点最优很容易是运气。
三、多跳为什么会输
Demo 后半段把块 200 那一档的四道多跳题逐题列了出来:三道的前后半句都在,一道只有前半句。 缺的那道是「二线城市住超标了谁承担」——前半句「二线城市每晚不超过四百元」在第四章的块里,找得到; 后半句「超额部分由个人自理」在第九章,排到了第三名之外。 两句话都在库里,但检索只给三块,凑不齐就答不出来。这就是多跳问题的本质:不是没有, 是没同时拿到。 而且它不会报错。模型拿到半份材料,会照着半份材料答,语气和答对时一模一样。 这是知识库应用最难发现的一类错。
四、三种治法,各有代价
块调大:让相邻的两条信息落在同一块里。 代价是每块混入更多无关内容,相似度被稀释,还吃上下文预算。Demo 里块 400 就是这么解决的。 k 调大:从 3 提到 5 或 8,让两半都进候选。代价是噪声变多,而且受上下文预算约束。 这是最省事的一种。 拆问题: 先把「二线城市住超标了谁承担」拆成「二线城市的住宿标准是多少」和「超标部分谁承担」两个子问题, 各查一次再合并。代价是要多一次模型调用,但准确率提升最明显。 三种可以叠加。 实践顺序通常先加 k,再考虑块,最后才上拆问题——因为前两个只改配置,第三个要改代码。 最后一句要说清: 这个 Demo 只量了「信息在不在候选里」,没量「模型会不会用」。 候选里没有就一定答不对, 候选里有了也可能答歪。切块是天花板,模型只是逼近它。
五、动手 Demo:块 100 时单跳 5/5、多跳 2/4;块 400 才双双满分
用同一份制度文档,切四档块大小,跑 5 道单跳题(一句话有答案)和 4 道多跳题(要两处原文拼起来), 统计单跳 top-1 和多跳 top-3 的命中;再把块 200 那一档的四道多跳题逐题列出,看缺的是哪一半。
SINGLE_Q = [(u'一等座谁能坐', u'一等座仅限总监及以上人员乘坐'), ...]
MULTI_Q = [(u'二线城市住超标了谁承担',
[u'二线城市每晚不超过四百元', u'超额部分由个人自理']), ...]
cs = cut(size, int(size * 0.1))
s1 = sum(1 for q, a in SINGLE_Q if a in cs[topk(cs, q, 1)[0][1]])
m3 = 0
for q, ans in MULTI_Q:
got = topk(cs, q, 3)
if all(any(a in cs[i] for _s, i in got) for a in ans):
m3 += 1
实跑输出:
--------------------------------------------------------------------
上传私有数据:单跳题考切块,多跳题考「能不能拼齐」
--------------------------------------------------------------------
知识库 1 份制度文档,979 字
问题 单跳 5 道、多跳 4 道
单跳=一句话就有答案;多跳=要两个地方的原文拼起来才答得出。
块大小 块数 单跳 top-1 多跳 top-3
---------------------------------------
100 11 块 5/5 2/4
200 6 块 4/5 3/4
400 3 块 5/5 4/4
800 2 块 5/5 4/4
看第三列和第四列的关系:**块越小,单跳越稳、多跳越差。**
块 100 的时候单跳 5/5,多跳只拼齐 2/4——因为要拼的两句话被切进了不同的块,top-3 里凑不齐。
块 400 的时候多跳才全中:**一块正好装下两章,相邻的两条信息落在同一块里。**
块 200、重叠 20%(就是单跳掉一道的那一档),四道多跳题逐题看缺哪一半:
二线城市住超标了谁承担 → 前半句在、后半句缺
出差没审批能报吗 → 前半句在、后半句在
电子发票怎么交 → 前半句在、后半句在
国际差旅要准备什么 → 前半句在、后半句在
这段输出就是「多跳问题为什么难」的直接证据:
**两句话都在库里,但检索只给三块,凑不齐就答不出来。**
这一档唯一的缺口正好落在后半句——它要么被切到隔壁块,要么在排序里被挤到第四位。
治法有三个:块调大、k 调大、或者对多跳问题先拆成两个子问题再各查一次。
最后提醒一句:**这个 Demo 只量了「信息在不在候选里」,没量「模型会不会用」。**
拿到三块之后能不能答对,还取决于模型。
但候选里没有,就一定答不对——**切块是天花板,模型只是逼近它。**
done -> _out_39e.txt
第一张表四行,单跳那一列基本稳在 5/5,多跳那一列从 2/4 一路涨到 4/4。块越小越糟, 方向非常清楚。 块 100 那行是最有说服力的:11 个块,单跳全中,多跳只拼齐一半。说明「切细」对单跳是好事, 对多跳是灾难。 块 200 那行单跳掉到 4/5,看着奇怪,其实是切点位移——块大小一变,边界就变, 某道题的答案句正好跨边界。这说明准确率不是单调的,调参不能只看一个点。 块 400 和 800 都是满分,但两者代价不同:上一节算过,块 800 时 k=3 要吃 2400 字。 所以「都满分」里,400 是更划算的那个。 逐题明细那一组是这一节的重点。 三道题前后半句都在, 一道只有前半句——缺的那半句不是不在库里,是排到第四名之后了。模型拿到半份材料照样会答, 语气还很笃定。
30 秒实验
- 把多跳题的 k 从 3 提到 5,看块 100 那一档能不能从 2/4 涨上去。 如果能,说明问题在 k 不在块;如果不能,才是真的被切碎了。
- 把某道多跳题拆成两个子问题各查一次,手工合并结果,看能不能答对。 这就是「拆问题」那条路的原型。
- 把重叠从 10% 提到 25%,再跑一遍块 100 那一档。看多跳能不能改善,同时看总字符涨到多少。 这就是在买什么、付什么。
常见误区
- 「上传完成就是能用」。上传只是存进去了。检索准不准、答得对不对,得拿真实问题量。
- 「块切小一点检索更精准」。单跳确实更准,但多跳会崩——要拼的信息被切散了。两类问题要分开量。
- 「多跳答错是模型不行」。先看候选里有没有那两段。候选里没有,换什么模型都答不对。
自测三问(点开对答案)
1. 单跳题和多跳题的难点有什么不同? —— 单跳考「切块细不细」——答案在一句话里,切太粗会混入无关内容; 多跳考「能不能拼齐」——要两处原文同时进候选,切太细就凑不齐。 2. 为什么块 200 时单跳反而掉了一道? —— 因为块大小一变,切点跟着变,某道题的答案句正好跨了边界。 准确率对块大小不是单调的,会抖——所以调参要多试几档。 3. 多跳问题答错了,先查什么? —— 先查候选里有没有那两段原文。有而没答对,是模型的问题; 没有,是检索的问题(k 太小、块太碎、或者该拆问题)。第三章到这里结束:数据从哪来、怎么造、怎么存、怎么验收,四件事都过了。 第四章开始讲标注——把「原始数据」变成「能训练的数据」。
- ☐ (本章完,下一节 4.1.1 数据标注的概念与技术原理)