向量集嵌入
使用 Redis 向量集索引和查询嵌入
Redis 向量集允许您存储一组唯一的键,每个键都有其关联的向量。然后,您可以根据存储向量与您指定的查询向量之间的相似性,从集合中检索键。
您可以使用向量集存储任何类型的数值向量,但它们经过特别优化,适用于处理文本嵌入向量(参阅用于AI的Redis以了解更多关于文本嵌入的信息)。下面的示例展示了如何使用sentence-transformers
库生成向量嵌入,然后使用向量集和redis-py
进行存储和检索。
初始化
首先使用以下命令安装redis-py
的预览版本
pip install redis==6.0.0b2
此外,安装sentence-transformers
pip install sentence-transformers
在一个新的 Python 文件中,导入所需的类
from sentence_transformers import SentenceTransformer
import redis
import numpy as np
第一个导入的是SentenceTransformer
类,它从一段文本生成嵌入。本示例使用带有all-MiniLM-L6-v2
模型的SentenceTransformer
实例来生成嵌入。无论输入文本的长度如何,此模型都会生成具有 384 个维度的向量,但请注意输入被截断为 256 个标记(参阅Hugging Face文档中的词片标记化以了解更多关于标记与原始文本之间关系的信息)。
model = SentenceTransformer("sentence-transformers/all-MiniLM-L6-v2")
创建数据
示例数据包含在一个字典中,其中包含了一些名人的简要描述
peopleData = {
"Marie Curie": {
"born": 1867, "died": 1934,
"description": """
Polish-French chemist and physicist. The only person ever to win
two Nobel prizes for two different sciences.
"""
},
"Linus Pauling": {
"born": 1901, "died": 1994,
"description": """
American chemist and peace activist. One of only two people to win two
Nobel prizes in different fields (chemistry and peace).
"""
},
"Freddie Mercury": {
"born": 1946, "died": 1991,
"description": """
British musician, best known as the lead singer of the rock band
Queen.
"""
},
"Marie Fredriksson": {
"born": 1958, "died": 2019,
"description": """
Swedish multi-instrumentalist, mainly known as the lead singer and
keyboardist of the band Roxette.
"""
},
"Paul Erdos": {
"born": 1913, "died": 1996,
"description": """
Hungarian mathematician, known for his eccentric personality almost
as much as his contributions to many different fields of mathematics.
"""
},
"Maryam Mirzakhani": {
"born": 1977, "died": 2017,
"description": """
Iranian mathematician. The first woman ever to win the Fields medal
for her contributions to mathematics.
"""
},
"Masako Natsume": {
"born": 1957, "died": 1985,
"description": """
Japanese actress. She was very famous in Japan but was primarily
known elsewhere in the world for her portrayal of Tripitaka in the
TV series Monkey.
"""
},
"Chaim Topol": {
"born": 1935, "died": 2023,
"description": """
Israeli actor and singer, usually credited simply as 'Topol'. He was
best known for his many appearances as Tevye in the musical Fiddler
on the Roof.
"""
}
}
将数据添加到向量集
下一步是连接到 Redis 并将数据添加到新的向量集。
下面的代码使用字典的items()
视图迭代所有键值对,并将相应的元素添加到名为famousPeople
的向量集中。
使用SentenceTransformer
的encode()
方法将嵌入生成为float32
值的数组。tobytes()
方法将数组转换为字节字符串,您可以将其传递给vadd()
命令来设置嵌入。注意,vadd()
也可以接受float
值列表来设置向量,但字节字符串格式更紧凑,并节省一些传输时间。如果您随后使用vemb()
检索嵌入,它将返回向量作为数组而不是原始字节字符串(请注意,这与哈希向量索引中字节字符串的行为不同)。
对vadd()
的调用还将原始字典中的born
和died
值作为属性数据添加。您可以在查询期间或通过使用vgetattr()
方法访问此数据。
r = redis.Redis(decode_responses=True)
for name, details in peopleData.items():
emb = model.encode(details["description"]).astype(np.float32).tobytes()
r.vset().vadd(
"famousPeople",
emb,
name,
attributes={
"born": details["born"],
"died": details["died"]
}
)
查询向量集
您现在可以查询集合中的数据。基本方法是使用encode()
方法为查询文本生成另一个嵌入向量。(这与向集合添加元素使用的方法相同。)然后,将查询向量传递给vsim()
,以返回集合中的元素,并按与查询的相似度排序。
从一个简单的查询开始:“演员”
query_value = "actors"
actors_results = r.vset().vsim(
"famousPeople",
model.encode(query_value).astype(np.float32).tobytes(),
)
print(f"'actors': {actors_results}")
这返回以下元素列表(为清晰起见略作格式化)
'actors': ['Masako Natsume', 'Chaim Topol', 'Linus Pauling',
'Marie Fredriksson', 'Maryam Mirzakhani', 'Marie Curie',
'Freddie Mercury', 'Paul Erdos']
列表中的前两个人是两位演员,正如预期,但从 Linus Pauling 开始的人并没有特别以表演著称(并且在简短的描述文本中肯定没有任何关于此的信息)。就目前而言,搜索试图根据嵌入模型中包含的信息对集合中的所有元素进行排名。您可以使用vsim()
的count
参数将元素列表限制为仅最相关的几项
query_value = "actors"
two_actors_results = r.vset().vsim(
"famousPeople",
model.encode(query_value).astype(np.float32).tobytes(),
count=2
)
print(f"'actors (2)': {two_actors_results}")
# >>> 'actors (2)': ['Masako Natsume', 'Chaim Topol']
使用文本嵌入而不是简单的文本搜索的原因是嵌入代表语义信息。这使得查询能够找到即使文本不同但含义相似的元素。例如,“entertainer”(艺人)这个词并未出现在任何描述中,但如果您将其用作查询,则演员和音乐家在结果列表中排名最高
query_value = "entertainer"
entertainer_results = r.vset().vsim(
"famousPeople",
model.encode(query_value).astype(np.float32).tobytes()
)
print(f"'entertainer': {entertainer_results}")
# >>> 'entertainer': ['Chaim Topol', 'Freddie Mercury',
# >>> 'Marie Fredriksson', 'Masako Natsume', 'Linus Pauling',
# 'Paul Erdos', 'Maryam Mirzakhani', 'Marie Curie']
类似地,如果使用“科学”作为查询,则会得到以下结果
'science': ['Marie Curie', 'Linus Pauling', 'Maryam Mirzakhani',
'Paul Erdos', 'Marie Fredriksson', 'Freddie Mercury', 'Masako Natsume',
'Chaim Topol']
科学家排名最高,其次是数学家。考虑到数学和科学之间的联系,这似乎是合理的。
您还可以将过滤表达式与vsim()
一起使用,以进一步限制搜索。例如,重复“科学”查询,但这次将结果限制在 2000 年之前去世的人们
query_value = "science"
science2000_results = r.vset().vsim(
"famousPeople",
model.encode(query_value).astype(np.float32).tobytes(),
filter=".died < 2000"
)
print(f"'science2000': {science2000_results}")
# >>> 'science2000': ['Marie Curie', 'Linus Pauling',
# 'Paul Erdos', 'Freddie Mercury', 'Masako Natsume']
请注意,布尔过滤表达式是在执行向量距离计算之前应用于列表中的项。未通过过滤测试的项将完全从结果中移除,而不仅仅是降低排名。这有助于提高搜索性能,因为对于已被过滤掉的元素无需计算向量距离。
更多信息
有关更多信息和代码示例,请参阅向量集文档。有关文本嵌入以及您可以与 Redis 一起使用的其他 AI 技术的更多详细信息,请参阅用于AI的Redis部分。
您可能也对向量搜索感兴趣。这是 Redis 查询引擎的一项功能,允许您根据存储在其字段中的向量数据检索 JSON 和 哈希文档。