多媒体数据处理实验4:LSH索引

第4次实验: LSH索引

1. 算法描述

功能:

  在corel数据集上实现LSH(局部敏感哈希)索引,并对数据集前1000个点分别进行近邻搜索,查找各点的前10个最近邻,并统计搜索算法的性能(准确率、时间)。

2.算法流程:

  (1) 确定哈希函数。令 h ( x ) h(x) h(x)表示样本 x x x的哈希变换, x x x y y y是两个样本, d ( x , y ) d(x,y) d(x,y) x x x y y y之间的距离。若 h ( x ) h(x) h(x)满足以下两个条件,则称哈希函数 h ( x ) h(x) h(x) ( d 1 , d 2 , p 1 , p 2 ) − s e n s i t i v e (d_1,d_2,p_1,p_2)-sensitive (d1,d2,p1,p2)sensitive
若 d ( x , y ) ≤ d 1 ,则 h ( x ) = h ( y ) 的概率至少为 p 1 若 d ( x , y ) ≥ d 2 ,则 h ( x ) = h ( y ) 的概率至多为 p 2 若d(x,y)≤d_1,则h(x)=h(y)的概率至少为p_1 \\ 若d(x,y)≥d_2,则h(x)=h(y)的概率至多为p_2 d(x,y)d1,则h(x)=h(y)的概率至少为p1d(x,y)d2,则h(x)=h(y)的概率至多为p2
在本次实验中,由于我取欧氏距离作为两点之间距离的度量标准,因此我选择 H ( v ) = ⌈ v ⋅ r + b a ⌉ H(v) = \lceil\frac{v·r + b}{a}\rceil H(v)=avr+b作为哈希函数,其中 r r r是一个随机向量, a a a是桶宽, b b b是一个在 [ 0 , a ] [0,a] [0,a]之间均匀分布的随机变量。 v ⋅ r v·r vr可以看做是将 v v v r r r上进行投影操作。可以证明 H ( v ) H(v) H(v) ( a 2 , 2 a , 1 2 , 1 3 ) (\frac{a}{2},2a,\frac{1}{2},\frac{1}{3}) (2a,2a,21,31)-sensitive的。

  (2) 执行哈希映射和分桶策略。对于数据集中的每个点,用一个哈希函数 H i H_i Hi(即选定一组r,b值),将它们全部映射到某个桶 b u c k e t i bucket_i bucketi中。哈希映射的结果即为这些点在该桶中的索引键值。当给定桶数 m m m后,我们可以用按一定方法随机生成的 m m m个哈希函数将这些点映射到 m m m中,并得到这些点在各桶中的索引键值。

  (3) 执行检索策略。对于一个给定的查询点 q q q,我们先用上述 m m m个哈希函数将其映射到所有桶中,即得到该点在各桶中的索引键值。然后,我们可以通过这样的方法来确定近邻点的候选集 c a n d i _ s e t candi\_set candi_set:对于第 i ( i = 0 , 1 , . . . , m − 1 ) i(i=0,1,...,m-1) i(i=0,1,...,m1)号桶,找到在该桶中的索引与点 q q q相同的点,并将这 m m m个桶中找到的所有符合条件的点并起来,构成近邻点候选集。然后,对于候选集,我们通过线性搜索的方法来逐一计算 q q q c a n d i _ s e t [ i ] candi\_set[i] candi_set[i]之间的欧氏距离,并最终返回前10个最近邻在原始数据集中的索引号。

  (3) 计算准确性。我们可以通过暴力搜索的方法来得到前1000个点各自的10近邻的索引,并将其存放在矩阵中。由于这是一个时间复杂度非常高的过程,因此在第一次计算完毕之后,我就将该矩阵写入了csv文件中,方便下次直接读取调用。对于第 i i i个点,设通过LSH得到的10近邻索引存于列表 p r e d pred pred中,设该点的真实10近邻的索引存于列表 t r u e true true中,那么有:

TP = 既在pred中也在true中的索引个数
TN = 不在pred中也不在true中的索引个数
FP = 在pred中但不在true中的索引个数
FN = 不在pred中却在true中的索引个数

  得到了每个点的TP, TN, FP, FN之后即可利用下面的公式计算出该点的precision, recall, accuracy值:
p r e c i s i o n = T P T P + F P ,   r e c a l l = T P T P + F N ,   a c c u r a c y = T P + T N T P + T N + F P + F N precision = \frac{TP}{TP+FP},\ recall = \frac{TP}{TP+FN},\ accuracy = \frac{TP+TN}{TP+TN+FP+FN} precision=TP+FPTP, recall=TP+FNTP, accuracy=TP+TN+FP+FNTP+TN
  将1000个点的检索精准率、召回率、准确率取平均即可得到整个实验的最终精准率、召回率、准确率。

3. 核心代码

# 计算欧氏距离
# 不用numba加速,用范数计算快,但用了numba,普通计算更快
@nb.jit(nopython=True)
def calc_dist(x, y):
    return np.sqrt(np.sum((x - y) ** 2)) 
    # return np.linalg.norm(x - y)

# R: 32x桶数, b: 1x桶数, a: 标量
# 将向量映射到各桶的索引
def hash_and_fill(inputs, R, b, a):
    # 初始化空的hash_table
    buckets = [{} for _ in range(bucket_num)]
    mapped_idxes = floor((dot(inputs, R) + b) / a) # 68040x10,每一行是这个点在所有桶中的哈希值
    for i, hash_keys in enumerate(mapped_idxes):
        for j, hash_key in enumerate(hash_keys):
            # 每个桶是一个字典,其中的所有key对应该桶的所有索引键值
            buckets[j].setdefault(hash_key, []).append(i)
    return buckets  # 形如:[{8: [0, 2, 5,...], 12: [2, 12, 16,...] }, {}, {}, ...]

# 并集方法
# 查询与q相似的向量,并输出相似度最高的k个向量在原数据集中的索引
def find(q, k, R, b, a, buckets):
    hash_keys = np.floor((dot(q, R) + b) / a)[0]   # 不加[0]返回的是1xtables_num的矩阵,取[0]转为数组
    # 遍历q点的索引键列表,找各桶中与其索引键值相等的点
    for i, hash_key in enumerate(hash_keys):
        if i == 0:
            candi_set = set(buckets[0][hash_key])
        else:
            candi_set = candi_set.union(buckets[i][hash_key])  # 候选集
    candi_set = list(candi_set)    # 转为list便于遍历
    dist = [calc_dist(ColorHist[i], q) for i in candi_set]
    set_idxes = argsort(dist)[1: k + 1]  # set_idxes是近邻点在候选集中的索引,要将其转为在原数据集中的索引
    res = [candi_set[i] for i in set_idxes]
    return res

# 交集方法
# 查询与q相似的向量,并输出相似度最高的k个向量在原数据集中的索引
def find2(q, k, R, b, a, buckets):
    hash_val = np.floor((dot(q, R) + b) / a)[0]   # 不加[0]返回的是1xtables_num的矩阵,取[0]转为数组
    # 遍历q点的索引键列表,找各桶中与其索引键值相等的点
    for i, idx_key in enumerate(hash_val):
        if i == 0:
            candi_set = set(buckets[0][idx_key])
        else:
            candi_set = candi_set.intersection(buckets[i][idx_key])  # 候选集
    candi_set = list(candi_set)    # 转为list便于遍历
    dist = [calc_dist(ColorHist[i], q) for i in candi_set]
    set_idxes = argsort(dist)[1: k + 1]  # set_idxes是近邻点在候选集中的索引,要将其转为在原数据集中的索引
    res = [candi_set[i] for i in set_idxes]
    return res

4. 实验结果

  由于在本次实验中, F N = F P = 10 − T P FN=FP=10-TP FN=FP=10TP,因此有 p r e c i s i o n = r e c a l l precision=recall precision=recall,且 a c c u r a c y = T P + T N T P + T N + F P + F N = T P + 68040 − 10 − ( 10 − T P ) T P + 68040 − 10 − ( 10 − T P ) + ( 10 − T P ) + ( 10 − T P ) = 2 T P + 68020 68040 ≥ 99.97 accuracy = \frac{TP+TN}{TP+TN+FP+FN}=\frac{TP+68040-10-(10-TP)}{TP+68040-10-(10-TP)+(10-TP)+(10-TP)}=\frac{2TP+68020} {68040} ≥ 99.97% accuracy=TP+TN+FP+FNTP+TN=TP+6804010(10TP)+(10TP)+(10TP)TP+6804010(10TP)=680402TP+6802099.97,这表明 a c c u r a c y accuracy accuracy一直是一个接近100%的值,我们不能通过 a c c u r a c y accuracy accuracy判断实验结果的好坏。

  在本次实验中,我设置了若干组不同的参数,并记录了在不同参数组合下检索前1000个点的10近邻的运行时间和检索精准率(precision),表格如下:

a = 0.01a = 0.03a = 0.05a = 0.07
m = 350s 40.21%103s 74.67%177s 89.98%202s 93.68%
m = 563s 50.09%182s 89.72%224s 96.46%313s 98.92%
m = 10138s 76.73%288s 97.83%375s 99.74%476s 99.97%
m = 15160s 85.64%351s 99.45%425s 99.97%564s 100.0%

  为了加速计算过程,我采用了numba的@nb.jit(nopython=True)功能。Numba可以将Python函数编译为机器代码(Numba把Python源码通过LLVMPy生成JIT后的.so文件以实现加速),经过Numba编译的Python代码,其运行速度可以接近C或FORTRAN。

  对于本实验的距离计算而言,我测试了(1) 使用朴素的平方再开根求距离,(2) 使用np.linalg.norm(x - y)求距离,(3) 使用numba+平方再开根求距离,(4) 使用numba+np.linalg.norm求距离这四种求距离的方法,并且比较了它们求解两点距离的速度,发现使用np.linalg.norm比原始方法快30%,而使用numba加速后计算速度是原始方法的5倍

  使用numba加速后检索前1000个点的10近邻的运行时间和检索精准率(precision)如下:

a = 0.01a = 0.03a = 0.05a = 0.07
m = 38s 37.64%24s 74.67%24s 88.33%36s 95.10%
m = 510s 50.96%22s 86.70%30s 95.50%55s 99.37%
m = 1024s 74.67%40s 97.10%53s 99.74%70s 99.96%
m = 1534s 85.56%58s 99.54%69s 99.98%73s 100.0%

源码:多媒体数据处理实验4:LSH索引

  • 5
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Polaris_T

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值