Milvus 实战 | 基于分区表实现结构化数据与非结构化数据的混合查询

五迷三道 提交于 2020-03-12 21:44:50

通过深度学习的神经网络模型,可以将图片、视频、语音、还有文本等非结构化数据转换为特征向量。除了结构化的向量,这些数据往往也需添加其他属性。如人脸图片,可以添加性别、是否戴眼镜、图片抓取时间等标签;文本可以添加语言类型、语料分类、文本创建时间等标签。由于这些特性,往往需要实现结构化数据和非结构化数据的混合查询。

Milvus 是一款开源的、针对海量特征向量的相似性搜索引擎,在此之前,Milvus 曾给出过一个结合 PostgreSQL 实现混合查询的方案。该方案是先将特征向量在 Milvus 内做相似度检索,然后 Milvus 返回的结果集再通过 PostgreSQL 过滤结构化属性,得到最终结果。由于是先做向量的相似度检索,然后再从相似度搜索得出的结果中筛选满足属性条件的结果,可能会造成结果集有损失,因为满足属性的一些结果集可能在向量相似度检索时已经被过滤掉了,而相似度靠前的向量集却不满足附加的属性条件。

得益于 Milvus 0.6.0 新增的分区功能,上述问题在 Milvus 0.6.0 有了新的解决方案,同时 Milvus 的应用场景也更加广阔( Milvus 分区功能详解: Milvus 0.6.0新增功能:分区表)。那么,Milvus 分区功能是如何来解决混合查询的问题呢?

 

解决方案

将非结构化数据对应的多个属性标签组合为一个字符串作为 Milvus 一个分区的标签,特征向量将按分区存储,检索时根据属性在对应的分区检索,就能快速得到非结构化和结构化数据混合过滤的结果。具体流程如下图所示(橙色线条表示存储过程,蓝色线条表示检索过程):

本文将通过一个用例来详细说明如何使用 Milvus 分区功能实现结构化数据与非结构化数据的混合查询。

 

场景说明

底库中有数亿张图片,通过深度学习模型将其转化为特征向量,每一张图片有其对应的属性(图片获取时间,性别,是否戴眼镜)。知道待查询图片的部分属性,需要在底库中快速找出满足该属性并给定图片相似的前 N 张图片。

利用 Milvus 分区功能,将具有相同属性的向量归为同一个分区,并将这些属性标签用一个字符串(例如:'2019-11-22/female/False’)代替作为该分区的标签(Partition_tag)。在进行检索时,将过滤条件按相应的规则转化为字符串在对应的一个或多个分区里查找相似向量。在选择分区时,支持用正则表达式匹配相对应的分区。

 

数据准备

本次实现的 Milvus 混合查询,其特征向量提取自ANN_SIFT1B中 Base Set 文件中的一亿数据( 128 维),特征向量检索时从 Query set 中提取,假定 ANN_SIFT1B 数据集为人脸特征向量。并随机的生成性别、图片抓取时间、是否戴眼镜的标签,将这三个属性标签合为一个字符串作为分区的 partition_tag。本文所述用例中,一共随机生成了十个分区标签。

# 随机生成性别、是否戴眼镜、图片抓取时间等标签
sex = random.choice(['female','male'])
get_time = fake.date_between(start_date="-30d", 
end_date="today")
is_glasses = random.choice(['True','False'])
#将三个属性组合为一个字符串作为分区标签
p_tag = str(get_time)+"/"+ sex +"/"+ str(is_glasses)

功能实现

1. 特征向量存储

数据准备完成之后需要在 Milvus 中创建一张表,同时按照上述产生的分区标签在该表上创建十个分区,每个分区有其对应的分区名和分区标签(各分区名和分区标签不能重复)。然后将向量导入对应的分区里。同一个分区里的特征向量其属性标签相同。数据导入 Milvus 后,其返回的 ids 是对各向量的唯一表示(该 ids 可自定义):

#创建一个分区
milvus.create_partition(MILVUS_TABLE, partition_name
=partition_name, partition_tag=partition_tag)
#将向量导入指定的分区中
status, ids = milvus.add_vectors(table_name=
MILVUS_TABLE, records=vectors, ids=vectors_ids, 
partition_tag=partition_tag)

2. 特征向量检索

向 Milvus 中传入需要搜索的向量。设定 TOP_K = 10 ,TOP_K 表示搜索与查询向量相似度最高的前 10 个结果。在检索时,按照给定的属性条件,Milvus 会在指定的一个或多个分区进行查找(指定 partition_tag 时支持正则表达式)。

由于分区标签是按照 'time/sex/glasses’ 方式设定的,在查询前可按如下方式指定要查询的分区。

#已知所有属性(时间、是否戴眼镜、性别),明确指定一个分区
$ partition_tag1 ='2019-11-22/female/False'
#指定部分属性对应的分区(性别)
$ partition_tag2 ='.*/male/.*'
#指定部分属性对应的分区(时间、性别)
$ partition_tag3 ='2019-12-21/male/.*'
#指定分区在某个月且性别为男:
$ partition_tag4 ='2019-12-.*/male/.*'

在指定分区里采用欧氏距离计算两个向量的相似度,进行查询时 Milvus 查询结果将返回向量对应的 ids 。

#指定一个分区查询
status, results = milvus.search_vectors(MILVUS_TABLE
, query_records=vector, top_k=10, nprobe=64, 
partition_tags=[partition_tag1])

#在多个分区查询
status, results = milvus.search_vectors(MILVUS_TABLE
, query_records=vector, top_k=10, nprobe=64, 
partition_tags=[partition_tag1, partition_tag2, 
partition_tag3])

在对一亿数据进行混合查询时,Milvus 在秒内即可得到检索结果。

上述解决方案的实现案例可以参考:

https://github.com/milvus-io/bootcamp/tree/0.6.0/solutions/partition_hybrid_search

 

相比于结合 PostgreSQL 实现的混合查询方案,基于分区功能实现的混合查询更贴近实际需求,即先根据已经确定的属性条件过滤,然后在过滤后的向量集中做向量的相似度检索,最后得到的结果集将是符合预期的。

属性标签类别较少时,基于 Milvus 的分区功能实现特征向量与结构化数据的混合查询是一个不错的选择。其良好的性能、简易的操作和维护等都将会带给你极好的体验。

 

| 欢迎加入 Milvus 社区

milvusio.slack.com | Slack 社区

zhihu.com/org/zilliz-11/columns | 知乎

zilliz.blog.csdn.net | CSDN 博客

© 2020 ZILLIZ

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!