ElasticSearch 2 (21) - 语言处理系列之单词识别
ElasticSearch 2 (21) - 語言處理系列之單詞識別
摘要
一個英語單詞相對容易識別:因為英語單詞是被空格或(某些)標點符號隔開的。但在英語中也有反例:you’re 這個詞是一個單詞還是兩個?那 o’clock、 cooperate、 half-baked 或 eyewitness 這些詞呢?
如德語或荷蘭語這樣的語言將多個獨立單詞組合在一起創(chuàng)建更長的復合詞(如:Wei?kopfseeadler 在英文里是 “white-headed sea eagle” 意思為“白頭海鷹”)。為了使 Wei?kopfseeadler 能夠作為 Adler (在英文里是 “eagle” 意思為“鷹”)的查詢結果返回,我們需要了解如何將復合詞分解成它的各自組成部分。
亞洲的各種語言更為復雜:有些語言的詞語、句子,甚至段落之間沒有空格,有些語言的詞語可以用單個字表示,但是當同一個字與其他字放在一起,組成一個長的詞語時,新詞語的意思可能截然不同。
顯然萬能金鑰匙式的分析器并不存在,想要處理所有的語言也是天方夜譚。Elasticsearch 為很多語言都配備了專門的分析器,而且更多的語言分析器以插件形式提供。
盡管如此,不是所有的語言都有專門的分析器,有時我們并不十分清楚正在處理的語言種類。這種情況下,我們需要一組標準工具,無論對何種語言都能進行合理的處理。
版本
elasticsearch版本: elasticsearch-2.x
內容
標準分析器(standard Analyzer)
standard 分析器是所有全文 analyzed 字符串字段的默認分析器。如果我們要重新實現(xiàn)一個自定義的standard 分析器,可以用以下方式定義:
{"type": "custom","tokenizer": "standard","filter": [ "lowercase", "stop" ] }在 標記的規(guī)范化(Normalizing Tokens) 和 停用詞:性能與精度(Stopwords: Performance Versus Precision) 中,我們提到過 lowercase 小寫字母和 stop token filters 停用詞標記過濾器,但現(xiàn)在,讓我們先關注 standard 標準標記器 。
標準標記器(standard Tokenizer)
標記器(tokenizer) 接受字符串輸入,對字符串進行處理分解成獨立的單詞、或 標記(token)(可能會丟棄某些標點符號),然后以 標記流(token stream) 的形式作為輸出。
令人感興趣的是用來識別單詞的算法。whitespace 空位標記器只是簡單的在空位處(如:空格、制表符、行符等等)將單詞拆解出來,并認為連續(xù)的非空位字符會組成一個標記。例如:
GET /_analyze?tokenizer=whitespace You're the 1st runner home!這個請求會返回以下這些詞項:You're、 the、 1st、 runner、 home!。
另一方面,letter 字母標記器在所有非字母處拆解,這樣就會得到以下詞項:You、re、the、st、runner、home。
standard 分析器使用 Unicode Text Segmentation 算法(定義在 Unicode Standard Annex #29 中)找到詞語之間的邊界,然后輸出邊界間的所有內容。由于采用 Unicdoe,它可以成功的對混合語言的文本進行標記。
標點符號可以作為,也可以不作為詞語的一部分,這取決于它出現(xiàn)的地方:
GET /_analyze?tokenizer=standard You're my 'favorite'.本例中,You're里的撇號是作為詞語的一部分的,但 'favorite' 里的引號卻沒有,結果中的詞項為:You're、 my、 favorite。
小貼士
uax_url_email 標記器與 standard 標記器的工作方式一樣,不過它還可以識別電子郵件地址和 URL 然后將它們作為單個標記輸出。與其相反,standard 標記器會嘗試將它們拆解。例如:電子郵件地址 joe-bloggs@foo-bar.com 可能會被拆解成 joe、bloggs、foo、bar.com。
standard 分析器是一個用來標記大多數(shù)語言的不錯起點,特別是西方的語言。實際上,它是多數(shù)語言分析器的基礎,如:英語、法語和西班牙語言分析器,但它對亞系語言的支持是有限的。對于亞系語言,我們應該考慮使用 icu_tokenizer 它以 ICU 插件的形式提供。
安裝 ICU 插件(Installing the ICU Plug-in)
Elasticsearch 的 ICU 分析插件 使用 International Components for Unicode (ICU) 函數(shù)庫(參見 site.project.org)為 Unicode 的處理提供了一組豐富的工具。它包括專門針對處理亞系語言的 icu_tokenizer 的分析器,以及針對除英語以外的其他語言的標記過濾器,它們對正確的匹配和排序至關重要。
注意:
ICU 插件是一個能處理英語以外其他語言的必備工具,在此強烈推薦安裝使用。不幸的是,因為它是基于外部的 ICU 函數(shù)庫,不同的插件版本可能不向前兼容,所以升級時可能需要重新索引我們的數(shù)據(jù)。
要安裝插件,首先需要關閉 Elasticsearch 節(jié)點然后在其安裝路徑下執(zhí)行以下命令:
./bin/plugin -install elasticsearch/elasticsearch-analysis-icu/$VERSION #1#1 當前版本 $VERSION 可以在 https://github.com/elasticsearch/elasticsearch-analysis-icu 找到。
一旦安裝成功,重啟 Elasticsearch,我們會在啟動日志中看到類似下面這行信息:
[INFO][plugins] [Mysterio] loaded [marvel, analysis-icu], sites [marvel]
如果我們在使用集群,需要為每個集群下的每個節(jié)點都安裝這個插件。
ICU插件(icu_tokenizer)
icu_tokenizer 同樣使用 Unicode Text Segmentation 算法作為 standard 標準標記器,但對某些亞系語言支持得更好,因為它使用一種基于字典的方式去識別泰語、老撾語、漢語、日語和漢語里的詞,并利用自定義規(guī)則將緬甸語和高棉語標記成音節(jié)。
例如,比較 standard 和 icu_tokenizers 分析器分別生成的標記,當對 “Hello. I am from Bangkok.” 使用泰語進行標記時:
GET /_analyze?tokenizer=standard ?????? ???????????????standard 標記器生成兩個標記,每個句子一個:??????, ???????????????。這只能在想搜索完整句子的時候起到作用 “I am from Bangkok”,但如果只搜索 “Bangkok” 會什么都找不到。
GET /_analyze?tokenizer=icu_tokenizer ?????? ???????????????icu_tokenizer 標記器正好相反,它能將文本拆解成多個獨立的單詞 (??????、 ??、 ??、 ???、 ????????),這讓搜索更容易。
與之形成對比的是 standard 分析器會對漢語和日語文本 “過度標記”,常常將完整的詞語拆解成獨立的字。因為詞語之間沒有任何空格,這是它難以判斷連續(xù)的字是應該拆解成單獨的詞,還是應該組成一個詞語。例如:
向 的意思是 facing,日 的意思是 sun,葵 的意思是 hollyhock。但當寫在一起時,向日葵 的意思是 sunflower。
五 的意思是 five 或 fifth,月 的意思是 month,雨 的意思是 rain。前兩個字寫在一起 五月 的意思是 the month of May,再加上第三個字,五月雨 的意思是 continuous rain。然后與第四個字 式(意思為:style)組合,詞 五月雨式 變成了個形容詞,用來形容連續(xù)不斷或永無止境。
盡管每個字可能都有它自己的正確意思,當能保留完整而不是部分的更多原始概念時,標記會更有意義:
GET /_analyze?tokenizer=standard 向日葵GET /_analyze?tokenizer=icu_tokenizer 向日葵之前示例中的 standard 會將每個字作為獨立標記輸出:向、日、葵。icu_tokenizer會輸出單個標記 向日葵 (sunflower)。
standard 與 icu_tokenizer 分析器還有一個區(qū)別是:后者會將不同的書寫形式的詞(如:βeta 被分成 β、eta)拆解成獨立的標記,前者會以單個標記的形式輸出:βeta。
清理輸入文本(Tidying Up Input Text)
標記器在輸入的文本整潔、有效時可以生成最佳結果,這里 有效 指的是它遵從 Unicode 算法期望的標點符號規(guī)則。但往往我們需要處理的文本就是不如我們的期望。在標記化過程之前整理好文本能有效提高輸出標記的質量。
標記 HTML(Tokenizing HTML)
將 HTML 內容傳入 standard 或 icu_tokenizer 分析器的輸出效果會很差。這些分析器完全不知如何處理 HTML 標簽。例如:
GET /_analyzer?tokenizer=standard <p>Some déjà vu <a href="http://somedomain.com>">website</a>standard 分析器無法區(qū)分 HTML 的標簽和實體,輸出以下標記:p、Some、d、eacute、j、 agrave、vu、a、href、http、somedomain.com、website、a。這顯然不是我們的初衷。
字符過濾器可以被加入到分析器,在文本傳入標記器之前進行預處理。本例中,我們可以用 html_strip 字符過濾器先移除 HTML 標簽再對 HTML 實體(如:é)解碼成對于的 Unicode 字符。
字符過濾器可以通過給 analyze API 提供一個查詢字符串來測試:
GET /_analyzer?tokenizer=standard&char_filters=html_strip <p>Some déjà vu <a href="http://somedomain.com>">website</a>為了把它們作為分析器的一部分使用,可以將它們加到一個自定義分析器定義中:
PUT /my_index {"settings": {"analysis": {"analyzer": {"my_html_analyzer": {"tokenizer": "standard","char_filter": [ "html_strip" ]}}}} }一旦創(chuàng)建,就可以使用 analyze API 來測試我們新的分析器 my_html_analyzer:
GET /my_index/_analyzer?analyzer=my_html_analyzer <p>Some déjà vu <a href="http://somedomain.com>">website</a>輸出標記正如我們所想:Some、déjà、vu、website。
清理標點符號(Tidying Up Punctuation)
standard 和 icu_tokenizer 標記器都知道單詞里的撇號(’)應該作為詞的一部分來處理,但不知如何處理詞周圍的單引號。標記文本 You're my 'favorite' 會正確輸出標記:You're、my、favorite。
不幸的是,Unicode 列出了一些字符有時會當作撇號處理:
U+0027
撇號(')—— 原始 ASCII 字符
U+2018
左單引號(‘) —— 引用的開始符號
U+2019
右單引號(‘) —— 引用的結束符號,同時也作為撇號使用。
當它們與單詞一起出現(xiàn)時,兩個標記器都將這三個字符當作撇號處理(即作為單詞的一部分)。然后有其他三個與撇號類似的字符:
U+201B
反撇號(? high-reversed-9)—— 與 U+2018 相同但呈現(xiàn)形態(tài)不一樣。
U+0091
ISO-8859-1 里的左單引號 —— 不應在 Unicode 中使用。
U+0092
ISO-8859-1 里的右單引號 —— 不應在 Unicode 中使用。
兩個標記器都將這三個字符作為詞的邊界進行處理,即將文本拆解成標記的位置。不幸的是,有些出版者用 U+201B 作為一種樣式來表示名字 M?coy,根據(jù)我們文本編輯器年代的不同,可以很容易的用它們制作出后兩個字符。
盡管使用了“可接受”的引號,用右單引號標記的詞(You’re)還是與撇號詞(You're)不一樣,這意味著以一種形式作為查詢就無法找到另外一種形式的結果。
幸運的是,我們可以使用 mapping 映射字符過濾器來解決這個問題,它可以為我們將一個字符全文替換成另一個,這樣,我們就可以將所有的撇號替換成簡單的 U+0027 符號:
PUT /my_index {"settings": {"analysis": {"char_filter": { #1"quotes": {"type": "mapping","mappings": [ #2"\\u0091=>\\u0027","\\u0092=>\\u0027","\\u2018=>\\u0027","\\u2019=>\\u0027","\\u201B=>\\u0027"]}},"analyzer": {"quotes_analyzer": {"tokenizer": "standard","char_filter": [ "quotes" ] #3}}}} }#1 用自定義 char_filter 名為 quotes 的過濾器,將所有撇號的變體映射到一個簡單撇號 u0027。
#2 為了清楚,我們?yōu)槊總€字符使用 JSON Unicode 語法形式,不過我們也可以用字符本身來表示:"‘=>'"。
#3 用自定義 quotes 字符過濾器來創(chuàng)建一個新的分析器,quotes_analyzer。
同樣,在創(chuàng)建后對其測試:
GET /my_index/_analyze?analyzer=quotes_analyzer You’re my ‘favorite’ M?Coy本例中返回以下標記,所有詞內的單引號都被撇號替代:You're、my、favorite、M'Coy。
在確保分析器接受高質量的輸入方面所花的功夫越多,搜索的結果就會越好。
參考
elastic.co: Identifying Words
總結
以上是生活随笔為你收集整理的ElasticSearch 2 (21) - 语言处理系列之单词识别的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 对象序列化
- 下一篇: Java集合源码学习(四)HashMap