当前位置:   article > 正文

redis向量数据库快速开始(三)_redis a vector database

redis a vector database

redis向量数据库中文使用手册-快速开始(二)-CSDN博客

具体见:Redis as a vector database quick start guide | Docs

一、创建索引:

  1. 创建索引,使用矢量字段;
  2. 创建一个基于矢量元数据或基于搜索的索引来支持搜索;使用命令create_index;
  1. schema = (
  2.     TextField("$.model"no_stem=Trueas_name="model"),
  3.     TextField("$.brand"no_stem=Trueas_name="brand"),
  4.     NumericField("$.price"as_name="price"),
  5.     TagField("$.type"as_name="type"),
  6.     TextField("$.description"as_name="description"),
  7.     VectorField(
  8. "$.description_embeddings",
  9. "FLAT",
  10. {
  11. "TYPE": "FLOAT32",
  12. "DIM": VECTOR_DIMENSION,
  13. "DISTANCE_METRIC": "COSINE",
  14. },
  15.         as_name="vector",
  16. ),
  17. )
  18. definition = IndexDefinition(prefix=["bikes:"], index_type=IndexType.JSON)
  19. res = client.ft("idx:bikes_vss").create_index(
  20.     fields=schema, definition=definition
  21. )
  22. >>> 'OK'
  1. import json
  2. import time
  3. import numpy as np
  4. import pandas as pd
  5. import redis
  6. import requests
  7. from redis.commands.search.field import (
  8.     NumericField,
  9.     TagField,
  10.     TextField,
  11.     VectorField,
  12. )
  13. from redis.commands.search.indexDefinition import IndexDefinition, IndexType
  14. from redis.commands.search.query import Query
  15. from sentence_transformers import SentenceTransformer
  16. url = "https://raw.githubusercontent.com/bsbodden/redis_vss_getting_started/main/data/bikes.json"
  17. response = requests.get(url)
  18. bikes = response.json()
  19. json.dumps(bikes[0], indent=2)
  20. client = redis.Redis(host="localhost", port=6379, decode_responses=True)
  21. res = client.ping()
  22. >>> True
  23. pipeline = client.pipeline()
  24. for i, bike in enumerate(bikes, start=1):
  25.     redis_key = f"bikes:{i:03}"
  26.     pipeline.json().set(redis_key, "$", bike)
  27. res = pipeline.execute()
  28. >>> [True, True, True, True, True, True, True, True, True, True, True]
  29. res = client.json().get("bikes:010", "$.model")
  30. >>> ['Summit']
  31. keys = sorted(client.keys("bikes:*"))
  32. >>> ['bikes:001', 'bikes:002', ..., 'bikes:011']
  33. descriptions = client.json().mget(keys, "$.description")
  34. descriptions = [item for sublist in descriptions for item in sublist]
  35. embedder = SentenceTransformer("msmarco-distilbert-base-v4")
  36. embeddings = embedder.encode(descriptions).astype(np.float32).tolist()
  37. VECTOR_DIMENSION = len(embeddings[0])
  38. >>> 768
  39. pipeline = client.pipeline()
  40. for key, embedding in zip(keys, embeddings):
  41.     pipeline.json().set(key, "$.description_embeddings", embedding)
  42. pipeline.execute()
  43. >>> [True, True, True, True, True, True, True, True, True, True, True]
  44. res = client.json().get("bikes:010")
  45. >>>
  46. # {
  47. #   "model""Summit",
  48. #   "brand""nHill",
  49. #   "price"1200,
  50. #   "type""Mountain Bike",
  51. #   "specs": {
  52. #     "material""alloy",
  53. #     "weight""11.3"
  54. #   },
  55. #   "description""This budget mountain bike from nHill performs well..."
  56. #   "description_embeddings": [
  57. #     -0.538114607334137,
  58. #     -0.49465855956077576,
  59. #     -0.025176964700222015,
  60. #     ...
  61. #   ]
  62. # }
  63. schema = (
  64.     TextField("$.model"no_stem=Trueas_name="model"),
  65.     TextField("$.brand"no_stem=Trueas_name="brand"),
  66.     NumericField("$.price"as_name="price"),
  67.     TagField("$.type"as_name="type"),
  68.     TextField("$.description"as_name="description"),
  69.     VectorField(
  70. "$.description_embeddings",
  71. "FLAT",
  72. {
  73. "TYPE": "FLOAT32",
  74. "DIM": VECTOR_DIMENSION,
  75. "DISTANCE_METRIC": "COSINE",
  76. },
  77.         as_name="vector",
  78. ),
  79. )
  80. definition = IndexDefinition(prefix=["bikes:"], index_type=IndexType.JSON)
  81. res = client.ft("idx:bikes_vss").create_index(
  82.     fields=schema, definition=definition
  83. )
  84. >>> 'OK'
  85. info = client.ft("idx:bikes_vss").info()
  86. num_docs = info["num_docs"]
  87. indexing_failures = info["hash_indexing_failures"]
  88. # print(f"{num_docs} documents indexed with {indexing_failures} failures")
  89. >>> 11 documents indexed with 0 failures
  90. query = Query("@brand:Peaknetic")
  91. res = client.ft("idx:bikes_vss").search(query).docs
  92. # print(res)
  93. >>> [Document {'id': 'bikes:008', 'payload': None, 'brand': 'Peaknetic', 'model': 'Soothe Electric bike', 'price': '1950', 'description_embeddings': ...
  94. query = Query("@brand:Peaknetic").return_fields("id", "brand", "model", "price")
  95. res = client.ft("idx:bikes_vss").search(query).docs
  96. # print(res)
  97. >>> [Document {'id': 'bikes:008', 'payload': None, 'brand': 'Peaknetic', 'model': 'Soothe Electric bike', 'price': '1950'}, Document {'id': 'bikes:009', 'payload': None, 'brand': 'Peaknetic', 'model': 'Secto', 'price': '430'}]
  98. query = Query("@brand:Peaknetic @price:[0 1000]").return_fields(
  99. "id", "brand", "model", "price"
  100. )
  101. res = client.ft("idx:bikes_vss").search(query).docs
  102. # print(res)
  103. >>> [Document {'id': 'bikes:009', 'payload': None, 'brand': 'Peaknetic', 'model': 'Secto', 'price': '430'}]
  104. queries = [
  105. "Bike for small kids",
  106. "Best Mountain bikes for kids",
  107. "Cheap Mountain bike for kids",
  108. "Female specific mountain bike",
  109. "Road bike for beginners",
  110. "Commuter bike for people over 60",
  111. "Comfortable commuter bike",
  112. "Good bike for college students",
  113. "Mountain bike for beginners",
  114. "Vintage bike",
  115. "Comfortable city bike",
  116. ]
  117. encoded_queries = embedder.encode(queries)
  118. len(encoded_queries)
  119. >>> 11
  120. def create_query_table(query, queries, encoded_queries, extra_params={}):
  121.     results_list = []
  122. for i, encoded_query in enumerate(encoded_queries):
  123.         result_docs = (
  124.             client.ft("idx:bikes_vss")
  125. .search(
  126.                 query,
  127. {
  128. "query_vector": np.array(
  129.                         encoded_query, dtype=np.float32
  130. ).tobytes()
  131. }
  132. | extra_params,
  133. )
  134. .docs
  135. )
  136. for doc in result_docs:
  137.             vector_score = round(1 - float(doc.vector_score), 2)
  138.             results_list.append(
  139. {
  140. "query": queries[i],
  141. "score": vector_score,
  142. "id": doc.id,
  143. "brand": doc.brand,
  144. "model": doc.model,
  145. "description": doc.description,
  146. }
  147. )
  148. Optional: convert the table to Markdown using Pandas
  149.     queries_table = pd.DataFrame(results_list)
  150.     queries_table.sort_values(
  151.         by=["query", "score"], ascending=[True, False], inplace=True
  152. )
  153.     queries_table["query"] = queries_table.groupby("query")["query"].transform(
  154. lambda x: [x.iloc[0]] + [""] * (len(x) - 1)
  155. )
  156.     queries_table["description"] = queries_table["description"].apply(
  157. lambda x: (x[:497] + "...") if len(x) > 500 else x
  158. )
  159.     queries_table.to_markdown(index=False)
  160. query = (
  161.     Query("(*)=>[KNN 3 @vector $query_vector AS vector_score]")
  162. .sort_by("vector_score")
  163. .return_fields("vector_score", "id", "brand", "model", "description")
  164. .dialect(2)
  165. )
  166. create_query_table(query, queries, encoded_queries)
  167. >>> | Best Mountain bikes for kids     |    0.54 | bikes:003... (+ 32 more results)
  168. hybrid_query = (
  169.     Query("(@brand:Peaknetic)=>[KNN 3 @vector $query_vector AS vector_score]")
  170. .sort_by("vector_score")
  171. .return_fields("vector_score", "id", "brand", "model", "description")
  172. .dialect(2)
  173. )
  174. create_query_table(hybrid_query, queries, encoded_queries)
  175. >>> | Best Mountain bikes for kids     |    0.3  | bikes:008... (+22 more results)
  176. range_query = (
  177.     Query(
  178. "@vector:[VECTOR_RANGE $range $query_vector]=>{$YIELD_DISTANCE_AS: vector_score}"
  179. )
  180. .sort_by("vector_score")
  181. .return_fields("vector_score", "id", "brand", "model", "description")
  182. .paging(0, 4)
  183. .dialect(2)
  184. )
  185. create_query_table(
  186.     range_query, queries[:1], encoded_queries[:1], {"range": 0.55}
  187. )
  188. >>> | Bike for small kids |    0.52 | bikes:001 | Velorim    |... (+1 more result)

关于字段的详细描述:

1)$.description_embeddings AS vector: 矢量字段的json路径和其别名;

2)FLAT:指定索引方法

3)TYPE FLOAT32:设置矢量分量的类型;

4)DIM 768:确定嵌入式方式的长度或者维度;

5)DISTANCE_METRIC COSINE: 距离函数:余弦;

Vectors | Docs

  • 检查索引状态

一旦执行FT.CREATE命令,索引过程就会在后台运行。在很短的时间内,所有JSON文档都应该被索引并准备好被查询。要验证这一点,可以使用FT.INFO命令,该命令提供有关索引的详细信息和统计信息。特别令人感兴趣的是成功索引的文档数量和失败的数量:

  1. info = client.ft("idx:bikes_vss").info()
  2. num_docs = info["num_docs"]
  3. indexing_failures = info["hash_indexing_failures"]
  4. # print(f"{num_docs} documents indexed with {indexing_failures} failures")
  5. >>> 11 documents indexed with 0 failures

二、搜索和查询;

  1. 嵌入提示
  1. queries = [
  2. "Bike for small kids",
  3. "Best Mountain bikes for kids",
  4. "Cheap Mountain bike for kids",
  5. "Female specific mountain bike",
  6. "Road bike for beginners",
  7. "Commuter bike for people over 60",
  8. "Comfortable commuter bike",
  9. "Good bike for college students",
  10. "Mountain bike for beginners",
  11. "Vintage bike",
  12. "Comfortable city bike",
  13. ]
  1. 首先需要使用相同的句子转换器模型对查询提示进行编码,那样:
  1. encoded_queries = embedder.encode(queries)
  2. len(encoded_queries)
  3. # >>> 11

三、执行K近邻查询

KNN是一种基础算法,旨在找到与给定输入最相似的项目。KNN算法基于所选择的距离函数来计算查询向量与数据库中每个向量之间的距离。然后,它返回到查询向量的距离最小的K个项目。这些是最相似的项目。

以下示例显示了一个不应用预过滤器的查询。预筛选表达式(*)表示全部,但您可以将其替换为按其他元数据进行筛选的查询表达式。

然后查询的KNN部分搜索三个最近的邻居。到查询向量的距离返回为vector_score。结果按此分数排序。最后,它返回结果集中的字段vector_score、id、$.brand、$.model和$.description。

  1. query = (
  2.     Query('(*)=>[KNN 3 @vector $query_vector AS vector_score]')
  3. .sort_by('vector_score')
  4. .return_fields('vector_score', 'id', 'brand', 'model', 'description')
  5. .dialect(2)
  6. )

必须将矢量化查询作为$query_vector作为字节数组传递。以下代码显示了从矢量化查询提示符(encoded_query)创建Python NumPy数组作为单精度浮点数组的示例,并将其转换为紧凑的字节级表示,该表示可以作为参数传递给查询:

client.ft(INDEX_NAME).search(query, { 'query_vector': np.array(encoded_query, dtype=np.float32).tobytes() }).docs

有了查询模板,就可以通过传递矢量化的查询提示来执行循环中的所有查询提示。请注意,脚本将每个结果的vector_score计算为1-doc.vector_score。因为使用余弦距离作为度量,所以距离最小的项目更接近,因此更类似于查询。

然后,在匹配的文档上循环,并创建一个结果列表,该列表可以转换为Pandas表以可视化结果:

  1. def create_query_table(query, queries, encoded_queries, extra_params={}):
  2.     results_list = []
  3. for i, encoded_query in enumerate(encoded_queries):
  4.         result_docs = (
  5.             client.ft("idx:bikes_vss")
  6. .search(
  7.                 query,
  8. {
  9. "query_vector": np.array(
  10.                         encoded_query, dtype=np.float32
  11. ).tobytes()
  12. }
  13. | extra_params,
  14. )
  15. .docs
  16. )
  17. for doc in result_docs:
  18.             vector_score = round(1 - float(doc.vector_score), 2)
  19.             results_list.append(
  20. {
  21. "query": queries[i],
  22. "score": vector_score,
  23. "id": doc.id,
  24. "brand": doc.brand,
  25. "model": doc.model,
  26. "description": doc.description,
  27. }
  28. )
  29. Optional: convert the table to Markdown using Pandas
  30.     queries_table = pd.DataFrame(results_list)
  31.     queries_table.sort_values(
  32.         by=["query", "score"], ascending=[True, False], inplace=True
  33. )
  34.     queries_table["query"] = queries_table.groupby("query")["query"].transform(
  35. lambda x: [x.iloc[0]] + [""] * (len(x) - 1)
  36. )
  37.     queries_table["description"] = queries_table["description"].apply(
  38. lambda x: (x[:497] + "...") if len(x) > 500 else x
  39. )
  40.     queries_table.to_markdown(index=False)

查询结果显示了各个查询的前三个匹配项(我们的K参数),以及每个查询的自行车id、品牌和型号。例如,对于查询“儿童最佳山地自行车”,相似度最高(0.54),因此最接近的匹配是“Nord”品牌的“Chook air 5”自行车型号,描述为:

Chook Air 5为六岁及以上的孩子们提供了一辆耐用且轻便的山地自行车,让他们第一次体验在赛道上的骑行,并轻松穿越森林和田野。顶部较低的管子使您在任何情况下都能轻松装卸,让您的孩子在小径上更安全。Chook Air 5是山地自行车的完美入门。

从描述来看,这辆自行车非常适合年幼的孩子,所使用的嵌入准确地捕捉到了描述的语义。

  1. query = (
  2.     Query("(*)=>[KNN 3 @vector $query_vector AS vector_score]")
  3. .sort_by("vector_score")
  4. .return_fields("vector_score", "id", "brand", "model", "description")
  5. .dialect(2)
  6. )
  7. create_query_table(query, queries, encoded_queries)
  8. >>> | Best Mountain bikes for kids     |    0.54 | bikes:003... (+ 32 more results)
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/木道寻08/article/detail/893515
推荐阅读
相关标签
  

闽ICP备14008679号