深入理解 significant terms 和 significant text 分组聚集
本文我们学习significant terms 和 significant text 聚集。它们目的是搜索数据集中有趣的和/或不寻常的词汇,这些词汇可以告诉你很多关于数据隐藏属性的信息。应用场景主要有:
-
根据用户查询包括的同义词、缩写词标识相关文档。举例,显著关键词(significant terms)聚集实现当用户搜“H1N1”时建议“禽流感”相关文档。
-
识别数据中的异常和有趣的事件。例如,通过根据位置过滤文档,我们可以识别特定地区最常见的犯罪类型。
-
对整型字段(如height, weight, income等)使用显著关键词聚集,可以识别一组主题的重要属性。
需要提醒的是,显著关键词和显著文本聚集都需要对直接查询返回文档(前端数据集)和索引中的其他文档(后端数据集)执行复杂统计计算。因此两个聚集都是计算密集型的,需要正确配置才能高效运行,一旦通过学习掌握了其中要的,你将获得在应用中构建有用特性的强大工具,从数据中获得特到的见解。让我们开始吧。
1. 环境准备
为了演示我们创建news
索引存储一组新闻。其映射包括作者、发布日期、标题、浏览量、主题。创建脚本如下:
PUT /news
{
"mappings": {
"properties": {
"published": {
"type": "date",
"format": "dateOptionalTime"
},
"author": {
"type": "keyword"
},
"title": {
"type": "text"
},
"topic": {
"type":"keyword"
},
"views": {
"type": "integer"
}
}
}
}
topic 和 author是keyword类型,title是text类型。keyword类型只能进行精确搜,而text字段可进行全文检索。
批量插入演示数据:
POST /news/_bulk
{"index":{}}
{"author":"John Michael", "published":"2018-07-08", "title":"Tesla is flirting with its lowest close in over 1 1/2 years (TSLA)", "topic":"automobile","views":"431" }
{"index":{}}
{"author":"John Michael", "published":"2018-07-22", "title":"Tesla to end up like Lehman Brothers (TSLA)", "topic":"automobile", "views":"1921" }
{"index":{}}
{"author":"John Michael", "published":"2018-07-29", "title":"Tesla (TSLA) official says that they are going to release a new self-driving car model in the coming year", "topic":"automobile", "views":"1849" }
{"index":{}}
{"author":"John Michael", "published":"2018-08-14", "title":"Five ways Tesla uses AI and Big Data", "topic":"ai", "views":"871" }
{"index":{}}
{"author":"John Michael", "published":"2018-08-14", "title":"Toyota partners with Tesla (TSLA) to improve the security of self-driving cars", "topic":"automobile", "views":"871" }
{"index":{}}
{"author":"Robert Cann", "published":"2018-08-25", "title":"Is AI dangerous for humanity", "topic":"ai", "views":"981" }
{"index":{}}
{"author":"Robert Cann", "published":"2018-09-13", "title":"Is AI dangerous for humanity", "topic":"ai", "views":"871" }
{"index":{}}
{"author":"Robert Cann", "published":"2018-09-27", "title":"Introduction to Generative Adversarial Networks (GANs) in self-driving cars", "topic":"automobile", "views":"1183" }
{"index":{}}
{"author":"Robert Cann", "published":"2018-10-09", "title":"Introduction to Natural Language Processing", "topic":"ai", "views":"786" }
{"index":{}}
{"author":"Robert Cann", "published":"2018-10-15", "title":"New Distant Objects Found in the Fight for Planet X ", "topic":"astronomy", "views":"542" }
另一个sports索引,具体字段设置如下:
PUT /sports
{
"mappings": {
"properties": {
"birthdate": {
"type": "date",
"format": "dateOptionalTime"
},
"location": {
"type": "geo_point"
},
"name": {
"type": "keyword"
},
"rating": {
"type": "integer"
},
"sport": {
"type": "keyword"
},
"age": {
"type":"integer"
},
"goals": {
"type": "integer"
},
"role": {
"type":"keyword"
},
"score_weight": {
"type": "float"
}
}
}
}
插入几条示例数据,完整数据可以在这里下载:
POST /sports/_bulk
{"index":{}}
{"name":"Michael", "birthdate":"1989-10-1", "sport":"Football", "rating": ["5", "4"], "location":"46.22,-68.45", "age":"23","goals": "43","score_weight":"3","role":"midfielder"}
{"index":{}}
{"name":"Bob", "birthdate":"1989-11-2", "sport":"Football", "rating": ["3", "4"], "location":"45.21,-68.35", "age":"33", "goals": "54","score_weight":"2", "role":"forward"}
{"index":{}}
{"name":"Jim", "birthdate":"1988-10-3", "sport":"Football", "rating": ["3", "2"], "location":"45.16,-63.58", "age":"28", "goals": "73", "score_weight":"2", "role":"forward" }
2. 显著关键词聚集
前面提及显著关键词聚集用于识别搜索数据集中有趣的和/或不寻常的词汇,主要用于下列场景:
-
识别与用户查询相关关键词/文档。举例,当用户查询“中国”,聚集建议词组“北京”、“长城”,或其他通常与“中国”相关联的关键词。
-
显著关键词聚集可用于自动新闻分类,基于频繁连接的关键图进行分类。
-
发现异常数据。举例,识别特定地理区域的异常犯罪类型或疾病。
需要提醒重要一点,通过显著关键词聚集不是简单在文档集合中选择最流行的关键词。举例,在1000万文档中缩写词"MSFT" 仅仅只有10个文档,如果在50个文档中有10个文档匹配用户的“Microsoft”查询,那么它仍然是相关的。这个频率也可以使得缩写词语用户搜索相关。
为了识别显著关键词,聚集在查询匹配结果及搜索索引上执行复杂统计分析。直接匹配的查询结果表示前端数据集,而从中返回结果的索引称为后端数据集。显著关键词聚集的任务就是比较这些数据集并发现与用户查询最相关的关键词。
听起来很抽象? 下面通过实际示例进行说明。我们尝试在索引中查询每个作者典型的主题。为了演示首先对author
字段使用关键词分组聚集,关键词分组聚集构建索引中所有唯一关键词进行分组。接下来对topic
字段使用显著关键词聚集发现每个作者最典型的主题。查询语句如下:
GET /news/_search?size=0
{
"aggs": {
"authors": {
"terms": {"field": "author"},
"aggs": {
"significant_topic_types": {
"significant_terms": {"field": "topic"}
}
}
}
}
}
我们使用简单配置使用significant_terms
聚集,仅使用一个field参数,响应结果如下:
{
"took" : 146,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 10,
"relation" : "eq"
},
"max_score" : null,
"hits" : [ ]
},
"aggregations" : {
"authors" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [
{
"key" : "John Michael",
"doc_count" : 5,
"significant_topic_types" : {
"doc_count" : 5,
"bg_count" : 10,
"buckets" : [
{
"key" : "automobile",
"doc_count" : 4,
"score" : 0.4800000000000001,
"bg_count" : 5
}
]
}
},
{
"key" : "Robert Cann",
"doc_count" : 5,
"significant_topic_types" : {
"doc_count" : 5,
"bg_count" : 10,
"buckets" : [
{
"key" : "ai",
"doc_count" : 3,
"score" : 0.2999999999999999,
"bg_count" : 4
}
]
}
}
]
}
}
}
从结果看AI
对Robert Cann是最典型的主题关键词,Automobile
是John Michael最典型的关键字。你可以验证两位作者都发表过其他主题的论文,但他们大多专注于AI
和Automobile
。
在上面的响应中,您可以看到每个词分组包含该词的评分。这些分值是根据前端和后端数据集中的文档频率计算出来的。简而言之,如果一个关键词出现在前端和后端数据集中的频率有明显的不同,那么这个关键词就被认为是显著的。
显著关键词聚集也可以在integer
字段上识别显著关键词。下载示例在sports索引上识别每种运行类型上最典型的年龄:
GET /sports/_search?size=0
{
"aggs": {
"authors": {
"terms": {"field": "sport"},
"aggs": {
"significant_topic_types": {
"significant_terms": {
"field": "age",
"min_doc_count": 2
}
}
}
}
}
}
我们设置了两个参数"field": "age", "min_doc_count": 2
,因此每个关键词至少有两个文档满足才创建分组。注意在高基数文档中,设置min_doc_count
较小值可能会导致返回各种不重要的术语,如介词和冠词被标记为重要的术语。但在我们的示例中文档不多,设置为2是比较合适:
{
"took" : 534,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 22,
"relation" : "eq"
},
"max_score" : null,
"hits" : [ ]
},
"aggregations" : {
"authors" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [
{
"key" : "Football",
"doc_count" : 9,
"significant_topic_types" : {
"doc_count" : 9,
"bg_count" : 22,
"buckets" : [ ]
}
},
{
"key" : "Basketball",
"doc_count" : 5,
"significant_topic_types" : {
"doc_count" : 5,
"bg_count" : 22,
"buckets" : [ ]
}
},
{
"key" : "Hockey",
"doc_count" : 5,
"significant_topic_types" : {
"doc_count" : 5,
"bg_count" : 22,
"buckets" : [ ]
}
},
{
"key" : "Handball",
"doc_count" : 3,
"significant_topic_types" : {
"doc_count" : 3,
"bg_count" : 22,
"buckets" : [
{
"key" : 29,
"doc_count" : 2,
"score" : 4.222222222222221,
"bg_count" : 2
}
]
}
}
]
}
}
}
从结果可得到:20岁是篮球、足球和曲棍球最常见的年龄,29岁是手球最常见的年龄。
注意,您只能在整数字段上使用重要的术语聚合。目前不支持浮点字段。这是因为整数或长字段可以用来表示分析感兴趣的概念(例如,年龄、银行账号),而浮动字段通常表示某物的数量。因此,对于这种类型的频率分析,单独的浮点项不是很有用。
2.1. 在自由文字字段上使用显著关键词聚集
significant_terms
聚集也能用在可分词的自由文本字段上,实现提炼用户搜并为过滤查询建议关键词,但一般不建议这样。对于自由文本字段使用显著关键词聚集分析计算非常密集,因为聚集需加载每个唯一分词至RAM中。如果索引数据量不大不是问题,故对于text
字段,使用显著文本聚集相比于显著关键词聚集更好。
我们示例索引数据非常小,运行示例对内存不是问题。下面我们继续,对自由文字字段使用显著关键词聚集分析,识别与查询包含"自动驾驶"相关的有趣关键词。和前面示例一样,我们设置min_doc_count
较大,排除不显著的关键词,如介词和冠词:
GET /news/_search?size=0
{
"query" : {
"match" : {"title" : "self-driving"}
},
"aggregations": {
"keywords" : {
"significant_text" : {
"field" : "title",
"min_doc_count":2
}
}
}
}
对于text字段需要设置fielddata
为true:
PUT /news/_mapping
{
"properties": {
"title": {
"type": "text",
"fielddata": true
}
}
}
返回结果包括:
{
"key" : "tsla",
"doc_count" : 2,
"score" : 0.4444444444444443,
"bg_count" : 4
},
{
"key" : "tesla",
"doc_count" : 2,
"score" : 0.22222222222222215,
"bg_count" : 5
}
我们看到返回结果中与自动驾驶相关有的tesla等。但前面我们提过,如果对于自由文本字段,最好使用显著文本聚集分析方法,下面我们开始讨论。
2.2. 子定义后端数据集
缺省情况下,将前台集与索引中所有文档作为后端数据集进行比较。但是在一些场景中,需要构建更精确的后端数据集进行比较。举例,当用户在所有文档中搜索北京时,查询结果获得中国作为显著关键词,虽然是对的,但你可能希望获得更好的关键词。这时你能应用background_filter
设置“中国”从而限制后端数据集。限制“中国”作为显而易见的,不属于显著关键词。需要了解的是使用background_filter
会影响性能。示例代码如下:
GET /_search?size=0
{
"query" : {
"match" : {
"city" : "paris"
}
},
"aggs" : {
"tags" : {
"significant_terms" : {
"field" : "tag",
"background_filter": {
"term" : { "text" : "france"}
}
}
}
}
}
显著关键词聚集将返回首先由索引的每个分片提供、然后由聚集进行细化形成最佳匹配的词汇分组。size
参数用于控制从完整的关键词分组中返回关键词分组数量。但如果唯一关键词数量大于你设置的大小,则返回列表则不够精确。
为了确保返回列表精确,可以将size参数与shard_size成对使用。shard_size 参数控制每个分片产生的候选关键词量。如果你对低频关键词感兴趣,可以设置shard_size 的值明显高于size值,这确保大量有希望的候选关键词。如果shard_size设置为-1(默认值),那么shard_size将根据分片的数量和size参数自动估计。
3. 显著文本聚集
显著文本聚集主要用于在自由文本字段上识别显著关键词。在大规模数据集上应用时需要大量的时间和内存,因此通常建议significant_text
聚集作为sampler 或 diversified sampler
聚集的子集,使得分析被限制在较小选择集上。
significant text
聚集和 significant terms
聚集的主要差异有:
- 设计用于
text
字段 - 不需要doc-values或field data
- 它可以动态地重新分析文本,意味着聚合还可以过滤重复的有噪声的文本,否则这些文本可能会歪曲统计数据。
与significant terms
聚集类似,significant text
聚集在前端数据集和后端数据集上执行词频统计分析,请看示例:
GET /news/_search?size=0
{
"query" : {
"match" : {"title" : "TSLA"}
},
"aggregations" : {
"my_sample" : {
"sampler" : {
"shard_size" : 100
},
"aggregations": {
"keywords" : {
"significant_text" : { "field" : "title","min_doc_count":4 }
}
}
}
}
}
这里在sampler聚集内使用significant_text
聚集,将分析限制在最佳匹配的抽样集合上。示例索引比较小无需抽样,但你应该记住当数据量较大时应该使用在抽象聚集内部使用。响应如下:
"aggregations" : {
"my_sample" : {
"doc_count" : 4,
"keywords" : {
"doc_count" : 4,
"bg_count" : 10,
"buckets" : [
{
"key" : "tsla",
"doc_count" : 4,
"score" : 1.5,
"bg_count" : 4
},
{
"key" : "tesla",
"doc_count" : 4,
"score" : 1.0,
"bg_count" : 5
}
]
}
}
}
结果表明,“tesla”关键词与股票首字母缩略词TSLA密切相关。
3.1 排除噪声数据
自由文本字段中的噪声数据可能包括剪切和粘贴段落、页眉/页脚、边栏新闻、地址等的引用模板。当一些冗长的文本被许多资源剪切和粘贴时,这种情况经常发生,因此,任何与查询不相关但包含在粘贴的片段中的罕见名称或数字都与匹配的查询在统计上相关。
显著文本聚集可以使用过滤删除已经发现的任何6个或以上词语序列。如果文本包括完整的html页面,可能有很多重复的噪声数据。Elasticsearch可以使用filter_duplicate_text参数动态清理数据。举例:
GET /_search?size=0
{
"query": {
"match": {
"content": "elasticsearch"
}
},
"aggs": {
"sample": {
"sampler": {
"shard_size": 100
},
"aggs": {
"keywords": {
"significant_text": {
"field": "content",
"filter_duplicate_text": true
}
}
}
}
}
}
显著文本聚集有下列限制:
-
不支持子聚集,这种限制是有意的,因为使用子聚集会有很高的内存成本。处理此限制的建议方法是使用包含include子句的term聚集进行后续查询和子聚集去以更有效的方式对所选关键字进行进一步分析。
-
不支持嵌套对象。显著文本聚集当前不支持嵌套对象的文本字段,因为其使用文档json。
-
包含关键词的文档计数是基于对每个分片返回样本的和。如果一些分片在顶部抽样没有列出给定关键词,这些数值会低,反之当考虑到背景频率时,因为它可能会计算被删除文档中出现的次数,所以会比较高。这是一种折衷的结果,Elasticsearch开发人员选择以一些(通常很小的)错误为代价来提供快速的性能。
与显著关键词聚集一样,显著文本聚集也支持自定义后端上下文和相应参数。
4. 总结
本文我们主要讲解两个重要分组聚集:significant term 和 significant text 聚集。两者在识别不常见或感兴趣的关键词非常有效。对于keyword字段应该使用significant term 聚集,对于自由文本字段应该使用 significant text 聚集。它们都是需要消耗大量时间和内存,因此应该仔细设计查询,避免高内存占用。
本文参考链接:https://blog.csdn.net/neweastsun/article/details/104467440