用 Eland 在 Elasticsearch Serverless 部署 Learning-to-Rank 排序模型

发布于:2025-08-05 ⋅ 阅读:(16) ⋅ 点赞:(0)

1 LTR 流程总览

阶段 关键产物 工具
离线 Judgment List → 特征 → 训练集 → model.json Pandas / scikit-learn / XGBoost
导入 MLModel.import_ltr_model() Eland
在线 retriever 召回 + text_similarity_reranker 重排 Search API

2 准备数据

2.1 Judgment List

qid  doc_id   relevance
1001  a12      4
1001  b77      3
1001  c02      0
...
  • 来源:点击/下单日志 + 人工标注
  • 平衡性:不同查询类型、正负样本比例要均衡

2.2 在 Python 里声明特征抽取器

from eland.ml.ltr import QueryFeatureExtractor

feature_extractors = [
    # title BM25 分数
    QueryFeatureExtractor(
        feature_name="title_bm25",
        query={"match": {"title": "{{query}}"}}
    ),
    # title 命中词个数
    QueryFeatureExtractor(
        feature_name="title_matched_terms",
        query={
            "script_score": {
              "query": {"match": {"title": "{{query}}"}},
              "script": {"source": "return _termStats.matchedTermsCount();"}
            }
        },
    ),
    # 文档属性:popularity
    QueryFeatureExtractor(
        feature_name="popularity",
        query={
            "script_score": {
              "query": {"exists": {"field":"popularity"}},
              "script": {"source": "return doc['popularity'].value;"}
            }
        },
    ),
]

打包成配置对象:

from eland.ml.ltr import LTRModelConfig
ltr_config = LTRModelConfig(feature_extractors)

2.3 批量抽特征

from eland import Elasticsearch
from eland.ml.ltr import FeatureLogger

es = Elasticsearch("https://<endpoint>", api_key="...")
logger = FeatureLogger(es, index="products", ltr_config=ltr_config)

# 针对单个 query 抽取部分文档的特征
df = logger.extract_features(
        query_params={"query": "wireless headset"},
        doc_ids=["doc-1","doc-9","doc-42"])

最佳实践:在 影子集群 做 feature logging,防止大批量请求影响线上搜索。

3 训练 XGBRanker

import xgboost as xgb
# 读取 DF => numpy
X = df[["title_bm25", "title_matched_terms", "popularity"]].values
y = df["relevance"].values
group = df.groupby("qid").size().to_list()  # 每个 query 的样本数

dtrain = xgb.DMatrix(X, label=y)
dtrain.set_group(group)

params = {
    "objective": "rank:ndcg",
    "eval_metric": "ndcg@10",
    "eta": 0.1,
    "max_depth": 6,
    "n_estimators": 200,
}
ranker = xgb.train(params, dtrain)
ranker.save_model("ltr_model.json")

4 Eland 导入模型

from eland.ml import MLModel

MLModel.import_ltr_model(
    es_client=es,
    model=ranker,
    model_id="ltr-wireless-v1",
    ltr_model_config=ltr_config,
    es_if_exists="replace"          # 已存在则覆盖
)

导入过程做了两件事:

  1. ltr_config(特征模板)与 XGBoost 模型打包。
  2. 调用 Create Trained Model API 把模型存入 .ml-inference-* 索引,并自动创建推理端点。

上线后可在 DevTools 验证:

GET _inference/_deployments/ltr-wireless-v1/_stats

5 在线检索 + LTR 重排

POST /products/_search
{
  "size": 10,
  "retriever": {                      # Stage-1 召回
    "rrf": {
      "retrievers": [
        { "standard": {               # BM25
          "query": { "match": { "title": "wireless headset" } },
          "k": 200 }
        },
        { "standard": {               # 语义稀疏向量
          "query": { "semantic": {
            "field": "semantic_text",
            "query": "wireless headset" } },
          "k": 200 }
        }
      ]
    }
  },
  "reranker": {                       # Stage-2 LTR
    "text_similarity_reranker": {
      "model_id": "ltr-wireless-v1",
      "field": "{{{features}}}",      # Eland 自动注入多特征模板
      "max_passages": 10
    }
  }
}
  • k=200:先召回 200 条,提供给重排;
  • 模板 {{{features}}}:Eland 生成的隐藏字段,服务端展开为多段 DSL,提取特征并送给模型。

6 模型管理与灰度

操作 API or 工具
查看已部署版本 GET _inference/_deployments/*
升级 v2 并灰度 MLModel.import_ltr_model(..., model_id="ltr-wireless-v2")
给部分流量切换到 v2 端点
回滚 DELETE _inference/_deployments/ltr-wireless-v2

可以通过 Search Template 参数化 model_id,由 AB proxy 控制分流,完成无损灰度。

7 常见坑与调优

问题 解决方案
线上返回 "Failed to load model" 端点未 startPOST /_inference/_deployments/<id>/_start
特征数量对不上 保证离线 ltr_config 与线上一致;import_ltr_model 会自动校验
QPS 降低 适当降低 k;或在 Elastic Cloud 升级 ML Node 规格
模型大小 > 20 MB 调大 num_parallel_tree / max_depth 会指数增加模型体积,线上推理内存需同步加大

结语

  • Eland = 特征模板 + 数据提取 + 导入模型 的官方胶水层,让 LTR 在 Elastic 全家桶里“无缝衔接”。
  • 借助 XGBoost LambdaMART,精排 100 条只需数毫秒,能轻松嵌入任何检索流程作为二阶段 Re-rank。
  • 当业务迭代、特征新增时,重复 3 行 Python 即可快速上线新模型,保持搜索体验常新。

赶紧把你的点击日志喂给 Notebook,五分钟后就能在结果页看到“AI 调味”的 Top 10!


网站公告

今日签到

点亮在社区的每一天
去签到