专业的编程技术博客社区

网站首页 > 博客文章 正文

如何在 Elasticsearch 上应用机器学习排序插件

baijin 2024-10-24 08:47:34 博客文章 14 ℃ 0 评论

文章转载自 http://opensourceconnections.com/blog/2017/02/14/elasticsearch-learning-to-rank/,对原文有删减以提升阅读性

很多公司尽可能地手动调优搜索引擎以提升效果,而成熟的搜索引擎使用更聪明的自学习系统来实现。现如今搜索引擎 Elasticsearch 上终于有了学习排序插件:

Elasticsearch Learning to Rank Plugin

https://github.com/o19s/elasticsearch-learning-to-rank

LTR 模型,可以通过用户行为来学习的相关性,实现这个模型需要:

  1. 通过统计度量用户 “投出” 的相关性,建立一个打分列表,对用户查询后召回的文档打上 “很相关”、“较相关”、“不相关”。

  2. 假设某些特征会帮助预测相关性,比如 特定领域的 TF*IDF、新颖性、用户画像。

  3. 训练模型以准确映射特征与分数的关系。

  4. 部署模型到搜索引擎上,用它来排序搜索结果。

这里面每个步骤都很复杂,没有一劳永逸的解决方案。本文重点介绍如何在 Elasticearch 上集成 LTR 模型。许多客户在部署 Elasticsearch 上都发现 LTR 在这个技术栈中有缺失,因此才有了 LTR 插件。确实 Elasticsearch 的 Query DSL 可以高效实现结果排序,有经验的相关度工程师能使用这个组件计算大量与查询特征,并定量回答下面问题:

  1. 标题中有多少搜索词项?

  2. 文章 / 电影 发布于多久以前?

  3. 文档如何关联于用户的浏览行为?

  4. 产品比买家期望的贵吗?

  5. 用户搜索的词与文章主题词概念相关吗?

很多这些特征不是静态属性,相反他们是查询时依赖的,依赖于用户、查询与文档的关系。所以问题来了,怎么才能让 Query DSL 充分利用机器学习的能力,这就是这个插件能做到的。

工作原理

这个插件融合了 RankLib (https://sourceforge.net/p/lemur/wiki/RankLib/) 与 Elasticsearch,RankLib 输入打分文件,输出可读格式的模型,训练模型可以通过编程或命令行操作。有了模型,Elasticsearch 插件就会有以下内容:

  1. ranklib: 定制的脚本语言,把 RankLib 生成的模型作为 ES 脚本。

  2. ltr query: 定制的查询,输入一个 Query DSL 查询(特征)、ranklib 模型名称,以及打分结果

LTR 模型耗时较长,ltr query 不会直接使用,而是对前 N 个结果重新打分,如下代码所示:

想看完整的示例移步 demo:

https://github.com/o19s/elasticsearch-learning-to-rank/tree/master/demo

该例子使用了 TMDB 数据库中的电影评分数据,使用 ES 建立了 TMDB 索引,通过命令行训练了 RankLib 模型,存储在 ES 中,并使用脚本应用模型。例子比较简单,但实际上成熟的解决方案有大量工作要做,包括用户研究、处理分析、数据工程,以及特征工程。小组织往往可以使用 ROI 手动调优来实现更好的效果。

训练和加载 LTR 模型

在这个例子里,可以看出模型是怎么训练的,从一个简单的打分列表开始。首先,RankLib 打分列表有个标准格式,第一栏是个 0-4 的打分,然后是 query id,如 qid:1,后续的列是特征索引以及对应的值,如 1:1, 4:0.2,示例如下:

其中如 # 1A 的注释代表文档的标识符,便于阅读,也便于 Elasticsearch 检索。

根据上述标准格式,我们的例子给出了一个精简版本的打分列表,只有一个打分、query id 和文档 id。

进一步的,我们映射每个 query id (qid:1) 到一个实际的 query (“rambo”),所以我们使用关键词生成特征值,我们提供这个映射在例子的头信息中:

上述例子仅仅是个精简版本的打分列表,完整版需要加上特征索引及特征值。生成这些特征还需要列举可能跟电影相关的特征。生成这些特征和特征值对可以通过 ES Query DSL 来查询出来,一个多文本字段复杂查询的例子模板见:

https://github.com/o19s/elasticsearch-learning-to-rank/blob/master/demo/2.json.jinja

LTR 模型有趣之处是需要猜测特征相关度,如果有太多特征则需要足够多的训练数据来保证合理的特征值。

基于以上的两个步骤,最小打分列表和给出的 Query DSL 查询/特征集合,我们需要生成一个全量评分列表,并且加载 RankLib 生成的模型 到 ES 上。这意味着:

  1. 为特征获取相关度得分,即让 ES 记录查询及相关度得分。

  2. 输出完整打分列表,包含打分、query id,及特征值

  3. 运行 RankLib 训练模型

  4. 加载模型到 ES 待搜索调用

训练程序调用过程如下:

  1. 下载 RankLib.jar (https://sourceforge.net/projects/lemur/files/lemur/RankLib-2.6/) 到 scripts 目录中

  2. 通过 requirement.txt 安装 Python 依赖

  3. 运行 python train.py (https://github.com/o19s/elasticsearch-learning-to-rank/blob/master/demo/train.py)

训练代码细节 (train.py)

加载最小打分列表,包含文档、query id、打分,以及 qid 与 query 的对应关系

然后我们发起批量查询记录每个打分项的特征

这个函数 kwDocFeaturs 从 1.json.jinja 一直读取到 N.json.jinja,进而批量生成 1 - N 每个特征的相关得分。

一点我们有了全量特征,就可以导出全量训练集 (sample_judgements_wfeatures.txt)

输出是一个完整的 RankLib 打分列表

其中特征 1 是用 title 检索 (1.json.jinja) “Rambo” 时的 TF*IDF 值,特征 2 是用更复杂的查询 (2.json.jinja) 检索的值。

下面进行训练,调用 RankLib.jar 来训练打分列表,训练了一个 LambdaMART 模型。

使用 LTR 模型搜索

完成训练后,可以进行搜索,例子中有个 search.py 文件可用来参考,执行命令 python search.py rambo 会使用训练好的模型检索 “Rambo” 并执行重打分查询:

这里只对前 20 个结果做重排序,也可以直接使用 LTR 查询,在全集上运行需要几百毫秒,性能原因最好支队前 N 个结果重排序。

后续内容

本文博客中还有其他内容,包括:

  1. 基础:介绍 LTR 到底是什么

  2. 应用:LTR 应用于搜索、推荐系统、个性化等

  3. 模型:流行的模型是什么?如何选择模型?

  4. 思考:使用 LTR 时有哪些技术和非技术因素需要考虑?

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表