索引压缩
目录
索引压缩:对词典和倒排记录表这两个数据结构进行压缩。
为什么需要压缩?
- 使用更少的磁盘空间
节省资金
- 在内存中存放更多信息
提高速度
- 提高数据从磁盘传输到内存的速度
读取压缩数据|解压缩 比 读取未压缩数据 快得多
前提:解压缩算法是高效的
词典尽可能全部放在内存中,如果放不下,
倒排表没有办法全部放在内存中,把查询最多的词项的倒排表放在内存。
词典:
- 使其足以存放在内存中
- 使内存可以多容纳一些倒排记录表
倒排记录表:
- 减少所需的磁盘空间
- 减少从磁盘中读取倒排记录表所需的时间
- 大型搜索引擎将一部分倒排记录表保存在内存中(压缩使内存能保存更多的倒排记录表)
两种压缩方式:
无损压缩:所有原始信息都保留(大多数IR系统的做法)
有损压缩:大小写转换,词干还原,停用词...
会造成一定的信息丢失,但是有时候对检索没影响
信息检索中词项的统计特性
- 词项的个数:Heaps定律
- 词项在文档出现的次数,有的词项出现的多,有的词项出现的次数少:Zipf定律
文档集频率:一个词项在文档集中总共出现次数
文档频率:一个词项在几个文档中出现,倒排索引一部分。
区别:词A中文档1中出现了3次,文档频率只算1个,但文档集频率算3
Heaps定律
词项数目的估计
- Heaps’ 定律: M = kTb
- M 是词汇表大小, T 是文档集中的词条个数
- 参数k和b的典型取值为: 30 ≤ k ≤ 100 , b ≈ 0.5
- Heaps定律认为,在对数空间中词汇量 M 和 T,存在线性关系
Zipf定律
对词项分布的建模
自然语言中存在一些高频词项,也存在一些极低频词项
- Zipf’s 定律: 排名第i多的词项的文档集频率与1/i 成正比.
- cfi 是 文档集频率: 文档集中排名第i多的词项的文档集频率.
- cfi ∝ 1/i = K/i ,K 是一个常量
-
如果出现最多的词项的出现次数是cf1的话
- 出现第二多的词项的出现次数就是 cf1的一半
- 出现第三多的词项出现次数是cf1的1/3 …
-
等价变换: cfi = K/i ,其中k=1, 因此
- log cfi = log K - log i
- log cfi 和 log i 是线性关系
词典压缩
定长数组(浪费空间)——长字符串(完全不浪费空间)——按块存储(继续减少词项指针占的空间)—— 前端编码
定长数组(浪费空间,11.2MB)——长字符串(完全不浪费空间,7.6MB)——按块存储(继续减少词项指针占的空间,7.1MB)——前端编码(5.9MB)
用定长数组来存储词典中的词项。
——浪费空间。词项没那么长,为了存储很长的单词为每个词项使用同样大的小存储空间。
terms:词项,每个词项采用20个字节来存储。
Freq:文档频率(查询优化使用,也要存储),4个字节
postings ptr:倒排表指针,4个字节
词典需要存储词项、文档频率、倒排表三部分,压缩重点在词项上。
每个词项用20个字节浪费。
about:占6个 computer:占9个 20个:有些词项太长
初始时直接设20个,为了容纳很 长的单词,为每个词项都 安排了20个字节
缺点:浪费空间,一般单词都没20个那么长
例子:
定长数组这种方法花多少空间来存储400K个词项
每个词项需要存三部分(词项本身20,文档频率4,倒排表指针4)
400K
0.4M*(20+4+4)=11.2MB
做实验:先用小数据集来做实验,再应用到大型的数据集。
单词的平均长度为8字节。
将词典看成单一字符串的压缩方法
-
- 为每个词项增加一个定位指针,它在指向下一词项的指针同时也标识着当前词项的结束
- 这种机制能够节省大约60% 的存储空间.
长字符串:将所有单词连接(没有结束符)
定长数组(浪费空间)——长字符串(完全不浪费空间)aboutcomputerinformation...
虽然不浪费空间,但是新问题,如何分隔长字符串(引入一个指针Term ptr,词项指针)
词项指针:指示词项位置在哪,花多少字节存储?
词项个数:400K*8就是长字符串的总长度
词项指针花多少字节表示。
长字符串指针所需的存储空间:
- 文档频率采用4 B存储.
- 词项到倒排记录表的地址指针也采用4 B存储.
- 词项指针采用3 B存储
- 词项字符串中每个词项平均为8 B
- 400K 词项 x 19 Þ 7.6 MB (定长数组总共需要 11.2MB)
计算机是二进制。。
举例:8个数据(0~7),用几个二进制位表示,计算方法 log8(以2为底)
000
001
010
011
100
101
110
111
同理,3.2MB的数据,用几个二进制位表示,log3.2M(以2为底)
bits:位
bytes:字节
1个字节8位
22位,转换为字节,24位用3个字节表示。
计算机以字节为单位处理比较快,以位处理很慢。
本例中,词项指针可以用3个字节来表示
长字符串的方法,占多少空间
400K
每个词项:词项本身8、文档频率4、倒排表指针4,词项指针3
0.4M*(8+4+4+3)=7.6MB
从定长数组:11.2MB转换为长字符串方法变成了7.6MB
按块存储
-
将长字符串的词项进行分组变成大小为k的块(即k个词项).
- 以 k=4 为例.
- 需要存储每个词项长度 (长度用1B表示)
- 块指针依然是3字节
思想方法:按块存储,把词项指针减少,分块,每个块有块指针,块内没指针如何间隔,在每个词项前存一个长度
长字符串与按块存储(减少指针) :
如果是长字符串方法 ,每4个词项有4个词项指针占12字节
如果是按块存储,每4个词项,1个块指针3字节+4个长度4字节,总共7字节
举例:
400K个词项,0.4M
每个词项(词项本身8、文档频率4、倒排表指针4,词项长度1),每个块的块指针3字节
0.4M*(8+4+4+1)+0.4M*3/4=0.4M*17+0.3M=7.1MB k=4为例
按块存储,可以减少存储空间
一个块中有4个词项(k=4):0.4M*(8+4+4+1)+0.4M*3/4=0.4M*17+0.3M=7.1MB
一个块中有8个词项(k=8):0.4M*(8+4+4+1)+0.4M*3/8=6.8M+0.15M=6.95MB
一个块中有16个词项(k=16):0.4M*(8+4+4+1)+0.4M*3/16=6.8M+0.075MB=6.875MB
k如果增大,压缩率更高,我们不会去增大K
平均比较次数:
- 使用块之后,平均比较次数变多(检索效率变低)
- 为了在压缩和检索效率之间平衡,k不能太大
- 按块存储了为了压缩,k越大,平均比较次数增多(检索变慢)
- 词典是B+(二叉搜索树),平均比较次数
- 使用块:块外是二叉搜索树,块间是线性
不使用块的词典检索平均比较次数为2.6
使用块的词典检索平均比较 次数为3
前端编码
前端编码:公共前缀被识别出来后,后续的词项可以使用一个特殊的字符来表示这段前缀
倒排记录表的压缩
每个倒排记录表用文档id表示
思路:文档id之间的间距不大,可以选择用间距来表示
数值越小,所需的存储空间越少
举例:文档id 33 47 154 159 202
使用间距33 14 107 5 43
倒排表的两种极端:
每个文档id用20个位存储
高频词:花20位存储,浪费空间。
低频词/罕见词:很少出现,花20位存储。
为了对间距进行空间压缩:
1.按字节压缩:对间距采用最短字节的方式进行编码
VB编码——根据间距的大小采用合适的字节进行编码
2.按位压缩:按位方式进行编码
Gamma编码——在更细的位粒度上进行编码长度的自适应调整
可变字节编码VB编码
VB(variable byte可变字节)编码:根据间距的大小采用合适的字节进行编码
思想:
- 字节的后七位是间距的有效编码区
- 间距使用整数字节进行编码。
- 每个字节的第一位是延续位,标识着本次编码的结束(1)与否(0)
- 一个字节有8位
编码:
- 转二进制
- 从低位(右边)每7位间隔
- 每7位补成8位,如果是最低字节(最右边)补1,否则全部补0直到8位
824的可变字节码如何计算
1.转二进制
1100111000
2.从低位(右边)每7位间隔
110 0111000
3.每7位补成8位,如果是最低字节(最右边)补1,否则全部补0直到8位
00000110 10111000
214577
1.转二进制
110100011000110001
2.从右边开始,每7位间隔
1101 0001100 0110001
3.最右一个字节最高位补1,剩下的全部补0到8位
00001101 00001100 10110001
每个间距花不同的字节数来存储,824用了2字节,214577用了3字节
只好用每个字节的最高位(延续位,区分字节的范围)来表示这个间距结束了没有
延续位如果为0,表示后面还是当前间距的字节,如果为1,后面不是当前间距,是下一个间距。
00000110 10111000 10000101 00001101 00001100 10110001
第一个间距的VB编码表示:00000110 10111000
第二个间距的VB编 码表示:10000101
第三个间距的VB编 码表示:00001101 00001100 10110001
一个字节本来有8位,最高位变成了延续,实际的数值,只在后7位。
在VB编码中,最高位是延续位(不是间距的数值),后7位才是间距的数
00000110 10111000
(一)给一个倒排表:824 829 215406,编码
1.先求间距:824 5 214577
2.一个一个的求VB编码
824:
00000110 10111000
5:后面没了,最高位一定要1,中间的补0
10000101
214577:
00001101 00001100 10110001
3.把上面的全部连起来
000001101011100010000101000011010000110010110001
(二)如何还原?解码
给一串VB编码:000001101011100010000101000011010000110010110001
题目:还原为原来的倒排表
1.每8个分一下(人阅读方便,程序没有这个步骤)
00000110 10111000 10000101 00001101 00001100 10110001
2.分出来每个间距对应的VB编码,从左往右看,分出三个间距
00000110 10111000
10000101
00001101 00001100 10110001
3.将每个间距的VB编码还原为整数的间距,切出每个字节的低7位,连接,转十进制。
(a)
00000110 10111000 最高位不能算(延续位),
00001100111000
(b)
10000101取低7位得5
(c)00001101 00001100 10110001
000110100011000110001
每个字节取低7位,连接,转十进制,得214577
4.得到间距
824 5 214577
5.转为原始的倒排表
824 829 215406
十进制为例:
4568,取低两位,4568%pow(10,2) mod
45689411220,取低7位,45689411220%pow(10,7)
要取低7位,mod pow(10,7)
二进制,取低7位
n mod pow(2,7)
n mod 128就是取低7位。
128本身是pow(2,7)
mod 128:模128 % 取余数
div 128:除128 / 整数除
1457 取低2位 1457%100(除100的余数,模),57
1457 1457/100(整数除),得到14,相当于把低2位去掉。
十进制:div pow(10,2),相当于十进制,相当于把低2位去掉
二进制:div pow(2,7),相当于把低7位去掉
十进制,取低n位,模pow(10,n)
二进制,取低n位,模pow(2,n)
二进制取低7位,除以pow(2,7),模128
n mod 128,取n的低7位
7个位7个位处理,处理完下一组低7位。
算法:
可变字节编码
VB编码 variable byte:
VBDecode(bytesstream):bytessteam:字节流
00000110 10111000 10000101 00001101 00001100 10110001——字节流
128:10000000
1.numbers:放最终结果,初始化为空
2.n:一个个解码,解完后添加到numbers
3.遍历字节流中的每一个字节
4.判断每一个元素是否<128,判断最高位是0还是1
5.<128
6~7.>128
6. -128相当于把最高位的1去掉(最高位是延续位,又不是数据,)
7.把求完的结果append往后加到numbers中
8.把n置0,用于求下一个数
9.返回最终结果
将字节流中的8位,转换成数字
'1457'将字符串转换为整数
n=0
循环:1
n=10*n+i
1. n=10*0+1=1
2. n=10*n+4=14
3. n=10*n+5=140+5=145
4. n=10*n+7=145*10+7=1457
十进制乘10
n=0
00000110 10111000 10000101 00001101 00001100 10110001
n=128*0+00000110=00000110
n=128*(00000110)+(10111000-10000000)
1100000000
0000111000
1100111000
128*二进制,相当于后面多了7个0
可变字节编码,以字节为单位。
gamma 编码
- 位方式编码可以得到更高的压缩率
- Gamma编码是最著名的.
- 将间距 G 表示为长度和偏移
- G的偏移实际上是G的二进制表示去掉了前端的1
- 例13 → 1101 → 101
- 长度指的是偏移的长度
- 例13 (偏移 101), 长度是3.
- 长度采用一元编码: 1110.
- 13的Gamma编码是长度与偏移的连接: 1110101
特性:
- G 编码使用了 2 ëlog Gû + 1 bits
- 偏移的长度是ëlog Gû bits
- 长度的长度是 ëlog Gû + 1 bits
- g编码的长度永远都是奇数位
- 与最优编码长度log2 G只相差一个因子2
- g编码是前缀无关的,即一个g编码不会是另一个g编码的前缀.
- g编码适用于任意分布
不常用:
- 机器有字长限制– 8, 16, 32, 64 bits
- 跨越机器字的操作非常缓慢
- 可变字节编码基于字节,因此比g编码更加高效
- 如不考虑效率,可变字节编码比g编码消耗的磁盘空间更大一点
按位操作,很慢
按字节操作,很快
尽量按字节操作
按位有可能更节省空间,变慢。
折中,压缩率高?速度?
倒排表压缩:可变字节编码(以字节为单位,最少也要使用一个字节进行存储)/Gamma编码(按位存储,最低可以只用1~2位存储,压缩率更高)
1
可变字节:10000001,需要8位表示1
Gamma编码:0,需要1位表示1
一元编码是Gamma编码的一部分。
824 5 214577
5的偏移01
00
101
偏移:转二进制,最高位1去掉
1的偏移:转二进制为1,也要去掉最高位
13:
先求偏移:转二进制最高位1去掉101
偏移:101
长度:偏移的长度,3,用一元编码1110
Gamma编码:长度连接偏移1110101
24:
偏移:1000
长度:4,一元:11110
结果:111101000
2:
偏移:0
长度:1,一元:10
连接:100
1:
偏移:没有
长度:0
连接:0
以字节操作:快
不是以字节/按位操作:慢