好,让我们开始这场关于 Redis Vector Search 的技术讲座,主题是:向量相似度搜索与 AI 应用。
各位观众,各位朋友,各位未来的人工智能大师们,大家好!今天咱们不整虚的,直接上干货,聊聊 Redis Vector Search 这玩意儿,看看它到底能帮咱们在 AI 的道路上走多远。
第一部分:啥是向量,啥是相似度?
在深入 Redis Vector Search 之前,咱们得先搞明白两个概念:向量和相似度。
-
向量 (Vector): 别一听这词儿就觉得高大上,其实它就是一堆数字。比如,你可以用一个向量
[0.2, 0.5, 0.1, 0.8]
来表示一篇文章,每个数字代表某个关键词在这篇文章里的重要程度。图像、音频、甚至用户行为,都可以转换成向量。关键在于,向量能把复杂的东西变成计算机能理解的数字。 -
相似度 (Similarity): 有了向量,我们就能算相似度了。相似度就是衡量两个向量有多像的指标。常用的相似度算法有:
-
余弦相似度 (Cosine Similarity): 这是最常用的。它计算的是两个向量夹角的余弦值。余弦值越接近 1,向量越相似;越接近 -1,向量越不相似;接近 0,则表示向量正交,也就是毫不相关。
-
欧几里得距离 (Euclidean Distance): 这就是我们熟悉的距离公式。距离越小,向量越相似。
-
点积 (Dot Product): 直接计算向量的乘积之和,但通常需要对向量进行归一化处理,使其长度相等,才能保证结果的准确性。
用表格来总结一下:
相似度算法 计算公式 优点 缺点 余弦相似度 cos(θ) = (A · B) / (||A|| * ||B||)
对向量长度不敏感,只关注方向,适合处理文本相似度等问题。 计算复杂度相对较高。 欧几里得距离 √∑(Ai - Bi)²
简单直观,容易理解。 对向量长度敏感,需要对向量进行归一化处理。 点积 A · B = ∑(Ai * Bi)
计算简单,速度快。 需要对向量进行归一化处理,否则结果会受到向量长度的影响。 -
第二部分:Redis Vector Search 是个啥?
现在主角登场了!Redis Vector Search,顾名思义,就是在 Redis 数据库里进行向量搜索。它能让我们快速找到与给定向量最相似的其他向量。
为啥要用 Redis 来做向量搜索呢?因为 Redis 快啊!Redis 是基于内存的数据库,读写速度非常快,这对于需要实时响应的 AI 应用来说至关重要。
Redis Vector Search 的核心在于它的索引结构。目前,Redis 支持两种索引结构:
-
FLAT: 暴力搜索,遍历所有向量,计算相似度,找到最相似的。适合数据量小的情况。
-
HNSW (Hierarchical Navigable Small World): 一种近似最近邻搜索算法。它通过构建多层图结构,加速搜索过程。适合数据量大的情况。
第三部分:动手实践:用 Redis Vector Search 搜索电影
光说不练假把式,咱们来个实战演练。假设我们有一堆电影,每个电影都有一个向量表示,表示了这部电影的剧情、演员、风格等等。现在,我们想找一部和《泰坦尼克号》最相似的电影。
1. 准备数据
首先,我们需要准备一些电影数据,并把它们转换成向量。这里为了简化,我们直接用 Python 模拟一些数据:
import redis
import numpy as np
# 连接 Redis
redis_client = redis.Redis(host='localhost', port=6379, db=0)
# 清空 Redis (可选)
redis_client.flushdb()
# 模拟电影数据
movies = {
"泰坦尼克号": [0.9, 0.8, 0.7, 0.6, 0.5],
"阿凡达": [0.2, 0.3, 0.9, 0.1, 0.4],
"星球大战": [0.1, 0.2, 0.8, 0.9, 0.3],
"复仇者联盟": [0.3, 0.9, 0.2, 0.8, 0.1],
"肖申克的救赎": [0.8, 0.7, 0.6, 0.5, 0.9]
}
# 将电影数据存入 Redis
for movie_name, movie_vector in movies.items():
# 将向量转换为字节数组
movie_vector_bytes = np.array(movie_vector, dtype=np.float32).tobytes()
redis_client.hset("movies", movie_name, movie_vector_bytes)
print("电影数据已存入 Redis")
2. 创建索引
接下来,我们需要在 Redis 中创建一个向量索引。这里我们使用 HNSW 索引:
# 创建索引
try:
redis_client.ft("movie_index").create_index(
fields=[
redis.ft.VectorField(
"value",
"HNSW",
{
"TYPE": "FLOAT32",
"DIM": 5, # 向量维度
"DISTANCE_METRIC": "COSINE" # 距离度量方式
}
)
]
)
print("索引创建成功")
except Exception as e:
print(f"索引创建失败: {e}")
这段代码的意思是:
redis_client.ft("movie_index")
:创建一个名为movie_index
的索引。create_index(...)
:定义索引的字段。VectorField("value", "HNSW", ...)
:创建一个向量字段,使用 HNSW 算法。"DIM": 5
:向量维度是 5。"DISTANCE_METRIC": "COSINE"
:使用余弦相似度作为距离度量方式。
3. 将向量数据加载到索引中
虽然我们已经将向量存入了 Redis,但是还没有把它们加载到索引中。我们需要使用 HSET
命令将向量数据添加到索引中。
# 将电影数据加载到索引中
for movie_name, movie_vector in movies.items():
movie_vector_bytes = np.array(movie_vector, dtype=np.float32).tobytes()
redis_client.hset(movie_name, mapping={"value": movie_vector_bytes}) # 这里的key是电影的名字,value是一个hash,hash里面有个字段叫value,存储的是向量
print(f"电影 {movie_name} 已添加到索引")
4. 进行搜索
现在,我们可以进行搜索了。我们想找到和《泰坦尼克号》最相似的电影:
# 搜索
query_vector = movies["泰坦尼克号"]
query_vector_bytes = np.array(query_vector, dtype=np.float32).tobytes()
# 构建查询语句
query = f"*=>[KNN 3 @value $vector AS score]" # KNN 3 表示返回3个结果
query_params = {"vector": query_vector_bytes}
# 执行查询
results = redis_client.ft("movie_index").search(query, query_params=query_params)
# 输出结果
print("搜索结果:")
for i, doc in enumerate(results.docs):
print(f" {i+1}. 电影: {doc.id}, 相似度: {doc.score}")
这段代码的意思是:
query = "*=>[KNN 3 @value $vector AS score]"
:构建查询语句,使用 KNN (K-Nearest Neighbors) 算法,返回 3 个最相似的电影。@value
表示向量字段,$vector
表示查询向量,AS score
表示将相似度分数命名为score
。query_params = {"vector": query_vector_bytes}
:设置查询参数,将查询向量传递给查询语句。results = redis_client.ft("movie_index").search(query, query_params=query_params)
:执行查询。results.docs
:包含了搜索结果的文档列表,每个文档都有一个id
(电影名称) 和一个score
(相似度分数)。
第五部分:更高级的用法
上面的例子只是 Redis Vector Search 的冰山一角。它还有很多更高级的用法,比如:
- 使用过滤器 (Filters): 可以在搜索时添加过滤器,例如只搜索特定类型的电影。
- 动态更新索引: 可以实时添加、删除、更新向量数据。
- 使用不同的距离度量方式: 除了余弦相似度,还可以使用欧几里得距离、点积等。
第六部分:Redis Vector Search 的应用场景
Redis Vector Search 在 AI 领域有着广泛的应用场景:
- 推荐系统: 根据用户的历史行为,推荐相似的商品、电影、音乐等。
- 图像搜索: 根据图像的特征向量,搜索相似的图像。
- 自然语言处理: 根据文本的语义向量,搜索相似的文本。
- 欺诈检测: 根据用户的行为向量,检测欺诈行为。
用表格来总结一下:
应用场景 | 描述 | 示例 |
---|---|---|
推荐系统 | 根据用户的历史行为,计算用户的兴趣向量,然后搜索与用户兴趣向量最相似的商品、电影、音乐等。 | 用户购买了《哈利波特》,推荐《魔戒》、《纳尼亚传奇》等奇幻小说。 |
图像搜索 | 将图像转换为特征向量,然后搜索与查询图像的特征向量最相似的图像。 | 用户上传一张猫的照片,搜索与该照片相似的其他猫的照片。 |
自然语言处理 | 将文本转换为语义向量,然后搜索与查询文本的语义向量最相似的文本。 | 用户输入“如何制作蛋糕”,搜索与该问题相关的菜谱、教程等。 |
欺诈检测 | 将用户的行为转换为行为向量,然后搜索与已知欺诈用户的行为向量最相似的用户。 | 检测信用卡欺诈,识别异常交易模式。 |
第七部分:总结与展望
今天,我们一起探索了 Redis Vector Search 的世界。它是一种强大的工具,可以帮助我们在 AI 应用中实现高效的向量相似度搜索。
当然,Redis Vector Search 还有很多需要完善的地方。比如,目前只支持 FLOAT32 数据类型,对于一些需要更高精度的数据,可能需要进行转换。另外,HNSW 索引的构建过程比较耗时,需要根据实际情况进行优化。
但是,我相信随着 Redis 的不断发展,Vector Search 会变得越来越强大,成为 AI 领域不可或缺的一部分。
好了,今天的讲座就到这里。希望大家有所收获,也希望大家能在 AI 的道路上越走越远!记住,编程的世界没有尽头,只有不断学习,才能保持进步!
附录:完整代码
为了方便大家学习,这里附上完整的代码:
import redis
import numpy as np
# 连接 Redis
redis_client = redis.Redis(host='localhost', port=6379, db=0)
# 清空 Redis (可选)
redis_client.flushdb()
# 模拟电影数据
movies = {
"泰坦尼克号": [0.9, 0.8, 0.7, 0.6, 0.5],
"阿凡达": [0.2, 0.3, 0.9, 0.1, 0.4],
"星球大战": [0.1, 0.2, 0.8, 0.9, 0.3],
"复仇者联盟": [0.3, 0.9, 0.2, 0.8, 0.1],
"肖申克的救赎": [0.8, 0.7, 0.6, 0.5, 0.9]
}
# 将电影数据存入 Redis
for movie_name, movie_vector in movies.items():
# 将向量转换为字节数组
movie_vector_bytes = np.array(movie_vector, dtype=np.float32).tobytes()
redis_client.hset("movies", movie_name, movie_vector_bytes)
print("电影数据已存入 Redis")
# 创建索引
try:
redis_client.ft("movie_index").create_index(
fields=[
redis.ft.VectorField(
"value",
"HNSW",
{
"TYPE": "FLOAT32",
"DIM": 5, # 向量维度
"DISTANCE_METRIC": "COSINE" # 距离度量方式
}
)
]
)
print("索引创建成功")
except Exception as e:
print(f"索引创建失败: {e}")
# 将电影数据加载到索引中
for movie_name, movie_vector in movies.items():
movie_vector_bytes = np.array(movie_vector, dtype=np.float32).tobytes()
redis_client.hset(movie_name, mapping={"value": movie_vector_bytes}) # 这里的key是电影的名字,value是一个hash,hash里面有个字段叫value,存储的是向量
print(f"电影 {movie_name} 已添加到索引")
# 搜索
query_vector = movies["泰坦尼克号"]
query_vector_bytes = np.array(query_vector, dtype=np.float32).tobytes()
# 构建查询语句
query = f"*=>[KNN 3 @value $vector AS score]" # KNN 3 表示返回3个结果
query_params = {"vector": query_vector_bytes}
# 执行查询
results = redis_client.ft("movie_index").search(query, query_params=query_params)
# 输出结果
print("搜索结果:")
for i, doc in enumerate(results.docs):
print(f" {i+1}. 电影: {doc.id}, 相似度: {doc.score}")
大家可以把这段代码复制到自己的电脑上,跑起来看看效果。如果遇到问题,欢迎提问!
记住,实践是检验真理的唯一标准。只有亲自尝试,才能真正理解 Redis Vector Search 的强大之处。
最后,祝大家编程愉快!