一个多月前,我和朋友@u84u产生了一个简单却让人好奇的问题:既然大语言模型通过把文本切分成子词(subword)而非原始字节,能让文本表示缩小30-45%,那为什么不在把文本交给LZMA或zstd这类字节级压缩器之前,先做一次分词处理?
这个想法听起来像是有人试过然后悄悄放弃的那种。与其写一篇关于这个想法的博客,我们干脆搭建了一个测试框架来实际验证它。最终成果就是parmar——一个面向字节级压缩器的子词分词预过滤器,外加一套相当严谨的基准测试装置,用来检验这个想法在大规模场景下是否成立。
结论先行:它确实有效,但原因比我们预想的更窄
字节级压缩器(如LZMA2和zstd)会在一个固定大小的滑动“字典窗口”内寻找重复模式,这个窗口以字节为单位计量。我们的前提是:如果在压缩之前,先用BPE token ID(即喂给大语言模型时使用的同一种分词方式)替换UTF-8文本,那么token流会比源文本小约45%。一个64 MiB的字典窗口通常能覆盖约64 MB的散文,而在散文被预先缩小后,同样的窗口现在能覆盖大约两倍的散文量。
关键在于,只有当语料规模大于压缩器窗口时,这个优势才变得可测。在一个5 MB的文件上,所有内容都已经能装进窗口里,没有扩展空间可测量,预先分词几乎带不来任何收益。这就是为什么在小型测试文件上单独看一个压缩比数字几乎毫无意义——真正重要的是(parmar压缩比 − 原始字节压缩比)这条曲线如何随语料规模增长而变化,以及这条曲线是否持续攀升。
测试设计:452组配置,全部验证通过
parmar由两部分拼接而成:
语料库采用PG-19(Rae等人2019年发布的长期序列建模数据集),从64 MB到4 GB构建了四个层级,约10,600篇文档。如果对每个轴做完全的笛卡尔积,每个层级会有8,000多个有效组合——需要数周运行时间。因此我们将其拆分为一个51格的“压缩比网格”,用来隔离真正影响压缩比的轴,另外再做一个单因子逐一扫描(OFAT),针对那些应该只影响速度的轴。压缩比同样会在每个OFAT单元中记录,所以如果某个“仅影响速度”的轴悄悄改变了压缩比,它会以矛盾的形式暴露出来,而不是被平均掉。
每一次解压缩都会被实际执行,并与写入归档页脚的sha256校验值比对。如果校验失败,该单元的压缩比会被剔除并单独报告,不会混入平均值。在452个矩阵单元中:452次验证往返,零失败。
优势确实随语料增大而扩大——但只对窗口足够大的后端成立,且会趋于平稳
在gzip那仅有32 KiB的小窗口上,+15%的优势从64 MB到4 GB完全持平,因为窗口早已被占满,没有扩展空间可供利用。而在窗口更大的后端上,差距确实会随语料规模增大而拉开,但最终会进入平台期。
热门跟贴