Elasticsearch做文本检索是基于文本之间的相似性的。在Elasticsearch 5.0中,Elasticsearch将默认算法由TF / IDF切换为Okapi BM25,该算法用于对与查询相关的结果进行评分。对Elasticsearch中的 TF / IDF 和 Okapi BM25感兴趣的可以直接查看Elastic的官方博客。(简单的说,TF/IDF和BM25的本质区别在于,TF/IDF是一个向量空间模型,而BM25是一个概率模型,他们的简单对比可以查看这个 文章)
TF/IDF中是一个向量空间模型,它将查询的每个term都视为向量模型的一个维度。通过该模型可将查询定义为一个向量,而Elasticsearch存储的文档定义另一个。然后,将两个向量的标量积视为文档与查询的相关性。标量积越大,相关性越高。而BM25属于概率模型,但是,它的公式和TF/IDF并没有您所期望的那么大。两种模型都将每个term的权重定义为一些idf函数和某些tf函数的乘积,然后将其作为整个文档对给定查询的分数(相关度)汇总。
但是,如果我们想根据更抽象的内容(例如单词的含义或写作风格)来查找类似的文档,该怎么办?这是Elasticsearch的密集矢量字段数据类型(dense vector)和矢量字段的script-score queries发挥作用的地方。
什么是Word Embeddings
因为计算机不认识字符串,所以我们需要将文字转换为数字。Word Embedding就是来完成这样的工作。 其定义是:A Word Embedding format generally tries to map a word using a dictionary to a vector。
如下图,计算机用ASC码来识别bread(面包)和toast(吐司),从ASC II码上,这两个单词没有任何的相关性,但如果我们将它们在大量的语料中的上下文关系作为向量维度进行分析,就可以看到,他们通常都和butter(黄油)、breakfast(早餐)等单词同时出现。
或许,我们的搜索空间里面没有面包这个词出现,但如果用户搜索了面包,我们可以尝试给他同时返回吐司相关的信息,这些或许会对用户有用
因此,如上图,我们把beard和toast映射为:
bread [0.80 0.11 .0.05 0.93 0.20 ...]
toast [0.76 0.22 0.15 0.95 0.12 ...]
对应的向量数组,即为bread和toast的 Word Embeddings
。当然,Word Embeddings
的生成可以采用不同的算法,这里不做详述。
索引Word Embeddings
Word Embeddings
是词的矢量表示,通常用于自然语言处理任务,例如文本分类或情感分析。相似的词倾向于在相似的上下文中出现。Word Embeddings
将出现在相似上下文中的单词映射为具有相似值的矢量表示。这样,单词的语义被一定程度上得以保留。
为了演示矢量场的使用,我们将经过预训练的GloVe 的 Word Embeddings
导入到Elasticsearch中。该glove.6B.50d.txt文件将词汇表的400000个单词中的每一个映射到50维向量。摘录如下所示。
public 0.034236 0.50591 -0.19488 -0.26424 -0.269 -0.0024169 -0.42642 -0.29695 0.21507 -0.0053071 -0.6861 -0.2125 0.24388 -0.45197 0.072675 -0.12681 -0.36037 0.12668 0.38054 -0.43214 1.1571 0.51524 -0.50795 -0.18806 -0.16628 -2.035 -0.023095 -0.043807 -0.33862 0.22944 3.4413 0.58809 0.15753 -1.7452 -0.81105 0.04273 0.19056 -0.28506 0.13358 -0.094805 -0.17632 0.076961 -0.19293 0.71098 -0.19331 0.019016 -1.2177 0.3962 0.52807 0.33352
early 0.35948 -0.16637 -0.30288 -0.55095 -0.49135 0.048866 -1.6003 0.19451 -0.80288 0.157 0.14782 -0.45813 -0.30852 0.03055 0.38079 0.16768 -0.74477 -0.88759 -1.1255 0.28654 0.37413 -0.053585 0.019005 -0.30474 0.30998 -1.3004 -0.56797 -0.50119 0.031763 0.58832 3.692 -0.56015 -0.043986 -0.4513 0.49902 -0.13698 0.033691 0.40458 -0.16825 0.033614 -0.66019 -0.070503 -0.39145 -0.11031 0.27384 0.25301 0.3471 -0.31089 -0.32557
为了导入这些映射,我们创建了一个words索引,并在索引mapping中指定dense_vector
为vector字段的类型。然后,我们遍历GloVe文件中的行,并将单词和向量分批批量插入该索引中。之后,例如可以使用GET
请求检索“ early”一词(/words/_doc/early
):
{
"_index": "words",
"_type": "_doc",
"_id": "early",
"_version": 15,
"_seq_no": 503319,
"_primary_term": 2,
"found": true,
"_source": {