关于同义词检索方案的一点实践经验
Jinyang Zhou 发表了文章 • 0 个评论 • 8813 次浏览 • 2018-06-15 13:36
最近一直在搞同义词搜索的问题,踩了一些坑,总结了一些经验,尤其是刚刚接触搜索和 ES,所以如果有不对的,或者不完备的地方也希望大家能提出改进意见。。。
下面是自己留下的文档记录:
----
需求
同义词检索也是搜索引擎必备的功能之一,例如,我们希望用户在搜索广东话的同时,也能找出和粤语有关的信息;用户在搜索苹果手机的同时,包含iPhone的内容也能被检索并呈现。
在现实生活中,相同语义的表述词汇往往有很多,而用户在检索的时候很难在一条 query 中将它们全部体现,所以识别和提供同义词检索显然可以获得更高的召回率。
需求剖析
在思考解决方案之前,我们不妨再来看看刚才提到的两个例子:
- 苹果手机与iPhone
- 广东话与粤语
我们先来看第一个,苹果手机和iPhone。
显然,这两个词是等价的,因为苹果公司发布的所有手机产品都叫 iPhone,而 iPhone 这个名字也没有被其他公司使用过。
于是,当用户搜索“苹果手机购买”的时候,我们也就有理由将它拆分成“苹果手机购买”和“iPhone购买”,分别进行检索,再将结果合并返回。
----
语言学中对这样的词组,称为是同义词中的相对同义词,或是等义词,等义同义词。它们表达意思完全一致,在绝大多数语境中都可以相互替换,同时对上下文也不会产生影响。
这样的词组还有很多,例如:猫熊和熊猫,柚子和文旦等等,这些等义词大抵来说有这样几种来源:
- 音节减缩形成:机枪和机关枪,坦克和坦克车,电扇和电风扇
- 音译和音译形成:出租车和的士 ,维生素和维他命
- 地域叫法不同,或新旧叫法:单车,自行车和脚踏车,西红柿和番茄,马铃薯,洋芋和土豆,黄瓜和胡瓜
- 昵称代称:周杰伦和周董
- 描述角度不同,学名方言差异:电脑和计算机,曲别针和回形针
这些词组多以名词呈现,数量比较少,词组规模也较小,同时变化也很小。
---
接下来我们再来看第二组词:广东话和粤语。
广东话和粤语这两个词代表的意思是相同的吗?它们也是可以相互替换的吗?
答案显然是否定的。
广东话从语义本身来说是一个比较粗糙的概念,它不仅指广东省内的粤语,还涵盖了潮汕话,客家话,雷州话等其他方言。而粤语却是一个非常严肃的概念,对语音语调都有非常详细的规定,不仅通用于广东省大部分地区,还包括广西、香港、澳门等地,甚至东南亚和北美。它们联系在于,大部分广东地区的人说的是粤语。
如此说来,给广东话和粤语这样非常相似却又并不完全一致的词直接划上等号是有失偏颇的。当然,其实仔细考虑也不难发现,和广东话有相似之处却又不完全相同的词还有很多,例如:客家话、广州话、广府话等等。
---
语言学中把这样的词汇,称作是相对同义词,或是近义词。它们在意义上有一些相似之处,只能在特定的语境中进行替换。
它们的差别可能包括:
- 语义上:毁坏和损坏(前者更严重),介绍和说明(前者可以对人施加作用)
- 色彩上:团结和勾结
对于这类相似却又不完全相同的近义词,在搜索的时候提供关联搜索是一个不错的方案。例如用户搜索“毁坏公物如何处罚”的时候,查询结果可以由90%的“损坏公物如何处罚”和10%的“毁坏公物如何处罚”查询结果合并后返回,从而获得更多的召回。
这些近义词以动词为主,不仅数量多,词组的规模也大,例如靠近的近义词可以是:靠拢,逼近,接近,迫近等等。
解决思路
在可替换的等义词问题中,我们可以直接使用 Elasticsearch 原生的 synonyms 功能来完成。虽说原生 synonyms 功能不支持热更新,而且需要将词典事先放进制定目录,不过好在这类等义词数量并不多,变化也并不大,尚且属于一劳永逸的任务。
对于不可直接替换的近义词问题,如果直接套用原生的 Synonyms 虽然可能会带来更多召回,但是查准率却骤降。
对于这类问题,我们期待的场景是,一旦发现用户 query 中的某个词有近义词,我们就将它拆分替换,成为多个 query 进行联合搜索。就像前面的例子:用户搜索“损坏公物如何处罚”的时候,查询结果可以由90%的“损坏公物如何处罚”和10%的“毁坏公物如何处罚”查询结果合并后返回。如此说来,使用 Elasticsearch 提供的 boosting_query 就成为了一个自然而然的想法。
不过稍加思考也不难发现,boostingquery 中 weight 的获得并不容易,也就是前面例子中的90%和10%这组数字应该怎么设定,这也是近义词联合搜索中的重点。
先回到我们刚才的例子:当用户搜索“损坏公物如何处罚”的时候,我们本能地觉得用90%的损坏和10%的毁坏合并在一起是“合理的”,这样的本能其实是来自于:我们主观地认为在检索“搞坏公物”这个事实的时候,90%的用户会使用毁坏来描述,10%的用户会使用损坏来描述。
简而言之,这组数字可以理解为用户群体描述同一个问题时,对词组选择的组成比例。再换个说法,也就是在当前这条 query 中,原词和近义词之间的可替换程度。
再举一例,在“广东话入门”这条 query 中,从“表达学习语言”的语义上来看,广东话和粤语差别并不大,这条 query 自然可以拆分成“广东话入门”和“粤语入门”,进行联合搜索,而且它们的 weight 甚至可以设置为 1:1 来获得更多合理的召回。
反过来,在“粤语歌曲推荐”这条 query 中,广东话的 weight 就需要慎重考虑,一方面是因为本身就没有“广东话歌曲”这种说法,另一方面也因为在广泛的语料中,歌曲和广东话这两个词极少同时被提及。所以几遍是“粤语歌曲推荐”的拆分成分中有“广东话歌曲推荐”,weight 也需要被设置地非常低(倘若真的没有“粤语歌曲”相关的内容,推荐“广东话”的内容作为替补)。
说到这里,其实已经很明白了,语言模型是可以在这里被使用的,而语言模型的困惑度也与前面提到的 weight 一脉相承。
所以大致计算流程可以是:获得用户的query之后进行分词,在词组中寻找所有可能的同义词替换,将所有替换后的 query 分别放进语言模型中获得困惑度(或其他 metrics),依据它们来作为 boosting query 中的 weight。
```mermaid
graph TD;
广东话入门-->'广东话','入门';
'广东话','入门'-->'广东话','入门';
'广东话','入门'-->'粤语','入门';
'广东话','入门'-->语言模型;
'粤语','入门'_-->语言模型;
语言模型-->PPL:0.65;
语言模型-->PPL:0.35;
```
对于这里语言模型的选择,可以使用传统的ngram,也可以使用双向的LSTM这样一些成熟的方案从语料中训练,也可以使用一些现成的方案:http://ai.baidu.com/tech/nlp/dnnlm_cn
es 怎么使用Java Api进行大于2个字段的聚合,例如sql的 group by a,b,c
strglee 回复了问题 • 1 人关注 • 1 个回复 • 4544 次浏览 • 2018-06-15 11:04
请教一个搜索结果的汇总统计的问题
wen 回复了问题 • 5 人关注 • 3 个回复 • 1789 次浏览 • 2018-06-15 10:26
关于Elasticsearch查询出结果再运算排序的问题
laoyang360 回复了问题 • 4 人关注 • 3 个回复 • 4857 次浏览 • 2018-06-17 00:19
filebeat设置了多行合并,kibana出现日志缺失
xiaoaps 回复了问题 • 5 人关注 • 8 个回复 • 4355 次浏览 • 2019-01-29 10:25
Elasticsearch查询问题
laoyang360 回复了问题 • 2 人关注 • 1 个回复 • 2170 次浏览 • 2018-06-14 19:29
elastic search 查询问题
laoyang360 回复了问题 • 2 人关注 • 1 个回复 • 2993 次浏览 • 2018-06-14 19:21
用curator删除定时删除索引,删除后集群不知为何又重建创建了这个索?
chengzi_xs 回复了问题 • 5 人关注 • 4 个回复 • 8388 次浏览 • 2019-09-17 17:48
elasticsearch更新,排序
kennywu76 回复了问题 • 3 人关注 • 1 个回复 • 1983 次浏览 • 2018-06-14 16:17
elaseticsearch update all能实现么
strglee 回复了问题 • 2 人关注 • 1 个回复 • 3399 次浏览 • 2018-06-14 11:40
ES5.3聚合内存溢出bug
yayg2008 发表了文章 • 1 个评论 • 5219 次浏览 • 2018-06-13 20:48
有以下DSL
json<br /> {<br /> "size" : 0,<br /> "query" : { },<br /> "_source" : false,<br /> "aggregations" : {<br /> "aggData" : {<br /> "terms" : {<br /> "field" : "url",<br /> "size" : 200,<br /> "min_doc_count" : 1,<br /> "shard_min_doc_count" : 0,<br /> "show_term_doc_count_error" : false,<br /> "order" : [<br /> {<br /> "PV" : "desc"<br /> }<br /> ]<br /> },<br /> "aggregations" : {<br /> "PV" : {<br /> "cardinality" : {<br /> "field" : "userssid"<br /> }<br /> }<br /> }<br /> }<br /> }<br /> }<br />
目的是对用户访问的URL进行分组统计,按独立用户数来排序。
执行后,data节点频繁FGC,内存无法回收,随即OOM,然后data节点脱离,集群变为red。
最初以为是cardinality精度问题导致内存使用过多,随即将precision_threshold设置为100,再次执行,内存使用量确实少了很多,但是还是用到GB级别。为了确认是否是cardinality问题,去掉外层聚合,直接执行
json<br /> "aggregations" : {<br /> "PV" : {<br /> "cardinality" : {<br /> "field" : "userssid"<br /> }<br /> }<br /> }<br />
发现响应非常快,而且内存占用只有KB级别。
再次单独执行外部聚合,发现也非常快,于是猜测是order导致,将order去掉,果然,如丝般顺滑,再也没有OOM。
为了解决这种OOM,首先想到的是熔断器。默认indices.breaker.request.limit配置是60%。改成10%后,触发熔断,集群正常,但是多点几次之后,data还是出现OOM了。
于是逐步调试,发现每执行1次,内存就增加一点,熔断返回后并没有被回收,直到OOM。基本确定是这里的order导致内存泄露了。
就在此时,同事反馈在5.6不会有这个问题,于是去查release note,果然在[5.5的版本](https://www.elastic.co/guide/e ... 0.html)发现fix了这个问题。[问题描述](https://github.com/elastic/elasticsearch/pull/24941)。
这个bug的根本原因是:
<br /> terms aggregations at the root level use the global_ordinals execution hint by default.<br /> When all sub-aggregators can be run in breadth_first mode the collected buckets for these sub-aggs are dense (remapped after the initial pruning).<br /> But if a sub-aggregator is not deferrable and needs to collect all buckets before pruning we don't remap global ords and the aggregator needs to deal with sparse buckets.<br /> Most (if not all) aggregators expect dense buckets and uses this information to allocate memories.<br /> This change forces the remap of the global ordinals but only when there is at least one sub-aggregator that cannot be deferred.<br />
解决方案:
1,升级到5.5以上版本;
2,DSL增加"execution_hint":"map",属性。
```json
{
"size" : 0,
"query" : { },
"_source" : false,
"aggregations" : {
"aggData" : {
"terms" : {
"field" : "url",
"size" : 200,
"execution_hint":"map",
"min_doc_count" : 1,
"shard_min_doc_count" : 0,
"show_term_doc_count_error" : false,
"order" : [
{
"PV" : "desc"
}
]
},
"aggregations" : {
"PV" : {
"cardinality" : {
"field" : "userssid"
}
}
}
}
}
}
ELK采集不同项目的日志,会不会存在日志打印到别的系统而获取不到的情况
strglee 回复了问题 • 2 人关注 • 1 个回复 • 2455 次浏览 • 2018-06-13 18:55