向量数据库
之前的实习中,曾经负责过构建向量数据库,当时使用的是milvus,相关代码在ModelDataBase: Es和向量数据库Milvus的构建与数据存储 (gitee.com)和milvus_support.py · 刘晓/ModelTraining - 码云 - 开源中国 (gitee.com)。训练数据集的清理和构建,相关代码在DataClean: 模型训练Excel数据的清理 (gitee.com)。
笔记开头,只介绍我曾经在构建和处理时遇到的一些问题。
首先,向量数据库的构建,我使用的embedding模型可在代码中找到,我用python写了对外的接口方便java程序调用(因为我熟悉使用java,没有第一时间考虑python构建),但是在后续构建中我发现,vector类型的向量vectorFloat在jdk版本低于16的时候不能用,stackflow中建议的加pom依赖也不行。其次,数据的结构和选择的索引也非常重要。java/python构建milvus的代码在仓库中,见上文链接。
以下为C3/4 搭建并使用向量数据库文章的学习和总结:
前序配置
这一部分参考上文中,milvus构建代码milvus_support.py · 刘晓/ModelTraining - 码云 - 开源中国 (gitee.com)### 数据存储与计算流程
- 数据库创建:构建所需Fields,以embedding为单索引(之后加入function calling后可以使用embedding向量和timestamp标量的双索引模式进行数据查找)
- Field配置:异步构建索引,后台构建,便于其他操作......
- 匹配数据:使用正则表达式匹配txt文件所需的数据......
- 处理数据:使用IO流以单个文件为单位读取数据并加以处理......
- Embedding:python调用大模型,提供计算接口,将question传入,以此计算向量值,存入embedding......
- 插入数据:以单个文件为单位插入向量数据库中以保证数据的一致性......
内容
Milvus是向量数据库模型构建,EsServiceImpl是Es的构建,存储清理后的训练数据,检查查询效率,MilvusServiceImpl中存放向量数据库的构建代码。MilvusIndexConstans中是向量数据库的结构设计。embedding中是调用的专门计算embedding的小模型,对外提供接口,供java后端调用。
Milvus
向Milvus向量数据库存储大模型问答对信息,python存储设置HNSW索引,对问题描述构建向量,修改索引参数但提升效果并不明显。调研发现,在少量不足百万数据下,构建合适的分区可以提升查询速度。
这里的数据已经读取并分割好存在excel表格中。
向量检索
MMR检索
最大边际相关性 (MMR, Maximum marginal relevance) 可以帮助我们在保持相关性的同时,增加内容的丰富度。
核心思想是在已经选择了一个相关性高的文档之后,再选择一个与已选文档相关性较低但是信息丰富的文档。这样可以在保持相关性的同时,增加内容的多样性,避免过于单一的结果。
Langchain的内置方法max_marginal_relevance_search已经帮我们首先了该算法,在执行max_marginal_relevance_search方法时,我们需要设置fetch_k参数,用来告诉向量数据库我们最终需要k个结果,向量数据库在搜索时会获取一个和问题相关的文档集,该文档集中的文档数量大于k,然后从中过滤出k个具有相关性同时兼顾多样性的文档。
#向量数据库地址
persist_directory = 'docs/chroma/'
embedding = OpenAIEmbeddings()
vectordb = Chroma(
persist_directory=persist_directory,
embedding_function=embedding
)
#打印向量数据库中的文档数量
print(vectordb._collection.count())
然后先用similarity_search方法来查询一下,它应该会返回两篇相同的文档:
question = "what did they say about matlab?"
docs_ss = vectordb.similarity_search(question,k=3)
docs_ss
下面我们使用max_marginal_relevance_search方法来搜索:
docs_mmr = vectordb.max_marginal_relevance_search(question,k=3)
docs_mmr
单向量搜索
指定目标集合名称、查询向量和所需的结果数。此操作返回一个结果集,其中包含最相似的向量、其 ID 以及与查询向量的距离。
下面是搜索与查询向量最相似的前 5 个实体的示例:
# Single vector search
res = client.search(
collection_name="test_collection", # Replace with the actual name of your collection
# Replace with your query vector
data=[[0.3580376395471989, -0.6023495712049978, 0.18414012509913835, -0.26286205330961354, 0.9029438446296592]],
limit=5, # Max. number of search results to return
search_params={
"metric_type": "IP", "params": {
}} # Search parameters
)
# Convert the output to a formatted JSON string
result = json.dumps(res, indent=4)
print(result)
输出类似于以下内容:
[
[
{
"id": 0,
"distance": 1.4093276262283325,
"entity": {
}
},
{
"id": 4,
"distance": 0.9902134537696838,
"entity": {
}
},
{
"id": 1,
"distance": 0.8519943356513977,
"entity": {
}
},
{
"id": 5,
"distance": 0.7972343564033508,
"entity": {
}
},
{
"id": 2,
"distance": 0.5928734540939331,
"entity": {
}
}
]
]
输出显示离查询向量最近的前 5 个相邻节点,包括其唯一 ID 和计算距离。
批量向量搜索
在批量向量搜索中,可以在字段中包含多个查询向量。系统并行处理这些向量,为每个查询向量返回一个单独的结果集,每个结果集都包含集合中找到的最接近的匹配项。
下面是从两个查询向量中搜索两组最相似实体的示例:
# Bulk-vector search
res = client.search(
collection_name="test_collection", # Replace with the actual name of your collection
data=[
[0.19886812562848388, 0.06023560599112088, 0.6976963061752597, 0.2614474506242501, 0.838729485096104],
[0.3172005263489739, 0.9719044792798428, -0.36981146090600725, -0.4860894583077995, 0.95791889146345]
], # Replace with your query vectors
limit=2, # Max. number of search results to return
search_params={
"metric_type": "IP", "params": {
}} # Search parameters
)
result = json.dumps(res, indent=4)
print(result)
输出类似于以下内容:
[
[
{
"id": 1,
"distance": 1.3017789125442505,
"entity": {
}
},
{
"id": 7,
"distance": 1.2419954538345337,
"entity": {
}
}
], # Result set 1
[
{
"id": 3,
"distance": 2.3358664512634277,
"entity": {
}
},
{
"id": 8,
"distance": 0.5642921924591064,
"entity": {
}
}
] # Result set 2
]
结果包括两组最近邻,每个查询向量一组,展示了批量向量搜索一次处理多个查询向量的效率。
下表列出了搜索参数中所有可能的设置。
参数名称 | 参数说明 |
---|---|
metric_type |
如何测量向量嵌入之间的相似性。 可能的值为 、 和 ,默认为加载的索引文件的值。IP``L2``COSINE |
params.nprobe |
搜索期间要查询的单位数。 该值位于 [1, nlist[1]] 范围内。 |
params.level |
搜索精度级别。 可能的值为 、 和 ,默认值为 。值越高,结果越准确,但性能越慢。1``2``3``1 |
params.radius |
查询向量和候选向量之间的最小相似度。 该值位于 [1, nlist[1]] 范围内。 |
params.range_filter |
相似性范围,可以选择优化对属于该范围的向量的搜索。 该值位于 [top-K[2], ∞] 范围内。 |
(此图COPY官方文档)