使用停用词的利弊
我们拥有更多硬盘资源,但是比较少的内存资源。从索引中去掉一些停用词平均每百万个文档中也就节省了4MB的资源。所以停用词并不是来节省存储资源的。
并且,我们移除一些停用词会带来一些坏处。比如我们就很难无法做到以下几种事情:
- 区分 happy from not happy
- 搜索 the band The The
- 找到莎士比亚的名言”to be, or not to be”
- 用国家的代码,例如 挪威:no
而移除停用词带来的好处主要是性能。用fox这个词来搜索一百万的文档。可能fox这个词仅仅存在于20个文档中,这样ElasticSearch只需要计算20个文档的相关度然后排序再返回top 10. 但是如果我们搜索 the or fox。 单词 the 可能存在于几乎所有的文档,这就意味着ElasticSearch必须计算所有文档的_score(相关度)再排序。这是不可想象的。
我们有办法来解决上述描述的问题。下面来讲一下如何使用停用词。
使用停用词
停用词和标准分析器
我们只需要创建一个配置的分析器,并且把停用词输入进去就可以使用用户自定义的停用词了
PUT /my_index
{
"settings": {
"analysis": {
"analyzer": {
"my_analyzer": {
"type": "standard",
"stopwords": [ "and", "the" ]
}
}
}
}
}
分治法
在访问字符串中,可以把字符串中的词分为两个部分,一个种是重要的词(低频词,在比较少文档出现的词),一种是不重要的词(在文档中经常出现的,例如the、a、to)。我们要的搜索结果尽量多匹配更多的重要的词。
而match查询可以增多一个cutoff_frequency的参数,用来区分方位字符串中重要的词和不重要的词。这样重要的词先把访问的文档查出来,然后不重要的词仅仅用来计算相关度,而不是匹配。通过两种不同的方式,我们可以在查询速度上得到一个巨大的提升。
Domain-Specific Stopwords
通过cutoff_frequency的方式我们可以得到一个很大的好处就是很轻松的获取了领域相关的停用词,例如在电影领域movie、color这些词基本上就没什么意义。如果是自定义停用词,还需要手动把这些停用词表加进去,而这个算法会通过cutoff_frequency自动将区分那些高频词。
话不多说,来个代码
{
"match": {
"text": {
"query": "Quick and the dead",
"cutoff_frequency": 0.01
}
}
这个查询会用cutoff_frequency 先把访问字符串中词氛围两个部分,一个是重要的词(quick dead),另一个一个部分是(and the)。然后再执行一个bool查询
如下
{
"bool": {
"must": {
"bool": {
"should": [
{ "term": { "text": "quick" }},
{ "term": { "text": "dead" }}
]
}
},
"should": {
"bool": {
"should": [
{ "term": { "text": "and" }},
{ "term": { "text": "the" }}
]
}
}
}
}
其中must块中意思是quick 和dead至少要匹配一个。 而should块仅仅是用来帮助计算相关度的,并没有强制要求匹配。这种方法的执行过程是先把必须要匹配的词 quick or dead的文档先查出来,然后在将所有的词综合考虑进去来计算相关度。因为must块中的词是低频词,所以这就大大减少了需要计算相关度和需要排序的文档,提纲了查询性能。
控制精度
minimum_should_match 可以跟cutoff_frequency 联合起来使用
只有高频词咋办?
比如查询字符串是 “to be, or not to be”,搜索这样词简直一脸懵逼。在这种情况下,没有一个词是重要的,我们仅仅关心拥有这所有词的文档,这种情况下EalsticSearch会执行如下语句{
“bool”: {
“must”: [
{ “term”: { “text”: “to” }},
{ “term”: { “text”: “be” }},
{ “term”: { “text”: “or” }},
{ “term”: { “text”: “not” }},
{ “term”: { “text”: “to” }},
{ “term”: { “text”: “be” }}
]
}
}含有所有这些不重要词的文档
更多的高频词(常用词的)查询可以访问https://www.elastic.co/guide/en/elasticsearch/reference/2.4/query-dsl-common-terms-query.html
停用词和短语查询
大约有5%的查询是短语查询,但是这些查询是查询速度比较慢的主力军。短语查询可能表现的很糟糕。特别是短语里存在一些非常常见的词,例如:to be, or not to be。所以我们需要做一些事情来解决短语查询的问题。
在上文讲过的停用词的优缺点中,我们说移除一些停用词仅仅会在反向索引中节省微不足道的存储空间。这仅仅对了一部分,一个典型的索引还会包含其他的数据。如下表
- 词典。 一个对所有词排过序的词典,并且保存了含有每个词的文档的数量。
- Posting list。 A list of which documents contain each term.
- 词频。每一个词出现在每一个文档中的次数
- 每一个词出现在每一个文档中的位置,这是为了短语查询
- The start and end character offsets of each term in each document, for snippet highlighting. Disabled by default.
- 一个用来归一化不同文档长度的参数,它会给短的文档更多的权重
数据位置
位置信息在analyzed 是缺省的,为了短语查询能表现的更好多一点。但是某个词出现的次数越多,那么就需要越多存储空间来存储这些位置数据。而非常常见的词,这些位置数据的量会达到兆字节甚至是G字节。
跑一个短语查询,用了比较常见的词,比如the,就会导致上G的数据从硬盘里读出来,这些数据会被保存在kernel文件系统中去加速后面的查询,这看起来是一个好事,但这会把其他数据从缓存中替换出来,这回减慢接来下来的查询。
这个问题需要被解决。
索引的选择
首先需要自己问自己一个问题:我们是否需要短语查询呢?
通常情况下,答案是不需要!index_options让我们来控制在索引中需要保存那些信息 。
- docs. 仅仅保存含有某些特定词的文档
- freqs。保存文档的信息,词频等,用来计算tf/idf
- positions. 不在多说
- offsets。
代码如下:
PUT /my_index
{
"mappings": {
"my_type": {
"properties": {
"title": {
"type": "string"
},
"content": {
"type": "string",
"index_options": "freqs"
}
}
}
}
停用词
移除停用词是一个非常有力的减少位置数据的办法。一个被移除停用词的索引依然可以用来短语查询,是因为剩下词的位置依然保存着,但是,移除的停用词不再能搜索到。举个例子,我们将不能区别 man in the moom 和 man on the moon.
幸运的是,我们还是有办法解决。
这篇就先说到这里~~~希望大家玩的开心