向量的作用
在机器学习和自然语言处理中首先将文本向量化,向量化目的是让文本可计算。看一个经典文本计算的例子,见下图。
如果我们把每一个单词看作向量:
queen(皇后) = king(国王)- man(男人)+ woman(女人)
这样计算机能明白, “皇后啊, 就是女性的国王呗!”
walked(过去式) = walking(进行时)- swimming(进行时)+ swam(过去式)
同理计算机也能明白, “walked, 就是walking的过去式啦!“
向量间的距离计算也可以描述词语间的关系:
如:”北京”是”中国”的首都, “巴黎”是”法国”的首都, 那么向量:|中国|-|北京|=|法国|-|巴黎|
在自然语言处理中查找语意相近或关联的内容一般都是采用计算向量距离的方法来实现的,具体内容可以搜索关键“向量召回算法”获取更多内容。
如上面所说,通过向量化后计算机就可以对文字等进行计算和操作(同理对图片、声音、视频等向量化后也可以操作)。
目前主流的向量表达方式是Embedding,它是一个低维稠密的向量“表示”对象。并且有大量的工具支持文本转Embedding,如Word2vec等,目前一些大LLM也提供Embedding的接口。如果对Word2Vec比较
向量数据库作用和应用场景
与传统数据库相比向量数据库使用向量化计算,能够高速地处理大规模的复杂数据;并可以处理高维数据,例如图像、音频和视频等,解决传统关系型数据库中的痛点;
同时向量数据库支持复杂的查询操作,也可以轻松地扩展到多个节点,以处理更大规模的数据。
目前矢量数据库在推荐系统、知识库系统和LLM应用系统中有大规模的应用等,目前向量数据有很多。Milvus是一款开源的云原生向量数据库。
Milvus是专门为处理输入向量查询而设计的数据库,它能够对万亿规模的向量进行索引。Milvus 是自下而上设计的,设计初衷处理从非结构化数据转换而来的嵌入向量。
Milvus常用概念解释
在使用数据库之前,需要了解的一些Milvus的概念:
Database
数据库的概念和传统关系型数据库的概念类似,每个数据库是一个独立集合,有对应的人员和权限管理。一个Milvus集群最多允许创建64个数据库。
Collection
集合对应关系数据库中表的概念,是数据存储和操作的载体。和传统表不同的是每个collection至少要有主键字段和向量字段。目前V2版本中支持的数据类型有:
- 主键:只支持Int64和VARCH类型,支持自增。
- 向量字段支持:FloatVector和BinaryVector,FloatVector一般是文本向量。
- 扩展字段支持:Int8,Int16,Int32,Int64,Float,Double,Boolean,Varch,JSON这些类型。
Partition
分区在一些传统数据库中也都有这个概念,目的是将Collection中的数据分块存放,以提升查询等操作的性能。
Index
索引为为了提升查询性能,支持的向量索引类型大部分采用近似最近邻搜索(ANNS)。索引类型有:
- FLAT 适合在小型、百万级数据集上寻求完全准确且精确的搜索结果的场景。
- IVF_FLAT 适合在精度和查询速度之间寻求理想平衡的场景。还有一个 GPU 版本GPU_IVF_FLAT。
- IVF_SQ8 适合寻求显著减少磁盘、CPU 和 GPU 内存消耗的场景,因为这些资源非常有限。
- IVF_PQ 适合追求高查询速度甚至不惜牺牲准确性的场景。还有一个 GPU 版本GPU_IVF_PQ 。
- HNSW HNSW 是基于图的索引,最适合对搜索效率要求较高的场景。
Similarity metrics
相似度指标用于衡量向量之间的相似度,在向量查询时可以指定相似度指标来获取较好结果和性能。
浮点向量的指标有:
- Euclidean distance (L2) :通常用于计算机视觉(CV)领域。
- Inner product (IP) :一般用于自然语言处理(NLP)领域。
二进制向量的指标有:
- Hamming:一般用在自然语言处理(NLP)领域。
- Jaccard:一般用在分子相似性搜索领域。
更多关于相似性指标的解释点链接了解。
Consistency
根据PACELC定理的定义,分布式数据库必须在一致性、可用性和延迟之间进行权衡。高一致性意味着高精度,但搜索延迟也较高,而低一致性则意味着搜索速度快,但会损失一定的数据可视性。因此,不同级别的一致性适合不同的场景。
Milvus 支持四种一致性级别:strong, bounded staleness, session, 和 eventually。Milvus 中默认的级别是bounded staleness。这四种一致性解释:
-
strong 最强一致性,确保用户能够读取到最新版本的数据。一致性级别设置为强,延迟会增加。因此,在功能测试时选择强一致性,以保证测试结果的准确性。强一致性也最适合那些对数据一致性有严格要求而牺牲搜索速度的应用。如处理订单支付和计费的在线系统等。
-
bounded staleness 允许在特定时间段内出现数据不一致。然而,一般来说,该时间段内的数据始终是全局一致的。适用于需要控制搜索延迟并且可以接受零星数据不可见性的场景。如在推荐引擎等系统中,数据不可见性有时对整体召回率影响很小,但可以提高推荐系统性能。
-
session 会话确保在同一会话期间所有数据写入都可以在读取中立即感知到。换句话说,当您通过一个客户端写入数据时,新插入的数据立即变得可搜索。适用同一会话中数据一致性要求较高的场景。如从图书馆系统中删除图书条目的数据,并且在确认删除并刷新页面(不同的会话)之后,该图书不应再在搜索结果中可见。
-
eventually 在“最终”的一致性下,副本开始处理具有最新更新值的读取请求。最终一致是四个级别中最弱的级别。最终一致性最适合对数据一致性要求不高但需要极快搜索性能的场景。如检索电商系统中产品评论和评级,其级别最终一致。
Milvus快速使用
下文快速介绍用java开发语言从数据库安装到最终一次向量相似性查找的步骤。
Milvus数据库安装
milvus提供dock、kubernetes集群安装和离线安装三种方式,具体安装步骤官网讲述详细并且易懂,具体安装的链接如下:
- dock安装方式见官网:milvus.io/docs/instal…
- kubernetes安装方式见官网:milvus.io/docs/instal…
- 离线安装方式见官网:milvus.io/docs/instal…
安装成功后自带一个网页版的管理系统,默认端口是8000。在浏览器输入: http://${ip}:8000 进入管理系统。
导入开发依赖包
- Maven
<dependency>
<groupId>io.milvus</groupId>
<artifactId>milvus-sdk-java</artifactId>
<version>2.3.0</version>
</dependency>
- Gradle/Grails
implementation 'io.milvus:milvus-sdk-java:2.3.0'
建立数据库连接
final MilvusServiceClient milvusClient = new MilvusServiceClient(
ConnectParam.newBuilder()
.withHost($Host)
.withPort($Port)
.build()
);
参数 | 描述 |
---|---|
Host |
Milvus 服务器的 IP 地址。 |
Port |
Milvus 服务器的端口,默认是: 19530 |
关于断开连接等请看官网教程
创建Collection
FieldType fieldType1 = FieldType.newBuilder()
.withName("book_id")
.withDataType(DataType.Int64)
.withPrimaryKey(true)
.withAutoID(false)
.build();
FieldType fieldType2 = FieldType.newBuilder()
.withName("word_count")
.withDataType(DataType.Int64)
.build();
FieldType fieldType3 = FieldType.newBuilder()
.withName("book_intro")
.withDataType(DataType.FloatVector)
.withDimension(2)
.build();
CreateCollectionParam createCollectionReq = CreateCollectionParam.newBuilder()
.withCollectionName("book")
.withDescription("Test book search")
.withShardsNum(2)
.addFieldType(fieldType1)
.addFieldType(fieldType2)
.addFieldType(fieldType3)
.withEnableDynamicField(true)
.build();
上述代码创建一个名为“book”的Collection,每个Collection会有一个默认Partition。关于Collection其它操作请查看官网教程。
新增数据
准备要插入的多条记录。
Random ran = new Random();
List<Long> book_id_array = new ArrayList<>();
List<Long> word_count_array = new ArrayList<>();
List<List<Float>> book_intro_array = new ArrayList<>();
for (long i = 0L; i < 2000; ++i) {
book_id_array.add(i);
word_count_array.add(i + 10000);
List<Float> vector = new ArrayList<>();
for (int k = 0; k < 2; ++k) {
vector.add(ran.nextFloat());
}
book_intro_array.add(vector);
}
保存记录到”book”中。
List<InsertParam.Field> fields = new ArrayList<>();
fields.add(new InsertParam.Field("book_id", book_id_array));
fields.add(new InsertParam.Field("word_count", word_count_array));
fields.add(new InsertParam.Field("book_intro", book_intro_array));
InsertParam insertParam = InsertParam.newBuilder()
.withCollectionName("book")
.withPartitionName("novel")
.withFields(fields)
.build();
milvusClient.insert(insertParam);
创建索引
final IndexType INDEX_TYPE = IndexType.IVF_FLAT; // IndexType
final String INDEX_PARAM = "{"nlist":1024}"; // ExtraParam
milvusClient.createIndex(
CreateIndexParam.newBuilder()
.withCollectionName("book")
.withFieldName("book_intro")
.withIndexType(INDEX_TYPE)
.withMetricType(MetricType.L2)
.withExtraParam(INDEX_PARAM)
.withSyncMode(Boolean.FALSE)
.build()
);
删除索引见官网教程。
搜索记录
Milvus内的所有搜索和查询操作都在内存中执行,在进行相似性搜索之前将集合加载到内存中。
milvusClient.loadCollection(
LoadCollectionParam.newBuilder()
.withCollectionName("book")
.build()
);
准备搜索参数。以下示例定义搜索将使用欧氏距离计算距离,并从 IVF_FLAT 索引构建的十个最接近的向量。
final Integer SEARCH_K = 2; // TopK
final String SEARCH_PARAM = "{"nprobe":10, "offset":5}"; // Params
使用 Milvus 搜索向量。要在特定分区中搜索,请指定分区名称列表。Milvus 支持专门为搜索设置一致性级别。本主题中的示例将一致性级别设置为Strong
。
List<String> search_output_fields = Arrays.asList("book_id");
List<List<Float>> search_vectors = Arrays.asList(Arrays.asList(0.1f, 0.2f));
SearchParam searchParam = SearchParam.newBuilder()
.withCollectionName("book")
.withConsistencyLevel(ConsistencyLevelEnum.STRONG)
.withMetricType(MetricType.L2)
.withOutFields(search_output_fields)
.withTopK(SEARCH_K)
.withVectors(search_vectors)
.withVectorFieldName("book_intro")
.withParams(SEARCH_PARAM)
.build();
R<SearchResults> respSearch = milvusClient.search(searchParam);
检查最相似向量的主键值及其距离。
SearchResultsWrapper wrapperSearch = new SearchResultsWrapper(respSearch.getData().getResults());
System.out.println(wrapperSearch.getIDScore(0));
System.out.println(wrapperSearch.getFieldData("book_id", 0));
当搜索完成时,释放 Milvus 中加载的集合以减少内存消耗。
milvusClient.releaseCollection(
ReleaseCollectionParam.newBuilder()
.withCollectionName("book")
.build());
至此基于Milvus的一次相似性查找完成。
引用内容
- 深度学习推荐系统中各类流行的Embedding方法
- Embedding 和语言模型 LanguageModel
- The Illustrated Word2vec
- 深度学习——Embedding降维处理
- Milvus官网