こんにちは、@shin0higuchiです😊
業務では、Elasticsearchに関するコンサルティングを担当しています。
最近すっかり春らしく、暖かくなってきました。
新年を迎えたばかりの感覚でしたが、あっという間に時が経ちますね。
さて、今回の記事では、Elasticsearchの検索を根本的に変える可能性を秘めたセマンティック検索に関して書かせていただきます。
概要
Elasticsearchは元々、キーワードベースのアプローチを主に取っており、クエリで指定されたキーワードを対象のドキュメント内で検索し、それらの出現頻度や位置に基づいて結果をランク付けしています。この方法では、文脈や意図に関係なく、単純にキーワードの一致度に基づいて検索結果が返されます。
一方、セマンティック検索とは、ユーザーのクエリの背後にある文脈と意図を理解しようとする検索手法で、キーワードだけに頼るのではなく、より正確で関連性のある結果を提供できます。このアプローチでは、自然言語処理(NLP)や機械学習(ML)技術を活用して、単語や概念の関係やコンテンツの意味を理解し、検索クエリを最も適切な結果とよりよくマッチさせることができます。
最近GPTなどを始めとするLLM(大規模言語モデル)の話題をよく耳にするかと思いますが、それに類する学習済みモデルの力をElasticsearchの検索に活かすことが出来る、というお話です。
今回はGoogleが開発したBERTというモデルをベースにした学習済みモデルを実際に取り込んで利用します。
Elasticsearch 8.7 で何が変わったか?
実を言うとこれまでも、PyTorchの学習済みモデルをElasticsearchに取り込んで、一部のNLPタスクに利用することは可能でした。
たとえば 過去にElasticsearch勉強会で発表させていただいた通り、質問応答のタスクなどにも対応しています。
speakerdeck.com
これまでセマンティック検索を活用しづらかった背景としては、「クエリを事前にベクトル化(embedding)してからElasticsearchに問い合わせる必要がある」という制約が挙げられます。
つまり、せっかく機械学習モデルをElasticsearchに取り込んでも、一度外部でベクトル化をおこなってから検索しなければならなかったのです。
Elasticsearch 8.7 では、検索のリクエストに文字列を渡してモデルを指定すると、検索と一緒にベクトル化もおこなってくれるようになりました。
なお、日本語解釈の精度を課題と考えている方もいらっしゃると思いますが、取り込む学習済みモデルに依存するためここでは深く言及しません。また、今回は後述の通り日本語データで学習したモデルを利用することである程度検索精度を上げられる想定です。
具体的な利用方法
モデルの取り込み
まずは下準備です。
PyTorchモデルを Elandというライブラリを利用して取り込みます。
github.com
今回はHugging Faceで公開されている学習済みモデルを利用します。
cl-tohoku/bert-base-japanese-v2 は日本語のWikipediaで学習したモデルですので、日本語の検索で効果を発揮してくれることを期待しましょう。
eland_import_hub_model --url https://XX.XX.XX.XX:9200 --hub-model-id cl-tohoku/bert-base-japanese-v2 -u elastic --task-type text_embedding -p XXXX --ca-certs XXXX
取込みが完了したモデルは、次のようにスタートのエンドポイントを叩くことで利用可能になります。
POST _ml/trained_models/cl-tohoku__bert-base-japanese-v2/deployment/_start
取り込んだモデルは、「Trained Models」のメニューから確認することができます。
※lang_ident_model_1 というモデルも表示されますが、こちらはデフォルトで入っているモデルであり、今回の操作とは無関係です。
サンプルデータの登録
今回は当ブログ、Taste of Tech Topicsの記事を取り込んでみます。
ひとまずタイトルと本文を検索対象にしてみましょう。
Elasticsearchでは、ingest pipeline という機能を利用することで、ドキュメント登録時にベクトル化処理をおこなうことが可能です。
inference processor で、先ほど取り込んだモデルのID「cl-tohoku__bert-base-japanese-v2」を指定します。
また、最終的には vector.content, vector.title といったフィールドに値を入れたいので、rename processorも利用しています。
PUT _ingest/pipeline/text_embedding { "processors": [ { "inference": { "target_field": "inference.content", "model_id": "cl-tohoku__bert-base-japanese-v2", "inference_config": { "text_embedding": {} }, "field_map": { "content": "text_field" } } }, { "rename": { "field": "inference.content.predicted_value", "target_field": "vector.content" } }, { "inference": { "target_field": "inference.title", "model_id": "cl-tohoku__bert-base-japanese-v2", "inference_config": { "text_embedding": {} }, "field_map": { "title": "text_field" } } }, { "rename": { "field": "inference.title.predicted_value", "target_field": "vector.title" } } ] }
次に、ElasticsearchのMapping(スキーマ)を定義しておきます。
ポイントとしては、dense_vectorというデータ型でフィールドを定義する点です。
今回は詳細な説明を省きますが、 indexパラメータをtrueにすること、similarityパラメータを指定することが必要になります。
k-nearest neighbor (kNN) search | Elasticsearch Guide [8.7] | Elastic
PUT blogs { "mappings": { "_source": { "excludes":["vector.title", "vector.content", "content"] }, "properties": { "title": { "type": "text", "analyzer": "kurmoji" }, "content": { "type": "text", "analyzer": "kurmoji" }, "vector":{ "properties": { "title": { "type": "dense_vector", "dims": 768, "index": true, "similarity": "l2_norm" }, "content": { "type": "dense_vector", "dims": 768, "index": true, "similarity": "l2_norm" } } } } } }
続いてドキュメントを登録します。
今回は次のように、Bulkリクエストで登録をおこないます。先ほど登録したパイプラインを指定するようにしましょう。
POST blogs/_bulk?pipeline=text_embedding {"index":{}} {"title": "XXXX", "content": "XXXX"} {"index":{}} {"title": "XXXX", "content": "XXXX"} ...
検索してみる
まず最初は、ベクトル検索を利用せずに「GPT3による質問応答システム」というクエリで全文検索を行ってみます。
GET blogs/_search { "query": { "multi_match": { "query": "GPT3による質問応答システム", "fields": ["content", "title"], "operator": "or" } } }
この際の検索結果は次のようになりました。
順位 | タイトル |
---|---|
1 | GPT-3を使って根拠付きで正確に質問応答してくれるシステムを作ってみる |
2 | GPTが出した回答の確からしさを見えるようにしてみる |
3 | 第49回Elasticsearch勉強会で、ElasticsearchによるNLP(質問応答)の発表をしてきました |
4 | コンテナ内のアプリが複数種類のログを出力する場合の収集方法 |
5 | GPT-3を使って自分だけのAIアシスタントを作る第一歩 |
6 | Azure Container Instancesを使ってAngular+FastAPIなWebアプリを動かしてみた |
7 | Karateに性能試験とUI試験を任せてみる |
8 | 新しいデータ基盤アーキテクチャである「データレイクハウス」について調べてみた |
9 | 特徴量エンジニアリングのライブラリ xfeat を使ってみて便利だったこと |
10 | NFLのPlayer Contact Detectionで金メダル獲得&コンペ振り返り |
この全文検索の結果では、「GPT3による質問応答システム」というクエリを形態素解析した結果でOR検索をおこなうため、GPT-3に関係ない質問応答関連の記事も上位に来てしまいます。
たとえば「第49回Elasticsearch勉強会で、ElasticsearchによるNLP(質問応答)の発表をしてきました」という記事が3番目に来ていますが、この記事はGPT-3に関連が無く、望んだ検索結果ではありません。
ここにセマンティック検索を追加することで検索結果が改善するかを見てみましょう。
セマンティック検索を利用するには通常の search APIにknnパラメータを指定して利用する形になります。以下のようなリクエストになります。
num_candidates の件数を各シャードで集めて、その中でスコアの高い k件を返却します。
GET blogs/_search { "query": { "multi_match": { "query": "GPT3による質問応答システム", "fields": ["content", "title"], "operator": "or" } }, "knn": [ { "field": "vector.title", "k": 3, "num_candidates": "100", "query_vector_builder": { "text_embedding": { "model_id": "cl-tohoku__bert-base-japanese-v2", "model_text": "GPT3による質問応答システム" } } }, { "field": "vector.content", "k": 3, "num_candidates": "100", "query_vector_builder": { "text_embedding": { "model_id": "cl-tohoku__bert-base-japanese-v2", "model_text": "GPT3による質問応答システム" } } } ] }
以下のような結果が返ってきました。
順位 | タイトル |
---|---|
1 | GPT-3を使って根拠付きで正確に質問応答してくれるシステムを作ってみる |
2 | GPTが出した回答の確からしさを見えるようにしてみる |
3 | GPT-3を使って自分だけのAIアシスタントを作る第一歩 |
4 | 第49回Elasticsearch勉強会で、ElasticsearchによるNLP(質問応答)の発表をしてきました |
5 | Azure Container Instancesを使ってAngular+FastAPIなWebアプリを動かしてみた |
6 | Karateに性能試験とUI試験を任せてみる |
7 | NFLのPlayer Contact Detectionで金メダル獲得&コンペ振り返り |
8 | 新しいデータ基盤アーキテクチャである「データレイクハウス」について調べてみた |
9 | Rustでトライ木による辞書検索のベンチマークをとってみた |
10 | 特徴量エンジニアリングのライブラリ xfeat を使ってみて便利だったこと |
初回の全文検索のみの検索結果と、knnによる検索を加えた後の検索結果を並べて比較してみましょう。
先ほどよりもGPT-3関連の記事がより上位に来ており、検索結果としては意図したものに近くなった印象があります。
順位 | 全文検索のみの場合 | ベクトル検索を追加した場合 |
---|---|---|
1 | GPT-3を使って根拠付きで正確に質問応答してくれるシステムを作ってみる | GPT-3を使って根拠付きで正確に質問応答してくれるシステムを作ってみる |
2 | GPTが出した回答の確からしさを見えるようにしてみる | GPTが出した回答の確からしさを見えるようにしてみる |
3 | 第49回Elasticsearch勉強会で、ElasticsearchによるNLP(質問応答)の発表をしてきました | GPT-3を使って自分だけのAIアシスタントを作る第一歩 |
4 | コンテナ内のアプリが複数種類のログを出力する場合の収集方法 | 第49回Elasticsearch勉強会で、ElasticsearchによるNLP(質問応答)の発表をしてきました |
5 | GPT-3を使って自分だけのAIアシスタントを作る第一歩 | Azure Container Instancesを使ってAngular+FastAPIなWebアプリを動かしてみた |
6 | Azure Container Instancesを使ってAngular+FastAPIなWebアプリを動かしてみた | Karateに性能試験とUI試験を任せてみる |
7 | Karateに性能試験とUI試験を任せてみる | NFLのPlayer Contact Detectionで金メダル獲得&コンペ振り返り |
8 | 新しいデータ基盤アーキテクチャである「データレイクハウス」について調べてみた | 新しいデータ基盤アーキテクチャである「データレイクハウス」について調べてみた |
9 | 特徴量エンジニアリングのライブラリ xfeat を使ってみて便利だったこと | Rustでトライ木による辞書検索のベンチマークをとってみた |
10 | NFLのPlayer Contact Detectionで金メダル獲得&コンペ振り返り | 特徴量エンジニアリングのライブラリ xfeat を使ってみて便利だったこと |
3位までにGPT関連の記事がランクインするようになりました。
4位以降はある程度質問応答に関する内容が書いてあったり、下位の記事は「システム」といった一般的な単語によってスコアが上がっているものと思われます。
簡単な検証ではありますが、8.7の新機能を利用することで、より意図したものに近い検索結果を得ることができるようになりました。
最後に
いかがだったでしょうか?
今回ご紹介したセマンティック検索を用いれば、キーワードベースの検索だけではうまく類似度が取れないドキュメントも、上手くヒットさせることができるケースがありそうです。
言語モデルの発達は近年すさまじいスピードで進んでいます。
Elasticsearchの検索にも当たり前に取り込まれるようになる時代が来ていると思いますので、是非活用してみてください。
今回の記事は以上となります。最後までお読みいただきありがとうございました。
4/18追記:
本記事に関して第53回のElasticsearch勉強会で発表させていただくことになりました。ご興味ございましたら是非ご参加ください。
www.meetup.com
Acroquest Technologyでは、キャリア採用を行っています。
- ディープラーニング等を使った自然言語/画像/音声/動画解析の研究開発
- Elasticsearch等を使ったデータ収集/分析/可視化
- マイクロサービス、DevOps、最新のOSSを利用する開発プロジェクト
- 書籍・雑誌等の執筆や、社内外での技術の発信・共有によるエンジニアとしての成長
少しでも上記に興味を持たれた方は、是非以下のページをご覧ください。世界初のElastic認定エンジニアと一緒に働きたい人Wanted! - Acroquest Technology株式会社のデータサイエンティストの採用 - Wantedlywww.wantedly.com