Azure Cognitive Search のベクトル検索とハイブリッド検索の比較デモネタ

# はじめに
[Azure OpenAI Developers セミナー第2回](https://www.youtube.com/watch?v=cEynsEWpXdA)でも語らせていただきました、Azure Cognitive Search のベクトル検索、ハイブリッド検索、セマンティックハイブリッド検索のデモネタについて紹介します。

Azure Cognitive Search がベクトル検索に対応したことで、クラシカルなキーワード検索と組み合わせたハイブリッド検索が可能になりました。今回はいつもの[戦国武将データセット](https://github.com/nohanaga/busho-index/blob/main/TextChunking_Embeddings_RegisterIndex.ipynb)を例に、それぞれの機能を比較していきましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/674344/e9491356-c216-02c0-2e73-58520dedbafa.png)

https://github.com/nohanaga/busho-index/blob/main/TextChunking_Embeddings_RegisterIndex.ipynb

# ベクトル検索
text-embeddings-ada-002 で生成した Embeddings を格納します。
![001.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/674344/26c5677a-e7a6-e0e5-5db7-df8a55c433f1.png)
「源実**友**の**お歌**にはどのような特徴があったのでしょうか？」というわざとスペルミスを入れたり和歌をお歌と言い換えたりしたクエリーで検索をかけます。`ja.lucene` というスタンダードな[日本語アナライザー](https://qiita.com/nohanaga/items/7296505f7b63e23f94a6#lucene-%E6%97%A5%E6%9C%AC%E8%AA%9E%E3%82%A2%E3%83%8A%E3%83%A9%E3%82%A4%E3%82%B6%E3%83%BC)に搭載されている辞書ベースのトークナイザーによって、これらのトークンに分解されます。このトークンを用いて転置インデックスが構築されます。

次に TF/IDF ベースの BM25 スコアリングアルゴリズムによって、文章中からトークンの一致頻度を見て関連性スコアが決定されます。細かくはトークンのレア度や文章中の密度なども重みづけされます。キーワード検索では、わざとスペルミスしたので、**源実朝**という一つのトークンにならずに、**一文字ずつのトークンになって関係ない人名の部分でヒット**してしまったりしてますね。また、「特徴」というワードをそのまま検索しており、文章中に「和歌の特徴」とあからさまに書いていない限りこのワードは意味を成しません。残念ながらこの検索方法では、「和歌の特徴を知りたい」というユーザーの意図は考慮されません。

ベクトル検索ではどうでしょうか。`text-embeddings-ada-002` で生成したベクトルを検索すると、キーワードの一致にとらわれずに、テキストの類似性だけを見て検索しています。スペルミスもいい具合に無視されて検索されてますね。図の赤枠が欲しい答えです。

また、キーワード検索とベクトル検索の融合であるハイブリッド検索ではトップに来ていますね。ベクトル検索はこのような**キーワードの一致にとらわれずに意図を検索したいとき**に効力を発揮します。

# ハイブリッド検索
![002.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/674344/42efd28a-38aa-4bf2-1dec-e6ece57c6540.png)

「源実朝は征夷大将軍として知られているだけでなく、ある有名な趣味も持っています。それは何ですか。」という人間が自然言語でチャットに問いかけるスタイルで、そのまま検索にかけてみます。普通は検索エンジンにこんな長いクエリーで聞きませんが、これからのチャット UI 時代を見据えた使い方です。

キーワード検索では、**源実朝**や**征夷大将軍**が入っている文書が上位に来ていますね。欲しい文書は得られてません。この検索方法では、趣味を知りたいという検索者の意図は考慮されません。そもそも歴史文書に「趣味」なんて書き方しませんよね。

残念ながらベクトル検索でも、源頼朝の文書が上位に来てしまい、欲しい文書はこちらでも得られません。**征夷大将軍**にひっぱられすぎです。欲しいドキュメントチャンク「源実朝-0.txt」の実際の順位は、キーワード検索が 13 位、ベクトル検索が 9 位でした。上位 3 件を ChatGPT に渡す作りをしていた場合、回答できませんね。

では、これをハイブリッド検索すると…　あら不思議、欲しい文書「源実朝-0.txt」が 2 位に浮上してきました。ふむ…　なぜなんだ🤔と

## Reciprocal Rank Fusion(RRF)
このハイブリッド検索ではどのように検索スコアを計算しているかといいますと、まずキーワード検索のスコアには [Okapi BM25 アルゴリズム](https://ja.wikipedia.org/wiki/Okapi_BM25)によるスコアが、ベクトル検索ではコサイン類似度をスコアとして用いており、両者異なるスコアです。これを融合するシンプルな計算式として、[Reciprocal Rank Fusion(RRF)](https://plg.uwaterloo.ca/~gvcormac/cormacksigir09-rrf.pdf) が採用されています。

![003.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/674344/9ead764e-1841-1322-16b7-c051e6d911fa.png)
RRF は両者の文書ランクの逆数の和を取ります。つまり、**その文書の順位だけを見て、どちらのランキングでも上位にきていればスコアが高くなる**仕組みです。

なので欲しかった「源実朝-0.txt」のドキュメントは両方で上位だったので、ここで浮上したということですね。*k* は定数ですが、私の計算では *k*=59 でスコアが一致します。

# セマンティックハイブリッド検索
これで終わりではありません。Azure Cognitive Search の最終兵器と言っても過言ではない、セマンティックハイブリッド検索について紹介します。

![004.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/674344/42cdcdf4-9582-5131-9b61-67de4fe06df0.png)

「１３人の合議制に含まれるメンバー一覧」という、検索クエリーだけでは答えを探しにくいクエリーで検索をかけます。わざと数字を全角で記述しているためキーワード検索では数字は壊滅的ですが、合議制がヒットしてるのでまぁまぁですね。ただしメンバー一覧が欲しいという意図は一切考慮されておりません。

ベクトル検索でも一覧は得られませんでした。ただし 1 位に源頼家の十三人の合議制に言及するチャンクが得られており、まぁまぁの結果です。3 位の源実朝の一覧は間違いです。これは源実朝が右大臣になった際の拝賀式の参列者一覧です。

ではセマンティックハイブリッド検索はというと、セマンティック検索機能に搭載されている Microsoft 製のLLMである Turing モデルが文章中から抽出的要約タスクを実行し、この**意図の答えとなるチャンクの断片をピンポイントで返してくれる**「[セマンティックアンサー](https://qiita.com/nohanaga/items/4b9506c62b9e5796f405#%E3%82%BB%E3%83%9E%E3%83%B3%E3%83%86%E3%82%A3%E3%83%83%E3%82%AF%E6%A4%9C%E7%B4%A2)」が生成されました。

さらにキーワード検索でもベクトル検索でも得られなかった合議制の構成メンバーの羅列を含む断片を抽出するだけでなく、ハイライトもしてくれてますよね。これをセマンティックハイライトといいます。まさにこのセマンティックハイライトでマークアップされている部分が欲しかった回答に近いです。

セマンティックアンサーは検索ランキングとは別に、精度が十分に高い水準に達した場合のみ返却されます。ピンポイントでアンサーが見つからなかった場合でも、チャンクの中から抽出的要約を用いて、[セマンティックキャプション](https://qiita.com/nohanaga/items/4b9506c62b9e5796f405#%E3%82%BB%E3%83%9E%E3%83%B3%E3%83%86%E3%82%A3%E3%83%83%E3%82%AF%E3%82%AD%E3%83%A3%E3%83%97%E3%82%B7%E3%83%A7%E3%83%B3)（青枠）を生成してくれます。こちらもチャンクの中から、意図に近いセクションを抽出してくれていることが分かります。

そしてランカースコアについては、ハイブリッド検索の結果上位 50 件を、[セマンティックランカー](https://qiita.com/nohanaga/items/4b9506c62b9e5796f405#%E3%82%BB%E3%83%9E%E3%83%B3%E3%83%86%E3%82%A3%E3%83%83%E3%82%AF%E3%83%A9%E3%83%B3%E3%82%AD%E3%83%B3%E3%82%B0)という機能がリランク（並び替え）て新たにスコアを生成しています。この機能は非常にコストの高い処理をしているので、検索には時間がかかる点にはご注意いただきたいと思います。実装としては、通常検索で回答が得られなかったときに、2 段階目で実行するなどの、インタラクションを UI に入れるなどすると自然かもしれません。

# 検索クエリーについて
https://qiita.com/nohanaga/items/f710cac82072b63bc73f#%E3%82%AF%E3%82%A8%E3%83%AA%E3%81%AE%E5%AE%9F%E8%A1%8C%E4%BE%8B

# おわりに
お持ちのデータやユーザークエリーの特性に応じて、利用する検索アルゴリズムを検証いただいて組み合わせていただければと思います。