dot 快速的未来即将在您所在的城市举办的活动中到来。

加入我们参加 Redis 发布会

.NET 向量搜索简化

Redis OM .NET 的向量搜索和语义缓存功能的演练

Redis OM .NET 现在支持 Redis 向量搜索,并与使用 OpenAI、Azure OpenAI、Hugging Face 和 ML.NET 的嵌入生成 API 集成。

向量数据库 技术正在重塑我们对数据的思考方式。文本段落、图像和音频都可以被编码和索引为向量,以便可以进行语义搜索。我们正在快速从一个世界走向另一个世界,在这个世界中,数据的机器可读语义在其内容而非结构中。

然而,向量数据库 可能不直观。您需要了解如何正确构建向量索引,如何将非结构化数据转换为可用的向量,如何将该数据添加到数据源,以及如何构建查询。实际上,这意味着从非结构化数据创建向量表示,然后使用这些表示来查询存储的向量。因此,这并不完全是传统的 SELECT/FROM/WHERE。

两年前,我们发布了 Redis OM 库,以便在 Redis 的搜索和查询功能之上构建一个直观的抽象。我们现在正在扩展该抽象,使其更容易为您的 AI 驱动的应用程序执行向量搜索。让我们浏览一下这些针对 Redis OM .NET 的新增强功能,以启用 Redis 向量搜索

Redis OM .NET API 的最新添加使存储和查询向量数据变得更加容易,即使您以前从未使用过向量。事实上,Redis OM .NET 现在包括用于向量搜索和语义缓存的直观接口。具体来说,我们已经构建了

  1. Redis 向量索引、数据建模和查询功能
  2. 一套易于使用的向量化器(也称为嵌入生成器),这些向量化器与 OpenAI、Azure OpenAI、Hugging Face 和 ML.NET 框架集成
  3. 一个语义缓存实现,它与任何向量化器配合使用,并减少昂贵的 LLM 调用

所有这些部分一起将一个可能难以理解和实施的过程压缩为几行简单的代码。

建模和索引创建

假设我们有一个包含一系列商品的产品目录,并且我们希望能够查找具有相似库存图像或相似商品描述的商品。我们还希望能够根据一系列方面进一步细化我们的查询。现在我们只需要这个模型

[Document(StorageType = StorageType.Json)]
public class Product
{
    [RedisIdField] [Indexed] public int Id { get; set; }

    [Indexed(Algorithm = VectorAlgorithm.HNSW, DistanceMetric = DistanceMetric.COSINE)] 
    [ImageVectorizer]
    public Vector<string> ImageUrl { get; set; }    

    [Indexed(Algorithm = VectorAlgorithm.FLAT, DistanceMetric = DistanceMetric.COSINE)]
    [SentenceVectorizer] 
    public Vector<string> ProductDisplayName { get; set; }
    
    public VectorScores? Scores { get; set; }

    [Indexed] public string Category { get; set; }

    [Indexed] public string ArticleType { get; set; }

    [Indexed] public string BaseColor { get; set; }

    [Indexed] public string Season { get; set; }

    // other facets 
}

定义完模型后,我们可以通过调用 CreateIndex 方法在 Redis 中创建所需的索引

var provider = new RedisConnectionProvider("redis://localhost:6379");
provider.Connection.CreateIndex(typeof(Product))

插入

创建完索引后,我们可以继续将商品插入到 Redis 数据库中

var collection = provider.RedisCollection<Product>();
var entry = new Product { 
    Id = id, 
    ImageUrl = Vector.Of(imageUrl), 
    ProductDisplayName = Vector.Of(productDisplayName),
    Category = category, 
    ArticleType = articleType, 
    BaseColor = baseColour, 
    Season = season
    // etc…
};
await collection.InsertAsync(entry);

请注意,从模型中我们定义了两个字段为向量:ImageUrl 和 ProductDisplayName。当我们要写入条目时,Redis OM 将使用提供的向量化器为您创建这些字段的向量表示。在本例中,我们使用的是 Redis.OM.Vectorizers.ResNet18 包中的 ImageVectorizer 和 Redis.OM.Vectorizers.AllMiniLML6V2 包中的句子 Vectorizer。

这些 Vectorizer 是 **VectorizerAttribute<T>** 的实现。这些属性告诉 Redis OM 使用哪个 **IVectorizer<T>**,以及生成的向量的类型和形状。在幕后,这两个向量化器都使用 ML.NET 来创建向量。**VectorizerAttribute<T>** 和 **IVectorizer<T>** 类型允许您抽象化创建向量的逻辑,使您的向量化器选择可扩展和可定制。此外,Redis OM .NET 包含用于直接的双精度和浮点数组的向量化器,以及用于 OpenAI、Azure OpenAI 和 Hugging Face 的向量化器。要使用 OpenAI、Azure OpenAI 或 Hugging Face 进行向量生成,您只需提供一个 API 密钥,在某些情况下还需要提供模型 ID 或资源名称。

查询

定义完模型、创建完索引并插入数据后,我们可以发出向量查询。为此,我们使用 **IRedisColleciton.NearestNeighbors** 和 **Vector<T>.VectorRange** 方法,并使用与任何其他 LINQ 查询一起使用的布尔表达式。

最近邻居

每个查询都支持一次最近邻居搜索。是 k 近邻搜索还是近似最近邻搜索由您在模型中的 IndexAttribute 中声明的算法决定。要运行最近邻居搜索,只需在 RedisCollection 上调用 NearestNeighbor

var response = collection.NearestNeighbors(x => x.ImageUrl, 15, url).ToList();

向量范围

要执行向量范围搜索(过滤在您的向量一定距离内的向量),请在表达式内的向量字段上使用 VectorRange 方法

var item = collection.First(x => x.ImageUrl.VectorRange(url, .15, "distance"));

混合查询

混合查询将传统的搜索过滤器与向量搜索相结合。要在 Redis OM .NET 中执行此操作,请将您通常的布尔表达式与向量范围和/或最近邻居查询结合起来。下面的查询检索秋季所有服装产品,并且其图像与提供的图像的距离在 .15 以内,选择最近邻居。

var item = collection
    .NearestNeighbors(x => x.ImageUrl, 1, url)
    .First(x=>x.ImageUrl.VectorRange(url, .15, "distance") && x.Season == "Fall" && x.Category == "Apparel")

在本例中,Redis OM .NET 会弄清楚如何构建查询,然后发出查询。

语义缓存

语义缓存是一种新的缓存技术,它得益于嵌入生成和向量搜索的最新进展。传统上,我们认为 Redis 缓存是将一些精确的值存储在一些精确的键中。您需要整个键才能恢复缓存的项目。当您存储诸如分布式会话状态或昂贵 API 调用或查询的结果之类的内容时,这当然很有意义。

但是,当您缓存来自 LLM 的提示/响应对时,需要精确的键值匹配会导致问题。这是因为语义缓存允许您按含义(即语义)存储和检索提示,而不仅仅是按键/值对。这意味着两个相关的查询(例如,“世界上最高的建筑是什么?”和“世界上最高的建筑是什么?”)具有足够接近的含义,因此当存储任一问题时,它们都应该返回相同的 LLM 响应。在 Redis OM .NET 中,语义缓存使用与向量搜索相同的向量化器。要使用语义缓存,您可以直接初始化 SemanticCache,也可以使用 Redis.OM.Vectorizers 包提供的其中一个

var provider = new RedisConnectionProvider("redis://localhost:6379");
var cache = provider.OpenAISemanticCache(apiKey, threshold: .15, ttl: 3600000);
cache.Store("What is the capital of France?", "Paris");
var res = cache.GetSimilar("What really is the capital of France?").First();

在本例中,我们使用 OpenAI REST API 来构建我们的向量。要使此功能正常工作,只需一个 OpenAI API 密钥。我们已将语义缓存配置为将项目存储一小时。当我们检索项目时,我们将获得距离小于 .15 的项目(余弦相似度的距离在 0 到 1 之间归一化)。

自定义向量生成

如果 Redis OM .NET 未能提供您项目所需的 Vectorizer,则可以实现您自己的 Vectorizer。您只需要一个 **IVectorizer<T>** 来执行实际的向量化,并且您需要一个 **VectorizerAttribute<T>** 来装饰模型中的 **Vector<T>** 字段。**VectorizerAttribute<T>** 负责告诉 Redis OM .NET 如何构建索引,以及如何在插入或查询时创建向量。

结论

向量数据库正在迅速普及。借助 Redis OM .NET,您不再需要成为将数据转换为向量的专家才能使用向量数据库。Redis OM .NET 的新向量化器、向量搜索和语义缓存功能消除了构建向量索引、将数据转换为向量和构建向量查询的麻烦。换句话说,它是一个直观且强大的工具,可以更轻松地使用 Redis 的闪电般快速的向量搜索功能。

相关资源