本页面介绍了如何在 Spanner 中执行全文搜索和向量混合搜索。混合搜索将关键字匹配(全文搜索,FTS)的精确性与语义匹配(向量搜索)的召回率相结合,以生成高度相关的搜索结果。
Spanner 支持以下混合搜索模式,这些模式分为三大类:
| 类别 | 说明 | 主要目标 |
| 融合 | Fusion 使用关键字搜索和向量搜索独立检索文档并对其进行排名,然后合并(融合)结果。 | 通过结合多种评分信号,最大限度地提高召回率和相关性。 |
| 已过滤 | 关键字用于过滤或缩小搜索范围。 | 确保关键字匹配是一项要求,同时利用语义相关性。 |
| 机器学习重新排名 | 机器学习模型会优化初始候选结果集,以获得更精确的最终排名。 | 针对少量最终结果集实现尽可能高的精确度。 |
融合搜索
融合搜索是指在同一数据语料库中分别运行 FTS 和向量搜索。然后,它会合并结果,以创建一个统一的、高度相关的排名列表。
虽然您可以在客户端执行独立查询,但 Spanner 中的混合搜索具有以下优势:
- 通过在服务器上管理并行请求和结果合并来简化应用逻辑。
- 避免将可能较大的中间结果集传输到客户端。
您可以使用 SQL 在 Spanner 中创建融合方法。本部分提供了倒数排序融合和相对得分融合的示例。不过,我们建议您评估不同的融合策略,以确定哪种策略最符合应用的要求。
基于排名的融合
如果来自不同检索方法(例如 FTS 得分和向量距离)的相关性得分难以比较或归一化(因为它们是在不同的空间中衡量的),请使用基于排名的融合。此方法使用每个检索器中每个文档的排名位置来生成最终得分和排名。
倒数排序融合 (RRF) 是一种基于排名的融合函数。文档的 RRF 得分是其在多个检索器中的排名的倒数之和,计算方式如下:
\[ score\ (d\epsilon D)\ =\ \sum\limits_{r\epsilon R}^{}(1\ /\ (\ 𝑘\ +\ ran{k}_{r}\ (𝑑)\ )\ )\ \]
其中:
- d 是文档。
- R 是检索器(FTS 和向量搜索)的集合。
- k 是一个常数(通常设置为 60),用于缓和排名非常高的文档的影响。
- w 是检索器 r 中文档的排名。
在 Spanner 查询中实现 RRF
向量指标(例如 APPROX_DOT_PRODUCT)和文本搜索得分(例如 SCORE_NGRAMS)采用的比例不兼容。为了解决这个问题,以下示例实现了 RRF,以根据排名位置(而非原始得分)对数据进行归一化处理。
该查询使用 UNNEST(ARRAY(...) WITH OFFSET 为每种方法的前 100 名候选者分配排名。然后,它使用这些排名的倒数计算标准化得分,并汇总结果以返回前 5 个匹配项。
SELECT SUM(1 / (60 + rank)) AS rrf_score, key
FROM (
(
SELECT rank, x AS key
FROM UNNEST(ARRAY(
SELECT key
FROM hybrid_search
WHERE embedding IS NOT NULL
ORDER BY APPROX_DOT_PRODUCT(@vector, embedding,
OPTIONS => JSON '{"num_leaves_to_search": 50}') DESC
LIMIT 100)) AS x WITH OFFSET AS rank
)
UNION ALL
(
SELECT rank, x AS key
FROM UNNEST(ARRAY(
SELECT key
FROM hybrid_search
WHERE SEARCH_NGRAMS(text_tokens_ngrams, 'foo')
ORDER BY SCORE_NGRAMS(text_tokens_ngrams, 'foo') DESC
LIMIT 100)) AS x WITH OFFSET AS rank
)
)
GROUP BY key
ORDER BY rrf_score DESC
LIMIT 5;
基于得分的融合
当不同方法的相关性得分具有可比性或您可以对其进行归一化处理时,基于得分的融合方法非常有效,这可能有助于实现更精确的排名,其中会纳入每种方法的实际相关性权重。
相对得分融合 (RSF) 是一种基于得分的方法,它会根据每种方法中的最高得分和最低得分对不同方法的得分进行归一化处理,通常使用 MIN() 和 MAX() 函数。由一组检索器检索到的文档的 RSF 得分计算方式如下:
\[ score(d\epsilon D)\ =\ \sum\limits_{r\epsilon R}^{}({w}_{r}*(scor{e}_{r}(d)\ -\ mi{n}_{r})\ /\ (ma{x}_{r\ }-mi{n}_{r})\ ) \]
其中:
- d 是文档。
- R 是检索器(FTS 和向量搜索)的集合。
- w 是分配给特定检索器的权重。
在 Spanner 查询中实现 RSF
如需实现 RSF,您必须将向量和 FTS 中的得分归一化为通用比例。以下示例在单独的 WITH 子句中计算最低分和最高分,以得出归一化系数。然后,它使用 FULL OUTER JOIN 合并结果,将近似最近邻 (ANN) 搜索(从 cosine_distance 转换而来)和 FTS 的归一化得分相加。
WITH ann AS (
SELECT key, APPROX_COSINE_DISTANCE(@vector, embedding,
OPTIONS => JSON '{"num_leaves_to_search": 50}') AS cosine_distance,
FROM hybrid_search
WHERE embedding IS NOT NULL
ORDER BY cosine_distance
LIMIT 100
),
fts AS (
SELECT key, SCORE_NGRAMS(text_tokens_ngrams, 'Green') AS score,
FROM hybrid_search
WHERE SEARCH_NGRAMS(text_tokens_ngrams, 'Green')
ORDER BY score DESC
LIMIT 100
),
ann_min AS (
SELECT MIN(1 - cosine_distance) AS min
FROM ann
),
ann_max AS (
SELECT MAX(1 - cosine_distance) AS max
FROM ann
),
fts_min AS (
SELECT MIN(score) AS min
FROM fts
),
fts_max AS (
SELECT MAX(score) AS max
FROM fts
)
SELECT IFNULL(ann.key, fts.key),
IFNULL(((1 - ann.cosine_distance) - ann_min.min) /
(ann_max.max - ann_min.min), 0) +
IFNULL((fts.score - fts_min.min) /
(fts_max.max - fts_min.min), 0) AS score
FROM ann
FULL OUTER JOIN fts
ON ann.key = fts.key
CROSS JOIN ann_min
CROSS JOIN ann_max
CROSS JOIN fts_min
CROSS JOIN fts_max
ORDER BY score DESC
LIMIT 5;
过滤后的搜索结果
过滤后的搜索使用 FTS 创建一个过滤器,以减少在 k 最近邻 (KNN) 向量搜索中考虑的文档集。您可以选择使用预排序来限制结果集的大小。
在 Spanner 查询中实现过滤式搜索
本部分中的示例搜索会执行以下步骤,以将向量搜索空间缩小到与关键字匹配的数据子集:
- 使用
SEARCH (text_tokens, 'Green')查找text_tokens列包含文本Green的行。返回的前 1,000 行是按搜索索引定义的重新排序顺序返回的。 - 使用向量函数
DOT_PRODUCT(@vector, embedding)计算查询向量 (@vector) 与存储的文档向量(嵌入)之间的相似度。然后,它会对结果进行排序,并返回最终的前 10 个匹配项。
SELECT key
FROM (
SELECT key, embedding
FROM hybrid_search
WHERE SEARCH (text_tokens, 'Green')
ORDER BY presort
LIMIT 1000)
ORDER BY DOT_PRODUCT(@vector, embedding) DESC
LIMIT 10;
机器学习重新排名
基于机器学习的重新排名是一种计算密集型但非常精确的方法。它将机器学习模型应用于已通过 FTS、向量搜索或两者组合缩减的小候选集。如需详细了解 Spanner Vertex AI 集成,请参阅 Spanner Vertex AI 集成概览。
您可以将机器学习重新排名与 Spanner ML.PREDICT 函数和已部署的 Vertex AI 模型集成。
实现基于机器学习的重新排名
- 将重排序器模型(例如来自 HuggingFace 的模型)部署到 Vertex AI 端点。
创建指向 Vertex AI 端点的 Spanner
MODEL对象。例如,在以下Reranker模型示例中:text ARRAY<string(max)>是文档。text_pair ARRAY<string(max)>是示例中的查询文本。score是机器学习模型为文本输入对分配的相关性得分。
CREATE MODEL Reranker INPUT (text ARRAY<string(max)>, text_pair ARRAY<string(max)>) OUTPUT (score FLOAT32) REMOTE OPTIONS ( endpoint = '...' );使用子查询检索初始候选结果(例如 ANN 搜索的前 100 个结果),然后将其传递给
ML.PREDICT。该函数会返回一个用于最终排序的新得分列。SELECT key FROM ML.PREDICT( MODEL Reranker, ( SELECT key, text, "gift for 8-year-old" AS text_pair FROM hybrid_search WHERE embedding IS NOT NULL ORDER BY APPROX_DOT_PRODUCT(@vector, embedding, options=>JSON '{"num_leaves_to_search": 50}') DESC LIMIT 100) ) ORDER BY score DESC LIMIT 3;