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
540
Elasticsearchを用いたPOI検索入門
タクシー乗務員の営業業務利用を背景としたPOI検索について、Elasticsearchを用いた実現方法や良くある問題に対する改善アプローチについて紹介する。
Tomoki Saito
November 17, 2022
Tweet
Share
Other Decks in Technology
See All in Technology
サイバーセキュリティと認知バイアス:対策の隙を埋める心理学的アプローチ
shumei_ito
0
380
Security-JAWS【第35回】勉強会クラウドにおけるマルウェアやコンテンツ改ざんへの対策
4su_para
0
170
dev 補講: プロダクトセキュリティ / Product security overview
wa6sn
1
2.3k
SSMRunbook作成の勘所_20241120
koichiotomo
2
120
開発生産性を上げながらビジネスも30倍成長させてきたチームの姿
kamina_zzz
2
1.7k
10XにおけるData Contractの導入について: Data Contract事例共有会
10xinc
5
570
ドメインの本質を掴む / Get the essence of the domain
sinsoku
2
150
データプロダクトの定義からはじめる、データコントラクト駆動なデータ基盤
chanyou0311
2
280
ノーコードデータ分析ツールで体験する時系列データ分析超入門
negi111111
0
410
20241120_JAWS_東京_ランチタイムLT#17_AWS認定全冠の先へ
tsumita
2
230
マルチプロダクトな開発組織で 「開発生産性」に向き合うために試みたこと / Improving Multi-Product Dev Productivity
sugamasao
1
300
Lambda10周年!Lambdaは何をもたらしたか
smt7174
2
110
Featured
See All Featured
Fontdeck: Realign not Redesign
paulrobertlloyd
82
5.2k
個人開発の失敗を避けるイケてる考え方 / tips for indie hackers
panda_program
93
16k
ピンチをチャンスに:未来をつくるプロダクトロードマップ #pmconf2020
aki_iinuma
109
49k
Art, The Web, and Tiny UX
lynnandtonic
297
20k
CoffeeScript is Beautiful & I Never Want to Write Plain JavaScript Again
sstephenson
159
15k
"I'm Feeling Lucky" - Building Great Search Experiences for Today's Users (#IAC19)
danielanewman
226
22k
It's Worth the Effort
3n
183
27k
How to Think Like a Performance Engineer
csswizardry
20
1.1k
The Invisible Side of Design
smashingmag
298
50k
No one is an island. Learnings from fostering a developers community.
thoeni
19
3k
Faster Mobile Websites
deanohume
305
30k
VelocityConf: Rendering Performance Case Studies
addyosmani
325
24k
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