稠密向量搜索
重要概念
稠密向量表示
传统的标记化 倒排索引 可以被认为是将文本建模为“稀疏”向量,其中语料库中的每个术语对应一个向量维度。在这种模型中,维度的数量通常很高(对应于术语字典的基数),并且任何给定文档的向量主要包含零(因此它是稀疏的,因为在任何给定文档中只会存在整体索引中存在的一小部分术语)。
稠密向量表示与基于术语的稀疏向量表示形成对比,后者将近似语义含义提取到固定(且有限)数量的维度中。
这种方法中的维度数量通常远低于稀疏情况,并且任何给定文档的向量都是稠密的,因为其大部分维度都填充了非零值。
与稀疏方法(使用分词器直接从文本输入生成稀疏向量)相比,生成向量的任务必须在 Apache Solr 外部的应用程序逻辑中处理。
可能存在直接搜索本身就作为向量存在的数据(例如,科学数据)的情况;但在文本搜索上下文中,用户很可能会利用 BERT 等深度学习模型将文本信息编码为稠密向量,在索引和查询时显式地向 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 个向量。
接受的值:
euclidean
、dot_product
或cosine
。
此相似度旨在作为执行余弦相似度的优化方法。为了使用它,所有向量(包括文档和查询向量)都必须是单位长度。将点积与非单位长度的向量一起使用可能会导致错误或搜索结果不佳。 |
-
cosine
:余弦相似度
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
(高级)指定稠密向量元素的底层编码。这会影响索引字段和存储字段(如果启用)的内存/磁盘占用。
接受的值:
FLOAT32
、BYTE
。 hnswMaxConnections
-
可选
默认值:
16
(高级)此参数特定于
hnsw
knn 算法。控制有多少最近邻候选项连接到新节点。
它与 2018 年论文中的
M
具有相同的含义。接受的值:任何整数。
hnswBeamWidth
-
可选
默认值:
100
(高级)此参数特定于
hnsw
knn 算法。这是在搜索图以查找每个新插入节点时要跟踪的最近邻候选项的数量。
它与 2018 年论文中的
efConstruction
具有相同的含义。接受的值:任何整数。
DenseVectorField
支持以下属性:indexed
、stored
。
目前不支持多值。 |
以下是如何索引 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
-
可选
默认值:无
表示只有具有指定
tag
的fq
过滤器才应考虑用于隐式预过滤。不得与preFilter
组合使用。 excludeTags
-
可选
默认值:无
表示应将具有指定
tag
的fq
过滤器排除在隐式预过滤的考虑范围之外。不得与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
隐式预过滤
虽然可以在 knn
或 vectorSimilarity
查询解析器的任何用法上显式指定 preFilter
参数,但默认的预过滤行为(当未指定 preFilter
参数时)将根据查询解析器的使用方式而有所不同。
-
当用作主
q
参数时:请求中的fq
过滤器(不是 Solr 后过滤器)将被组合以形成隐式图预过滤器。-
此默认行为优化了考虑的向量距离计算的数量,消除了最终会被
fq
过滤器排除的文档。 -
includeTags
和excludeTags
可用于限制预过滤器中使用的fq
过滤器的集合。
-
-
当向量搜索查询解析器用作
fq
参数或较大查询中的子查询子句时:不使用隐式预过滤器。-
在这些情况下,不得使用
includeTags
和excludeTags
。
-
以下示例请求显示了向量查询解析器的两种用法,它们不会从任何 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]
在重排序中使用 只有当来自第一遍的文档 这意味着第二遍 最终排名结果列表会将第一遍分数(主查询 有关使用重排序查询解析器的详细信息,请参见 查询重排序 部分。 |