写点什么

向量数据库落地实践

  • 2024-04-03
    北京
  • 本文字数:3950 字

    阅读完需:约 13 分钟

一、前言

本文基于京东内部向量数据库 vearch 进行实践。Vearch 是对大规模深度学习向量进行高性能相似搜索的弹性分布式系统。详见: https://github.com/vearch/zh_docs/blob/v3.3.X/docs/source/overview.rst

二、探索

初次认识向量数据库,一脸懵逼?



向量是什么?如何将文本转换为向量?如何确定维度?如何定义表结构?如何选择索引方式,建表参数如何配置?检索参数如何配置?分片数副本数如何选择等等


随着对文档的逐渐熟悉以及和 vearch 相关同事的沟通,以上问题迎刃而解,具体的不再赘述。主要记住以下几点:


1、 文本转向量:采用大模型网关接口 domain/embeddings 传入对应的模型如:text-embedding-ada-002-2 和待转换的文本即可;


2、 向量维度:这个和向量转换所采用的模型有关,细节不用关注;


3、 建表参数的选择以及表结构:主要在于 retrieval_type 检索模型的选择,具体的可以参考文档。经过综合考虑,决定采用 HNSW:



"retrieval_type": "HNSW","retrieval_param": {    "metric_type": "InnerProduct",    "nlinks": 32,    "efConstruction": 40}
注意: 1、向量存储只支持MemoryOnly 2、创建索引不需要训练,index_size 值大于0均可
复制代码


具体的建表示例见后文。


4、 分片数和副本数结合实际数据量评估,如果无法评估,按照最少资源申请即可,后续可扩展。

三、实践

1、 建表(space)

为了简化操作,实行 db(库)-space(表)一对一的方案,弱化库的概念。经过一系列探索之后定义出了通用的 space 结构:


{    "name": "demphah",    "partition_num": 3,    "replica_num": 3,    "engine": {        "name": "gamma",        "index_size": 1,        "id_type": "String",        "retrieval_type": "HNSW",        "retrieval_param": {            "metric_type": "InnerProduct",            "nlinks": 32,            "efConstruction": 100,            "efSearch": 64        }    },    "properties": {        "vectorVal": {            "type": "vector",            "dimension": 1536        },        "contentVal": {            "type": "string"        },        "chunkFlagId": {            "type": "string",            "index": true        },        "chunkIndexId": {            "type": "integer",            "index": true        }    }}
复制代码


字段说明:


engine、partition_num 等都是固定的参数,properties 中所列字段皆为通用字段,如果有扩展字段如:skuId,storeId 追加即可



这里 file 的概念可以理解为一个单元,可能是一个文件,也可能是一个 url,总之就是一个数据整体。

2、 分段写入

这里针对通用文件描述,比如提供一个 pdf 文件如何导入向量库:


a. 首先上传文件到 oss,然后根据对应的 fileKey 获取到文件数据流


b. 再根据各种拆分场景(按行、字节数、正则拆分等)分成片段


c. 分段写入向量库:


    /**     * 将字符串转换为向量并插入数据库     * <p>     * 目前所有的知识库管理端写入全走这个方法     *     * @param dbName       数据库名称     * @param spaceName    空间名称     * @param str          字符串     * @param flagId       标志ID     * @param chunkIndexId 块索引ID     * @param properties   属性     */    private void embeddingsAndInsert(String dbName, String spaceName, String str, String flagId, Integer chunkIndexId, Map<String, Object> properties) {        // 先向数据库写入一条记录,记录当前文档的写入操作        int success = knbaseDocRecordService.writeDocRecord(spaceName, flagId, chunkIndexId.longValue(), 0, str);        if (success <= 0) {            log.error("writeDocRecord失败 {},{},{}", spaceName, flagId, chunkIndexId);        }
// 分块转向量并写入 List<Float> embeddings = GatewayUtil.baseEmbeddings(str); if (CollectionUtils.isEmpty(embeddings)) { return; }
KnBaseVecDto knBaseVecDto = buildKnBaseVecDto(new FeaVector(embeddings),flagId,chunkIndexId,str);
Map<String, Object> newPros = JsonUtil.obj2Map(knBaseVecDto); if (MapUtils.isNotEmpty(properties)) { newPros.putAll(properties); } // {"_index":"kn_base_file_db","_type":"kn_base_file_space","_id":"-8182839813441244911","status":200} String insert = VearchUtil.insert(dbName, spaceName, null, newPros); if (StringUtils.isBlank(insert) || !insert.contains("_index")) { log.error("写入失败的块:{},{}", chunkIndexId, insert); } }
复制代码

3、 数据记录

上文写知识库的过程有个 knbaseDocRecordService.writeDocRecord 的逻辑,用于记录写入的片段。下文详细介绍其中用到的 mysql 表:


1、 表 1 space 记录表


注:主要用于记录创建的 space,以及查询管控,如禁用某个 space 等


CREATE TABLE `xxx_vearch_spaces` (  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增主键',  `type` tinyint(3) NOT NULL COMMENT '类型',  `status` tinyint(3) NOT NULL COMMENT '状态',  `space` varchar(127) NOT NULL COMMENT '空间标识',  `db` varchar(127) NOT NULL COMMENT '库标识',  `desc` varchar(127) NOT NULL COMMENT '空间描述',  `ext` varchar(4095) NOT NULL DEFAULT '' COMMENT '扩展字段',  `creator` varchar(127) NOT NULL DEFAULT '' COMMENT '创建人',  `created` timestamp NOT NULL COMMENT '创建时间',  `modifier` varchar(127) NOT NULL DEFAULT '' COMMENT '修改人',  `modified` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',  `deleted` tinyint(3) NOT NULL DEFAULT '0' COMMENT '已删除(0:否;1:是)',  PRIMARY KEY (`id`) USING BTREE,  UNIQUE KEY `uniq_space_db` (`space`,`db`) USING BTREE) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8mb4 COMMENT='xxx向量空间'
复制代码


2、 表 2 file 记录表


注:主要用于记录 space 下的 file,以及查询管控,如禁用某个 file,以及关联查询对应的全部片段。


CREATE TABLE `xxx_spaces_knbase` (  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增主键',  `status` tinyint(3) NOT NULL COMMENT '状态',  `space` varchar(127) NOT NULL COMMENT '空间标识',  `file_name` varchar(255) NOT NULL COMMENT '文件名',  `file_desc` varchar(511) NOT NULL COMMENT '文件描述',  `byte_num` bigint(20) unsigned NOT NULL COMMENT '字符数',  `hit_count` int(10) unsigned NOT NULL COMMENT '命中次数',  `ext` varchar(4095) NOT NULL DEFAULT '' COMMENT '扩展字段',  `creator` varchar(127) NOT NULL DEFAULT '' COMMENT '创建人',  `created` timestamp NOT NULL COMMENT '创建时间',  `modifier` varchar(127) NOT NULL DEFAULT '' COMMENT '修改人',  `modified` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',  `deleted` tinyint(3) NOT NULL DEFAULT '0' COMMENT '已删除(0:否;1:是)',  `file_flag_id` varchar(255) DEFAULT NULL COMMENT '文件唯一标识',  PRIMARY KEY (`id`) USING BTREE,  KEY `idx_space` (`space`) USING BTREE) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8mb4 COMMENT='xxx空间知识库'
复制代码


3、 表 3 paragraph 记录表


注:主要用于记录 file 拆分的片段,包括当前位置,查询命中数等。


CREATE TABLE `xxx_knbase_doc_record` (  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增主键',  `space` varchar(127) NOT NULL COMMENT '空间标识',  `file_flag_id` varchar(255) NOT NULL COMMENT '文件标识',  `d_index` bigint(20) unsigned NOT NULL COMMENT '文档位置',  `hit_count` int(10) unsigned NOT NULL COMMENT '命中次数',  `ext` varchar(4095) NOT NULL DEFAULT '' COMMENT '扩展字段',  `creator` varchar(127) NOT NULL DEFAULT '' COMMENT '创建人',  `created` timestamp NOT NULL COMMENT '创建时间',  `modifier` varchar(127) NOT NULL DEFAULT '' COMMENT '修改人',  `modified` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',  `deleted` tinyint(3) NOT NULL DEFAULT '0' COMMENT '已删除(0:否;1:是)',  PRIMARY KEY (`id`) USING BTREE,  UNIQUE KEY `uniq_space_file_idx` (`space`,`file_flag_id`,`d_index`) USING BTREE) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8mb4 COMMENT='xxx知识文档记录'
复制代码

四、总结

向量数据库对于大模型应用落地来说至关重要,有些不可外露的内部数据可以存储在向量库中,用于内部检索。随着向量库中数据的丰富,大模型推理回答的能力也将更加精准。


上文的设计比如 space 中的 chunkFlagId 可以关联出原始的整个文件;chunkIndexId 可以控制数据的查询范围,另一方面可以通过此字段实现分页(vearch 目前不支持分页查询)以及全文导出。xxx_knbase_doc_record 表中记录了片段的记录,可用于计算片段的 chunkIndexId,一方面避免重复,另一方面保证属性的递增,可用于扩展很多能力。


目前向量数据库的检索只支持基本的向量检索和关键字检索,后续会逐步优化混合检索等方案以提高检索准确率等。

发布于: 刚刚阅读数: 4
用户头像

拥抱技术,与开发者携手创造未来! 2018-11-20 加入

我们将持续为人工智能、大数据、云计算、物联网等相关领域的开发者,提供技术干货、行业技术内容、技术落地实践等文章内容。京东云开发者社区官方网站【https://developer.jdcloud.com/】,欢迎大家来玩

评论

发布
暂无评论
向量数据库落地实践_京东科技开发者_InfoQ写作社区