学习路程五 向量数据库Milvus操作

前序

前面安装好了docker且成功拉取Milvus镜像,启动。通过python成功连接上了数据。接下来就继续更多Milvus的操作

在开始之前,先来简单了解一下向量数据库内一些东西的基本概念

概念 描述
数据库(Database) 类似与MySQL的database,首先需要一个库
集合 (Collection) 集合类似于MySQL中的表,它是用来存储向量的容器。集合可以有多个字段,每个字段对应一个向量属性。
向量 (Vector) 向量是多维空间中的点,通常用于表示数据的特征,是集合中的基本存储单元。
索引 (Index) 索引是用来加速向量搜索的数据结构,有多种索引类型,如 FLAT、IVF、HNSW 等,各自都有特定的适用场景。
Filed 字段,可以是结构化数据、向量;
Entity 一组Filed,类似表的一条记录。

〇、可视化界面工具attu

在开始之前,可以先安装一个可视化工具,这样能方便查看自己每一步操作后的结果。

下载地址:https://github.com/zilliztech/attu/releases

如果有使用Mac的,和我一样,安装后一直提示已损坏,无法使用的。可以按照下面步骤操作,我按网上搜的,亲测可用。

  1. 打开终端,输入

    sudo spctl --master-disable

    然后输入自己的密码就好

  2. 在终端输入

    sudo xattr -r -d com.apple.quarantine

    然后把这个不能打开的attu拖过来

    注意中间需要有个空格。

经过上面操作后,再重新运行,就能正常使用了。

一、数据库操作

有点类似MySQL,最先也得创建database,然后在这个database下使用,建表,查询,删除等。这块没什么特殊的内容

列出数据库

python 复制代码
from pymilvus import MilvusClient
client = MilvusClient()  # 上面的内容可以简写
# 列出数据库
res = client.list_databases()

print(res)
# 断开连接
client.close()

"""
['default']
"""

创建数据库

python 复制代码
# 创建数据库
res1 = client.create_database("test")

使用数据库

python 复制代码
client.using_database('test')

删除数据库

python 复制代码
client.drop_database("my_database")

二、集合相关操作

创建集合

python 复制代码
# 创建集合,重复运行,不会反复创建集合。
client.create_collection(
  collection_name="test_collection",  # 集合名称
  dimension=2048  # 向量的维度,这里的维度,关系到后面添加的向量数据的维度
)

这里的维度跟选用的embedding模型有关,不同模型向量化出来的数据维度不同。像之前用的zhipu的embedding-3,向量化出来就是2048维度。

默认创建的主键名为id,不自增。向量字段名为vector,计算向量相似度的方法是COSINE

判断集合存在

python 复制代码
client.has_collection('test_collection')  # 判断集合是否存在

查看集合

python 复制代码
res = client.describe_collection('test_collection')
print(res)
"""
{'collection_name': 'test_collection', 'auto_id': False, 'num_shards': 1, 'description': '', 'fields': [{'field_id': 100, 'name': 'id', 'description': '', 'type': <DataType.INT64: 5>, 'params': {}, 'is_primary': True}, {'field_id': 101, 'name': 'vector', 'description': '', 'type': <DataType.FLOAT_VECTOR: 101>, 'params': {'dim': 2048}}], 'functions': [], 'aliases': [], 'collection_id': 456228714208843162, 'consistency_level': 2, 'properties': {}, 'num_partitions': 1, 'enable_dynamic_field': True}
"""

删除集合

python 复制代码
client.drop_collection('demo_collection')

三、数据操作

插入数据&更新数据

首先准备数据,进行向量化,刚才看到了一个其他向量化的东西,不需要key值,先弄来用用试试

python 复制代码
from langchain_community.embeddings import TensorflowHubEmbeddings

# 向量化
embeddings = TensorflowHubEmbeddings()
text = "苹果"
query_result = embeddings.embed_query(text)
print(query_result, len(query_result))

运行过程会报错,按照报错,缺什么就安装什么。

结果是可用的,但是很慢,我也不确定是不是🪜的问题。

最好还是用之前的那个zhipu的embedding模型,反正有免费的Token数

创建collection时需要注意的是,zhipu那个embedding结果是2048维的。

python 复制代码
# 创建milvus对象
from pymilvus import MilvusClient

client = MilvusClient()
client.using_database('test')

# 创建一个集合,维度使用向量化后的数据维度
client.create_collection(
    collection_name="collection1",
    dimension=len(query_result)
)

# 组装数据
data = {
    "id": 0,
    "vector": query_result,
    "text":"苹果"
}
# 插入数据
res = client.insert("collection1", data)
print(res)


可以看到数据已经成功添加

如果要插入多条数据,把data做成一个列表就行了。

python 复制代码
data = [{
    "id": 1,
    "vector": [...],
    "text":"苹果手机"
}, {
    "id": 2,
    "vector": [...],
    "text":"菠萝手机"
}]
# 插入数据
res = client.insert("collection1", data)
print(res)

更新数据id不变,后面数据变一下,重新插入就好了

Milvus异步操作

在Milvus中,为了高性能运作,所以添加或更新数据的操作都是异步非阻塞的,对于刚添加入库的数据,有可能无法立即查询出来或者无法查询到最新的更新的数据。

python 复制代码
data = [{
    "id": 3,
    "vector": [...],
    "text":"苹果"
}, {
    "id": 4,
    "vector": [...],
    "text":"菠萝手机"
},{
    "id": 5,
    "vector": [...],
    "text":"菠萝手机"
},]
# 插入数据
res = client.insert("collection1", data)
print(res)

# 刚插入或者刚修改的数据,是无法被查询到的。
res = client.get('collection1', ids=[3, 4, 5],output_fields=["id","text"])
print(res)

client.close()

"""
{'insert_count': 3, 'ids': [3, 4, 5]}
data: []
"""

查询数据

查询相关操作可以访问Milvus官网查阅更多:https://milvus.io/docs/get-and-scalar-query.md

下面只尝试了一下一些基本用法

查询单条数据

python 复制代码
res = client.get('collection1', 0)

查询多条数据

python 复制代码
res = client.get('collection1', [0,2])

基于pyhton运算过滤条件查询

python 复制代码
res = client.query(
    collection_name="collection1",  # 集合名称
    # filter="id in [0,1,2]",  # python的运算符写查询过滤条件,但必须是字符串,而且内容必须符合python语法
    filter="id > 0",
    offset=1,  # 偏移量,开始下标+1
    limit=2,  # 结果限制条数2,限制返回指定数量的结果
    output_fields=["id", "vector"],  # 输出字段,*表示所有字段,可以指定想要的字段字段
)
print(res)

筛选搜索

也可以像SQL查询一样,模糊匹配查询

python 复制代码
res = client.query(
    collection_name="collection1",  # 集合名称
    # filter="id in [0,1,2]",  # python的运算符写查询过滤条件,但必须是字符串,而且内容必须符合python语法
    filter="text like \"%手机\"",
    # offset=1,  # 偏移量,开始下标+1
    # limit=2,  # 结果限制条数2,限制返回指定数量的结果
    output_fields=["id", "text"],  # 输出字段,*表示所有字段,可以指定想要的字段字段
)
print(res)
"""
data: data: ["{'id': 1, 'text': '苹果手机'}", 
"{'id': 2, 'text': '菠萝手机'}", 
"{'id': 4, 'text': '苹果手机'}", 
"{'id': 5, 'text': '菠萝手机'}"]
"""

基本 近似最近邻 (ANN)搜索

python 复制代码
query_vector = ...
res = client.search(
    collection_name="collection1",
    anns_field="vector",
    data=[query_vector],
    limit=2,
    search_params={"metric_type": "COSINE"}
)

for hits in res:
    for hit in hits:
        print(hit)

"""
{'id': 0, 'distance': 1.0, 'entity': {}}
{'id': 1, 'distance': 0.9998013377189636, 'entity': {}}
"""

这里我直接把第一个向量数据当做查询数据,进行查询。所以id=0的数据,和查询条件一模一样,余弦0,值就是1。

删除数据

查询条件的写法,与query的条件写法一致。

python 复制代码
client.delete(
    collection_name="collection1",
    filter="id in [1, 8, 9] and text like \"%手机\""
)

四、字段操作

数据类型

Milvus中提供了Schema类用于定义集合的属性(CollectionSchema)和字段(FieldSchema)的属性。字段存储的数据按不同结构分不同数据类型,这些都是提前通过Schema来进行定义。首先了解关于数据类型部分的内容。

常量 常数 字段类型 描述
BOOL 1 标量 布尔类型
INT8 2 标量 8位整型
INT16 3 标量 16位整型
INT32 4 标量 32位整型
INT64 5 主键,标量 64位整型
FLOAT 10 标量 单精度浮点型
DOUBLE 11 标量 双精度浮点型
VARCHAR 21 主键,标量 字符串类型,max_length=65,535
BINARY_VECTOR 100 向量 二进制向量类型
FLOAT_VECTOR 101 向量 32位单精度浮点数向量
FLOAT16_VECTOR 102 向量 16位半精度浮点数向量
BFLOAT16_VECTOR 103 向量 16位半精度浮点数向量,比FLOAT16_VECTOR显存占用量更小,精度更低。
SPARSE_FLOAT_VECTOR 104 向量 稀疏向量,存储非零元素及其相应索引的列表
ARRAY 22 复合 数组,就是python中的列表
JSON 23 复合 json文档,就是python中的字典
NONE 0 空类型,无法设置为字段类型,milvus返回的格式,
UNKNOWN 999 未知类型,无法设置为字段类型,milvus返回的格式。

Milvus允许在创建schema时为每个标量字段指定默认值,从而减低插入数据的复杂性,但不包括主键字段。如果在插入数据时将字段留空,则将应用为此字段指定的默认值。

通过schema创建集合

python 复制代码
from pymilvus import DataType
from pymilvus import MilvusClient

client = MilvusClient()


client.using_database('test')
# enable_dynamic_field=True 表示开启动态字段,开启后,除 id 和 vector 之外的所有字段都被视为动态字段。
# 这些额外的字段被保存为在名为 $meta 的特殊字段内的键值对。在后面的操作当前集合的数据时允许在插入数据时包含未定义的额外字段。
schema = client.create_schema(enable_dynamic_field=True)

# schema.add_field(field_name="字段名", datatype=DataType.字段类型, is_primary=True, auto_id=True, description="字段描述"), # 设置主键字段
# schema.add_field(field_name="字段名", datatype=DataType.字段类型, default_value=默认值, dim=维度, description="字段描述"),
schema.add_field(field_name="id", datatype=DataType.INT64, is_primary=True, auto_id=True)
schema.add_field(field_name="vector", datatype=DataType.FLOAT_VECTOR, dim=5)

index_params = client.prepare_index_params()
# 当把索引设置为AUTOINDEX,则表示将来这里的索引类型是根据存储内容来自动识别。
index_params.add_index(field_name="vector",index_type="AUTOINDEX",metric_type="COSINE")
collection = client.create_collection(
    collection_name="test_01", # 集合名称
    schema=schema, # 字段参数
    index_params=index_params,  # 索引参数
)

五、索引

索引是提高数据检索效率的关键技术。在 Milvus 中,索引用于加速向量数据的搜索过程。通过索引,可以快速定位到与查询向量最相似的数据向量。

索引类型

Milvus 提供了多种索引类型对字段值进行排序,以实现高效的相似性搜索。

索引 适用场景
FLAT 原始文件索引,适合在小型、百万级数据集上寻求完全准确和精确搜索结果的,且需要100%召回率的场景。
IVF_FLAT 基于量化的索引 倒排文件索引,适合寻求在准确性和查询速度之间取得理想平衡的场景。GPU_IVF_FLAT是IVF_FLAT的GPU版本实现。
IVF_SQ8 基于量化的索引 适合寻求显著减少磁盘、CPU和GPU内存消耗的场景
IVF_PQ 基于量化的索引 适合寻求高查询速度,即使以牺牲准确性为代价的场景。GPU_IVF_PQ是IVF_PQ的GPU版本实现。
HNSW 基于图的索引 适合对搜索效率有高要求的场景

注意:

Milvus 目前仅支持对向量和标量字段创建索引。其中给向量字段建立的索引叫向量索引,同理,给标量字段创建的索引叫标量索引。

建议对经常使用的向量字段和标量字段创建索引。

Milvus 目前只支持为集合的每个字段创建一个索引文件。注意:是一个字段只能设置一个索引,不是一个集合只有一个索引!!

Milvus 还提供三种度量类型:余弦相似度 (COSINE)、欧几里得距离 (L2) 和内积 (IP,Inner Product)来测量向量嵌入之间的距离。

常用度量方法参考:https://blog.csdn.net/qq_42222846/article/details/145829907

删除索引

因为之前创建集合时默认是有索引的,先删除

python 复制代码
client.release_collection('test_01')  # 先从内存中释放数据
client.drop_index(
    collection_name="test_01",
    index_name="vector"
)

删除完成后就变成没有索引了

创建索引

python 复制代码
index_params = client.prepare_index_params()

index_params.add_index(
    collection_name="test_01",
    field_name="vector",
    index_params=index_params,
    metric_type="L2",
    index_type="IVF_FLAT",
    params={"nlist": 100},  # list,分区设置
)

client.create_index(collection_name='test_01', index_params=index_params)
# 创建完索引以后,需要手动加载下集合
client.load_collection(collection_name='test_01')

创建完成后就是我们设置的索引了

查看索引

python 复制代码
res = client.describe_index(
     collection_name="test_01",
     index_name="vector"
 )

print(res)

"""
{'nlist': '100', 
'index_type': 'IVF_FLAT', 
'collection_name': 'test_01',
'index_params': "[{'field_name': 'vector', 'index_type': 'IVF_FLAT', 'index_name': '', 'collection_name': 'test_01', 'index_params': <pymilvus.milvus_client.index.IndexParams object at 0x1278cffd0>, 'metric_type': 'L2', 'params': {'nlist': 100, 'index_type': 'IVF_FLAT', 'collection_name': 'test_01', 'index_params': <pymilvus.milvus_client.index.IndexParams object at 0x1278cffd0>, 'metric_type': 'L2'}}]", 
'metric_type': 'L2', 
'field_name': 'vector',
'index_name': 'vector', 
'total_rows': 0, 
'indexed_rows': 0, 
'pending_index_rows': 0, 
'state': 'Finished'}
"""

六、结合智谱AI向量化存储Milvus,再查询

1. 向量化,存储

python 复制代码
import os

os.environ["ZHIPUAI_API_KEY"] = "a22568xxx"

from langchain_community.embeddings import ZhipuAIEmbeddings
embeddings = ZhipuAIEmbeddings(
    model="embedding-3",
    dimensions=1024
)

data = [
    "欢迎来到成都。",
    "欢迎来到重庆。",
    "欢迎来到北京。",
    "我喜欢成都。",
    "你是重庆人吗?"
]
embeddings = embeddings.embed_documents(data)
data = [{"vector": item} for item in embeddings]

# 创建milvus对象,操作数据集
from pymilvus import MilvusClient

client = MilvusClient()
client.using_database('test')

from pymilvus import DataType

schema = client.create_schema(enable_dynamic_field=True)
schema.add_field(field_name="id", datatype=DataType.INT64, is_primary=True, auto_id=True),
schema.add_field(field_name="vector", datatype=DataType.FLOAT_VECTOR, dim=1024)  # 与上面embedding模型得到的dim值保持一致

index_params = client.prepare_index_params()
index_params.add_index(field_name="vector", index_type="IVF_FLAT", metric_type="L2", nlist=128)
collection = client.create_collection(
    collection_name="my_collection",  # 集合名称
    schema=schema,  # 字段参数
    index_params=index_params,  # 索引参数
)

client.load_collection('my_collection')

# 把zhipu ai向量化的数据存储起来
client.insert(collection_name='my_collection', data=data)

client.close()

可以看到数据被存起来了。度量方式是L2

2.模拟用户查询(L2)

python 复制代码
# -*- coding: utf-8 -*-
# @Author : John
# @Time : 2025/02/25
# @File : query.py

import os
from pymilvus import MilvusClient

os.environ["ZHIPUAI_API_KEY"] = "a225xxx"

client = MilvusClient()

# 切换数据库
client.using_database('test')

# 把要查询的内容转换嵌入变量


from langchain_community.embeddings import ZhipuAIEmbeddings

embeddings = ZhipuAIEmbeddings(
    model="embedding-3",
    dimensions=1024
)
# 把用户查询的数据向量化
query_sentence_embedding = embeddings.embed_query("欢迎来到深圳")

# 到milvus中查询
search_params = {
    "metric_type": "L2",  # 因为集合中的索引使用的就是L2(欧氏距离),所以我们这里必须写成欧式距离
    "params": {}
}
result = client.search(
    collection_name="my_collection",
    data=[query_sentence_embedding],
    search_params=search_params
)

print(result)
client.close()

得到结果如下

python 复制代码
{'id': 456235719503916998, 'distance': 0.23892742395401, 'entity': {}}, 
{'id': 456235719503916999, 'distance': 0.24597115814685822, 'entity': {}}, 
{'id': 456235719503917000, 'distance': 0.2479812204837799, 'entity': {}}, 
{'id': 456235719503917001, 'distance': 0.43177342414855957, 'entity': {}}, 
{'id': 456235719503917002, 'distance': 0.47823566198349, 'entity': {}}]
python 复制代码
存储的数据:
    "欢迎来到成都。",
    "欢迎来到重庆。",
    "欢迎来到北京。",
    "我喜欢成都。",
    "你是重庆人吗?"
查询的数据:
	"欢迎来到深圳"

可以看出,查询的这句话,与前三句很相近,只有2个字不同,而和下面2句差距就比较大。输出的结果也是如此。

L2距离查询,也就是两点之间的距离,越小就越相近。

七、通过LangChain调用milvus

python 复制代码
# 1.初始化嵌入模型
import os
os.environ["ZHIPUAI_API_KEY"] = "a2256xxx"

from langchain_community.embeddings import ZhipuAIEmbeddings
embeddings_model = ZhipuAIEmbeddings(
    model="embedding-3",
    dimensions=1024
)

# 2. Milvus数据库配置
milvus_host = "127.0.0.1"  # Milvus服务器的主机名或IP地址
milvus_port = "19530"  # Milvus服务器的端口
collection_name = "my_collection"  # 数据集合的名称

# 3. 初始化Milvus向量存储【
from langchain_milvus import Milvus
vector_store = Milvus(
    embedding_function=embeddings_model,  # 指定嵌入模型
    collection_name=collection_name,
    connection_args={"host": milvus_host, "port": milvus_port},
    auto_id=True,  # 设置集合中的数据为自动增长ID,默认是False
    # primary_field="id",  # 设置主键名称,默认是id,可以不改
)

# 4.嵌入文本数据并存储到Milvus
texts = [
    "小红是公司财务。",
    "小张是开发人员。",
    "小刘是前台。"
]
from langchain.schema import Document
documents = [Document(page_content=text, metadata={'tid': key}) for key,text in enumerate(texts)]
vector_store.add_documents(documents)


# 5. 查询并搜索相似文本
query = "公司的前台是谁?"
embedded_query = embeddings_model.embed_query(query)
# 打印搜索结果【1个结果】
# print(embedded_query)

# 使用Milvus进行相似度搜索
search_results = vector_store.similarity_search(query)
# 6. 打印搜索结果【多个结果】,第一个是相似度最高的结果
for result in search_results:
    print("相关文本:", result.page_content, result.metadata)

"""
相关文本: 小刘是前台。 {'tid': 2, 'pk': 456235719503917023}
相关文本: 小红是公司财务。 {'tid': 0, 'pk': 456235719503917021}
相关文本: 小张是开发人员。 {'tid': 1, 'pk': 456235719503917022}
"""

更换问题后,相似度计算靠前的答案也更改了。

python 复制代码
....
query = "谁是公司的财务?"
...
"""
相关文本: 小红是公司财务。 {'pk': 456235719503917021, 'tid': 0}
相关文本: 小刘是前台。 {'pk': 456235719503917023, 'tid': 2}
相关文本: 小张是开发人员。 {'pk': 456235719503917022, 'tid': 1}
"""

常用的距离度量方法

如文中出现的 L2, COSIN等这些

可以参考:https://blog.csdn.net/qq_42222846/article/details/145829907

相关推荐
桐桐桐几秒前
FastAPI 学习笔记
python·学习·fastapi
一抓掉一大把1 分钟前
git -学习笔记
笔记·git·学习
山河君13 分钟前
音频进阶学习十六——LTI系统的差分方程与频域分析一(频率响应)
学习·音视频·信号处理
avi911133 分钟前
[AI相关]问问DeepSeek如何基于Python,moviePy实现视频字幕功能
python·音视频·moviepy·deepseek
鑫仔的记忆35 分钟前
【Oracle】视图用法和示例
数据库·oracle
zxfeng~43 分钟前
深度学习之“雅可比矩阵与黑塞矩阵”
人工智能·python·深度学习·神经网络
大橙子房1 小时前
AI学习第六天-python的基础使用-趣味图形
前端·python·学习
小王子10241 小时前
设计模式Python版 观察者模式(上)
python·观察者模式·设计模式
Channing Lewis1 小时前
如何用python将pdf转为text并提取其中的图片
服务器·python·pdf
码代码的小仙女1 小时前
学习笔记-07生产者-消费者模型4种实现方式
java·学习