Redis 作为向量数据库快速入门指南
提示
来自deepseek解释
原文链接:https://redis.io/docs/latest/develop/get-started/vector-database/
代码示例图例
以下代码示例展示了如何使用不同的编程语言和客户端库执行相同的操作:
- Redis CLI:Redis 的命令行界面
- C#(同步):StackExchange.Redis 同步客户端
- C#(异步):StackExchange.Redis 异步客户端
- Go:go-redis 客户端
- Java(同步 - Jedis):Jedis 同步客户端
- Java(异步 - Lettuce):Lettuce 异步客户端
- Java(响应式 - Lettuce):Lettuce 响应式/流式客户端
- JavaScript(Node.js):node-redis 客户端
- PHP:Predis 客户端
- Python:redis-py 客户端
- Rust(同步):redis-rs 同步客户端
- Rust(异步):redis-rs 异步客户端
每个代码示例都展示了跨不同语言的相同基本操作。具体语法和模式因语言和客户端库而异,但底层的 Redis 命令和行为保持一致。
本快速入门指南将帮助您:
- 了解什么是向量数据库
- 创建 Redis 向量数据库
- 创建向量嵌入并存储向量
- 查询数据并执行向量搜索
本指南使用 RedisVL,这是一个专为向量处理高度优化的 Python 客户端库。您可能还对其他客户端库的向量查询示例感兴趣:
了解向量数据库
数据通常是非结构化的,这意味着它没有由良好定义的模式来描述。非结构化数据的示例包括文本段落、图像、视频或音频。存储和搜索非结构化数据的一种方法是使用向量嵌入。
什么是向量? 在机器学习和 AI 中,向量是表示数据的数字序列。它们是模型的输入和输出,以数字形式封装底层信息。向量将非结构化数据(如文本、图像、视频和音频)转换为机器学习模型可以处理的格式。
- 为什么它们很重要? 向量捕获数据中固有的复杂模式和语义含义,使其成为各种应用程序的强大工具。它们允许机器学习模型更有效地理解和操作非结构化数据。
- 增强传统搜索。 传统的关键词或词汇搜索依赖于单词或短语的精确匹配,这可能具有局限性。相比之下,向量搜索(或语义搜索)利用了向量嵌入中捕获的丰富信息。通过将数据映射到向量空间,相似的项目根据其含义被放置在彼此附近。这种方法允许更准确和更有意义的搜索结果,因为它考虑了查询的上下文和语义内容,而不仅仅是使用的确切单词。
创建 Redis 向量数据库
您可以使用 Redis 开源版 作为向量数据库。它允许您:
- 在哈希或 JSON 文档中存储向量及关联的元数据
- 创建和配置用于搜索的二级索引
- 执行向量搜索
- 更新向量和元数据
- 删除和清理
开始使用的最简单方式是使用 Redis Cloud:
- 创建一个免费账户。
2. 按照说明创建一个免费数据库。
这个免费的 Redis Cloud 数据库开箱即用地包含所有 Redis 开源功能。
您也可以选择遵循安装指南在本地计算机上安装 Redis。
安装所需的 Python 包
创建一个 Python 虚拟环境并使用 pip 安装以下依赖:
redis:您可以在本文档站的客户端部分找到有关redis-py客户端库的更多详细信息。pandas:Pandas 是一个数据分析库。sentence-transformers:您将使用 SentenceTransformers 框架在全文本上生成嵌入。tabulate:pandas使用tabulate来渲染 Markdown。
您还需要在 Python 代码中进行以下导入:
基础篇:导入向量数据库操作所需的 Python 包,包括 redis、pandas 和 sentence-transformers
难度: 初级
可用语言: Python
Python
import json
import time
import numpy as np
import pandas as pd
import requests
import redis
from redis.commands.search.field import (
NumericField,
TagField,
TextField,
VectorField,
)
from redis.commands.search.index_definition import IndexDefinition, IndexType
from redis.commands.search.query import Query
from sentence_transformers import SentenceTransformer连接
连接到 Redis。默认情况下,Redis 返回二进制响应。要解码它们,您需要传递 decode_responses 参数并将其设置为 True:
基础篇:连接到 Redis 服务器,启用 decode_responses 以接收解码后的字符串而非字节
难度: 初级
可用语言: Python
Python
client = redis.Redis(host="localhost", port=6379, decode_responses=True)您也可以不使用本地 Redis 服务器,而是从 Redis Cloud 数据库配置页面复制并粘贴连接详情。以下是一个托管在 AWS 区域 us-east-1 并监听端口 16379 的云数据库的连接字符串示例:redis-16379.c283.us-east-1-4.ec2.cloud.redislabs.com:16379。连接字符串的格式为 host:port。您还必须复制并粘贴云数据库的用户名和密码。使用默认用户连接的代码行随后变为 client = redis.Redis(host="redis-16379.c283.us-east-1-4.ec2.cloud.redislabs.com", port=16379, password="your_password_here", decode_responses=True)。
准备演示数据集
本快速入门指南还使用了 bikes 数据集。以下是其中的一个示例文档:
{
"model": "Jigger",
"brand": "Velorim",
"price": 270,
"type": "Kids bikes",
"specs": {
"material": "aluminium",
"weight": "10"
},
"description": "Small and powerful, the Jigger is the best ride for the smallest of tikes! ..."
}description 字段包含自行车的自由格式文本描述,将用于创建向量嵌入。
1. 获取演示数据
您首先需要以 JSON 数组的形式获取演示数据集:
基础篇:以 JSON 数组形式获取演示数据集,为向量数据库操作准备数据
难度: 初级
可用语言: Python
Python
URL = ("https://raw.githubusercontent.com/bsbodden/redis_vss_getting_started"
"/main/data/bikes.json"
)
response = requests.get(URL, timeout=10)
bikes = response.json()检查其中一个自行车 JSON 文档的结构:
基础篇:检查演示数据集中单个文档的结构以了解数据格式
难度: 初级
可用语言: Python
Python
json.dumps(bikes[0], indent=2)2. 将演示数据存储到 Redis
现在迭代 bikes 数组,使用 JSON.SET 命令将数据作为 JSON 文档存储在 Redis 中。以下代码使用管道来最小化网络往返时间:
实用模式:使用 JSON.SET 配合管道将演示数据作为 JSON 文档存储到 Redis,以最小化网络往返
难度: 初级
可用语言: Python
Python
pipeline = client.pipeline()
for i, bike in enumerate(bikes, start=1):
redis_key = f"bikes:{i:03}"
pipeline.json().set(redis_key, "$", bike)
res = pipeline.execute()
# >>> [True, True, True, True, True, True, True, True, True, True, True]加载后,您可以使用 JSONPath 表达式从 Redis 中的 JSON 文档检索特定属性:
基础篇:使用 JSONPath 表达式从 Redis 中的 JSON 文档检索特定属性
难度: 初级
可用语言: Python
Python
res = client.json().get("bikes:010", "$.model")
# >>> ['Summit']3. 选择文本嵌入模型
HuggingFace 拥有大量可通过 SentenceTransformers 框架在本地服务的文本嵌入模型。这里我们使用广泛用于搜索引擎、聊天机器人和其他 AI 应用的 MS MARCO 模型。
from sentence_transformers import SentenceTransformer
embedder = SentenceTransformer('msmarco-distilbert-base-v4')4. 生成文本向量嵌入
迭代所有前缀为 bikes: 的 Redis 键:
基础篇:检索具有特定前缀的所有 Redis 键,以识别要处理的文档
难度: 初级
可用语言: Python
Python
keys = sorted(client.keys("bikes:*"))
# >>> ['bikes:001', 'bikes:002', ..., 'bikes:011']将这些键作为 JSON.MGET 命令的输入,配合 $.description 字段,将描述收集到一个列表中。然后将描述列表传递给 .encode() 方法:
创建嵌入:使用 SentenceTransformers 模型从文档描述生成文本嵌入,创建向量表示
难度: 中级
可用语言: Python
Python
descriptions = client.json().mget(keys, "$.description")
descriptions = [item for sublist in descriptions for item in sublist]
embedder = SentenceTransformer("msmarco-distilbert-base-v4")
embeddings = embedder.encode(descriptions).astype(np.float32).tolist()
VECTOR_DIMENSION = len(embeddings[0])
# >>> 768使用 JSON.SET 命令将向量化的描述插入到 Redis 中的自行车文档中。以下命令在每个文档的 JSONPath $.description_embeddings 下插入一个新字段。同样,使用管道以避免不必要的网络往返:
实用模式:使用 JSON.SET 配合管道将生成的向量嵌入插入到 JSON 文档中,实现高效的批量更新
难度: 中级
可用语言: Python
Python
pipeline = client.pipeline()
for key, embedding in zip(keys, embeddings):
pipeline.json().set(key, "$.description_embeddings", embedding)
pipeline.execute()
# >>> [True, True, True, True, True, True, True, True, True, True, True]使用 JSON.GET 命令检查其中一个更新后的自行车文档:
基础篇:使用 JSON.GET 检查包含原始数据和生成向量嵌入的更新文档
难度: 中级
可用语言: Python
Python
res = client.json().get("bikes:010")
# >>>
# {
# "model": "Summit",
# "brand": "nHill",
# "price": 1200,
# "type": "Mountain Bike",
# "specs": {
# "material": "alloy",
# "weight": "11.3"
# },
# "description": "This budget mountain bike from nHill performs well..."
# "description_embeddings": [
# -0.538114607334137,
# -0.49465855956077576,
# -0.025176964700222015,
# ...
# ]
# }在 JSON 文档中存储向量嵌入时,嵌入以 JSON 数组的形式存储。在上面的示例中,为了可读性,数组被大幅缩短。
创建索引
1. 创建包含向量字段的索引
您必须创建索引才能查询文档元数据或执行向量搜索。使用 FT.CREATE 命令:
FT.CREATE idx:bikes_vss ON JSON
PREFIX 1 bikes: SCORE 1.0
SCHEMA
$.model AS model TEXT WEIGHT 1.0 NOSTEM
$.brand AS brand TEXT WEIGHT 1.0 NOSTEM
$.price AS price NUMERIC
$.type TAG SEPARATOR ","
$.description AS description TEXT WEIGHT 1.0
$.description_embeddings AS vector VECTOR FLAT 6 TYPE FLOAT32 DIM 768 DISTANCE_METRIC COSINEPython
index_definition = IndexDefinition(
prefix=["bikes:"], index_type=IndexType.JSON
)
schema = (
TextField("$.model", as_name="model", no_stem=True),
TextField("$.brand", as_name="brand", no_stem=True),
NumericField("$.price", as_name="price"),
TagField("$.type", as_name="type", separator=","),
TextField("$.description", as_name="description"),
VectorField(
"$.description_embeddings",
"FLAT",
{
"TYPE": "FLOAT32",
"DIM": 768,
"DISTANCE_METRIC": "COSINE",
},
as_name="vector",
),
)
client.ft("idx:bikes_vss").create_index(
schema, definition=index_definition
)以下是 VECTOR 字段定义的分解:
$.description_embeddings AS vector:向量字段的 JSON 路径及其字段别名vector。FLAT:指定索引方法,可以是平面索引或分层可导航小世界图(HNSW)。TYPE FLOAT32:设置向量分量的浮点精度,此处为 32 位浮点数。DIM 768:嵌入的长度或维度,由所选的嵌入模型决定。DISTANCE_METRIC COSINE:选择的距离函数:余弦距离。
您可以在向量参考文档中找到关于所有这些选项的更多详细信息。
2. 检查索引状态
一旦执行 FT.CREATE 命令,索引过程就在后台运行。在短时间内,所有 JSON 文档应该都被索引并准备好查询。要验证这一点,您可以使用 FT.INFO 命令,该命令提供关于索引的详细信息和统计信息。特别值得关注的是成功索引的文档数和失败数:
实用模式:使用 FT.INFO 检查索引的状态和统计信息,验证文档是否成功索引
难度: 中级
可用语言: Redis CLI, Python
Redis CLI
FT.INFO idx:bikes_vssPython
info = client.ft("idx:bikes_vss").info()
num_docs = info["num_docs"]
indexing_failures = info["hash_indexing_failures"]
# print(f"{num_docs} documents indexed with {indexing_failures} failures")
# >>> 11 documents indexed with 0 failures执行向量搜索
本快速入门指南重点关注向量搜索。但是,您可以在文档数据库快速入门指南中了解有关如何基于文档元数据进行查询的更多信息。
1. 嵌入查询文本
以下代码片段显示了一个文本查询列表,您将使用它在 Redis 中执行向量搜索:
基础篇:定义一个文本查询列表,用于向量搜索操作
难度: 初级
可用语言: Python
Python
queries = [
"Bike for small kids",
"Best Mountain bikes for kids",
"Cheap Mountain bike for kids",
"Female specific mountain bike",
"Road bike for beginners",
"Commuter bike for people over 60",
"Comfortable commuter bike",
"Good bike for college students",
"Mountain bike for beginners",
"Vintage bike",
"Comfortable city bike",
]首先,使用相同的 SentenceTransformers 模型将每个输入查询编码为向量嵌入:
创建查询嵌入:使用与文档嵌入相同的 SentenceTransformers 模型将文本查询编码为向量嵌入
难度: 中级
可用语言: Python
Python
encoded_queries = embedder.encode(queries)
len(encoded_queries)
# >>> 11至关重要的是,您必须使用与嵌入文档相同的嵌入模型来嵌入查询。使用不同的模型将导致语义搜索结果不佳或出错。
2. K近邻(KNN)搜索
KNN 算法根据选择的距离函数计算查询向量与 Redis 中每个向量之间的距离。然后返回距离查询向量最近的 K 个项。这些是语义上最相似的项。
现在构建一个查询来实现这一点:
query = (
Query('(*)=>[KNN 3 @vector $query_vector AS vector_score]')
.sort_by('vector_score')
.return_fields('vector_score', 'id', 'brand', 'model', 'description')
.dialect(2)
)让我们分解上面的查询模板:
- 过滤表达式
(*)表示all。换句话说,没有应用任何过滤。您可以将其替换为按额外元数据过滤的表达式。 - 查询的
KNN部分搜索前 3 个最近邻。 - 查询向量必须作为参数
query_vector传入。 - 到查询向量的距离作为
vector_score返回。 - 结果按此
vector_score排序。 - 最后,它返回每个结果的
vector_score、id、brand、model和description字段。
要使用 FT.SEARCH 命令进行向量查询,您必须指定 DIALECT 2 或更高版本。
您必须将向量化的查询作为字节数组以参数名 query_vector 传递。以下代码从查询向量创建一个 Python NumPy 数组,并将其转换为紧凑的字节级表示,可作为参数传递给查询:
for i, encoded_query in enumerate(encoded_queries):
result = client.ft('idx:bikes_vss').search(
query,
{
'query_vector': np.array(encoded_query, dtype=np.float32).tobytes()
}
)
print(f"查询 '{queries[i]}' 的结果:")
for doc in result.docs:
print(f"\t{doc.id}: {doc.brand} {doc.model}")有了查询模板后,您可以在循环中执行所有查询。请注意,脚本将每个结果的 vector_score 计算为 1 - doc.vector_score。因为使用余弦距离作为度量,距离最小的项更接近,因此与查询更相似。
然后,遍历匹配的文档并创建一个结果列表,可以将其转换为 Pandas 表格以可视化结果:
查询数据:执行多个向量搜索查询,并将结果格式化为 Pandas 表格以进行可视化分析
难度: 高级
可用语言: Python
Python
def create_query_table(query, queries, encoded_queries, extra_params=None):
"""
创建查询结果表格。
"""
results_list = []
for i, encoded_query in enumerate(encoded_queries):
result_docs = (
client.ft("idx:bikes_vss")
.search(
query,
{"query_vector": np.array(encoded_query, dtype=np.float32).tobytes()}
| (extra_params if extra_params else {}),
)
.docs
)
for doc in result_docs:
vector_score = round(1 - float(doc.vector_score), 2)
results_list.append(
{
"query": queries[i],
"score": vector_score,
"id": doc.id,
"brand": doc.brand,
"model": doc.model,
"description": doc.description,
}
)
# 可选:使用 Pandas 将表格转换为 Markdown
queries_table = pd.DataFrame(results_list)
queries_table.sort_values(
by=["query", "score"], ascending=[True, False], inplace=True
)
queries_table["query"] = queries_table.groupby("query")["query"].transform(
lambda x: [x.iloc[0]] + [""] * (len(x) - 1)
)
queries_table["description"] = queries_table["description"].apply(
lambda x: (x[:497] + "...") if len(x) > 500 else x
)
return queries_table.to_markdown(index=False)查询结果显示每个查询的前三个匹配项(我们的 K 参数),以及每个查询对应自行车的 id、品牌和型号。
例如,对于查询 "Best Mountain bikes for kids",最高相似度得分(0.54),因此最接近的匹配是 'Nord' 品牌 'Chook air 5' 自行车型号,描述如下:
The Chook Air 5 gives kids aged six years and older a durable and uberlight mountain bike for their first experience on tracks and easy cruising through forests and fields. The lower top tube makes it easy to mount and dismount in any situation, giving your kids greater safety on the trails. The Chook Air 5 is the perfect intro to mountain biking.
从描述来看,这款自行车非常适合年幼儿童,嵌入准确地捕获了描述的语义。
语义搜索:使用 FT.SEARCH 配合 KNN 算法执行 K近邻向量搜索,查找语义相似的文档
难度: 高级
可用语言: Python
Python
query = (
Query("(*)=>[KNN 3 @vector $query_vector AS vector_score]")
.sort_by("vector_score")
.return_fields("vector_score", "id", "brand", "model", "description")
.dialect(2)
)
table = create_query_table(query, queries, encoded_queries)
print(table)
# >>> | Best Mountain bikes for kids | 0.54 | bikes:003...| query | score | id | brand | model | description |
|---|---|---|---|---|---|
| Best Mountain bikes for kids | 0.54 | bikes:003 | Nord | Chook air 5 | The Chook Air 5 gives kids aged six years and older a durable and uberlight mountain bike for their first experience on tracks and easy cruising through forests and fields. The lower top tube makes it easy to mount and dismount in any situation, giving your kids greater safety on the trails. The Chook Air 5 is the perfect intro to mountain biking. |
| 0.51 | bikes:010 | nHill | Summit | This budget mountain bike from nHill performs well both on bike paths and on the trail. The fork with 100mm of travel absorbs rough terrain. Fat Kenda Booster tires give you grip in corners and on wet trails. The Shimano Tourney drivetrain offered enough gears for finding a comfortable pace to ride uphill, and the Tektro hydraulic disc brakes break smoothly. Whether you want an affordable bike that you can take to work, but also take trail riding on the weekends or you’re just after a stable,... | |
| 0.46 | bikes:001 | Velorim | Jigger | Small and powerful, the Jigger is the best ride for the smallest of tikes! This is the tiniest kids’ pedal bike on the market available without a coaster brake, the Jigger is the vehicle of choice for the rare tenacious little rider raring to go. We say rare because this smokin’ little bike is not ideal for a nervous first-time rider, but it’s a true giddy up for a true speedster. The Jigger is a 12 inch lightweight kids bicycle and it will meet your little one’s need for speed. It’s a single... |
后续步骤
- 您可以通过阅读向量参考文档了解有关查询选项的更多信息,例如过滤器和向量范围查询。
- 完整的 Redis Search 文档 可能对您有用。
- 如果您想更交互式地跟随代码示例,可以使用启发了本快速入门指南的 Jupyter notebook。
- 如果您想查看更多 Redis 向量数据库实际应用的高级示例,请访问 GitHub 上的 Redis AI Resources 页面。