什麼是phrase suggester?
Suggesters | Elasticsearch Guide [8.6] | Elastic
phrase suggester是基于term Suggester,加入了ngram思想的Suggester設計。
要了解phrase Suggester,必須先了解term Suggester和ngram。
phrase suggester 和 term Suggester的效果示例
asr文本:深圳市 福田區 香蜜湖北路 西園
期望糾錯:深圳市 福田區 香蜜湖北路 熙園
term Suggester
GET address-company-廣東省-深圳市/_search
{
"suggest": {
"term_suggestion": {
"text": "深圳市 福田區 香蜜湖北路 西園",
"term": {
"field": "ner",
"suggest_mode": "popular",
"size": 20,
"min_word_length": 2,
"prefix_length": 0
}
}
}
}
傳回
"suggest" : {
"term_suggestion" : [
{
"text" : "深圳市",
"offset" : 0,
"length" : 3,
"options" : [ ]
},
{
"text" : "福田區",
"offset" : 4,
"length" : 3,
"options" : [ ]
},
{
"text" : "香蜜湖北路",
"offset" : 8,
"length" : 5,
"options" : [
{
"text" : "香蜜湖路",
"score" : 0.75,
"freq" : 1228
},
{
"text" : "香蜜湖街道",
"score" : 0.6,
"freq" : 37053
},
{
"text" : "香蜜湖大廈",
"score" : 0.6,
"freq" : 84
},
{
"text" : "香蜜湖1号",
"score" : 0.6,
"freq" : 39
},
{
"text" : "香蜜湖水榭",
"score" : 0.6,
"freq" : 33
},
{
"text" : "香蜜湖酒店",
"score" : 0.6,
"freq" : 14
},
{
"text" : "香蜜湖體育",
"score" : 0.6,
"freq" : 12
},
{
"text" : "香蜜湖較高價的電梯大廈",
"score" : 0.6,
"freq" : 9
},
{
"text" : "香蜜湖新村",
"score" : 0.6,
"freq" : 8
},
{
"text" : "香蜜湖一号",
"score" : 0.6,
"freq" : 6
},
{
"text" : "香梅北路",
"score" : 0.5,
"freq" : 292
},
{
"text" : "香密湖路",
"score" : 0.5,
"freq" : 11
}
]
},
{
"text" : "西園",
"offset" : 14,
"length" : 2,
"options" : [
{
"text" : "家園",
"score" : 0.5,
"freq" : 1339
},
{
"text" : "福園",
"score" : 0.5,
"freq" : 1033
},
{
"text" : "園西",
"score" : 0.5,
"freq" : 890
},
{
"text" : "佳園",
"score" : 0.5,
"freq" : 739
},
{
"text" : "名園",
"score" : 0.5,
"freq" : 620
},
{
"text" : "南園",
"score" : 0.5,
"freq" : 552
},
{
"text" : "松園",
"score" : 0.5,
"freq" : 513
},
{
"text" : "圍園",
"score" : 0.5,
"freq" : 445
},
{
"text" : "可園",
"score" : 0.5,
"freq" : 407
},
{
"text" : "桃園",
"score" : 0.5,
"freq" : 404
},
{
"text" : "竹園",
"score" : 0.5,
"freq" : 310
},
{
"text" : "蘭園",
"score" : 0.5,
"freq" : 277
},
{
"text" : "桂園",
"score" : 0.5,
"freq" : 275
},
{
"text" : "公園",
"score" : 0.5,
"freq" : 260
},
{
"text" : "北園",
"score" : 0.5,
"freq" : 259
},
{
"text" : "樂園",
"score" : 0.5,
"freq" : 201
},
{
"text" : "麗園",
"score" : 0.5,
"freq" : 182
},
{
"text" : "智園",
"score" : 0.5,
"freq" : 172
},
{
"text" : "嘉園",
"score" : 0.5,
"freq" : 166
},
{
"text" : "莊園",
"score" : 0.5,
"freq" : 140
}
]
}
]
}
可以看到
可園的候選項,傳回的size設定為20,都沒有包含正确答案。(實際 熙園 的排序在70多位,因為詞頻低)。
再來看phrase Suggester的效果
GET address-company-廣東省-深圳市/_search
{
"suggest": {
"ner_pinyin_suggest": {
"text": "深圳市 福田區 香蜜湖北路 西園",
"phrase":{
"field": "ner.trigram",
"gram_size": 3,
"direct_generator": [ {
"field": "ner.trigram",
"suggest_mode": "always",
"min_word_length": 2,
"prefix_length": 0,
"size": 100
} ],
"highlight": {
"pre_tag": "<em>",
"post_tag": "</em>"
},
"shard_size": 2000,
"max_errors": 2,
"size": 10
}
}
}
}
傳回
"suggest" : {
"ner_pinyin_suggest" : [
{
"text" : "深圳市 福田區 香蜜湖北路 西園",
"offset" : 0,
"length" : 16,
"options" : [
{
"text" : "深圳市 福田區 香蜜湖街道 熙園",
"highlighted" : "深圳市 福田區 <em>香蜜湖街道 熙園</em>",
"score" : 0.07858969
},
{
"text" : "深圳市 福田區 香蜜湖街道 嘉園",
"highlighted" : "深圳市 福田區 <em>香蜜湖街道 嘉園</em>",
"score" : 0.07858969
},
{
"text" : "深圳市 福田區 香蜜湖街道 竹園",
"highlighted" : "深圳市 福田區 <em>香蜜湖街道 竹園</em>",
"score" : 0.07858969
},
{
"text" : "深圳市 福田區 香蜜湖街道 西路",
"highlighted" : "深圳市 福田區 <em>香蜜湖街道 西路</em>",
"score" : 0.07858969
},
{
"text" : "深圳市 福田區 香蜜湖路 熙園",
"highlighted" : "深圳市 福田區 <em>香蜜湖路 熙園</em>",
"score" : 0.04161656
},
{
"text" : "深圳市 福田區 香密湖路 西南",
"highlighted" : "深圳市 福田區 <em>香密湖路 西南</em>",
"score" : 0.023261044
},
{
"text" : "深圳市 福田區 香蜜湖路 西南",
"highlighted" : "深圳市 福田區 <em>香蜜湖路 西南</em>",
"score" : 0.019111233
},
{
"text" : "深圳市 福田區 香蜜湖路 西北",
"highlighted" : "深圳市 福田區 <em>香蜜湖路 西北</em>",
"score" : 0.017365677
},
{
"text" : "深圳市 福田區 香密湖路 西北",
"highlighted" : "深圳市 福田區 <em>香密湖路 西北</em>",
"score" : 0.017214466
},
{
"text" : "深圳市 福田區 香蜜湖北路 西鄉",
"highlighted" : "深圳市 福田區 香蜜湖北路 <em>西鄉</em>",
"score" : 0.002201289
}
]
}
]
}
可以看到
phrase suggester的第一條就是:深圳市 福田區 香蜜湖街道 熙園
雖然 熙園 是低頻詞,但是考慮了ngram之後,得分卻是最高的。
解密Phrase Suggester
GET address-company-廣東省-深圳市
可以檢視索引的資訊
"mappings" : {
"properties" : {
"address" : {
"type" : "text"
},
"area" : {
"type" : "keyword"
},
"city" : {
"type" : "keyword"
},
"id" : {
"type" : "keyword"
},
"ner" : {
"type" : "text",
"fields" : {
"trigram" : {
"type" : "text",
"analyzer" : "trigram",
"search_analyzer" : "whitespace"
}
},
"analyzer" : "whitespace"
},
"ner_pinyin" : {
"type" : "text",
"fields" : {
"trigram" : {
"type" : "text",
"analyzer" : "trigram",
"search_analyzer" : "whitespace"
}
},
"analyzer" : "whitespace"
},
"province" : {
"type" : "keyword"
}
}
},
我可以看到ner 和 ner_pinyin除了主field(text,whitespace)外,還有 trigram的field,這個trigram是自定義的類型,專門服務phrase suggester。
trigram這個field裡有一個自定義的trigram analyzer。
索引資訊裡可以檢視到這個analyzer的定義:
"analyzer" : {
"trigram" : {
"filter" : [
"lowercase",
"shingle"
],
"type" : "custom",
"tokenizer" : "whitespace"
},
這個analyzer除了使用空格分詞,關鍵使用了filter。
filter 并不是過濾器,更像流式程式設計中的map函數,輸入的token流經常變換得到新的token流 Token filter reference | Elasticsearch Guide [8.6] | Elastic
lowercase非常好了解,就是全部變換為小寫;
shingle是一個自定義的filter
什麼是shingle filter?
shingle就是token ngram(詞級别的ngram)的意思,這個詞來自ES的底層lucene。
自定義的shingle filter如下:
shingle filter的定義
"analysis" : {
"filter" : {
"shingle" : {
"max_shingle_size" : "3",
"min_shingle_size" : "2",
"output_unigrams": false,
"type" : "shingle"
}
},
我們可以通過如下方法,形象的檢視和測試shingle filter的行為
如何檢視shingle的行為?
GET /_analyze
{
"tokenizer": "whitespace",
"filter": [
{
"type": "shingle",
"min_shingle_size": 2,
"max_shingle_size": 3,
"output_unigrams": false
}
],
"text": "深圳市 福田區 香蜜湖北路 西園"
}
輸出如下:
{
"tokens" : [
{
"token" : "深圳市 福田區",
"start_offset" : 0,
"end_offset" : 7,
"type" : "shingle",
"position" : 0
},
{
"token" : "深圳市 福田區 香蜜湖北路",
"start_offset" : 0,
"end_offset" : 13,
"type" : "shingle",
"position" : 0,
"positionLength" : 2
},
{
"token" : "福田區 香蜜湖北路",
"start_offset" : 4,
"end_offset" : 13,
"type" : "shingle",
"position" : 1
},
{
"token" : "福田區 香蜜湖北路 西園",
"start_offset" : 4,
"end_offset" : 16,
"type" : "shingle",
"position" : 1,
"positionLength" : 2
},
{
"token" : "香蜜湖北路 西園",
"start_offset" : 8,
"end_offset" : 16,
"type" : "shingle",
"position" : 2
}
]
}
如何了解shingle的參數定義
"min_shingle_size": 2,
"max_shingle_size": 3,
"output_unigrams": false
根據前面shingle的執行個體輸出,可以發現,這是一個3gram的輸出(但不輸出單詞條,因為output_unigrams為false)
如何提升shingle性能?
shingle是動态生成的,如果需要更高性能,則需要提前預計算,這時可以采用index-phrases。
簡單的說,就是将ngram的輸出在建索引時,就寫在另一個field上,用空間換時間。
index_phrases | Elasticsearch Guide [8.6] | Elastic
shingle和ngram tokenizer的差別?
shingle:token ngram ,是一個基于詞級别的ngram Shingle token filter | Elasticsearch Guide [8.6] | Elastic
ngram tokenizer: char ngram,是一個基于字元級别的ngram N-gram tokenizer | Elasticsearch Guide [8.6] | Elastic
舉例:
GET /_analyze
{
"tokenizer": {
"type": "ngram",
"min_gram": 3,
"max_gram": 3
},
"text": "深圳市 福田區 香蜜湖北路 西園"
}
{
"tokens" : [
{
"token" : "深圳市",
"start_offset" : 0,
"end_offset" : 3,
"type" : "word",
"position" : 0
},
{
"token" : "圳市 ",
"start_offset" : 1,
"end_offset" : 4,
"type" : "word",
"position" : 1
},
{
"token" : "市 福",
"start_offset" : 2,
"end_offset" : 5,
"type" : "word",
"position" : 2
},
{
"token" : " 福田",
"start_offset" : 3,
"end_offset" : 6,
"type" : "word",
"position" : 3
},
{
"token" : "福田區",
"start_offset" : 4,
"end_offset" : 7,
"type" : "word",
"position" : 4
},
{
"token" : "田區 ",
"start_offset" : 5,
"end_offset" : 8,
"type" : "word",
"position" : 5
},
{
"token" : "區 香",
"start_offset" : 6,
"end_offset" : 9,
"type" : "word",
"position" : 6
},
{
"token" : " 香蜜",
"start_offset" : 7,
"end_offset" : 10,
"type" : "word",
"position" : 7
},
{
"token" : "香蜜湖",
"start_offset" : 8,
"end_offset" : 11,
"type" : "word",
"position" : 8
},
{
"token" : "蜜湖北",
"start_offset" : 9,
"end_offset" : 12,
"type" : "word",
"position" : 9
},
{
"token" : "湖北路",
"start_offset" : 10,
"end_offset" : 13,
"type" : "word",
"position" : 10
},
{
"token" : "北路 ",
"start_offset" : 11,
"end_offset" : 14,
"type" : "word",
"position" : 11
},
{
"token" : "路 西",
"start_offset" : 12,
"end_offset" : 15,
"type" : "word",
"position" : 12
},
{
"token" : " 西園",
"start_offset" : 13,
"end_offset" : 16,
"type" : "word",
"position" : 13
}
]
}
很明顯,ngram的傳回并不是我們預期需要的。
什麼是direct generator?
phrase suggester是基于term suggester的ngram,那麼direct generator就類似term suggester,生成候選集,然後ngram基于這些基礎資料,進行計算。
是以direct generator的配置,要參考term suggester.
但有幾個配置不一樣。
"direct_generator": [ {
"field": "ner",
"suggest_mode": "always",
"min_word_length": 2,
"prefix_length": 0,
"size": 100
} ]
suggest_mode 是選popular 還是 always
term suggester建議為popular,通過更高詞頻來判斷糾錯
但是phrase suggester是基于ngram,有上下文關系,不需要通過不嚴謹的詞頻來設計,是以,應該為always
field 是否要加.trigram ?
網上的教程會有加.trigram的用法,那到底是用 ner 還是 ner.trigram
我們将ner.trigram 應用在term suggester中,看看其行為
GET address-company-廣東省-深圳市/_search
{
"suggest": {
"term_suggestion": {
"text": "深圳市 福田區 香蜜湖北路 西園",
"term": {
"field": "ner.trigram",
"suggest_mode": "always",
"size": 80,
"min_word_length": 2,
"prefix_length": 0
}
}
}
}
輸出,和ner的差不多,但是,增加了一些:
香蜜湖 1,香蜜湖 店,香蜜湖 北環路 等等的輸出。
很明顯。ner.trigram的行為是,不僅僅用單個詞條作為糾錯,而是可以将後續的2,3個詞,一起作為整體進行糾錯。
如果建索引和搜尋時,采用的是相同粒度的分詞,則采用ner即可。
如果建索引采用細粒度分詞,搜尋的時候,采用粗粒度分詞,則采用ner.trigram
TODO:待測試對比效果。
phrase suggester的參數如何設定?
gram_size:3
深圳市 福田區 香蜜湖北路 西園
如果不設定,第一條糾錯建議為:深圳市 福田區 香蜜湖街道 西鄉, 也就是unigram的糾錯能力。(西鄉是西園的最高頻單詞條糾錯建議)—— 很奇怪,官方說會從filed的filter中推導這個值,實際不會推導,是以手動設定。
max_errors:2
表示最多糾錯的詞條數量(注意,不是一個詞條内的最大糾錯字數)
舉例:
深圳市 福田區 香蜜湖北路 西園
因為最大錯誤數量是2,是以可以糾正為:深圳市 福田區 香蜜湖街道 熙園
如果設定為1,則隻能糾正為:深圳市 福田區 香蜜湖北路 西鄉
shard_size:100
每個shard傳回的最大數量的建議詞條,預設是5
如果采用預設值,會發現, 無法将 西園 糾錯為 熙園。 因為,熙園的詞頻低,shard隻傳回了Top 5的詞頻詞條,熙園不在phrase suggester的候選資料裡,是以無法糾正對。
使用collate過濾掉不合理的suggestion
在phrase suggestion的建議中,存在一些不合理的,如:深圳市 福田區 香蜜湖北路 西鄉。(因為 福田區 根本沒有西鄉,西鄉在 寶安區)
這是一個unigram的糾錯(即使shingle設定不輸出unigram,phrase suggester還是會有unigram的糾錯,不知道為什麼)
可以采用collate參數,如下是示例:(具體使用參見:Suggesters | Elasticsearch Guide [8.6] | Elastic)
match_phrase是要求全部精确比對,且詞的順序也要符合的嚴格match模式。
prune預設為false,表示不符合query條件的,不輸出。
這裡設定為true,表示都會輸出,但是輸出增加了collate_match的标記,query比對的為true,不比對的為false,友善調試和做後續的優先級設計等。
(之是以保留不比對的原因如下:
使用者輸入:AAA BXB CCC DDD
語料有:AAA BBB CCC 和 AAA BBB DDD
根據BBB CCC,ES将BXB CCC 修正為 BBB CCC,最終輸出為:AAA BBB CCC DDD
根據match_phrase的全部比對要求,語料裡沒有一條可以和它比對。)
"collate": {
"query": {
"source" : {
"match_phrase": {
"{{field_name}}" : "{{suggestion}}"
}
}
},
"params": {"field_name" : "ner"},
"prune": true
}
Phrase Suggester的打分機制
smooth 模型,預設采用 stupid backoff 。
stupid backoff 比較簡單,比對上3gram是1,比對不上,如果比對上2gram,權重乘以0.4,如果還比對不上,比對unigram,權重在2gram的基礎上,再乘以0.4
詳細可以了解google的論文 https://aclanthology.org/D07-1090.pdf
基于拼音的編輯距離排序
根據phrase suggester的建議,存在高頻詞排序靠前的問題。
輸入:深圳市 龍崗區 龍崗街道 寶平路 五号
期望:深圳市 龍崗區 龍崗街道 寶坪路 五号
phrase suggester 糾錯為:
深圳市 龍崗區 龍崗街道 寶荷路 五号
而ASR位址糾錯的特點是音近,是以,需要加入一個根據編輯距離排序的功能。
排序後,可以得到期望的答案。