• 文章
  • 利用 Qdrant 的新 API 提供更出色的推荐
返回 Qdrant 文章

利用 Qdrant 的新 API 提供更出色的推荐

Kacper Łukawski

·

2023 年 10 月 25 日

Deliver Better Recommendations with Qdrant’s new API

向量搜索引擎(例如 Qdrant)最流行的用例是基于单个查询向量的语义搜索。给定查询,我们可以将其向量化(嵌入)并找到索引中最接近的点。但是搜索之外的向量相似性确实存在,推荐系统就是一个很好的例子。推荐可以看作是一种多目标搜索,我们希望找到接近正面例子而远离负面例子的物品。这种对向量数据库的使用有许多应用,包括电子商务、内容甚至是约会应用的推荐系统。

Qdrant 提供了推荐 API已有一段时间,随着最新版本 Qdrant 1.6 的发布,我们很高兴能为您提供更大的灵活性和对推荐 API 的控制。在这里,我们将讨论一些内部机制,并展示它们如何在实践中使用。

旧推荐 API 回顾

Qdrant 之前的推荐 API 带有一些限制。首先,它要求为正面和负面示例点传递向量 ID。如果您想直接使用向量嵌入,您必须要么在集合中创建一个新的点,要么通过使用搜索 API来模拟推荐 API 的行为。此外,在 Qdrant 之前的版本中,总是要求您提供至少一个正面示例。这一要求基于用于将多个样本组合成单个查询向量的算法。这是一种简单而有效的方法。但是,如果您只有用户不喜欢某些物品的信息,您就无法直接使用它。

Qdrant 1.6 带来了更灵活的 API。您现在可以提供正面和负面示例的 ID 和向量。您甚至可以在单个请求中将它们组合起来。这使得新实现向后兼容,因此您可以轻松升级现有的 Qdrant 实例而无需更改代码。而且 API 的默认行为仍然与之前相同。但是,我们扩展了 API,因此您现在可以选择查找推荐点的策略

POST /collections/{collection_name}/points/recommend
{
  "positive": [100, 231],
  "negative": [718, [0.2, 0.3, 0.4, 0.5]],
  "filter": {
        "must": [
            {
                "key": "city",
                "match": {
                    "value": "London"
                }
            }
        ]
  },
  "strategy": "average_vector",
  "limit": 3
}

请求中有两个关键变化。首先,我们可以调整搜索策略并将其设置为 average_vector(默认)或 best_score。此外,我们可以将 ID(718)和嵌入([0.2, 0.3, 0.4, 0.5])作为正面和负面示例传递。

HNSW ANN 示例和策略

让我们从一个示例开始,以帮助您理解HNSW 图。假设您想去另一个大陆的一个小城市旅行

  1. 您从家乡出发,乘坐巴士前往当地机场。
  2. 然后,乘坐航班前往最近的枢纽之一。
  3. 从那里,您必须乘坐另一班航班前往目的地大陆的枢纽。
  4. 希望最后一班航班能飞到您的目的地城市。
  5. 您还需要乘坐当地交通工具前往您的最终地址。

这段旅程类似于 HNSW 图在 Qdrant 近似最近邻搜索中的使用。

Transport network

HNSW 是一个由向量(嵌入)组成的多层图,连接基于向量的接近度。顶层点最少,这些点之间的距离最大。我们向下层走得越深,点越多,距离越近。图的构建方式是,点在每一层都连接到它们最近的邻居。

来自特定层的所有点也都在下层,因此可以在同一位置切换搜索层。在交通网络的情况下,顶层将是航空枢纽,连接良好但机场之间距离较远。当地机场以及铁路和巴士,密度更高,距离更小,构成了中间层。最后,底层由当地交通工具组成,这是最密集的,点之间的距离最小。

您旅行时不必检查所有可能的连接。您选择一次跨洲航班,然后是当地航班,最后是巴士或出租车。所有决定都基于点之间的距离。

HNSW 中的搜索过程也基于类似地遍历图。从顶层的入口点开始,找到其最近点,然后将该点用作进入下一密集层的入口点。此过程重复直到到达底层。访问过的点和到原始查询向量的距离都保存在内存中。如果当前点的任何邻居都不比最佳匹配好,我们可以停止遍历,因为这是一个局部最小值。我们从最大的尺度开始,然后逐渐缩小。

在这个过于简化的示例中,我们假设点之间的距离是唯一重要的因素。实际上,我们可能希望考虑其他标准,例如机票价格,或由于某些限制而避免某些特定位置。这意味着,选择最佳匹配有多种策略,这在向量推荐的情况下也同样适用。我们可以通过改变在遍历过程中计算候选点分数的方式来使用不同的方法来确定遍历 HNSW 图的路径。默认行为基于纯粹的距离,但 Qdrant 1.6 为推荐 API 公开了两种策略。

平均向量 (Average vector)

默认策略称为 average_vector,它是之前的策略,基于正面和负面示例的平均值。它简化了推荐过程,并将其转换为单向量搜索。它支持点 ID 和向量作为参数。例如,您可以根据与现有点的过去交互与查询向量嵌入相结合来获得推荐。在内部,该机制基于正面和负面示例的平均值,并使用以下公式计算:

$$ \text{平均向量} = \text{avg}(\text{正面向量}) + \left( \text{avg}(\text{正面向量}) - \text{avg}(\text{负面向量}) \right) $$

average_vector 将推荐问题转换为单向量搜索。

新亮点 - 最佳得分 (Best score)

新策略称为 best_score。它不依赖于平均值,并且更灵活。它允许您只传递负面样本,并在底层使用稍微更复杂的算法。

在 HNSW 图遍历的每一步都选择最佳得分。我们分别计算遍历点与每个正面和负面示例之间的距离。在使用最佳得分策略的情况下,不再只有一个查询向量,而是一堆正面和负面查询。因此,对于查询中的每个样本,我们都有一组距离,每个样本一个。在下一步,我们简单地取正面和负面的最佳得分,创建两个独立的值。最佳得分仅仅是查询到正面和负面的最近距离。其思想是:如果一个点更接近任何负面示例而不是任何正面示例,我们就不想要它。我们对接近负面示例进行惩罚,因此我们不直接使用相似度值,而是检查它是否更接近正面或负面示例。使用以下公式计算遍历的潜在点的得分:

if best_positive_score > best_negative_score {
    score = best_positive_score
} else {
    score = -(best_negative_score * best_negative_score)
}

如果该点更接近负面示例,我们通过取最佳负面得分的负平方值来惩罚它。对于更接近的负面示例,候选点的得分将始终低于或等于零,从而显著降低选择该点的几率。但是,如果最佳负面得分高于最佳正面得分,我们仍然偏爱那些离负面示例更远的点。这个过程有效地将遍历过程拉离负面示例

如果您想了解更多关于 HNSW 内部机制的信息,您可以查看关于可过滤 HNSW 的文章,该文章全面涵盖了该主题。

美食发现演示

我们的美食发现演示是基于新推荐 API构建的应用。它允许您根据喜欢的和不喜欢的照片查找餐食。新 Qdrant 版本带来了一些更新,包括:

  • 能够在推荐请求中包含多个文本查询。以前,我们只允许传递单个查询来解决冷启动问题。现在,您可以传递多个查询,并将它们与喜欢/不喜欢的照片混合使用。这得益于参数的新灵活性。我们可以在同一请求中同时传递点 ID 和嵌入向量,而用户查询显然不是集合的一部分。
  • 在推荐策略之间切换。您现在可以在 average_vectorbest_score 评分算法之间进行选择。

策略之间的差异

美食发现演示的 UI 允许您在策略之间切换。best_score 是默认策略,但只需一个开关,您就可以看到使用之前的 average_vector 策略时结果有何不同。

如果您只选择一个正面示例,两种算法的工作方式完全相同。

一个正面示例

差异只有在您开始添加更多示例时才会显现出来,尤其是在您选择一些负面示例时。

一个正面和一个负面示例

我们添加的喜欢和不喜欢的示例越多,best_score 策略的结果就越多样化。在旧策略中,只有一个向量,所以所有示例都与它相似。新策略单独考虑所有示例,使多样性更丰富。

多个正面和负面示例

选择正确的策略取决于数据集,并且嵌入在这里起着重要作用。因此,在特定情况下,尝试这两种策略并比较结果总是值得的。

仅处理负面示例

在我们的美食发现演示中,仅传递负面图片可以作为异常检测机制。虽然数据集应该只包含美食照片,但这实际上并不正确。找到这些异常值的一个简单方法是将食物照片作为负面示例传递,从而导致结果是那些“最不像”食物的图片。在我们的例子中,您会看到药瓶和书籍。

average_vector 策略仍然要求提供至少一个正面示例!然而,由于演示中使用的集合设置了余弦距离,我们使用上一篇文章中描述的一个技巧进行了模拟。简而言之,如果您只传递负面示例,它们的向量将被平均,并且求反的结果向量将用作搜索端点的查询。

仅负面示例

尽管如此,两种方法仍然返回不同的结果,因此它们各有其用武之地,具体取决于提出的问题和使用的数据集。

多模态的挑战

美食发现使用了 CLIP 嵌入模型,它是多模态的,允许将图像和文本编码到同一个向量空间中。使用这个模型可以进行图像查询、文本查询或两者结合。我们在更新后的演示中利用了这一机制,允许您传递文本查询以进一步过滤结果。

单个文本查询

文本查询可以与喜欢和不喜欢的照片混合使用,因此您可以在一个请求中将它们结合起来。但是,如果您开始添加负面示例,使用新策略获得的结果可能会让您感到惊讶。

带有负面示例的单个文本查询

这是与嵌入本身相关的问题。我们的数据集包含大量图像嵌入,它们彼此非常接近。另一方面,我们的文本查询与大多数图像嵌入相距甚远,但与其中一些相对接近,因此文本到图像搜索似乎工作良好。当所有查询项目来自同一领域(例如仅文本)时,一切正常。但是,如果我们将正面文本和负面图像嵌入混合,best_score 的结果会被负面样本淹没,因为它们只是更接近数据集的嵌入。如果您遇到此类问题,average_vector 策略可能是更好的选择。

查看演示

美食发现演示在线可用,您可以测试并查看差异。这是一个开源项目,因此您可以轻松地自行部署。源代码可在 GitHub 仓库中找到,README 描述了设置过程。由于计算嵌入需要一些时间,我们预先计算了它们并将它们导出为一个快照,可以轻松导入到任何 Qdrant 实例中。Qdrant Cloud 是最简单的入门方式

本页是否有用?

感谢您的反馈!🙏

听到这个消息我们很抱歉。😔 您可以在 GitHub 上编辑此页面,或创建一个 GitHub issue。