Vearch 系统介绍

概述

Vearch 是对大规模深度学习向量进行高性能相似搜索的弹性分布式系统。

整体架构

Architecture

数据模型: 空间,文档,向量,标量。

组件: Master,Router,PartitionServer。

Master: 负责schema管理,集群级别的源数据和资源协调。

Router: 提供RESTful API: create、delete、search、update; 请求路由转发及结果合并。

PartitionServer(PS): 基于raft复制的文档分片; Gamma向量搜索引擎,它提供了存储、索引和检索向量、标量的能力。

功能简介

1、支持CPU与GPU两种版本。

2、支持实时添加数据到索引。

3、支持单个文档定义多个向量字段, 添加、搜索批量操作。

4、支持数值字段范围过滤与string字段标签过滤。

5、支持IVFPQ、HNSW、二进制等索引方式(HNSW、二进制方式4月下旬发布)。

6、支持Python SDK本地快速开发验证。

7、支持机器学习算法插件方便系统部署使用。

系统特性

1、自研gamma引擎,提供高性能的向量检索。

2、IVFPQ倒排索引支持compaction,检索性能不受文档更新次数的影响。

3、支持内存、磁盘两种数据存储方式,支持超大数据规模。

4、基于raft协议实现数据多副本存储。

5、支持内积(InnerProduct)与欧式距离(L2)方法计算向量距离。

安装使用

编译

环境依赖

  1. 支持CentOS, Ubuntu and Mac OS。推荐CentOS 7.2以上, 检查安装cmake、make工具。
  2. Go版本>=1.11.2。
  3. Gcc版本>=5。
  4. Faiss master分支(commit:833d417db1b6b6fd4b19e092f735373f07eab33f)。
  5. 如果使用RocksDB, 版本6.2.2, 使用RocksDB中INSTALL.md文件的make shared_lib进行编译。
  6. 如果使用GPU, CUDA版本>=9.0

编译

  • 进入GOPATH工作目录, cd $GOPATH/src mkdir -p github.com/vearch cd github.com/vearch

  • 下载源代码: git clone https://github.com/vearch/vearch.git (后续使用$vearch 代表vearch目录绝对路径)

  • 下载gamma代码: cd vearch git submodule update --init --recursive

  • 如果使用GPU版本, 修改$vearch/engine/CMakeLists.txt文件中BUILD_WITH_GPU值为on

  • 编译gamma

    1. cd $vearch/engine
    2. mkdir build && cd build
    3. export FAISS_HOME=faiss安装路径
    4. 如果使用RocksDB export ROCKSDB_HOME=RocksDB安装路径
    5. cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=$vearch/ps/engine/gammacb/lib ..
    6. make -j && make install
  • 编译vearch

    1. cd $vearch
    2. export FAISS_HOME=faiss安装路径
    3. 如果使用RocksDB export ROCKSDB_HOME=RocksDB安装路径
    4. export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$FAISS_HOME/lib:$ROCKSDB_HOME:$ROCKSDB_HOME/lib:$vearch/ps/engine/gammacb/lib/lib
    5. go build -a --tags=vector -o  vearch

    生成vearch文件表示编译成功

部署

单机模式:

  • 生成配置文件config.toml(master_server端口使用8817, router_server端口使用9001)
[global]
    # the name will validate join cluster by same name
    name = "vearch"
    # you data save to disk path ,If you are in a production environment, You'd better set absolute paths
    data = ["datas/"]
    # log path , If you are in a production environment, You'd better set absolute paths
    log = "logs/"
    # default log type for any model
    level = "info"
    # master <-> ps <-> router will use this key to send or receive data
    signkey = "vearch"
    skip_auth = true

# if you are master, you'd better set all config for router、ps and router, ps use default config it so cool
[[masters]]
    # name machine name for cluster
    name = "m1"
    # ip or domain
    address = "127.0.0.1"
    # api port for http server
    api_port = 8817
    # port for etcd server
    etcd_port = 2378
    # listen_peer_urls List of comma separated URLs to listen on for peer traffic.
    # advertise_peer_urls List of this member's peer URLs to advertise to the rest of the cluster. The URLs needed to be a comma-separated list.
    etcd_peer_port = 2390
    # List of this member's client URLs to advertise to the public.
    # The URLs needed to be a comma-separated list.
    # advertise_client_urls AND listen_client_urls
    etcd_client_port = 2370

[router]
    # port for server
    port = 9001

[ps]
    # port for server
    rpc_port = 8081
    # raft config begin
    raft_heartbeat_port = 8898
    raft_replicate_port = 8899
    heartbeat-interval = 200 #ms
    raft_retain_logs = 10000
    raft_replica_concurrency = 1
    raft_snap_concurrency = 1
  • 启动

启动vearch前,需要设置LD_LIBRARY_PATH环境变量的值,添加faiss, gamma, rocksdb等依赖的lib包(比如$vearch/ps/engine/gammacb/lib/lib、$FAISS_HOME/lib等目录下的lib包; 执行ldd vearch 和 ldd $vearch/ps/engine/gammacb/lib/lib/libgamma.so.0.1 命令可以查看vearch和gamma依赖的包)。

./vearch -conf config.toml

集群模式:

  • vearch 有三个模块: ps, master, router, run ./vearch -conf config.toml ps/router/master 启动相应模块

假如有5台机器, 2台作为master管理, 2台作为ps计算节点, 1台请求转发

  • master
    • 192.168.1.1
    • 192.168.1.2
  • ps
    • 192.168.1.3
    • 192.168.1.4
  • router
    • 192.168.1.5
  • 生成toml格式配置文件 config.toml, 作为master的机器ip配置在[[masters]]中,支持多个,router和ps所在机器ip无需配置。
[global]
    name = "vearch"
    data = ["datas/"]
    log = "logs/"
    level = "info"
    signkey = "vearch"
    skip_auth = true

# if you are master, you'd better set all config for router、ps and router, ps use default config it so cool
[[masters]]
    name = "m1"
    address = "192.168.1.1"
    api_port = 8817
    etcd_port = 2378
    etcd_peer_port = 2390
    etcd_client_port = 2370
[[masters]]
    name = "m2"
    address = "192.168.1.2"
    api_port = 8817
    etcd_port = 2378
    etcd_peer_port = 2390
    etcd_client_port = 2370
[router]
    port = 9001
    skip_auth = true
[ps]
    rpc_port = 8081
    raft_heartbeat_port = 8898
    raft_replicate_port = 8899
    heartbeat-interval = 200 #ms
    raft_retain_logs = 10000
    raft_replica_concurrency = 1
    raft_snap_concurrency = 1
  • 启动vearch前,设置LD_LIBRARY_PATH环境变量加载依赖包
  • on 192.168.1.1 , 192.168.1.2 run master
./vearch -conf config.toml master
  • on 192.168.1.3 , 192.168.1.4 run ps
./vearch -conf config.toml ps
  • on 192.168.1.5 run router
./vearch -conf config.toml router

集群监控

http://master_server代表master服务

集群状态

curl -XGET http://master_server/_cluster/stats

健康状态

curl -XGET http://master_server/_cluster/health

端口状态

curl -XGET http://master_server/list/server

清除锁

curl -XGET http://master_server/clean_lock

在创建表时会对集群加锁,若在此过程中,服务异常,会导致锁不能释放,需要手动清除才能新建表。

副本扩容缩容

curl -XPOST -H "content-type: application/json"  -d'
{
    "partition_id":1,
    "node_id": 1,
    "method": 0
}
' http://master_server/partition/change_member

method=0: node id 1上添加分片id 1 的副本; method=1: 删除 node id 1 上 分片 id 1 的副本。

库操作

http://master_server代表master服务,$db_name是创建的库名

查看集群中所有库

curl -XGET http://master_server/list/db

创建库

curl -XPUT -H "content-type:application/json" -d '{
    "name": "db_name"
}
' http://master_server/db/_create

查看库

curl -XGET http://master_server/db/$db_name

删除库

curl -XDELETE http://master_server/db/$db_name

若库下存在表空间则无法删除

查看指定库下所有表空间

curl -XGET http://master_server/list/space?db=$db_name

表空间操作

http://master_server代表master服务,$db_name是创建的库名, $space_name是创建的表空间名

创建表空间

curl -XPUT -H "content-type: application/json" -d'
{
    "name": "space1",
    "partition_num": 1,
    "replica_num": 1,
    "engine": {
        "name": "gamma",
        "index_size": 70000,
        "id_type": "String",
        "retrieval_type": "IVFPQ",
        "retrieval_param": {
            "ncentroids": 256,
            "nsubvector": 32
        }
    },
    "properties": {
        "field1": {
            "type": "keyword"
        },
        "field2": {
            "type": "integer"
        },
        "field3": {
            "type": "float",
            "index": true
        },
        "field4": {
            "type": "string",
            "array": true,
            "index": true
        },
        "field5": {
            "type": "integer",
            "index": true
        },
        "field6": {
            "type": "vector",
            "dimension": 128
        },
        "field7": {
            "type": "vector",
            "dimension": 256,
            "format": "normalization",
            "store_type": "RocksDB",
            "store_param": {
                "store_param": 2048,
                "compress": false
            }
        }
    }
}
' http://master_server/space/$db_name/_create

参数说明:

字段标识 字段含义 类型 是否必填 备注
name 空间名称 string  
partition_num 分片数量 int  
replica_num 副本数量 int  
engine 引擎配置 json 引擎配置
properties 空间配置 json 定义表字段及类型

1、name 不能为空,不能以数字或下划线开头,尽量不使用特殊字符等。

2、partition_num 指定表空间数据分片数量,不同的分片可分布在不同的机器,来避免单台机器的资源限制。

3、replica_num 副本数量,建议设置成3,表示每个分片数据有两个备份,保证数据高可用。

engine配置:

字段标识 字段含义 类型 是否必填 备注
name 引擎名称 string 目前固定为gamma
index_size 分片索引阀值 int  
id_type 唯一主键类型 string  
retrieval_type 检索模型 string  
retrieval_param 检索模型参数配置 json  

1、name 不能为空,不能以数字或下划线开头,尽量不使用特殊字符等。

2、index_size 指定每个分片的记录数量达到多少开始创建索引,不指定则不创建索引。

3、max_size 指定每个分片最多存储的记录数量,防止服务器内存占用过多。

4、id_type 指定唯一记录主键类型,支持String和Long(定义为Long可节省内存占用)。

5、retrieval_type 检索模型,目前支持六种类型,IVFPQ,HNSW,GPU,IVFFLAT,BINARYIVF,FLAT,不同的检索模型需要的参数配置及默认值如下:

IVFPQ:

字段标识 字段含义 类型 是否必填 备注
ncentroids 聚类中心数量 int 默认2048
nsubvector PQ拆分子向量大小 int 默认64, 值为4的倍数
"retrieval_type": "IVFPQ",
"retrieval_param": {
    "ncentroids": 2048,
    "nsubvector": 64
}

HNSW:

字段标识 字段含义 类型 是否必填 备注
nlinks 节点邻居数量 int 默认32
efConstruction 构图时寻找节点邻居过程中在图中遍历的深度 int 默认40
"retrieval_type": "HNSW",
"retrieval_param": {
    "nlinks": 32,
    "efConstruction": 40
}

注意: 1、向量存储只支持MemoryOnly
      2、创建索引不需要训练,index_size 值大于0均可

GPU(针对GPU编译版本):

字段标识 字段含义 类型 是否必填 备注
ncentroids 聚类中心数量 int 默认2048
nsubvector PQ拆分子向量大小 int 默认64, 值为4的倍数
"retrieval_type": "GPU",
"retrieval_param": {
    "ncentroids": 2048,
    "nsubvector": 64
}

IVFFLAT:

字段标识 字段含义 类型 是否必填 备注
ncentroids 聚类中心数量 int 默认256
 "retrieval_type": "IVFFLAT",
 "retrieval_param": {
     "ncentroids": 256
 }

注意: 1、向量存储方式只支持RocksDB

BINARYIVF:

字段标识 字段含义 类型 是否必填 备注
ncentroids 聚类中心数量 int 默认256
"retrieval_type": "BINARYIVF",
"retrieval_param": {
    "ncentroids": 256
}

注意: 1、向量长度是8的倍数

properties配置:

1、表空间结构定义字段支持的类型(即type的值)有4种: keyword,integer,float,vector(keyword等价于string)。

2、keyword类型的字段支持index、array属性,index定义是否创建索引,array指定是否允许多个值。

3、integer,float类型的字段支持index属性,index设为true的字段支持使用数值范围过滤查询。

4、vector 类型字段为特征字段,一个表空间中支持多个特征字段,vector类型的字段支持的属性如下:

字段标识 字段含义 类型 是否必填 备注
dimension 特征维数 int  
format 归一化处理 string 设置为normalization对添加的特征向量归一化处理
store_type 特征存储类型 string 支持MemoryOnly、Mmap和RocksDB, 默认MemoryOnly
store_param 存储参数设置 json 针对不同store_type的存储参数
model_id 特征插件模型 string 使用特征插件服务时指定

5、dimension 定义type是vector的字段,指定特征维数大小。

6、store_type 特征向量存储类型,有以下三个选项:

“MemoryOnly”:原始向量都存储在内存中,存储数量的多少受内存限制,适用于数据量不大(千万级),对性能要求高的场景

“RocksDB”:原始向量存储在RockDB(磁盘)中,存储数量受磁盘大小限制,适用单机数据量巨大(亿级以上),对性能要求不高的场景

“Mmap”:原始向量存储在磁盘文件中,存储数量受磁盘大小限制,适用单机数据量巨大(亿级以上),对性能要求不高的场景

7、store_param 针对不同store_type的存储参数,其包含以下两个子参数。

cache_size: 数值类型,单位是M bytes,默认1024。store_type=”RocksDB”时,它表示RocksDB的读缓冲大小,值越大读向量的性能越好,一般设置1024、2048、4096和6144即可;store_type=”Mmap”时,它表示写缓冲的大小,不用太大,一般512、1024或2048即可;store_type=”MemoryOnly”,它没有用。

compress: bool类型,默认false。true表示对原始向量进行压缩,一般会将原始向量压缩为原来的50%,可以节省内存和磁盘;false表示不压缩;目前支持RocksDB和MemoryOnly, Mmap暂不支持。

查看表空间

curl -XGET http://master_server/space/$db_name/$space_name

删除表空间

curl -XDELETE http://master_server/space/$db_name/$space_name

数据操作

http://router_server 代表router服务,$db_name是创建的库名, $space_name是创建的空间名, $id是数据记录的唯一id.

单条插入

插入时不指定唯一标识id

curl -XPOST -H "content-type: application/json"  -d'
{
    "field1": "value1",
    "field2": "value2",
    "field3": {
        "feature": [0.1, 0.2]
    }
}
' http://router_server/$db_name/$space_name

field1和field2是标量字段,field3是特征字段。所有字段名、值类型和定义表结构时保持一致。

返回值格式如下:

{
    "_index": "db1",
    "_type": "space1",
    "_id": "AW5J1lNmJG6WbbCkHrFW",
    "status": 200
}

其中_index 库名称, _type 表空间名称,_id 是服务端生成的记录唯一标识,可以由用户指定,对数据的修改和删除需要使用该唯一标识。

插入时指定唯一标识

curl -XPOST -H "content-type: application/json"  -d'
{
    "field1": "value1",
    "field2": "value2",
    "field3": {
        "feature": [0.1, 0.2]
    }
}
' http://router_server/$db_name/$space_name/$id

$id 是插入数据时使用指定的值替换服务端生成的唯一标识,$id值不能使用url路径等特殊字符。若库中已存在该唯一标识的记录则覆盖。

批量插入

curl -H "content-type: application/json" -XPOST -d'
{"index": {"_id": "v1"}}\n
{"field1": "value", "field2": {"feature": []}}\n
{"index": {"_id": "v2"}}\n
{"field1": "value", "field2": {"feature": []}}\n
' http://router_server/$db_name/$space_name/_bulk

json格式的变体,{“index”: {“_id”: “v1”}} 指定记录的id, _id值为空后台自动生成唯一id, {“field1”: “value”, “field2”: {“feature”: []}} 指定插入的数据,每行json字符串均以\n结尾。

更新

更新时必须指定唯一标识id

curl -H "content-type: application/json" -XPOST -d'
{
    "field1": "value1",
    "field2": "value2",
    "field3": {
        "feature": [0.1, 0.2]
    }
}
' http://router_server/$db_name/$space_name/$id/_update

请求路径中指定唯一标识$id,使用指定id插入的方式进行数据覆盖更新(后续可支持单个字段修改)。

删除

根据唯一id标识删除数据

curl -XDELETE http://router_server/$db_name/$space_name/$id

根据查询过滤结果删除数据

curl -H "content-type: application/json" -XPOST -d'
{
    "query": {
        "sum": [{}]
    }
}
' http://router_server/$db_name/$space_name/_delete_by_query

查询详细语法见下文

查询

查询示例

curl -H "content-type: application/json" -XPOST -d'
{
    "query": {
        "sum": [{
            "field": "field_name",
            "feature": [0.1, 0.2, 0.3, 0.4, 0.5],
            "min_score": 0.9,
            "boost": 0.5
        }],
        "filter": [{
            "range": {
                "field_name": {
                    "gte": 160,
                    "lte": 180
                }
            }
        },
        {
             "term": {
                 "field_name": ["100", "200", "300"],
                 "operator": "or"
             }
        }]
    },
    "retrieval_param": {
        "nprobe": 20
    },
    "fields": ["field1", "field2"],
    "is_brute_search": 0,
    "online_log_level": "debug",
    "quick": false,
    "vector_value": false,
    "client_type": "leader",
    "l2_sqrt": false,
    "sort": [{"field1":{"order": "asc"}}],
    "size": 10
}
' http://router_server/$db_name/$space_name/_search

查询参数整体json结构如下:

{
    "query": {
        "sum": [],
        "filter": []
    },
    "retrieval_param": {"nprobe": 20},
    "fields": ["field1", "field2"],
    "is_brute_search": 0,
    "online_log_level": "debug",
    "quick": false,
    "vector_value": false,
    "client_type": "leader",
    "l2_sqrt": false,
    "sort": [{"field1":{"order": "asc"}}],
    "size": 10
}

参数说明:

字段标识 类型 是否必填 备注
sum json数组 查询特征
filter json数组 查询条件过滤: 数值过滤 + 标签过滤
fields json数组 指定返回那些字段, 默认只返回唯一id和分值
is_brute_search int 默认0
online_log_level string 值为debug, 开启打印调试日志
quick bool 默认false
vector_value bool 默认false
client_type string 默认leader
ivf_flat bool 默认false,仅适用于IVFPQ模型,结果开根号
sort json数组 指定字段排序(只针对匹配结果,非整体)
size int 指定返回结果数量,默认50

retrieval_param 参数指定模型计算时的参数,不同模型支持的参数不同,如下示例:

  • metric_type: 计算类型,支持InnerProduct和L2, 默认L2。
  • nprobe: 搜索桶数量。
  • recall_num: 召回数量,默认等于查询参数中size的值,设置从索引中搜索数量,然后计算size个最相近的值。
  • parallel_on_queries: 默认1, 搜索间并行;0代表桶间并行。
  • efSearch: 图遍历的距离。

IVFPQ:

"retrieval_param": {
    "parallel_on_queries": 1,
    "recall_num" : 100,
    "nprobe": 80,
    "metric_type": "L2"
}

GPU:

"retrieval_param": {
    "recall_num" : 100,
    "nprobe": 80,
    "metric_type": "L2"
}

HNSW:

"retrieval_param": {
    "efSearch": 64,
    "metric_type": "L2"
}

IVFFLAT:

"retrieval_param": {
    "parallel_on_queries": 1,
    "nprobe": 80,
    "metric_type": "L2"
}

FLAT:

"retrieval_param": {
    "metric_type": "L2"
}
  • sum json结构说明:
"sum": [{
          "field": "field_name",
          "feature": [0.1, 0.2, 0.3, 0.4, 0.5],
          "min_score": 0.9,
          "boost": 0.5
       }]
  1. sum 支持多个(对应定义表结构时包含多个特征字段)。
  2. field 指定创建表时特征字段的名称。
  3. feature 传递特征,维数和定义表结构时维数必须相同。
  4. min_score 指定返回结果中分值必须大于等于0.9,两个向量计算结果相似度在0-1之间,min_score可以指定返回结果分值最小值,max_score可以指定最大值。如设置: “min_score”: 0.8,“max_score”: 0.95 代表过滤0.8<= 分值<= 0.95 的结果。同时另外一种分值过滤的方式是使用: “symbol”:”>=”,”value”:0.9 这种组合方式,symbol支持的值类型包含: > 、 >= 、 <、 <= 4种,value及min_score、max_score值在0到1之间。
  5. boost指定相似度的权重,比如两个向量相似度分值是0.7,boost设置成0.5之后,返回的结果中会将分值0.7乘以0.5即0.35。
  • filter json结构说明:
"filter": [
             {
                 "range": {
                     "field_name": {
                          "gte": 160,
                          "lte": 180
                     }
                 }
             },
             {
                 "term": {
                     "field1": ["100", "200", "300"],
                     "operator": "or"
                 },
                 "term": {
                     "field2": ["a", "b", "c"],
                     "operator": "and"
                 },
                 "term": {
                     "field3": ["A1", "B2"],
                     "operator": "not"
                 }
             }
          ]
  1. filter 条件支持多个,多个条件之间是交的关系。
  2. range 指定使用数值字段integer/float 过滤, filed_name是数值字段名称, gte、lte指定范围, lte 小于等于, gte大于等于,若使用等值过滤,lte和gte设置相同的值。上述示例表示查询field_name字段大于等于160小于等于180区间的值。
  3. term 使用标签过滤, field1是定义的标签字段,允许使用多个值过滤,可以求并“operator”: “or” , 求交: “operator”: “and”,不包含: “operator”: “not”。
  • is_brute_search 0使用索引搜索(建完索引前查询结果为空), 1使用暴力搜索,默认值0。
  • online_log_level 设置成”debug” 可以指定在服务端打印更加详细的日志,开发测试阶段方便排查问题。
  • quick 搜索结果默认将PQ召回向量进行计算和精排,为了加快服务端处理速度设置成true可以指定只召回,不做计算和精排。
  • vector_value 为了减小网络开销,搜索结果中默认不包含特征数据只包含标量信息字段,设置成true指定返回结果中包含原始特征数据。
  • client_type leader,random,no_leader,默认leader仅从主数据节点查询,random: 从ps主从节点随机选择,no_leader:只查询从节点。
  • size 指定最多返回的结果数量。若请求url中设置了size值http://router_server/$db_name/$space_name/_search?size=20优先使用url中指定的size值。

id查询

curl -XGET http://router_server/$db_name/$space_name/$id

批量id查询

curl -H "content-type: application/json" -XPOST -d'
{
    "query": {
              "ids": ["id1", "id2"],
              "fields": ["field1"]
    }
}
' http://router_server/$db_name/$space_name/_query_byids

ids指定多个id, fields 指定返回每条记录中那些字段

批量特征查询1

curl -H "content-type: application/json" -XPOST -d'
[{
   "query": {
       "sum": [{
           "field": "vector_field_name",
           "feature": [0.1, 0.2]
       }]
   }
},
{
   "query": {
       "sum": [{
           "field": "vector_field_name",
           "feature": [0.1, 0.2]
       }]
    }
}]
' http://router_server/$db_name/$space_name/_bulk_search

把多个单条查询的参数拼接成数组作为请求参数,返回结果和请求顺序保持一致。

批量特征查询2

curl -H "content-type: application/json" -XPOST -d'
{
    "query": {
        "sum": [{
            "field": "vector_field_name",
            "feature": [0.1, 0.2]
        }]
    }
}
' http://router_server/$db_name/$space_name/_msearch

适用于多个查询使用相同过滤条件的情况,将多个查询的特征拼接成一个特征数组(比如定义128维的特征,批量查询10条,则将10个128维特征按顺序拼接成1280维特征数组赋值给feature字段),后台接收到请求后按表结构定义的特征字段维度进行拆分,按顺序返回匹配结果。

根据id特征查询

curl -H "content-type: application/json" -XPOST -d'
{
    "query": {
        "ids": ["id1", "id2"]
     },
     "size": 10
}
' http://router_server/$db_name/$space_name/_query_byids_feature

传入记录id, 首先查询出该记录的特征,然后再用特征进行查询,返回匹配结果。

多向量查询

表空间定义时支持多个特征字段,因此查询时可以支持相应数据的特征进行查询。以每条记录两个向量为例:定义表结构字段

{
    "field1": {
        "type": "vector",
        "dimension": 128
    },
    "field2": {
        "type": "vector",
        "dimension": 256
    }
}

field1、field2均为向量字段,查询时搜索条件可以指定两个向量:

{
    "query": {
        "sum": [{
            "field": "filed1",
            "feature": [0.1, 0.2, 0.3, 0.4, 0.5],
            "min_score": 0.9
        },
        {
            "field": "filed2",
            "feature": [0.8, 0.9],
            "min_score": 0.8
        }]
    }
}

field1和field2过滤的结果求交集,其他参数及请求地址和普通查询一致。

GPU使用

注意事项

  1. 编译环境需要带GPU安装GUDA>=9.0.
  2. 编译前Vearch引擎CMakeLists.txt配置中BUILD_WITH_GPU设置为on.
  3. 创建表时gamm设置参数nprobe不大于1024(CUDA9.0)或者不大于2048(CUDA>=9.2), index_size设置为0.
  4. 建表时特征字段retrieval_type参数设置为GPU.
  5. 数据插入和建索引时不支持搜索。
  6. 数据不自动创建索引,调用curl -XPOST http://router_server/$db_name/$space_name/_forcemerge创建索引.
  7. 不支持实时添加数据到索引,新增数据只有重建索引后才会生效。(重建索引:curl -XPOST http://router_server/$db_name/$space_name/_forcemerge)

效果评估

基准

本文档显示了我们做的实验和得到的结果。我们做了两个系列的实验。首先,我们在单个节点上进行了基于faiss的改进的Vearch ivfpq模型的召回率实验。其次,基于Vearch 集群进行了实验。

我们使用检索返回的k个候选值(k∈{1,10,100})是否包含最近邻的结果来计算召回,其中标签通过暴力搜索获得。

实验数据会因为实现、不同机器等的变化而略有变化。

数据集

实验分别在128维SIFT特征和512维VGG特征上进行。

数据集SIFT1M

可以从如下网址下载ANN_SIFT1M

http://corpus-texmex.irisa.fr/

数据集VGG1M 和 VGG10M

分别收集100万和1000万的图片数据提取VGG特征得到VGG1M (100万)和VGG10M(1000万),其中VGG1M 和VGG10M并不相关。

数据集VGG100M , VGG500M 和 VGG1B

另外收集了10亿图片数据来构建VGG100M(1亿) , VGG500M(5亿) 和 VGG1B(10亿)。

Nprobe 实验

实验分别在SIFT1M, VGG1M 和 VGG10M上进行。其中ncentroids =256,nbytes = 32,nprobe ∈{1,5,10,20,30,40,50,80,100,200}。图中数据为recall@1结果。

结果

Architecture

可以看到当nprobe超过25后,召回基本上没有明显的变化了。

Ncentroids 实验

实验在VGG10M进行。其中nprobe = 50,nbytes = 32, ncentroids∈{64,128,256,512,1024,2048,4096,8192} 。图中数据为recall@1结果。

结果

Architecture

可以看到召回基本不随ncentroids变化而变化,但是ncentroids越大,QPS越高。

Nbytes 实验

实验在VGG10M进行。其中nprobe = 50,ncentroids = 256, nbytes ∈{4,8,16,32,64}。图中数据为recall@1结果。

结果

Architecture

当nbytes越大,召回越高,当然QPS随之降低。

对比实验

实验在 SIFT1M, VGG1M 和 VGG10M 上进行,并与faiss中的一些模型进行对比。

模型参数

表格中参数为空,则对应模型不包含该参数。其中 links, efSearch 和 efConstruction 为 faiss 中的 hnsw 定义的参数。

model ncentroids nprobe bytes of SIFT bytes of VGG links efSearch efConstruction
pq |   32 64      
ivfpq |256 20 32 64      
imipq 2^(2*10) 2048 32 64      
opq+pq     32 64      
hnsw         32 64 40
ivfhnsw 256 20     32 64 40
Vearch 256 20 32 64      

结果

SIFT1M的召回:

model recall@1 recall@10 recall@100
pq 0.6274 0.9829 0.9999
ivfpq 0.6167 0.9797 0.9960
imipq 0.6595 0.9775 0.9841
opq+pq 0.6250 0.9821 1.0000
hnsw 0.9792 0.9867 0.9867
ivfhnsw 0.9888 0.9961 0.9961
Vearch 0.9581 0.9645 0.9645

VGG1M的召回 :

model recall@1 recall@10 recall@100
pq 0.5079 0.8922 0.9930
ivfpq 0.4985 0.8792 0.9704
imipq 0.5077 0.8618 0.9248
opq+pq 0.5213 0.9105 0.9975
hnsw 0.9496 0.9550 0.9551
ivfhnsw 0.9690 0.9744 0.9745
Vearch 0.9536 0.9582 0.9585

VGG10M的召回 :

model recall@1 recall@10 recall@100
pq 0.5842 0.8980 0.9888
ivfpq 0.5913 0.8896 0.9748
imipq 0.5925 0.8878 0.9570
opq+pq 0.6126 0.9160 0.9944
hnsw 0.8877 0.9069 0.9074
ivfhnsw 0.9638 0.9839 0.9843
Vearch 0.9272 0.9464 0.9468

集群实验

集群实验分别对 VGG100M , VGG500M 和 VGG1B进行实验,并添加是否过滤来进行实验,其中过滤是指在搜索的时候指定过滤条件来缩小搜索范围。VGG100M 搭建了 3 个masters, 3个 routers 和5 个 partition services 的集群。 VGG500M搭建了 3 个masters, 3个 routers 和24个 partition services 的集群。VGG1B搭建了 3 个masters, 6个 routers 和48 个 partition services 的集群。

结果

Architecture

可以看到当average latency超过一定程度,QPS就不再发生明显变化了。

HNSW性能评测

data_size = 10M, 参数为 InnerProduct, nlinks = 32, efConstruction = 40, efSearch = 64

集群 机器配置 数据维度 构建索引时间
masterrouterps各一个,均在一台机器上 56核256G内存 128 28min
masterrouterps各一个,均在一台机器上 56核256G内存 512 33min10s
masterrouterps各一台,且在不同机器上 8核16G内存 128 2h38m

内存使用率

以8核16G机器为样本进行分析,vearch ps共使用内存16G * 0.627 = 10.032G,其中原始数据特征大小为10M * 128 * 4 ~ 5G,其它为索引以及正排字段占用内存。使用时仅创建一个正排字段和向量字段。

Architecture

10M数据, 机器配置56核256G,add time = 28min01s, 左侧是构建索引CPU利用率,右侧是搜索时CPU利用率

Architecture
model 返回结果数 并发数 QPS tp99 tp100
HNSW 100 2000 5069.48 959 1944
HNSW 100 1000 4680.62 521 994
HNSW 100 100 4508.33 49 86
HNSW 100 50 4146.55 22 35
HNSW 100 10 2048.43 8 117
HNSW 100 1 182.74 9 160

10M数据,机器配置56核256G,d = 512, add time = 33min10s,左侧是构建索引CPU利用率,右侧是搜索时CPU利用率

Architecture
model 返回结果数 并发数 QPS tp99 tp100
HNSW 100 2000 4194.72 1181 2650
HNSW 100 1000 4058.59 628 1059
HNSW 100 100 3784.56 57 172
HNSW 100 50 3723.75 24 43
HNSW 100 10 1248.46 15 1208
HNSW 100 1 142.70 12 71

10m数据,机器配置8核16G,d = 128, add_time = 2h38m

构建索引时CPU使用率基本保持在100%, 搜索时cpu使用率,当并发数为50,100时,可以看到cpu使用率已经100%,故不做并发数1000/2000的对比实验

Architecture
model 返回结果数 并发数 QPS tp99 tp100
HNSW 100 100 888.25 279 402
HNSW 100 50 1126.06 95 199
HNSW 100 10 918.50 15 31
HNSW 100 1 91.74 14 25

召回评测使用sift1M

model parameters recall@1 recall@10 recall@100
HNSW InnerProduct, nlinks = 32, efConstruction = 40, efSearch = 64 0.9769 0.9852 0.9852

GPU版性能

服务器配置

hardware config
CPU E5-2683 v4 16cores
memory 16G
GPU Tesla P40

数据:128维float数据,总插入数据量2亿

性能:并发20查询,QPS达到3000,tp99在30ms以内

常见问题

  1. Vearch的向量搜索引擎gamma基于faiss实现,faiss版本有较大改动不兼容历史版本时Vearch可能编译不成功。