Graph部分,通过先构建KNN图,再根据RNG Rule移除不符合要求的边,得到RNG。
KNN图是指对于样本数据中的每一个点,将其自身与K个近邻点连接而形成的图。
由于样本数据规模非常大,我们采用了一定的算法构建近似的KNN图,具体算法[1]如下:
由于每次随机划分一组子空间,会包含部分新的近邻点,而与之前划分的空间重叠的近邻点,可以将两组子空间构建的KNN子图连接成更大的KNN图。因此,划分次数越多,KNN子图越大,直到得到真实的KNN图为止。
例如,
第一次划分出一组子空间,样本中包含了两个p点的真实最近邻p4和p6,利用Brute-Force对该子空间构建KNN图后,P点会与这两个点连接,得到kNN子图。
在第二次划分子空间的时候,样本中包含了两个新的真实最近邻p3和p5,对该子空间构建KNN后,会使p点与p3和p5相连,此时的kNN图已经包含了4个p的真实最近邻。
继续重复多次划分并对划分的子空间构建KNN图后,会使p点与大部分的真实最近邻连通,从而构建比较接近真实KNN图的近似KNN图。
注:
[1]?算法来源:Scalable k-NN graph construction for visual descriptors.王静, 王敬东, 曾刚, 涂卓文, 甘瑞, 李世鹏. CVPR 2012.
[2]?上述的TPTree可以认为是KDTree的变种,在论文《Trinary-Projection Trees for Approximate Nearest Neighbor Search》中被提出。其与KDTree主要区别在于采用了不同的划分函数,相比KDTree能对空间更灵活地划分,如下图:
基于KNN图,我们需要根据RNG Rule删除不符合要求的边。这样做的目的是避免陷入局部最优。
RNG Rule:删除三角形中的最长的边。
对于KNN图,若点a, b, q相互连接,我们要分别计算3点的距离,删除最长的边。例如,图中需删除qb边,因为我们可以通过a从q访问到b。
确保安装以下依赖:
Window安装可以参考文档:Windows Installation。
克隆仓库mcirosoft/SPTAG。
<span style="background-color:var(--bgColor-muted, var(--color-canvas-subtle))"><span style="color:var(--fgColor-default, var(--color-fg-default))"><span style="background-color:var(--bgColor-muted, var(--color-canvas-subtle))"><code>git clone https://github.com/microsoft/SPTAG
</code></span></span></span>
进入克隆的仓库的目录,执行以下命令:
<span style="background-color:var(--bgColor-muted, var(--color-canvas-subtle))"><span style="color:var(--fgColor-default, var(--color-fg-default))"><span style="background-color:var(--bgColor-muted, var(--color-canvas-subtle))"><code> mkdir build
cd build
cmake -A x64 ..
</code></span></span></span>
注:如果提示“CMAKE Could not find Boost 1.67”,可以使用以下命令指定Boost目录。
<span style="background-color:var(--bgColor-muted, var(--color-canvas-subtle))"><span style="color:var(--fgColor-default, var(--color-fg-default))"><span style="background-color:var(--bgColor-muted, var(--color-canvas-subtle))"><code> cmake -DBOOST_ROOT=[your boost root] -DBOOST_INCLUDEDIR=[your boost include directory] -DBOOST_LIBRARYDIR=[your boost library directory] ..
</code></span></span></span>
其中,
BOOST_INCLUDEDIR
:C:\INSTALL_DIR\boost_1_67_0\
BOOST_LIBRARYDIR
:C:\INSTALL_DIR\boost_1_67_0\lib64-msvc-12.0
BOOST_ROOT
:C:\INSTALL_DIR\boost_1_67_0\boost
进入刚刚新建的目录,在Visual Studio中打开,编译运行。build
SPTAGLib.sln
编译完成后,会在目录下生成我们需要的内容,将这个目录添加到环境变量中。build/release
PYTHONPATH
打开Python,执行,若无报错则已完成SPTAG的构建。import SPTAG
完成了SPTAG的构建后,我们可以在Python中使用,下面是SPTAG的Python接口。详细使用内容请参考:SPTAG Quick Start。
初始化索引
<span style="background-color:var(--bgColor-muted, var(--color-canvas-subtle))"><span style="color:var(--fgColor-default, var(--color-fg-default))"><span style="background-color:var(--bgColor-muted, var(--color-canvas-subtle))"><code>SPTAG.AnnIndex(algo, type, dimension)
algo: 索引算法的类型,可选'BKT', 'KDT'
type: 向量数据类型,如'Float','Int8', 'Int16'
dimension: 输入向量的维度
</code></span></span></span>
设置构建参数
<span style="background-color:var(--bgColor-muted, var(--color-canvas-subtle))"><span style="color:var(--fgColor-default, var(--color-fg-default))"><span style="background-color:var(--bgColor-muted, var(--color-canvas-subtle))"><code> SPTAG.AnnIndex.SetBuildParam(key,value)
key: 参数名
value:参数值
</code></span></span></span>
构建索引
<span style="background-color:var(--bgColor-muted, var(--color-canvas-subtle))"><span style="color:var(--fgColor-default, var(--color-fg-default))"><span style="background-color:var(--bgColor-muted, var(--color-canvas-subtle))"><code> SPTAG.AnnIndex.Build(vectors, sample_num)
vectors: 输入向量数据集
sample_num: 输入向量的数量
</code></span></span></span>
保存索引
<span style="background-color:var(--bgColor-muted, var(--color-canvas-subtle))"><span style="color:var(--fgColor-default, var(--color-fg-default))"><span style="background-color:var(--bgColor-muted, var(--color-canvas-subtle))"><code> SPTAG.AnnIndex.Save(index)
index: 输出保存的索引名,加载索引时需指定
</code></span></span></span>
加载索引
<span style="background-color:var(--bgColor-muted, var(--color-canvas-subtle))"><span style="color:var(--fgColor-default, var(--color-fg-default))"><span style="background-color:var(--bgColor-muted, var(--color-canvas-subtle))"><code> SPTAG.AnnIndex.Load(index)
index: 待加载的索引名
</code></span></span></span>
搜索
<span style="background-color:var(--bgColor-muted, var(--color-canvas-subtle))"><span style="color:var(--fgColor-default, var(--color-fg-default))"><span style="background-color:var(--bgColor-muted, var(--color-canvas-subtle))"><code> SPTAG.AnnIndex.Search(query, k)
query: 查询向量
k: 指定返回前k个最近邻
</code></span></span></span>
添加至索引
<span style="background-color:var(--bgColor-muted, var(--color-canvas-subtle))"><span style="color:var(--fgColor-default, var(--color-fg-default))"><span style="background-color:var(--bgColor-muted, var(--color-canvas-subtle))"><code> SPTAG.AnnIndex.Add(vectors, sample_num)
vectors: 待添加向量数据集
sample_num: 待添加向量的数量
</code></span></span></span>
从索引中删除
<span style="background-color:var(--bgColor-muted, var(--color-canvas-subtle))"><span style="color:var(--fgColor-default, var(--color-fg-default))"><span style="background-color:var(--bgColor-muted, var(--color-canvas-subtle))"><code> SPTAG.AnnIndex.Delete(vectors, sample_num)
vectors: 待删除向量数据集
sample_num: 待删除向量的数量
</code></span></span></span>
下面我们提供了两个SPTAG的使用示例。
我们先随机生成包含100条2维的随机向量数据集
<span style="background-color:var(--bgColor-muted, var(--color-canvas-subtle))"><span style="color:#1f2328"><span style="color:var(--fgColor-default, var(--color-fg-default))"><span style="background-color:var(--bgColor-muted, var(--color-canvas-subtle))"><code>import numpy as np
d = 2 # Dimension
nb = 100 # Dataset size
np.random.seed(1234)
randomData = np.random.random((nb,d)).astype('float32')
</code></span></span></span></span>
利用生成的数据构建索引testIndex
<span style="background-color:var(--bgColor-muted, var(--color-canvas-subtle))"><span style="color:#1f2328"><span style="color:var(--fgColor-default, var(--color-fg-default))"><span style="background-color:var(--bgColor-muted, var(--color-canvas-subtle))"><code>import SPTAG
# Build index
algorithm = 'KDT' # 'BKT' or 'KDT'
distmethod = 'L2' # 'L2' or 'Cosine'
i=SPTAG.AnnIndex(algorithm,'Float',randomData.shape[1])
i.SetBuildParam("NumberOfThreads",'4')
i.SetBuildParam("DistCalcMethod",distmethod)
if i.Build(randomData,randomData.shape[0]):
i.Save("testIndex")
</code></span></span></span></span>
任意指定查询向量,在此我们指定查询向量xq为[0.2, 0.4]。加载索引,搜索并指定返回前4个与xq的最近邻。testIndex
<span style="background-color:var(--bgColor-muted, var(--color-canvas-subtle))"><span style="color:#1f2328"><span style="color:var(--fgColor-default, var(--color-fg-default))"><span style="background-color:var(--bgColor-muted, var(--color-canvas-subtle))"><code># Search
k=4 # Number of results to return
xq = np.array([0.2 , 0.4 ]).astype('float32') # The query vector
i = SPTAG.AnnIndex.Load('testIndex') # load index
result = i.Search(xq,k)
print(result[0]) # ids
print(result[1]) # distances
</code></span></span></span></span>
图片向量搜索的原理如下图:
首先,利用VGG16模型将图片数据集转换成向量,再利用SPTAG构建索引。
当给定查询图片时,我们利用同样的算法将图片转换成查询向量,使用查询向量在索引中搜索K个最近邻向量,搜索得到的结果中的metadata存储了向量对应的图片路径,通过该路径可以找出对应的图片。