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

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

这就是为什么我们很高兴能够发布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个结果,例如:

{
 "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中的示例是:

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元组。像这样:

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”),以便我们可以使用该关键字来生成特征值。当示例代码将被取出时,我们将在其头部加上这种映射。

# 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查询:

 { "query": { "match": { "title": "" } } } 

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

{ "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)。

然后你可以运行:

python train.py 

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

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

judgements = judgmentsByQid(judgmentsFromFile(filename='sample_judgements.txt'))

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

kwDocFeatures(es, index='tmdb', searchType='movie', judgements=judgements)

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

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

buildFeaturesJudgmentsFile(judgements, filename='sample_judgements_wfeatures.txt')

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

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并使用保存的这个文件作为判断数据

trainModel(judgmentsWithFeaturesFile='sample_judgements_wfeatures.txt', modelOutput='model.txt') 

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

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中:

saveModel(es, scriptName='test', modelFname='model.txt') 

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

用排序学习模型进行搜索

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

{ "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个结果。

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

以后的文章

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

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

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

本文的版权归 用户1191492 所有,如需转载请联系作者。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏PPV课数据科学社区

PyTorch和TensorFlow哪家强:九项对比读懂各自长项短板

近日,斯坦福大学计算机科学系博士生 Awni Hannun 也发表了一篇文章,谈了自己对 PyTorch 和 TensorFlow 这两大明星框架的心得体验,并...

47160
来自专栏AI研习社

Tensorflow 入门与安装 | Tensorflow 最全资料汇总【2】

【AI研习社】关注AI前沿、开发技巧及技术教程等方面的内容。欢迎技术开发类文章、视频教程等内容投稿,邮件发送至:zhangxian@leiphone.com 自...

38360
来自专栏机器人网

PID控制原理:看完这个故事你就明白了

小明接到这样一个任务:有一个水缸漏水,且漏水的速度是不定的,但要求水面高度维持在某个位置,一旦发现水面高度低于要求位置,就要往水缸里加水。 ? 开始小明用瓢加水...

35650
来自专栏人工智能头条

DMLC深盟分布式深度机器学习开源平台解析

24060
来自专栏CVer

小米开源自研移动端深度学习框架MACE

Mobile AI Compute Engine (MACE) 是一个专为移动端异构计算平台优化的神经网络计算框架。MACE 支持 TensorFlow 和 C...

21940
来自专栏大数据文摘

手把手 | 如何在你的iPhone上建立第一个机器学习模型(Apple最新CoreML框架入门)

38850
来自专栏机器之心

深度 | PyTorch和TensorFlow哪家强:九项对比读懂各自长项短板

选自GitHub 作者:Awni Hannun 机器之心编译 参与:Panda 现在是各种机器学习框架群雄争霸的时代,各种各样的比较文章也层出不穷。近日,斯坦福...

34760
来自专栏AI科技评论

开发 | 紧跟未来深度学习框架需求,TensorFlow推出Eager Execution

AI科技评论按:Google的TensorFlow是AI学习者中使用率最高、名气也最大的深度学习框架,但由于TensorFlow最早是基于Google的需求开发...

35760
来自专栏CDA数据分析师

学习R语言,一篇文章让你从懵圈到入门

在实际工作中,每个数据科学项目各不相同,但基本都遵循一定的通用流程。具体如下: ? 数据科学工作流程: 1.数据导入 2.数据整理 3.反复理解数据 数据可视...

25660
来自专栏AI研习社

用神经网络对页面登录进行多参数优化的小妙招

我很乐意分享我用神经网络对页面登录进行多参数优化的一些实验。我想到这个点子已经有半年了,而且我发现从自动操作这个角度来看它十分有趣。A/B 测试会消耗市场专家...

7520

扫码关注云+社区

领取腾讯云代金券