12 | 局部敏感哈希:如何在常数时间内搜索Embedding最近邻?
12 | 局部敏感哈希:如何在常数时间内搜索Embedding最近邻?
讲述:王喆
时长15:50大小14.46M
推荐系统中的“快速”Embedding 最近邻搜索问题
使用“聚类”还是“索引”来搜索最近邻?
局部敏感哈希的基本原理及多桶策略
1. 局部敏感哈希的基本原理
2. 局部敏感哈希的多桶策略
局部敏感哈希实践
小结
课后思考
赞 28
提建议
精选留言(40)
- Dikiwi2020-11-01b 是 0 到 w 间的一个均匀分布随机变量,避免分桶边界固化。这是什么意思呢?是说可以通过调整b来形成另外一个一个hash函数?
作者回复: 这是个好问题,推荐其他同学也关注。 因为如果总是固定边界,很容易让边界两边非常接近的点总是被分到两个桶里。这是我们不想看到的。 所以随机调整b,生成多个hash函数,并且采用或的方式组合,就可以一定程度避免这些边界点的问题。
62 - kaijien2020-10-31老师您好,您提到点数越多越应该增加桶的个数,还有Embedding维度越大越应该增加哈希函数并多用且的方式,那从您的经验上: 1 每个桶维持多少个点比较好? 2 Embedding一般多少算大?比如768维是否应该用且的方式?应该用多少哈希函数比较好?
作者回复: 1、每个桶取多少点跟你在线上想寻找top N的规模有关系。比如召回层想召回1000个物品,那么N就是1000,那么桶内点数的规模就维持在1000-5000的级别是比较合适的。当然了点数还跟你想取且还是或,有多少个哈希函数有关系,但基本上需要跟N在一个量级且高于N。 2、Embedding在实践中其实很少取768那么高的维度,我们训练模型时候的经验是,超过100维后,增加维度的作用就没那么明显了,通常取10-50维就足够了。比如说50维,这其实已经是非常高维的embedding了,我推荐用比较复杂一点的操作,比如取5个哈希函数,同时落在3+个桶里的点作为候选点。 但还是那句话,要自己观察数据,观察LSH的召回率如何,因为每家的数据都不一样,从别人那得来的经验经常不奏效是很正常的。
共 2 条评论37 - Alan2021-03-03悄悄告诉大家:embedding层K值的初始判断,有个经验公式:K= Embedding维数开4次方 ,x=初始的维度数; 后续,K值调参按照2的倍数进行调整,例如:2,4,8,16;
作者回复: 赞,这个我也不知道,非常感谢分享!
共 4 条评论37 - Alr2020-12-25课后思考问题:以item_id作为key, item_id对应的BucketId作为value存储在redis, 再以每个BucketId作为key, item_id作为value存储在redis, 在召回的时候遍历item_id的所有BucketId,获取BucketId对应的item_id就是需要召回的item, 请问老师这个思路对吗
作者回复: 对的。就是建立倒排索引的思路。
24 - 浣熊当家2020-10-31请问老师关于这句话 “在训练物品和用户的 Embedding 向量时,如果二者的 Embedding 在同一个向量空间内”, 我们在之前6-7节embedding的中,讲了怎么把物品序列信息转化为embedding, 想知道,用户的embedding是怎么生成的呢? 然后,物品和用户在同一个向量空间,这个是怎么得到的呢?
作者回复: 好问题。在咱们的项目里,用户embedding就是通过平均这个用户评论过的高分电影的embedding得到的。 所以他们肯定是在一个向量空间里。 只要是利用用户历史的item embedding生成的用户embedding,可以说都是在一个向量空间内,这些生成方式包括但不限于average pooling,sum pooling,attention等等。
共 2 条评论15 - 范闲2020-12-02LSH也有自己的问题。数据量太大的时候,hash的个数不好选择,另外存在hash冲突,容易降低召回率。 同基于树的,基于量化的,基于的图的方法来比,在召回率,速度和内存使用上都不占优势。
作者回复: 没有magic嘛,各种方法都有优势,适用的数据量也不同。所以facebook在faiss里面其实是融合了多种索引方式,大家有兴趣还是推荐深入去看一下faiss的原理。
12 - haydenlo2020-11-07请问对于计算距离,欧几里得距离和余弦距离等应该怎么选择?
作者回复: 根据你训练embedding时候选择的相似度来确定。 比如你训练embedding模型时就采用了欧式距离,那么这里就用欧式距离。训练模型时用了内积,这里就用内积。
9 - Infp2021-07-26本人用过faiss,LSH无论是召回率还是速度方面都不是很好。基于图的HNSW或者HNSWSQ是比较好的索引方式,当然缺点是会占用较大的存储空间,还有很多其他索引方式,可参考faiss的GitHub介绍。另外,faiss的wiki里面有关于如何选择索引的指南,有需要的同学可以去了解一下:https://github.com/facebookresearch/faiss/wiki/Guidelines-to-choose-an-index。
作者回复: LSH只是ANN的一种解决方法,Faiss采用了多种索引的结构,可以扩展学习一下。
5 - Yang Hong2021-07-20课后思考: 离线训练:LSH model为每个item embedding生成m个分桶,同时为每个user embedding生成m个分桶。 离线存储:1)在redis中存储item的分桶结果,key为item_id, value为item对应的BucketId;建立倒排索引,再以每个BucketId作为key, value为对应的item_id;2)在redis中存储user的分桶结果,key为user_id, value为user对应的BucketId; 在线召回:1)取出目标user的user embedding,和user对应的BucketId;2)查询redis分别获取m个BucketId对应的item_id,用且/或的多桶策略找到需要召回的item。 不知道这个思路对不对。展开
作者回复: user emb不用预存分桶,保存hash函数在线算就可以
5 - JustDoDT2020-11-01如何精确的控制每个桶内的点的规模是 C?
作者回复: 很难精确控制每个桶内的规模是C,但能通过控制桶的宽度w,来大概控制桶的规模在C附近。去掉一些噪声点后,如果点的分布比较均匀,那么每个桶的规模就比较稳定。
5 - 梁栋💝2021-01-27分桶宽度怎么决定的,非常迷惑。4
- 那时刻2020-10-30请问老师局部敏感哈希里的minhash和simhash是否有应用呢?
作者回复: 这是个好问题,但我觉得自己思考不难得出结论。minhash和simhash主要用在文档去重这样的场景,你觉得能不能把minhash和simhash应用在embedding的分桶过程中?如果可以的话,应用过程是什么呢?
共 2 条评论4 - 马龙流2020-11-14Embedding 向量的维度越大,我们越应该增加哈希函数的数量,尽量采用且的方式作为多桶策略;这话怎么理解呢?还有就是faiss这种,里面用到的就是局部哈希原理?
作者回复: LSH是Faiss index选择的一种,Faiss的详细细节太多,选择也比较多,需要参照开源项目展开学习。 Embedding向量的维度越大,单个LSH分桶错误的概率就越大,多个分桶联合才更容易找到相似物品。
3 - 魔法海2021-07-07老师,遇到个问题,在使用faiss的时候,cpu占用率在train的时候一直是100%。网上设置的两个参数: 1. export OMP_WAIT_POLICY=PASSIVE已经加入了环境变量 2. faiss.omp_set_num_threads(4) 其他过程都ok,就是在train和add的时候一直是100%。这个需要怎么修复?2
- 挖掘机2021-06-15如何判断用户和物品是否在一个向量空间呢?我看后面双塔的时候又说物品和向量不在一个空间,这是如何判断的呢?
作者回复: 用户和物品向量直接dot prodcut或者cosin similarity交叉,那么在一个空间内,如果经过了concat层,或者MLP进行交叉,那么不在一个空间内
2 - 梁栋💝2021-01-06课后思考: 1)首先user embedding是基于历史浏览item embedding平均后生成的,这个一般是online实时计算的。 2)当拿到user embedding后,我们需要离线训练好的LSH model对这个emb向量求出3个分桶。 3)拿到3个分桶,我们需要召回3个桶内的item embedding到内存,再进行Online计算求最近距离。 那么难点在于: 1)冷启动用户没有历史行为,无法用。 2)LSH model怎么导出到online服务使用。展开
作者回复: 过程基本正确。但难点2不太成立,就是把每个item和user emb的桶id存到redis就可以了,或者建立倒排索引,这个过程是比较直接的。
共 3 条评论2 - 杨佳亦2020-12-31请问老师,为什么: Embedding 向量维度越大,越应增加哈希函数的数量,用“且”分桶;相反,Embedding 向量维度越小,我们越应减少哈希函数的数量,用“或”分桶? 我的理解是,Embedding维度较大,特征密集不好分,采用多个哈希函数做映射再取交的确可以找到相似的Embedding;Embedding维度较小,特征分散,需要的桶不多,用或可以增加结果数量。 但是疑虑在于,Embedding大,需要的桶也多,计算量岂不是会变得非常大?展开
作者回复: 是这样,Embedding维度大计算量大是肯定的。这本身就是一个tradeoff,有时候,为了线上过程更高效,减少emb的维度也是可以牺牲的选择。一切工程问题都是取舍的问题。
共 2 条评论2 - follow-fate2020-11-28facebook开源的faiss是不是可以替代LSH了?
作者回复: 可以。faiss是业界主流解决方案,LSH是它的原理之一,这里LSH主要是为了大家学习原理。
共 2 条评论2 - 。LEAF2020-11-19老师好,最后得到 每个电影的 分桶,比如[-2.0], [14.0], [8.0]],相当于再做召回的时候,比如使用“或”策略,就直接再剩余所有电影里找到 在[-2.0], [14.0], [8.0] 3个桶里的电影就可以了吧
作者回复: 是这样的。 如果觉得不保险,还可以在临近桶找,这里面还是一个召回率和准确率之间权衡的问题。
2 - ALAN2020-10-30老师,numhashtable为3,是指使用了3个分桶函数吗?
作者回复: 是的,所以得到了三个bucketid
2