3.6 Elasticsearch-深度学习排序:Learning to Rank 插件安装与特征工程
(基于 8.11.1 版本,Linux-x86_64 环境)
3.6.1 为什么要在 Elasticsearch 里做 LTR
传统 TF-IDI/BM25 只能反映“查询-文档”的字面匹配度,无法利用用户行为、商品属性、上下文等强信号。Learning to Rank(LTR)把排序问题转化为监督学习问题:
训练集 = { 特征向量 ⟨x_q,d ⟩ , 相关性标签 y }
在线阶段 = 把模型输出的分数字段写入 Function Score Query,实现毫秒级重排。
Elasticsearch 的 ltr 插件提供了“特征抽取 → 缓存 → 模型推理”一条闭环,且与 DSL 无缝集成,因此成为工业界事实标准。
3.6.2 插件安装与集群配置
-
版本对齐
Elasticsearch 8.11.1 对应 ltr 8.11.1.0,可在 GitHub Release 页面下载预编译包:
wget https://github.com/o19s/elasticsearch-learning-to-rank/releases/download/v8.11.1.0/ltr-8.11.1.0-es8.11.1.zip -
安装
逐节点滚动安装,避免重启全集群:
$ES_HOME/bin/elasticsearch-plugin install file:///path/ltr-8.11.1.0-es8.11.1.zip
安装完会生成 config/ltr 目录,默认缓存 1 GB,可通过 jvm.options 追加:
-Des.ltr.cache.size=2g -
安全加固
若开启 xpack.security,需给推理角色额外授权:
cluster:admin/ltr/*
建议单独创建 ltr_admin 角色,避免超权。
3.6.3 特征工程:从业务信号到 feature vector
LTR 效果 80% 取决于特征。Elasticsearch 里特征分三类:
A. 查询端(query-only)
如 query.length、query.category、用户地理位置。
B. 文档端(doc-only)
如 item.price、item.sales、doc.update_time。
C. 交叉(query-doc)
如 BM25、TF*IDF、embedding.cosine、用户-商品历史点击率。
特征脚本统一用 painless 写在 ltr_feature_set 里,支持返回 number、boolean、date 三种类型,布尔自动转 0/1。示例:
PUT _ltr/featureset/product_rank
{
“featureset”: {
“name”: “product_rank”,
“features”: [
{
“name”: “title_bm25”,
“params”: [“keywords”],
“template_language”: “mustache”,
“template”: {
“match”: {
“title”: “{{keywords}}”
}
}
},
{
“name”: “price_discount”,
“params”: [],
“template”: {
“script”: {
“lang”: “painless”,
“source”: “”"
if(doc[‘origin_price’].size()==0 || doc[‘price’].size()==0) return 0;
return (doc[‘origin_price’].value - doc[‘price’].value)
/ doc[‘origin_price’].value;
“”"
}
}
},
{
“name”: “ctr_7d”,
“params”: [“item_id”],
“template”: {
“script”: {
“lang”: “painless”,
“source”: “”"
String key = 'item_ctr’+doc[‘item_id’].value;
if(!params._source.containsKey(key)) return 0.05;
return params._source[key];
“”",
“params”: {
“_source”: “{{ctr_map}}”
}
}
}
}
]
}
}
注意:
- 特征脚本必须幂等,禁止随机数或当前时间。
- 对高基数稀疏特征(如 item_id)先 hash 到桶再喂入模型,避免维度爆炸。
- 若需读取外部 Redis,可在 painless 里用 ingest processor 预写,避免在线 io。
3.6.4 负采样与标签构造
线上曝光 → 点击 → 成交 三级漏斗,常用 5 档标签:
4-成交、3-加购、2-点击>10s、1-曝光未点、0-未曝光。
训练集必须保证每个 query 正负样本比例≈1:4,否则用 XGBoost 的 scale_pos_weight 自动调整。
采样策略:
• 随机负采样:简单但容易选到“显然不相关”样本,导致模型过于保守。
• 强负采样(hard negative):选 BM25 前 30 但未点击的文档,增加区分度。
• 对抗采样:用上一版模型打分 Top-50 中的未点击样本,持续迭代。
3.6.5 模型训练与格式转换
以 XGBoost 为例,训练完导出 json 格式:
xgb.train(param, dtrain, num_round=500)
xgb_json = xgb.get_booster().get_dump(with_stats=False, dump_format=‘json’)
再使用 ltr 官方工具 elasticsearch-ltr-tools 转换:
python -m ltr.convert --xgb_json xgb_model.json --output xgb.es.json
生成的 es json 包含“split_feature、threshold、left/right、value”四元组,可直接 POST 到 Elasticsearch:
POST _ltr/_model/product_xgb
{
“model”: {
“name”: “product_xgb”,
“model”: {
“type”: “model/xgboost+json”,
“definition”: “…<xgb.es.json内容>…”
}
}
}
3.6.6 在线推理:FunctionScore 与 rescore 双级加速
- 粗排:用 FunctionScore 把 BM25>50 的文档召回,减少候选集。
- 精排:用 rescore 窗口 1000,调用 ltr query:
GET product/_search
{
“query”: {
“bool”: {
“must”: [
{ “match”: { “title”: “蓝牙耳机” } }
],
“filter”: [
{ “range”: { “stock”: { “gt”: 0 } } }
]
}
},
“rescore”: {
“window_size”: 1000,
“query”: {
“rescore_query”: {
“sltr”: {
“model”: “product_xgb”,
“params”: {
“keywords”: “蓝牙耳机”,
“ctr_map”: { “123”:0.32, “124”:0.18 }
}
}
},
“query_weight”: 0,
“rescore_query_weight”: 1
}
}
}
sltr 会逐条计算特征向量并调用原生 C++ 推理,单分片 1k 文档耗时 <15 ms(8C16G 节点)。
如需 GPU 加速,可在本地服务化模型(torchserve/triton)再用 script_score 调用, latency 可压到 5 ms,但增加一次网络 hop。
3.6.7 特征缓存与热更新
ltr 插件默认把特征向量缓存到堆外内存,key=<query+docId+featureSet>,TTL=300 s。
对价格、库存等实时信号,可在 feature 脚本里加“版本号”参数:
“params”: { “price_version”: 12345 }
当价格变更时,业务方主动刷新版本号,触发缓存失效。
模型热更新:直接 PUT 同名 model,Elasticsearch 会 CAS 替换,无需滚动重启;旧版本仍在旧查询上下文里使用,实现零中断。
3.6.8 A/B 实验与效果验证
离线指标:NDCG@10、MAP、AUC。
在线指标:UV_CTR、GMV、人均翻页数。
实验方式:
• 集群层:按 user_id hash 到不同索引,索引级绑定不同 model。
• 查询层:在 sltr 里加 “model”: “product_xgb_v2{{#ab}}_exp{{/ab}}” ,利用 mustache 变量动态切换。
建议先 5% 流量灰度,观测 2 个完整交易日,确认 p-value<0.01 再全量。
3.6.9 踩坑总结
- 特征脚本返回 NaN 会导致整个查询失败,务必兜底。
- 深度分页 + rescore 会全量物化 TopN,window_size 不要盲目放大。
- XGBoost 的 missing 值默认走左子树,若 Elasticsearch 里缺失字段含义不同,需手动补 0 或 -1。
- 8.x 默认开启 SIMD 优化,旧版自定义特征脚本若含 sqrt/log 等浮点运算,可能出现 -0.0,导致 json 序列化异常,统一加 0.0 修正。
3.6.10 小结
通过 ltr 插件,Elasticsearch 从“文本检索引擎”升级为“ learned 排序平台”,把离线深度学习模型无缝搬到在线,实现毫秒级、可解释、可灰度的智能排序。特征工程是成败关键:先保证日志埋点完整,再围绕业务目标做交叉特征,最后通过负采样与模型迭代持续放大收益。
更多技术文章见公众号: 大城市小农民

被折叠的 条评论
为什么被折叠?



