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" # 已存在则覆盖
)
导入过程做了两件事:
- 将
ltr_config
(特征模板)与 XGBoost 模型打包。 - 调用 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" |
端点未 start ;POST /_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!