Link Search Menu Expand Document Documentation Menu

高级功能

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 收集的统计信息的聚合类型。例如,如果您指定术语 ramborocky,则查询会收集这两个术语的统计信息。由于您只能返回单个值,因此需要决定使用哪种统计计算。可用的聚合类型有 min(最小值)、max(最大值)、avg(平均值)、sum(总和)和 stddev(标准差)。查询还提供以下计数:matches(当前文档中匹配的术语数量)和 unique(查询中传递的唯一术语数量)。

terms 参数指定一个术语数组,您希望为其收集统计信息。仅支持单个术语,不支持短语或跨度查询。如果您的字段已分词,则可以在数组中以一个字符串的形式传递多个术语。

fields 参数指定要检查指定 terms 的字段。如果未指定 analyzer,则使用每个字段配置的 search_analyzer

可选参数如下表所示。

类型 描述
分析器 如果指定,则使用此分析器而不是每个字段配置的 search_analyzer
pos_aggr 由于每个术语可以有多个位置,您可以使用此参数指定要应用于术语位置的聚合。它支持与 aggr 参数相同的值,并默认为 avg

脚本注入

脚本注入提供了将术语统计信息注入脚本上下文的能力。在使用 ScriptFeatures 时,您可以传递一个带有 termsfieldsanalyzer 参数的 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"
            }
        }
    }
}

剩余 350 字符

有问题?

想做贡献?