Azure Cognitive Searchの新機能、Semantic Searchを試してみた

Posted by johtani on Thursday, July 1, 2021

目次

Azure Cognitive Searchで新機能「Semantic Search(セマンティック検索)」という機能の日本語対応が発表されました。 実際にはPublic Previewという状態ではありますが、どんな機能か気になったので調べてみました。 なお、本ブログは2021年6月時点での内容となります。

公式ドキュメント

公式ドキュメントでいろいろと説明されています。まずは公式ドキュメントを見ていただくのが良いかなと。最初のドキュメントには10分ちょっとの紹介ビデオもあります。ピックアップしたのは概要、クエリ、レスポンスの構造、紹介ブログになります。ブログ以外は日本語(たぶん機械翻訳?)になっています(新機能の紹介の日本語ページにはまだ記載がありません)。

簡単にですがデータを入れて試してみたので、どんな仕組みなのかどういう挙動なのかを紹介します。

Semantic Searchとは?

言語理解、セマンティック関連性などを提供する機能です。BingとMSリサーチにより開発されて、Azure Cognitive Searchに導入されました。 クエリの書き方を少し変更する必要がありますが、再インデックスや設定変更などは必要ないという仕組みになっています。

  • 検索クエリからコンテキストを読み取って、検索し、リランクするアルゴリズム(Semantic Ranker)
  • 関連する一節を強調表示(Semantic Caption)
  • クエリに対するセマンティック回答(Semantic Answer)
  • スペルチェック機能

言語によって利用できる機能が異なります

Semantic Searchの利点は先ほどの紹介にも書きましたが、インデキシング時に特殊処理をする必要がないことかなと。 すでに登録してあるデータ(制限がありますが)でもすぐに試すことができます。

仕組み

https://docs.microsoft.com/ja-jp/azure/search/media/semantic-search-overview/semantic-query-architecture.png

  1. スペル修正機能
  2. クエリパース
  3. スコアリング
  4. 上位50件に対してSemantic Rankerにより再評価(検索ヒットが50件以上の場合でも上位50件のみがリランクの対象)
    • 言語表現モデルを利用
    • 結果の要約として最適な部分をキャプションとして抽出。
    • クエリが質問形式の場合は一番よさそうな場所を回答として選択

1および4番目の処理が今回新しく使えるSemantic Searchの機能になります。スペルチェックは日本語がまだ対象外なので、4番目の機能について紹介します。

利用できる環境と制限

冒頭でも書きましたが、Public Previewの段階です。実際に利用するにはリクエストページから申し込みが必要になっています。

利用リクエスト時に利用するAzure Cognitive Searchのサービス名が必要になるので、あらかじめ起動しておかなければいけません。 また、利用できるリージョンが限定されているのでサービス起動時のリージョンには気を付けてください。

  • 利用できるリージョン
    • 米国中北部、米国西部、米国西部 2、米国東部 2、北ヨーロッパ、西ヨーロッパ
  • 利用できるTier
    • セマンティック検索はStandardレベル
    • スペルチェックはレベル制限なし
  • 価格(セマンティック検索のみ)
    • 7月初旬までは25万クエリで500ドル
  • Analyzerの制限
    • Azure Cognitive Searchが提供する言語アナライザーが対象(Custom Analyzerでは利用不可)

サービス起動後にプレビュープログラムを申請すると、利用できるようになるとメールが届きます。そこから利用ができるようになります。

今回は日本語のWikipediaのデータをいれてどんな機能なのかを確認してみました。

Wikipediaのデータを入れて試してみた

日本語のWikipedia(あとで英語も入れました)を一部(10万件程度)登録してSemantic Search(主にRanker)の動きを確認してみました。

データの登録には自作ツールを利用。

  • JSONデータ生成ツール:https://github.com/johtani/wiki-extractor-rs
  • JSONデータ登録ツール:https://github.com/johtani/wiki-json-loader

日本語Wikipediaの2020年6月のデータ、英語Wikipediaの2021年6月のデータを利用。上記ツールで生成したもののうち約10万件をAzure Cognitive Searchに登録して動作確認をしました(英語のデータでJSONデータ生成ツールでバグがあるのですが修正していません。。。)。 また、クエリの実行にはVisual Studio CodeのREST Client拡張(どんなものかは以前のブログをご覧ください)を使いました。簡単にREST APIを呼び出せるのはすばらしい。

日本語のWikipedia

日本語のWikipediaのデータを「96,344件」登録し実験しました。 スキーマは次のような形です(説明に必要なものだけ抽出してあります)。自然分が入っているtitlecontentsheadingsの3つのフィールドを今回はSemantic Searchの対象にしてみるので、AnalyzerをLuceneベースの日本語Analyzerであるja.luceneを設定しました。

  • フィールド名 : タイプ / Analyzer
  • id : Edm.String / keyword
  • categories : Collection(Edm.String) / keyword
  • title : Edm.String / ja.lucene
  • contents : Edm.String / ja.lucene
  • headings : Edm.String / ja.lucene

Analyzerに言語Analyzerを設定するくらいで、他の設定は特に必要ありません。

クエリとデータの確認

デモの動画や説明のサンプルを見たところ英語で「Where was Alan Turing born?」や「What’s the capital of France?」のような自然文のクエリが出てきたので、次のようなクエリで試してみました。

  • フランスの首都はどこですか?

実際にパリのページがインデックスに存在していることは以下のクエリで確認しました。

  • パリ:3047件
POST https://{{host}}/indexes/{{index-name}}/docs/search?api-version=2020-06-30-Preview
Content-Type: application/json
api-key: {{api-key}}

{
    "count": true,
    "search": "パリ",
    "top": 10,
    "searchFields": "title, categories, contents",
    "select": "title, id, contents"
}

※ここで{{host}}などはREST Client拡張で変数として定義したものです。

  • host : Azure Cognitive Searchのサービス名
  • index-name : インデックス名
  • api-key : 接続のためのAPIキー

URLパラメータのapi-version=2020-06-30-PreviewがPreview機能を呼び出すための指定になっています。

3047件の1件目が「パリ」のページで以下のようなWikipediaの文章をセクションごとに文字列としてページ全体を配列に入れてcontentsに登録してあります。

"パリ(、巴里)は、フランス北部、イル=ド=フランス地域圏にある都市。フランスの首都であり、イル=ド=フランス地域圏の首府である。\nフランス最大の都市であり、同国の政治、経済、文化などの中心である。ロンドンと共に欧州を代表する世界都市。行政上では、1コミューン単独で県を構成する特別市であり、ルーヴル美術館を含む1区を中心に、時計回りに20の行政区が並ぶ(エスカルゴと形容される)。",

このインデックスに対して「フランスの首都はどこですか?」というクエリを、セマンティック検索のクエリと通常のクエリの2パターンで検索をしてみました。

これまでのクエリ(セマンティックではない検索)

まずは通常のクエリです。 searchがクエリ文字列、searchFieldsが検索対象フィールドになります。

POST https://{{host}}/indexes/{{index-name}}/docs/search?api-version=2020-06-30-Preview
Content-Type: application/json
api-key: {{api-key}}

{
    "top": 100,
    "count": true,
    "search": "フランスの首都はどこですか?",
    "searchFields": "title, contents",
    "select": "title,  id"

}

クエリを実行するとヒット件数が'13996’件で、トップ5件のデータは次のようになりました。

  "@odata.count": 13996,
  "value": [
    {
      "@search.score": 17.059355,
      "id": "462",
      "title": "首都"
    },
    {
      "@search.score": 16.086372,
      "id": "21053",
      "title": "首都圏"
    },
    {
      "@search.score": 15.059893,
      "id": "71414",
      "title": "どこでもドア"
    },
    {
      "@search.score": 14.086605,
      "id": "51972",
      "title": "首都機能移転"
    },
    {
      "@search.score": 12.996841,
      "id": "3877",
      "title": "首都の一覧"
    },

見るとわかりますが、「首都」「どこ」といった単語を含むデータが上位に入っています。「フランス」を含むデータは8件目と10件目に「フランス植民地帝国」、「ラ・フランス」といったデータが出てきます。

    {
      "@search.score": 12.705561,
      "id": "129279",
      "title": "フランス植民地帝国"
    },
    ...
    {
      "@search.score": 12.036648,
      "id": "3173",
      "title": "ラ・フランス"
    },

残念ながら望んでいる結果にはほど遠いかと。

どんな検索になっているのでしょうか?まずは、クエリの解釈から。 デフォルトで、Azure Cognitive Searchはデフォルトでは、入力された文字列をtype=simplemode=anyで検索します(クエリパラメータ参照)。 対象となるフィールドはsearchFieldsで今回は指定してあるので、これらのフィールドに対して、入力された文字列を渡してAnalyzerで単語分割し(今回はsimpleクエリ用の特殊文字が入っていないため文字列として扱われる)、 出てきた単語で(mode=anyだから)OR検索します。 ja.luceneのAnalyzerが出力する単語列がどんなものかは以下のリクエストを実行するとわかります。

POST https://{{host}}/indexes/{{index-name}}/analyze?api-version=2020-06-30-Preview
Content-Type: application/json
api-key: {{api-key}}

{
   "analyzer":"ja.lucene",
   "text": "フランスの首都はどこですか?"

}

結果は以下の通りです。

  "tokens": [
    {
      "token": "フランス",
      "startOffset": 0,
      "endOffset": 4,
      "position": 0
    },
    {
      "token": "首都",
      "startOffset": 5,
      "endOffset": 7,
      "position": 2
    },
    {
      "token": "どこ",
      "startOffset": 8,
      "endOffset": 10,
      "position": 4
    }
  ]

これで、どのような単語が出てきているかがわかりました。 それぞれのフィールドでどのようなスコアになっているかまではわからないですが、検索結果には上記3つの単語のいずれかがsearchFieldsにヒットすれば出てきます。おそらく、ヒットした単語数が多い、短い文字列のフィールドがより高いスコアになるというような動きになっていると思われます(BM25になってるはず)。

セマンティック検索

では、セマンティック検索のクエリに変更してみましょう。

POST https://{{host}}/indexes/{{index-name}}/docs/search?api-version=2020-06-30-Preview
Content-Type: application/json
api-key: {{api-key}}

{
    "top": 100,
    "count": true,
    "search": "フランスの首都はどこですか?",
    "searchFields": "title, contents",
    "select": "title,  id",
    "queryType": "semantic",
    "queryLanguage": "ja-jp"
}

queryTypequeryLanguageがセマンティック検索のクエリを実行するためのパラメータになります(公式ガイド参照)。 今回は日本語のデータであるため、日本語のモデルを利用するようにqueryLanguageを指定します(許可されていない文字列を渡すとエラーになる)。 この2つを追加するだけです。簡単ですね。

結果は次のようになります(上位5件抜粋)。

  "@odata.count": 13996,
  "value": [
    {
      "@search.score": 12.036648,
      "@search.rerankerScore": 2.3456064984202385,
      "@search.captions": [
        {
          "text": "ラ・フランス",
          "highlights": "<em>ラ・フランス</em>"
        }
      ],
      "id": "3173",
      "title": "ラ・フランス"
    },
    {
      "@search.score": 11.144733,
      "@search.rerankerScore": 2.3173404783010483,
      "@search.captions": [
        {
          "text": "ラン (フランス)",
          "highlights": "<em>ラン</em> (<em>フランス)</em>"
        }
      ],
      "id": "108094",
      "title": "ラン (フランス)"
    },
    {
      "@search.score": 11.144793,
      "@search.rerankerScore": 2.1937477812170982,
      "@search.captions": [
        {
          "text": "ブレスト (フランス)",
          "highlights": "<em>ブレスト</em> (<em>フランス)</em>"
        }
      ],
      "id": "116154",
      "title": "ブレスト (フランス)"
    },
    {
      "@search.score": 10.199907,
      "@search.rerankerScore": 2.1590753793716431,
      "@search.captions": [
        {
          "text": "フランス・ハルス",
          "highlights": "<em>フランス・ハルス</em>"
        }
      ],
      "id": "81566",
      "title": "フランス・ハルス"
    },
    {
      "@search.score": 11.273148,
      "@search.rerankerScore": 2.1434053033590317,
      "@search.captions": [
        {
          "text": "バカロレア (フランス)",
          "highlights": "<em>バカロレア</em> (<em>フランス</em>)"
        }
      ],
      "id": "54514",
      "title": "バカロレア (フランス)"
    },

検索結果数を見ると、通常のクエリと同じ値です。 これは、公式ドキュメントのセマンティック検索に説明があるように、 セマンティック検索が通常のクエリで実行した後の検索結果に対して、上位50件のデータだけに対してsemantic rankerによってスコアを再計算するという仕様のためです(ドキュメントからの推測)。

実際に検索結果の50件目、51件目は次のようになっていました。50件目までのデータには@search.rerankerScoreというスコア(と@search.captions)が追加されています。 51件目のデータについては、前の通常のクエリの51件目のデータと同じデータでした。

    {
      "@search.score": 10.909212,
      "@search.rerankerScore": 0.07321979058906436,
      "@search.captions": [
        {
          "text": "首都高速埼玉大宮線",
          "highlights": null
        }
      ],
      "id": "67667",
      "title": "首都高速埼玉大宮線"
    },
    {
      "@search.score": 10.049902,
      "id": "68569",
      "title": "スタッド・ド・フランス"
    },

上位5件のデータに戻ります。 これらは、通常のクエリとは異なる結果となりました。 見ると@search.captionsというところにハイライトされた項目が表示されています。 検索クエリをもとにそれっぽい部分がキャプションという形で返ってくる仕組みです。

結果を見るとわかりますが、残念ながら「パリ」のページは返ってきませんでした。 もともと通常のクエリを実行したときの「326」番目の結果として返ってきているため、セマンティック検索の対象とはならなかったためです。 ただ、「首都」「どこ」といった単語よりも「フランス」に関するドキュメントが通常の検索よりも上位に来ています。 クエリに存在する単語で「フランス」がより重要であると判断されているようです。

Semantic Rankerに処理させてみる

ちなみに、今回登録したデータにはWikipediaのカテゴリも登録してあります。 「ヨーロッパの首都」というカテゴリで絞り込んでから検索してみるとどうなるかもやってみました。 filterのパラメータが絞り込み条件です。

POST https://{{host}}/indexes/{{index-name}}/docs/search?api-version=2020-06-30-Preview
Content-Type: application/json
api-key: {{api-key}}

{
    "top": 100,
    "count": true,
    "search": "フランスの首都はどこですか?",
    "searchFields": "title, contents",
    "select": "title,  id",
    "queryType": "semantic",
    "queryLanguage": "ja-jp",
    "filter": "search.ismatch('ヨーロッパの首都', 'categories')"
}

結果は次の通りです。カテゴリで絞り込みをしているので検索結果数は39件となります。このため、すべてのデータがSemantic Rankerの対象です。 ですが、「パリ」のページは11件目に出てきました。

  "@odata.count": 39,
  "value": [
    {
      "@search.score": 3.8405614,
      "@search.rerankerScore": 2.0472740978002548,
      "@search.captions": [
        {
          "text": "ウィーン",
          "highlights": null
        }
      ],
      "id": "9465",
      "title": "ウィーン"
    },
...
    {
      "@search.score": 4.8915977,
      "@search.rerankerScore": 1.8480307757854462,
      "@search.captions": [
        {
          "text": "レイキャヴィーク",
          "highlights": "<em>レイキャヴィーク</em>"
        }
      ],
      "id": "112253",
      "title": "レイキャヴィーク"
    },
...
    {
      "@search.score": 7.277628,
      "@search.rerankerScore": 1.7982495501637459,
      "@search.captions": [
        {
          "text": "パリ",
          "highlights": "<em>パリ</em>"
        }
      ],
      "id": "31",
      "title": "パリ"
    },

キャプションは入力された単語だけではなく、クエリが持っている単語をもとにした何かしらの基準(おそらく言語モデルで近い単語?)で選択されていると思われます。首都というカテゴリーで絞り込んでしまったので、ヒットした文章が似たようなものが多くなったせいか、残念ながらそこまでSemantic Rankerが意図を汲み取るところまでは行きませんでした。

フランスの歴史

視点を変えてみましょう。質問そのものの答えではなく、クエリの意図に近いものが出てくるかを見てみましょう。 ここまでのクエリでフランスに関するクエリを投げていました。今回のインデックスに「フランスの歴史」で検索すると「30820」件のドキュメントがヒットし、上位にはフランスには関係ない「歴史」のページや「フランス」の文学、都市など「フランスの歴史」とは少し離れた意味のものが上位に来ています。

  "@odata.count": 30820,
  "value": [
    {
      "@search.score": 12.888079,
      "id": "17875",
      "title": "スコットランドの歴史"
    },
    {
      "@search.score": 12.718105,
      "id": "10665",
      "title": "世界の歴史"
    },
    {
      "@search.score": 12.701981,
      "id": "31397",
      "title": "ベトナムの歴史"
    },
    {
      "@search.score": 12.579847,
      "id": "22433",
      "title": "フランス文学"
    },
    {
      "@search.score": 12.524254,
      "id": "34667",
      "title": "フランスの国旗"
    },
    {
      "@search.score": 12.52136,
      "id": "108094",
      "title": "ラン (フランス)"
    },
    {
      "@search.score": 12.41088,
      "id": "20717",
      "title": "歴史小説"
    },
    {
      "@search.score": 12.388911,
      "id": "56229",
      "title": "演劇の歴史"
    },
    {
      "@search.score": 12.343941,
      "id": "39669",
      "title": "イギリスの歴史"
    },

このクエリをセマンティッククエリで検索すると次のようになりました。

  "@odata.count": 30820,
  "value": [
    {
      "@search.score": 12.274498,
      "@search.rerankerScore": 1.482184374704957,
      "@search.captions": [
        {
          "text": "自由フランス",
          "highlights": "<em>自由フランス</em>"
        }
      ],
      "id": "96475",
      "title": "自由フランス"
    },
    {
      "@search.score": 11.446766,
      "@search.rerankerScore": 1.3535655084997416,
      "@search.captions": [
        {
          "text": "フランスの地域圏",
          "highlights": "<em>フランスの地域圏</em>"
        }
      ],
      "id": "51845",
      "title": "フランスの地域圏"
    },
    {
      "@search.score": 10.926437,
      "@search.rerankerScore": 1.3261859538033605,
      "@search.captions": [
        {
          "text": "フランス第一帝政",
          "highlights": "<em>フランス第一帝政</em>"
        }
      ],
      "id": "57573",
      "title": "フランス第一帝政"
    },

上位には「フランスの歴史」に関連しそうなタイトルが上がってきています。 また、先ほど「歴史」という単語でヒットして上位に来ていた「スコットランドの歴史」は@search.rerankerScoreの値が低くなっていました(31件目に出現)。


    {
      "@search.score": 12.888079,
      "@search.rerankerScore": 0.42768346797674894,
      "@search.captions": [
        {
          "text": "スコットランドの歴史",
          "highlights": null
        }
      ],
      "id": "17875",
      "title": "スコットランドの歴史"
    },

考察

日本語のWikipediaを約10万件入れたインデックスでセマンティック検索を試してみました。 デモや説明などでクエリが自然文の質問だったこともあり、質問の回答になりそうなページが上に来るかと期待しましたが、期待しすぎました。

これは、Azure Cognitive Searchのセマンティック検索の仕組みを考えると納得のいくものです。 説明を読んだ時にも感じたのですが、入力された文字列でまず検索をし、上位50件をセマンティック検索の対象としています。

ですので、そもそもクエリの文章に出てくる単語が含まれたデータだけが対象となります。 このため、クエリに出てこない単語のみで構成されているものは残念ながら対象とできません。 また、上位50件だけがSemantic Rankerの対象となっているため、検索にヒットしたとしても上位に食い込まなければ言語モデルによるランキングの対象にもなりません。 セマンティック検索といっても、残念ながらドキュメントに出てこない単語(似た単語、質問に対するそのものの回答)まで読み取る機能はありません。

ただ、「フランスの歴史」の検索で見たようにそれぞれの単語だけで検索をしていた場合とは異なり、 クエリの意味に沿ったスコアづけにより、通常の検索よりも意図したものに近いデータが出ていることが確認できました。 複数の単語で構成されるクエリの場合に、単語だけでスコアを計算した場合よりもよさそうな結果が上位に出てくることがわかりました。

ただし、重要なのは「上位50件」までしかSemantic Rankerの対象にはならない点です。

英語のWikipedia

セマンティックは英語対応が一番最初にリリースされているので、英語のWikipediaについてもデータを登録してみました。

英語のWikipediaのデータを「111,649件」登録しました。 スキーマは以下の通り(今回の調査に利用していないものは省略)です。日本語と異なる点はAnalyzerがen.luceneになっていることぐらいです。

  • フィールド名 : タイプ / Analyzer
  • id : Edm.String / keyword
  • categories : Collection(Edm.String) / keyword
  • title : Edm.String / en.lucene
  • contents : Edm.String / en.lucene
  • headings : Edm.String / en.lucene

クエリとデータの確認

日本語同様に首都に関する質問形式にしてみました。

  • What’s the capital of Italy?

実際にRomeのページがインデックスに存在していることは以下のクエリで確認済みです。

Rome:2302件

POST https://{{host}}/indexes/{{index-name}}/docs/search?api-version=2020-06-30-Preview
Content-Type: application/json
api-key: {{api-key}}

{
    "count": true,
    "search": "Rome",
    "top": 10,
    "searchFields": "title, contents",
    "select": "title, id, contents"
}

以下のようなWikipediaの文章をセクションごとに文字列としてページ全体を配列に入れてcontentsに登録してあります(これも日本語と同じです)。

""\n\nRome (Italian and Latin: Roma ) is the capital city and a special comune of Italy (named Comune di Roma Capitale), as well as the capital of the Lazio region. The city has been a major human settlement for almost three millennia. With 2,860,009 residents in , it is also the country's most populated comune...",

このデータ群に対して「What’s the capital of Italy?」というクエリを、Semantic Searchのクエリと通常のクエリの2パターンで検索をしてみる。

これまでのクエリ(セマンティックではない検索)

まずは、Semantic Searchではないクエリです。

POST https://{{host}}/indexes/{{index-name}}/docs/search?api-version=2020-06-30-Preview
Content-Type: application/json
api-key: {{api-key}}

{
    "top": 100,
    "count": true,
    "search": "What's the capital of Italy?",
    "searchFields": "title, contents",
    "select": "title,  id"

}

ヒット件数は18049件で、トップ5件は次のような形です。

  "@odata.count": 18049,
  "value": [
    {
      "@search.score": 17.870815,
      "id": "14532",
      "title": "Italy"
    },
    {
      "@search.score": 16.596167,
      "id": "14702",
      "title": "Economy of Italy"
    },
    {
      "@search.score": 16.180624,
      "id": "183878",
      "title": "Louis II of Italy"
    },
    {
      "@search.score": 15.917025,
      "id": "29665",
      "title": "State capitalism"
    },
    {
      "@search.score": 15.88901,
      "id": "215741",
      "title": "Capitalization"
    },

日本語の場合よりもItalyに関連するものが上位に多く含まれます。 ただ、Rome自体のページは上位50件には出てきていませんでした。 また、capitalとは意味の異なるcapitalizationやcapitalismが上位に来ています。 日本語の場合と同様にクエリがどのように解釈されているのか?も見てみます。

POST https://{{host}}/indexes/{{index-name}}/analyze?api-version=2020-06-30-Preview
Content-Type: application/json
api-key: {{api-key}}

{
   "analyzer":"en.lucene",
   "text": "What's the capital of Italy?"

}

結果は以下の通りです。

  "tokens": [
    {
      "token": "what",
      "startOffset": 0,
      "endOffset": 6,
      "position": 0
    },
    {
      "token": "capit",
      "startOffset": 11,
      "endOffset": 18,
      "position": 2
    },
    {
      "token": "itali",
      "startOffset": 22,
      "endOffset": 27,
      "position": 4
    }
  ]

日本語とあまり変わってはいません。ただ、単語が入力されたものとは少し変わっています。 これは、en.luceneの処理で、stemmingが行われているためだと思われます。たとえば、「capital」の場合、Stemmingにより「capital」「capitalize」などの単語が元になりますが、動詞、名詞などの違いなく検索できるようにするためにこのようなStemmingの処理が行われます。 ということで、これらの単語を含むドキュメントがヒットしています。Italyが一番上に来ているのはItalyという単語が多く含まれるからのようです。

Semantic Searchクエリ

では、Semantic Searchのクエリに変更してみましょう。

POST https://{{host}}/indexes/{{index-name}}/docs/search?api-version=2020-06-30-Preview
Content-Type: application/json
api-key: {{api-key}}

{
    "top": 100,
    "count": true,
    "search": "What's the capital of Italy?",
    "searchFields": "title, contents",
    "select": "title,  id",
    "queryType": "semantic",
    "queryLanguage": "en_us"
}

queryTypequeryLanguageがsemantic searchのクエリを実行するためのパラメータになります(公式ガイド参照)。 今回は英語のデータであるため、英語のモデルを利用するようにqueryLanguageを指定します(許可されていない文字列を渡すとエラーになる)。

結果は次のようになります(上位5件抜粋)。

  "@odata.count": 18049,
  "value": [
    {
      "@search.score": 17.870815,
      "@search.rerankerScore": 1.9688458815217018,
      "@search.captions": [
        {
          "text": "Italy",
          "highlights": null
        }
      ],
      "id": "14532",
      "title": "Italy"
    },
    {
      "@search.score": 14.239678,
      "@search.rerankerScore": 1.9419755563139915,
      "@search.captions": [
        {
          "text": "Capital city",
          "highlights": null
        }
      ],
      "id": "181337",
      "title": "Capital city"
    },
    {
      "@search.score": 10.181764,
      "@search.rerankerScore": 1.8902588561177254,
      "@search.captions": [
        {
          "text": "Lepontii",
          "highlights": null
        }
      ],
      "id": "325133",
      "title": "Lepontii"
    },
    {
      "@search.score": 10.865718,
      "@search.rerankerScore": 1.875102698802948,
      "@search.captions": [
        {
          "text": "ItalY",
          "highlights": null
        }
      ],
      "id": "14482",
      "title": "ItalY"
    },
    {
      "@search.score": 11.689348,
      "@search.rerankerScore": 1.8201303631067276,
      "@search.captions": [
        {
          "text": "Savoy",
          "highlights": null
        }
      ],
      "id": "27885",
      "title": "Savoy"
    },

日本語で見てきたように、検索結果数を見ると、通常のクエリと同じ値で、残念ながら50件までにRomeは入っていないため、Semantic Rankerによるブーストもかかりませんでした。 ただし、「Capitalization」や「State capitalism」といった別の意味(地理に関するものではない単語)のものが上位に出てこなくなっています。 これは、capitalという単語が地理に関する意味を持っていると判断された結果のように見えます。 Capitalizationの@search.rerankerScoreは上位のスコアに比べて小さいものとなっています。

    {
      "@search.score": 15.88901,
      "@search.rerankerScore": 0.93921028636395931,
      "@search.captions": [
        {
          "text": "Capitalization",
          "highlights": null
        }
      ],
      "id": "215741",
      "title": "Capitalization"
    },
What’s the capital of Finland?

上位50件以内(20件目)に「Helsinki」のページが出てくるクエリでセマンティック検索をしてみました。


POST https://{{host}}/indexes/{{index-name}}/docs/search?api-version=2020-06-30-Preview
Content-Type: application/json
api-key: {{api-key}}

{
    "top": 100,
    "count": true,
    "search": "What's the captial of Finland?",
    "searchFields": "title, contents",
    "select": "title,  id",
    "queryType": "semantic",
    "queryLanguage": "en-us"
}

結果としては、2件目にHelsinkiが返ってきていたので、Semantic Rankerがによる並び替えがうまくいきました。 (下記の結果は上位3件を抜粋したものです)。

  "@odata.count": 16673,
  "value": [
    {
      "@search.score": 14.026371,
      "@search.rerankerScore": 2.42378556355834,
      "@search.captions": [
        {
          "text": "Vanaja (Finland)",
          "highlights": null
        }
      ],
      "id": "7132102",
      "title": "Vanaja (Finland)"
    },
    {
      "@search.score": 12.434971,
      "@search.rerankerScore": 2.383675143122673,
      "@search.captions": [
        {
          "text": "Helsinki",
          "highlights": null
        }
      ],
      "id": "13696",
      "title": "Helsinki"
    },
    {
      "@search.score": 13.380153,
      "@search.rerankerScore": 2.2026549801230431,
      "@search.captions": [
        {
          "text": "Rauma, Finland",
          "highlights": null
        }
      ],
      "id": "178635",
      "title": "Rauma, Finland"
    },

考察

今回の英語のクエリではSemantic Rankerがある程度の役割をはたしていそうだということはわかりました。 Capitalがいくつもの意味を持っていることもあり、文章をもとにより意味の近いドキュメントが上位に来たということです。 ただ、英語の場合も基本的には上位50件以内のデータに対して処理が行われるので上位50件以内にデータが入らなければうまくいかないようです。

まとめ

Azure Cognitive Searchの新機能であるセマンティック検索について調べてみました。 まだPublic Previewですが、簡単に使えるような仕組み(データの再登録なし、クエリでパラメータを追加するだけ)が用意されているのは便利でした。 ただ、最初に想像していたもの(私の早とちりもありますが)ほどのセマンティック検索とは行きませんでした。 似たような言葉の意味を持ったものを探してくれたり、答えそのもののページのスコアがすごく高くなるといったものではありませんでした。

仕組み上、クエリに含まれる単語で検索し、上位50件以内のデータだけが対象になる(残念ながら現時点ではこの件数の変更はできません。)ため、これまでの検索とは劇的に変化するものではないことはわかりました(銀の弾丸はないんですけどね)。

ある程度の挙動をわかっていれば、Wikipediaのようなデータでもそれなりの結果は取得できそうだということはわかりました。

「言語モデル」でクエリとヒットしたデータをもとにスコア計算をし直しているようですが、この部分がどのようなデータ・クエリだと有効活用できるのか?というのをもう少しいろいろな角度で試してみる必要がある気がします(適材適所が何なのか?)。 あとは、上位50件までが対象なので、それ以上の件数のデータの並びに関してはこれまでと同様の結果となる点は注意しないといけません。 また、今回試していないほかの機能(スペルチェック、Semantic Answerなど)にメリットがあるのかもしれません。

ざっくりですがセマンティック検索について調べてみました。 こんなデータだとどうなの?ここの考え方が間違ってるのでは?などの指摘がありましたら、コメント欄またはTwitter(@johtani)でコメントを頂けたらと。


comments powered by Disqus

See Also by Hugo


Related by prelims-cli