稠密向量搜索

Solr 的稠密向量搜索增加了对索引和搜索稠密数值向量的支持。

深度学习可用于生成信息语料库中查询和文档的向量表示。

这些基于神经网络的技术通常被称为神经搜索,是学术领域 神经信息检索 的行业衍生。

重要概念

稠密向量表示

传统的标记化 倒排索引 可以被认为是将文本建模为“稀疏”向量,其中语料库中的每个术语对应一个向量维度。在这种模型中,维度的数量通常很高(对应于术语字典的基数),并且任何给定文档的向量主要包含零(因此它是稀疏的,因为在任何给定文档中只会存在整体索引中存在的一小部分术语)。

稠密向量表示与基于术语的稀疏向量表示形成对比,后者将近似语义含义提取到固定(且有限)数量的维度中。

这种方法中的维度数量通常远低于稀疏情况,并且任何给定文档的向量都是稠密的,因为其大部分维度都填充了非零值。

与稀疏方法(使用分词器直接从文本输入生成稀疏向量)相比,生成向量的任务必须在 Apache Solr 外部的应用程序逻辑中处理。

可能存在直接搜索本身就作为向量存在的数据(例如,科学数据)的情况;但在文本搜索上下文中,用户很可能会利用 BERT 等深度学习模型将文本信息编码为稠密向量,在索引和查询时显式地向 Apache Solr 提供结果向量。

有关更多信息,您可以参考这篇博客文章

稠密检索

给定一个模拟信息需求的稠密向量 v,提供稠密向量检索的最简单方法是计算 v 与表示信息语料库中文档的每个向量 d 之间的距离(欧几里得距离、点积等)。

这种方法非常昂贵,因此许多近似策略目前正在积极研究中。

Apache Lucene 中实现并由 Apache Solr 使用的策略基于可导航的小世界图。

它为高维向量提供了高效的近似最近邻搜索。

索引时间

这是一个旨在支持稠密向量搜索的 Apache Solr 字段类型。

DenseVectorField

稠密向量字段允许索引和搜索浮点元素构成的稠密向量。

例如:

[1.0, 2.5, 3.7, 4.1]

以下是在 schema 中配置 DenseVectorField 的方法:

<fieldType name="knn_vector" class="solr.DenseVectorField" vectorDimension="4" similarityFunction="cosine"/>
<field name="vector" type="knn_vector" indexed="true" stored="true"/>
vectorDimension

必需

默认值:无

要传入的稠密向量的维度。

接受的值:任何整数。

similarityFunction

可选

默认值:euclidean

向量相似度函数;用于搜索以返回与目标向量最相似的 K 个向量。

接受的值:euclideandot_productcosine

此相似度旨在作为执行余弦相似度的优化方法。为了使用它,所有向量(包括文档和查询向量)都必须是单位长度。将点积与非单位长度的向量一起使用可能会导致错误或搜索结果不佳。
Solr 返回的余弦相似度得分按以下方式标准化:(1 + cosine_similarity) / 2
执行余弦相似度的首选方法是将所有向量归一化为单位长度,然后使用 DOT_PRODUCT。只有在需要保留原始向量并且无法提前将其归一化时,才应使用此函数。

要使用以下自定义编解码器格式和 HNSW 算法超参数的高级参数,请确保正在使用 Schema Codec Factory

以下是如何使用高级超参数配置 DenseVectorField

<fieldType name="knn_vector" class="solr.DenseVectorField" vectorDimension="4" similarityFunction="cosine" knnAlgorithm="hnsw" hnswMaxConnections="10" hnswBeamWidth="40"/>
<field name="vector" type="knn_vector" indexed="true" stored="true"/>
knnAlgorithm

可选

默认值:hnsw

(高级)指定要使用的底层 knn 算法。

接受的值:hnsw

请注意,knnAlgorithm 接受的值可能会在将来的版本中更改。

vectorEncoding

可选

默认值:FLOAT32

(高级)指定稠密向量元素的底层编码。这会影响索引字段和存储字段(如果启用)的内存/磁盘占用。

接受的值:FLOAT32BYTE

hnswMaxConnections

可选

默认值:16

(高级)此参数特定于 hnsw knn 算法。

控制有多少最近邻候选项连接到新节点。

它与 2018 年论文中的 M 具有相同的含义。

接受的值:任何整数。

hnswBeamWidth

可选

默认值:100

(高级)此参数特定于 hnsw knn 算法。

这是在搜索图以查找每个新插入节点时要跟踪的最近邻候选项的数量。

它与 2018 年论文中的 efConstruction 具有相同的含义。

接受的值:任何整数。

DenseVectorField 支持以下属性:indexedstored

目前不支持多值。

以下是如何索引 DenseVectorField

  • JSON

  • XML

  • SolrJ

[{ "id": "1",
"vector": [1.0, 2.5, 3.7, 4.1]
},
{ "id": "2",
"vector": [1.5, 5.5, 6.7, 65.1]
}
]
<add>
<doc>
<field name="id">1</field>
<field name="vector">1.0</field>
<field name="vector">2.5</field>
<field name="vector">3.7</field>
<field name="vector">4.1</field>
</doc>
<doc>
<field name="id">2</field>
<field name="vector">1.5</field>
<field name="vector">5.5</field>
<field name="vector">6.7</field>
<field name="vector">65.1</field>
</doc>
</add>
final SolrClient client = getSolrClient();

final SolrInputDocument d1 = new SolrInputDocument();
d1.setField("id", "1");
d1.setField("vector", Arrays.asList(1.0f, 2.5f, 3.7f, 4.1f));


final SolrInputDocument d2 = new SolrInputDocument();
d2.setField("id", "2");
d2.setField("vector", Arrays.asList(1.5f, 5.5f, 6.7f, 65.1f));

client.add(Arrays.asList(d1, d2));

查询时间

Apache Solr 提供了两个与稠密向量字段一起使用的查询解析器,每个解析器都支持基于向量相似度的不同文档匹配方式:knn 查询解析器和 vectorSimilarity 查询解析器。

两个解析器都会为检索到的文档返回分数,这些分数是到目标向量的近似距离(由索引时配置的 similarityFunction 定义),并且都支持“预过滤”文档图,以减少评估的候选向量的数量(而无需计算其向量相似度距离)。

两个查询解析器的通用参数为:

f

必需

默认值:无

要在其中搜索的 DenseVectorField

preFilter

可选

默认值:取决于用法,请参见下文。

指定要使用的预过滤器查询字符串的显式列表。

includeTags

可选

默认值:无

表示只有具有指定 tagfq 过滤器才应考虑用于隐式预过滤。不得与 preFilter 组合使用。

excludeTags

可选

默认值:无

表示应将具有指定 tagfq 过滤器排除在隐式预过滤的考虑范围之外。不得与 preFilter 组合使用。

knn 查询解析器

knn k-最近邻查询解析器将 k 个最近的文档与目标向量匹配。

除了上述通用参数外,它还接受以下参数:

topK

可选

默认值:10

要返回的 k 个最近结果的数量。

以下是一个简单的 knn 搜索示例:

?q={!knn f=vector topK=10}[1.0, 2.0, 3.0, 4.0]

检索到的搜索结果是与输入向量 [1.0, 2.0, 3.0, 4.0] 最接近的 k=10 个文档,按索引时配置的 similarityFunction 排序。

vectorSimilarity 查询解析器

vectorSimilarity 向量相似度查询解析器匹配与目标向量的相似度高于最小阈值的文档。

除了上述通用参数外,它还接受以下参数:

minReturn

必需

默认值:无

图中要作为匹配项返回的节点的最小相似度阈值。

minTraverse

可选

默认值:-无穷大

图中要继续遍历其邻居的节点的最小相似度。

以下是一个简单的 vectorSimilarity 搜索示例:

?q={!vectorSimilarity f=vector minReturn=0.7}[1.0, 2.0, 3.0, 4.0]

检索到的搜索结果是所有与输入向量 [1.0, 2.0, 3.0, 4.0] 的相似度基于索引时配置的 similarityFunction 至少为 0.7 的文档。

图预过滤

在遍历图时考虑的候选文档集上的预过滤可以根据何时以及如何使用这些稠密向量查询解析器显式或隐式地指定(基于现有的 fq 参数)。

显式预过滤

可以显式指定 preFilter 参数以减少为距离计算评估的候选文档的数量。

?q={!vectorSimilarity f=vector minReturn=0.7 preFilter=inStock:true}[1.0, 2.0, 3.0, 4.0]

在上面的示例中,只有与预过滤器 inStock:true 匹配的文档才会是在评估针对指定向量的 vectorSimilarity 搜索时的候选文档。

preFilter 参数可以为空(例如:preFilter="")以指示不应执行预过滤;或者它可以是多值的——通过重复或通过重复的 参数引用

以下两个示例是等效的:

?q={!knn f=vector topK=10 preFilter=category:AAA preFilter=inStock:true}[1.0, 2.0, 3.0, 4.0]
?q={!knn f=vector topK=10 preFilter=$knnPreFilter}[1.0, 2.0, 3.0, 4.0]
&knnPreFilter=category:AAA
&knnPreFilter=inStock:true

隐式预过滤

虽然可以在 knnvectorSimilarity 查询解析器的任何用法上显式指定 preFilter 参数,但默认的预过滤行为(当未指定 preFilter 参数时)将根据查询解析器的使用方式而有所不同。

  • 当用作主 q 参数时:请求中的 fq 过滤器(不是 Solr 后过滤器)将被组合以形成隐式图预过滤器。

    • 此默认行为优化了考虑的向量距离计算的数量,消除了最终会被 fq 过滤器排除的文档。

    • includeTagsexcludeTags 可用于限制预过滤器中使用的 fq 过滤器的集合。

  • 当向量搜索查询解析器用作 fq 参数或较大查询中的子查询子句时:不使用隐式预过滤器。

    • 在这些情况下,不得使用 includeTagsexcludeTags

以下示例请求显示了向量查询解析器的两种用法,它们不会从任何 fq 参数获得任何隐式预过滤,因为这两种用法都不是作为主 q 参数。

?q=(color_str:red OR {!vectorSimilarity f=color_vector minReturn=0.7 v="[1.0, 2.0, 3.0, 4.0]"})
&fq={!knn f=title_vector topK=10}[9.0, 8.0, 7.0, 6.0]
&fq=inStock:true

但是,下一个示例显示了一个基本请求,其中所有 fq 参数都将用作主 knn 查询上的隐式预过滤器。

?q={!knn f=vector topK=10}[1.0, 2.0, 3.0, 4.0]
&fq=category:AAA
&fq=inStock:true

如果我们修改上述请求以将标签添加到 fq 参数,则可以在 knn 解析器上指定 includeTags 选项,以限制用于预过滤的 fq 过滤器。

?q={!knn f=vector topK=10 includeTags=for_knn}[1.0, 2.0, 3.0, 4.0]
&fq=category:AAA
&fq={!tag=for_knn}inStock:true

在此示例中,只有 inStock:true 过滤器将用于预过滤以找到 topK=10 个文档,并且 category:AAA 过滤器将独立应用;可能导致总匹配数少于 10 个。

以下是一些 includeTags 和/或 excludeTags 可能比显式 preFilter 参数更有用的用例:

  • 您有一些 在许多请求中重复使用fq 参数(即使您不使用搜索稠密向量字段),您希望在您确实搜索稠密向量字段时将其用作预过滤器。

  • 您通常希望将所有 fq 参数用作 knn 查询上的图预过滤器,但是当用户“向下钻取”到方面时,您希望将添加的 fq 参数排除在预过滤之外,以便结果集变小;而不是只计算一个新的 topK 集。

在重排序查询中的用法

两个稠密向量搜索查询解析器都可以用于重新排序第一遍查询结果。

&q=id:(3 4 9 2)&rq={!rerank reRankQuery=$rqq reRankDocs=4 reRankWeight=1}&rqq={!knn f=vector topK=10}[1.0, 2.0, 3.0, 4.0]

在重排序中使用 knn 时,请注意 topK 参数。

只有当来自第一遍的文档 d 在要搜索的目标向量的 k 个最近邻居(在整个索引中)中时,才会计算第二遍分数(从 knn 派生)。

这意味着第二遍 knn 无论如何都会在整个索引上执行,这是一个当前的限制。

最终排名结果列表会将第一遍分数(主查询 q)与第二遍分数(到要搜索的目标向量的近似 similarityFunction 距离)乘以乘法因子(reRankWeight)相加。

有关使用重排序查询解析器的详细信息,请参见 查询重排序 部分。