3.6 Elasticsearch-深度学习排序:Learning to Rank 插件安装与特征工程

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 插件安装与集群配置

  1. 版本对齐
    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

  2. 安装
    逐节点滚动安装,避免重启全集群:
    $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

  3. 安全加固
    若开启 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}}”
}
}
}
}
]
}
}

注意:

  1. 特征脚本必须幂等,禁止随机数或当前时间。
  2. 对高基数稀疏特征(如 item_id)先 hash 到桶再喂入模型,避免维度爆炸。
  3. 若需读取外部 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 双级加速

  1. 粗排:用 FunctionScore 把 BM25>50 的文档召回,减少候选集。
  2. 精排:用 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 踩坑总结

  1. 特征脚本返回 NaN 会导致整个查询失败,务必兜底。
  2. 深度分页 + rescore 会全量物化 TopN,window_size 不要盲目放大。
  3. XGBoost 的 missing 值默认走左子树,若 Elasticsearch 里缺失字段含义不同,需手动补 0 或 -1。
  4. 8.x 默认开启 SIMD 优化,旧版自定义特征脚本若含 sqrt/log 等浮点运算,可能出现 -0.0,导致 json 序列化异常,统一加 0.0 修正。

3.6.10 小结
通过 ltr 插件,Elasticsearch 从“文本检索引擎”升级为“ learned 排序平台”,把离线深度学习模型无缝搬到在线,实现毫秒级、可解释、可灰度的智能排序。特征工程是成败关键:先保证日志埋点完整,再围绕业务目标做交叉特征,最后通过负采样与模型迭代持续放大收益。
更多技术文章见公众号: 大城市小农民

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

乔丹搞IT

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值