浅谈 Semantic Search

标签: dev | 发表时间:2023-06-13 00:00 | 作者:
出处:https://itindex.net/relian
顾名思义,关键词搜索就是当用户输入关键词( Query),搜索引擎会去搜索并匹配含有 Query的文档,然后返回相关的结果( Response)。一般来说,一个文档包含越多的用户关键词,它在结果中的排序就越靠前。

举个简单的例子。假设有以下的 QueryResponses

Quer y: 

  • Where is the world cup?

Responses:

  • The world cup is in Qatar.

  • The sky is blue.

  • The bear lives in the woods.

  • An apple is a fruit.

我们可以观察 Responses中每个结果中的词与 Query 中的词,然后找出在  Query  Response 都出现的词的个数:

  • The world cup is in Qatar. (4 words in common)

  • The sky is blue. (2 words in common)

  • The bear lives in the woods. (2 words in common)

  • An apple is a fruit. (1 word in common)

非常幸运的是,排在第一个的就是我们最想要的回答。

然而,哪有那么多幸运啊。现实情况是,用户输入的 Query总有你想不到的。比如这个:“Where in the world is my cup of coffee? ”(我的咖啡在哪里?)

这种场合下,排在第一个的回答就与用户问的问题风马牛不相及了。

当然,我们可以采取一些办法来改善搜索结果。比如,可以通过删除诸如 “the”、“and”、“is” 等停止词,使用类似 TF-IDF的方法来区分相关词和非相关词等。

但,这还远远不够。为了让搜索结果更精准,还需要对 Query进行一系列的处理,比如:纠错、同义词处理、意图识别、多国语言处理等。另外,如果系统除了搜索文本,还需要支持搜索语音、图片、视频,那又该怎么呢?当我们谈及这些问题时,其实已经进入语义搜索讨论的范畴了。

好了,进入正题。

语义搜索的工作原理是怎样的呢?简而言之,可以归纳如下:

  • 使用 Embedding 技术,将文档转换为向量;

  • 将向量存储在向量数据库中(如Pinecone,Milvus, Weaviate, Chroma 等);

  • 使用 Embedding 技术,将 Query 也转成向量;

  • 使用向量检索算法(如 IVD,Annoy,LSH,HNSW 等),查找与 Query 向量相似的文档向量;

  • 返回 Top N 个结果。

如何使用 Embedding 

就像万物都可以做 NFT一样,万物都可以做 embedding(中文:“嵌入”)。比如:一个词,一个 Query,一篇文章,一段语音,一张图片,你在电商网站上的历史购物清单,等等。我们可以通过 OpenAIHuggingface 等第三方提供的接口获取文本的 embedding

那么,什么是 embedding呢?

Embedding是一种将对象(指上面说的 词、 Query、文章、语音、图片等)进行语义赋值的方法。赋值后得到一个特征向量:如 (0.221, -0.012, 0.048, 0.321, 0.561, -0.159)。例子中的向量是一个 1X6 维向量。实际应用中,特征向量的维度可以基于场景做选择,通常维度越大,向量能表达的信息越多,当然计算量也就越大。

特征向量中的每个维度对应不同的特征,有些可能是有物理意义的特征(如年龄、性别等),但更多的是人不能辨别的抽象特征。

有了特征向量,接下来我们就可以计算不同对象之间的相似性了。两个语义相近的词,其特征向量间的距离也是很靠近的。

下面,我们来举个例子。

为了强化视觉效果,我们通过降维算法(如 t-SNE,UMAP),把高维的向量压缩到 2 维空间,可以用 X、Y坐标表示。例如,假设句子 “ The world cup is in Qatar?”的向量为 ( 4, 2) , 也就是图中 X坐标是4, Y坐标是 2 的那个足球。

把所有句子对应的向量在坐标轴上标注好后,我们就肉眼可见:由 “奖杯” 表示的 Query最接近由 “足球” 表示的句子:“ The world cup is in Qatar”,而其他的句子都离 “奖杯” 比较远。距离的远近反映了相似的程度,距离越近,语义越相似。这样,“ The world cup is in Qatar.” 这个回答就会排在第一。

再来看一个复杂一点的例子。

先准备 4 个问句和对应的 4 个回答:

Queries:

Q1: Where does the bear live?

Q2: Where is the world cup?

Q3: What color is the sky?

Q4: What is an apple?

Responses:

R1: The bear lives in the woods.

R2: The world cup is in Qatar.

R3: The sky is blue.

R4: An apple is a fruit.

这里,我使用了   Sentence-Transformers 来获取上面 8 个句子的 embedding,然后再使用 UMAP降维算法将向量降到 2 维,这样就可以在 X,Y坐标上标注它们了。

  fromsentence_transformersimportSentenceTransformer  importumap  importmatplotlib.pyplotasplt     
model = SentenceTransformer('all-mpnet-base-v2')
#Our sentences we like to encode sentences = ['Where does the bear live?', 'Where is the world cup?', 'What color is the sky?', 'What is an apple?', 'The bear lives in the woods.', 'The world cup is in Qatar', 'The sky is blue.', 'An apple is a fruit.' ]
#Sentences are encoded by calling model.encode() embeddings = model.encode(sentences)
# Compress the embeddings to 2 dimensions reducer = umap.UMAP(init='spectral', random_state =42, n_neighbors =49, min_dist =0.0, n_components =2) umap_embeds = reducer.fit_transform(embeddings)
plt.figure(figsize=(10,8)) plt.scatter(umap_embeds[:,0], umap_embeds[:,1], c='green') fori, txtinenumerate(sentences): plt.annotate(txt, (umap_embeds[i,0], umap_embeds[i,1]))
plt.grid(color='gray', linestyle='--', linewidth=0.5) plt.show()
将各向量在坐标轴上标注后,如下图所示:

结合上图,我们可以发现每个 Query最接近其对应的 Response。这意味着,如果我们使用语义搜索来查询,并且返回结果只取排名最高的 1 个,那么查询上面 4 个 Query,每一个我们都能得到正确的 Response

怎么找到相似的文档

在上面的例子中,其实我们使用了欧几里得距离,也就是两个点之间的直线距离,来度量向量之间的相似度,距离越近就越相似。

这种距离度量简单直接,而且在低维空间(如2维,3维)也表现得很好,但是当向量维度高了之后,比如 4096 维的向量,这时再用欧几里得距离来度量相似度就不合适了。因为在高维情况下,每个向量之间的欧几里得距离都很靠近,也就无法起到度量相似性的作用了。

那么,怎么度量高维向量的相似度呢?通常有下面两种方法:

  • Dot product similarity 点积相似度:计算高维向量之间的距离,距离越小就越相似;

  • Cosine similarity 余弦相似度:计算两个向量之间的夹角,夹角越小,向量之间越相似。

这两种度量方式,一定程度上可以转换,这里我们选择余弦相似度。余弦相似度的返回值在 0 到 1 之间,一段文本与其自身的相似度总是 1,而两段风马牛不相及的文本相似度是0。

接下来,我们用余弦相似度代替欧几里得距离,再来计算一下上述数据集中 8 个句子两两之间的相似度。计算结果如下图所示:

观察上图,可以得出如下结论:

  • 颜色越黑表示越不相似,颜色越浅越相似;

  • 对角线上全是 1:因为每个句子和它自己的相似度是1;

  • 每个 Query 与其相应的 Response 之间的相似度在 0.7 左右;

  • 任何其他句子之间的相似度得分都很低。

到目前为止,我们是怎么找到与一个 Query最相似的 Response的呢?

没错,目前其实就是用 “强大的” 暴力搜索!也就是说,为了找到一个已知点最近的 “邻居”,需要计算该点与数据集中所有其他点之间的距离!

 比如,为了找到与 “ Where is the world cup?” 最近的点(标记为红色),我们必须计算除自己外的所有 7 个点的距离,如下图所示:

在数据集比较小的时候,比如一个总共只有几百条或几十条问答对的 QA系统,暴力搜索其实也可以工作得很好,我们没必要过早对算法进行优化。

但是,当数据集变得越来越大的时候,暴力搜索就力不从心了,如果一个用户 Query过来,系统要响应 10 多秒,甚至分钟级别,那用户早就跑了。所以,我们需要改进检索模型,让查找 “邻居” 的算法更加高效一些。

我们可以用一种近似的算法,来缩小候选集合,而不是再傻傻地去和所有的数据做比较。常用的近似最近邻检索算法,主要有:

  • IVD: 先对所有的文档进行聚类,得到聚类后,再分别用一个中心向量表示每个聚类。查询的时候,先找到离 Query 最近的中心向量,再计算 Query 和这个中心向量代表的聚类中所有点的距离;

  • LSH:局部敏感 Hash。核心思想是将潜在相似的文档放在同一个哈希桶。查询的时候,先计算用户 Query 落在哪一个哈希桶,然后再计算 Query 和桶中的各个“邻居”之间的距离;
  • Annoy:通过构建多个独立的二叉树,每棵二叉树包含部分的数据点。查询的时候,通过遍历多个二叉树来寻找最近的 “邻居”;
  • HNSW: 这是一种快速且高效的近似最近邻搜索算法。它通过将数据点组织成多层结构,然后在每一层上构建图形连接,进而实现快速地搜索与目标点距离最近的数据点。通俗地说,HNSW 算法就像一个分层的城市导航系统,可以帮助你寻找到离目标位置附近的点。

有 Embedding 就够了吗?

说到现在,似乎一直都在说基于 embedding的语义搜索是多么棒,多么有效。

但,它就真的这么完美吗?答案当然是: No!

我们来扩展一下最初的数据集,针对 “ Where is the world cup?” 这个问题添加一些回答。然后如同之前一样,获取 embedding并进行向量压缩,最后标注在坐标轴上。如下:

Query: 

  • “Where is the world cup?”

Responses:

  • The world cup is in Qatar.
  • The world cup is in the moon.
  • The previous world cup was in Russia.

Notes:眼尖的你或许发现了,下图中句子的坐标和之前的不一样。这是因为 UMAP使用邻域图和基于梯度的优化方法来降维,当你向数据集中添加一个新的样本时,整个数据结构可能会发生变化。这意味着,在将新句子添加到数据集中并对其进行降维时,之前句子的降维表示可能会发生变化。

如上图所示, Query:“W here is the world cup ?” 的3个回答都比较靠近它。但是,距离最近的却并不是我们最想要的 “ The world cup is in Qatar.”,而是 “ The world cup is in the moon.”。虽然从 embedding的相似性上来判断,这个回答看起来和 Query最相似,但这完全是错误的。再看回答:“ The previous world cup was in Russia.”,虽然从语义上来说,这个回答是一个正确的陈述,但它也不是当前问题的最佳答案。

从这个例子可以看出,如果仅依靠  embedding 之间的相似性,诸如此类的错误或不相关的回答,是完全有可能会成为语义搜索模型中的 “最佳答案” 的。

那么,怎么解决这个问题,从而让模型返回用户最想要的结果,或者至少接近这个目标?

实践中方法有多种,列举一二:

1. 使用注意力机制:构造( query_i, response_i) 之间的上下文向量 context_vector_i,再计算 context_vector_iresponse_i之间的相似性。相似度越高的回答,最终的排名更高;

2. 使用强化学习:设计奖励函数,将相关的答案排名更高,不相关的答案排名更低。

比如:

  • 如果相关的答案: “The world cup is in Qatar” 排名第一,那么可以给予正的奖励,比如 +1。这样可以鼓励模型学习将相关的答案排名更高;
  • 如果不相关的答案:“The world cup is in the moon” 排名第一,可以给予负的奖励,比如 -1。这样可以鼓励模型学习将不相关的答案排名更低;
  • 如果相关的答案排名第二,可以给予较小的正奖励,比如 +0.5。这仍然可以鼓励模型将相关的答案排名更高,但效果不如排名第一时那么理想;
  • 如果不相关的答案排名较高;比如前三名,可以给予较小的负奖励,比如 -0.5。这仍然可以鼓励模型将不相关的答案排名更低,但效果不如排名第一时那么理想。
  • 除以上规则之外,不给予任何奖励或惩罚。
3. 结合知识图谱:将知识图谱与现有模型相结合,利用知识图谱中的关系数据来辅助语义相似度判断,从而提高模型的准确性。结论

本文中,我们讨论了基于 embedding的语义搜索,以及常见的距离度量和向量搜索算法。

  • Embedding 是强大的表示方法,无论是在向量之间的距离方面还是在向量算术方面;

  • 文中使用的 3 个主要距离度量是欧几里得距离、向量点积以及余弦相似度;

  • 近似近邻搜索算法可以有效提升查找相似文档的效率。目前最常用的是 HNSW,但也要根据实际情况选择,不过早做优化。

最后,如果你想玩一下语义搜索,除了可以使用文中提及的各种向量数据库外,也可以使用 Elasticsearch8.0 以上版本。 ES8.0 以上版本支持了 dense_vector 以及  knn_search。这样的好处就是,你可以根据业务场景觉定只使用关键词搜索,亦或是只使用语义搜索,还是两者结合使用。

相关 [semantic search] 推荐:

浅谈 Semantic Search

- - IT瘾-dev
顾名思义,关键词搜索就是当用户输入关键词( Query),搜索引擎会去搜索并匹配含有 Query的文档,然后返回相关的结果( Response). 一般来说,一个文档包含越多的用户关键词,它在结果中的排序就越靠前. 假设有以下的 Query和 Responses:. 我们可以观察 Responses中每个结果中的词与 Query 中的词,然后找出在  Query和   Response 都出现的词的个数:.

Latent Semantic Analysis(LSA) - CSDN博客

- -
Latent Semantic Analysis(LSA)中文翻译为潜语义分析,也被叫做Latent Semantic Indexing ( LSI ). 意思是指通过分析一堆(不止一个)文档去发现这些文档中潜在的意思和概念,什么叫潜在的意思. 我第一次看到这个解释,直接懵逼. 假设每个词仅表示一个概念,并且每个概念仅仅被一个词所描述,LSA将非常简单(从词到概念存在一个简单的映射关系).

Semantic:来自未来的CSS布局框架

- 小宇 - 雷锋网
CSS网格框架可以让你享受轻松的生活,但这依然无法掩盖它们的不足. 让我们感到幸运的是,先进技术为我们引入了新的方法去布局网页. 在我们前往了解先进技术前,我们必须首先了解当前影响CSS网格样式的三个看似不可克服的缺陷. 这是我从“纯化论者”口中听到过的最大的抱怨,自我在两年前创建1KB CSS Grid框架开始,CSS网格系统就规定标记和描述必须在一起.

MySQL Fulltext Search 使用方式

- - Tsung's Blog
使用 MySQL 來達到 Fulltext 的效果, MySQL 對於 英文會自己依照空格去斷開, 中文就得要自行斷詞囉~. MySQL Fulltext Search 使用方式 與 注意事項. MySQL Fulltext 不支援 InnoDB, 需要使用 MyISAM.. 建立 Table 時, 需要設定 FULLTEXT(Col-name)..

Steve Souders: In Search of Speed 学习笔记

- arbeitandy - 知道分子
原雅虎首席性能官、现任 Google Web 性能专家 Steve Souders,近期在 LA 隆重举行的 SpeedGeeks 会议上发表重要讲话:In Search of Speed(slides, video),以下为学习笔记:. 以 iGoogle 为例,前端页面组件渲染的时间占了整个页面打开时间的 91%,前端优化的重要性不言而喻.

Google、「Buzz」や「Code Search」も終了へ

- GOT4416 - ITmedia News 速報 最新記事一覧
製品担当副社長のホロビッツ氏は、「Google Buzz」で学んだ多くのことを「Google+」に反映させていくと語った.

Google Code Search 終了のお知らせ

- 三十不归 - スラッシュドット・ジャパン
ある Anonymous Coward 曰く、. SourceForge.JP Magazine の記事によると、Google はソースコード検索サービスである「Google Code Search」を 2012 年 1 月 15 日に終了するとのことだ. 主要サービスにいっそう集中していくための再編の一環で、「Google Buzz」や「Jaiku」など 5 つのプロジェクトが対象となっている.

学生及研究者必备:Academic Search

- - 威锋网新闻- 最新RSS订阅
Academic Search来自微软研究院(Microsoft Research),对学生和研究者而言是最有帮助的服务之一. 该应用服务允许用户搜索并仔细查阅3600万个学术著作,内容涵盖14个领域.

Graph Search:貌不惊人的颠覆者

- - 《商业价值》杂志
尽管年轻的Graph Search暂时还无法对传统搜索形成全面冲击,但它未来颠覆的将可能是整个互联网以流量为核心的商业模式. 1月16日,Facebook发布了社交图谱搜索——Graph Search,这是Facebook自去年5月上市饱受质疑近8个月后做出的最大的动作,扎克伯格亲自主持产品的发布,足见其不容忽视的里程碑意义.

集成Hibernate Search做全文检索

- - CSDN博客架构设计推荐文章
1、修改hibernate主配置文件,增加:. 一个是存储的实现,一个是存储的路径. @Analyzer(impl=SmartChineseAnalyzer.class)//分词器 public class Pageinfo implements java.io.Serializable {. // columns START //省略1000字.