信息检索 存在 查找相似的文本片段
丰富查询所用的数据 :
问1
输入的查询 R【{q0,q1,..qk}】 = 用户生成的查询【用户输入文本片段 q】 + 生成的合成查询【索引中已有的其他查询 Q】
那么必须匹配并让它们相似 输入查询与索引中已有的其他查询 。q 应当和 R中的qi 高度相似 。
文本嵌入到向量空间 :
词嵌入: 将文本或单词映射成向量 ;word2vec , GloVe
将问题移到向量空间。
在向量空间中,可以使用数学运算,例如对向量求和或计算距离,使用传统的向量聚类技术将相似的单词链接.
即 从单词生成向量 组合成 文本【句】向量;组合方法使用 向量求和(句子中单词向量和)
然后通过 角距离 就是判断 相似性。
优 : 对关键字匹配的补充,能体现 文本间语义相似性
问2 --> 最近邻问题
q 应当和 R中的qi 的角距离 小于 同 Q-R 中的角距离 则相似
虽然有很多算法可以快速解决低微空间的最近邻问题,
但是对于词嵌入生成的 高维向量 。应计算量难以满足,精确的方法会崩溃
最近邻检索(NN)和近似最近邻(ANN)检索
【近似近邻 ANN】如果可以接受 只得到部分最近邻或者稍接近的近邻 ,
近似最近邻检索利用数据量增大后数据之间会形成簇状聚集分布的特性,通过对数据分析聚类的方法对数据库中的数据进行分类或编码,对于目标数据根据其数据特征预测其所属的数据类别,返回类别中的部分或全部作为检索结果。
分层可导航小世界图【HNSW hierarchical navigable small world gragh 】 快速的近似近邻搜索算法
HNSW 中的搜索索引是一个多层结构,每一层都是一个邻近图。
从最上层的一个入口节点开始,在每一层上递归执行贪婪图遍历,直到到达最下层的局部最小值。
索引数十亿查询 面临挑战
问3 索引or 数据大小问题
索引 40 亿个 200 维的查询向量; 每一维 = 4 字节的浮点数
向量大小 = 3TB ; 40*200 *4 *100000000 =3200000000000 < 3TB = 3072*1024*1024*1024 =3298534883328
此外 还有方法中需要的额外搜索索引。
方法1 数据子集 限制索引中向量的数量。仅使用所有可用数据【3TB】的十分之一【300GB】
缺点是因为可供匹配的查询更少, 搜索质量受到影响。
方法2 量化
允许数据上的舍入错误 (5.232 == 5. )
每一维 = 4 字节的浮点数 --> 每一维 = 1 字节的浮点数 ==》 向量只有 1/4 【750GB】
方法3 Granne ( 基于图的近似近邻)解决内存问题 ,Cliqz 开发并使用它来查找类似的查询
查询向量的压缩表示形式 方法 1,2 证明了减少向量本身 的大小可以带来好处。
查询由单词组成,查询向量是词向量的和。
牺牲一些速度来降低 存储
创建查询向量 时是在需要时在进行计算
每个查询存储为一个整数 id 列表,每个单词的 id 用 3 个字节 。
每个查询 = 查询文本中单词数 * 3 查询表示的最终大小自然取决于所有查询中单词组合的数量
best restaurant of munich 存储为 [ i best ,i restaurant ,i of, i munich]
查询向量 = V i best + V i restaurant + V i of + V i munich
40亿个查询 3TB ==》【80GB】 > 1600 万*3; 40亿个查询文本有 1600 万个词向量 , 与原始查询向量相比大小减少了 97% 以上。
还有一个问题需要解决。对于单个搜索,我们需要访问图中的大约 200 到 300 个节点。每个节点有 20 到 30 个邻居。因此,我们需要计算从输入查询向量到索引中的 4000 到 9000 个向量的距离,而在此之前,我们需要生成这些向量。动态创建查询向量的时间代价是否过高?事实证明,使用一个相当新的 CPU,它可以在几毫秒内完成。
对于之前花费 1 毫秒的查询==》 现在需要大约 5 毫秒。同时将向量的 RAM 使用量减少 90%——这种是可接受。
问4 在向量的内存占用显著的大小缩减之后,限制因素变成了索引结构本身的内存需求。
Granne 中的图结构被紧凑地存储为每个节点具有可变数量邻居的邻接表。 因此,在元数据存储上几乎不会浪费空间。索引结构的大小在很大程度上取决于结构参数和图的属性。 为 40 亿个向量构建一个可用的索引,其总大小大约为 240GB
Granne 的一个重要特性是它能够将索引和查询向量进行内存映射。这使我们能够延迟加载索引并在进程之间共享内存。
索引和查询文件实际上被分割成单独的内存映射文件, 【将索引文件放在 SSD 上,将查询文件放在 RAM 】仍然可以获得合理的查询速度,而且不需要付出过多的 RAM
提高数据局部性 速度
索引放置在 SSD 上,每次搜索需要对 SSD 进行 200 到 300 次读取操作
可以对元素相近的向量进行排序,增加数据的局部性,进而使它们的 HNSW 节点在索引中紧密地排列在一起。
效果 1 数据局部性提高了性能,因为单个读取操作(通常获取 4KB 或更多)更可能包含图遍历所需的其他节点。
2 减少了在一次搜索中需要获取数据的次数。
能在更少的读取次数中获得更多的必需的数据
重新排列元素顺序并不会改变结果,而仅仅是加速搜索。只有特定的顺序才能加快速度,且找到最优排序
一种启发式方法是根据每个查询中最重要的单词对查询进行排序。查询向量 =【V I ...】
Baseline | Quantization | Granne (RAM only) | Granne (RAM+SSD) | |
---|---|---|---|---|
Memory GB | 3000+240 | 750 +240 | 80+240 | 80 -150 |
SSD GB | 240 | |||
Latency ms | 1 | 1 | 5 | 10-50 |
使用 Granne 来构建和维护一个数十亿的查询向量索引,用相对较低的内存需求实现了相似查询搜索
适用于任何元素可以由更小数量的块(例如查询和单词)生成的情况.
如果不是,那对原始向量使用 Granne,它需要更多的内存,如同其他库。