Slide 1

Slide 1 text

Lucene/Elasticsearch の Character Filter で ユニコード正規化するとトークンのオフセットが ズレるバグへの Workaround Shunsuke Kanda Search Engineering Tech Talk 2024 Spring @ LegalOn Technologies

Slide 2

Slide 2 text

About me 氏名 ● 本名 – 神田 峻介 ● ハンドル – Kampersanda / かんぱさんだ 経歴 ● 理研AIP ● LegalOn Technologies(旧LegalForce) ○ 研究員@LegalForce Research ○ MLエンジニア@機械学習チーム ○ 検索エンジニア@検索推薦チーム よく使うアイコン (自分で描いた)

Slide 3

Slide 3 text

今日の発表について Elasticsearch でユニコード正規化したとき、トークンのオフセットに不正な値が 入るバグが報告されている 頻繁に起こるケースでは無いが、発生する以上は対応が必要 ➔ バグの内容と原因を解説し、Workaround を提案

Slide 4

Slide 4 text

Lucene/Elasticsearch の Analyzer Doc Character Filter Tokenizer Token Filter Index トークン化の前処理 トークンに分割 トークンに後処理 (e.g., ユニコード正規化) (e.g., 形態素解析) (e.g., ストップワード除去)

Slide 5

Slide 5 text

Lucene/Elasticsearch の Analyzer 1㍑の涙 1リットルの涙 1/リットル/の/涙 1/リットル/涙 Index トークン化の前処理 トークンに分割 トークンに後処理 (e.g., ユニコード正規化) (e.g., 形態素解析) (e.g., ストップワード除去)

Slide 6

Slide 6 text

Lucene/Elasticsearch の Analyzer 1㍑の涙 1リットルの涙 1/リットル/の/涙 1/リットル/涙 Index トークン化の前処理 トークンに分割 トークンに後処理 (e.g., ユニコード正規化) (e.g., 形態素解析) (e.g., ストップワード除去) { "tokens" : [ { "token" : "1", "start_offset" : 0, "end_offset" : 1, "type" : "", "position" : 0 }, { "token" : "リットル", "start_offset" : 1, "end_offset" : 2, "type" : "", "position" : 1 }, { "token" : "涙", "start_offset" : 3, "end_offset" : 4, "type" : "", "position" : 3 } ] }

Slide 7

Slide 7 text

Lucene/Elasticsearch の Analyzer 1㍑の涙 1リットルの涙 1/リットル/の/涙 1/リットル/涙 Index トークン化の前処理 トークンに分割 トークンに後処理 (e.g., ユニコード正規化) (e.g., 形態素解析) (e.g., ストップワード除去) { "tokens" : [ { "token" : "1", "start_offset" : 0, "end_offset" : 1, "type" : "", "position" : 0 }, { "token" : "リットル", "start_offset" : 1, "end_offset" : 2, "type" : "", "position" : 1 }, { "token" : "涙", "start_offset" : 3, "end_offset" : 4, "type" : "", "position" : 3 } ] } 0 1 2 3 offset:

Slide 8

Slide 8 text

バグの再現: 合字 “㍻” を解析 ㍻ 平成 平/成 Index

Slide 9

Slide 9 text

バグの再現: 合字 “㍻” を解析 ㍻ 平成 平/成 Index { "tokens" : [ { "token" : "平", "start_offset" : 0, "end_offset" : 0, "type" : "", "position" : 0 }, { "token" : "成", "start_offset" : 0, "end_offset" : 1, "type" : "", "position" : 1 } ] }

Slide 10

Slide 10 text

バグの再現: 合字 “㍻” を解析 ㍻ 平成 平/成 Index { "tokens" : [ { "token" : "平", "start_offset" : 0, "end_offset" : 0, "type" : "", "position" : 0 }, { "token" : "成", "start_offset" : 0, "end_offset" : 1, "type" : "", "position" : 1 } ] } 空文字列として扱われている!

Slide 11

Slide 11 text

Lucene の Character Filter の設計に起因 解析後のオフセット i から原文のオフセット j へのマッピングは correctOffset(i) = j で管理 トークン “平” に期待されるマッピング ● 開始地点:correctOffset(0) = 0 ● 終了地点:correctOffset(1) = 1 トークン “成” に期待されるマッピング ● 開始地点:correctOffset(1) = 0 ● 終了地点:correctOffset(2) = 1 なぜ起こるか i j

Slide 12

Slide 12 text

Lucene の Character Filter の設計に起因 解析後のオフセット i から原文のオフセット j へのマッピングは correctOffset(i) = j で管理 トークン “平” に期待されるマッピング ● 開始地点:correctOffset(0) = 0 ● 終了地点:correctOffset(1) = 1 トークン “成” に期待されるマッピング ● 開始地点:correctOffset(1) = 0 ● 終了地点:correctOffset(2) = 1 なぜ起こるか i j

Slide 13

Slide 13 text

現状 ユニコード正規化に限らず Character Filter でオフセットがズレるバグは、少なくとも 2015 年辺りから認識されているが、現在も Open のまま 一文字が複数のトークンに分割されることは、そもそも設計ポリシー的に想定されてい ないように思える

Slide 14

Slide 14 text

Workaround: どこで、どのように対処するか? Doc Character Filter Tokenizer Token Filter Index ① 原文を事前にユニコード正規化 ② バグ原因の文字以外を正規化 ③ バグ原因のトークンを分割しないように制御 ④ 後処理でユニコード正規化

Slide 15

Slide 15 text

Workaround: どこで、どのように対処するか? Doc Character Filter Tokenizer Token Filter Index ① 原文を事前にユニコード正規化 ② バグ原因の文字以外を正規化 ③ バグ原因のトークンを分割しないように制御 ④ 後処理でユニコード正規化

Slide 16

Slide 16 text

良さそうな Workaround どこで ● Character Filter の時点 どのように ● バグの原因となる文字を正規化対象外にする ● 具体的には ICU normalization character filter には、unicode_set_filter という正規 化する/しない文字を指定できるオプションが存在する Pros ● 公式に提供されるオプションを使って素直に実現可能 Cons ● 正規化対象外の文字がヒットしない(例えば “㍻” は “平成” でヒットしない)

Slide 17

Slide 17 text

対象外となる文字の影響範囲を調査 全てのユニコード文字(コードポイント 0x00 から 0x10FFFF まで)について、 NFKC + Case Folding で複数文字に展開される文字を調査 その結果 1,114,112 文字中 1,326 文字(0.12%)が該当 ● ㌖ ㍿ ㈱ ㋉ ⑩ ㍢ ㍻ etc. これらを正規化の対象外としても、検索結果への影響は少なそうに思える ※もちろん実際に検索したい文書データでも調査した方がいいです

Slide 18

Slide 18 text

具体的な設定例 以下のように unicode_set_filter を設定するだけ "char_filter": { "custom_icu_normalizer": { "type": "icu_normalizer", "name": "nfkc_cf", "unicode_set_filter": "[^\u00a8\u00af\u00b4\u00b8\u00bc\u00bd\u00be\u00df… } } ㌖ ㍿ ㈱ ㋉ ⑩ ㍢ ㍻ etc.

Slide 19

Slide 19 text

Elasticsearch でユニコード正規化したとき、トークンのオフセットに不正な値が 入るバグの紹介 & Workaround を提案 マイナーなバグなので、多くの人にとって今すぐ対策が必要なものでは無い もし直面したときに、この発表が脳内の片隅に残ってて解決に繋がったら幸い 完全版は今日公開したテックブログ記事にあります ➔ 「Legalon Technologies Engineering Blog」で検索! 今回スキップした他の案の議論やルールを生成するスクリプトなどがあります まとめ