40 篇真实中文文章,67,615 字,10 种切法横向实测

同一批文章,换一种切法,检索命中率差 4 个百分点。这个幅度比很多人预期的小,而真正拉开差距的地方在别处。

我拿 40 篇中文技术文章做了这组测试,总共 67,615 字,平均一篇 1,690 字,用 10 种常见切法各切一遍,然后量三件事:块被从句子中间切断的比例、切片总量相对原文多出来多少重复字符、以及检索时正确的块能不能排到第一。

测试怎么做的

语料是从一个真实站点上抓的已发布文章,正文只保留中文和常用标点,去掉网页标签。切法分四类:固定字数(256 / 512 / 1024)、固定字数加重叠(512 加 10%、512 加 25%、1024 加 25%)、按段落聚合到目标长度(400 / 800)、按句子数量聚合(3 句一块 / 8 句一块)。

检索用的是 BM25,把字符二元组当作词元。中文没有空格,用字符二元组代替分词,好处是不依赖分词库,坏处是对长词的语义不敏感。查询从正文句子里随机截 14 个字,金标准是这句话所在的那一块。

硬切基本都在句子中间下刀

切法块数平均长度切断比例碎片比例重复字符
fixed_256282239.695.74%3.90%0.00%
fixed_512152444.796.71%3.95%0.00%
fixed_102485795.598.82%2.35%0.00%
fixed_512_ov10165448.396.97%3.03%9.40%
fixed_512_ov25193449.495.85%2.59%28.27%
fixed_1024_ov25108770.8100.00%3.70%23.11%
para_400193350.320.73%2.59%0.00%
para_800105644.038.10%0.95%0.00%
sent_389076.04.49%62.13%0.00%
sent_8350193.211.43%2.57%0.00%

固定长度那三种,切断比例都在 95% 以上。也就是说你按字数硬切,几乎每一块都断在半句话上,前后两块各缺一半意思。这个数字比我想的极端,尤其是 1024 加 25% 重叠那一档,108 块没有一块落在句号后面。

按段落切的差别一眼能看出来。para_400 的切断比例是 20.73%,只有固定长度那几种的四分之一多一点。段落边界是人写文章时本来就有的停顿,对齐它不花成本。

按段落切,被切断的块从九成五降到两成
按段落切,被切断的块从九成五降到两成

重叠是拿重复换边界,这笔账不一定划算

重叠的本意是让上下文别丢。25% 听起来不多,落在字符上是 28.27% 的重复。也就是说库里将近三成的字被存了两遍。

代价付了,收益却没出现。fixed_512 的 R@1 是 91.15%,加了 10% 重叠掉到 88.33%,加 25% 掉到 89.17%。三种固定 512 的切法里,不加重叠的那个反而最好。

同样的 67,615 字,加 25% 重叠后切片总量多出近三成
同样的 67,615 字,加 25% 重叠后切片总量多出近三成

我猜原因是重复块挤占榜单。同一段内容在库里出现两三次,BM25 按词频打分,长得几乎一样的几块会一起被捞出来,把别的候选挤到后面。你花了三成存储和索引成本,换来的是自己的结果互相打架。

按段落切的那一档排第一

全库检索的结果如下,120 个查询,候选池是全部块而不是限定在同一篇文章里,所以这个数字是有区分度的。

切法块数R@1R@3R@5
fixed_25628291.74%92.66%96.33%
fixed_51215291.15%96.46%97.35%
fixed_10248591.38%93.97%95.69%
fixed_512_ov1016588.33%92.50%94.17%
fixed_512_ov2519389.17%95.00%97.50%
fixed_1024_ov2510890.83%95.00%97.50%
para_40019392.50%95.83%98.33%
para_80010593.33%96.67%97.50%
sent_389090.00%92.50%95.00%
sent_835090.83%94.17%94.17%

para_800 的 R@1 是 93.33%,最高。它只有 105 块,平均 644 字,碎片率 0.95%,是唯一在结构指标和检索指标上都靠前的切法。

sent_3 值得单独说。它切断比例只有 4.49%,看着最完整,但切出 890 块,平均 76 字,62.13% 的块不到 80 字,等于大半是碎片。890 块意味着 890 次向量化和存储,检索时要扫的候选也多。R@1 是 90.00%,比 para_800 低三个多点。

sent_8 是结构上最均衡的一个,350 块,平均 193 字,切断 11.43%,碎片 2.57%,R@1 90.83%。

全库检索 R@1,按段落切的两档排在前两位
全库检索 R@1,按段落切的两档排在前两位

四个点撑不起一次重构

把 R@3 也看一遍,10 种切法全落在 92.50% 到 96.67% 之间。R@5 是 94.17% 到 98.33%。最好的和最差的差 3.5 到 4 个百分点。

这个幅度值得知道,但不足以支撑结论说分块是第一优先级。在纯词法检索这套设定下,分块方式对命中率的影响是有限的。真正贵的部分是前面的嵌入和后面的生成,块切得再好也只是把候选送到门口。

需要说明我这组测试的边界。语料只有 40 篇同一个站点、同一个题材域的文章,块数最多 890,规模不大。检索用的是词法匹配,没有做向量检索,而分块对向量检索的影响通常比词法大,这一块我没有测。查询是从原文里截的连续片段,属于偏简单的词法题,真正用户提问会更模糊,那部分难度没被覆盖。

要在你自己的语料上做决定,有个更省事的办法:先按段落切,跑一遍你手上真实的 50 个问题,看命中排第几。段落切法几乎不花成本,先把这个基线立住,再决定要不要为重叠付那三成存储。至于固定字数加大量重叠这种组合,从这次数据看,它同时付出了重复成本和切断成本,两边都没换到。