Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Elasticsearchを用いたPOI検索入門
Search
Tomoki Saito
November 17, 2022
Technology
1
580
Elasticsearchを用いたPOI検索入門
タクシー乗務員の営業業務利用を背景としたPOI検索について、Elasticsearchを用いた実現方法や良くある問題に対する改善アプローチについて紹介する。
Tomoki Saito
November 17, 2022
Tweet
Share
Other Decks in Technology
See All in Technology
品質文化を支える小さいクロスファンクショナルなチーム / Cross-functional teams fostering quality culture
toma_sm
0
120
「経験の点」の位置を意識したキャリア形成 / Career development with an awareness of the “point of experience” position
pauli
4
100
AIと開発者の共創: エージェント時代におけるAIフレンドリーなDevOpsの実践
bicstone
1
320
プロダクト開発におけるAI時代の開発生産性
shnjtk
2
240
Amazon CloudWatch を使って NW 監視を行うには
o11yfes2023
0
170
Conquering PDFs: document understanding beyond plain text
inesmontani
PRO
0
110
彩の国で始めよう。おっさんエンジニアから共有したい、当たり前のことを当たり前にする技術
otsuki
0
150
The Tale of Leo: Brave Lion and Curious Little Bug
canalun
1
120
Стильный код: натуральный поиск редких атрибутов по картинке. Юлия Антохина, Data Scientist, Lamoda Tech
lamodatech
0
740
AWSのマルチアカウント管理 ベストプラクティス最新版 2025 / Multi-Account management on AWS best practice 2025
ohmura
4
310
C++26アップデート 2025-03
faithandbrave
0
240
AIコーディングの最前線 〜活用のコツと課題〜
pharma_x_tech
3
1.8k
Featured
See All Featured
4 Signs Your Business is Dying
shpigford
183
22k
Speed Design
sergeychernyshev
29
900
Fashionably flexible responsive web design (full day workshop)
malarkey
407
66k
Put a Button on it: Removing Barriers to Going Fast.
kastner
60
3.8k
YesSQL, Process and Tooling at Scale
rocio
172
14k
Build The Right Thing And Hit Your Dates
maggiecrowley
35
2.6k
Being A Developer After 40
akosma
91
590k
CSS Pre-Processors: Stylus, Less & Sass
bermonpainter
356
30k
Imperfection Machines: The Place of Print at Facebook
scottboms
267
13k
Making the Leap to Tech Lead
cromwellryan
133
9.2k
The Language of Interfaces
destraynor
157
25k
10 Git Anti Patterns You Should be Aware of
lemiorhan
PRO
656
60k
Transcript
2022.11.10 齋藤 智輝 AI技術開発部 アルゴリズムグループ 株式会社 Mobility Technologies Elasticsearchを用いた POI検索入門
2 自己紹介 ▪ 齋藤 智輝 (@tstomoki ) ▪ 職歴 ▪
ヤフー株式会社 ▪ エンティティパネルの開発 ▪ ユーザ位置情報を用いた研究開発 ▪ 画像系ベンチャー企業 ▪ 機械学習モデルの開発・導入 ▪ 株式会社Mobility Technologies (2019/10 ~) AI技術開発部 アルゴリズムグループ ▪ ETA (迎車時間予測エンジンの開発) ▪ AI予約 ・優先パス, etc. 趣味 ▪ サバゲー(最近) ▪ サウナ ▪ サイクリング (ロードバイク)
3 項目 01|背景 02|論文紹介 03|Elasticsearchを用いたPOI検索 04|検索精度改善
4 01 背景
5 POIとは Point of Interest の略 一般的に POI というと「目標物」を指すことが多く、地図データベースや カーナビゲーションシステムにおいては、店舗や施設を意味し、この数が
多いほど情報が豊富であるということになります。 背景: POIとは cf. esriジャパン,「POI」https://www.esrij.com/gis-guide/gis-other/point-of-interest/
6 タクシー乗務員端末向けアプリにて実装されている 背景: POI検索機能の利用 フリーワード検索・サジェスト含む 表示されたPOIへのナビ開始
7 1. 表記ゆれ 例. ナイキストア, nikeストア, 一丁目, 1丁目, etc. 2.
距離制限 例. 大阪周辺で、「空港」と検索 => 「成田空港」 東京駅周辺で、「スシロー」と検索 => 「スシロー帯広店」 3. 別称・通称 例.「成田国際空港」: 「成田空港, 新東京国際空港, NRT, ...」 「ビッグサイト」: 「東京国際展示場, 東京ビッグサイト, ...」 4. 打ち間違い 例. 「木更津」=> 「きさらず」?「きさらづ」? 背景: 問題の難しさ
8 02 論文紹介
9 「ウェブ検索クエリに対する教師なしエンティティリンキング」(ヤフー) 知識ベース (Knowledge Base, KB) を用いて実現 曖昧性をウェブクリックログから構築した確率モデルで回避 論文紹介 クエリ「増上寺」に対する
エンティティパネル 論文リンク Entity 増上寺 三縁山広度 院増上寺 東京都港区芝公園4丁目7番35号 https://www .zojoji.or.jp/ 35.39267, 139.44538 名称 別称 所在地 公式サイト 座標 エンティティの構造化された情報
10 03 Elasticsearchを用いたPOI検索
11 ▪ 分散型RESTful検索/分析エンジン ▪ AWSなどでも使いやすく整備されている ▪ https://aws.amazon.com/jp/opensearch-service/the-elk-stack/what-is- elasticsearch/ Elasticsearchとは
12 ▪ インデックス・クエリ加工時にチューニング Elasticsearchの利用例 POI インデックス POI検索サーバ 検索リクエスト (フリーワード) 検索リクエスト
(加工クエリ) 端末
13 項目 03-01|基本的なクエリ 03-02|インデックス 03-03|形態素解析器の利用
14 03-01 基本的なクエリ
15 ▪ “name” fieldへの完全一致(1語のみ) ▪ “name” fieldへの完全一致(複数語) ▪ “name” fieldへのAND検索
▪ “name” fieldへの部分一致(ワイルドカード指定) 基本的なクエリ: 単一field {"query": {"term": {"name": "成田空港"}}} {"query": {"terms": {"name": ["成田空港", "成田国際空港"]}}} {"query": {"match": {"name": {"query": "成田 国際 空港"}}}} {"query": {"wildcard": {"name": "*成田空港*"}}
16 ▪ カテゴリ指定をした“name”への完全一致 基本的なクエリ: 複数条件 { "query": { "bool": {
"should": [ { "match": { “name”: "東京ドーム" } }, { "terms": { "categories": [ "poi0" ] } } ] } } } これだと以下のいずれかを満たす • name が “東京ドーム” に完全一致 • カテゴリが poi0 must : 必ず含まれる条件 must_not : 条件を満たすものを除外 should : いずれかの条件を満たす filter : 条件を満たすものに絞る
17 ▪ カテゴリ指定をした“name”への完全一致 基本的なクエリ: 複数条件 { "query": { "bool": {
"must": [ { "match": { "name" : "東京ドーム" } } ], "filter": [ { "terms": { "categories": [ "poi0" ] } } ] } } } • name が “東京ドーム” に完全一致 • カテゴリ poi0 に絞る must : 必ず含まれる条件 must_not : 条件を満たすものを除外 should : いずれかの条件を満たす filter : 条件を満たすものに絞る
18 ▪ 距離制限にも対応 ▪ 指定方法 ▪ 起点となる座標 ▪ 検索対象とする範囲 ▪
半径 ▪ Range ▪ Bounding box ▪ polygon 基本的なクエリ: 距離制限 検索地点 半径 検索対象のPOI 検索対象外のPOI
19 ▪ 5km以内の“name”への完全一致 基本的なクエリ: 距離制限 { "query": { "bool": {
"must": [ { "wildcard": { "name": { "value": "*セブンイレブン*" } } } ], "filter": { "geo_distance": { "distance": "5km", "location": { "lat": 35.681427924515276, "lon": 139.7670926134856 } } } } } } 検索位置から5km以内のセブンイレブンを検索できる!
20 ▪ 類似スコアの計算 (公式ドキュメント) ▪ BM25 (okapi BM25, default) ▪
TF/IDFベースの類似度 ▪ 文書に含まれる単語数が多いとペナルティ ▪ DFR: Divergence From Randomness framework ▪ DFI: Divergence From Independence ▪ 頻出単語に重み付けした確率モデル ▪ IB: Information Based model ▪ トピックモデルベースの文書モデル 基本的なクエリ: スコア計算
21 ▪ tf-idfの拡張 okapi BM25 𝑠𝑐𝑜𝑟𝑒 𝑑, 𝑄 = *
!"# $ 𝐼𝐷𝐹 𝑞! , 𝑑 ⋅ 𝑓 𝑞! , 𝑑 ⋅ (𝑘# + 1) 𝑓 𝑞! , 𝑑 + 𝑘# ⋅ 1 − 𝑏 + 𝑏 ⋅ 𝑑 𝑎𝑣𝑔𝑑𝑙 𝐼𝐷𝐹 𝑞, 𝑑 = log 文書の総数 𝑞が含まれる文書数 𝑠𝑐𝑜𝑟𝑒 𝑑, 𝑄 = * !"# $ 𝐼𝐷𝐹 𝑞! , 𝑑 ⋅ 𝑇𝐹(𝑞! , 𝑑) 𝑇𝐹 𝑞, 𝑑 = 𝑓(𝑞, 𝑑) 𝑑に含まれる単語数 = 𝑑における𝑞の出現頻度 𝑑に含まれる単語数 tf-idf: BM25: ここが異なる 𝑑: 文書(𝑃𝑂𝐼の名称)
okapi BM25 の解釈 𝑓 𝑞! , 𝑑 ⋅ (𝑘" +
1) 𝑓 𝑞! , 𝑑 + 𝑘" ⋅ 1 − 𝑏 + 𝑏 ⋅ 𝑑 𝑎𝑣𝑔(𝑑𝑙) 文書𝑑における𝑞の出現頻度 文書𝑑の単語数 定数 文書全体の平均単語数 文書𝑑の相対的な長さ
23 ▪ ある文書dが平均的な長さだと仮定して検証 okapi BM25 の解釈 𝑓 𝑞! , 𝑑
⋅ (𝑘# + 1) 𝑓 𝑞! , 𝑑 + 𝑘# ⋅ 1 − 𝑏 + 𝑏 ⋅ 𝑑 𝑎𝑣𝑔(𝑑𝑙) = 𝑓 𝑞! , 𝑑 ⋅ (𝑘# + 1) 𝑓 𝑞! , 𝑑 + 𝑘# ⋅ 1 − 𝑏 + 𝑏 ⋅ 𝑎𝑣𝑔(𝑑𝑙) 𝑎𝑣𝑔(𝑑𝑙) ∵ 𝑑 = 𝑎𝑣𝑔(𝑑𝑙) = 𝑓 𝑞! , 𝑑 ⋅ (𝑘# + 1) 𝑓 𝑞! , 𝑑 + 𝑘# ⋅ 1 − 𝑏 + 𝑏 = 𝑓 𝑞! , 𝑑 ⋅ (𝑘# + 1) 𝑓 𝑞! , 𝑑 + 𝑘# 出現頻度が高すぎるスコアを抑制する効果がある
24 ▪ ある文書dの長さによってどう変化するか okapi BM25 の解釈 𝑓 𝑞! , 𝑑
⋅ (𝑘# + 1) 𝑓 𝑞! , 𝑑 + 𝑘# ⋅ 1 − 𝑏 + 𝑏 ⋅ 𝑑 𝑎𝑣𝑔(𝑑𝑙) = 𝑓 𝑞! , 𝑑 ⋅ (𝑘# + 1) 𝑓 𝑞! , 𝑑 + 𝑘# ⋅ 1 − 𝑏 + 𝑏𝑥 相対的に短い文書(name)を重視することができる こちらが重視される “東京駅” “セブンイレブン東京駅前店”
25 03-02 インデックス
26 ▪ RESTfulな手法 ▪ POI情報を1件登録 ▪ 複数のPOI情報を一括登録 インデックス POI インデックス
{ "name": "六本木グランドタワー" "category": "POI1" } POST /<index名>/_doc POST _bulk { "index" : { "_index" : " <index名> ", "_id" : "1" } } { ”name" : "六本木グランドタワー", "category": "POI1"} { "index" : { "_index" : " <index名> ", "_id" : "2" } } { ”name“ : ”六本木一丁目駅", "category": "POI2"} POST /<index名>/_bulk
27 Logstashの利用 ▪ Logstashとは ▪ Elastic社の開発したサーバーサイドデータ処理パイプライン ▪ IPアドレスから地理的座標を解読、あるいはセンシティブ情報を 含むフィールドを匿名化・除外するなども可能 ▪
ϩάॲཧػೳオープンソースプラグインが充実 ▪ "84ͱͷ࿈ܞ༰қͦ͏ DG"NB[PO0QFO4FBSDI4FSWJDF POI インデックス
28 03-03 形態素解析器の利用
29 ▪ データのインデックス作成時や検索時に形態素解析を用い ることができる ▪ POI情報登録時 ▪ POI検索時 形態素解析器の利用 入力クエリ:
「東京駅ファミリーマート」 検索クエリ: • 「東京駅ファミリーマート」 • 「東京駅 ファミリーマート」 • 「東京 駅 ファミリーマート」 わかちがき: 「ファミリーマート 東京駅 前 店 」 元名称: 「ファミリーマート東京駅前店」 登録名称: • 「ファミリーマート 東京駅前店」
30 ▪ Analyzerとして文字列正規化フローを定義できる ▪ “kana_analyzer” ▪ 記号の削除 ▪ 小文字に変換 ▪
Unicode正規化, etc. 文字列正規化 “神谷バー★Tokyo Base★店” “神谷バー tokyo base店”
31 ▪ 導入済みanalyzerの結果を確認できる ▪ “東京駅” を分かち書き(tokenize) ▪ “東京駅” をかな変換 &
分かち書き(tokenize) Analyzerの動作確認 { ”analyzer“: <analyzer名>, ”text“: ”東京駅" } POST /<index名>/_analyze 東京 駅 { ”analyzer“: <analyzer名>, ”text“: ”東京駅" } POST /<index名>/_analyze とうきょう えき
32 04 検索精度改善
33 1. 表記ゆれ 例. ナイキストア, nikeストア, 一丁目, 1丁目, etc. 2.
距離制限 例. 大阪周辺で、「空港」と検索 => 「成田空港」 東京駅周辺で、「スシロー」と検索 => 「スシロー帯広店」 3. 別称・通称 例.「成田国際空港」: 「成田空港, 新東京国際空港, NRT, ...」 「ビッグサイト」: 「東京国際展示場, 東京ビッグサイト, ...」 4. 打ち間違い 例. 「木更津」=> 「きさらず」?「きさらづ」? 背景: 問題の難しさ (再掲)
34 1. 表記ゆれ 例. ナイキストア, nikeストア, 一丁目, 1丁目, etc. 2.
距離制限 例. 大阪周辺で、「空港」と検索 => 「成田空港」 東京駅周辺で、「スシロー」と検索 => 「スシロー帯広店」 3. 別称・通称 例.「成田国際空港」: 「成田空港, 新東京国際空港, NRT, ...」 「ビッグサイト」: 「東京国際展示場, 東京ビッグサイト, ...」 4. 打ち間違い 例. 「木更津」=> 「きさらず」?「きさらづ」? 背景: 問題の難しさ への対応 正規化 + 別称追加 距離制限 / 減衰スコア 別称追加 クエリ拡張
35 ▪ それぞれの名称に対する別称・通称を集めたい ▪ Wikidataが使えそう ▪ Pros ▪ 商用利用可能 ▪
ダンプデータも提供 ▪ SPARQLでデータ取得も可能 ▪ 誰でも編集可能 ▪ Cons ▪ 誰でも編集可能 ▪ ノイズが多い 別称追加 Wikidataでの「成田国際空港」の名称・別称
36 ノイズがあるので名称(Label)と別称(Also known as)は 重要度を考える必要がありそう 別称追加 Wikidataでの「東京ドーム」の名称・別称 そのまま使うの は怪しい。。
37 ▪ 距離制限だとユースケースに合わないことも ▪ 例えば検索範囲を半径50km以内に絞ってしまうと... 距離減衰スコアの導入 東京駅 成田国際空港 直線距離約60km 経路距離約70km
東京駅から「成田国際空港」と 検索してもヒットしない...
38 ▪ 減衰(Decay)関数を定義する ▪ 検索対象は絞らず検索スコアに反映 ▪ クエリ例 ▪ 東京駅とloc間の距離を対象 ▪
2km以内であれば減衰なし ▪ Scale, decayで減衰率を調整 距離減衰スコアの導入 "DECAY_FUNCTION": { "location": { "origin": "35.68191, 139.76693", "scale": "10km", "offset": "2km", "decay": 0.7 } }
39 ▪ 減衰(Decay)関数を定義する ▪ DECAY_FUNCTIONはgauss, exp, linearの3つから指定できる 距離減衰スコアの導入 𝜎! =
− 𝑠𝑐𝑎𝑙𝑒! 2 ⋅ ln(𝑑𝑒𝑐𝑎𝑦) 𝑆 𝑑𝑜𝑐 = 𝑒𝑥𝑝 − max 0, 𝑙𝑜𝑐"#$ − 𝑜𝑟𝑖𝑔𝑖𝑛 − 𝑜𝑓𝑓𝑠𝑒𝑡 ! 2𝜎! gauss 𝑆 𝑑𝑜𝑐 = 𝑒𝑥𝑝 𝜆 ⋅ max(0, 𝑙𝑜𝑐"#$ − 𝑜𝑟𝑖𝑔𝑖𝑛 − 𝑜𝑓𝑓𝑠𝑒𝑡) exp 𝜆 = ln(𝑑𝑒𝑐𝑎𝑦) 𝑠𝑐𝑎𝑙𝑒 linear 𝑠 = 𝑠𝑐𝑎𝑙𝑒 (1.0 − 𝑑𝑒𝑐𝑎𝑦) 𝑆 𝑑𝑜𝑐 = 𝑚𝑎𝑥 𝑠 − max(0, 𝑙𝑜𝑐"#$ − 𝑜𝑟𝑖𝑔𝑖𝑛 − 𝑜𝑓𝑓𝑠𝑒𝑡) 𝑠 , 0 cf. elastic, “Supported decay functions”
40 ▪ 減衰(Decay)関数の比較 ▪ 20km(offset)以下であれば減衰なし ▪ 10(scale)kmごとに0.8(decay)減衰 (expの場合) 距離減衰スコアの導入
41 「木更津」=> 「きさらず」?「きさらづ」? 類義語登録によるクエリ拡張 別称ではない! 検索ログから特定のワードを拡張 入力クエリ: 「きさらず」 検索クエリ: 「きさらず」
or 「きさらづ」
42 ▪ 管理方法は以下の2つ ▪ ファイルベース ▪ configディレクトリにファイルを設置 ▪ インデックスベース ▪
“synonyms” fieldにそのまま記載 ▪ 書き方 類義語登録の利用方法 { "settings": { "index": { "analysis": { "analyzer": { "synonym_analyzer": { "tokenizer": "whitespace", "filter": [ "my_synonyms" ] } }, "filter": { "my_synonyms": { "type": "synonym", "synonyms_path": "my_synonyms.txt", "updateable": true } } } } } } cf. elastic, “違い”を生む“同じ”:Elasticsearchのパワーを増大させる“同義語” きさらず,きさらづ 入力: 「きさらず」検索: 「きさらず」「きさらづ」 きさらず => きさらづ 入力: 「きさらず」検索: 「きさらづ」
43 ▪ elasticsearchについて基本的な利用方法について紹介 ▪ クエリ構築方法 ▪ インデックス方法 ▪ 形態素解析(Analyzer)の利用方法 ▪
POI検索の文脈で発生しがちな問題について の対応例を紹介 ▪ 別称追加 ▪ 減衰関数 ▪ 類義語辞書の利用 まとめ
44 EOP