小白学 ElasticSerach(二):原理介绍篇(中)

写在前面:

在我阅读学习其它资料的过程,发现Analyzer和Tokenizer有时候同时会被翻译成"分词器",可能会让像我一样的小白读者将两者混淆。因此在这篇笔记里面,在容易混淆的地方直接用的英文名词。

通常说"ES分词器"指的其实是Analyzer,"分析器"也是指的Analyzer,但Tokenizer(中文翻译过来也是分词器)是Analyzer组成不可缺少的部分。

一、Analyzer基础概念

分词

分词是指将文本转换成一系列单词( term or token )的过程,也可以叫做文本分析,在es里面称为Analysis ,如下图所示:

Analyzer与ES

Analyzer是ES中专门处理分词的组件,称为"分词器"或者"分析器" ,ES建立索引和查询过程都离不开它。

  • 建立倒排索引:将文档通过分词器分成词条(term),每一个Term都指向包含这个Term的文档集合。 name字段的倒排索引
  • 查询:ES会根据搜索类型决定是否对query进行分词,然后和倒排索引中的term进行相关性查询,匹配相应的文档。

当搜索"小米手机" 的时候,小米手机会被分词为"小米"和"手机",去name字段对应的倒排索引里面查询,通过"小米"得到文档id集合:[1,2,3],通过"手机"得到文档id集合:[3,4],id为3的文档同时命中"小米"和"手机",相关性最高,会被优先展示。 默认情况下,相关性越大,分数越高,会被优先展示。理想状态下,我们想要的当然是同时匹配"小米"和"手机"的结果。但是如果因为想把华为产品进行优先展示,可以人为调整分数,把包含"华为"的文档进行优先展示。比较典型的是,以前一些搜索引擎在搜索医院和疾病的时候,会对搜索结果进行分数调控,优先把莆田系医院放在搜索结果的上面。

Analyzer的组成

这三个部分是有先后顺序的,过程比较好理解,先对原始文本处理,然后进行切分,最后对切分的单词进行再处理。

如果我们自己想要自定义一个分词器,也是由这三部分组成,数量如下:

Analyzer = Charater Filters(0个或多个) + Tokenizer(有且仅有一个) + Token Filters(0个或多个)

二、Charater Filters

HTML Strip Character Filter

  • 从输入中去除HTML元素,只保留文本内容,比如<br></br>,<p></p>等标签,<p>Hello World</p> 被处理为 "Hello World"。

  • HTML中预定义字符的替换,比如&apos替换为单引号'

HTML中预定义字符对照可参考:www.madore.org/\\~david/co...

示例:

Markdown 复制代码
GET /_analyze
{
  "tokenizer": "keyword",
  "char_filter": [
    "html_strip"
  ],
  "text": "<p>I'm so <b>happy</b>!</p>"
}

处理之后:

Markdown 复制代码
I'm so happy!

可配置项:

  • escaped_tags:可选,字符串数组,不处理包含尖括号 ( < >) 的 HTML 元素数组。从文本中剥离 HTML 时,过滤器会跳过这些 HTML 元素。例如,"escaped_tags": ["p"] 会跳过<p></p>标签,不会删除这对标签。

Mapping Character Filter

设置预定义的映射关系,将指定的字符或字符串替换为其他字符或字符串。例如,可以定义一个规则将 "&" 替换为 "和",将 "||" 替换为 "或者"。

Markdown 复制代码
GET /_analyze
{
  "tokenizer": "keyword",
  "char_filter": [
    {
      "type":"mapping",
      "mappings": [
        "& => 和",
        "|| => 或者"
      ]
    }
  ],
  "text": "午餐吃面条||米饭,菜要牛肉&青菜"
}

处理之后:

Markdown 复制代码
午餐吃面条或者米饭,菜要牛肉和青菜

Pattern Replace Character Filter

使用正则表达式匹配字符,并将匹配的内容替换为指定的字符串。这对于处理有复杂需求的文本非常有用。

参数:

  • pattern:一个Java正则表达式. 必填。

  • replacement:替换字符串,可以使用 <math xmlns="http://www.w3.org/1998/Math/MathML"> 1.. 1 .. </math>1.. 9语法来引用捕获组,可以参考这里

  • flags:Java正则表达式标志。标志应该用"|"进行分割,例如"CASE_INSENSITIVE | COMMENTS"。

示例:

Markdown 复制代码
PUT my-index-000001
{
  "settings": {
    "analysis": {
      "analyzer": {
        "my_analyzer": {
          "tokenizer": "standard",
          "char_filter": [
            "my_char_filter"
          ]
        }
      },
      "char_filter": {
        "my_char_filter": {
          "type": "pattern_replace",
          "pattern": "(\\d+)-(?=\\d)",
          "replacement": "$1_"
        }
      }
    }
  }
}

POST my-index-000001/_analyze
{
  "analyzer": "my_analyzer",
  "text": "My credit card is 123-456-789"
}

处理之后;

Markdown 复制代码
[ My, credit, card, is, 123_456_789 ]

三、Tokenizer

单词分词器(Word Oriented Tokenizers)

1.1 Standard Tokenizer

standard tokenizer 按照 Unicode 文本分段算法的定义,将文本划分为字边界上的术语。它删除了大多数标点符号,是大多数语言的最佳选择。

可选参数:

  • max_token_length:单个 token 的最大长度。如果一个 token 超过这个长度,则以max_token_length 为间隔分割。默认值是255.

示例:

Markdown 复制代码
PUT my-index-000001
{
  "settings": {
    "analysis": {
      "analyzer": {
        "my_analyzer": {
          "tokenizer": "my_tokenizer"
        }
      },
      "tokenizer": {
        "my_tokenizer": {
          "type": "standard",
          "max_token_length": 5
        }
      }
    }
  }
}

POST my-index-000001/_analyze
{
  "analyzer": "my_analyzer",
  "text": "The 2 QUICK Brown-Foxes jumped over the lazy dog's bone."
}

处理之后:

Markdown 复制代码
[ The, 2, QUICK, Brown, Foxes, jumpe, d, over, the, lazy, dog's, bone ]

1.2 Letter Tokenizer

Letter Tokenizer每当遇到非字母的字符时,分词器就会将文本划分为词条 。

Markdown 复制代码
POST _analyze
{
  "tokenizer": "letter",
  "text": "The 2 QUICK Brown-Foxes jumped over the lazy dog's bone."
}

上面的句子会生成如下的词元:

Markdown 复制代码
[ The, QUICK, Brown, Foxes, jumped, over, the, lazy, dog, s, bone ]

1.3 Lowercase Tokenizer

类似于 letter tokenizer,遇到非字母时分割文本,并将所有分割后的词元转为小写。功能上等同于 letter tokenizer + lowercase token filter,但是由于单次执行了所有步骤,所以效率更高。

1.4 whitespace Tokenizer

类似 standard tokenizer,每当遇到 whitespace 字符时将文本分解成词条。

Markdown 复制代码
POST _analyze
{
  "analyzer": "whitespace",
  "text": "The 2 QUICK Brown-Foxes jumped over the lazy dog's bone."
}

处理之后:

Markdown 复制代码
[ The, 2, QUICK, Brown-Foxes, jumped, over, the, lazy, dog's, bone. ]

1.5 UAX URL Email Tokenizer

和Standard Tokenizer类似,只不过它会把 URL和 email 地址当成一个词元,可选参数也一样是max_token_length。

Markdown 复制代码
POST _analyze
{
  "tokenizer": "uax_url_email",
  "text": "Email me at john.smith@global-international.com"
}

分解结果:

Markdown 复制代码
[ Email, me, at, john.smith@global-international.com ]

而 standard tokenizer 会生成:

Markdown 复制代码
[ Email, me, at, john.smith, global, international.com ]

1.6 Classic Tokenizer

Classic Tokenizer是一种基于语法的标记器,适用于英文文档。 特殊处理首字母缩略词、公司名称、电子邮件地址和互联网主机名。 但是,这些规则并不总是起作用,而且除了英文之外,大多数语言中并不能正常工作。

1.7 Thai Tokenizer

将泰文文本分成单词,使用的是 java 的泰语分割算法。文本中的其他语言按照standard tokenizer 处理。

部分词分词器(Partial Word Tokenizers)

Partial Word Tokenizers将文本或单词分解成小片段,以进行部分单词匹配。主要有两个。

2.1 N-gram tokenizer

N-gram tokenizer遇到指定的字符(如 : 空白、标点)时分割文本,然后返回指定长度的每个单词的 N-grams

N-grams 就像一个滑动窗口在单词上移动,是一个连续的指定长度的字符序列。 通常用于查询不使用空格或具有较长复合词(如德语)的语言。

使用默认设置,ngram tokenizer将初始文本视为单个词元,并生成最小长度为1且最大长度为2的 N-gram:

Markdown 复制代码
POST _analyze
{
  "tokenizer": "ngram",
  "text": "Quick Fox"
}

分词结果:

Markdown 复制代码
[ Q, Qu, u, ui, i, ic, c, ck, k, "k ", " ", " F", F, Fo, o, ox, x ]

可选参数:

  • min_gram:以 gram 为单位的最小字符长度。 默认为1。

  • max_gram:以 gram 为单位的最大字符长度。 默认为2。

  • token_chars:应包含在词元中的字符类。 Elasticsearch将分割不属于指定类的字符。 默认为[](保留所有字符)。字符类可能是以下任何一种:(1)单词:例如a,b等;(2)数字 :例如3或17;(3)空格 : 例如" "或换行符 (4)标点符号: 例如"!",","等 (5)一些特殊符号:例如$或√

2.2 Edge NGram Tokenizer

首先将文本分解为单词,然后发出每个单词的N 元语法,其中 N 元语法的开头锚定到单词的开头。

具体可以参考;www.elastic.co/guide/en/el...

结构化分词器(Structured Text Tokenizers)

这里介绍三种常见的结构化分词器

3.1 Keyword Tokenizer

Keyword Tokenizer将整个输入字符串作为单个词条返回,适用于如小写电子邮件地址等不想分割的地方。

Markdown 复制代码
POST _analyze
{
  "tokenizer": "keyword",
  "text": "New York"
}

处理之后:

Markdown 复制代码
[ New York ]

3.2 Pattern Tokenizer

使用正则表达式分割文本。遇到单词分隔符将文本分割为词元, 或者将捕获到匹配的文本作为词元。默认的匹配模式时 \W+ ,遇到非单词的字符时分割文本。

3.3Path Tokenizer

path_hierarchy tokenizer 把分层的值看成是文件路径,用路径分隔符分割文本,输出树上的各个节点。

Markdown 复制代码
POST _analyze
{
  "tokenizer": "path_hierarchy",
  "text": "/one/two/three"
}

处理之后:

Markdown 复制代码
[ /one, /one/two, /one/two/three ]

Analyzer的第三部分是Token Filters,可以翻译成词元过滤器,它接受来自 Tokenizer(分词器) 的 tokens 流,并且可以修改 tokens(例如小写转换),删除 tokens(例如,删除 stopwords停用词)或添加 tokens(例如同义词)。

Elasticsearch 提供了很多内置的词元过滤器,可以用于构建自定义分词器。

在第四部分的1.4,讲了Stop词元过滤器的配置与使用。

四、Analyzer

ES自带分词器

1.1 Standard Analyzer

这里对"The 2 QUICK Brown-Foxes jumped over the lazy dog's bone."进行分词,其它Analyzer也会使用这个例子分词,从而比较它们的异同。

在上文提到了Analyzer 的组成: Charater Filters(0个或多个) + Tokenizer(有且仅有一个) + Token Filters(0个或多个),并且它们的处理顺序是:Charater Filters------> Tokenizer------> Token Filters。下面给出Standard Analyzer的组成部分,并且给出每一部分处理之后的结果(其它Analyzer也是这样)。

Standard Analyzer是默认的分词器,特性为:

  • 按词切分,基于Unicode文本分割算法,支持多语言。对于中文支持不太好,汉字会被处理按照每个字进行拆分。

  • 大写的字母会被转为小写

1.2 Simple Analyzer

特性为:

  • 按照非字母切分,数字和符号会被过滤掉

  • 小写处理

代码示例:

Markdown 复制代码
POST _analyze
{
  "analyzer": "simple",
  "text":     "Happy NewYear%春节快 乐"
}

根据空格和%切分,Happy和NewYear里面的大写字母会被转为小写:

Markdown 复制代码
[happy,newyear,春节快,乐]

1.3 Whitespace Analyzer

按照空格切分,不过滤内容

Markdown 复制代码
POST _analyze
{
  "analyzer": "whitespace",
  "text":     "Happy NewYear%春节快 乐"
}

处理结果:

Markdown 复制代码
[Happy,NewYear%春节快,乐]

1.4 Stop Analyzer

相比 Simple Analyzer多了Stop Word处理。

Stop Word指语气助词等修饰性的词语,比如the、an等等。

扩展:Stop token filter

Stop token filter在没有自定义的时候,过滤器默认删除以下英文停用词:

a, an, and, are, as, at, be, but, by, for, if, in, into, is, it, no, not, of, on, or, such, that, the, their, then, there, these, they, this, to, was, will, with

Stop token filter支持自定义词语,从而让过滤器删除它们,下面举个🌰

(1)设置Stop token filter的过滤词为:[ "there","are","two","and"],同时在过滤的时候忽略大小写。

Markdown 复制代码
PUT /my-index-02
{
  "settings": {
    "analysis": {
      "analyzer": {
        "default": {
          "tokenizer": "whitespace",
          "filter": [ "my_custom_stop_words_filter" ]
        }
      },
      "filter": {
        "my_custom_stop_words_filter": {
          "type": "stop",
          "ignore_case": true,
          "stopwords": [ "there","are","two","and"]
        }
      }
    }
  }
}

(2)对"There are two dogs and a cat"用空格进行分词,用自定义的词语过滤

Markdown 复制代码
POST my-index-02/_analyze
{
  "analyzer": "default",
  "text": "There are two dogs and a cat"
}

处理之后的结果:

Markdown 复制代码
[dogs, a, cat]

1.5 Keyword Analyzer

不分词,直接将输入作为一个单词输出,所以这也是为什么keyword 用于精准匹配。

1.6 Pattern Analyzer

特性为:

通过正则表达式自定义分割符

默认是\W+ ,即非字词的符号作为分隔符

自定义分词器

在前面,我们提到了 Analyzer由三部分组成,Analyzer = Charater Filters(0个或多个) + Tokenizer(有且仅有一个) + Token Filters(0个或多个),在一些场景,我们想要实现实现业务特定功能。

下面举个🌰

对于"I'm a :) <b>person</b>, and you?",我们想把输入的英文都转为小写,且过滤掉里面的HTML标签,并且想把 :) 映射为 happy,:( 映射为 sad

处理过程如下:

Markdown 复制代码
PUT my-index-000002
{
  "settings": {
    "analysis": {
      "analyzer": {
        "my_custom_analyzer": { 
          "char_filter": [
            "html_strip",
            "emoticons"
          ],
          "tokenizer": "standard",
          "filter": [
            "lowercase"
          ]
        }
      },
      "char_filter": {
        "emoticons": { 
          "type": "mapping",
          "mappings": [
            ":) => _happy_",
            ":( => _sad_"
          ]
        }
      }
    }
  }
}
Markdown 复制代码
POST my-index-000002/_analyze
{
  "analyzer": "my_custom_analyzer",
  "text": "I'm a :) <b>person</b>, and you?"
}

分词结果:

Markdown 复制代码
["i'm","a","_happy_","person","and","you"]

五.中文分词器

中文分词指的是将汉字的序列切分成单独的词。在英文中,单词之间是以空格作为自然分界符,但汉语中词没有一个形式上的分界符。

IK

可自定义词库,支持热更新分词词典 网址:github.com/medcl/elast...

1.1 IK的两种模式

实现中英文单词的切分

ik分词器包含两种模式:

  • ik_smart:最少切分,粗粒度
Markdown 复制代码
GET /_analyze
{
  "text":"中国人民",
  "analyzer":"ik_smart"
}

分词结果:

Markdown 复制代码
[中国人民]
  • ik_max_word:最细切分,细粒度
Markdown 复制代码
GET /_analyze
{
  "text":"中国人民",
  "analyzer":"ik_max_word"
}

分词结果:

Markdown 复制代码
[中国人民,中国人,中国,国人,人民]

1.2 实践与踩坑:IK的拓展词典和停用词典

设想我们常见的一种场景,一些敏感词我们想把它屏蔽掉,一些网络新兴词我们想把它加入分词的词条行列。

1.原始模式下,我们没做额外配置,但是假如我们想把"Happy新春"作为一个词,并且不需要"新春"这个词条。

Markdown 复制代码
GET /_analyze
{
  "text":"Happy新春快乐",
  "analyzer":"ik_max_word"
}

没做额外配置的分词结果如下:

Markdown 复制代码
[happy,春节快乐,春节,快乐]

2.ik在es_plugins下面,路径是:

es-plugins\_data\ik\config

这个文件夹下有很多配置文件。

ik配置文件描述

  • IKAnalyzer.cfg.xml:IK分词配置文件。
  • main.dic:主词库。
  • stopword.dic:英文停用词,不会建立在倒排索引中。
  • quantifier.dic:特殊词库:计量单位等。
  • suffix.dic:特殊词库:行政单位。
  • surname.dic:特殊词库:百家姓。
  • preposition:特殊词库:语气词。

3.拓展

要拓展ik分词器的词库,在名为ext.dic的文件中,添加想要拓展的词语即可:

4.禁用

要禁用某些敏感词条,只需要修改一个ik分词器目录中的config目录中的IkAnalyzer.cfg.xml文件

然后在名为stopword.dic的文件中,添加想要禁用的词语:

4.生效

接下来只需要修改分词器目录中的config目录中的IkAnalyzer.cfg.xml文件:

然后在生效之后,我们输入原来的语句,可以发现结果是一开始我们想要的

Markdown 复制代码
GET /_analyze
{
  "text":"Happy新春快乐",
  "analyzer":"ik_max_word"
}

这就是我们想要的结果了

分词结果:

Markdown 复制代码
[happy新春,happy,新春快乐,快乐]

踩坑实录(自己的修改一开始没有生效,在对下面两项进行修改才真正生效):

  • 注意ext.dic,stop.dic的编码方式是UTF-8编码
  • 重启es

1.3 IK原理浅析

IK的设计包含了统计学,多种算法的知识,这里从浅层概括一下主要内容:

1.词典

词典是IK的核心部分,包括了主词典、停用词词典、姓氏词典等。Dictionary字典管理类加载它们到内存结构。

词典结构使用了Tire Tree(字典树),它是一种结构相当简单的树型结构,用于构建词典,通过前缀字符逐一比较来快速查找词,所以有时也称为前缀树。这里举个简单的例子:

中国,中国人,蓝天,蓝色这些词就是存在树中的单词。

2.算法:

  • 在ik_max_word模式下,IK分词器使用一种正向最大匹配法(FMM),从文本的最左侧开始匹配最长的词语,直到无法匹配为止,然后再从剩余文本中继续执行相同的过程。

  • 在ik_smart模式下,IK分词器借鉴Viterbi算法,这是一种动态规划算法,用于寻找最有可能的词组合,以此优化分词结果。

3.优化

为了提高处理速度,Ik做了一些优化。例如:

  • 懒加载:在不影响启动速度的前提下,按需加载词典,减少系统启动时的资源消耗。

  • 缓存:缓存热点数据,如一些常用词汇,避免重复分词带来的性能损耗。

  • 多线程提高兵发能力:对于大规模文本,支持多线程并发处理,提高分词速度。

2.jieba

python中最流行的分词系统,支持分词和词性标注 支持繁体分词、自定义词典、并行分词等 github.com/sing1ee/ela...

拓展:可根据上下文内容进行分词,基于自然语言处理的分词系统 Hanlp:

  • 由一系列模型与算法组成的Java工具包,目标是普及自然语言处理在生产环境中 的应用
  • github.com/hankcs/HanL...

THULAC:

  • THU Lexical Analyzer for Chinese ,由清华大学自然语言处理与社会人文计算 实验室研制推出的一套中文词法分析工具包,具有中文分词和词性标注功能
  • github.com/microbun/el...

感谢阅读,如果本篇文章有任何错误和建议,欢迎给我留言指正,感谢🙏

参考:

相关推荐
sp_fyf_20242 小时前
[大语言模型-论文精读] 更大且更可指导的语言模型变得不那么可靠
人工智能·深度学习·神经网络·搜索引擎·语言模型·自然语言处理
丶21365 小时前
【大数据】Elasticsearch 实战应用总结
大数据·elasticsearch·搜索引擎
闲人编程5 小时前
elasticsearch实战应用
大数据·python·elasticsearch·实战应用
世俗ˊ10 小时前
Elasticsearch学习笔记(3)
笔记·学习·elasticsearch
weixin_4662866810 小时前
ElasticSearch入门
大数据·elasticsearch·搜索引擎
Elasticsearch12 小时前
使用模拟和真实的 Elasticsearch 来测试你的 Java 代码
elasticsearch
沐曦可期13 小时前
Elasticsearch学习记录
大数据·学习·elasticsearch
alfiy1 天前
ElasticSearch学习笔记(三)Ubuntu 2204 server elasticsearch集群配置
笔记·学习·elasticsearch
hengzhepa1 天前
ElasticSearch备考 -- 多字段查询
学习·elasticsearch·搜索引擎·全文检索·es
hengzhepa1 天前
ElasticSearch 备考 -- 备份和恢复
大数据·学习·elasticsearch·搜索引擎·es