Slide 1

Slide 1 text

Elasticsearch における類似度ベクトル検索の ベストプラクティスを求めて 伊藤 敬彦

Slide 2

Slide 2 text

注意 ● この発表の内容はたたき台的な位置づけです。 ● もっと良い方法について知っている人があれば教えてください

Slide 3

Slide 3 text

自己紹介 ● ソフトウェアエンジニア at クックパッド ● 博士(工学) ● 12年前に検索エンジンの会社(Fast Search & Transfer)に就職した後、検索エン ジンやデータマイニング界隈をフラフラしてます ● Twitter アカウント: takahi_i ● オープンソース:RedPen、Cookiecutter Docker Science

Slide 4

Slide 4 text

ある日の会話 ● 知人:「伊藤、お前検索やってたよな。類似画像検索、シュッと やりたいんだけど、どうしたらいいの?」 ● 僕:「類似ベクトル検索かーーー。。。最近だと要望の多い使い 方だし、Elasticsearchのデフォルト機能ですぐできるんちゃう ?」 ● 僕:「うちのサービスで使うこともありそうなんで調べてみる かーーー。」

Slide 5

Slide 5 text

準備:関連検索 ● テキスト文書の関連検索はメジャーな検索プラットフォーム(Solr、Elasticsearch)で サポートされている ● しかし、いろいろ調べたところ画像の類似度検索には決定打となるのが見つからな かった。。。。 ● 既存のElasticsearch をベースにした画像の類似度検索 ○ https://github.com/EdjoLabs/image-match ○ https://github.com/kzwang/elasticsearch-image ● 問題:ベクトルを古典的な手法(Hashing)で離散化する仕様 ● 最新のEmbedding手法で生成したベクトルがそのまま使えない。。。

Slide 6

Slide 6 text

準備:Faiss ● Facebook によって開発された類似度検索用のパッケージ ● URL: https://github.com/facebookresearch/faiss ● 感想:よくできている。これで十分では? ○ メルカリさんも利用: https://speakerdeck.com/metalunk/merukariniokeru-ai-huo-yong-shi-li-pyc on-jp-2018?slide=21 ● ただ、やはり検索プラットフォーム(ElasticsearchやSolr) で実現したい。。。

Slide 7

Slide 7 text

Elasticsearchで実現したい理由 ● Elasticsearchの運用しているチームは多い ○ 類似ベクトル検索単体で新しいパッケージを導入するのは敷居が高い ○ 普通のエンジニアリングチーム:検索エンジンの専任がいなかったり、いてもせ いぜい1人 ○ Elasticsearchのプラグイン追加+インデクス追加ぐらいで実現したい ● 検索に関する補助機能を自作したくない(開発リソースがないチームが多い) :Sharding、Replication、Boosting、マルチテナント etc...

Slide 8

Slide 8 text

まとめ:類似ベクトル検索パッケージに望むこと 1. 速度 ○ 高速に結果が帰ってほしい。検索にかかる時間がO(N)とか┐(´д`)┌ 2. 精度 ○ 精度が悪いのはNG。できれば最近のEmbedded 手法使いたい 3. 運用性 ○ 空間木インデクスだけ与えられてるのはNG。。。。。 ○ Elasticsaerch とか Solr がインデクスの Replication や Sharding とか提供して くれている時代に。。。 ○ ほとんどの検索チームは専任一人がいいところ、シュッと構築できないと

Slide 9

Slide 9 text

調査 1 ● 質門:Elasticsearchでベクトルの関連検索に利用できるパッケージがあるか? ● 回答:存在する ○ https://github.com/lior-k/fast-elasticsearch-vector-scoring ● 問題:検索に時間がかかる(O(N)) ○ 一般的なWebサービスでは使えない

Slide 10

Slide 10 text

調査 2 ● Elasticsaerch で高速に取ってくる手法はないか? ● 頑張って探したところ、ブログ記事を発見した ● URL:https://www.linkedin.com/pulse/searching-deep-learning-eike-dehling/ ● 処理の流れ(二段階) ○ KD-木で粗く候補を取ってくるプラグイン ■ Lucene のKD 木インデクスを ESから利用できるようにした ■ NOTE: Lucene のKD木は8次元までしか対応していないので、絞り込み 用に低次元ベクトルを別途用意する必要がある。。。 ○ 候補とクエリに含まれるベクトルのコサイン類似度を計算してランキングし直す プラグイン(全ページで紹介したプラグインを利用する) ●

Slide 11

Slide 11 text

復習:空間木 ● Wikipediaより: k次元のユークリッド空間にある点を分類する空間分割データ構造 である。kd木は、多次元探索鍵を使った探索(例えば、範囲探索や最近傍探索)な どの用途に使われるデータ構造である ● 平たく言うと:入力ベクトルに類似するベクトル郡を高速(O(Log(N)))に取得できる ● 実はLuceneでサポートされている ○ See https://mocobeta-backup.tumblr.com/post/142559495812/hello-lucene- 600 ○ 先のプラグインはLuceneが提供するKD木を使用して絞り込み検索をする

Slide 12

Slide 12 text

実験 ● word2vec で単語のベクトル(8次元、200次元)を作成してESへの入力 とする(画像とは。。。。) ○ 10万単語 ○ 画像データで実験する時間が取れず。。。

Slide 13

Slide 13 text

ES のマッピング(スキーマ) ポイント:二種類のベクトルを保持するフィー ルド作る - 低次元ベクトル(8次元) - 高次元ベクトル(200次元) { "mappings": { "doc": { "properties": { "name" : {"type": "text"}, "reduced_vector": { "type": "vector", "dimensions": 8 }, "full_vector": { "type": "binary", "doc_values": true } } } } }

Slide 14

Slide 14 text

インデクス ● Python 用ESクライアントでword2vecから算出されたベクトル(8次元、200次元) をESインスタンスにインデクスする。 es.index("full_vectors", "doc", { "name": vocab, "reduced_vector": ",".join([str(x) for x in vector8]), "full_vector": base64.b64encode(np.array(vector200). astype(np.dtype('>f8'))).decode("utf-8"), })

Slide 15

Slide 15 text

検索クエリ { "query": { "function_score": { "query": { "range": { "reduced_vector": { "from": from_vector, "to": to_vector } } }, "functions": [ { "script_score": { "script": { "source": "staysense", "lang" : "fast_cosine", "params": { "field": "full_vector", "cosine": True, "vector" : target_full_vector } } } } ], "boost_mode": "replace" } }} 第一段階の処理:rangeクエリ でターゲットベクトルを含むベク トル部分空間を指定(次ページ で解説) 第二段階の処理:第一段階 の処理で粗く取得された候補 を cosine でリランキング

Slide 16

Slide 16 text

絞り込み検索用ベクトルの作成 ● KD木で抽出する部分空間(入力ベクトルが内部に存在する部分空間)作 る必要がある。 ● 上限ベクトル、下限ベクトルを指定する def generate_ranges(vector): from_vector = [element -2.5 for element in vector] to_vector = [element + 2.5 for element in vector] return ",".join([str(x) for x in from_vector]), ",".join([str(x) for x in to_vector]) 下限ベクトル(入 力ベクトルの要 素から定数を引 いたもの) 上限ベクトル(入 力ベクトルの要 素に定数を足し たもの)

Slide 17

Slide 17 text

結果:グラタン ランキング 単語 1 グラタン 2 ベシャメルソース 3 ソテー 4 クロワッサン 5 フィリング

Slide 18

Slide 18 text

結果:落胆 ランキング 単語 1 落胆 2 驚愕 3 決心 4 あきらめ 5 裏切ら

Slide 19

Slide 19 text

デモ ● 脱脂粉乳 ● 麻耶 ● グラタン

Slide 20

Slide 20 text

結果 ● 速度はまあまあ ○ 近傍の距離に依存する ● 精度もとりあえず、似たのは取れている

Slide 21

Slide 21 text

課題 ● 絞り込みベクトルの大きさが8次元で固定 ○ Lucenceを再ビルドすると大きさを変更できるらしい(要検証) ○ https://github.com/textkernel/vector-search-plugin#intended-use-case に よると ‘by default 8, you can re-compile lucene with higher values..’ とは書 いてある。。。 ● ベクトルと、超低次元ベクトルの2つを作るのが面倒 ○ REST API にベクトル入れるとよしなに低次元ベクトルを作ってくれるとより便 利そう ○ 入力の高次元ベクトルに対して、低次元ベクトル数でmodを取得し足し合わせ るとか

Slide 22

Slide 22 text

今回の実験 ● 実験で使ったレポジトリ ○ 関連するプラグインを同梱したもの(KD木による絞り込み、コサイン類似度、イ ンデクス、検索 REST API 経由での文書追加) ○ URL: https://github.com/takahi-i/es_embedded_vector_search ● 自分では検証、改善する時間が取れないので、そのうち消します

Slide 23

Slide 23 text

お願い ● このやり方、Elasticsearchを利用してできるんで、よさそうではある ● 使えるようであれば、だれか洗練されたESプラグインを作ってくれると嬉しい(質門 をくれた知人にはそれを推薦したい)

Slide 24

Slide 24 text

まとめ ● Elasticsearch で(ある程度)高速に類似画像検索をする方法についていて調査、 検証しました ● (ある程度)簡単にElasticsearch上でKD木を併用した方法ができる ○ 2つベクトルフィールドを保持しないといけないけど。。。。 ○ とはいえ、Elasticsearchの提供する機能を利用できるのは良さそう

Slide 25

Slide 25 text

ご清聴ありがとうございました