网站设计与制作说明书,网站太花哨,学校网站 源码,网页设计入门与提高前言
随着NLP预训练模型#xff08;大模型#xff09;以及多模态研究领域的发展#xff0c;向量数据库被使用的越来越多。
在XOP亿级题库业务背景下#xff0c;对于试题召回搜索单单靠着ES集群已经出现性能瓶颈#xff0c;因此需要预研其他技术方案提高试题搜索召回率。…前言
随着NLP预训练模型大模型以及多模态研究领域的发展向量数据库被使用的越来越多。
在XOP亿级题库业务背景下对于试题召回搜索单单靠着ES集群已经出现性能瓶颈因此需要预研其他技术方案提高试题搜索召回率。
现一个方案就是使用Bert等模型提取试题题干特征然后存储到向量数据库检索试题先走向量数据库拿到具体的试题ID等信息在走ES进行相似题召回从而提高搜索的性能。需要考虑的就是特征提取的效率Milvus的性能比较吃服务器资源然后进行评估。
本篇博客主要对Bert等模型以及主流的Milvus进行实践以及一些相关知识学习。
目录
概述实践原理分析
一、概述
https://milvus.io/ Milvus创建于2019年其唯一目标是存储、索引和管理由深度神经网络和其他机器学习ML模型生成的大量嵌入向量。 作为一个专门设计用于处理输入向量查询的数据库它能够索引万亿级的向量。与现有的关系数据库主要处理遵循预定义模式的结构化数据不同Milvus是自底向上设计的用于处理从非结构化数据转换而来的嵌入向量Embedding Vector。 基础概念
标量无向量只有数值大小没有方向向量区别于具体的单维度数值标量可以认为是一条线有多个数值表示。嵌入向量Embedding Vector是非结构化数据的特征抽象例如电子邮件、物联网传感器数据、Instagram照片、蛋白质结构等等。从数学上讲嵌入向量是一个浮点数数组或二进制数组。向量范数向量范数是指向量的大小或长度计算向量范式可以用来衡量向量的大小、相似度等。计算方式分为 L1曼哈顿范数将向量元素绝对值相加之和L2欧几里得范数将向量元素的平房和开更号 向量归一化对向量进行归一化操作以确保不同维度上的特征权重相等避免某些维度对结果产生较大影响。可以采用L1、L2归一化一种常用的归一化方法是使用 L2 范数进行单位化处理即将向量除以其 L2 范数得到单位向量。向量内积点积、数量积两个向量的点积就是吧对应位置元素点积之和对于某个位置元素的点积a · b a·b·cos v。其中表示向量范数。点乘的几何意义是可以用来表征或计算两个向量之间的夹角以及在b向量在a向量方向上的投影。向量外积外积英语Outer product在线性代数中一般指两个向量的张量积其结果为一矩阵与外积相对向量的外积是矩阵的克罗内克积的特殊情况。余弦相似度向量之间的夹角的余弦值范围为[-1,1]越接近1表示两个向量越来约相似。
https://oi-wiki.org/math/linear-algebra/product/
数据模型相关概念
Bitset位图集合Channel有两个不同的Channel在Milvus。它们是PChannel和VChannel。 每个PChannel对应一个日志存储主题。PChannel是物理Channel。每个PChannel对应一个日志存储主题。当Milvus集群启动时默认情况下将分配一组256个PChannels来存储记录数据插入、删除和更新的日志。每个VChannel对应于集合中的一个分片。VChannel代表逻辑信道。每个VChannel代表集合中的一个分片。每个集合将被分配一组VChannels用于记录数据插入、删除和更新。VChannel在逻辑上是分离的但在物理上共享资源。 Collection数据实体集合类比表Schema集合模式模式是定义数据类型和数据属性的Meta信息。每个集合都有自己的集合模式该模式定义集合的所有字段、自动ID主键分配启用和集合描述。集合架构中还包括定义字段的名称、数据类型和其他属性的字段架构。Entity数据实体每个实体会有一个主键。Field数据字段类型可以是数字、字符串、向量等结构化数据。Normalization归一化归一化是指转换嵌入向量以使其范数等于1的过程。如果使用内积IP来计算嵌入相似度则所有嵌入都必须归一化。归一化后内积等于余弦相似度。Vector index向量索引是从原始数据中派生出来的重组数据结构可以大大加速向量相似性搜索的过程。Milvus支持多种向量索引类型。Vector similarity search向量相似性搜索是将向量与数据库进行比较以找到与目标搜索向量最相似的向量的过程。近似最近邻ANN搜索算法用于计算向量之间的相似性。
系统设计概念作为云原生矢量数据库Milvus通过设计将存储和计算分离。为了增强弹性和灵活性Milvus中的所有组件都是无状态的。
接入层提供访问的API协调服务大脑将任务分配给工作节点工作节点四肢执行大脑下发的DML命令存储服务骨骼负责数据持久化。它包括Meta存储、日志代理和对象存储。 相关概念
Message storage消息存储是Milvus的日志存储引擎。Dependency其他依赖Milvus的依赖项包括etcd存储Meta数据MinIO或S3对象存储和Pulsar管理快照日志。Milvus cluster在Milvus的集群部署中服务由一组节点提供以实现高可用性和易扩展性。Partition分区是集合的物理划分。Milvus支持将收集数据划分为物理存储上的多个部分。这个过程称为分区每个分区可以包含多个段。Segment段是由Milvus自动创建的用于保存插入数据的数据文件。一个集合可以有多个段一个段可以有多个实体。在向量相似性搜索期间Milvus扫描每个片段并返回搜索结果。段可以是增长的也可以是密封的。一个不断增长的段不断接收新插入的数据直到它被密封。密封的段不再接收任何新数据并将被刷新到对象存储中留下新数据插入到新创建的增长段中。增长段将被密封因为它持有的实体数量达到预定义的阈值或者因为“增长”状态的跨度超过指定的限制。Sharding分片是指将写操作分配到不同的节点一个节点可以存储多个分区以充分利用Milvus集群的并行计算潜力来写数据。默认情况下单个集合包含两个分片。Milvus采用基于主键哈希的分片方法。Milvus的开发路线图包括支持更灵活的分片方法如随机和自定义分片。
日志相关概念
Log Broker日志代理支持回放的系统负责流数据持久化、可靠的异步查询、事件通知和返回查询结果以及当工作节点故障恢复后增量数据的完整性。Log sequence日志序列记录更改集合状态的所有操作。Log snapshot日志快照二进制日志一个较小的段单元记录和处理对Milvus矢量数据库中数据的更新和更改。来自一个段的数据被持久化在多个binlog中。Milvus中有三种类型的binlogInsertBinlog、DeleteBinlog和DDLBinlog。Log subscriber日志订阅者订阅日志序列以更新本地数据并以只读副本的形式提供服务。
二、实践
2.1、安装Milvus服务
因为是云原生的设计架构安装可以使用k8s、docker compose安装https://milvus.io/docs/prerequisite-helm.md内存至少8g配置挂在目录以及端口https://milvus.io/docs/configure-docker.md
也可以使用普通安装方式
# Install Milvus
sudo yum https://github.com/milvus-io/milvus/releases/download/v2.0.0-pre-ga/milvus-2.0.0-preGA.1.el7.x86_64.rpm# Check Milvus status
sudo systemctl status milvus
sudo systemctl status milvus-etcd
sudo systemctl status milvus-minio或者直接使用Python安装轻量级的Milvus LiteMilvus Lite是Milvus的轻量级版本可与Google Colab和Google Notebook无缝协作。https://milvus.io/docs/milvus_lite.md
// 安装docker以及docker-compose插件// 下载yml
wget https://github.com/milvus-io/milvus/releases/download/v2.3.3/milvus-standalone-docker-compose.yml -O docker-compose.yml// 启动
docker-compose up -d// 查看启动状态
docker compose ps// 关闭
docker compose down2.2、安装可视化界面
https://github.com/zilliztech/attu可以下载桌面版 or docker or k8s
2.3、使用Milvus
SDK支持Python、Java、Go、NodejsPython的SDK相对功能完善其他语言的还在活跃的开发中https://milvus.io/docs/install-pymilvus.md
1、使用Python SDK
// 安装依赖
python -m pip install pymilvus2.3.3
2、使用Java SDK https://github.com/milvus-io/milvus-sdk-java https://milvus.io/api-reference/java/v2.3.x/About.md
dependencygroupIdio.milvus/groupIdartifactIdmilvus-sdk-java/artifactIdversion2.3.3/version
/dependency
使用流程
创建数据库与传统的数据库引擎类似您也可以在Milvus中创建数据库并将权限分配给某些用户来管理它们。然后这些用户有权管理数据库中的集合。Milvus集群最多支持64个数据库。默认存在数据库default。创建集合集合由一个或多个分区组成。在创建新集合时如果不指定分区数Milvus会创建一个默认的partition分区_default。创建集合之前需要指定元数据支持为标量设置默认值。创建集合可以指定分片数量相比分区分区通过指定分区名称来减少读取负载而分片在多个服务器之间分散写入负载。创建索引需要指定为某向量字段、普通标量创建索引的类型标量默认索引类型为字典树额外参数传入聚类参数nlist。比如IVF_FLAT索引将向量数据划分为nlist聚类单元然后比较目标输入向量与每个聚类中心之间的距离。根据系统设置为查询的聚类数nprobe仅基于目标输入和最相似聚类中的向量之间的比较返回相似性搜索结果-大大减少查询时间。加载集合将集合、集合分区加载进内存Milvus2.1允许用户将集合按照分区加载为多个副本以利用额外查询节点的CPU和内存资源。此功能可提高整体QPS和吞吐量无需额外硬件。插入数据可以指定partition_name将数据插入指定分区可以将文件中的实体数据插入集合支持manualCompaction手动压缩数据搜索数据根据创建的向量索引以及指定的相似度度量参数IP、L2等来进行相似性索引、标量搜素。
数据字段类型
TypeDescriptionNoneFor internal usage.BoolBoolean.Int8Integer number stored with 8 bit.Int16Integer number stored with 16 bit.Int32Integer number stored with 32 bit.Int64Integer number stored with 64 bit.FloatFloating-point numbers.Double64-bit IEEE 754 floating point numbers.StringReserved. Do not use this.VarCharVariable-length string with a limit on the maximum length.BinaryVectorBinary vector. Each dimension is represented by 1 bit.FloatVectorFloat vector. Each dimension is represented by 1 float (4 bits) value.
动态数据类型
为了使Milvus插入数据更加灵活对于之前创建的集合可以指定动态元数据模式。 动态模式使用户能够将具有新字段的实体插入到Milvus集合中而无需修改现有模式。这意味着用户可以在不知道集合的完整架构的情况下插入数据并且可以包括尚未定义的字段。
索引类型
ANN紧邻搜索的索引实现的几种方式
Tree-based indexGraph-based indexHash-based indexQuantization-based index
在Milvus中根据数据类型将向量索引种类分为
内存索引 浮点嵌入索引二进制嵌入索引标量前缀索引 磁盘索引默认启用DiskANN可选择关闭。
https://milvus.io/api-reference/java/v2.3.x/Misc/IndexType.md
INVALIDFor internal usage.FLATOnly for FloatVector type field.IVF_FLATOnly for FloatVector type field.IVF_SQ8Only for FloatVector type field.IVF_PQOnly for FloatVector type field.HNSWOnly for FloatVector type field.ANNOYOnly for FloatVector type field.DISKANNOnly for FloatVector type field.BIN_FLATOnly for BinaryVector type field.BIN_IVF_FLATOnly for BinaryVector type field.TRIEOnly for VARCHAR type field.
聚类近似搜索
其中IVF_FLAT、IVF_SQ8、IVF_PQ、BIN_FLAT等索引创建的时候支持 nlist查询时候支持nporbe参数将向量数据划分为nlist聚类单元然后比较目标输入向量与每个聚类中心之间的距离。根据系统设置为查询的聚类数nprobe仅基于目标输入和最相似聚类中的向量之间的比较返回相似性搜索结果-大大减少查询时间。
聚类单元是指进行聚类分析时将数据点划分为不同的簇或群组的基本单位。每个聚类单元代表一个特定的数据集合其内部的数据点在某种程度上相似。聚类算法通过计算各个数据点之间的距离或相似性来确定如何将它们分配到不同的聚类单元中。
聚类单元可以用于对数据进行分类、识别隐藏的模式和结构并产生有关数据集的洞察力。利用聚类单元可以将复杂的数据集简化为更易理解和解释的形式同时可作为进一步分析、预测和决策制定的基础。
相似度量规则
TypeDescriptionINVALIDFor internal usage.L2Euclidean distance. Only for float vectors.IPInner product. Only for normalized float vectors.COSINECosine Similarity. Only for normalized float vectors.HAMMINGOnly for binary vectors.JACCARDOnly for binary vectors.TANIMOTOOnly for binary vectors.
代码demo
具体的API参考官网文档下面举例向量标量的混合搜索demo
milvusClient.loadCollection(LoadCollectionParam.newBuilder().withCollectionName(book).build()
);final Integer SEARCH_K 2;
final String SEARCH_PARAM {\nprobe\:10, \”offset\”:5};
ListString search_output_fields Arrays.asList(book_id);
ListListFloat search_vectors Arrays.asList(Arrays.asList(0.1f, 0.2f));SearchParam searchParam SearchParam.newBuilder().withCollectionName(book).withMetricType(MetricType.L2).withOutFields(search_output_fields).withTopK(SEARCH_K).withVectors(search_vectors).withVectorFieldName(book_intro).withExpr(word_count 11000).withParams(SEARCH_PARAM).build();
RSearchResults respSearch milvusClient.search(searchParam);Python SDK demo
// 执行demo代码
# hello_milvus.py demonstrates the basic operations of PyMilvus, a Python SDK of Milvus.
# 1. connect to Milvus
# 2. create collection
# 3. insert data
# 4. create index
# 5. search, query, and hybrid search on entities
# 6. delete entities by PK
# 7. drop collection
import timeimport numpy as np
from pymilvus import (connections,utility,FieldSchema, CollectionSchema, DataType,Collection,
)fmt \n {:30} \n
search_latency_fmt search latency {:.4f}s
num_entities, dim 3000, 8#################################################################################
# 1. connect to Milvus
# Add a new connection alias default for Milvus server in localhost:19530
# Actually the default alias is a buildin in PyMilvus.
# If the address of Milvus is the same as localhost:19530, you can omit all
# parameters and call the method as: connections.connect().
#
# Note: the using parameter of the following methods is default to default.
print(fmt.format(start connecting to Milvus))
connections.connect(default, hostlocalhost, port19530)has utility.has_collection(hello_milvus)
print(fDoes collection hello_milvus exist in Milvus: {has})#################################################################################
# 2. create collection
# Were going to create a collection with 3 fields.
# -------------------------------------------------------------------------
# | | field name | field type | other attributes | field description |
# -------------------------------------------------------------------------
# |1| pk | VarChar | is_primaryTrue | primary field |
# | | | | auto_idFalse | |
# -------------------------------------------------------------------------
# |2| random | Double | | a double field |
# -------------------------------------------------------------------------
# |3|embeddings| FloatVector| dim8 | float vector with dim 8 |
# -------------------------------------------------------------------------
fields [FieldSchema(namepk, dtypeDataType.VARCHAR, is_primaryTrue, auto_idFalse, max_length100),FieldSchema(namerandom, dtypeDataType.DOUBLE),FieldSchema(nameembeddings, dtypeDataType.FLOAT_VECTOR, dimdim)
]schema CollectionSchema(fields, hello_milvus is the simplest demo to introduce the APIs)print(fmt.format(Create collection hello_milvus))
hello_milvus Collection(hello_milvus, schema, consistency_levelStrong)################################################################################
# 3. insert data
# We are going to insert 3000 rows of data into hello_milvus
# Data to be inserted must be organized in fields.
#
# The insert() method returns:
# - either automatically generated primary keys by Milvus if auto_idTrue in the schema;
# - or the existing primary key field from the entities if auto_idFalse in the schema.print(fmt.format(Start inserting entities))
rng np.random.default_rng(seed19530)
entities [# provide the pk field because auto_id is set to False[str(i) for i in range(num_entities)],rng.random(num_entities).tolist(), # field random, only supports listrng.random((num_entities, dim)), # field embeddings, supports numpy.ndarray and list
]insert_result hello_milvus.insert(entities)# 测试打印
for x in range(3):print(entities[x])hello_milvus.flush()
print(fNumber of entities in Milvus: {hello_milvus.num_entities}) # check the num_entities################################################################################
# 4. create index
# We are going to create an IVF_FLAT index for hello_milvus collection.
# create_index() can only be applied to FloatVector and BinaryVector fields.
print(fmt.format(Start Creating index IVF_FLAT))
index {index_type: IVF_FLAT,metric_type: L2,params: {nlist: 128},
}hello_milvus.create_index(embeddings, index)################################################################################
# 5. search, query, and hybrid search
# After data were inserted into Milvus and indexed, you can perform:
# - search based on vector similarity
# - query based on scalar filtering(boolean, int, etc.)
# - hybrid search based on vector similarity and scalar filtering.
## Before conducting a search or a query, you need to load the data in hello_milvus into memory.
print(fmt.format(Start loading))
hello_milvus.load()# -----------------------------------------------------------------------------
# search based on vector similarity
print(fmt.format(Start searching based on vector similarity))
vectors_to_search entities[-1][-2:]
search_params {metric_type: L2,params: {nprobe: 10},
}start_time time.time()
result hello_milvus.search(vectors_to_search, embeddings, search_params, limit3, output_fields[random])
end_time time.time()for hits in result:for hit in hits:print(fhit: {hit}, random field: {hit.entity.get(random)})
print(search_latency_fmt.format(end_time - start_time))# -----------------------------------------------------------------------------
# query based on scalar filtering(boolean, int, etc.)
print(fmt.format(Start querying with random 0.5))start_time time.time()
result hello_milvus.query(exprrandom 0.5, output_fields[random, embeddings])
end_time time.time()print(fquery result:\n-{result[0]})
print(search_latency_fmt.format(end_time - start_time))# -----------------------------------------------------------------------------
# pagination
r1 hello_milvus.query(exprrandom 0.5, limit4, output_fields[random])
r2 hello_milvus.query(exprrandom 0.5, offset1, limit3, output_fields[random])
print(fquery pagination(limit4):\n\t{r1})
print(fquery pagination(offset1, limit3):\n\t{r2})# -----------------------------------------------------------------------------
# hybrid search
print(fmt.format(Start hybrid searching with random 0.5))start_time time.time()
result hello_milvus.search(vectors_to_search, embeddings, search_params, limit3, exprrandom 0.5, output_fields[random])
end_time time.time()for hits in result:for hit in hits:print(fhit: {hit}, random field: {hit.entity.get(random)})
print(search_latency_fmt.format(end_time - start_time))###############################################################################
# 6. delete entities by PK
# You can delete entities by their PK values using boolean expressions.
ids insert_result.primary_keysexpr fpk in [{ids[0]} , {ids[1]}]
print(fmt.format(fStart deleting with expr {expr}))result hello_milvus.query(exprexpr, output_fields[random, embeddings])
print(fquery before delete by expr{expr} - result: \n-{result[0]}\n-{result[1]}\n)hello_milvus.delete(expr)result hello_milvus.query(exprexpr, output_fields[random, embeddings])
print(fquery after delete by expr{expr} - result: {result}\n)###############################################################################
# 7. drop collection
# Finally, drop the hello_milvus collection
# print(fmt.format(Drop collection hello_milvus))
# utility.drop_collection(hello_milvus)
3、Bert实践
使用NLP模型对文本进行特征提将特征向量存储到Milvus数据库然后进行相似搜索。
参考 https://www.cnblogs.com/henx/p/13802855.html https://zhuanlan.zhihu.com/p/567922534
// TODO