Given a set S⊂U of n points, a metric d defined on U, and a query point v, the nearest neighbor search (NNS) problem finds v's nearest point in S:
u=args∈Smind(v,s)
Potential applications include web search and clustering.
If U is a Euclidean space Rd for some d, the hardness of NNS depends largely on the value of d. Suppose d=1, then we can pre-process the points by building a balanced binary search tree on S. Finding the nearest point to v∈/S reduces to searching for its predecessor and successor, which takes O(logn) time. For d=2, we can construct a Voronoi diagram.
For higher dimensions d it is not easy to extend the index structures. A naïve search loops over all points and takes O(nd) time. It is natural to ask whether we can do better. If we are willing to sacrifice accuracy for efficiency, this is possible. Instead of finding the exact nearest neighbor, we relax our goal to finding an approximate nearest neighbor whose distance is within 1+ϵ of the minimum one.
One possible solution is Johnson–Lindenstrauss decomposition. We can generate a matrix A∈RO(ϵ2logn)×d and multiply it with every element in S. The mapping u→Au preserves pair-wise distance up to a relative error of ϵ. The pre-processing takes O(ϵ2ndlogn) time. To answer an query, we map v to Av, which takes O(ϵ2dlogn) time. Then we perform a loop over all elements of {Au:u∈S}, which takes O(ϵ2nlogn) time. Therefore, the total time complexity is O(ϵ2(n+d)logn).
Hashing
[1] solves the problem by hashing. We call the pair of points that are close to each other the similar pair and the pair that are far from each other the dissimilar pair. Intuitively, we want similar pairs to be hashed to the same bucket and that dissimilar pairs to be hashed to different buckets.
Instead of finding the (1+ϵ) approximate nearest neighbor directly, [1] further reduces it to the (c,r)-approximate neighbor search (ANNS(c,r)) problem, which is defined as follows:
Given a query point v, if ∃s∈S, then return a point u, such that
d(v,u)≤c⋅r
The reduction from 1+ϵ to ANNS(c,r) is not easy. Here we show one that returns the (1+ϵ)2 approximate nearest neighbor. Let dmax=maxs,s′d(s,s′) and dmin=mins,s′d(s,s′). We build a set of structures that answers ANNS(1+ϵ, r) queries for the values of r:
dmin,dmin(1+ϵ),dmin(1+ϵ)2,...,dmax
Then given the query point v, we do a binary search to find the smallest r that returns a point. This imposes an additional cost of lnln(1+ϵ)lndmindmax.
NNS⟶(1+ϵ)NNS⟶ANNS(c,r)⟶(r,cr,p1,p2)-LSH⟶Boosting Probability
The Problem Reduction Chain
(r,cr,p1,p2)-Locality Sensitive Hashing Family
Now we show how to solve the ANNS(c,r) problem with hashing. Designing a perfecting hashing that solves the problem directly is not easy. It is desirable to have some hashing which successes with some probability and then boost its probability systematically. To achieve this, we introduce the idea of (r,cr,p1,p2)-locality sensitive hashing family.
Given a set of point V, a metric d(⋅,⋅) defined on V, a collection of hash function H={h:V→Z} is called (r,cr,p1,p2) locality sensitive if for any v,u∈V and an h sampled uniformly from H, it holds that
If d(v,u)<r, then Prh∈H[h(v)=h(u)]≥p1.
If d(v,u)≥cr, then Prh∈H[h(v)=h(u)]≤p2
where p2<p1≤1. Intuitively, pairs near to each other should be more likely to collide. Hence p1=p2ρ for some ρ=logp1/logp2<1.
Ideally, we have p1≈1 and p2≈0. For other cases we can magnify the gap between p1 and p2 and boost p1 to approximate 1 and p2 to approximate 0. The hardness of the boosting highly depends on the value of c.
Example
Suppose that U={0,1}d, d(u,v)=u⊕v=∣u−v∣1 and H={hi(u)=ui}i∈[d], where hi selects the ith bit of u. Picking an h uniformly from H and applying h to u is equivalent to selecting a bit uniformly from u. Therefore,
If d(v,u)<r (u and v has less then r common bits), then Prh∈H[h(v)=h(u)]≥dd−r=1−dr=p1.
If d(v,u)≥cr, then Prh∈H[h(v)=h(u)]≤dd−cr=1−dcr=p2
Now p1≈exp(−r/d) and p2≈exp(−cr/d). Hence ρ≈c1. Indeed it can be proven rigorously that
ρ=ln(1−dcr)ln(1−dr)≤c1
as (1−dr)c≥1−dcr. There are many ways to prove this. Since we are dealing with probability, we give a probabilistic interpretation of this inequality. Suppose that are c independent events each happening with probability dr. Then the probability that at least one of them happens is given by
1−Pr[none of them happens ]=1−(1−dr)c
On the other hand, by union bound, this is upper bounded by dcr.
Boosting the Probabilities
We would like the boost the probability such that p1≈1 and p2≈0. We follow a two-step approach. First we sample k (to be determined) hash functions independently from H can concatenate them as a single hash function g:
g(v)=[h1(v),h2(v),...,hk(v)]
Here g hash a point v to a k dimensional vector. After this, g(v)=g(u) happens only if hi(v)=hi(u) for all i∈[k]. By independence of hi's,
If d(v,u)≥cr, the collision probability of u with v drops to p2k:
Pr[g(v)=g(u)]≤p2k
On the other hand, if d(v,u)<r, the collision probability also drops to p1k=(p2k)ρ
Pr[g(v)=g(u)]≥p1k=p2ρk=(p2k)ρ
which is relatively small and not desirable.
We need a second level of boosting to increase the collision probability of similar pairs. Instead of using only one function g, we use a collection of independent copies:
g1,g2,...,gl
where l is the number to be determined. Now,
If d(v,u)<r, the probability that at least one of the gi(u) equals to gi(v) is given byPr[∃i∈[l],s.t.,gi(u)=gi(v)]=1−(1−p1k)l=1−(1−(p2k)ρ)l≥1−exp(−(p2k)ρl)
We can take
p2k=n1, i.e., k=log1/p2logn.
exp(−(p2k)ρl)=n1, i.e., l=(p2k)ρ1lnn=nρlnn.
For a fixed query point q and for any fixed i∈[l], by linearity of expectation, the expected number of dissimilar collision is given by
Therefore, there are l of dissimilar collisions over all i∈[l]. This implies an expected overhead of l examinations of dissimilar points. However, by Markov inequality, the number of such points is less than 2l with probability at least 1/2. The time for examination of dissimilar point is O(ld)=O(dnρlnn).
Time and Space
Now, given a query point q, computing gi(q) for a fixed i takes O(k) time. Computing l of them takes O(kl) time. In expectation, there are l dissimilar collision points with q. Checking one collision point takes O(d) time. Therefore, the expected query time is given by (note that we stop upon finding the first ANN of q):
As for the space usage, for each point u in S, we need to compute gi(u) and store this value (we need to store this, because gi(u) is self could be a large number and when putting {gi(u):u∈S} into a hash table, we need to treat gi(u) as the key and store it for later collision comparison. But is it possible that when there is a collision, we re-compute this value again to avoid storing it ?). We need to repeat this for l hash functions. Therefore, the space usage is given by
O(nkl)=O(n1+ρln2n/lnp21)
Another Boosting Scheme
In last section, we have a scheme that checks O(nρlnn) dissimilar points in expectation. In this section we achieve it with high probability.
First, to reduce the collision probability of dis-similar pairs, we concatenate k functions from H. This gives a(r,cr,p1k,p2k)locative sensitive hash function. We determine the value of k later.
Second, to boost the collision probability of similar pairs, we use l concatenated hash functions independently. The probability at least one collision occurs is given by1−(1−p1k)l≥1−e−p1k⋅lWe require this to happen with constant probability 1−e1, hence we need to set l=p1k1=(p2k1)ρ.
Now consider a query point q. We compute g1(q),g2(q),...,gl(q) and check the points u's such that gi(u)=gi(q) for some i∈[l]. Note that we check at most the first 4np2kl+1 points we found. Why 4np2kl+1? In expectation, there are np2kl dissimilar collisions. By Markov inequality, the probability of dissimilar collisions is no more than 4l is at most 1/4. By union bound,Pr[∃ more than 4l bad collision ∨∄ good collision ]≤e1+41≤2.51+41=165
Therefore, the boosting strategy successes with constant probability. Now consider the running time of such boosting strategy. When a query point q comes, we need to compute g1(q),g2(q),...,gl(q), which takes time kl. Further, the strategy checks at most 4np2kl+1 points and checking each point takes O(d) time. The overall time used is
O(kl+(4np2kl+1)d)
To minimize the running time, we need to choose proper value of k, such that
k=4np2kd→lnk+klnp21=ln(4nd)
It suffice to take k=lnp21ln4nd. It follows that
p2k=4nd1l=(p2k1)ρ=(4nd)ρ
and the running time is given by
O((4nd)ρlnp21ln(4nd))
As n≻d, this is equivalent to
O((4nd)ρlnp21lnn)
We can repeat the above steps by lnn/ln516 times, to boost the success probability to 1−n1. The overall running time is given by
O((4nd)ρlnp21ln2n)
Reference
Indyk, P. and Motwani, R., 1998, May. Approximate nearest neighbors: towards removing the curse of dimensionality. In Proceedings of the thirtieth annual ACM symposium on Theory of computing (pp. 604-613).