前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >使用Elasticsearch进行智能搜索的机器学习

使用Elasticsearch进行智能搜索的机器学习

作者头像
大数据弄潮儿
发布2018-05-25 16:02:07
3.1K0
发布2018-05-25 16:02:07
举报
文章被收录于专栏:大数据大数据

众所周知,机器学习正在改变许多行业。搜索行业也是如此,公司通过手动调整搜索相关性来压榨潜能。成功的搜索组织希望通过“足够好”的手动调整来构建更智能的自学习搜索系统。

这就是为什么我们很高兴能够发布Elasticsearch排名学习插件。什么是排名学习?通过排名学习训练机器学习模型,来了解用户认为相关的内容。

在实施学习排名时,你需要:

  • 分析评估用户认为相关的内容并构建评估列表,将文档分为完全相关,中等相关或无关。
  • 假设哪些特征可能有助于预测相关性,例如TF*IDF这样的特定字段匹配,新近性,搜索用户的个性化等。
  • 训练一个模型,一个可以准确地将功能映射到相关性得分的模型。
  • 将模型部署到你的搜索服务器上,在你的产品上对搜索结果进行排名。

在上述的每个步骤中,都有复杂的技术难题和非技术性问题。直到现在还没有银弹(指能极大的提高软件生产率的东西)。正如我们在相关性搜索中提到的那样,手动调整搜索结果带来了许多相同的挑战,但是其也是一个很好的学习排名解决方案。在以后的博客文章中,我们将会讲述更多的基础设施,技术和非技术的挑战来完善我们的排名学习解决方案。

在这篇博客文章中,我想向你们介绍我们将排名学习集成到Elasticsearch中的工作。我们可以为我们的客户提供一项技术几乎所有相关的咨询服务,不管这项技术是否能帮到他们。然而,尽管Bloomberg让Solr有一个明确的方向,但Elasticsearch却没有。许多客户想要Elasticsearch的现代化功能,但是发现却发现其缺少关键的搜索技术可供选择。

事实上,Elasticsearch的查询DSL(一个帮助书写和运行查询的高级Elasticsearch库)可以对巨大且复杂的结果进行排名。熟练的工程师可以使用查询DSL来计算各种可能表示相关性的查询时间特征,从而给出以下问题的定量答案:

  1. 标题中提到的搜索字词有多少?
  2. 这篇文章/电影/...是多久之前发表的?
  3. 文档如何与用户的浏览行为相关联?
  4. 相对于买方的期望,这种产品有多贵?
  5. 用户的搜索术语和文章主题在概念上的关系如何?

许多这些功能不是搜索引擎中文档的静态属性。相反,它们是依赖查询的,这意味着这些功能度量用户或其查询与文档之间的某种关系。对于《相关搜索》的读者来说,这就是我们在该书中所说的信号。

所以,问题变成了,我们如何能够将机器学习的能力和Elasticsearch Query DSL的现有功能结合起来?这正是我们的插件所做的工作:使用Elasticsearch Query DSL查询作为机器学习模型的特征输入。

该插件如何工作?

该插件集成了RankLib和Elasticsearch。Ranklib以一个判断文件作为输入,并在本地输出一个人类可读的模型。然后,Ranklib可让您以编程方式或通过命令行训练模型。一旦你得到了一个模型,Elasticsearch插件包含以下内容:

  • 一种名为ranklib的自定义Elasticsearch脚本语言,可以接受ranklib生成的模型作为Elasticsearch脚本。
  • 一个自定义ltr查询,用于输入Query DSL查询(特点)和模型名称(在1处上传的内容)并对结果进行评分。

由于实施排名学习模型可能代价很大,你可能几乎不希望直接使用ltr查询。相反,你会重新排列前N个结果,例如:

代码语言:javascript
复制
{
 "query": { /*这里是一个简单的基础查询*/ },
 "rescore": {
  "window_size": 100,
  "query": {
   "rescore_query": {
    "ltr": {
     "model": {
      "stored": "dummy"
     },
     "features": [{
        "match": {
         "title": < users keyword search >
        }
       }...

你可以在项目的scripts目录中发现一个功能完整的示例。这是一个封装好的例子,它使用TMDB的电影人工判断。我使用TMDB的Elasticsearch索引来执行对应于特征的查询,用这些查询和功能的相关性得分来增加判断文件,并且在命令行上训练一个Ranklib模型。我将模型存储在Elasticsearch中,并提供一个脚本来使用该模型进行搜索。

不要被这个例子的简单所迷惑。现实中的排名学习解决方案是一项巨大的工作,包括研究用户,过程分析,数据工程和特征工程。我这么说,不是要阻止你,因为这样做的回报是值得的;只要知道你在做什么。较小的组织仍可能使用手工调整结果后在ROI(投资回报率)方面表现得更好。

训练并加载排名学习模型

先从我提供的手动创建的最小判断列表开始,来展示我们的示例如何训练模型。

Ranklib判断列表的格式相当标准。第一列包含对文档的判断(0-4)。下一列是查询ID,例如“qid:1”。紧跟其后的列包含与该查询-文档对关联的特征的值。冒号左侧是特征从1开始的索引。右侧是该特征的值。Ranklib README中的示例是:

代码语言:javascript
复制
3 qid:1 1:1 2:1 3:0 4:0.2 5:0 # 1A 2 qid:1 1:0 2:0 3:1 4:0.1 5:1 # 1B 1 qid:1 1:0 2:1 3:0 4:0.4 5:0 # 1C 1 qid:1 1:0 2:0 3:1 4:0.3 5:0 # 1D 1 qid:2 1:0 2:0 3:1 4:0.2 5:0 # 2A

还有注释(如#1A)。该注释是这一判断的文件标识符。Ranklib不需要文档标识符,但它对于读者来说非常有用。当我们通过Elasticsearch查询收集特征时,我们会发现它对我们也很有用。

我们的例子从上述文件的最小版本开始(在这里看)。我们需要从经过裁剪的文件开始,裁剪过的文件只包含分数,查询ID和文档ID元组。像这样:

代码语言:javascript
复制
4 qid:1 # 7555 3 qid:1 # 1370 3 qid:1 # 1369 3 qid:1 # 1368 0 qid:1 # 136278 ...

如上所述,我们为分级文档提供Elasticsearch _id作为每行注释。

这个方法需要进一步优化。我们必须将每个查询ID(qid:1)映射到实际的关键字查询(“Rambo”),以便我们可以使用该关键字来生成特征值。当示例代码将被取出时,我们将在其头部加上这种映射。

代码语言:javascript
复制
# Add your keyword strings below, the feature script will 
# Use them to populate your query templates # # qid:1: rambo # qid:2: rocky # qid:3: bullwinkle # # https://sourceforge.net/p/lemur/wiki/RankLib%20File%20Format/ # # 4 qid:1 # 7555 3 qid:1 # 1370 3 qid:1 # 1369 3 qid:1 # 1368 0 qid:1 # 136278 ... 

为了能够消除一些混淆,我将开始讨论ranklib“查询”(qid:1等)作为“关键字”,来和Elasticsearch Query DSL“查询”相区分,其中Elasticsearch Query DSL“查询”是Elasticsearch用来产生特征值的专用构建器。

以上内容并不是完整的Ranklib判断列表。对于给定关键字搜索的给定文档,这只是相关性等级的最小样本。要成为一个完整的训练集,它还需要包含上面展示的特征值,在展示的第一个判断列表的每一行后面都需要有1:0 2:1 ...

为了生成这些特征值,我们还需要提出可能对应于电影相关性的特征。这些正是我们所说的Elasticsearch查询。这些Elasticseach查询的分数将填满上面的判断列表。在上面的例子中,我们使用与每个要素编号对应的jinja模板来执行此操作。例如,文件1.json.jinja就是以下Query DSL查询:

代码语言:javascript
复制
 { "query": { "match": { "title": "" } } } 

换句话说,我们已经决定,我们的电影搜索系统的特征1应该是用户的关键字与标题字段匹配时的TF * IDF相关性分数。还有2.jinja.json,它在多个文本字段中执行更复杂的搜索:

代码语言:javascript
复制
{ "query": { "multi_match": { "query": "", "type": "cross_fields", "fields": ["overview", "genres.name", "title", "tagline", "belongs_to_collection.name", "cast.name", "directors.name"], "tie_breaker": 1.0 } } } 

排名学习的部分有趣之处来自假设什么功能可能与相关性相关。在该示例中,您可以将特征1和2更改为任何Elasticsearch查询。你还可以通过添加很多其他特征来实验。由于很多问题的特征很多,那么你需要获取足够多的具有代表性的训练样本,来涵盖所有合理的特征值。我们将在以后的博客中讨论更多的培训和测试排名学习的模型。

通过这两个组件,最小判断列表和一组推荐的Query DSL查询/特征,我们需要为Ranklib生成完备的判断列表,并将Ranklib生成的模型加载到Elasticsearch中以供使用。也就是说:

  1. 获取每个关键字/文档对的每个特征的相关性分数。也就是向Elasticsearch发出查询以记录相关性分数。
  2. 输出一个不仅包含成绩和关键字查询ID,还包含从步骤1中获得的特征值的判断文件:
  • 运行Ranklib来训练模型。
  • 将模型加载到Elasticsearch以便在搜索时使用。

进行这些步骤的代码都捆绑在train.py中,我鼓励你将它们分解开来。你需要通过以下步骤来进行分解:

  • RankLib.jar下载到脚本文件夹中。
  • Python安装Elasticsearch和Jinja2软件包(如果你不熟悉的话,请看Python requirements.txt)。

然后你可以运行:

代码语言:javascript
复制
python train.py 

这一个脚本贯穿上述所有步骤。为了引导您阅读代码:

首先,我们使用文件头,关键字查询ID,等级元组,以及文件头中特定的搜索关键字来加载最小判断列表:

代码语言:javascript
复制
judgements = judgmentsByQid(judgmentsFromFile(filename='sample_judgements.txt'))

然后,我们进行批量的Elasticsearch查询来记录每个判断的特征(增加判断中的传递)。

代码语言:javascript
复制
kwDocFeatures(es, index='tmdb', searchType='movie', judgements=judgements)

kwDocFeatures函数通过N.json.jinja(特性/查询)查找1.json.jinja,并使用Elasticsearch的批量搜索(_msearch)API ,进行批量的Elasticsearch查询来获取每个关键字/文档元组的相关性分数。代码很乏味,你可以在这里看到它。

一旦我们拥有了完备的功能,我们就可以将完整的训练集(判断和特征)输出到一个新文件(sample_judgements_wfeatures.txt)中:

代码语言:javascript
复制
buildFeaturesJudgmentsFile(judgements, filename='sample_judgements_wfeatures.txt')

输出文件将对应一个成熟的Ranklib判断列表,就像下面的:

代码语言:javascript
复制
3 qid:1 1:9.476478 2:25.821222 # 1370 3 qid:1 1:6.822593 2:23.463709 # 1369 

特征1是在标题(1.json.jinja)上搜索的“Rambo”的TF * IDF得分;特征2是更复杂的搜索(2.json.jinja)的TF * IDF分数。

接下来,我们开始训练!这行代码通过命令行运行Ranklib.jar并使用保存的这个文件作为判断数据

代码语言:javascript
复制
trainModel(judgmentsWithFeaturesFile='sample_judgements_wfeatures.txt', modelOutput='model.txt') 

如下所示,这只是运行java -jar Ranklib.jar来训练LambdaMART模型

代码语言:javascript
复制
def trainModel(judgmentsWithFeaturesFile, modelOutput): # java -jar RankLib-2.6.jar -ranker 6 -train sample_judgements_wfeatures.txt -save model.txt cmd = "java -jar RankLib-2.6.jar -ranker 6 -train %s -save %s" % (judgmentsWithFeaturesFile, modelOutput) print("Running %s" % cmd) os.system(cmd) 

然后,我们使用简单的Elasticsearch命令将模型存储到Elasticsearch中:

代码语言:javascript
复制
saveModel(es, scriptName='test', modelFname='model.txt') 

在这里,saveModel,像这里所展示的一样,只是读取文件内容作为要存储的ranklib脚本并将其发布到Elasticsearch。

用排序学习模型进行搜索

一旦你完成训练,你就可以进行搜索了!你可以在search.py​​中看到一个例子;这个例子里面的简单的查询非常直白。您可以运行python search.py rambo,它将使用训练的模型搜索“rambo”,执行以下rescoring查询:

代码语言:javascript
复制
{ "query": { "match": { "_all": "rambo" } }, "rescore": { "window_size": 20, "query": { "rescore_query": { "ltr": { "model": { "stored": "test" }, "features": [{ "match": { "title": "rambo" } }, { "multi_match": { "query": "rambo", "type": "cross_fields", "tie_breaker": 1.0, "fields": ["overview", "genres.name", "title", "tagline", "belongs_to_collection.name", "cast.name", "directors.name"] } }] } } } } } 

在这里,我们只是重新排名前20的结果。我们可以直接使用ltr查询。事实上,直接运行模型可以工作得很好。虽然它需要几百毫秒来运行整个集合。对于更大的集合,这是不可行的。总体而言,由于排名学习模型的性能成本,最好重新排名前N个结果。

这就是可以运行的例子。当然,这只是一个愚蠢的例子,旨在让你发掘创造力。你自己的问题可能需要更多的组件来解决。您选择的特征,如何记录特征,如何训练模型以及实现一个基准排名功能取决于你的研究领域。我们在相关搜索中撰写的大部分内容仍然适用。

以后的文章

在未来的博客文章中,我们将有更多关于学习排名的内容,包括:

  • 基础:排名学习到底是什么。
  • 应用程序:使用排名学习搜索,推荐系统,个性化等等。
  • 模型:什么是流行的模型?选择模型需要考虑什么?
  • 注意事项:排名学习需要考虑哪些技术和非技术因素?

如果你认为你想讨论你的搜索应用程序如何从排名学习中受益,请告诉我们。我们也一直在寻找合作者或在实际生产系统来比我们做的更好的人。所以,试一下,并给我们发送反馈!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 该插件如何工作?
  • 训练并加载排名学习模型
  • 用排序学习模型进行搜索
  • 以后的文章
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档