Azure AI Search (旧：Azure Cognitive Search) で使用できる検索方法をまとめた

# はじめに
本記事は、自分の勉強用に`Azure Cognitive Search`についての公式ドキュメントをまとめた記事になります。詳細を知りたい方は以下のドキュメントをご覧ください。また、一部`GPT-3.5`モデルの`ChatGPT`を使って調べさせてもらっています。

:::note warn
2023年11月の`Microsoft Ignite`での発表で、`Azure Cognitive Search`は`Azure AI Search`に名称が変わりました。
:::

https://learn.microsoft.com/ja-jp/azure/search/

# Azure Cognitive Searchとは
一言でまとめると、クラウド検索サービスのことです。テキスト検索、フィルタリング、ファセット付きの検索結果、スペル修正、類似検索、自然言語の理解、地理空間検索など、さまざまな検索シナリオをサポートしています。

- [フルテキスト検索](#フルテキスト検索)
- [セマンティック検索](#セマンティック検索)
- [ベクター検索](#ベクター検索)
- [地理空間検索](#地理空間検索)
- [高度な検索](#高度な検索)

https://learn.microsoft.com/ja-jp/azure/search/search-what-is-azure-search

# フルテキスト検索
[フルテキスト検索](https://learn.microsoft.com/ja-jp/azure/search/search-lucene-query-architecture)とは、テキストデータ内のキーワードやフレーズを検索するための技術です。フルテキスト検索では、単語の多義性や、スペルミス、言語や地域の違いによる、適切なコンテキストの把握が困難であるという課題があるため、次に紹介するセマンティック検索との組み合わせや、ベクター検索が使われ始めています。本記事のフルテキスト検索の説明では、次のような`Azure Cognitive Search`に対する`REST API`を使用した検索要求を例に扱います。

:::note info
検索要求とは、返すべき内容を詳細に規定した仕様のことです。
:::
```
POST /indexes/hotels/docs/search?api-version=2020-06-30
{
    "search": "Spacious, air-condition* +\"Ocean view\"",
    "searchFields": "description, title",
    "searchMode": "any",
    "filter": "price ge 60 and price lt 300",
    "orderby": "geo.distance(location, geography'POINT(-159.476235 22.227659)')", 
    "queryType": "full" 
}
```

## 検索の流れ
クエリの実行は次の4段階で行われます。
1. [クエリ解析](#1-クエリ解析)
1. [字句解析](#2-字句解析)
1. [文書検索](#3-文書検索)
1. [ポイントの計算](#4-ポイントの計算)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2452857/f27eea51-2167-3f7a-ea4e-b1e5ba928b76.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2452857/b6ed0d56-873b-139a-ef60-6c06284cd033.png)

### 1. クエリ解析
クエリ分析では、クエリパーサーが検索語から演算子を切り離し、検索クエリをサブクエリに分解します。次のようなクエリ文字列があるとします。
```
"search": "Spacious, air-condition* +\"Ocean view\"",
```
このクエリ文字列の場合、"Ocean View"が必須であり、"Spacious,"と"air-condition"が勧告になるので、クエリ構造は次の通りになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2452857/917df787-7e62-6c82-8ee9-f81b30968b91.png)

#### queryType
`queryType`は`simple`と`full`の2種類があります。`full`は`simple`を拡張したクエリ言語であり、より多くの演算子やクエリの種類がサポートされています。クエリの種類には、ワイルドカード、あいまい一致、正規表現などがあります。

#### searchMode
`searchMode`は`any`と`all`があります。`any`とした場合、区切り文字は`OR(||)`としての役割を果たすので、次のようなクエリテキストになります。
```
Spacious,||air-condition*+"Ocean view"
```
一方で`all`とした場合、区切り文字は`AND(+)`としての役割を果たし、次のようなクエリテキストとして解釈されます。
```
+Spacious,+air-condition*+"Ocean view"
```
つまり、次の図のように3つの全てのサブクエリが必須のものとして解釈されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2452857/5f3f87d7-fef5-a167-2a25-443272b74b54.png)

### 2. 字句解析
字句解析では、クエリがアナライザーによって加工されます。一般的な字句解析として、次のような言語分析があります。
- 検索語の単語を原形にする
- 重要性の低い単語を削除
- 複合語を分解
- 大文字を小文字に変換

アナライザーによって加工されたクエリ構造を次に示します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2452857/cc2a4f7a-67a9-875d-7fb0-213cf478d3e0.png)

:::note info
アナライザーの動作は[Analyze API](https://learn.microsoft.com/ja-jp/rest/api/searchservice/test-analyzer)を使ってテストをすることができます。"air-condition"は次のように加工されます。
```json:JSON
{
    "text": "air-condition",
    "analyzer": "standard"
}
```
```json:JSON
{
  "tokens": [
    {
      "token": "air",
      "startOffset": 0,
      "endOffset": 3,
      "position": 0
    },
    {
      "token": "condition",
      "startOffset": 4,
      "endOffset": 13,
      "position": 1
    }
  ]
}
```
:::

### 3. 文書検索
文書検索とは、一致する語句がインデックスに存在する文書を見つけることです。具体例で考えた方がわかりやすいため、ここでは"hotels"というインデックスを考えます。

:::note info
**インデックスとは**
データの高速で効率的な検索を実現するためのデータ構造です。検索対象となるデータをテキストや数値などの属性で分割・整理して、検索の高速化や結果の精度向上を目指します。
:::

```json:JSON
{
    "name": "hotels",
    "fields": [
        { "name": "id", "type": "Edm.String", "key": true, "searchable": false },
        { "name": "title", "type": "Edm.String", "searchable": true },
        { "name": "description", "type": "Edm.String", "searchable": true }
    ] 
}
```
このインデックスには、次の4つの文書が追加されているとします。
```json:JSON
{
    "value": [
        {
            "id": "1",
            "title": "Hotel Atman",
            "description": "Spacious rooms, ocean view, walking distance to the beach."
        },
        {
            "id": "2",
            "title": "Beach Resort",
            "description": "Located on the north shore of the island of Kauaʻi. Ocean view."
        },
        {
            "id": "3",
            "title": "Playa Hotel",
            "description": "Comfortable, air-conditioned rooms with ocean view."
        },
        {
            "id": "4",
            "title": "Ocean Retreat",
            "description": "Quiet and secluded"
        }
    ]
}
```
以上のインデックスから、転置インデックスを構築します。インデックスに含まれている全ての用語に対して、どの文章リストに含まれているかを調べますが、転置インデックス全体を示すと大きくなってしまうため、ここではその一部を表示します。

"title"フィールドの転置インデックスの一部は次になります。
|用語|文書リスト|
|---|---|
|air|-|
|conditioned|-|
|ocean|4|
|spacious|-|
|view|-|

"description"フィールドの転置インデックスの一部は次になります。
|用語|文書リスト|
|---|---|
|air|3|
|conditioned|3|
|ocean|1, 2, 3|
|spacious|1|
|view|1, 2, 3|

"ocean view"が必須フレーズであるため、1,2,3の文書が一致します。

:::note info
**転置インデックスとは**
全文検索を行う対象となる文書群から単語の位置情報を格納するための索引構造のことを言います。今回のケースでは、各用語に対する文書リストの番号を格納する対応表になります。
:::

### 4. ポイントの計算
検索結果セット内のすべての文書に、関連度スコアが割り当てられます。スコアは[TF/IDF(Term Frequency-Inverse Document Frequency)](https://en.wikipedia.org/wiki/Tf%E2%80%93idf)をメインで算出されます。

:::note info
**TF/IDFとは**
出現頻度の低い語句と高い語句を含んだ検索において、出現頻度の低い語句を含んだ結果により高いランクを与えます。

**TF(Term Frequency)とは**
特定の文書内で単語が出現する頻度を表します。単語が文書内でより頻繁に現れるほど、その単語のTFは高くなります。TFは、文書内の単語の重要性を示す指標として使用されます。

**IDF(Inverse Document Frequency)とは**
単語の普遍性を測定するための指標です。IDFは、ある単語がどれだけの文書で出現するかを逆数で表し、一般的な単語ほどIDFは低くなります。IDFは、コーパス全体での単語の希少性を示す指標として使用されます。

**TF/IDFのスコアリング例**
"the president"というクエリにおいては、"president"で一致した文書の方がより関連性が高いと見なされて高いスコアが与えられます。
:::

一致した1,2,3の文書に対して、スコアリングした結果が次の通りになりました。"spacious"を含む文書1のスコアが最も高く、"description"がより短い文書3が文書2よりもスコアが高い結果となりました。
```json:JSON
{
  "value": [
    {
      "@search.score": 0.25610128,
      "id": "1",
      "title": "Hotel Atman",
      "description": "Spacious rooms, ocean view, walking distance to the beach."
    },
    {
      "@search.score": 0.08951007,
      "id": "3",
      "title": "Playa Hotel",
      "description": "Comfortable, air-conditioned rooms with ocean view."
    },
    {
      "@search.score": 0.05967338,
      "id": "2",
      "title": "Ocean Resort",
      "description": "Located on a cliff on the north shore of the island of Kauai. Ocean view."
    }
  ]
}
```

# セマンティック検索
[セマンティック検索](https://learn.microsoft.com/ja-jp/azure/search/semantic-search-overview)とは、検索エンジンがユーザーの検索意図を正しく把握し、ユーザーが求める検索結果を表示する検索方法になります。セマンティック検索は次の2つの方法で拡張されます。セマンティック検索は、2023年7月現在プレビュー段階です。

:::note warn
2023年11月の`Microsoft Ignite`での発表で、セマンティック検索の一般提供が開始が発表されました。
https://techcommunity.microsoft.com/t5/ai-azure-ai-services-blog/announcing-general-availability-of-vector-search-and-semantic/ba-p/3978525
:::

1. セマンティックの再ランク付け
    - 最初の結果セットに対する2次ランク付けが追加
    - セマンティック的に最も関連性の高い結果が一覧の先頭にレベル上げ
1. セマンティックキャプションとハイライト
    - 検索ページにおいて、キャプションと回答が抽出されて返答
    - コンテンツを最もよく要約している文やフレーズをドキュメントから抽出して強調表示

:::note warn
セマンティック検索は、使用量によって課金されるプレミアム機能です。無料プランは、1か月あたり1000クエリに制限されています。
:::

## セマンティック検索によるランク付けの仕組み
`BM25Similarity`というアルゴリズムを使い、フルテキスト検索で見つかった一致ごとに計算された関連性スコアに対し、既定で上位50件の結果に対してのみ適応させることで再スコアリングを行います。

https://learn.microsoft.com/ja-jp/azure/search/index-similarity-and-scoring

### BM25Similarity
`BM25Similarity`も`TF/IDF`を使っています。`BM25Similarity`は、単語の出現頻度、逆文書頻度、クエリの長さなどを考慮して関連性スコアを計算します。また、特徴としては高度なカスタマイズオプションがあり、各単語の出現頻度やドキュメントの長さが関連性スコアに与える影響のパラメーターを調整することができます。

## 演算順序
セマンティック検索ではスペル修正を有効にすることができます。スペルチェックによって用語が検索エンジンに到達する前に、クエリ開始時の入力ミスを修正できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2452857/dd0b5104-1be2-50e1-3dd1-69f5158e904d.png)
以下の順序で演算が行われます。
1. クエリ実行&スペルチェック
    - スコアリングアルゴリズムによるスコア付け
    - クエリ言語とインデックス内の一致する用語との言語的類似性に基づいて計算
1. 転置インデックス
    - 用語の解析、分析、スキャンの実施
1. セマンティックモデル
    - 各ドキュメントを要約する節を検索
    - 機械読解による内容評価
    - 検索クエリによるキャプションと回答の返答
1. 再スコアリング
    - クエリ用語の概念的類似性

## セマンティック機能と制限
セマンティック検索は新しい技術であり、できることとできないことがあります。

できることは次の通りです。
- 元のクエリの意図に近い一致のレベル上げ
- 検索結果ページに表示できる文字列の検出

その一方で、できないことは次の通りです。
- コーパス全体に対するクエリの実行
- 新しい情報や文字列を作成すること

:::note info
**コーパスとは**
データベース化された大規模な言語資料のことです。
:::

# ベクター検索
[ベクター検索](https://learn.microsoft.com/ja-jp/azure/search/vector-search-overview)とは、従来のキーワード検索での制限を克服することを目的とした検索方法です。機械学習モデルを使用することで、クエリ用語との一致のみに依存するのではなく、コンテキスト内の単語とフレーズの意味捉えることができます。ベクトル空間モデルでは、データをベクトルで表現し、ベクトルの間の距離や類似度を計算することで、データの関連性を評価するため、高次元のデータを効率的に検索できることが特徴です。ベクター検索は、2023年7月現在プレビュー段階です。ベクター検索により、次のことができます。

:::note warn
2023年11月の`Microsoft Ignite`での発表で、ベクトル検索の一般提供が開始が発表されました。
https://techcommunity.microsoft.com/t5/ai-azure-ai-services-blog/announcing-general-availability-of-vector-search-and-semantic/ba-p/3978525
:::

- 類似性検索
    - 画像検索
        - 特徴ベクトルに変換して類似度を計算
    - 関連性の高い文書の検索
        - 単語や文書のベクトル化
- マルチモーダル検索
    - テキスト、画像、動画、音声、などの複数モーダル
    - 各モーダルの特徴ベクトルの統合
- RAGアーキテクチャ
    - `Retrieval-Augmented Generation`
    - 検索と生成を組み合わせによる、より高度な言語理解と文章生成
    - 大規模な事前学習済みの言語モデル(トランスフォーマー)
    - 情報検索モデル(フルテキスト検索エンジン)

:::note info
ベクター検索は、`Cognitive Search`の一部として追加料金なしで利用できます。
:::

## ベクター検索のシナリオ
- テキストのベクター検索
    - 埋め込みモデルによる、テキストのエンコード
- 異なるデータ型間でのベクター検索
    - テキスト、画像、動画、音声
- 多言語検索
    - 多言語埋め込みモデル
- ハイブリッド検索
    - テキストベース検索×類似度検索、など

:::note info
埋め込み(`Embedding`)とは、高次元のデータを低次元のベクトル空間に変換する手法です。テキストや画像の意味を捉える機械学習モデルによって変換されます。また、変換されてマッピングされたベクトル空間は、埋め込み空間と呼ばれます。
:::

## ベクター検索の関連サービス
- テキスト埋め込みモデル
    - [text-embedding-ada-002](https://learn.microsoft.com/ja-jp/azure/cognitive-services/openai/concepts/models#embeddings-models)
- 画像埋め込みモデル
    - [Vectorize Image API](https://learn.microsoft.com/ja-jp/azure/cognitive-services/computer-vision/how-to/image-retrieval#call-the-vectorize-image-api)
- 言語モデルを利用したアプリケーションを開発するためのフレームワーク
    - [LangChain](https://docs.langchain.com/docs/)
- AI LLMと従来のプログラミング言語の統合を可能にする軽量SDK
    - [セマンティックカーネル](https://github.com/microsoft/semantic-kernel/blob/main/README.md)

:::note info
セマンティックカーネルは大きなドキュメントをチャンクする場合に便利です。
:::

## ベクター類似度メトリック
- ユークリッド
    - 2つのベクトル差の長さ
- コサイン
    - 2つのベクトル間の角度
- 内積
    - 2つのベクトル差の長さと間の角度

## ベクトル空間での一致の検索アルゴリズム
ベクトル空間での一致を検索するアルゴリズムには、近似最近隣検索(ANN: `Approximate Nearest Neighbor Search`)が使用されます。近似最近隣検索は、高次元のデータセットからクエリと近似して類似したデータを効率的に見つける手法です。この手法は検索精度が犠牲になりますが、近似最近傍のスケーラブルかつ高速な取得ができるため、精度と効率のバランスを取るために最適な手法です。

:::note info
`Azure Cognitive Search`では、ANNアルゴリズムのHNSW(`Hierarchical Navigable Small World`)が使用されています。
:::

# 地理空間検索
[地理空間検索](https://learn.microsoft.com/ja-jp/azure/search/search-query-odata-geo-spatial-functions)は、位置情報や地理的な属性に基づいてデータを検索するための技術や手法です。地理空間検索は、地図や位置情報サービス(GPS)、地理情報システム(GIS)などで広く使用されています。

## 地理空間クエリ
`Azure Cognitive Search`では、以下の2つの地理空間クエリがサポートされています。
- `geo.distance`
    -  2点間の距離をキロメートル単位で返す
    -  `geography'POINT(<longitude> <latitude>)`
```
geo.distance(location, geography'POINT(-122.131577 47.678581)')
```
- `geo.intersects`
    - 指定された地点が指定した多角形の内側であればtrueを返す
    - 地点は時計回り
```
geo.intersects(location, geography'POLYGON((-122.031577 47.578581, -122.031577 47.678581, -122.131577 47.678581, -122.031577 47.578581))')
```

:::note info
`Azure Cognitive Search`で使われるOData(`Open Data Protocol`)地理空間関数では、`$filter`、`$orderby`、`$select`が使用できます。ODataはRESTfulなWebサービス間でデータを共有するためのプロトコルであり、Microsoftによって開発されています。
:::

# 高度な検索
`Azure Cognitive Search`は、他にも様々な検索方法があります。

## フィールド検索
[フィールド検索](https://learn.microsoft.com/ja-jp/azure/search/search-query-lucene-examples#example-1-fielded-search)は、データベースや情報管理システムなどで特定のフィールド(データの属性やカラム)を対象にしてデータを検索する操作です。フィールド検索は、データの検索やフィルタリング、集計などさまざまなデータ操作において重要な役割を果たします。以下は特定のカテゴリーで用語"hotel"を含むが、"motel"を含まないホテルを検索する例です。

```:HTTP
POST /indexes/hotel-samples-index/docs/search?api-version=2020-06-30
{
    "search": "HotelName:(hotel NOT motel) AND Category:'Resort and Spa'",
    "queryType": "full",
    "select": "HotelName, Category",
    "count": true
}
```

## あいまい検索
[あいまい検索](https://learn.microsoft.com/ja-jp/azure/search/search-query-lucene-examples#example-2-fuzzy-search)は、指定された検索キーワードに対して厳密な一致ではなく、類似性や近似性を考慮してデータを検索する手法です。あいまい検索は、ユーザーが正確なキーワードを入力できない場合や、タイプミスや誤りがある場合に特に有用です。あいまい検索を実行するには、1つの言葉の終わりにチルダ記号`~`を付けます。

```:HTTP
POST /indexes/hotel-samples-index/docs/search?api-version=2020-06-30
{
    "search": "Tags:conserge~",
    "queryType": "full",
    "select": "HotelName, Category, Tags",
    "searchFields": "HotelName, Category, Tags",
    "count": true
}
```

## 近接検索
[近接検索](https://learn.microsoft.com/ja-jp/azure/search/search-query-lucene-examples#example-3-proximity-search)は、一定の距離や順序で近接して存在するデータを検索する手法です。近接検索を行うには、言葉の終わりにチルダ記号`~`を挿入し、近接境界となる語数を続けます。
```:HTTP
POST /indexes/hotel-samples-index/docs/search?api-version=2020-06-30
{
    "search": "Description: \"hotel airport\"~5",
    "queryType": "full",
    "select": "HotelName, Description",
    "searchFields": "HotelName, Description",
    "count": true
}
```

## 用語ブースト検索
[ブースト検索](https://learn.microsoft.com/ja-jp/azure/search/search-query-lucene-examples#example-4-term-boosting)は、特定の検索結果の重要度や優先度を上げるための手法です。用語をブーストするには、検索する用語の末尾に正の数値であるブースト係数を持つキャレット記号`^`を使用します。
```:HTTP
POST /indexes/hotel-samples-index/docs/search?api-version=2020-06-30
{
    "search": "beach^2 access",
    "queryType": "full",
    "select": "HotelName, Description, Tags",
    "searchFields": "HotelName, Description, Tags",
    "count": true
}
```

## 正規表現検索
[正規表現検索](https://learn.microsoft.com/ja-jp/azure/search/search-query-lucene-examples#example-5-regex)は、文字列のパターンを表現するために使用される検索手法です。正規表現検索では、スラッシュ`/`の間のコンテンツに基づいて一致が検索されます。
```:HTTP
POST /indexes/hotel-samples-index/docs/search?api-version=2020-06-30
{
    "search": "HotelName:/(Mo|Ho)tel/",
    "queryType": "full",
    "select": "HotelName",
    "count": true
}
```

## ワイルドカード検索
[ワイルドカード検索](https://learn.microsoft.com/ja-jp/azure/search/search-query-lucene-examples#example-6-wildcard-search)は、部分的な一致を含む文字列を検索する手法です。複数`*`または単数`?`の文字をワイルドカード検索で使用できます。
```:HTTP
POST /indexes/hotel-samples-index/docs/search?api-version=2020-06-30
{
    "search": "HotelName:sc*",
    "queryType": "full",
    "select": "HotelName",
    "count": true
}
```

# おわりに
`Azure Cognitive Search`で使用できる検索方法を、プレビュー段階の検索方法を含めてまとめました。普段何気なく使用していても、実は様々な種類の検索方法があったり、その裏には複雑なアルゴリズムでスコアが算出されていることを知り、とても興味深く思いました。