OpenSearchCon North America 2024のセッション「How AI/ML is changing information retrieval」を日本語でまとめます。 可能な限り正確に内容を拾えるようにリスニングに努めたつもりですが、もし誤りがあればご指摘ください。
OpenSearchCon とは?
各セッションはYouTubeで視聴可能
How AI/ML is changing information retrieval
セッションリンクは以下
スピーカー
- John Handler
- AWSのシニアプリンシパルソリューションアーキテクト
- OpenSearchと検索技術について長年の経験を持つ
セッションまとめ
情報検索の歴史

情報検索の歴史は、人類が文字を記録し始めた時から始まっている。古代シュメールでは、官僚たちが粘土板に記録を刻み、それらの税務記録などを整理するためのインデックスを作成していた。古代ギリシャのアレクサンドリア図書館では、カリマコスという人物が「ピナケス」と呼ばれる巻物のインデックスを作成し、膨大な知識を整理していた。
近代になってもカード目録などで情報を整理し、司書が決めた主題分類に従って資料を探していた。しかし最終的には、図書館でも司書に相談することが最も効率的だった。司書は利用者の言葉を理解し、必要な資料を見つける手助けをしてくれる存在だったのである。
AI/MLによる検索の変革

現在、私たちはAI/MLの大きな波の中にいる。大規模言語モデル(LLM)は、生成能力だけでなく、自然言語をより良く扱うための埋め込み機能も提供している。これにより、チャットインターフェースやチャットボットが急速に普及し、情報検索のプロセスがより対話的になってきた。
従来の単発的な検索から、会話を通じて継続的にやり取りする形へと変化し、時間軸もより広がりを持つようになった。そして何より、インターフェースが自然言語中心になったことで、検索は「図書館の司書と話す」ような体験に近づいている。

ただし、すべての検索が自然言語による対話型になるわけではない。例えば「ML 64-7パイプフィッティング、8インチから1/4インチ」のような具体的な製品を探す場合は、従来通りの検索で十分である。このような具体的なクエリと、より対話的で抽象的なクエリの両方が今後も共存していくだろう。
レキシカル検索の基本

レキシカル検索では、単語が意味の単位となる。単語は非常に具体的なものから抽象的なものまで幅広く、検索エンジンに入力する単語を通じて、ユーザーは自分の意図を表現する。検索クエリは自然言語とともに、ファセットやその他の情報を含む形で、この目的を表現するものだ。
レキシカル検索の基本的な考え方は、単語の意味をマッチングして、その意味の一致に基づいて文書を取得することである。より多くの単語がマッチすればするほど良い結果となり、コーパス全体で希少な単語(より識別力の高い単語)ほど価値が高くなる。
検索のプロセスでは、ユーザーが得たい情報の目標を持ち、テキスト、ファセット、地理情報などを通じてそれを表現する。OpenSearchは、潜在的にマッチする可能性のある文書のセットをエンコードしており、何らかの類似性を計算してランク付けされた結果を返す役割を担う。
文書のエンコーディング

検索される文書は通常、フィールドと値のペアを持つJSON形式でエンコードされる。例として、Amazonの製品Q&Aデータセットを見てみよう。「私はかなり細身だが、SかMどちらがいいか?」という質問に対する回答と製品情報が含まれている。

このデータを検索可能にするため、すべてのテキストを結合したチャンクを作成する。レキシカル検索では、単語の意味を保持しながら、類似した意味の単位を単一の用語にまとめるための準備が必要だ。これは分析、ストップワードの除去、ステミング、同義語の追加によって実現される。
例えば「Miss Kobayashi's Dragon Maid Tohru Cosplay Dress Outfit」というテキストは、アナライザーによって意味を保持しながらトークン数を削減する処理が行われる。ステミングでは「runs」「run」「running」がすべて「run」に統一され、「a」や「the」のようなストップワードは検索価値が低いため削除される(ただし、「The The」というイギリスのバンド名のような例外もあり、特別な処理が必要になることもある)。
インデックスの仕組み

処理されたすべての用語からインデックスが作成される。インデックスは単一の用語を、その用語を含む文書のセット(ポスティングリストと呼ばれる)にマッピングする。分析を通じて処理されたデータは、フィールドごとのインデックスとしてポスティングリストを指すようになる。

クエリの処理例を見てみよう。「Miss Kobayashi」の文書と「very soft silicone vinyl stuffed PP cotton body baby」という赤ちゃん人形の文書があり、「cotton washable clothes」というクエリが来たとする。直感的には「Miss Kobayashi」がマッチすべきだろう。


まず、クエリも同じ分析処理を通す。インデックスと異なる用語でクエリを実行しても適切にマッチしないためだ。「cotton washable clothes」は「cotton」「washable」「cloth」に分析され、それぞれの用語でインデックスを検索する。
デフォルトでは、これらのポスティングリストに対してAND演算(積集合)が適用され、すべての用語を含む文書のみが結果として返される。OR演算(和集合)を使用することも可能で、その場合はより広範な候補が得られ、スコアリングによって最も関連性の高いものが上位に来るようになる。
スコアリングの仕組み

スコアリングには主に2つの要素がある。一つは各クエリ用語の価値を表すIDF(逆文書頻度)で、コーパス全体での出現頻度が高いほど値が小さくなる。もう一つはTF(用語頻度)で、特定の文書内での出現回数を表す。

実際に使用されるスコアリング関数はOkapi BM25と呼ばれるもので、TF-IDFの基本的な考え方に、文書長の正規化や出現回数の影響を調整する重み付けパラメータを加えたものだ。これにより、より精度の高いランキングが実現される。

レキシカル検索の典型的なフレームワークでは、ユーザーがクエリを入力し、結果を得る。満足できない場合は、異なる単語でクエリを書き直すという推測ゲームを繰り返すことになる。
ベクトル検索への進化

ベクトルが注目される理由は、大規模言語モデル(より正確にはトランスフォーマー)が単語やテキストブロックの意味を捉え、多次元空間にベクトルとして配置できるからだ。これにより、最近傍探索に基づいて関連する単語や概念をクラスタリングできる。

ベクトルとは、大きさと方向を持つ量である。例えば、XY軸上で7.21の大きさと56度の角度を持つベクトルを考えてみよう。Y軸をオレンジ、X軸をリンゴに対応させれば、リンゴはX軸上のベクトルとして表現できる。

従来のワンホットエンコーディングでは、各概念(リンゴ、オレンジなど)に対して1つの次元を割り当てていた。しかし、この方法では一般化ができない。「リンゴ+バナナ」や「赤色+リンゴ」のような組み合わせを表現できないのだ。

スパースコーディングでは、約30,000の用語に次元を削減し、ほとんどの軸をゼロに保ちながら一部に正の値を持たせる。これにより、ある程度の意味情報を維持しながら一般化も可能になる。

デンスコーディングでは、元の用語をすべて破棄し、バックプロパゲーションによって学習された概念を表す軸を使用する。例えば、赤、黄、果物という軸があれば、リンゴは「赤い果物」、バナナは「黄色い果物」として表現できるかもしれない。ただし実際はそこまで単純ではなく、各次元が何を意味するかを直接的に解釈することは困難だ。

一般化の能力は高いが、そこには過度の一般化やハルシネーションの問題が存在する。テキスト生成のLLMに何らかのプロンプトを送ると、必ず何かしらの答えを作り出してしまう。これは統計的に単語を生成しているだけなのだ。この点が実際のブレイキングポイントであり、我々が対処しなければならない問題となっている。一般化は良いことだが、同時にハルシネーションも発生してしまうのだ。
これは非常に複雑な空間である。信じられないほど非線形で、n次元を持ち、その空間を特徴付けるために数百万、数十億のパラメータが必要となる。そして複雑な関数があり、それがバックプロパゲーションによる学習なのである。
検索空間への応用

これらのレキシカルとベクトルの概念を検索空間に戻して考えると、私たちにとって本当に重要なのは、これが言語をどのようにコーディングし、特定の文書を参照してマッチングを行うために使用するかということだ。新しいパラダイムとして、良好な検索を提供するスパースベクトルを生成するスパースモデル、テキストを超えて画像もその空間にエンコードできるマルチモーダル、そしてハイブリッド検索がある。
ハイブリッド検索は最も重要なものとして浮上してきている。ハルシネーションや、非常に具体的なものと非常に抽象的なものの識別能力について話したように、レキシカル(非常に具体的)とセマンティック(非常に抽象的)を適用し、それらの結果を組み合わせることが、実際に非常に良い戦略であることが分かっている。そして会話型は、我々が向かっている方向の一つである。
ベクトル検索の基本的な仕組み

これらの新しいパラダイムを実現する基本的な考え方は、データベースに格納された情報に対してLLMを使用してベクトルを作成することだ。データのチャンク化、図表やその他の情報を統合する準備フェーズがある。すべてがエンコードされる。
ユーザーは欲しい情報の目標を持ってやってくる。単語、構造化情報、場合によっては画像も含まれる。これらすべてをベクトルモデルに通して、その大きなベクトル空間内の点を作成し、その近傍を見つける。そして依然としてランク付けされた結果を得るが、私にとってこれは言語に関することだ。つまり、何を探しているかをどのように指定するかということなのだ。
ベクトル類似性の概念

ベクトル類似性について話すとき、多次元空間を持ち、それをごちゃごちゃと2次元に投影すると(これは難しいことだが)、このような図が得られる。これが概念を示している。関連する概念のクラスターが、その大きな多次元空間内に大まかに存在することになる。
つまり、類似した文書であれば、他の類似した文書の近くにエンコードされるということだ。マッチを見つけたい場合は、クエリをこの空間に投影し、最近傍を見つける。それがマッチとなる。
OpenSearchのNeuralプラグイン


OpenSearchにはNeuralプラグインがあり、このプロセスを支援する。通常のフェーズでは、Neuralプラグインを使用していない場合、常にソース文書を取得し、チャンクを作成し、モデル(おそらくBedrock)に送信する。それによって、その文書を表す1つまたは複数のベクトルを含む拡張文書が得られ、K-NNインデックスに格納される。
クエリ側では、ユーザークエリを取得し、モデルに通してベクトルを得て、K-NN検索を実行し、検索結果を得る。Neuralプラグインは、文書の埋め込みとクエリの埋め込みをバイパスすることで、これを簡単にする。つまり、使いやすくするためのシンタックスシュガーのようなものだ。

使用する際は、インジェストパイプラインにNeuralプラグインが必要になる。インジェストパイプラインはインデックスに付属し、インデクシングのために来るすべての文書をこのパイプラインに送る。パイプラインはモデルIDに基づいてモデルに渡す。検索側では、検索パイプラインもある。これにより、この場合はレキシカルとベクトルの両方に対して実行されるハイブリッドクエリを取得し、算術平均でスコアを組み合わせることができる。スコアの正規化も行われる。
ハイブリッド検索の実装

ハイブリッド検索を図式的に見ると、組み合わせを行うNLP検索パイプラインであると言える。クエリは「hybrid」と指定するだけだ。実際には、ハイブリッドはレキシカルとベクトルの2つのクエリだけを実行する必要はない。ハイブリッドクエリは最大5つの句を取り、算術または正規化と、それらすべてのポストプロセッシングを適用する。つまり、複数のベクトルクエリ、複数のレキシカルクエリがある場合でも、ハイブリッドを通してブレンディングを実行できる。
スパース検索の設定



スパース検索の場合、パイプラインを定義する。インデックスを定義する際に、スパースパイプラインを使用したいと指定する。そのインデックスに対してrank_featuresタイプのフィールドを指定し、それがスパースエンコーディングを取得する。テキストのチャンクを送信すると、スパースエンコーディングされる。
インジェストパイプライン側では、インジェストパイプラインに適用するスパースエンコーディングプロセッサがある。これによりエンコーディングが実行される。クエリ側では、ニューラルクエリで、チャンキングフィールドに対して実行している。これによってスパースコーディングとクエリを実行する。
マルチモーダル検索

マルチモーダルも非常に似た仕組みだ。これらすべてを見せたかったのは、機械的にはそれほど難しくないことを示すためだ。特にNeuralを使用している場合は簡単だ。インジェスト側では、マルチモーダルを実行し、モデルを指定し、テキスト埋め込みのフィールドと画像埋め込みのフィールドを指定する。
クエリ側では、2つの埋め込みを送信するだけで、クエリが実行される。
会話型検索

OpenSearchの一部として会話型検索もある。これにより、基本的にRAGを簡単にセットアップできる。RAGを知らない人のために説明すると、検索拡張生成(Retrieval Augmented Generation)のことだ。小さなチャットインターフェースがあり、テキストを送信する。
この場合、OpenSearchはインデックス内の文書を検索し、LLMがハルシネーションではなく実際の情報を伝えることができるようにコンテキストを提供する。これらの検索結果をLLMに送信する。LLMはハルシネーションではなく実際の情報を伝えることができる。
OpenSearchには会話メモリも組み込まれている。メモリIDを使用すると、そのメモリを指定するだけで、それもLLMへのコンテキストの一部となる。
デモの実装詳細

実際のコードを見てみよう。これはOpenSearchクライアントを使用している。OpenSearchには素晴らしいクライアントライブラリがあり、Pythonクライアントも含まれている。ここではメモリ機能を有効にし、RAGパイプラインを有効にし、データを送信する信頼できるコネクタエンドポイントを設定している。

コネクタの例を見ると(画面に収まっていないが)、リポジトリ(MLコモンズリポジトリだと思う)にブループリントがある。これらのブループリントにより、基本的にコネクタをセットアップできる。これはコネクタに何をすべきかを伝えるもので、ここではBedrockの認証情報、送信するパラメータ(またはプラグインが送信するパラメータ)、実行するアクション(この場合は予測)が含まれている。

非常に重要なのは、プリプロセッサとポストプロセッサだ。これは内部で実行されるが、リクエストボディによってそこに入り込むことができる。プロンプトをハックしようと試みることもできるが、単にプロンプトを渡すだけだ。そしてモデルを登録してデプロイする。これをコネクタとして送信する。

検索用のパイプラインを追加する。これはRAGパイプラインになる。レスポンスプロセッサ、検索拡張生成、会話デモだ。ここにコネクタから来るモデルIDがある。LLMに検索結果からコンテキストを提供するために使用したいフィールドがある。そしてユーザープロンプトとユーザー指示がある。


これはAmazon PQAデータで、質問と回答のセットだ。私がここで行ったのは、実際に質問を検索に使用したことだ。これを私の図書館司書のように考えて、話しかけたい。「このヘッドフォンは使えるか?」のようなことを言いたい。質問へのマッチに基づいて、質問と回答である検索結果を取得する。
私のインデックスには特別なものはない。検索用のデフォルトパイプラインがそのRAGパイプラインでなければならないだけだ。イヤホン、ヘッドセット、ディフューザー、マットレス、MP3とMP4プレーヤー、シーツと枕カバーから選んだ。これらの質問のカテゴリは約100種類ある。

クライアントを使用し、JSONを1行ずつ読み取るセットを作成し、PQAデータから読み取る。各カテゴリから5,000件を送信し、クイックループを実行する。
会話コンテキストを作成し、これらのスリープは私のラップトップが自分自身と話すのが速すぎるため、インデックスが十分に速く定着しないので、いくつかスリープを入れる必要があった。しかし、基本的にはwhileループで、このリクエストを送信する。ここでのクエリは、入力したものに対する質問テキストに対する単純なクエリ文字列だ。
そして、この生成QAパラメータを指定する。LLMモデルはBedrock Claudeで、質問とメモリIDを渡し、レスポンスを得る。
デモの実行結果

実際に実行するとレキシカル検索を行っているため、質問がより具体的な場合により良い回答が得られる。
質問は「H2 251n PN 70はサウンドをフィルタリングするのに良いか?」だった。LLMを使用して回答を生成している。

返ってきた検索結果は、ソフトツインシートセット、Skull Candy P54、Xbox、ホテルラグジュアリーベッドシーツ、HyperXクラウドゲーミングなど、様々なものだ。それがLLMへのコンテキストの一部となる。
そして、「結果に基づくと、アクティブノイズキャンセレーションはなく、外部音をフィルタリングするのに特に優れているわけではないようだ。レビューは音楽やゲーミングの品質に焦点を当てている」といった回答が得られる。

これらすべてがOpenSearchにバンドルされており、クエリを指定してその回答を得る方法の例を示している。
検索の今後

検索の全体的なスタックを見ると、上部にソースと処理がある。これは重要なステップだ。入れなかったものは取得できない。そのデータをどのように入れ、どのように構造化するかが、どのように取得できるかを制御する。
入力時の埋め込みの構築だけでなく、チャンキングもある。情報の関連する部分は何か、それらをマッチ可能にするためにどのように切り分けるかを把握する必要がある。エンリッチメントもある。eコマースサイトの在庫情報など、その文書に集める可能性のある他のあらゆる情報がある。そして、通常はUIやアプリケーションとのインタラクションを駆動する正規ストアに入る。
中央のクエリパスでは、クエリが入ってくる。そのクエリでできることはたくさんある。この人が以前このデータとどのようにやり取りしたかを知っている可能性がある。ブランドやその他の観点での好みがある可能性がある。そのため、そのクエリを書き換える可能性がある。
そして、いつかアルゴリズム選択を通過する。このクエリはどの程度具体的か、どの程度抽象的か、マッチする適切なフィールドは何か、適切な要因は何か、そのすべてだ。クエリの通常の分析、クエリのマッチング、クエリのランキング、リランキングがある。
これは特にLLMやセマンティック検索のコンテキストでよく出てくる。ランカーを使用して結果を改善したい。しかし、よく起こることは、そもそも最初に素材を取得していなかったということだ。ランキングが実際にオブジェクトを生成していない、マッチングがオブジェクトを生成していないのだ。すでにランク付けしなかったものを再ランク付けすることはできない、というのが私のポイントだ。
このチェーン全体が、フロントエンドに送られる検索結果のリストで終わる。そこから、実際にそれらの結果を取得し、ユーザーが何をしているかを追跡したい。誰かが3番目の結果をクリックしたか?それは2番目や1番目の結果をクリックするよりも良くない。しかし、そのすべてを把握したい。人々が何をクリックしているかを知りたい。eコマースであっても、単なる情報検索であっても、そのすべての行動を取得し、ポストプロセスし、クエリにロールバックするだけでなく、文書をエンリッチするためにも使用する。このものがホットだ、トレンドだ、といったことを知るためだ。
現在発展中のいくつかの要素がある。ユーザー行動インサイトに取り組んでおり、インタラクション行動をOpenSearchに送信しやすくしている。リランキング選択もある。リランキングはあるが、実際にどのリランキングアルゴリズムを使用するか、どれが実際に最良の最終結果を与えるかを把握することは、まだこれからだ。

私は本を書いた。この講演の多くは、その本から直接引用している。外のデスクに座って配布する予定なので、本が欲しい人は取りに来てほしい。

検索は私にとっては常に言語との繋がりだ。私たちがどのように話し、理解するか。言語処理の観点から来たものが、そのセマンティックマッチングを可能にした。それが自然言語を相互作用の手段にしている。情報とのやり取り方法は実際に変化しており、今後も変化し続けるだろう。