Graph部分
Graph部分,通过先构建KNN图,再根据RNG Rule移除不符合要求的边,得到RNG。
KNN图的构建
KNN图是指对于样本数据中的每一个点,将其自身与K个近邻点连接而形成的图。
由于样本数据规模非常大,我们采用了一定的算法构建近似的KNN图,具体算法[1]如下:
- 随机划分一组子空间。在这里我们可以采用不同的划分方式,SPTAG采用了TPTree[2]的方式划分,即通过构建一定深度的TPTree,取其中一个叶子结点的作为子空间。
- 对该子空间内的点,利用Brute-Force方式,构建KNN子图。
- 重复以上步骤N次。N越大,得到的KNN图越接近真实的KNN图。
由于每次随机划分一组子空间,会包含部分新的近邻点,而与之前划分的空间重叠的近邻点,可以将两组子空间构建的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能对空间更灵活地划分,如下图:
KNG的构建
基于KNN图,我们需要根据RNG Rule删除不符合要求的边。这样做的目的是避免陷入局部最优。
RNG Rule:删除三角形中的最长的边。
对于KNN图,若点a, b, q相互连接,我们要分别计算3点的距离,删除最长的边。例如,图中需删除qb边,因为我们可以通过a从q访问到b。
SPTAG的使用
安装
确保安装以下依赖:
- 斯威格>= 3.0
- cmake >=3.12.0
- 升压 == 1.67.0
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。
Python接口
-
初始化索引
<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存储了向量对应的图片路径,通过该路径可以找出对应的图片。