文本分类聚合
编辑文本分类聚合编辑
一种多桶聚合,将半结构化文本分组到桶中。每个 text
字段都使用自定义分析器重新分析。然后对生成的词元进行分类,创建格式相似的文本值的桶。此聚合最适合用于机器生成的文本,例如系统日志。只有前 100 个分析的词元用于对文本进行分类。
用于分类的算法在版本 8.3.0 中完全改变了。因此,此聚合在混合版本集群中不起作用,其中某些节点位于版本 8.3.0 或更高版本,而其他节点位于版本 8.3.0 之前的版本。如果遇到与此更改相关的错误,请将集群中的所有节点升级到相同版本。
参数编辑
-
categorization_analyzer
-
(可选,对象或字符串)分类分析器指定在分类之前如何分析和标记化文本。语法与在 分析端点 中定义
analyzer
所用的语法非常相似。此属性不能与categorization_filters
同时使用。categorization_analyzer
字段可以指定为字符串或对象。如果它是字符串,则它必须引用 内置分析器 或另一个插件添加的分析器。如果它是一个对象,则它具有以下属性categorization_analyzer
的属性-
char_filter
- (字符串或对象的数组)一个或多个 字符过滤器。除了内置字符过滤器之外,其他插件还可以提供更多字符过滤器。此属性是可选的。如果未指定,则在分类之前不应用任何字符过滤器。如果您要自定义分析器的某些其他方面,并且需要实现
categorization_filters
的等效功能(当自定义分析器的某些其他方面时,不允许使用这些功能),请在此处将它们添加为 模式替换字符过滤器。 -
tokenizer
- (字符串或对象)应用字符过滤器后要使用的 标记器 的名称或定义。如果
categorization_analyzer
指定为对象,则此属性是必需的。机器学习提供了一个名为ml_standard
的标记器,它以一种已被确定为对各种日志文件格式(英语日志)产生良好分类结果的方式进行标记化。如果要使用该标记器但要更改字符或标记过滤器,请在categorization_analyzer
中指定"tokenizer": "ml_standard"
。此外,还提供了ml_classic
标记器,它以与旧版本产品(6.2 之前)中的不可自定义标记器相同的方式进行标记化。ml_classic
是版本 6.2 到 7.13 中的默认分类标记器,因此,如果您需要与在这些版本中创建的作业的默认分类相同,请在categorization_analyzer
中指定"tokenizer": "ml_classic"
。
从 Elasticsearch 8.10.0 开始,使用新的版本号来跟踪机器学习插件中的配置和状态更改。此新版本号与产品版本分离,并将独立递增。
-
filter
- (字符串或对象的数组)一个或多个 标记过滤器。除了内置标记过滤器之外,其他插件还可以提供更多标记过滤器。此属性是可选的。如果未指定,则在分类之前不应用任何标记过滤器。
-
-
categorization_filters
- (可选,字符串数组)此属性需要一个正则表达式数组。这些表达式用于从分类字段值中过滤掉匹配的序列。您可以使用此功能通过在定义类别时排除序列来微调分类。例如,您可以排除日志文件中出现的 SQL 语句。此属性不能与
categorization_analyzer
同时使用。如果您只想定义在标记化之前应用的简单正则表达式过滤器,则设置此属性是最简单的方法。如果您还想自定义标记器或标记化后过滤,请改用categorization_analyzer
属性,并将过滤器包含为pattern_replace
字符过滤器。 -
field
- (必需,字符串)要分类的半结构化文本字段。
-
max_matched_tokens
- (可选,整数)此参数现在不起作用,但为了与最初的 8.3.0 之前的实现兼容而允许使用。
-
max_unique_tokens
- (可选,整数)此参数现在不起作用,但为了与最初的 8.3.0 之前的实现兼容而允许使用。
-
min_doc_count
- (可选,整数)将桶返回到结果的最小文档数。
-
shard_min_doc_count
- (可选,整数)合并之前从分片返回桶的最小文档数。
-
shard_size
- (可选,整数)在合并所有结果之前从每个分片返回的分类桶数。
-
similarity_threshold
- (可选,整数,默认值:
70
)要将文本添加到类别桶中,必须匹配的标记权重的最小百分比。必须介于 1 到 100 之间。值越大,类别越窄。较大的值将增加内存使用量并创建更窄的类别。 -
size
- (可选,整数,默认值:
10
)要返回的桶数。
响应正文编辑
-
key
- (字符串)由包含在类别中的输入字段的所有值的通用标记(由
categorization_analyzer
提取)组成。 -
doc_count
- (整数)与类别匹配的文档数。
-
max_matching_length
- (整数)包含少量标记的短消息中的类别也可能与包含来自更长消息的许多标记的类别匹配。
max_matching_length
指示应视为属于该类别的消息的最大长度。在搜索与类别匹配的消息时,应排除任何长度超过max_matching_length
的消息。使用此字段可以防止搜索短消息类别中的成员与更长的消息匹配。 -
regex
- (字符串)一个正则表达式,它将匹配包含在类别中的输入字段的所有值。如果包含在类别中的值之间的顺序不同,则
regex
可能不会包含key
中的每个术语。但是,在简单的情况下,regex
将是有序术语连接成一个正则表达式,该表达式允许它们之间有任意部分。不建议使用regex
作为搜索已分类的原始文档的主要机制。使用正则表达式进行搜索非常慢。相反,应使用key
字段中的术语来搜索匹配的文档,因为术语搜索可以使用倒排索引,因此速度要快得多。但是,在某些情况下,使用regex
字段来测试尚未编入索引的一小组消息是否与类别匹配,或者确认key
中的术语在所有匹配的文档中是否按正确的顺序出现,这可能很有用。
基本用法编辑
示例
POST log-messages/_search?filter_path=aggregations { "aggs": { "categories": { "categorize_text": { "field": "message" } } } }
响应
{ "aggregations" : { "categories" : { "buckets" : [ { "doc_count" : 3, "key" : "Node shutting down", "regex" : ".*?Node.+?shutting.+?down.*?", "max_matching_length" : 49 }, { "doc_count" : 1, "key" : "Node starting up", "regex" : ".*?Node.+?starting.+?up.*?", "max_matching_length" : 47 }, { "doc_count" : 1, "key" : "User foo_325 logging on", "regex" : ".*?User.+?foo_325.+?logging.+?on.*?", "max_matching_length" : 52 }, { "doc_count" : 1, "key" : "User foo_864 logged off", "regex" : ".*?User.+?foo_864.+?logged.+?off.*?", "max_matching_length" : 52 } ] } } }
以下是使用 categorization_filters
的示例
POST log-messages/_search?filter_path=aggregations { "aggs": { "categories": { "categorize_text": { "field": "message", "categorization_filters": ["\\w+\\_\\d{3}"] } } } }
请注意 foo_<number>
标记如何不属于类别结果的一部分
{ "aggregations" : { "categories" : { "buckets" : [ { "doc_count" : 3, "key" : "Node shutting down", "regex" : ".*?Node.+?shutting.+?down.*?", "max_matching_length" : 49 }, { "doc_count" : 1, "key" : "Node starting up", "regex" : ".*?Node.+?starting.+?up.*?", "max_matching_length" : 47 }, { "doc_count" : 1, "key" : "User logged off", "regex" : ".*?User.+?logged.+?off.*?", "max_matching_length" : 52 }, { "doc_count" : 1, "key" : "User logging on", "regex" : ".*?User.+?logging.+?on.*?", "max_matching_length" : 52 } ] } } }
以下是使用 categorization_filters
的示例。默认分析器使用 ml_standard
标记器,它类似于空格标记器,但会过滤掉可以解释为十六进制数字的标记。默认分析器还使用 first_line_with_letters
字符过滤器,以便仅考虑多行消息的第一行有意义的行。但是,某个标记可能是已知的、高度可变的标记(格式化的用户名、电子邮件等)。在这种情况下,最好提供自定义的 categorization_filters
来过滤掉这些标记,以便更好地进行分类。这些过滤器还可以减少内存使用量,因为为类别保存在内存中的标记更少。(如果不同用户名、电子邮件等的示例足够多,则会形成自然地将它们作为变量丢弃的类别,但对于只存在一个示例的小输入数据,则不会发生这种情况。)
POST log-messages/_search?filter_path=aggregations { "aggs": { "categories": { "categorize_text": { "field": "message", "categorization_filters": ["\\w+\\_\\d{3}"], "similarity_threshold": 11 } } } }
生成的类别现在非常广泛,合并了日志组。(similarity_threshold
为 11% 通常过低。设置超过 50% 通常更好。)
{ "aggregations" : { "categories" : { "buckets" : [ { "doc_count" : 4, "key" : "Node", "regex" : ".*?Node.*?", "max_matching_length" : 49 }, { "doc_count" : 2, "key" : "User", "regex" : ".*?User.*?", "max_matching_length" : 52 } ] } } }
此聚合可以同时具有子聚合并自身作为子聚合。这允许收集如下所示的每日热门类别和热门样本文档。
POST log-messages/_search?filter_path=aggregations { "aggs": { "daily": { "date_histogram": { "field": "time", "fixed_interval": "1d" }, "aggs": { "categories": { "categorize_text": { "field": "message", "categorization_filters": ["\\w+\\_\\d{3}"] }, "aggs": { "hit": { "top_hits": { "size": 1, "sort": ["time"], "_source": "message" } } } } } } } }
{ "aggregations" : { "daily" : { "buckets" : [ { "key_as_string" : "2016-02-07T00:00:00.000Z", "key" : 1454803200000, "doc_count" : 3, "categories" : { "buckets" : [ { "doc_count" : 2, "key" : "Node shutting down", "regex" : ".*?Node.+?shutting.+?down.*?", "max_matching_length" : 49, "hit" : { "hits" : { "total" : { "value" : 2, "relation" : "eq" }, "max_score" : null, "hits" : [ { "_index" : "log-messages", "_id" : "1", "_score" : null, "_source" : { "message" : "2016-02-07T00:00:00+0000 Node 3 shutting down" }, "sort" : [ 1454803260000 ] } ] } } }, { "doc_count" : 1, "key" : "Node starting up", "regex" : ".*?Node.+?starting.+?up.*?", "max_matching_length" : 47, "hit" : { "hits" : { "total" : { "value" : 1, "relation" : "eq" }, "max_score" : null, "hits" : [ { "_index" : "log-messages", "_id" : "2", "_score" : null, "_source" : { "message" : "2016-02-07T00:00:00+0000 Node 5 starting up" }, "sort" : [ 1454803320000 ] } ] } } } ] } }, { "key_as_string" : "2016-02-08T00:00:00.000Z", "key" : 1454889600000, "doc_count" : 3, "categories" : { "buckets" : [ { "doc_count" : 1, "key" : "Node shutting down", "regex" : ".*?Node.+?shutting.+?down.*?", "max_matching_length" : 49, "hit" : { "hits" : { "total" : { "value" : 1, "relation" : "eq" }, "max_score" : null, "hits" : [ { "_index" : "log-messages", "_id" : "4", "_score" : null, "_source" : { "message" : "2016-02-08T00:00:00+0000 Node 5 shutting down" }, "sort" : [ 1454889660000 ] } ] } } }, { "doc_count" : 1, "key" : "User logged off", "regex" : ".*?User.+?logged.+?off.*?", "max_matching_length" : 52, "hit" : { "hits" : { "total" : { "value" : 1, "relation" : "eq" }, "max_score" : null, "hits" : [ { "_index" : "log-messages", "_id" : "6", "_score" : null, "_source" : { "message" : "2016-02-08T00:00:00+0000 User foo_864 logged off" }, "sort" : [ 1454889840000 ] } ] } } }, { "doc_count" : 1, "key" : "User logging on", "regex" : ".*?User.+?logging.+?on.*?", "max_matching_length" : 52, "hit" : { "hits" : { "total" : { "value" : 1, "relation" : "eq" }, "max_score" : null, "hits" : [ { "_index" : "log-messages", "_id" : "5", "_score" : null, "_source" : { "message" : "2016-02-08T00:00:00+0000 User foo_325 logging on" }, "sort" : [ 1454889720000 ] } ] } } } ] } } ] } } }