高级功能
OpenSearch Learning to Rank (LTR) 提供了额外的功能。建议您在开始使用这些功能之前,先对 OpenSearch LTR 有一个基础的了解。
可重用功能
构建功能涉及上传功能列表。为避免在多个功能集中重复使用常见功能,您可以维护一个可重用功能的库。
例如,如果标题字段查询在您的功能集中频繁使用,则可以使用功能 API 创建一个可重用的标题查询
POST _ltr/_feature/titleSearch
{
"feature":
{
"params": [
"keywords"
],
"template": {
"match": {
"title": ""
}
}
}
}
常规的 CRUD 操作均适用,因此您可以使用以下操作删除功能
DELETE _ltr/_feature/titleSearch
要获取单个功能,您可以使用以下请求
GET _ltr/_feature/titleSearch
要查看按名称前缀过滤的所有功能列表,您可以使用以下请求
GET /_ltr/_feature?prefix=t
要创建或更新功能集,您可以使用以下请求引用 titleSearch
功能
POST /_ltr/_featureset/my_featureset/_addfeatures/titleSearch
这将 titleSearch
功能添加到 my_featureset
功能集中的下一个序数位置。
派生功能
派生功能是建立在其他功能之上的功能。它们可以表示为 Lucene 表达式,并通过 "template_language": "derived_expression"
进行标识。
此外,派生功能可以接受 Number
类型的查询时变量,如创建功能集中所述。
脚本功能
脚本功能是一种派生功能。这些功能可以访问 feature_vector
,但它们是作为原生或 Painless OpenSearch 脚本实现的,而不是作为 Lucene 表达式。
要识别这些功能,请设置 "template_language": "script_feature""
。自定义脚本可以通过 Java Map 访问 feature_vector
,如创建功能集中所述。
基于脚本的功能可能会影响 OpenSearch 集群的性能,因此如果您需要高性能查询,最好避免使用它们。
脚本功能参数
脚本功能是 LTR 上下文中的原生或 Painless 脚本。这些脚本功能可以接受参数,如 OpenSearch 脚本文档中所述。在使用 LTR 脚本时,您可以覆盖参数值和名称。参数化的优先级(按递增顺序)如下
- 参数名称和值直接传递给源脚本,但不在 LTR 脚本参数中。这些不能在查询时配置。
- 参数名称同时传递给
sltr
查询和源脚本,允许在查询时覆盖脚本参数值。 - LTR 脚本参数名称到原生脚本参数名称的间接引用,允许您在 LTR 功能定义中使用与底层原生脚本中不同的参数名称。这为您在 LTR 上下文中定义和使用脚本提供了灵活性。
例如,要设置一种可自定义的方式来对搜索结果中的电影进行排序,同时考虑标题匹配和其他可调整因素,您可以使用以下请求
POST _ltr/_featureset/more_movie_features
{
"featureset": {
"features": [
{
"name": "title_query",
"params": [
"keywords"
],
"template_language": "mustache",
"template": {
"match": {
"title": ""
}
}
},
{
"name": "custom_title_query_boost",
"params": [
"some_multiplier",
"ltr_param_foo"
],
"template_language": "script_feature",
"template": {
"lang": "painless",
"source": "(long)params.default_param * params.feature_vector.get('title_query') * (long)params.some_multiplier * (long) params.param_foo",
"params": {
"default_param": 10,
"some_multiplier": "some_multiplier",
"extra_script_params": {
"ltr_param_foo": "param_foo"
}
}
}
}
]
}
}
多个功能存储
功能存储对应一个独立的 LTR 系统,包括功能、功能集和由单个索引和缓存支持的模型。功能存储通常代表一个单一的搜索问题或应用,如 Wikipedia 或 Wiktionary。要在 OpenSearch 集群中使用多个功能存储,您可以使用提供的 API 创建和管理它们。例如,您可以为 wikipedia
功能存储创建功能集,如下所示
PUT _ltr/wikipedia
POST _ltr/wikipedia/_featureset/attempt_1
{
"featureset": {
"features": [
{
"name": "title_query",
"params": [
"keywords"
],
"template_language": "mustache",
"template": {
"match": {
"title": ""
}
}
}
]
}
}
在记录功能时,您可以在查询的 sltr
部分使用 store
参数指定功能存储,如下例结构所示。如果您未提供 store
参数,则使用默认存储来查找功能集。
{
"sltr": {
"_name": "logged_featureset",
"featureset": "attempt_1",
"store": "wikipedia",
"params": {
"keywords": "star"
}
}
}
要删除功能集,您可以使用以下操作
DELETE _ltr/wikipedia/_featureset/attempt_1
模型缓存
模型缓存插件使用内部缓存来存储编译后的模型。要强制重新编译模型,您可以清除功能存储的缓存
POST /_ltr/_clearcache
要获取特定存储的集群范围缓存统计信息,请使用以下请求
GET /_ltr/_cachestats
您可以通过使用以下节点设置来控制内部缓存的特性
# limit cache usage to 12 megabytes (defaults to 10mb or max_heap/10 if lower) ltr.caches.max_mem: 12mb
# Evict cache entries 10 minutes after insertion (defaults to 1hour, set to 0 to disable) ltr.caches.expire_after_write: 10m
# Evict cache entries 10 minutes after access (defaults to 1hour, set to 0 to disable) ltr.caches.expire_after_read: 10m
额外日志记录
如日志记录功能中所述,您可以使用日志扩展来返回每个文档的功能值。对于原生脚本,您还可以随记录的功能一起返回其他任意信息。
对于原生脚本,extra_logging
参数会被注入到脚本参数中。此参数是一个 Supplier<Map<String,Object>>
,它仅在日志获取阶段提供一个非空的 Map<String,Object>
。您添加到此映射中的任何值都将与记录的功能一起返回
{
@Override
public double runAsDouble() {
...
Map<String,Object> extraLoggingMap = ((Supplier<Map<String,Object>>) getParams().get("extra_logging")).get();
if (extraLoggingMap != null) {
extraLoggingMap.put("extra_float", 10.0f);
extraLoggingMap.put("extra_string", "additional_info");
}
...
}
}
如果访问了额外日志映射,它将作为记录功能的一个额外条目返回。记录功能的格式,包括额外日志信息,将类似于以下示例
{
"log_entry1": [
{
"name": "title_query",
"value": 9.510193
},
{
"name": "body_query",
"value": 10.7808075
},
{
"name": "user_rating",
"value": 7.8
},
{
"name": "extra_logging",
"value": {
"extra_float": 10.0,
"extra_string": "additional_info"
}
}
]
}
功能分数缓存
默认情况下,功能分数缓存插件会为模型推理和功能分数日志记录计算功能分数。例如,如果您编写一个查询来重新评分前 100 个文档并返回前 10 个带有功能分数的文档,那么该插件会计算前 100 个文档的功能分数用于模型推理,然后计算并记录前 10 个文档的分数。
以下查询显示此行为
POST tmdb/_search
{
"size": 10,
"query": {
"match": {
"_all": "rambo"
}
},
"rescore": {
"window_size" : 100,
"query": {
"rescore_query": {
"sltr": {
"params": {
"keywords": "rambo"
},
"model": "my_model"
}
}
}
},
"ext": {
"ltr_log": {
"log_specs": {
"name": "log_entry1",
"rescore_index": 0
}
}
}
}
在某些环境中,缓存模型推理的功能分数并将其用于日志记录可能会更快。要启用功能分数缓存,请在作为功能分数日志记录目标的 sltr
查询中添加 cache: "true"
标志,如下例所示
{
"sltr":{
"cache":true,
"params":{
"keywords":"rambo"
},
"model":"my_model"
}
}
统计
您可以使用 Stats API 检索插件的整体状态和统计信息。为此,请发送以下请求
GET /_ltr/_stats
响应包括有关集群、已配置存储以及各种插件组件的缓存统计信息
{
"_nodes":{
"total":1,
"successful":1,
"failed":0
},
"cluster_name":"es-cluster",
"stores":{
"_default_":{
"model_count":10,
"featureset_count":1,
"feature_count":0,
"status":"green"
}
},
"status":"green",
"nodes":{
"2QtMvxMvRoOTymAsoQbxhw":{
"cache":{
"feature":{
"eviction_count":0,
"miss_count":0,
"hit_count":0,
"entry_count":0,
"memory_usage_in_bytes":0
},
"featureset":{
"eviction_count":0,
"miss_count":0,
"hit_count":0,
"entry_count":0,
"memory_usage_in_bytes":0
},
"model":{
"eviction_count":0,
"miss_count":0,
"hit_count":0,
"entry_count":0,
"memory_usage_in_bytes":0
}
}
}
}
}
您可以通过发送以下请求,使用过滤器检索单个统计信息
GET /_ltr/_stats/{stat}
您可以通过发送以下请求,将信息限制到集群中的单个节点
GET /_ltr/_stats/nodes/{nodeId}
GET /_ltr/_stats/{stat}/nodes/{nodeId}
术语统计查询
实验性
TermStatQuery
处于实验阶段,其领域特定语言 (DSL) 可能会随着代码的演进而改变。要访问稳定的术语统计信息,请参见 [ExplorerQuery]{.title-ref}。
TermStatQuery
是旧版 ExplorerQuery
的重新构想版本。它提供了一种更清晰的方式来指定术语,并为实验提供了更大的灵活性。此查询显示的数据与 [ExplorerQuery]{.title-ref} 相同,但它允许您指定自定义 Lucene 表达式来检索所需数据,例如以下示例
POST tmdb/_search
{
"query": {
"term_stat": {
"expr": "df",
"aggr": "max",
"terms": ["rambo", "rocky"],
"fields": ["title"]
}
}
}
expr
参数用于指定 Lucene 表达式。此表达式按术语运行。表达式可以是简单的统计类型,也可以是包含多个统计类型的自定义公式,例如 (tf * idf) / 2
。Lucene 表达式上下文中可用的统计类型如下表所示。
类型 | 描述 |
---|---|
df | 术语的直接文档频率。例如,如果 rambo 在多个文档的三个电影标题中出现,则该值为 3 。 |
idf | 使用公式 log((NUM_DOCS+1)/(raw_df+1)) + 1 计算逆文档频率 (IDF)。 |
tf | 文档的术语频率。例如,如果 rambo 在同一文档的电影概要中出现三次,则该值为 3 。 |
tp | 文档的术语位置。单个术语可以返回多个位置,因此您应该查看 pos_aggr 参数的行为。 |
ttf | 术语在整个索引中的总术语频率。例如,如果 rambo 在所有文档的 overview 字段中总共被提及 100 次,则该值为 100 。 |
aggr
参数指定要应用于从 expr
收集的统计信息的聚合类型。例如,如果您指定术语 rambo
和 rocky
,则查询会收集这两个术语的统计信息。由于您只能返回单个值,因此需要决定使用哪种统计计算。可用的聚合类型有 min
(最小值)、max
(最大值)、avg
(平均值)、sum
(总和)和 stddev
(标准差)。查询还提供以下计数:matches
(当前文档中匹配的术语数量)和 unique
(查询中传递的唯一术语数量)。
terms
参数指定一个术语数组,您希望为其收集统计信息。仅支持单个术语,不支持短语或跨度查询。如果您的字段已分词,则可以在数组中以一个字符串的形式传递多个术语。
fields
参数指定要检查指定 terms
的字段。如果未指定 analyzer
,则使用每个字段配置的 search_analyzer
。
可选参数如下表所示。
类型 | 描述 |
---|---|
分析器 | 如果指定,则使用此分析器而不是每个字段配置的 search_analyzer 。 |
pos_aggr | 由于每个术语可以有多个位置,您可以使用此参数指定要应用于术语位置的聚合。它支持与 aggr 参数相同的值,并默认为 avg 。 |
脚本注入
脚本注入提供了将术语统计信息注入脚本上下文的能力。在使用 ScriptFeatures
时,您可以传递一个带有 terms
、fields
和 analyzer
参数的 term_stat
对象。然后,名为 termStats
的注入变量将提供对自定义脚本中原始值的访问。这通过让您访问所有底层数据来实现高级功能工程。
要访问匹配词元的计数,请使用 [params.matchCount.get
]{.title-ref}。要访问唯一词元的计数,请使用 [params.uniqueTerms
]{.title-ref}。
您可以在脚本定义中硬编码 term_stat
参数,也可以传递要在查询时设置的参数。例如,以下示例查询定义了一个功能集,其中包含一个使用硬编码 term_stat
参数的脚本功能
POST _ltr/_featureset/test
{
"featureset": {
"features": [
{
"name": "injection",
"template_language": "script_feature",
"template": {
"lang": "painless",
"source": "params.termStats['df'].size()",
"params": {
"term_stat": {
"analyzer": "!standard",
"terms": ["rambo rocky"],
"fields": ["overview"]
}
}
}
}
]
}
}
在本地指定分析器名称时,必须在其前面加上感叹号 (!)。否则,它们将被视为参数查找值。
要设置参数查找,您可以传递要从中提取值的参数名称,如下例请求所示
POST _ltr/_featureset/test
{
"featureset": {
"features": [
{
"name": "injection",
"template_language": "script_feature",
"template": {
"lang": "painless",
"source": "params.termStats['df'].size()",
"params": {
"term_stat": {
"analyzer": "analyzerParam",
"terms": "termsParam",
"fields": "fieldsParam"
}
}
}
}
]
}
}
或者,您可以将 term_stat
参数作为查询时参数传递,如下例请求所示
POST tmdb/_search
{
"query": {
"bool": {
"filter": [
{
"terms": {
"_id": ["7555", "1370", "1369"]
}
},
{
"sltr": {
"_name": "logged_featureset",
"featureset": "test",
"params": {
"analyzerParam": "standard",
"termsParam": ["troutman"],
"fieldsParam": ["overview"]
}
}}
]
}
},
"ext": {
"ltr_log": {
"log_specs": {
"name": "log_entry1",
"named_query": "logged_featureset"
}
}
}
}