Azure AI Search の最新セマンティックランカーとクエリリライティングで精度を向上

Ignite 2024 にて Azure AI Search に RAG の精度を向上させるための以下の新機能が搭載されたことを発表しました。

- セマンティックランカーモデル（SR）の精度向上
- クエリ時のクエリリライティング（QR）の実装

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/674344/125e71ed-091e-974a-bbdb-a006d298d012.png)


クエリリライティングは現在米国東部、北ヨーロッパ、東南アジアリージョンでのみ利用可能です。セマンティック ランカーが有効になっている Basic レベル以上の検索サービスである必要があります。

# クエリエンジンアーキテクチャ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/674344/4e3572bf-b22c-a380-61c3-0ab23ec59399.png)


# 精度評価
いつも使っている [MIRACL](https://qiita.com/nohanaga/items/e159712acbf80e40ca1a#%E3%83%99%E3%82%AF%E3%83%88%E3%83%AB%E6%A4%9C%E7%B4%A2%E3%81%AE%E7%B2%BE%E5%BA%A6%E8%A9%95%E4%BE%A1) 日本語データセットを用いて Recall を測定します。

## 条件
- **model**: `text-embedding-3-large`
- **dimension**: `3072`
- **k_nearest_neighbors**: `5`
- **top**: `5`


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/674344/626dcff2-58fb-af2b-e828-137252b44d0b.png)

このテストでサンプリングしたデータではベクトル検索が有効でした。Recall は 0.813 ですでに高い精度ではあるのですが、**新しいセマンティックランカーとクエリリライティングがその記録を打ち破りました**。ベクトル単体、ハイブリッドで精度が頭打ちになってしまっているパターンで検証をお勧めします。

### ハイブリッド検索＋セマンティックランカー（SQ）
0.628 -> 0.847 (+0.219)

### ハイブリッド検索＋クエリリライティング（QR）＋セマンティックランカー（SQ）
0.628 -> 0.859 (+0.231)

クエリリライティングは増やせばいいというわけではないようです。（10件＜5件）

# クエリリライティング
クエリリライティングとは、生成 AI を使って元のクエリを拡張し、用語を追加して検索結果を調整する機能です。このために専用にファインチューニングした SLM が搭載されています。クエリに入力ミスまたはスペルミスが含まれる場合やシノニムを追加したい場合などに有効です。

## コード
Python SDK を用いる場合は、`11.6.0b8` 以降を使用します。
`pip install azure-search-documents==11.6.0b8`

```python
docs = search_client.search(
    search_text=None,
    vector_queries= [vector_query],
    select=["text,docid"],
    query_type=QueryType.SEMANTIC, 
    semantic_configuration_name='default', 
    top=top,
    debug="all",
    query_language="ja-JP",
    query_rewrites="generative|count-5"
) 
```

`query_rewrites` を "generative|count-5" に設定すると 5 つ分のクエリ書き換えを生成します。最大値は 10 です。`query_language` は日本語に対応しており、`ja-JP` を設定します。

Python SDK のレスポンスで `queryRewrites` をキャプチャするために以下のような logger をセットします。

```python
import sys
import logging
# Create a logger for the 'azure' SDK
logger = logging.getLogger('azure')
logger.setLevel(logging.DEBUG)

# Configure a console output
handler = logging.StreamHandler(stream=sys.stdout)
logger.addHandler(handler)
```

```json
{"semantic": null, "queryRewrites": {"text": {"inputQuery": "平清盛の出身はどこ？\\t", "rewrites": ["平清盛の家系", "平清盛の出身地", "平清盛はどこで生まれたか", "平清盛の歴史的出身", "平清盛の血統"]}, "vectors": []}}
```

## 生成結果
#### query_rewrites: `generative|count-5`
元クエリ:`平清盛の出身はどこ？`
生成クエリ:`["平清盛の家系", "平清盛の出身地", "平清盛はどこで生まれたか", "平清盛の歴史的出身", "平清盛の血統"]`

#### query_rewrites: `generative|count-10`
`["平清盛の家系", "平清盛の出身地", "平清盛はどこで生まれたのか", "平清盛の血統", "平清盛はどこで生まれたか", "平清盛はどこから生まれたのか", "平清盛の歴史的出身", "平清盛はどこから来たのか", "平清盛の出身地はどこですか", "平清盛はどこから生まれたか"]`

あー最大値の `10` にするとネタ切れになるようですね。

### 別のクエリ生成例
#### query_rewrites: `generative|count-5`
元クエリ:`源実朝 趣味`
生成クエリ:`["源実朝の趣味", "源実朝の興味", "源実朝の趣味について", "源実朝の日常と趣味", "源実朝の好きなこと"]`

#### query_rewrites: `generative|count-10`
`["源実朝の趣味", "源実朝の興味", "源実朝の趣味について", "源実朝の日常と趣味", "源実朝の好きなこと", "源実朝の嗜好", "源実朝のプライベートライフ", "源実朝のプライベートタイム", "日本の源実朝の趣味", "源実朝の個人的な趣味"]`


クエリの生成には。低レイテンシーに最適化されたファインチューニングされた SLM を搭載しています。クエリの生成方法をカスタマイズするためのプロンプトを指定することはできません。

# まとめ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/674344/618a45e3-a787-ddfa-ad2b-455441ceed8f.png)

Azure AI Foundry のデータの中心を担う Azure AI Search は Out-of-the-Box で簡単に RAG を構築でき、最先端機能と高い精度という観点でも良い選択肢になると言えます。

https://qiita.com/nohanaga/items/d3cc240758e41ee3ec3f