以 Eland 玩转 Elasticsearch 8.12 Learning-to-Rank

1 为什么要在 Elasticsearch 上做 LTR?

适用版本: Elasticsearch ≥ 8.12.0
前置条件: 需拥有包含 "Serverless LTR" 的订阅等级(详见官方订阅矩阵)
技术栈: Elasticsearch + Python + Eland + XGBoost / LightGBM / scikit-learn

  • 相关性瓶颈:经典 BM25 只能"打基础",很难兼顾业务特征(点击率、销量、评分......)。
  • 端到端成本:把向量检索或深度模型接到 ES 之外,需要额外的服务网关、时延和 DevOps。
  • Elasticsearch 8.12.0 新特性 :官方 Serverless LTR 能直接在 search → rescore 阶段调用训练好的模型,既继承 ES 的伸缩性,又省去了微服务运维。

2 整体流程概览

3 环境准备

bash 复制代码
pip install "elasticsearch>=8.12" eland pandas scikit-learn xgboost lightgbm
export ELASTICSEARCH_URL="http://localhost:9200"
export ELASTIC_PASSWORD="elastic_password"

贴士:离线训练最好用一套隔离的 ES 集群,避免大规模特征抽取冲击线上查询。

4 定义特征抽取模板(QueryFeatureExtractor)

python 复制代码
from eland.ml.ltr import QueryFeatureExtractor, LTRModelConfig

feature_extractors = [
    # 1️⃣  BM25 分数
    QueryFeatureExtractor(
        feature_name="title_bm25",
        query={"match": {"title": "{{query}}"}}
    ),
    # 2️⃣  Title 命中词数
    QueryFeatureExtractor(
        feature_name="title_matched_term_count",
        query={
            "script_score": {
                "query": {"match": {"title": "{{query}}"}},
                "script": {"source": "return _termStats.matchedTermsCount();"}
            }
        }
    ),
    # 3️⃣  直接把文档字段当特征
    QueryFeatureExtractor(
        feature_name="popularity",
        query={
            "script_score": {
                "query": {"exists": {"field": "popularity"}},
                "script": {"source": "return doc['popularity'].value;"}
            }
        }
    ),
    # 4️⃣  查询词长度
    QueryFeatureExtractor(
        feature_name="query_term_count",
        query={
            "script_score": {
                "query": {"match": {"title": "{{query}}"}},
                "script": {"source": "return _termStats.uniqueTermsCount();"}
            }
        }
    ),
]

ltr_config = LTRModelConfig(feature_extractors)

为什么不用自己写脚本?

Eland 与 Elasticsearch 同步开发&测试,可确保训练阶段与线上推理的特征完全一致,避免"训练-服务漂移"。

5 采集训练数据(FeatureLogger)

python 复制代码
from eland.ml.ltr import FeatureLogger
from elasticsearch import Elasticsearch
import pandas as pd

es = Elasticsearch(ELASTICSEARCH_URL, basic_auth=("elastic", ELASTIC_PASSWORD))
MOVIE_INDEX = "movies_demo"

logger = FeatureLogger(es, MOVIE_INDEX, ltr_config)

# 假设我们已有一份人工评判文件 judgements.csv
judgements = pd.read_csv("judgements.csv")  # query, doc_id, relevance

records = []
for _, row in judgements.iterrows():
    feats = logger.extract_features(
        query_params={"query": row.query},
        doc_ids=[row.doc_id]
    )
    record = feats[0].to_dict()
    record["label"] = row.relevance
    records.append(record)

df_train = pd.DataFrame(records)
df_train.to_csv("ltr_train.csv", index=False)

性能提示 :FeatureLogger 会自动合并批量请求、最小化查询次数,但大量特征 × 大量样本仍可能对 ES 造成压力。调低 size 或切分批次能进一步减负。

6 训练 Ranker

以下以 XGBRanker 为例(LightGBM、RandomForest、DecisionTree 均可):

python 复制代码
from xgboost import XGBRanker
import numpy as np

X = df_train[feature_extractors_names].values
y = df_train["label"].values
group = df_train.groupby("query").size().to_numpy()  # 每个 query 的样本数

ranker = XGBRanker(objective="rank:pairwise",
                   n_estimators=300,
                   max_depth=6,
                   learning_rate=0.1)
ranker.fit(X, y, group=group)

7 模型部署一键化(MLModel.import_ltr_model)

python 复制代码
from eland.ml import MLModel

MODEL_ID = "movies_ltr_xgboost_v1"

MLModel.import_ltr_model(
    es_client=es,
    model=ranker,
    model_id=MODEL_ID,
    ltr_model_config=ltr_config,
    es_if_exists="replace"       # 本例重复执行时覆盖
)

执行后,Eland 会自动:

  1. 将模型序列化为 ES 接受的 Eland ML Pack 格式;
  2. 调用 Create Trained Model API 上传;
  3. 生成 inference_config,包含上文 4 个特征查询模板。

8 线上调用:Search + Rescore

json 复制代码
POST movies_demo/_search
{
  "query": {
    "match": {
      "title": "science fiction"
    }
  },
  "rescore": {
    "window_size": 100,
    "query": {
      "rescore_query": {
        "ltr_model": {
          "model_id": "movies_ltr_xgboost_v1",
          "params": { "query": "science fiction" }
        }
      },
      "query_weight": 0.2,
      "rescore_query_weight": 1.0
    }
  }
}
  • window_size 控制重新排序的命中窗口,100-500 较常见;
  • params.query 必须与特征模板中的 {``{query}} 对应;
  • query_weight 越小,模型重排权重越大。

9 模型管理运维

操作 API 说明
查看模型 GET _ml/trained_models/<id> 包含元数据、大小、创建时间
移除旧版 DELETE _ml/trained_models/<id> 勿忘同时清理索引模板里旧 rescore 配置
批量迁移 POST _ml/trained_models/_revert 结合 CI/CD 做蓝绿发布
性能监控 _nodes/stats inference 关注推理延迟 & CPU/内存

10 最佳实践与踩坑

场景 建议
特征库膨胀 控制在 20-40 个以内;复杂逻辑交给模型学习权重,而不是写 N 个脚本评分
交叉验证 使用 group k-fold,确保同一 query 不同时出现在 train&test
线上流量回滚 给旧 BM25 排序保留 20% 流量,异常时快速切换
版本化 movies_ltr_xgboost_v1v2,避免热更新导致 cache 冲突
安全与订阅 开启 HTTPS + token;无服务器版本仅在满足订阅级别时可用

11 总结

借助 8.12.0 新增的 Serverless LTR + Eland:

  • 开发侧 :用 Python 就能完成 特征抽取 → 训练 → 部署 闭环,无需自研特征解析与序列化。
  • 运维侧:模型与索引共生,统一在 ES 集群里监控、热更新,简化了线上链路。
  • 业务侧:短期内即可验证"学习排序 vs 传统 BM25"的收益,并可逐步升级至更复杂的多阶段检索 / 向量召回方案。

开源笔记本示例https://github.com/elastic/elasticsearch-labs/tree/main/notebooks/ltr

赶紧试试把你的点击日志、评分字段喂给 XGBoost,让搜索结果 "更懂业务" 吧!

相关推荐
louyu6668883 分钟前
国产楼宇自控系统哪家靠谱?
大数据·人工智能
天天爱吃肉821825 分钟前
# 商用车多体动力学实战笔记|第7篇:制动系统与制动热衰退、ABS滞环控制
大数据·人工智能·笔记·python·嵌入式硬件·汽车
Elasticsearch1 小时前
使用 Elasticsearch open inference API 对 OpenAI chat completions 进行支持
elasticsearch
风途科技~1 小时前
隧道氮氧化物检测仪|实时监测 NO、NO₂浓度,助力公路隧道通风智能调控
大数据·人工智能
Linux运维技术栈1 小时前
业务不停机、数据零丢失:Redis / RabbitMQ / Elasticsearch 三大核心中间件升级改造集群无缝平滑迁移
redis·elasticsearch·rabbitmq
hzcj8881 小时前
汇正财经:核能项目核准,降碳行动推进
大数据·科技
ACP广源盛139246256732 小时前
Qwen3.8-Max 开源预期下@ACP#企业级终端硬件演进机遇与 PCIe 交换芯片落地分析
大数据·人工智能·分布式·单片机·嵌入式硬件
Elasticsearch3 小时前
15 行点击跟踪代码,告诉你搜索日志无法揭示的信息
elasticsearch
遨游DATA3 小时前
Jenkins 修改 config.xml 后页面仍是旧配置:让 live job 配置真正生效
ci/cd·jenkins·devops·故障排查·config.xml
汽车仪器仪表相关领域4 小时前
NHA-604/605汽车排放气体测试仪:00级国标计量|多动力车型兼容|移动/固定双工况|智能联网尾气检测设备
大数据·人工智能·功能测试·安全·汽车·压力测试·可用性测试