<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
  <channel>
    <title><![CDATA[Vector Database - Elasticsearch Labs]]></title>
    <description><![CDATA[Articles and tutorials from the Search team at Elastic]]></description>
    <copyright><![CDATA[© 2026. Elasticsearch B.V. All Rights Reserved]]></copyright>
    <image>
      <title><![CDATA[Vector Database - Elasticsearch Labs]]></title>
      <url>https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt1121c0bf0e8a6e65/6a88da6340a1841030ef456f/search-labs-thumbnail.png</url>
      <link>https://www.elastic.co/jp/search-labs/blog/category/vector-database</link>
    </image>
    <link>https://www.elastic.co/jp/search-labs/blog/category/vector-database</link>
    <atom:link href="https://www.elastic.co/jp/search-labs/rss/category/vector-database.xml" rel="self" type="application/rss+xml"/>
    <language><![CDATA[jp]]></language>
    <lastBuildDate>Wed, 23 Sep 2026 05:48:09 GMT</lastBuildDate>
  <item>
    <title><![CDATA[Elasticsearch Vector Database：数分でシッピング、数百億規模へ手頃なコストでスケール]]></title>
    <description><![CDATA[ハイブリッド検索の難しい部分はすでに完了しています。最適化されたデフォルト設定、サードパーティおよびネイティブのJina AIモデル、マネージドGPU推論をすべてすぐに利用できます。インフラの構築ではなく、高速でスケーラブルなAIアプリの構築に専念しましょう。]]></description>
    <content:encoded><![CDATA[<p>Elasticsearchは、ベクトルワークロード向けに世界で最も広くデプロイされているプラットフォームの1つであり、GitHub、Docusign、Seismicなど多くの企業のセマンティック検索、Retrieval-Augmented Generation（RAG）、レコメンデーションを支えています。本日、ベクトルベースのアプリケーション向けに最適化された新しいサーバーレス製品、Elasticsearch Vector Databaseを発表します。ドキュメントとクエリをご用意いただければ、埋め込み、インデックスチューニング、インフラは当社が対応します。さらに、低価格で拡張性にも優れています。</p><p>新規ユーザーにとって、これは高品質なベクトル検索を最も迅速に開始する方法です。すでにElasticsearchをご利用の場合は、新しいシステムを導入する必要はなく、データが既に存在するプラットフォーム上でベクトル検索を利用できるようになります。Elasticsearch Vector Databaseは、大規模言語モデル（LLM）のグラウンディングから、AIエージェントへの検索機能とメモリの提供、数千億ものベクトルの提供まで、幅広いシナリオをサポートします。わずか数分で<a href="https://cloud.elastic.co/registration?onboarding_token=vector">新しいプロジェクトを立ち上げ</a>、開始できます。</p><h2>1つのエンジン、あらゆるベクトルユースケース</h2><p>Elasticsearch Vector Databaseは、ベクトルを使用してアプリケーションを構築するすべての人向けに設計されています。</p><ul><li><p><strong>RAG：</strong>高密度ベクトル検索と低密度ベクトル検索でLLMに最適なコンテキストを取得するか、ベクトル検索と語彙検索の両方を組み合わせたハイブリッド検索を利用できます。生成結果の質は、検索結果の質に比例して向上します。</p></li><li><p><strong>AIエージェント：</strong>マルチステップのエージェントループで求められる低レイテンシで、ドキュメントや会話記憶に対する高速でフィルタリングされた検索をエージェントに提供します。</p></li><li><p><strong>セマンティック検索：</strong>1つのフィールドタイプとパイプラインコード不要で、キーワードではなく意味に基づいて一致させます。</p></li><li><p><strong>レコメンデーションと類似性：</strong>製品、画像、その他あらゆるコンテンツにわたり、大規模なスケールで最近傍を検索できます。</p></li></ul><h2>ベクトルワークロードに必要なすべてを、すぐに使えるよう最適化</h2><p>ベクトルベースのアプリケーションを構築するには、いくつかの個別の要素を連携させる必要があります。具体的には、埋め込みモデルの設定とホスティング、それらを用いたドキュメントのインデックス作成、ベクトルの効率的な保存、各クエリへの埋め込みモデルの適用、ベクトルストアとの照合、そして最後に、一致したドキュメントの取得です。Elasticsearch Vector Databaseは、追加の設定やセットアップなしで、これらすべてを自動的に処理します。</p><h3>vectordb_documentインデックスモードを使用したベクトルのインデキシング</h3><p>ベクトル優先ワークロード専用に設計された新しいインデックス構成である<a href="https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/dense-vector#dense-vector-vectordb-document-mode">vectordb_document</a>インデックスモードはデフォルトで有効になっており、エキスパートが選択するような設定が適用されます。以下が有効化されます。</p><ul><li><p><strong>デフォルトでbfloat16：</strong>ベクトルはfloat32の半分のサイズで格納され、再現率への影響はごくわずかで、量子化を適用する前の段階でもディスク使用量をほぼ半分に削減します。</p></li><li><p><strong>ソースベクトルの除外：</strong>Elasticsearchでは、埋め込みはすでに検索に使用されるインデックス構造内に存在します。_sourceに未加工のコピーを重複して保持すると、ストレージを消費し、結果の取得が遅くなるだけです。重複を除外することで、レスポンスが高速化され、格納するデータ量を削減できます。</p></li><li><p><strong>適切なファイルをキャッシュに事前読み込み：</strong>ベクトルクエリが最初にアクセスするデータ構造があらかじめメモリに読み込まれるされるため、1番目の（そして1,000番目の）クエリも超高速で実行されます。</p></li><li><p><strong>並列マージ：</strong>マージによってセグメントがより整理されたベクトル構造に統合され、再現率とレイテンシーの両方が向上します。さらに、これらのマージをマルチスレッドで実行することで、より高速に完了できます。</p></li></ul><h3>ベクトルストレージ、圧縮、自動チューニング</h3><ul><li><p>ベクトルは自動的に圧縮されます。<a href="https://www.elastic.co/jp/search-labs/blog/better-binary-quantization-lucene-elasticsearch">Better Binary Quantization（BBQ）</a>により、再現率を維持しながらベクトルのメモリ・フットプリントを最大32倍削減できます。さらに、DiskBBQは大規模なワークロードのメモリ要件をさらに削減します。<a href="https://www.elastic.co/jp/search-labs/blog/vector-quantization-auto-calibration-diskbbq"> </a></p></li><li><p><a href="https://www.elastic.co/jp/search-labs/blog/vector-quantization-auto-calibration-diskbbq">自動キャリブレーション</a>を有効にすると、各セグメントの量子化がデータに合わせて調整され、データのずれに応じてマージのたびに再調整されます。18種類のデータセットでテストを行った結果、1秒あたりのクエリ数（QPS）は平均16.7%向上し、そのほとんどで再現率も向上しました。</p></li></ul><h3>マネージドGPU推論での埋め込み</h3><ul><li><p>ネイティブの<a href="https://www.elastic.co/jp/jina-search-models">Jina AIの埋め込みモデルとリランカーモデル</a>で埋め込みを生成することも、サードパーティ製モデルを導入することもできます。いずれも<a href="https://www.elastic.co/docs/explore-analyze/elastic-inference/eis">Elastic Inference Service（EIS）</a>を介したマネージドGPUで実行でき、モデルサーバーを運用する必要はありません。または、ご自身でホストすることも可能です。</p></li><li><p><a href="https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/semantic-text"><strong>semantic_text</strong></a>フィールドタイプは、チャンキングと埋め込み、そしてクエリを自動的に処理し、市場で最もシンプルなセマンティック検索への道を提供します。</p></li></ul><h3>ハイブリッド検索とフィルタリングされたベクトル検索</h3><ul><li><p><a href="https://www.elastic.co/jp/elasticsearch/hybrid-search">ハイブリッド検索</a>が組み込まれており、単一のクエリで全文検索とベクトル検索を組み合わせることができます。逆順位融合（RRF）やその他の任意のブレンドメカニズムを使用して結果を統合できます。通常、ベクトル検索はハイブリッド検索の中で適切に設定するのが最も難しい部分です。Elasticsearch Vector Databaseを使用すれば、その問題は解決し、ハイブリッドスタック全体の性能が向上します。</p></li><li><p><a href="https://www.elastic.co/jp/search-labs/blog/filtered-hnsw-knn-search">フィルタリングされたベクトル検索</a>では、再現率を損なう後処理としてではなく、ベクトル検索自体の一部としてメタデータフィルタを適用できます。</p></li></ul><h3>初日からエンタープライズ対応</h3><p>また、純粋なベクトルデータベースには一般的に備わっていない、ロールベースのアクセス制御（RBAC）、監査ログ、コンプライアンス認定も利用できます。</p><h2>スケールしても手頃で予測可能</h2><p>Elasticsearch Vector Databaseは、成長に合わせてコスト効率を維持できるように設計されています。ストレージを線形に保ち、メモリ使用量を低く抑えるBBQおよびDiskBBQ圧縮により、数千億のベクトルに拡張してもコストが急増することはありません。支払う料金は、格納するデータ量、作成するインデックス量、必要な検索容量など、すでに把握している数値に基づいて算出されます。ドキュメント数、ベクトルの次元数、クエリ負荷を見積もれば、プロジェクトを作成する前に料金を算出できます。月末には、請求内容を項目ごとに確認することもできます。不透明な計算ユニットはなく、バックグラウンド処理に対する予期せぬ料金も発生しません。</p><h2>Elasticsearch Vector Databaseの利用開始方法</h2><h3>サーバーレスベクトルデータベースプロジェクトを作成する</h3><p><a href="https://cloud.elastic.co/registration?onboarding_token=vector">Elastic Cloudで新しいサーバーレスVector Databaseプロジェクトを作成します</a>。データをエンドポイントに向ければ、インデックスの準備は完了です。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt556cbdfba551f248/6aa10b4332b53038406d321a/image1.png" alt="Elastic Cloud Serverless project types: Elasticsearch, Vector Database, Observability and Security" /><h3>semantic_textを使用したインデックスの作成</h3><p>ベクトルインデックスモードは、ベクトルの構成を処理します。semantic_textを使用すると、マネージドGPU推論上でインデックス設定と同様に埋め込みやチャンキングの設定も自動的に管理されるため、埋め込みパイプラインを構築する必要はありません。</p>PUT my-vectors
{
"mappings": {
"properties": {
"description": { "type": "semantic_text" }
    }
  }
}<h3>ドキュメントを投入する</h3><p>テキストをインデックスすると、埋め込みが自動的に生成されます。</p>POST /my-vectors/_doc
{
  "id": "park_rocky-mountain",
  "title": "Rocky Mountain",
  "description": "Bisected north to south by the Continental Divide, this portion of the Rockies has ecosystems varying from over 150 riparian lakes to montane and subalpine forests to treeless alpine tundra."
}<h3>セマンティック検索クエリを実行する</h3><p>先ほど作成したセマンティックフィールドをクエリします。</p>GET /my-vectors/_search
{
  "query": {
    "semantic": {
      "field": "description",
      "query": "a mountain range in the middle of north america"
    }
  }
}<p>そして、結果が返されます。</p>{
  "took": 80,
  "hits": {
    "max_score": 0.7792325,
    "hits": [
      {
        "_index": "my-vectors",
        "_score": 0.7792325,
        "_source": {
          "id": "park_rocky-mountain",
          "title": "Rocky Mountain",
          "description": "Bisected north to south by the Continental Divide, ..."
        }
      }
    ]
  }
}<p>セマンティック検索は始まりにすぎません。完全なテキストクエリを実行することも、両方を組み合わせてハイブリッドクエリにすることもできます。独自のベクトルクエリを作成して、完全に制御することもできます。詳しい手順については、ドキュメントの<a href="https://www.elastic.co/docs/solutions/vector-database/vector-full-text-search">セマンティック検索クイックスタート</a>をご覧ください。</p><h2>Elasticsearchにおけるベクトル検索の今後</h2><p>私たちは既に次の改善に取り組んでいます。</p><ul><li><p><strong>より優れたマルチテナント処理：</strong>テナントごとにデータを分離する必要がある場合、より少ないコードでより迅速に実現する方法を提供します。</p></li><li><p><strong>インデックスの自動最適化：</strong>できるだけ手をかけずに、「新規作成直後のインデックス」から「完全に最適化済み」へ。</p></li><li><p><strong>継続的なインフラストラクチャーの改善：</strong>Vector Databaseの設定とインフラストラクチャーを継続的に調整することで、常に最高のスループットと最速の対応を得られるようにします。</p></li></ul><h2>Elastic Cloud ServerlessでElasticsearchベクトルデータベースをお試しください</h2><p>本番環境レベルのデフォルト設定がチューニングを行うため、空のプロジェクトから数分でフィルター適用済みのハイブリッドベクトルクエリを作成できます。インフラの構築ではなく、高速でスケーラブルなAIアプリの構築に専念しましょう。</p><p><a href="https://cloud.elastic.co/registration?onboarding_token=vector">Elastic Cloud Serverless</a>を開始するか、<a href="https://www.elastic.co/docs/solutions/vector-database">詳細ドキュメント</a>と<a href="https://www.elastic.co/docs/api/doc/elastic-cloud-serverless/group/endpoint-vectordb-projects">APIリファレンス</a>をご覧ください。</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/vector-database-rag-serverless</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/vector-database-rag-serverless</guid>
    <category><![CDATA[Vector Database]]></category>
    <category><![CDATA[Elastic Cloud Serverless]]></category>
    <category><![CDATA[ハイブリッド検索]]></category>
    <dc:creator><![CDATA[Dustin Coates]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt4def84aae6aff861/6aa10ab1ee57e53d9b05253c/cover.png" length="0" type="image/png"/>
    <pubDate>Wed, 09 Sep 2026 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Elasticsearchの検索再現率を測定・改善する方法：ハイブリッド検索で0.43から0.75へ]]></title>
    <description><![CDATA[Elasticsearchにおける検索再現率を測定および改善する方法を学びましょう。BM25の語彙検索とJina AIのベクトル埋め込みを組み合わせ、rank_eval APIを使用して実際の数値で改善効果を検証します。]]></description>
    <content:encoded><![CDATA[<p><a href="https://www.elastic.co/blog/practical-bm25-part-1-how-shards-affect-relevance-scoring-in-elasticsearch">BM25ランキングアルゴリズム</a>を用いた<a href="https://www.elastic.co/docs/solutions/search/full-text">語彙検索</a>は、低コストで高速であり、幅広いクエリに対して非常に効果的です。しかし盲点があります。それは、ドキュメントとトークンを共有しないクエリです。この記事では、BM25が足りないところを正確に測定します。Elasticsearchの<a href="https://www.elastic.co/docs/reference/elasticsearch/rest-apis/search-rank-eval"> ランキング評価API</a>（<code>rank_eval</code>）を使用し、<a href="https://www.elastic.co/docs/explore-analyze/elastic-inference/eis">Elastic Inference Service</a>（EIS）を介して<a href="https://www.elastic.co/search-labs/es/blog/jina-embeddings-v3-elastic-inference-service">Jina AIの埋め込みを追加することでそのギャップを埋めます。</a>再現率スコアが <code>0.43</code> から <code>0.75</code> に上がるのを見て、その理由がわかるでしょう。</p><h2>リコールとは何ですか？</h2><p><a href="https://www.elastic.co/docs/reference/elasticsearch/rest-apis/search-rank-eval#k-recall">再現率</a>は、<code>0</code>から<code>1</code>のスケールで、ユーザーが実際に欲しいドキュメントが検索結果にどれだけ含まれているかを測定します。クエリで3点の製品が表示されるはずなのに、検索結果の上位10位に2つしか表示されない場合は、そのクエリの再現率は<code>recall@10 = 0.67</code>となります。これは集合ベースの指標であり、その<em>k</em>件の結果内の関連ドキュメントの位置は考慮されません。10番目の位置にある関連ドキュメントは、1番目の位置にある関連ドキュメントと同じものとして扱われます。再現率が高いということは、関連性の高い検索結果を見逃さないということです。</p><p>
</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5ffd147b13705680/6a170a6fe8fbce11a539fc22/b13af2a5d0ca055535d8bfe3dfe4b3d1093ee6da-1457x796.png" alt="Recall@10の計算方法を示すベン図です。すべての関連文書とBM25によって検索された上位10件の結果の重複を示し、Recall@10のスコアは0.40となります。" /><p>この図は2つの集合を示しています。1つは関連するすべてのドキュメント（左側）、もう1つはBM25が実際に取得したドキュメント（上位10件、右側）です。再現率に影響する交差部分、<code>prod_1</code>と<code>prod_2</code>が見つかり、<code>prod_3</code>、<code>prod_4</code>、<code>prod_6</code>は完全に見落とされました。結果：<code>Recall@10 = 2/5 = </code><strong><code>0.40</code></strong>。</p><h2>要件</h2><p>再現率がどのように機能するのかをよりよく理解するために、本題に入りましょう。このデモンストレーションではPythonを使用します。付属のノートブック（<a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/relevance-tuning-improving-recall-adding-vectors/notebook.ipynb">notebook.ipynb</a>）で一緒に進めることができます。すべてのコードブロックは、実行準備が整ったセルです。</p><p>提供されたコードでは、以下を使用しています。</p><ul><li><p>Elasticsearch 9.3以降</p></li><li><p>Python 3.10+</p></li></ul>pip install elasticsearch pandas plotly python-dotenv<ul><li><p>Elasticsearchの認証情報が含まれる<code>.env</code>ファイル</p></li></ul>ELASTICSEARCH_URL=https://your-cluster-url
ELASTICSEARCH_API_KEY=your-api-key<h2>データセット</h2><p>靴、電子機器、工具など、さまざまなカテゴリーにわたる1,000点の製品を掲載した製品カタログを使用します。</p><p>各ドキュメントには4つのフィールドがあります。</p><p>フィールド</p><p>タイプ</p><p>`タイトル`</p><p>テキスト</p><p>`description`</p><p>テキスト</p><p>`ブランド`</p><p>キーワード</p><p>`category`</p><p>キーワード</p><p>データセットは <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/relevance-tuning-improving-recall-adding-vectors/dataset.csv"><code>dataset.csv</code></a>から読み込まれます。</p><h2>語彙検索の力と限界</h2><p>BM25は、Elasticsearchおよびほとんどの検索エンジンのデフォルトのランキングアルゴリズムです。ドキュメントは、クエリ用語がどれだけ頻繁に出現するかによってスコアリングされます。これは、ドキュメントの長さと、インデックス全体でのそれらの用語の頻度に合わせて調整されます。さらに、小文字への正規化、語幹抽出、ストップワード除去といった<a href="https://www.elastic.co/docs/reference/text-analysis/analyzer-reference">アナライザー</a>も搭載されています。「running shoes」というクエリは、「Running Shoes」と一致し、おそらく「run」とも一致します。</p><p>これは、多くの種類のクエリに対して有効です。</p><ul><li><p>「running shoes」はタイトルにこれらのトークンを含む商品を即座にマッチングします。</p></li><li><p>「Bluetoothスピーカー」は、トークンが逐語的に表示されるため、携帯オーディオ製品として表示されます。</p></li></ul><p>結果は決定論的で説明可能です。つまり、検索クエリに含まれる用語がドキュメント中に含まれているからこそ、そのドキュメントは上位にランク付けされるのです。関連性のデバッグは単純明快です。</p><h3>問題が発生する箇所</h3><p>では、同じカタログに対してこれらのクエリを試してみましょう。</p><ul><li><p><strong>「スキンケアルーティン」：</strong>どの製品名にも「ルーティン」という単語は含まれません。BM25は、「スキンケア」で部分一致することができますが、フェイス美容液、ボディオイル、モイスチャライザーは、「ビタミンC」、「レチノール」、または「ブライトニング」のような用語を使用して説明されており、いずれもクエリと重複しません。完全なスキンケアルーティンを形成する製品は、共通のトークンを持たずにインデックス全体に散らばっています。</p></li></ul>ID: B06XX6DS3P, Score: 9.0552, Title: Replenix Retinol Smooth + Tighten Body Lotion - Collagen-Boosting, Regenerating Anti-Aging Body Cream, Reduces Appearance of Stretch Marks, 6.7 oz.

  ID: B08XMPKJ1L, Score: 5.2699, Title: Bio-Oil Skincare Body Oil (Natural) Serum for Scars and Stretchmarks, Face and Body Moisturizer Hydrates Skin, with Organic Jojoba Oil and Vitamin E, For All Skin Types, 6.7 oz

  ID: B01CY764KQ, Score: 5.0057, Title: Nike Up Or Down Men Deodorant - Pack of 2 | Long-Lasting Fragrance, Body Spray Combo for Men | Deodorant for Active Living | Nike Men's Deo Set | Ultimate Odor Protection | Grooming Essentials | Signature Nike Scent | High-Performance Men's Deodorant<ul><li><p><strong>「ペット用旅行用品」：</strong>これはユースケースのグループ分けであり、製品カテゴリではありません。犬用スリングキャリア、ペット用カーシート、旅行用クレートはどれも関連性のあるアイテムですが、それらの説明文は「旅行用品」というよりは、携帯性、安全性、快適性について述べています。BM25は「ペット」と大まかに一致しますが、旅行固有の商品を他のペットカタログと区別するシグナルはありません。</p></li></ul>ID: B0BVV7BKTW, Score: 7.4371, Title: Large Foldable Travel Duffel Bag with Shoes Compartment

ID: B07TNPHYNV, Score: 6.6455, Title: 40 Pieces Christmas Bronze Jingle Bells Craft Small Bells

ID: B08R8FRW53, Score: 6.6335, Title: CUBY Dog and Cat Sling Carrier
ID: B08QMCQYGM, Score: 6.5259, Title: YTFGGY Whiteboard Pinstripe Tape 6 Rolls 1/8"
ID: B0CP3LQSWM, Score: 6.2994, Title: Portable Dog Water Bottle 32 Oz<p>これは <strong>再現率の問題</strong>です。関連するドキュメントはインデックスに存在しますが、ユーザーの言葉と文書の単語が十分に一致しないため、BM25はそれらを見つけることができません。</p><p>同義語を追加することは、既知の事例に役立ちますが、ユーザーが意図を表現するすべての方法を列挙することはできません。そこでベクトルが役立ちます。</p><h2>リコールを測定すべき理由</h2><p>問題を解決する前に、まずそれを定量化する必要があります。</p><p><a href="https://www.elastic.co/docs/reference/elasticsearch/rest-apis/search-rank-eval#k-recall"><strong>Recall@k</strong></a>は、ユーザーが実際に求めているドキュメントのうち、検索結果に表示されるドキュメントの数を測定します。正式には：</p>Recall@k = (relevant documents found in top k) / (total relevant documents)<p><a href="https://www.elastic.co/docs/reference/elasticsearch/rest-apis/search-rank-eval#k-precision"><strong>Precision@k</strong></a>は、上位k件の結果のうち、実際にどれだけ関連性があるかを測定します。</p>Precision@k = (relevant documents in top k) / k<p>高精度とは、得られる結果が良好であることを意味します。電子商取引においては、関連商品を見落とすこと（再現率の低さ）は、多少不完全な結果を表示すること（精度の低さ）よりも深刻です。なぜなら、商品が表示されないということは、販売機会の損失を意味するからです。</p><p>Elasticsearchの<code>rank_eval</code> APIは、両方を体系的に測定できます。それぞれに評価されたドキュメントを含むクエリのリストを提供すると、Elasticsearchがすべてのクエリのメトリックを計算します。</p><h2>評価の設定</h2><p><code>rank_eval</code> APIには<strong>評価データセット</strong>が必要です。これは、クエリとそれに関連するドキュメントのマッピング、および関連性グレード（0 = 関連性なし、1 = 関連性あり、2 = 非常に関連性あり）で構成されます。</p><p>ノートブックでは、これは<a href="https://www.elastic.co/docs/solutions/search/ranking/learning-to-rank-ltr#learning-to-rank-judgement-list">判断リスト</a>です。</p>judgments = [
    # Query 1: "running shoes" BM25 handles well (tokens appear in product titles) 
    {"query_id": "q1", "doc_id": "B09NQJFRW6", "grade": 2, "query": "running shoes"},
    {"query_id": "q1", "doc_id": "B08JMD4LMM", "grade": 2, "query": "running shoes"},
    {"query_id": "q1", "doc_id": "B08VRJ6F2Q", "grade": 2, "query": "running shoes"},
    {"query_id": "q1", "doc_id": "B07S8NRRWR", "grade": 2, "query": "running shoes"},
    {"query_id": "q1", "doc_id": "B01HD620I8", "grade": 2, "query": "running shoes"},
    {"query_id": "q1", "doc_id": "B07DX86321", "grade": 2, "query": "running shoes"},
    {"query_id": "q1", "doc_id": "B0968YVLQ8", "grade": 1, "query": "running shoes"},
    {"query_id": "q1", "doc_id": "B093QJ39ZS", "grade": 1, "query": "running shoes"},
    {"query_id": "q1", "doc_id": "B096FGSC39", "grade": 1, "query": "running shoes"},
    {"query_id": "q1", "doc_id": "B01GVQWVV2", "grade": 1, "query": "running shoes"},

    # Query 2: "skincare routine" intent-based, "routine" never appears in product titles
    {"query_id": "q2", "doc_id": "B08XMPKJ1L", "grade": 2, "query": "skincare routine"},
    {"query_id": "q2", "doc_id": "B0BN3WQB92", "grade": 2, "query": "skincare routine"},
    {"query_id": "q2", "doc_id": "B0BT7B7P5T", "grade": 2, "query": "skincare routine"},
    {"query_id": "q2", "doc_id": "B00NPA2WEY", "grade": 2, "query": "skincare routine"},
    {"query_id": "q2", "doc_id": "B06XX6DS3P", "grade": 1, "query": "skincare routine"},
    {"query_id": "q2", "doc_id": "B07PDRD1KT", "grade": 1, "query": "skincare routine"},
    {"query_id": "q2", "doc_id": "B074J7869B", "grade": 1, "query": "skincare routine"},
    {"query_id": "q2", "doc_id": "B08JV31QW4", "grade": 1, "query": "skincare routine"},
    {"query_id": "q2", "doc_id": "B00K3TVJMQ", "grade": 1, "query": "skincare routine"},

    # Query 3: "study desk setup" intent-based, products are desks/stands/organizers
    {"query_id": "q3", "doc_id": "B08CS35J2T", "grade": 2, "query": "study desk setup"},
    {"query_id": "q3", "doc_id": "B09B3LFDXJ", "grade": 2, "query": "study desk setup"},
    {"query_id": "q3", "doc_id": "B07W58LMND", "grade": 1, "query": "study desk setup"},
    {"query_id": "q3", "doc_id": "B0CHYDX91L", "grade": 1, "query": "study desk setup"},

    # Query 4: "pet travel accessories" use-case grouping, products are carriers/crates/seats
    {"query_id": "q4", "doc_id": "B08R8FRW53", "grade": 2, "query": "pet travel accessories"},
    {"query_id": "q4", "doc_id": "B01MYUYX33", "grade": 2, "query": "pet travel accessories"},
    {"query_id": "q4", "doc_id": "B003C5RKE4", "grade": 2, "query": "pet travel accessories"},
    {"query_id": "q4", "doc_id": "B09GF8GBF6", "grade": 1, "query": "pet travel accessories"},
    {"query_id": "q4", "doc_id": "B0CP3LQSWM", "grade": 1, "query": "pet travel accessories"},
]<p>この組み合わせは意図的なものです。 <code>q1</code>はBM25が適切に処理するクエリ（製品タイトル内の正確なトークン）であり、 <code>q2</code> 、 <code>q3</code> 、 <code>q4</code>はユーザーの意図が特定の製品キーワードではなく概念として表現されるインテントベースのクエリです。</p><h2>BM25ベースライン再現率の測定</h2><p>まず、Elasticsearchクライアントを設定し、生のテキストデータをインデックス化します。</p>import os
import json
import pandas as pd
import plotly.graph_objects as go
from elasticsearch import Elasticsearch, helpers
from dotenv import load_dotenv

load_dotenv()

es = Elasticsearch(
    os.getenv("ELASTICSEARCH_URL"),
    api_key=os.getenv("ELASTICSEARCH_API_KEY")
)

INDEX_NAME = "ecommerce-products"<p>次にBM25の<code>rank_eval</code>リクエストを作成しましょう。リスト内の各リクエストは、クエリとその評価を組み合わせたものです。</p>judgments_df = pd.DataFrame(judgments)

bm25_requests = []
for query_id, query_text in (
    judgments_df[["query_id", "query"]].drop_duplicates().values
):
    relevant_docs = judgments_df[judgments_df["query_id"] == query_id]
    ratings = [
        {"_index": INDEX_NAME, "_id": row["doc_id"], "rating": row["grade"]}
        for _, row in relevant_docs.iterrows()
    ]

    bm25_requests.append({
        "id": query_id,
        "request": {
            "query": {
                "multi_match": {
                    "query": query_text,
                    "fields": ["title", "description"]
                }
            }
        },
        "ratings": ratings,
    })

bm25_eval = {
    "requests": bm25_requests,
    "metric": {"recall": {"k": 10, "relevant_rating_threshold": 1}},
}

bm25_result = es.rank_eval(index=INDEX_NAME, body=bm25_eval)
print("BM25 Recall@10:", bm25_result.body["metric_score"])<p>次の結果が得られます。</p>BM25 Recall@10: 0.43<p><code>0.43</code> は、4つのクエリすべてで、BM25が見つけるべきドキュメントの43%しか見つけられないということです。不足している点は、意図に基づくクエリに集中している。「スキンケアルーティン」という検索では、「ルーティン」が製品タイトルに含まれていないため、フェイス美容液やボディオイルが見つかりません。また、「ペット用旅行用品」という検索では、旅行用品ではなく携帯性や安全性の観点から説明されているキャリーケースやクレートが見つからない一方で、関連性のないペット用品が検索結果に表示されます。</p><p>これがベースラインです。これで、破るべき数値ができました。</p><h2>Jina埋め込みを用いたベクトル検索機能の追加</h2><p><a href="https://www.elastic.co/docs/solutions/search/vector"><code>Vector search</code></a> はドキュメントとクエリを高次元ベクトルとしてエンコードします。高次元ベクトルとは、数百または数千の数値から構成されるベクトルの一種で、各数値はそれが表すデータの特定の特徴をエンコードします。意味が似ているドキュメントは、たとえ共通の単語が一つもなくても、ベクトル空間上では互いに近い位置に配置されます。「ジム器具」と「ダンベルセット」は、概念が関連しているため、近くに配置されるでしょう。私がベクトルデータベースとしてElasticsearchを選んだ理由は、ハイブリッド検索をサポートしており、セマンティックな理解とキーワードの精度をすぐに実現できるからです。</p><p><a href="https://www.elastic.co/docs/explore-analyze/elastic-inference/eis">EIS</a>には、<a href="https://www.elastic.co/docs/api/doc/elasticsearch/group/endpoint-inference">推論API</a>を通じてモデル埋め込みを標準でサポートする機能が含まれています。</p><h3>ステップ1：Jina埋め込みv5を推論エンドポイントとして使用する</h3>INFERENCE_ENDPOINT_ID = ".jina-embeddings-v5-text-small"<p>クラスターにGPUリソースがある場合（Elastic CloudおよびElasticsearch 9.3以降で利用可能）、埋め込みはGPU上で生成されます。これはCPU推論よりも大幅に高速であり、従来、大規模な環境でベクトル処理を高価にしていたパフォーマンスのトレードオフを解消します。</p><p>なぜJina埋め込みが特に重要なのでしょうか。<a href="https://www.elastic.co/search-labs/blog/jina-embeddings-v5-text">JINA-embeddings-v5-text</a>は、32,000トークンのコンテキストウィンドウを持ち、タスク固有の <a href="https://arxiv.org/abs/2106.09685">Low-Rank Adaptation (LoRA) アダプター</a>をサポートする多言語モデル（119言語以上に対応）です。短い商品説明であれば、そのままでも十分に機能します。<code>jina-embeddings-v5-text</code>モデルの詳細については<a href="https://huggingface.co/jinaai/jina-embeddings-v5-text-small">こちらを</a>ご覧ください。</p><h3>ステップ2：セマンティックフィールドを持つインデックスを作成</h3>index_mappings = {
    "mappings": {
        "properties": {
            "title": {"type": "text", "copy_to": "semantic_field"},
            "description": {"type": "text", "copy_to": "semantic_field"},
            "brand": {"type": "keyword"},
            "category": {"type": "keyword"},
            "semantic_field": {
                "type": "semantic_text",
                "inference_id": INFERENCE_ENDPOINT_ID,
            },
        }
    }
}

if not es.indices.exists(index=INDEX_NAME):
    es.indices.create(index=INDEX_NAME, body=index_mappings)
    print(f"Created index: {INDEX_NAME}")<p>ここで重要なのは <a href="https://www.elastic.co/docs/solutions/search/semantic-search/semantic-search-semantic-text"><code>semantic_text</code></a> フィールドタイプです。これは<a href="https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/dense-vector"><code>dense_vector</code></a>よりも高レベルの抽象化です。推論エンドポイントを指定すると、Elasticsearchが埋め込みの生成を自動的に行います。</p><p><a href="https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/copy-to"><code>copy_to</code></a>プロパティが<code>title</code>と<code>description</code>にある場合、両方のフィールドのコンテンツが<a href="https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/semantic-text"><code>semantic_field</code></a>に流れ込んで埋め込まれるため、単一のベクトルで製品の完全な表現を捉えることができます。</p><h3>ステップ3：製品をインデックス化する</h3>def bulk_index(products, index_name):
    actions = []
    for product in products:
        doc_id = product.get("_id")
        source = {k: v for k, v in product.items() if k != "_id"}
        action = {"_index": index_name, "_source": source}
        if doc_id:
            action["_id"] = doc_id
        actions.append(action)

    success, failed = helpers.bulk(es, actions, raise_on_error=False)
    if failed:
        for error in failed:
            print(f"Error: {error}")
    else:
        print(f"Successfully indexed {success} documents")

bulk_index(products, INDEX_NAME)<p>インデックス時には、Elasticsearchは各文書の推論エンドポイントを呼び出し、その結果得られた埋め込みを<code>semantic_field</code>に保存します。余計なコードは必要ありません。</p><h2>ハイブリッド検索：BM25とベクトルおよびRRFの組み合わせ</h2><p>ベクトルを追加すると再現率は向上しますが、ベクトルのみを使用すると、完全一致クエリの精度が低下するリスクがあります。「ランニングシューズ」は、依然として逐語的に一致するものを最優先にランク付けするべきです。ハイブリッド検索は、その精度を維持するために、語彙要素を意図的に保持します。</p><p>ハイブリッド<a href="https://www.elastic.co/docs/reference/elasticsearch/rest-apis/reciprocal-rank-fusion">逆順位融合</a>（RRF）検索は、両方の長所を保持します。</p><ul><li><p>BM25は正確およびほぼ正確なクエリを高精度で処理します。</p></li><li><p>セマンティック検索は、意図に基づく多言語クエリを高い再現率で処理します。</p></li><li><p>RRFは2つのランキングリストを1つのランキングに統合します。</p></li></ul><p>RRF式は、各結果リストのランクに基づいて各文書にスコアを割り当てます。</p>score = sum(1 / (rank_constant + rank))<p>両方のリストで上位に表示されるドキュメントほど、統合スコアは高くなります。<code>rank_constant</code>は、より低いランクの文書に与えられる重みを制御します。</p>hybrid_requests = []

for query_id, query_text in (
    judgments_df[["query_id", "query"]].drop_duplicates().values
):
    relevant_docs = judgments_df[judgments_df["query_id"] == query_id]
    ratings = [
        {"_index": INDEX_NAME, "_id": row["doc_id"], "rating": row["grade"]}
        for _, row in relevant_docs.iterrows()
    ]

    hybrid_requests.append({
        "id": query_id,
        "request": {
            "retriever": {
                "rrf": {
                    "retrievers": [
                        {
                            "standard": {
                                "query": {
                                    "multi_match": {
                                        "query": query_text,
                                        "fields": ["title", "description"],
                                    }
                                }
                            }
                        },
                        {
                            "standard": {
                                "query": {
                                    "match": {
                                        "semantic_field": {"query": query_text}
                                    }
                                }
                            }
                        },
                    ],
                    "rank_window_size": 50,
                    "rank_constant": 5,
                }
            }
        },
        "ratings": ratings,
    })

hybrid_eval = {
    "requests": hybrid_requests,
    "metric": {"recall": {"k": 10, "relevant_rating_threshold": 1}},
}

hybrid_result = es.rank_eval(index=INDEX_NAME, body=hybrid_eval)
print("Hybrid Recall@10:", hybrid_result.body["metric_score"])<p>次の結果が得られます。</p>Hybrid Recall@10: 0.75<p>ハイブリッドはBM25（<code>0.43</code>）よりも大幅に改善されており、「ランニングシューズ」のような完全一致クエリに対しても精度を維持します。</p><h2>結果：ビフォーアフター</h2><p>3つのアプローチの完全な比較は次のとおりです。</p>methods = {
    "BM25 (Lexical)": bm25_requests,
    "Hybrid (BM25 + Vectors)": hybrid_requests,
}

recall_metric = {"recall": {"k": 10, "relevant_rating_threshold": 1}}

comparison_data = []
for method_name, requests in methods.items():
    result = es.rank_eval(
        index=INDEX_NAME,
        body={"requests": requests, "metric": recall_metric}
    )
    comparison_data.append({
        "method": method_name,
        "recall@10": result.body["metric_score"]
    })

comparison_df = pd.DataFrame(comparison_data)
print(comparison_df.to_string(index=False))<p>次の結果が得られます。</p><p>メソッド</p><p>Recall@10</p><p>BM25（語彙）</p><p>0.43</p><p>ハイブリッド（BM25 + ベクター）</p><p>0.75</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5a1d72b57056fe64/6a170a71c1e8a56c58f882ab/e49f6c10516b0a48a0ad75962c6590ee07311407-700x500.png" alt="BM25語彙検索と、BM25とベクトルを組み合わせたハイブリッド検索のRecall@10を比較した棒グラフ。ハイブリッド検索の方がはるかに高いリコール率を達成していることがわかります。" /><p>クエリごとに分解すると次のようになります。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt871347f754c866d0/6a170a73839dfa40abdcfeb4/40e36dcb7b34cbf4649c512bcb60cef60f1778a6-700x500.png" alt="グループ化された棒グラフ。4つの製品クエリにおけるBM25語彙検索とハイブリッド検索のRecall@10を比較し、各クエリでハイブリッド検索が一貫して語彙検索を上回ることを示しています。" /><h2>まとめ</h2><p>この記事を通して、BM25の語彙検索は、ユーザーが正確なクエリを入力する場合には信頼性が高いものの、キーワードではなく意図に基づいて検索する場合は再現率が低下することがわかりました。<code>rank_eval</code>を用いて、そのギャップを実数で測定するための再現可能な基準を確立しました。そこから、Jina埋め込みを利用した<code>semantic_text</code>フィールドを追加し、評価を再度実行しました。結果：ハイブリッド検索により、<code>0.43</code>から<code>0.75</code>への再現率が向上し、正確な一致クエリでの精度を維持しました。ただし、実際の差はクエリの組み合わせによって異なります。</p><p>このパターンは、この例を超えてスケールします。ユーザーの実際のクエリから判断を収集し、<code>rank_eval</code>をベースラインとして実行し、<code>semantic_text</code>を追加して再度測定します。何がどれだけ改善したかが正確にわかるでしょう。</p><h2>今後の見通し</h2><ul><li><p>再現率とベクトル検索についてさらに詳しく：<a href="https://www.elastic.co/search-labs/blog/recall-vector-search-quantization">再現率とベクトル検索の量子化</a>（Jeff Vestal著）</p></li><li><p>上位結果の精度をさらに向上させるために再ランキング機能を追加</p></li><li><p><a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/rrf.html">Elasticsearchハイブリッド検索のドキュメント</a>をご覧ください。</p></li><li><p><a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/search-rank-eval.html"><code>rank_eval</code></a><a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/search-rank-eval.html"> API</a>について詳しくはこちら</p></li></ul>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/elasticsearch-relevance-tuning-improve-recall</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/elasticsearch-relevance-tuning-improve-recall</guid>
    <category><![CDATA[ハイブリッド検索]]></category>
    <category><![CDATA[Vector Database]]></category>
    <dc:creator><![CDATA[Jeffrey Rengifo]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt37c9d2971b5a2db3/6a170a75cf4f254223b2d149/492c9b5432a2b9e40cebb3b60f0df019a8c7bf6d-1280x720.png" length="0" type="image/png"/>
    <pubDate>Mon, 04 May 2026 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[ベクトル検索を世界最速のものにするためにElasticsearch simdvecを構築した方法]]></title>
    <description><![CDATA[Elasticsearchのすべてのベクトル検索クエリの基盤となる、手作業で調整されたSIMDカーネルライブラリElasticsearch simdvecの構築方法。]]></description>
    <content:encoded><![CDATA[<p>Elasticsearch simdvecは、Elasticsearch内のすべてのベクトル距離計算のエンジンです。Elasticsearchがサポートするすべてのベクトルタイプに手動で調整されたAVX-512とNEONカーネルを提供します。そのバルクスコアリングアーキテクチャは、x86では明示的なプリフェッチ、ARMではインターリーブロードによってメモリレイテンシを隠蔽し、データがCPUキャッシュを超える場合、FAISSやjvectorなどのライブラリを最大4倍上回るパフォーマンスを発揮します。この記事では、これを構築した理由、その内部構造、そしてこれがElasticsearchのベクトル検索を世界最速の1つにしている理由について説明します。</p><h2>Elasticsearch simdvecの構築方法</h2><p>Elasticsearchにおけるすべてのベクトル検索クエリは、<a href="https://arxiv.org/abs/1603.09320">Hierarchical Navigable Small World（HNSW）</a>トラバーサル、転置ファイル（IVF）スキャン、再ランキングパスのいずれであっても、結局は同じ問題に帰着します。すなわち、クエリごとにベクトル間の距離を何百万回も計算しなければならないということです。Elasticsearchは、float32からint8、bfloat16、バイナリ、Better Binary Quantization（BBQ）まで、幅広いデータ型と量子化戦略をサポートしています。それぞれにメモリ、スループット、リコールのトレードオフが異なります。そのすべての背後にあるのは、simdvecという単一のエンジンです。</p><p>私たちは、ハードウェアが許す限りすべての距離計算を高速に行うためにsimdvecを構築しました。この記事では、simdvecを構築した理由、その内部構造、そしてどのような場面で最も効果を発揮するのかを説明します。</p><h3>レースカーのような構造</h3><p>F1愛好家として（当社チームには以前フェラーリのF1チームで勤務経験があるメンバーもいます）明確な類似点を見いだせます。F1カーは、最高のラップタイムを達成するという唯一の目的のために設計されています。エンジンのパワー、空力性能、シャーシ設計は、その結果に貢献する限りにおいてのみ重要です。ベクトルデータベースについても同様で、インデキシングのスループット、クエリのレイテンシ、リコールが成功を定義します。</p><p>最終結果は重要ですが、最高レベルのパフォーマンスを達成するには、各コンポーネントが最高の状態である必要があります。<em>必要十分</em>ではなく、そのカテゴリーで<em>最高</em>でなければなりません。simdvecはその考え方で構築されており、システムの重要な部分であるエンジンに焦点を当てています。これは、専用に構築された、<a href="https://en.wikipedia.org/wiki/Single_instruction,_multiple_data">単一命令多重データ</a>（SIMD）最適化カーネルライブラリで、Javaから<a href="https://openjdk.org/projects/panama/">Panama</a>外部関数インターフェース（FFI）を介して呼び出される、手動で調整されたネイティブC++距離関数を提供します。一括スコアリング、キャッシュラインのプリフェッチ、Elasticsearchで使用されるすべてのベクトルの種類とレイアウトをサポートしています。</p><p>それがすべてのクエリの背後にあるエンジンです。</p><h3>自社開発した理由</h3><p>当社は2023年にApache LuceneのPanama Vector APIを使用してスタートしました。float32の内積計算にはうまく機能しましたが、すぐにその機能ではElasticsearchのニーズには対応できなくなりました。Elasticsearchは、int8、int4、bfloat16、シングルビット、非対称BBQなど、幅広い量子化されたベクトルタイプをサポートしています。それぞれにSIMD戦略、パッキングレイアウト、アキュムレータの要件が異なります。型カバレッジに加えて、Elasticsearchのスコアリングパスは単一ペアのスループット以上のものを要求します。HNSWは1回のパスで複数のグラフ隣接ノードをスコアリングする必要があり、IVFはプリフェッチを使用して数千の候補を一括スコアリングする必要があり、ディスクベースのスコアリングはコピーせずにmmapされたメモリ上で直接動作する必要があります。入手可能なものを調べてみましたが、すべての条件を満たすものは見つかりませんでした。</p><p>そこで、simdvecを構築しました。これは、JavaからFFI経由で呼び出される手作業で調整されたネイティブC++カーネルで、一括スコアリング、プリフェッチ、Elasticsearchが使用するすべてのベクトルタイプをサポートしています。ライブラリを所有することで、私たちはフルスタックを制御できます。BBQのような新しい量子化タイプを追加すると、システム全体にわたって調整されたSIMDカーネルが組み込まれます。上流のライブラリがそれをサポートするのを待つ必要はなく、いかなる型においてもパフォーマンスに妥協することはありません。Elasticsearchにおけるすべてのベクトルクエリ（HNSW、IVF、リランキング、ハイブリッドなど）は、実際に使用する操作とタイプに基づいて構築されたこのエンジン上で実行されます。</p><p>simdvecには、x86とARMそれぞれに対応したネイティブライブラリが用意されており、起動時に複数の命令セットアーキテクチャ（ISA）階層を選択できます。FFIを介したJavaからの呼び出しオーバーヘッドは非常に低く、<a href="https://github.com/ldematte/simsimd-benchmarks/blob/main/COMPARISON.md#ffm-downcall-overhead-measurements">1桁ナノ秒</a>です。</p><h3>ランドスケープ</h3><p>SIMD最適化されたベクトル距離カーネルを構築しているのは私たちだけではありません。このエコシステムは豊かで、私たちはsimdvecがどのように動作するのかを理解したいと考えました。プロジェクトの優劣を決めるのではなく、背景情報を提供し、Elasticsearchエンジンの位置づけを説明することが目的です。私たちは異なるアプローチを表す3つのプロジェクトを基準点として選びました。</p><ul><li><p><strong>jvector：</strong>ベクトル化された距離計算にPanama Vector APIを使用するJava近似最近傍探索（ANN）ライブラリ。x86上ではオプションでネイティブCアクセラレーションが可能。</p></li><li><p><strong>FAISS：</strong>手作業で調整されたAVX2/AVX-512カーネルを備えた、広く展開されているオープンソースのベクトル検索フレームワーク。</p></li><li><p><strong>NumKong</strong> （旧称 SimSIMD）：距離関数、行列演算、地理空間計算など、2,000種類以上の手作業で調整されたSIMDカーネルを網羅した包括的なスイート。</p></li></ul><p>各プロジェクトは異なる目的を持ち、異なるトレードオフをもたらします。Elasticsearchが必要とする特定の操作におけるsimdvecのパフォーマンスのコンテキストを提供するために、それらからの参照番号を含めています。</p><h3>測定方法</h3><p>simdvecと<a href="https://github.com/ChrisHegarty/jvector-kernel-benchmarks">jvectorのベンチマーク</a>は、FFIオーバーヘッドを含めた標準JVMマイクロベンチマークハーネスであるJMHを使用してJavaで記述されています。<a href="https://github.com/ldematte/simsimd-benchmarks">NumKongベンチマーク</a>と<a href="https://github.com/ChrisHegarty/faiss-kernel-benchmarks">FAISSベンチマーク</a>については、標準的なC++マイクロベンチマークフレームワークであるGoogle Benchmarkを使って小さなC/C++ハーネスを作成しました。どちらのフレームワークも、ウォームアップと反復キャリブレーションを含めた1操作あたりのナノ秒数の精度を報告しています。ハードウェアパフォーマンスカウンターを介して、すべてのライブラリが両方のプラットフォームでSIMDを使用していることを確認しました。ベンチマークコードはすべて、リンク先のGitHubリポジトリ（およびsimdvecの場合は<a href="https://github.com/elastic/elasticsearch">elasticsearch</a>リポジトリ）で公開されています。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt90a7d66553be3c9d/6a1701d166c4f942caf8bea5/aee116772df161cf86b7668f575ac34c733a23c5-1580x238.png" alt="表には、2つのプラットフォームが示されています。AMD EPYC Turin（Zen 5）、AVX2およびAVX-512を搭載したx86アーキテクチャAWS c8a.4xlarge、もう1つは、Graviton 4（Neoverse V2）、NEONおよびSVE2を搭載したARMアーキテクチャAWS c8g.4xlargeです。" /><p><strong>ソフトウェア：</strong> JDK 25.0.2、JMH 1.37、GCC 14、Google Benchmark（最新版）。</p><h2>一度に1つのベクトル</h2><p>ベクトル検索における最も基本的な操作は、2つのベクトル間の距離を計算することです。すべてのHNSW近隣評価、すべてのIVF候補スコア、すべての再ランク比較は、この内側のループに還元されます。</p><p>両プラットフォームで1024次元における単一ペアのスループットを測定しました。まず、ベースラインとなる型であり、エコシステムの競争が最も激しいfloat32から評価を開始しました。simdvecをFAISSおよびjvectorと比較しました。NumKongはfloat32にfloat64アキュムレータを使用するため、スループットよりも数値精度を優先し、処理速度が3.2倍から5.3倍遅くなる（プラットフォームによって異なる）ため、比較対象から除外しました。比較を同じように保つために、代わりにint8でNumKongのベンチマークを行います。ここでは、simdvecと同じアキュムレータ戦略を使用しています。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte3bc2ecaf3ad2b61/6a1701d2ab7f08039ddb9d0f/352cfa0fc18f123140f746d843404f80127fb1b7-1500x675.png" alt="「float32 Dot Product — AMD Turin」と題された横棒グラフで、5つの実装を比較しています：FAISS AVX-512：23.2 ns/op、ES simdvec AVX-512：28.3 ns/op、FAISS AVX2：36.4 ns/op、ES simdvec AVX2：38.9 ns/op、jvector：43.9 ns/op。" /><p>x86アーキテクチャーでは、FAISS AVX-512が23ナノ秒と最速のシングルペアカーネルです。simdvec AVX-512は28ナノ秒で続きますが、この差はFFI呼び出しのオーバーヘッドを反映したものです。どちらもマルチアキュムレータアンローリングを備えた512ビットFMAを使用しています。AVX2レベルでは、両者の性能差ははるかに小さく、それぞれ36ナノ秒と39ナノ秒で、どちらも256ビットのレジスタとメモリのロード幅によって制約されています。jvectorはJava Panama Vector APIを使用して44ナノ秒で到達します。Panamaは優れたSIMDコードを生成しますが、手作業で調整されたC++ intrinsicsが優位を保っています。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltcbbbe442631a575d/6a1701d42b835f81bcf4b083/95e44d9767f21ec4bccaed835e3c99aa180431ee-1500x495.png" alt="「float32 Dot Product・Graviton 4（ARM）」と題された横棒グラフ。ES simdvecが70.2 ns/op、jvectorが110.0 ns/op、FAISSが155.6 ns/opを示しています。" /><p>ARMでは、simdvecは70ナノ秒でリードしており、110ナノ秒のjvectorと156ナノ秒のFAISSをはるかに上回っています。simdvecはaarch64向けにNEONカーネルを独自にチューニングしました。JvectorにはネイティブのARMコードがなく、Panamaに依存しています。FAISSは明示的なNEON組み込み関数ではなく、コンパイラの自動ベクトル化に依存しているため、差が大きくなっています。これは、カーネルライブラリを所有することによる実用的な利点を反映しています。ElasticsearchがGravitonに拡張された際、専用に構築されたNEONカーネルを追加しました。jvectorもFAISSも、ARMネイティブコードを同程度に優先しているわけではありません。</p><p>しかし、Elasticsearchはfloat32だけを評価するわけではありません。<strong>Int8</strong>の量子化はメモリ使用量を4分の1に削減し、bfloat16は2分の1に、BBQは32分の1に削減します。それぞれのタイプには独自のSIMD戦略が必要であり、simdvecはすべてのタイプ向けに手動チューニングされたネイティブカーネルを提供しています。</p><p>比較したライブラリの中で、int8用の同等のカーネルを備えているものはNumKongのみでした。int8ドット積、二乗ユークリッド、余弦を1024次元で測定しました。</p><p><strong>Int8シングルペアスコアリング（1024次元、ns/vec 演算 - 数値が小さいほど良い）</strong></p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt3ac255d158e93504/6a1701d5a292997e48d00eb1/a0b852fd5f51d57bd2488886600472ee65ab64da-1594x378.png" alt="ドット積、二乗ユークリッド距離、コサイン演算におけるx86とARMのパフォーマンスを比較した表。各演算について、両アーキテクチャにおけるES、NumKong、およびdiffの値が示されています。" /><p>どちらのアーキテクチャでも、NumKongは小～中次元では同等かより高速です。その差は主に呼び出しのオーバーヘッドが少ないことに起因します（Cの直接呼び出しとJavaのFFI）。より大きな次元では、simdvecが追いつき、より効率的なカーネル実装（カスケードアンローリングを使用する）が呼び出しコストを償却します。次元が増えるにつれて、<a href="https://github.com/ldematte/simsimd-benchmarks/blob/main/COMPARISON.md#single-pair-i8-nsop-2">このギャップは閉じ、最終的には逆転します</a>。クロスオーバーのサイズは、機能や構造によって768から1536の間となります。</p><p>Java FFIのオーバーヘッドが若干高いにもかかわらず、simdvecは高度に最適化されたC/C++ライブラリと同等の性能を発揮します。float32<em>と</em>int8の両方に対応した最適化されたカーネルを備えた唯一のライブラリであるだけでなく、ARMアーキテクチャではトップクラスの性能を誇り、x86アーキテクチャではFAISSにわずかに劣る程度（float32の場合）、そして両アーキテクチャにおいてNumKongに非常に近い性能（int8の場合）を実現しています。また、bfloat16、int4、binary、BBQについては、代替手段は存在するものの、simdvecは各型のデータレイアウトに合わせて手作業で調整されたSIMDによって差別化を図っています。</p><p>しかし、実際の検索エンジンは一度に1つのベクトルを評価するのではなく、クエリごとに数千のベクトルを評価します。次の質問は、そのスケールで何が起こるかということです。</p><h3>一度に数千件</h3><p>シングルペアのパフォーマンスは全体の一部に過ぎません。実際には、システムが負荷時にどのように動作するかが重要です。単一のHNSWクエリは数百のグラフ近傍をスコアリングすることがあります。IVFスキャンは、数千件の投稿リストエントリーをスコアリングすることがあります。リランクパスは数万の候補をスコアリングすることがあります。シングルペアのスループットは重要ですが、より重要なのは、多くのベクトルをどれだけ速くスコアリングできるか、そして作業セットがCPUキャッシュから溢れ出すにつれてパフォーマンスがどれだけスムーズに低下するかです。</p><p>simdvecは、あらゆるデータタイプに対して一括スコアリング機能を提供します。これらは単なる単一ペアカーネル上のループではなく、クエリベクトルを1次元ストライドごとに1回ロードし、複数のドキュメントベクトル間で共有するマルチアキュムレータ内部ループを使用します。次のバッチに対しては、明示的なキャッシュラインプリフェッチが行われます。jvectorもFAISSも同等の機能を提供していません（執筆時点では）。Jvectorには一括処理APIがないため、呼び出し元はループ内で一度に1組ずつスコアを計算します。FAISSは<code>fvec_inner_products_ny</code>を公開していますが、執筆時点では、クエリ償却やプリフェッチを行わずに、シングルペア距離関数のループとして実装されています。</p><p><strong>Float32。</strong>カーネルレベルでのインパクトを測定するために、HNSWのような散在グラフの近傍検索をシミュレートするランダムアクセスパターンを用いて、1024次元float32ドキュメントベクトルの数を増やしながら単一のクエリのスコアを計算しました。3つのデータセットサイズ（32、625、32,500ベクトル）は、それぞれL1、L2、L3キャッシュを超えるように選択されています。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt2df64ec77a1921ae/6a1701d714b270393ae3c4de/1d90267be1c63ac82b8ba588617ebedb0be0d1b6-1334x558.png" alt="AMD Turin（x86、AVX-512）とGraviton 4（ARM、NEON）におけるElasticsearch simdvec、FAISS、jvectorのバルクFloat32ドット積スコアリング時間を3つのサイズで比較した2つの棒グラフ。サイズは32ベクトル、625ベクトル、32,500ベクトル。" /><p>データがキャッシュに収まる場合、simdvecはどちらのプラットフォームでも最速ですが、カーネル演算が支配的であるため、マージンは控えめです。実際の分離は、ワーキングセットがL3を超えるにつれて現れます。x86では、simdvecのスコアは1ベクトルあたり95ナノ秒ですが、FAISSは165ナノ秒、jvectorは412ナノ秒です。ARM上でも同様の傾向が見られ、simdvecは162ナノ秒で安定しているのに対し、FAISSは347ナノ秒、jvectorは476ナノ秒に上昇します。simdvecのプリフェッチとクエリの償却により、シングルペアカーネル上の単純なループでは対応できない方法でメモリの待ち時間が隠され、メインメモリの奥深くで実際の検索ワークロードが動作する場所でその利点が広がります。</p><p><strong>Int8。</strong>同じパターンは量子化された型にも当てはまります。int8ドット積の一括スコアリングを1024次元で測定し、データセットのサイズが同じL1、L2、L3キャッシュ境界を超えるように選択して、simdvecのバルクスコアリングをループ内のNumKongシングルペアスコアリングと比較しました。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltcda85e10007188d0/6a1701d9cf4f25d722b2cff7/9ee97d98b40d13b19370b76b33e67ea66bfb3250-1580x338.png" alt=" 「x86 — Bulk scoring, int8 dot product (ns/op, lower is better)」と題された表は、ES simdvecとNumKongを3つのベクトルサイズ（128、2,500、130,000）で比較したもので、対応するns/op値とスピードアップ比率が示されています。" /><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt972c311b7d08ba73/6a1701dadc55dec02ee00c87/601f50a03fa3e6263cbac9700281dfe3e511de60-1580x338.png" alt="「ARM — Bulk scoring, int8 dot product (ns/op, lower is better)」と題し、ES simdvecとNumKongを3つのベクトルサイズ（128、2,500、130,000）で比較し、対応するns/op値とスピードアップ比を比較しました。" /><p>x86では、simdvecは1.2倍～1.9倍高速で、これは明示的なプリフェッチとバッチ処理の組み合わせによってもたらされます。ARM環境では、すべてのデータセットサイズにおいて、simdvecが再び優位に立ちました（1.7倍から1.9倍高速）。その利点は、4つのベクトルを一度にバッチ処理することで、インターリーブアクセスパターンを介してメモリレベルの並列処理を実現することにあります。いずれの場合も、最も顕著な結果は最大のデータセットサイズで何が起こるかであり、それが最も重要な場所です。</p><p>結果は、二乗距離とコサインで同様のパターンを示し、ARMでは1.4倍から1.8倍、x86では1.3倍から3.0倍の速度向上が見られました（詳細は<a href="https://github.com/ldematte/simsimd-benchmarks/blob/main/COMPARISON.md">こちら</a>）。</p><h3>メモリが重要となる場合</h3><p>本番環境のベクトルインデックスは通常、CPUキャッシュに収まりません。1024次元の10Mベクトルint8インデックスは10GBです。候補のスコアリングとは、DRAMからデータをストリーミングすることを意味し、そこでバルクスコアリングアーキテクチャが大きな違いを生むのです。</p><p>バルクスコアリング中のCPU内部で何が起こるかを測定するためにハードウェアパフォーマンスカウンターを使用し、メモリーレイテンシを隠すためには、アーキテクチャごとに根本的に異なる2つの戦略が必要であることを発見しました。</p><p><strong>x86アーキテクチャでは、明示的なプリフェッチによってキャッシュミスが解消されます。</strong>バルクカーネルはベクトルを逐次的に処理し、次のベクトルを処理する前に1つのベクトルを完全に計算しながら、次のバッチのためのプリフェッチ命令を発行します。将来のデータはCPUが必要とする前にL1に取り込まれます。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt368f29b5143d0f5a/6a1701dcc1e8a51780f8817b/a39548f8060c2a4a5154521a4047dd92d8cd77be-1580x309.png" alt="「x86 (AMD Turin) — Hardware counters per int8 operation」というタイトルの表では、L1キャッシュミス、IPC、dTLBミスのシングルモードとバルクモードを比較し、それに対応する改善要因を示しています。" /><p>ARMアーキテクチャでは、プリフェッチを使用しても、同じ逐次的なアプローチではパフォーマンスが低下しました。代わりに、<strong>バルクカーネルが4つのベクターからすべてのストライド位置でロードをインターリーブ</strong>し、アウトオブオーダーエンジンに4つの独立したメモリストリームを提供します。CPUのデータの取得速度が速くなったわけではなく、メモリ要求の処理中に常に別の計算処理を行うことで、待ち時間を短縮しているのです。詳細な分析については<a href="https://github.com/elastic/elasticsearch/issues/145412">こちらのGitHubイシュー</a>をご覧ください。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt8751ac5b20b75508/6a1701deab7f08db83db9d13/832de3bc0d556a493b7bf3f250196018acfc1585-1580x238.png" alt="表「ARM (Graviton 4) — Hardware counters per int8 operation」は、L1 キャッシュミスとバックエンドのストールについて、シングルモードとバルクモードを比較し、対応する改善点を示しています。" /><p>数字は2つの異なる物語を語っています。</p><ol><li><p>x86アーキテクチャでは、プリフェッチによって139,000回のキャッシュミスが19,000回に減り、1サイクルあたりの命令実行数（IPC）が2倍以上になります。データセットのサイズが大きくなるにつれてプリフェッチによってコストのかかるDRAM往復処理が徐々に隠蔽されるため、データ量の増加によるメリットは大きくなり、L2レベルでは1.2倍、L3レベルを超えると2.8倍になります。</p></li><li><p>ARMではキャッシュミスはほとんど変わりません。変化するのは利用率です。インターリーブアクセスパターンによってパイプラインへの供給が維持されるため、バックエンドの停止時間が40%減少します。この利点は、データセットのサイズに関係なく一貫して1.8倍です。これは、データがキャッシュまたはDRAMから来ているかに関係なく、メモリレベルの並列処理が適用されるためです。</p></li></ol><p>2つのアーキテクチャ、2つの戦略、結果は1つ：本番環境規模では、simdvecはベクトルがメインメモリ全体に分散している場合でも、CPUパイプラインを常にフル稼働させます。</p><h2>Elasticsearchユーザーへの影響</h2><p>これらのカーネルレベルの能力は相乗効果を発揮します。単一のベクトル検索クエリでは、HNSWグラフの走査、候補のスコアリング、再ランキングなど、数百万もの距離演算が計算される場合があります。数千回の同時クエリにおいて、1回の操作でナノ秒単位がクエリ遅延やクラスタスループットに直接変換されます。float32、int8、bfloat16、BBQのいずれを使用する場合でも、インデックスがメモリ上にあるかディスク上にあるかに関わらず、simdvecが基盤となるエンジンであり、これらのすべての操作は同じエンジンを通して実行され、ナノ秒単位まで最適化されています。</p><p>重要なポイントは、本番規模では、ベクトル検索のパフォーマンスは主に生のSIMDスループットによって決まるわけではないということです。システムのパフォーマンスは、数百万もの小さな演算処理において計算能力を維持しながら、メモリの遅延をいかに効率的に隠蔽できるかに大きく左右されます。</p><p>simdvecカーネルは、ほぼすべてのElasticsearchリリースで改善されています。新しい量子化タイプやハードウェアプラットフォームが登場すると、初日からチューニング済みのカーネルが提供されます。また、既に出荷されている実装を改良していくにつれて、既存の型もますます高速化していきます。</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/elasticsearch-vector-search-simdvec-engine</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/elasticsearch-vector-search-simdvec-engine</guid>
    <category><![CDATA[Vector Database]]></category>
    <category><![CDATA[Elastic内部の実情]]></category>
    <dc:creator><![CDATA[Chris Hegarty,Lorenzo Dematte,Simon Cooper]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blta18088409621369b/6a1701dfdc55de7297e00c8b/df9646091bafbbf0a6dfd212ff8a6bd1e8589708-1280x720.png" length="0" type="image/png"/>
    <pubDate>Thu, 23 Apr 2026 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Elasticsearch + Jina埋め込みによる教師なし文書クラスタリング]]></title>
    <description><![CDATA[ElasticsearchとJina埋め込みを使用した教師なし文書クラスタリングへの実用的で再現可能なアプローチ。]]></description>
    <content:encoded><![CDATA[<p>ベクターサーチはクエリから始まりますが、もしクエリがなければどうすればよいのでしょうか？</p><p>組織は大量のドキュメントコレクション（サポートチケット、法的書類、ニュースフィード、研究論文など）を蓄積しており、適切な質問をする前にその内容を理解する必要があります。ラベルやトレーニングデータがなければ、何千もの文書を手動で確認するのは非現実的です。何を検索すればいいかわからない場合、従来の検索は役に立ちません。</p><p>この投稿では、この検索の問題に対処するElasticsearchネイティブのアプローチによる教師なし文書クラスタリングと時間軸に沿ったストーリー追跡について説明します。最終的には、次のようにストーリーの文脈を日々にわたってたどることができるようになります。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blta8c243e0c5773440/6a17093fa6c2b98c86e7968c/100a60a7fb85da8ab3813fd071a82c93f2c3f318-1300x650.png" alt="2025年2月全体の時間軸に沿ったストーリーチェーン。各色のパスは複数日にわたって続くストーリーを表し、リンクの幅はkNNの重なり強度を示しています" /><p><strong>読み取れる内容：</strong></p><ul><li><p>なぜ<strong>クラスタリング埋め込み</strong>（検索埋め込みではない）が、クエリーなしでトピック発見を行いたい場合に重要なのか。</p></li><li><p>Elasticsearchのk-nearest-neighbor（kNN）とバッチ処理<code>msearch</code> を使用したトピックにより、密度探査型重心分類で文書をグループ化する方法。</p></li><li><p><a href="https://www.elastic.co/docs/reference/aggregations/search-aggregations-bucket-significanttext-aggregation"><code>significant_text</code></a> がクラスターに自動ラベルを付けることで、モデルをトレーニングしなくてもテーマを読めるようにする方法。</p></li><li><p>テーマが日々どのように変化するかを示す上で、時間軸に沿ったストーリーチェーンが日々のクラスターをどのように結び付けるのか。</p></li></ul><p>このパイプラインでは、2025年2月のBBCニュースとガーディアンの記事を約8,500件、テストコーパスとして使用しています。ニュースは明確な時間的推移を示すため便利ですが、このパターンは文書の発見が重要なあらゆる場面に適用されます：法的レビュー、コンプライアンスの監視、研究の統合、カスタマーサポートのトリアージ。</p><p><strong>スタック：</strong></p><ul><li><p><a href="https://www.elastic.co/search-labs/blog/jina-embeddings-v5-text"><strong>Jina v5</strong></a>の<strong>クラスタリング埋め込み：</strong>トピックグループ化のためのタスク固有の低ランク適応（LoRA）アダプター。<a href="https://www.elastic.co/blog/elastic-jina-ai">JinaはElasticに統合され</a>、そのモデルは<a href="https://www.elastic.co/docs/explore-analyze/elastic-inference/eis">Elastic Inference Service（EIS）</a>を通じてネイティブで利用可能です。</p></li><li><p><strong>Elasticsearch：</strong>拡張性のある<a href="https://www.elastic.co/docs/solutions/search/vector/knn">kNN</a>、<code>significant_text</code>ラベリング、ベクトルストレージ。</p></li><li><p><a href="https://www.elastic.co/search-labs/blog/diskbbq-elasticsearch-introduction"><strong>DiskBBQ：</strong></a>ディスクベースのベクトルインデックス形式で、 <a href="https://www.elastic.co/docs/reference/elasticsearch/index-settings/bbq">Better Binary Quantization（BBQ）</a>と階層的なk-平均法による分割を組み合わせ、近似最近近接傍（ANN）加速を実現します。このインデックス分割はベクトル探索の内部で行われ、本投稿で用いる密度探査型クラスタリング・アルゴリズムとは別です。<code>bbq_disk</code>はディスクに量子化されたベクトルを格納し、ヒープにはパーティションメタデータのみを保持することで、<code>bbq_hnsw</code>と比較してリソース要件を大幅に削減しながら、高い再現率を維持します。</p></li><li><p><strong>グローバルクラスタリング + 日々の時間軸に沿ったリンク付け：</strong>発見とストーリーの進化。</p></li></ul><p><strong>以下が必要です：</strong></p><ul><li><p>Elasticsearchの導入（Elastic Cloud、Elasticsearchサーバーレス、またはElastic Self-Managed 8.18+/9.0+）：<code>bbq_disk</code> 8.18以降が必要です。オプションのdiversify retrieverセクションは9.3+またはサーバーレスが必要です。</p></li><li><p><a href="https://jina.ai/embeddings/">Jina APIキー</a>：無料プランには1000万トークンが含まれており、これはコアクラスタリング・パイプライン（約425万トークン）をカバーします。オプションの検索対クラスタリング比較は、2番目の埋め込みパスを使用します。</p></li><li><p><a href="https://bonobo.capi.gutools.co.uk/register/developer">Guardian APIキー</a>（無料）。</p></li></ul><h2>セットアップ</h2><p>必要なパッケージをインストールしてください：</p>pip install elasticsearch pandas numpy plotly umap-learn python-dotenv pydantic-settings datasets requests<p>オプション（このリポジトリからスクレイピングヘルパーを実行する場合のみ）：</p>pip install beautifulsoup4<p>次に、プロジェクトのルートにある<code>.env</code>ファイルでAPIキーを設定します。</p>ELASTIC_CLOUD_ID=your-cloud-id        # or ELASTIC_HOST=https://...
ELASTIC_API_KEY=your-api-key
JINA_API_KEY=your-jina-key
GUARDIAN_API_KEY=your-guardian-key<p>このノートブックは <code>load_dotenv(override=True)</code>を呼び出し、局所的な <code>.env</code> 値が優先されます。</p>Connected to Elasticsearch<h2>パート1：ディスカバリークラスタリング - なぜ埋め込みをクラスタリングするのか？</h2><p>ほとんどのベクター検索は、<em>クエリ</em>を関連<em>文書</em>に一致させるようにトレーニングされた<strong>検索埋め込み</strong>を使用します 。これは検索には最適ですが、発見には適していません。まったくクエリーせずにコーパスに存在するトピックを見つけたい場合は、類似した文書をグループ化する埋め込みが必要です。</p><p>Jina v5では、<strong>タスク固有の低ランク適応（LoRA）アダプタ</strong>を使用してこの問題を解決します。LoRAは、ほとんどのベースモデルの重みを凍結したまま、ターゲットとなる内部層に小さな低ランクの更新を加えるため、完全な再トレーニングを行うことなく、モデルの挙動が特定のタスクにシフトします。同じベースモデルでも、<code>task</code>パラメータによって異なる埋め込みが生成されます。</p><p>タスク</p><p>訓練の目的</p><p>ユースケース</p><p>retrieval.passage</p><p>クエリーと文書のマッチング</p><p>Search、Retrieval-Augmented Generation（RAG）</p><p>クラスタリング</p><p>トピックのグループ化（密度が高いクラスタリングの最適化）</p><p>発見、分類</p><p>クラスタリング・アダプターは、同じトピックに関する文書を埋め込み空間で<em>近づけ</em>、異なるトピックに関する文書を<em>離す</em>ように訓練されています。下のビジュアル比較で、その違いが具体的にわかります。</p><h3>検索とクラスタリング：視覚的な比較</h3><p>違いを確認するために、両方のタスクタイプを含むサンプル文書を埋め込んでいます。クラスタリングは元の1024次元の埋め込み空間で実行されます。均一多様体近似と射影（UMAP）は、可視化のためそれらの埋め込みを2Dに投影する目的でのみ使用されます。東映UMAPは局所的な近傍構造を保持するため、クラスターの分離を比較する上で有用です。</p><p>以下では、同じ480件の文書のサンプルが両方のタスクタイプに埋め込まれ、UMAPで2Dに投影されています。クラスタリングパネルで、より密集していて、分離された色群を探します。</p>    Full dataset: 8,495 articles
    Sources: guardian: 5749, bbc: 2746
    Date range: 2025-02-01 to 2025-02-28


    Sample: 480 docs across 8 sections
    section
    Film              60
    World news        60
    Australia news    60
    Opinion           60
    Football          60
    US news           60
    Sport             60
    Business          60


    Clustering embeddings: 480
    Retrieval embeddings:  480


    UMAP projection complete<img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt4b3733dccad212b6/6a1709407d8d67aaeb70e6a4/9bcf7a744900560c1c6c63a2dc3af2f9bfd33e11-1100x500.png" alt="UMAPによる検索埋め込みとクラスタリング埋め込みの比較" /><p><em>検索埋め込み（左）はトピックを広く分散させます。クラスタリング埋め込み（右）は、同じドキュメントからより緊密で分離されたグループを生成します。</em></p><p>クラスタリング埋め込みは、より緊密で視覚的に際立ったグループを生み出します。検索用の埋め込みはトピックをより均等に分散させ、（きめの細かい類似性で）検索する上で理想的です。しかし、発見のためには、緊密なトピッククラスターが重要です。</p><p>このため、このチュートリアルの残りの部分では <code>task="clustering"</code> が使用されています。</p><h3>データセットの読み込み</h3><p>次のコーパスは、2025年2月の2つのニュースソースを組み合わせています。</p><ul><li><p><a href="https://huggingface.co/datasets/RealTimeData/bbc_news_alltime">RealTimeData/bbc_news_alltime</a> HuggingFaceデータセット経由の<strong>BBCニュース</strong>。</p></li><li><p><a href="https://open-platform.theguardian.com/">Guardian Open Platform API</a>経由の<strong>The Guardian</strong>。</p></li></ul><p>複数のソースがあると、クラスタリングが<em>トピック</em>ではなく<em>ソース固有のスタイル</em>を見つけることを検証するのに役立ちます。</p>    Total articles:  8,495
    
    Source breakdown:
    source
    guardian    5749
    bbc         2746
    
    Date range: 2025-02-01 → 2025-02-28
    Days covered: 28
    
    Sample article:
      Source:  guardian
      Title:   Carbon monoxide poisoning ruled out in death of Gene Hackman and wife, police sa
      Section: Film
      Text:    Authorities have ruled out that Gene Hackman and his wife, Betsy Arakawa, died from carbon monoxide poisoning earlier this week in their home in Santa Fe, New Mexico. The Santa Fe county sheriff, Adan...<h3>クラスタリングタスクによる埋め込み</h3><p>Jina v5 APIはすべての文書に対して<code>task="clustering"</code>で呼び出されます。埋め込みはディスクにキャッシュされるため、その後の実行ではAPIを完全にスキップします。</p><p>API呼び出しはシンプルです。<code>task</code>パラメータが、典型的な埋め込み使用との主な違いです。</p>payload = {
    "model": "jina-embeddings-v5-text-small",
    "input": texts,
    "task": "clustering",  # ← This selects the clustering LoRA adapter
}<p>以下のタイミングはキャッシュヒットを反映しています。APIに対する最初の実行は、コーパスサイズによって時間がかかります。</p>    Embeddings ready: 8,495 vectors of dimension 1024
    Time: 0.6s<h3>単一のElasticsearchインデックスへのインデキシング</h3><p>発見クラスタリングでは、1か月間を1つのインデックス（<code>docs-clustering-all</code>）にまとめます。日々の分割は、時間軸に沿ったストーリーリンクのために後から行われます。</p><p>インデックスマッピングでは、ベクトルフィールドに<a href="https://www.elastic.co/docs/reference/elasticsearch/index-settings/bbq"><code>bbq_disk</code></a>を使用します。</p>{
  "embedding": {
    "type": "dense_vector",
    "dims": 1024,
    "index": true,
    "similarity": "cosine",
    "index_options": {
      "type": "bbq_disk"        // hierarchical k-means partitioning for ANN index lookup; separate from this post's clustering algorithm
    }
  }
}<p>1024次元のfloat32ベクトルは4KBです。<a href="https://www.elastic.co/search-labs/blog/diskbbq-elasticsearch-introduction"><code>bbq_disk</code></a> は階層的k-means法を使用してベクトルを小さなクラスターに分割し、それらをバイナリ量子化し、再スコアリングのためにフルプレシジョンのベクトルをディスクに格納します。パーティションのメタデータのみがヒープに存在し、そのため大規模なコーパスでもメモリ要件は低く抑えられます。より多くのヒープを許容できるワークロードの場合、<a href="https://www.elastic.co/docs/reference/elasticsearch/index-settings/bbq"><code>bbq_hnsw</code></a> は、より高いリソースコストでより高速な検索を行うための階層的ナビゲーシブル・スモールワールド（HNSW）グラフを構築します。</p><p><a href="https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/dense-vector"><code>dense_vector</code></a>フィールドタイプは複数の量子化戦略をサポートしています：<code>bbq_disk</code>と<code>bbq_hnsw</code>は、ここで使用されている1024次元ベクトルのような高次元埋め込みに最適です。</p>    Indexed 8,495 documents into docs-clustering-all
    Time: 57.5s<h3>クラスタリング：密度探査型重心分類</h3><p>HDBSCANのような従来型のクラスタリングアルゴリズムでは、完全なn×dベクトル行列をメモリに保持し、フルパス更新を繰り返し実行できることを前提としています。8,495件のドキュメントを1024次元で扱う場合、処理可能（最大35MB）ではあるものの、このアプローチは追加のインフラストラクチャーなしでは数百万の文書にスケールすることはできません。</p><p>このアルゴリズムは、ボロノイ領域への割り当て割り当てとノイズフロアによるKMeans++法の初期化と概念的には似ていますが、Elasticsearchの<a href="https://www.elastic.co/docs/solutions/search/vector/knn">kNN検索</a>を計算プリミティブとして使用し、ほぼ全ての作業をサーバー側で行います。</p><ol><li><p><strong>文書の5%を密度プローブとしてサンプリング</strong>します（ランダムサンプル、最低50件）。</p></li><li><p><strong>バッチ処理された　msearch　kNNによるプローブ密度。</strong>各プローブはkNNクエリを実行し、隣接プローブの平均類似度を記録します。平均類似度が高い＝埋め込み空間の密な領域。<a href="https://www.elastic.co/docs/api/doc/elasticsearch/operation/operation-msearch"><code>msearch</code></a> は単一のHTTPコールで複数の検索リクエストを送信し、これは重要です。密度プローブは数百のkNNクエリを生成し、それらをバッチ処理することでリクエストごとのオーバーヘッドを回避します。</p></li><li><p><strong>多様性を考慮した高密度シードの選択</strong>：中央値以上の密度を持つ候補は、密度の降順でソートされ、既存のすべてのシードとのコサイン類似度が分離閾値を下回る場合にのみ貪欲に受け入れられます。これがクライアント側での唯一の計算です（8,000件の文書で最大0.01秒）。</p></li><li><p><strong><code>msearch</code></strong> <strong>kNN</strong><strong>で全ての書類を重心に照らして分類します</strong>。各シードが重心として機能し、kNN検索は類似度しきい値を超える近接文書を取得します。各文書は、最も高いスコアを返した重心に割り当てられます。小さなクラスターはノイズとして処理されます。</p></li></ol><p>Elasticsearchが面倒な処理を担当します：<code>msearch</code>は密度プローブ、<code>msearch</code>は分類、<code>significant_text</code>はラベル付けを担います。このコーパス（8,495件の文書）では、5％の密度プローブサンプルが425件のkNNプローブクエリを開始し、<code>msearch</code>が9つのHTTPコール（バッチサイズ50）にバッチ処理され、プローブごとに1つのリクエストを処理することによるオーバーヘッドを回避します。<code>bbq_disk</code>ANNルックアップと組み合わせることで、クラスタリング段階を高速かつスケーラブルに保つことができます。kNNクエリは、クラスタリングパス中の速度のために最小の<a href="https://www.elastic.co/docs/deploy-manage/production-guidance/optimize-performance/approximate-knn-search"><code>num_candidates</code></a>値を使用します。本番環境の検索クエリでは、レイテンシを犠牲にしてリコールを向上させるために、より高い<code>num_candidates</code>値を使用する必要があります。</p><p>クラスターは各重心の周りの埋め込み空間の密度によって決定される実際的なサイズを持ち、厳格な<code>k</code>の上限によって決定されるものではありません。密なトピック領域はより大きなクラスターを生み出し、ニッチなトピックは小さなクラスターを生み出します。</p><h4>KMeansやHDBSCANが実用的ではない理由</h4><p>KMeans法は球状クラスターを想定しており、メモリに完全なn×d行列が必要です。メモリに収まるコーパスの場合、<a href="https://scikit-learn.org/stable/modules/generated/sklearn.cluster.HDBSCAN.html">HDBSCAN</a>は強力な代替手段です。任意のクラスタ形状に対応でき、密度に関するセマンティクスも十分に理解されています。</p><p>密度探査型重心アプローチは、異なるニッチをターゲットとしています。ストレージ、検索、およびクラスタリングを1つのシステムで行いたい場合や、スケールがクライアント側の行列操作を実用的でなくするようなコーパスです。これは、Elasticsearch kNNを計算プリミティブとして使用し、任意のクラスターサイズを処理し、ほぼすべての計算をサーバー側で行います。</p>    Clustered global index in 31.6s
      Total clusters: 82
      Total noise:    2420 (28.5%)
      Density probes: 425 kNN queries via 9 _msearch HTTP calls<h4>ノイズレートについて理解する</h4><p>最大28%のノイズ率は設計によるもので、故障モードではありません。設定された<code>similarity_threshold</code>でどの高密度クラスターにも適合しない文書は、一致率が低いと強制的に判断されるのではなく、割り当てられずに残されます。これは品質ゲートの役割を果たします：意見を記したコラム、短い記事、そして一回限りのストーリーは、一貫したグループを定義する主題の密度が不足しているため、クラスタリングに抵抗する性質があります。</p><p>しきい値は調整可能です。<code>similarity_threshold</code>を下げると、より積極的なクラスタリングが生成されます（より多くの文書が割り当てられますが、クラスターは緩くなります）。一方、これを上げると、クラスターが引き締まり、ノイズの割合が増加します。こうした様々なニュースコンテンツを含むコーパスにおいては、約30％のノイズは妥当な動作基点と言えます。本番環境での導入は、分野固有の品質基準に合わせてしきい値を調整することが推奨されます。</p><h3>significant_textを使用した自動ラベル付け</h3><p>各クラスターには人間が読みやすいラベルが必要です。Elasticsearchの<code>significant_text</code>アグリゲーションは、フォアグラウンドセット（クラスター）とバックグラウンドセット（完全なコーパス）を比較して、異常に頻繁に現れる用語を見つけます。</p><p>内部的には、絶対頻度の変動と相対頻度の変動のバランスを取る統計的ヒューリスティック（デフォルトではJLHスコア）を使用しており、機械学習や大規模言語モデル（LLM）の呼び出しは行っていません。英国の政治に関するクラスターでは、<code>starmer</code>、<code>labour</code>、<code>downing</code>のような用語が浮かび上がる可能性があります。これらの用語は、全体的なニュースコーパスと比較して、そのクラスターで不釣り合いに頻出しているためです。</p><p>このグローバルパスでは、ラベルは<code>docs-clustering-all</code>に対して直接計算されるため、前景と背景の両方が月全体のデータから描画されます。パート2では、ラベル付けに日次インデックスパターン（<code>docs-clustering-*</code>）を使用します。これは、クエリで一致するすべてのインデックスを同時に対象にできるワイルドカードで、<code>significant_text</code>により広い背景を与えてコントラストを高めます。</p><p>最小クエリー形状は次のようになります。</p>{
  "size": 0,
  "query": { "term": { "cluster_id": "72" } },
  "aggs": {
    "label_terms": {
      "significant_text": {
        "field": "text",
        "size": 5,
        "filter_duplicate_text": true
      }
    }
  }
}<p><code>significant_text</code> また、品質ゲートの役割も果たします。有意な用語を生成しないクラスターには、特徴的な語彙がありません。これらはまとまりのないグループであり、誤解を招くようなラベルを付けるのではなく、ノイズとして分解する必要があります。</p><p>軽量で決定論的なクリーンアップステップで、ノイズの多いラベルの用語（数値トークン、一般的な単語）を削除し、必要に応じて代表的な見出しに切り替えます。これにより、ラベルをElasticsearchネイティブのまま維持しつつ、可読性を向上させます。</p>    Sample cluster labels:
      cluster   3  (200 docs)  arsenal | mikel | villa
      cluster   1  (198 docs)  volodymyr | ukrainian | kyiv
      cluster   0  (196 docs)  hostages | hamas | israeli
      cluster   4  (187 docs)  scrum | rugby | borthwick
      cluster  52  (185 docs)  fossil | renewable | renewables
      cluster  10  (156 docs)  labour | gwynne | mps
      cluster  40  (151 docs)  novel | novels | literary
      cluster  11  (149 docs)  mewis | sarina | wiegman
      cluster  44  (143 docs)  flooding | rainfall | rain
      cluster  13  (131 docs)  doge | musk | elon
      cluster  12  (128 docs)  murder | insp | knockholt
      cluster   5  (124 docs)  putin | backstop | starmer


    Reassigned 35 docs from incoherent clusters to noise
    Total docs: 8,495
    Clustered:  6,040 (71.1%)
    Noise:      2,455 (28.9%)<h3>クラスターの可視化</h3><p>以下の可視化は、グローバルクラスタリングパスが発見した内容を示しています：クラスター化された文書とノイズ文書の日付ごとの内訳、全月のUMAP投影、およびクラスターがソースではなくトピックを反映していることを確認するソースミックスチャート。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt4ed087d8b6dac2a0/6a17094260084b44543c4501/99099f5adaa945ae4097c50b0d7151c7dd28872e-1000x400.png" alt="クラスター化された文書とノイズ文書の日次分布" /><p>2025年2月におけるクラスター化された文書とノイズ文書の日次分布。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltf5ca320bc91131ab/6a17094366c4f95828f8bfbf/477c6c7177942955a942f85f5c881da50e517915-1100x700.png" alt="全文書を含む1か月分のUMAP投影" /><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5f554a6bc2bc367b/6a170945a929cfa7a1ae0947/4f4302556c8974c416842452cf33bca06e90b966-1100x700.png" alt="クラスター化された文書のみを表示するUMAP投影" /><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt8e40c26f89ce5523/6a17094747d49c147d2d8974/327f96a79e382ef30614cb0570aa7fccd822b8f8-1100x700.png" alt="[単一のクラスターを強調表示したUMAP投影" /><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltf1dd2c19ae628f1d/6a1709481949f7a630e7a9a3/acfb1524a10e24d6ff2412e7c3ec0f2b3ac75193-900x600.png" alt="トピックベースのグループ化を示すクラスターごとのソースミックス" /><p>UMAPの色のついた島はそれぞれがクラスターを表しています。クラスターは、純粋に類似性を埋め込むことで発見された、同じトピックに関する記事の集合です。灰色のノイズポイントは、いずれのクラスターにもきれいに収まらなかった記事（多くの場合、短い記事、意見記事、または一回限りのストーリー）です。</p><p>情報源の内訳図を見ると、各クラスターにはBBCニュースとガーディアンの<strong>両方</strong>からの記事が含まれていることが確認できます。クラスタリングは、<em>ソース</em>ではなく<em>トピック</em>を見つけ出すものであり、まさに教師なし学習から期待される結果です。</p><h3>Diversify Retrieverによるクラスター幅の調査</h3><p>通常のkNNは、クラスターの重心（密なコア）に最も類似した文書を返します。しかし、実際のクラスターはサブトピックも対象にします。The <a href="https://www.elastic.co/docs/reference/elasticsearch/rest-apis/retrievers/diversify-retriever"><strong>Diversify Retriever</strong></a>は、Maximal Marginal Relevance（MMR） を使用して、重心に関連するだけでなく、<em>互いに異なる</em>文書を抽出します。</p><p>鍵となるパラメータは <strong>λ（ラムダ）</strong>です。</p><ul><li><p>λ = 1.0 → 純粋な関連性（通常のkNNと同じ）。</p></li><li><p>λ = 0.0 → 純粋多様性（最大限に分散された結果）。</p></li><li><p>λ = 0.5 → バランスが取れている：トピックに関連しているが、異なる角度をカバーしている。</p></li></ul><p>最小Retrieverリクエストの形状は次のようになります。</p>{
  "size": 8,
  "retriever": {
    "diversify": {
      "type": "mmr",
      "field": "embedding",
      "lambda": 0.5,
      "query_vector": "&lt;cluster-centroid-vector&gt;",
      "retriever": {
        "knn": {
          "field": "embedding",
          "query_vector": "&lt;cluster-centroid-vector&gt;",
          "k": 50,
          "num_candidates": 100
        }
      }
    }
  }
}<p><code>type</code>、<code>field</code>、および <code>query_vector</code> パラメーターは、diversifyレベルで必要です。<code>field</code> はMMRに結果間の類似性に使用する dense_vector フィールドを指示し、<code>query_vector</code> は関連性スコアリングの参照点を提供します。</p><p>これにより、単に「その中心は何か？」という問いではなく、「このクラスターは実際に何を対象としているのか？」という問いに答えることができます。</p>    Exploring cluster 52 (185 docs)
    Label: fossil | renewable | renewables
    Centroid computed (dim=1024)


    ========================================================================
    Plain kNN (closest to centroid)
    ========================================================================
      1. [0.9738] Green campaigners fear ministers are poised to award billions of pounds in fresh subsidies to Drax power station, despite strong concerns...
      2. [0.9710] Thirteen more oil and gas licences could be cancelled as ministers decide new guidance for fossil fuel extraction after a landmark court...
      3. [0.9699] Experts have accused the fossil fuel industry of seeking special treatment after lobbyists argued greenhouse gas emissions from oilfields...
      4. [0.9681] Burning wood is a terrible way of producing electricity . Chopping down trees destroys habitats for wildlife, and growing new trees cannot...
      5. [0.9649] Keir Starmer will do huge damage to the global fight against climate change if he gives in to political pressure and allows the development...
      6. [0.9641] Labour will next week be confronted with stark policy choices that threaten to expose the fault lines between the Treasury and the...
      7. [0.9638] The Drax power station near Selby in north Yorkshire burns imported wood pellets  The government has agreed a new funding arrangement with...
      8. [0.9581] If you care about the world we are handing on to future generations, the news on Thursday morning was dramatic. This January was the...
    
    ========================================================================
    Diversify retriever (MMR, lambda=0.5)
    ========================================================================
      1. [0.9738] Green campaigners fear ministers are poised to award billions of pounds in fresh subsidies to Drax power station, despite strong concerns...
      2. [0.9434] Oil and gas interests have waged a coordinated campaign to kill pro-electrification policies that ban gas connections in new buildings ,...
      3. [0.9303] It was interesting to read that new licences for oil and gas production in the North Sea are being delayed by legal action ( Thirteen more...
      4. [0.9139] The US energy secretary, Chris Wright, has said he “would love to see Australia get in the game of supplying uranium and maybe going down...
      5. [0.9077] Rachel Reeves was facing criticism on Saturday night as it was confirmed that a report she cited as evidence that a third ­runway at...
      6. [0.8996] When Margaret Thatcher opened the Hadley Centre for Climate Change in 1990 journalists suggested she was attempting to appear to be doing...
      7. [0.8993] The vast majority of governments are likely to miss a looming deadline to file vital plans that will determine whether or not the world has...
      8. [0.8987] European imports of seaborne gas shipments fell by a fifth last year to their lowest level since the pandemic, according to a new report,...
    
    Overlap: 1/8 documents appear in both result sets
    
    Avg pairwise similarity (lower = more diverse):
      Plain kNN:          0.9057
      Diversify retriever: 0.6965<p>プレーンなkNNは、トピックの1つの角度、つまり中心点および互いに最も類似したドキュメントの周りにクラスターを形成します。Diversify Retrieverは、同じクラスターの異なるファセット、つまりサブトピック、異なるソース、多様な視点を提示します。</p><p>多様性指標はこれを定量的に裏付けます。Diversify Retrieverの結果では、平均ペアワイズ類似度が低く、これは返された文書がより広範囲をカバーしていることを意味します。</p><p>これは以下の用途に役立ちます。</p><ul><li><p><strong>クラスターが実際に対象としている内容を理解</strong>。その中心だけでなく端も含め理解します。</p></li><li><p><strong>要約の生成</strong>。多様で代表的な文書は、LLMにより良い材料を提供します。</p></li><li><p>人によるレビューや下流工程でのラベリングのために、<strong>代表的な例を発見</strong>。</p></li><li><p><strong>品質チェック</strong>。多様な結果が一貫性に欠けている場合、クラスターの分割が必要かもしれません。</p></li></ul><h2>パート2：時間軸に沿ったストーリーチェーン</h2><h3>日をまたいだデータストーリーの追跡</h3><p>パート1では、トピック発見のために1か月全体をグローバルにクラスタリングしました。時間軸に沿ったフローでは、同じ密度プローブの重心分類が<strong>日次インデックス</strong>ごとに1日単位で独立して実行され、その後、クラスターが隣接する日々にわたってリンクされます。注意：日々のクラスターはパート1のグローバルクラスターとは独立しており、それぞれの1日はその日のコンテンツに合わせて独自のクラスター割り当てとラベルを生成します。</p><h4><strong>関連付けの手法：サンプルとクエリ</strong></h4><p>A日目の各クラスターについて：</p><ol><li><p>いくつかの代表的な文書をサンプルとして選択します。</p></li><li><p>kNNをB日目のインデックスに対して実行します。</p></li><li><p>B日目のクラスターごとにヒット数を数えます。</p></li><li><p>ヒット率がしきい値（kNNの割合≥ 0.4）を超える場合は、リンクを記録します。</p></li></ol><p>この処理は高速で（クラスターごとにわずかなドキュメントのみがクエリされ、すべてではありません）、ElasticsearchのネイティブkNNを使用し、外部ツールは必要ありません。</p>Preparing daily indices for temporal linkage...


Indexed 8,495 docs into 28 daily indices


Temporal links found: 808 in 145.4s

Strongest links:
  2025.02.01 'league | arsenal | premier' -&gt; 2025.02.02 'league | season | striker'  (100%)
  2025.02.03 'league | striker | loan' -&gt; 2025.02.04 'league | striker | season'  (100%)
  2025.02.03 'score | operator | gedling' -&gt; 2025.02.04 'league | striker | season'  (100%)
  2025.02.12 'playoff | leg | bayern' -&gt; 2025.02.13 'league | players | injury'  (100%)
  2025.02.14 'league | injury | football' -&gt; 2025.02.15 'league | premier | football'  (100%)
  2025.02.18 'russia | ukraine | talks' -&gt; 2025.02.19 'saudi | russia | arabia'  (100%)
  2025.02.18 'football | league | bayern' -&gt; 2025.02.19 'league | manchester | players'  (100%)
  2025.02.21 'league | premier | manchester' -&gt; 2025.02.22 'game | players | defeat'  (100%)
  2025.02.21 'rugby | calcutta | brilliant' -&gt; 2025.02.22 'game | players | defeat'  (100%)
  2025.02.26 'metals | kyiv | ukrainian' -&gt; 2025.02.27 'ukraine | russia | talks'  (100%)<p>kNN分数が100％ということは、ソースクラスターからサンプリングされたすべてのドキュメントが同じターゲットクラスターに到達していることを意味し、これは異なる日付間のリンクが理論上最も強くなります。ほとんどの上のリンクはフットボール関連で、これは理にかなっています。プレミアリーグは毎日報道され、高いトピックの一貫性があります。</p><p>The <code>score | operator | gedling</code> → <code>league | striker | season</code> リンクは、ニッチなローカルフットボールクラスター（Gedling はノンリーグクラブです）が翌日に広範なプレミアリーグクラスターに吸収される例であり、これは異なる粒度での日々の再クラスタリングの自然な効果です。</p><h3>ストーリーチェーンの構築</h3><p>ストーリーチェーンとは、連日にわたって連続した一連のクラスターです。</p><p>個々のペアワイズリンクは、月曜日の「UK politics」クラスターが火曜日のクラスターにリンクしていることを示しています。チェーンはストーリー全体を明らかにします。月曜日に始まり、週を通して展開し、金曜日までに消えていくストーリーです。</p><p>チェーンは、kNN分数が0.4以上のリンクから貪欲に構築されます。これは、ソースクラスターからサンプリングされたドキュメントの少なくとも40％が単一のターゲットクラスターに到達したことを意味します。最も古いクラスターから開始し、アルゴリズムは常に最も強い発信リンクを追跡します。
</p>    Strong links (kNN fraction &gt;= 0.4): 244
    Story chains spanning 3+ days: 18
      Chain 1: 'ukrainian | kyiv | eastern' (19 days: Feb 3 → Feb 21)
      Chain 2: 'playing | opposition' (19 days: Feb 10 → Feb 28)
      Chain 3: 'tadhg | maro | cadan' (10 days: Feb 1 → Feb 10)
      Chain 4: 'invade | china | putin' (8 days: Feb 21 → Feb 28)
      Chain 5: 'elected | labour | leader' (7 days: Feb 12 → Feb 18)
      Chain 6: 'film | swift | awards' (6 days: Feb 2 → Feb 7)
      Chain 7: 'amendment | termination | reporting' (6 days: Feb 12 → Feb 17)
      Chain 8: 'officers | scene | police' (5 days: Feb 1 → Feb 5)<p>最も長いチェーンは、ウクライナとロシアに関する報道を19日間連続で追跡していますが、2025年2月の地政学的な緊張が持続していることを考えると、これは驚くべきことではありません。2番目に長いチェーンは、19日間にわたるプレミアリーグのサッカーを追跡するものです。より短いチェーンは、映画賞シーズン（6日間）、シックス・ネーションズラグビー（10日間）、英国の政治指導者に関する報道（7日間）を追跡しています。それぞれのチェーンは、アルゴリズムが日々のインデックス全体にわたる埋め込み類似性のみから発見したストーリー展開を表しています。</p><h3>サンキー：ストーリーの流れを可視化する</h3><p>サンキーダイアグラムは、リンクの幅がつながりの強さを表す流れの可視化です。ここでは、各垂直バンドが1日を表し、各ノードは日々のクラスター（ドキュメント数によってサイズが決まる）であり、各色のパスは時間の経過に沿って1つのストーリーチェーンを追跡します。リンク幅はkNNのオーバーラップ強度をエンコードします。リンクが太いほど、サンプリングされたドキュメントの数がより多くターゲットクラスターに到達したことになります。色はチェーンごとに分かれているので、左から右へ流れる1つのカラーパスで1つのストーリーの進行がわかります。</p><p>たとえば、ウクライナとロシアのチェーン（比較的長い経路の一つとして示されている）は、2月初旬から第3週まで途切れることなく続いており、一貫して太い線で結ばれていることから、日々の話題の連続性が強いことがわかります。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blta8c243e0c5773440/6a17093fa6c2b98c86e7968c/100a60a7fb85da8ab3813fd071a82c93f2c3f318-1300x650.png" alt="2025年2月に流れる時間軸に沿ったストーリーチェーン" /><p><em>2025年2月に流れる時間軸に沿ったストーリーチェーン各色のパスは、複数日にわたって続くストーリーを表し、リンクの幅はkNNの重なりの強さを示します。</em></p><h2>このアプローチがもたらすメリット</h2><p>このウォークスルーでは、Elasticsearch上に構築された完全な教師なしドキュメントクラスタリング・パイプラインについて説明しました。</p><ol><li><p><strong>クラスタリング埋め込み</strong>：Jina v5のタスク固有のアダプターは、トピックのグループ化に最適化された埋め込みを生成し、単なるクエリと文書のマッチングだけではありません。</p></li><li><p><strong>グローバルな発見クラスタリング</strong>：1つのインデックスで1か月間をクラスタリングすることで、日をまたいだトピックの発見を最大化します。</p></li><li><p><strong>密度プローブによる重心分類</strong>：5％をサンプリングし、<code>msearch</code> kNNを介して密度をプローブし、多様な高密度シードを選択し、すべての文書を重心に対して分類します。Elasticsearchは負荷の高い計算を処理します。シード選択のみがクライアント側で実行されます（最大0.01秒）。</p></li><li><p><a href="https://www.elastic.co/docs/reference/aggregations/search-aggregations-bucket-significanttext-aggregation"><strong><code>significant_text</code></strong></a><strong>ラベリング</strong>：有意性テストは、MLモデルや手動アノテーションなしで意味のあるクラスタラベルを生成します。有意な項を生み出さないクラスタは非整合となり、ノイズに格下げされます。これは内蔵された品質ゲートです。</p></li><li><p><strong>時間軸に沿ったストーリーのリンク付け</strong>：日次インデックスとサンプルおよびクエリのクロスインデックスkNNは、ストーリーが時間とともにどのように進化するかを追跡します。</p></li></ol><p><strong>重要なポイント</strong></p><ul><li><p>埋め込みタスクの種類が重要です。クラスタリング埋め込みは、測定可能かつより緊密な話題のグループを生成します。</p></li><li><p>Elasticsearchはストレージ層<em>および</em>クラスタリングエンジンの両方として<a href="https://www.elastic.co/docs/solutions/search/vector/knn">kNN検索</a>を通じて機能します。</p></li><li><p>密度探査型重心分類は、ほぼすべての計算をサーバー側で行い、埋め込み空間の密度によって決定される合理的なサイズのクラスターを生成します。</p></li><li><p><code>significant_text</code> 高速で解釈可能で、自動ラベリングと品質管理の両方に効果的です。</p></li></ul><p><strong>このアプローチは、以下の場合に有用です。</strong></p><ul><li><p>タイムスタンプ付きのテキストがあり、ラベル付きトレーニングデータを使用せずにトピックを発見したい場合があります。</p></li><li><p>ストレージ、ベクトル検索、ラベル付け、および時間軸に沿ったリンク付けのために、1つのスタックが必要な場合があります。</p></li></ul><p><strong>検討すべき拡張機能：</strong></p><ul><li><p>複数期間クラスタリング（週間、月間集計）。</p></li><li><p>リアルタイムのインジェストと段階的なクラスター割り当て。</p></li><li><p>LLM生成のクラスタサマリーは、significant_text項をシードとして用います。</p></li><li><p>より大規模なスケールでは、サンプリングされたKMeansの重心が密度ベースのクラスタリングのウォームスタートシードとして機能し、探査フェーズのコストを削減できます。</p></li></ul><h2>はじめましょう</h2><p>タイムスタンプ付きの文書コーパスを差し替えます。日付のあるテキストのコレクションであれば、このパイプラインで利用可能です。完全なノートブックとサポートコードは、<a href="https://github.com/elastic/elasticsearch-labs/tree/main/supporting-blog-content/unsupervised-document-clustering-elasticsearch-jina-embeddings">付属レポジトリ</a>で入手できます。</p><ul><li><p><a href="https://cloud.elastic.co/registration?onboarding_token=vectorsearch&amp;cta=cloud-registration&amp;tech=trial&amp;plcmt=article%20content&amp;pg=search-labs"><strong>Elastic Cloudの無料トライアルを開始</strong></a>：<code>bbq_disk</code>サポート付きのマネージドクラスターを数分でご利用いただけます。</p></li><li><p><a href="https://www.elastic.co/elasticsearch/serverless"><strong>Elasticsearch Serverlessをお試しください</strong></a>：クラスターの管理は不要で、自動的にスケールし、このウォークスルーのすべてをサポートします。</p></li></ul>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/unsupervised-document-clustering-elasticsearch-jina-embeddings</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/unsupervised-document-clustering-elasticsearch-jina-embeddings</guid>
    <category><![CDATA[Vector Database]]></category>
    <category><![CDATA[MLの調査]]></category>
    <category><![CDATA[Jina AI]]></category>
    <dc:creator><![CDATA[Matthew Adams]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt4bd7dd10a7cd6dc8/6a17094a14b270581de3c5b6/662c00694c3e0c2fb2128098bdb6813df9e86a72-1280x720.png" length="0" type="image/png"/>
    <pubDate>Fri, 10 Apr 2026 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[TSDSとILMが出会うとき：遅延データを拒否しない時系列データストリームの設計]]></title>
    <description><![CDATA[TSDSの時間制限はILMフェーズとどのように相互作用するのか、そして遅れて到着するメトリクスを許容するポリシーを設計する方法。]]></description>
    <content:encoded><![CDATA[<p>最近、ある顧客のメトリクス・クラスタを「すべてホットティアに格納する」構成からHot/Cold/Frozenアーキテクチャに移行しました。これまでに何十回も実施してきた変更でした。数分のうちに、Logstashはデータの転送を完全に停止しました。</p><p>Elasticsearchは、遅れて到着するメトリクスを拒否していました。これらの拒否により、パイプラインが遅延し、結果としてより多くの遅延データが発生し、さらに多くの拒否を引き起こしました。最終的にパイプラインは完全に停止しました。</p><p>復旧のためには、スナップショットからの復元、データの再インデックス作成、データ取り込みパイプラインの再設計が必要でした。</p><p>根本的な原因はインデックスライフサイクル管理（ILM）自体ではなく、時系列データストリーム（TSDS）と、それらが時間的制約のあるバッキングインデックスを強制する方法にありました。</p><p>TSDSは指標のストレージ要件を40〜70％削減できますが、TSDSを効率的にするアーキテクチャの変更により、時間の経過とともにインデックスの動作も変わります。これらの変更は、ILMポリシーを設計する時、またはインジェストパイプラインで遅れて到着するデータが生成される可能性がある場合に重要です。</p><h2>TL;DR</h2><p>TSDSを使用する場合：</p><ul><li><p>バックアップインデックスは、特定の時間枠内でのみ文書を受け付けます。</p></li><li><p>遅延データがインデックスがColdまたはFrozen状態に移行した後に取り込まれた場合、Elasticsearchはそれらのドキュメントを拒否するか、設定されている場合は障害ストアにルーティングします。</p></li></ul><p>デザインルール：</p>warm_min_age &gt; rollover_max_age + maximum_expected_lateness<h2>時系列データストリームとは何ですか？</h2><p><em>時系列データストリーム</em>（TSDS）は、メトリクスデータに最適化された特殊なデータストリームです。データは関連するドキュメントが同じシャード内に配置されるようにルーティングされ、クエリと検索のための最適化が行われます。Elasticsearchは以下の方法で行います：</p><p>各文書には以下が含まれます。</p><ul><li><p>1つのタイムスタンプ。</p></li><li><p>時系列を識別するディメンションフィールド。</p></li><li><p>測定値を表すメトリックフィールド。</p></li></ul><p>例：</p><ul><li><p>ホストあたりのCPU使用率。</p></li><li><p>サービスごとのリクエスト遅延。</p></li><li><p>センサーごとの温度測定値。</p></li></ul><p><em>ディメンション</em>は測定したい対象を特定し、<em>メトリクス</em>は時間とともに変化する値を表します。</p><h3>ディメンション</h3><p>ディメンションは測定対象を表します。</p><p>例：</p>host.name
service.name
container.id<p>それらをマッピングで次のように定義します：</p>time_series_dimension: true<h3>メトリクス</h3><p>メトリクスは数値を表し、以下によって定義されます。</p>time_series_metric<p>一般的なメトリクスの種類：</p><ul><li><p>ゲージ：増減する値。</p></li><li><p>カウンター：リセットされるまで増加する値。</p></li></ul><p>Elastic Agentは主にメトリクスおよびログデータを収集するため、TSDSのインデックスを手動で有効にしていなくても、クラスター内に存在している場合があります。</p><h3>_tsidフィールド</h3><p>Elasticsearchは内部的にディメンションフィールドから <code>_tsid</code> 値を生成します。これにより、同一のディメンションを持つドキュメントを同じシャードにルーティングすることができ、以下が改善されます：</p><ul><li><p>圧縮。</p></li><li><p>クエリのローカル環境。</p></li><li><p>アグリゲーションのパフォーマンス。</p></li></ul><h2>主な違い：時間制限付きバッキングインデックス</h2><p>従来のデータストリームは常に最新のバッキングインデックス（<em>書き込みインデックス</em>と呼ばれる）に書き込みますが、TSDSは異なる動作をします。</p><p>各TSDSバッキングインデックスには定義された時間ウィンドウがあり、そのウィンドウ内に収まる<code>@timestamp</code>値を持つドキュメントのみを受け入れます：</p>GET _data_stream/my-metrics-data-stream


     "index_mode": "time_series",
     "time_series": {
       "temporal_ranges": [
         {
           "start": "2026-01-15T14:35:50.000Z",
           "end": "2026-03-16T11:34:40.000Z"
         }
       ]
     }<p>ドキュメントがインデックス化されると、Elasticsearchはそれをタイムスタンプに関わるバッキングインデックスにルーティングします。これは、従来のインデックスとは異なり、TSDSが複数のバッキングインデックスに同時に書き込む可能性があることを意味します。</p><p>例：</p><ul><li><p>リアルタイムデータ → 最新のインデックス。</p></li><li><p>遅延データ → その期間をカバーする以前のインデックス。</p></li></ul><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt7b001853af30d5f8/6a17dc2cfaa9137a7d93c751/31af2bb3b3dc24db8342e791e1db77a44659ba7a-1589x502.png" alt="遅延したドキュメントが古いインデックスにルーティングされ、最新のドキュメントが最新のインデックスにルーティングされる様子を示すタイムライン。" /><h2>遅延データに対応する設計方法</h2><p>実際のインジェストパイプラインは、ほとんどの場合、完全に期限内に指標を届けるわけではありません。メトリクスは、ネットワークの停止、途中のバックログ、バッチのインジェスト、およびエッジデバイスの損失（これらのデバイスは再接続し、キャッチアップを始めます）によって遅延する可能性があります。</p><p>従来のインデックスは、静かにその遅延を吸収しますが、TSDSはしません。</p><p>ドキュメントのタイムスタンプが書き込み可能なバッキングインデックスの範囲外にある場合、Elasticsearchはそれを拒否します。これは、ILMポリシーが遅延データを考慮する必要があることを意味します。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt6e8eae1b2ddad142/6a17dc2e1d1b8335a793e35c/32a103b95b20e31615c214271e27811a7ee315ae-1999x691.png" alt="インデックスライフサイクルのタイムライン" /><h2>重要な制約</h2><p>バッキングインデックスは遅延データを受け入れられるだけの期間、書き込み可能な状態を維持する必要があります。</p><p>実際には、これは次のことを意味します。</p>time_until_readonly &gt; maximum_expected_lateness<p>ILMはロールオーバーからの時間経過を測定するため、運用ルールは次のようになります。</p>warm_or_cold_min_age &gt; rollover_max_age + maximum_expected_lateness<p></p><p>たとえば、メトリクスが最大6時間遅延する可能性がある場合、インデックスはロールオーバー後少なくとも6時間は書き込み可能な状態を維持する必要があります。</p><p></p><p>この制約を考慮しなかったことが、前述のインジェスト障害の正確な原因でした。遅れて到着したデータは、既にColdティアにあり、そのため書き込みがブロックされていた以前のインデックスに向けられていました。</p><p></p><h2>拒否されたドキュメントの処理</h2><p>TSDS がドキュメントを拒否すると、Elasticsearch は、タイムスタンプが書き込み可能なインデックスの範囲内にないことを示すエラーを返します。インジェストパイプラインがそのエラーをどのように処理するかによって、データを失うか、インジェストが停止するかが決まります。</p><p>拒否されたドキュメントを処理する主要なメカニズムは、障害ストアです。</p><h3>障害ストア（Elasticsearch 9.1以降で推奨）</h3><p>Elasticsearch 9.1では、拒否されたドキュメントを自動的に格納するfailure storeが導入されました。Elasticsearchは、クライアントにエラーを返す代わりに、失敗したドキュメントをデータストリーム内の専用の障害インデックスに書き込みます。</p><p>障害の調査には以下の方法があります。</p>GET metrics-myapp::failures/_search<p>障害ストアを使用することで、拒否エラーによるインジェストパイプラインの停止を防ぎつつ、障害データを分析または<a href="https://www.elastic.co/docs/manage-data/data-store/data-streams/reindex-tsds">再インデックス</a>のために保存します。</p><h2>拒否される問題の監視</h2><p>遅れて到着する問題は通常、最初にインジェスト異常として現れます。最初は、以下によって気付く場合があります：</p><ul><li><p>インデキシングレートの突然の低下。</p></li><li><p>拒否されたドキュメントの急増。</p></li><li><p>障害ストアエントリ数の増加。</p></li><li><p>パイプラインの入力数と出力数の不一致。</p></li></ul><p>これらの兆候に基づいて警告を発することで、オペレーターはパイプラインが停止する前に問題を検知することができます。ワークフロー、機械学習ジョブ、およびその他のメカニズムを使用して、検出と通知を自動化できます。</p><h2>TSDS + ILMの移行チェックリスト</h2><p>メトリクスクラスターをTSDSに移行する場合、ILM階層化を導入する場合、またはメトリクスがデフォルトでTSDSであるElasticsearchバージョンにアップグレードする場合は、まずこれらの項目を確認してください。</p><h3><strong>1. インジェストレイテンシの測定</strong></h3><p>ILMの方針を変更する前に、以下を決定してください。</p><ul><li><p>通常のインジェスト遅延。</p></li><li><p>最悪の場合、インシデント時の遅延。</p></li><li><p>バッチパイプラインによる遅延。</p></li></ul><p>ILM設計は、現実的な最大限の遅延に対応する必要があります。</p><h3><strong>2. インデックス付けの時間ウィンドウを検証する</strong></h3><p>TSDSのバッキングインデックスを調べます。</p>GET _data_stream/&lt;your-stream&gt;<p>以下について探します：</p><ul><li><p><code>time_series.start_time</code></p></li><li><p><code>time_series.end_time</code></p></li></ul><p>これらの制限が、どのインデックスがドキュメントを受け入れられるかを決定します。これらのウィンドウを理解することで、データがどのくらい遅れていても拒否されないのかを判断できます。</p><h3><strong>3. 遅延データを考慮してHotティアのサイズを調整する</strong></h3><p>遅延データに対しても、バッキングインデックスが書き込める状態を保ちます。</p><p>運用ルール：</p><ul><li><p><code>warm_min_age &gt; rollover_max_age + maximum_expected_lateness</code></p></li></ul><p>メトリクスが6時間遅れて届く場合、インデックスは少なくとも6時間は書き込める状態を維持する必要があることに留意してください。</p><h3><strong>4. 拒否されたドキュメントの処理方法を決定する</strong></h3><p>TSDSを有効にする前に以下のストラテジーを選択します：</p><ul><li><p>障害ストア（Elasticsearch 9.1以降で推奨）。</p></li><li><p>Logstashのデッドレターキュー。</p></li><li><p>到着が遅れた場合のフォールバックインデックス。</p></li><li><p>限定的なデータ損失を受け入れる。</p></li></ul><h3><strong>5. インジェストの正常性を監視する</strong></h3><p>以下のアラートを追加します：</p><ul><li><p>インデキシングの速度低下。</p></li><li><p>却下された書類。</p></li><li><p>ストアの拡大失敗。</p></li><li><p>パイプラインの入力と出力の不一致。</p></li></ul><p>遅延データの問題は、多くの場合、インジェスト時の異常として最初に現れます。</p><h2>まとめ</h2><p>時系列データストリームは、メトリックワークロードに対して主要なストレージとパフォーマンスの改善を提供しますが、重要なアーキテクチャ上の変更も導入します。バッキングインデックスには時間的な制約があり、これがILMの動作に影響します。</p><p>TSDSを使用する場合：</p><ul><li><p>インデックスは、遅延データを受け入れられるだけの期間、書き込み可能な状態を維持する必要があります。</p></li><li><p>インジェストパイプラインは、拒否されたドキュメントを安全に処理する必要があります。</p></li></ul><p>留意する重要なルールは次の通りです：</p>warm_min_age &gt; rollover_max_age + maximum_expected_lateness<p>その制約に基づいてILMポリシーを設計すると、TSDSはメトリクスのワークロードに非常に適しています。</p><p>無視した場合、インジェストパイプラインがその時間的制約によって機能不全に陥る可能性があります。</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/tsds-ilm-elasticsearch</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/tsds-ilm-elasticsearch</guid>
    <category><![CDATA[データのインデキシング]]></category>
    <category><![CDATA[Vector Database]]></category>
    <dc:creator><![CDATA[Bret Wortman]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltcacf154aeacb29d8/6a17dc30dbb4ffc7ddfb557f/e4c46e4a6f746d9c845857e80de036f5d51cd4e7-1280x720.png" length="0" type="image/png"/>
    <pubDate>Thu, 02 Apr 2026 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[LINQ to Elasticsearch ES|QL：C#を記述してElasticsearchをクエリ]]></title>
    <description><![CDATA[Elasticsearch .NETクライアントに新しく追加されたLINQ to Elasticsearch ES|QLプロバイダをご紹介します。C#コードを自動的にES|QLクエリに変換できます。]]></description>
    <content:encoded><![CDATA[<p><strong>v9.3.4</strong>および<strong>v8.19.18</strong>以降のElasticsearch .NETクライアントには、実行時にC# LINQ式を<a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/esql.html">Elasticsearchクエリ言語（ES|QL）クエリに変換する</a><a href="https://learn.microsoft.com/en-us/dotnet/csharp/linq/">Language Integrated</a> Query（LINQ）プロバイダーが含まれています。ES|QL文字列を手作業で記述する代わりに、 <code>Where</code>、 <code>Select</code>、 <code>OrderBy</code>、 <code>GroupBy</code>などの標準演算子を使用してクエリを構成します。このプロバイダーは、結果セットのサイズに関係なくメモリ使用量を一定に保つ行ごとのストリーミングを含め、変換、パラメータ化、結果の逆シリアル化を処理します。</p><h2>最初のクエリ</h2><p>まず、Elasticsearchインデックスにマップする普通のCLRオブジェクト（POCO）を定義します。プロパティ名は、標準的な<code>System.Text.Json</code>属性（<code>[JsonPropertyName]</code>など）または設定された<code>JsonNamingPolicy</code>を通じてES|QL列名に解決されます。クライアントの他の部分に適用される<a href="https://www.elastic.co/docs/reference/elasticsearch/clients/dotnet/source-serialization">ソースシリアル化</a>ルールは、ここでも同様に適用されます。</p>using System.Text.Json.Serialization;

public class Product
{
    [JsonPropertyName("product_id")]
    public string Id { get; set; }

    public string Name { get; set; }

    public string Brand { get; set; }

    [JsonPropertyName("price_usd")]
    public double Price { get; set; }

    [JsonPropertyName("in_stock")]
    public bool InStock { get; set; }
}<p>型を指定すると、クエリは次のようになります。</p>var minPrice = 100.0;
var brand = "TechCorp";

await foreach (var product in client.Esql.QueryAsync&lt;Product&gt;(q =&gt; q
    .From("products")
    .Where(p =&gt; p.InStock &amp;&amp; p.Price &gt;= minPrice &amp;&amp; p.Brand == brand)
    .OrderByDescending(p =&gt; p.Price)
    .Take(10)))
{
    Console.WriteLine($"{product.Name}: ${product.Price}");
}<p>プロバイダーはこれを次のES|QLに変換します。</p><p>いくつか注意すべき点があります。</p><ul><li><p><strong>プロパティ名の解決：</strong> <code>p.Price</code>は<code>[JsonPropertyName]</code> 属性のため<code>price_usd</code>になり、<code>p.Brand</code>はデフォルトのcamelCase命名規則に従って <code>brand</code>になります。</p></li><li><p><strong>パラメーターのキャプチャ：</strong>C#変数 <code>minPrice</code>と<code>brand</code>は、名前付きパラメーター（<code>?minPrice</code>、<code>?brand</code>）としてキャプチャされます。これらはJSONペイロード内のクエリ文字列とは別に送信されるため、インジェクション攻撃を防ぎ、サーバー側のクエリプランのキャッシュを可能にします。</p></li><li><p><strong>ストリーミング：</strong><code>QueryAsync&lt;T&gt;</code>は<code>IAsyncEnumerable&lt;T&gt;</code>を返します。Elasticsearchからデータが到着すると、行は1つずつマテリアライズされます。</p></li></ul><p>また、実行せずに生成されたクエリとそのパラメーターを検査することもできます。</p>var query = client.Esql.CreateQuery&lt;Product&gt;()
    .Where(p =&gt; p.InStock &amp;&amp; p.Price &gt;= minPrice &amp;&amp; p.Brand == brand)
    .OrderByDescending(p =&gt; p.Price)
    .Take(10);

Console.WriteLine(query.ToEsqlString());
// FROM products | WHERE (in_stock == true AND price_usd &gt;= 100) | SORT price_usd DESC | LIMIT 10

Console.WriteLine(query.ToEsqlString(inlineParameters: false));
// FROM products | WHERE (in_stock == true AND price_usd &gt;= ?minPrice AND brand == ?brand) | SORT price_usd DESC | LIMIT 10

var parameters = query.GetParameters();
// { "minPrice": 100.0, "brand": "TechCorp" }<h2>これはどのように機能するのでしょうか？LINQ の簡単なおさらい</h2><p>LINQプロバイダーを可能にするメカニズムは、<code>IEnumerable&lt;T&gt;</code>と<code>IQueryable&lt;T&gt;</code>の区別にあります。</p><p><code>.Where(p =&gt; p.Price &gt; 100)</code> を <code>IEnumerable&lt;T&gt;</code> 上で呼び出すと、ラムダは <code>Func&lt;Product, bool&gt;</code> にコンパイルされます。これは、ランタイムがインプロセスで実行する通常のデリゲートです。これはLINQ-to-Objectsです。</p><p>同じメソッドを <code>IQueryable&lt;T&gt;</code> で呼び出すと、C#コンパイラはラムダを <code>Expression&lt;Func&lt;Product, bool&gt;&gt;</code> でラップします。これは実行可能な形式ではなく、コードの<em>構造</em>を表すデータ構造です。式ツリーは実行時に検査、分析、および別の言語への変換を行うことができます。</p>// IEnumerable: the lambda is a compiled delegate
IEnumerable&lt;Product&gt; local = products.Where(p =&gt; p.Price &gt; 100);

// IQueryable: the lambda is an expression tree, a data structure
IQueryable&lt;Product&gt; remote = queryable.Where(p =&gt; p.Price &gt; 100);<p><code>IQueryProvider</code>インターフェースは拡張ポイントです。どのプロバイダーでも、これらの式ツリーをターゲット言語に変換するために <code>CreateQuery&lt;T&gt;</code> と <code>Execute&lt;T&gt;</code> を実装できます。Entity FrameworkはSQLを発行するためにこれを使用します。LINQからES|QLへのプロバイダーはこれをES|QLの生成に使用します。</p><p>上記のクエリの式ツリーは次のようになります。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt521838e8b9c36649/6a1705b1839dfa5f40dcfdfe/f864cd18a390831f8d28503a29b5835efb1842f7-1000x720.png" alt="例のクエリに対する式ツリー。" /><p><em>例のクエリに対する式ツリー。</em></p><p>ツリーは内側から外側にネストされています。<code>Take</code>が <code>OrderByDescending</code>をラップし、これが<code>Where</code>をラップし、これが<code>From</code>, をルート定数<code>EsqlQueryable&lt;Product&gt;</code> をラップします。<code>Where</code>述語自体が<code>BinaryExpression</code>ノードのサブツリーであり、<code>&amp;&amp;</code>、<code>&gt;=</code>、および<code>==</code>演算子に対して<code>MemberExpression</code>リーフがプロパティアクセス用、<code>minPrice</code>および<code>brand</code>変数用のクロージャキャプチャ用に存在します。これは、プロバイダーが最終的なES|QLを生成するために使用するデータ構造です。</p><h2>内部構造：変換パイプライン</h2><p>LINQ式からクエリ結果までの経路は、6段階のパイプラインをたどります。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt930670a505dd61ea/6a1705b3b339d58a54769ecf/2a2c772b63d720f61fc9a28b2f85668fa2db8d38-1999x1036.png" alt="データ変換パイプラインの概要。" /><p><em>データ変換パイプラインの概要。</em></p><h3>1. 式ツリーのキャプチャ</h3><p><code>.Where()</code>、<code>.OrderBy()</code>、<code>.Take()</code>などの演算子を<code>IQueryable&lt;T&gt;</code> に連鎖させると、標準のLINQインフラストラクチャーが式ツリーを構築します。<code>EsqlQueryable&lt;T&gt;</code> は<code>IQueryable&lt;T&gt;</code> を実装し、<code>EsqlQueryProvider</code> に委譲します。</p><h3>2. 変換</h3><p>クエリが実行されると（列挙、 <code>ToList()</code>の呼び出し、または<code>await foreach)</code>使用によって）、 <code>EsqlExpressionVisitor</code>は式ツリーを内側から外側へと走査します。各LINQメソッド呼び出しを専門のビジターに送信します。</p><p>ビジター</p><p>翻訳します</p><p>対象</p><p>WhereClauseVisitor</p><p>.Where(predicate)</p><p>WHERE 条件</p><p>SelectProjectionVisitor</p><p>.Select(selector)</p><p>EVAL + KEEP + RENAME</p><p>訪問者別にグループ化</p><p>.GroupBy().Select()</p><p>STATS ... BY</p><p>OrderByVisitor</p><p>.OrderBy() / .ThenBy()</p><p>SORTフィールド [ASC\|DESC]</p><p>EsqlFunctionTranslator</p><p>EsqlFunctions.*、Math.*、文字列メソッド</p><p>80+ ES|QL関数</p><p>翻訳中、式で参照されるC#変数は名前付きパラメーターとしてキャプチャされます。</p><h3>3. クエリモデル</h3><p>ビジターは直接文字列を生成しません。代わりに、<code>QueryCommand</code>オブジェクト、すなわち不変の中間表現を生成します。<code>FromCommand</code>、<code>WhereCommand</code>、<code>SortCommand</code>、および<code>LimitCommand</code>の各々が、1つのES|QL処理コマンドを表しています。これらは<code>EsqlQuery</code>モデルに集められます。</p><p></p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt788c9936976f2f62/6a1705b50e2e4910da419ff0/2adc349b6cf655b96b7b3e826a134e8a17fe42fd-1999x1036.png" alt="クエリモデルとコマンドパターン。" /><p><em>クエリモデルとコマンドパターン。</em></p><p>この中間モデルは、式ツリーと出力形式の両方から切り離されています。フォーマット前に検査、傍受（<code>IEsqlQueryInterceptor</code>経由）、または修正が可能です。</p><h3>4. フォーマット</h3><p><code>EsqlFormatter</code> 各<code>QueryCommand</code>を順番に訪問し、最終的なES|QL文字列を生成します。各コマンドは1行になり、ES|QLが処理コマンドを連鎖させるために使用するパイプ (|) 演算子で区切られます。特殊文字を含む識別子は自動的にバッククォートでエスケープされます。</p><h3>5. 実行</h3><p>フォーマットされたES|QL文字列とキャプチャされたパラメーターは、JSONペイロードとしてElasticsearchの<code>/_query</code>エンドポイントに送信されます。<code>IEsqlQueryExecutor</code>インターフェースはトランスポートレイヤーを抽象化し、ここで階層型パッケージアーキテクチャが登場します。</p><h3>6. マテリアライズ</h3><p><code>EsqlResponseReader</code> JSON応答をストリーム化し、結果セット全体をバッファリングせずに処理します。<code>ColumnLayout</code>ツリーは、1クエリにつき1回事前に計算され、フラットなES|QL列名（<code>address.street</code>、<code>address.city</code>など）をネストされたPOCOプロパティにマップします。各行は<code>T</code>インスタンスに組み立てられ、 <code>IEnumerable&lt;T&gt;</code> または <code>IAsyncEnumerable&lt;T&gt;</code>によって1行ずつ生成されます。</p><h2>レイヤーアーキテクチャ</h2><p>LINQ to ES|QL機能は、以下の3つのパッケージに分かれています。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt662bd0dd8861b6b6/6a1705b7a929cf7086ae08a2/41b8aae860ecdc2480edcb1c1d4cc9b03cfb78c9-1999x1036.png" alt="パッケージアーキテクチャー。" /><p><em>パッケージアーキテクチャー。</em><a href="https://www.nuget.org/packages/Elastic.Esql"><strong><code>Elastic.Esql</code></strong></a> は純粋な変換エンジンです。HTTPへの依存関係は一切なく、式ビジター、クエリモデル、フォーマッター、レスポンスリーダーが含まれています。スタンドアロンで使用すると、Elasticsearch接続がなくてもES|QLクエリを構築および検査できます。これは、テスト、クエリロギング、または独自の実行レイヤーの構築に役立ちます。</p>// Translation-only: no Elasticsearch connection needed
var provider = new EsqlQueryProvider();
var query = new EsqlQueryable&lt;Product&gt;(provider)
    .From("products")
    .Where(p =&gt; p.InStock)
    .OrderByDescending(p =&gt; p.Price);

Console.WriteLine(query.ToEsqlString());
// FROM products | WHERE in_stock == true | SORT price_usd DESC<p><a href="https://www.nuget.org/packages/Elastic.Clients.Esql"><strong><code>Elastic.Clients.Esql</code></strong></a> は軽量なスタンドアロンのES|QLクライアントです。<code>Elastic.Transport</code>を経由して<code>Elastic.Esql</code>上にHTTP実行を追加します。もしアプリケーションが他のElasticsearch APIではなく、ES|QLのみを必要とする場合、これが最小限の依存関係オプションです。</p><p><a href="https://www.nuget.org/packages/Elastic.Clients.Elasticsearch"><strong><code>Elastic.Clients.Elasticsearch</code></strong></a> は完全なElasticsearch .NETクライアントです。また、<code>Elastic.Esql</code> を基盤とし、<code>client.Esql</code>名前空間を通じてLINQプロバイダーを公開します。これはほとんどのアプリケーションで推奨されるエントリーポイントです。</p><p>どちらの実行層パッケージも、変換と転送をつなぐ戦略インターフェースである<code>IEsqlQueryExecutor</code>の独自の実装を提供します。</p><p>これら3つのパッケージはすべて、ソース生成の<code>JsonSerializerContext</code>と併用する場合、ネイティブAOTと互換性があります。完全なクライアントについては、<a href="https://www.elastic.co/docs/reference/elasticsearch/clients/dotnet/source-serialization#native-aot">Native AOTのドキュメント</a>をご覧ください。</p><h2>基本を超えて</h2><p>上記の例では、フィルタリング、ソート、ページネーションについて説明しています。このプロバイダーはより幅広い操作をサポートしています。</p><h3>アグリゲーション</h3><p><code>GroupBy</code><code>Select</code>の集約関数と組み合わせるとES|QL <a href="https://www.elastic.co/docs/reference/query-languages/esql/commands/stats-by"><code>STATS ... BY</code></a>に変換されます。</p>var stats = client.Esql.Query&lt;Product, object&gt;(q =&gt; q
    .GroupBy(p =&gt; p.Brand)
    .Select(g =&gt; new
    {
        Brand = g.Key,
        Count = g.Count(),
        AvgPrice = g.Average(p =&gt; p.Price),
        MaxPrice = g.Max(p =&gt; p.Price)
    }));

// -&gt; FROM products | STATS COUNT(*), AVG(price_usd), MAX(price_usd) BY brand<h3>予測</h3><p><code>Select</code>匿名型を持つと、 <a href="https://www.elastic.co/docs/reference/query-languages/esql/commands/eval"><code>EVAL</code></a>、 <a href="https://www.elastic.co/docs/reference/query-languages/esql/commands/keep"><code>KEEP</code></a>、 <a href="https://www.elastic.co/docs/reference/query-languages/esql/commands/rename"><code>RENAME</code></a> コマンドが生成されます。</p>var query = client.Esql.CreateQuery&lt;Product&gt;()
    .Select(p =&gt; new { ProductName = p.Name, p.Price, p.InStock });

// -&gt; FROM products | KEEP name, price_usd, in_stock | RENAME name AS ProductName<h3>豊富な関数ライブラリ</h3><p>80以上のES|QL関数が <code>EsqlFunctions</code>クラスを通じて利用可能で、日付/時間、文字列、数学、IP、パターンマッチング、スコアリングをカバーしています。標準的な<code>Math.*</code>および<code>string.*</code>メソッドも変換されています。</p>.Where(p =&gt; p.Name.Contains("Pro"))       // -&gt; WHERE name LIKE "*Pro*"
.Where(p =&gt; EsqlFunctions.CidrMatch(      // -&gt; WHERE CIDR_MATCH(ip, "10.0.0.0/8")
    p.IpAddress, "10.0.0.0/8"))<h3>ルックアップ結合</h3><p>クロスインデックス検索はES|QL <a href="https://www.elastic.co/docs/reference/query-languages/esql/commands/lookup-join"><code>LOOKUP JOIN</code></a>に変換されます。</p>var enriched = client.Esql.Query&lt;Product, object&gt;(q =&gt; q
    .LookupJoin&lt;Product, CategoryLookup, string, object&gt;(
        "category-lookup-index",
        product =&gt; product.Id,
        category =&gt; category.CategoryId,
        (product, category) =&gt; new { product.Name, category!.CategoryLabel }));<h3>未加工のES|QLエスケープハッチ</h3><p>LINQプロバイダーでまだサポートされていないES|QL機能については、生のフラグメントを追加できます。</p>var results = client.Esql.Query&lt;Product&gt;(q =&gt; q
    .Where(p =&gt; p.InStock)
    .RawEsql("| EVAL discounted = price_usd * 0.9"));<h3>サーバー側の非同期クエリ</h3><p>実行時間の長いクエリについては、サーバー上でバックグラウンド処理を行うように設定します。</p>await using var asyncQuery = await client.Esql.SubmitAsyncQueryAsync&lt;Product&gt;(
    q =&gt; q.Where(p =&gt; p.InStock),
    asyncQueryOptions: new EsqlAsyncQueryOptions
    {
        WaitForCompletionTimeout = TimeSpan.FromSeconds(5),
        KeepAlive = TimeSpan.FromMinutes(10)
    });

await asyncQuery.WaitForCompletionAsync();
await foreach (var product in asyncQuery.AsAsyncEnumerable())
    Console.WriteLine(product.Name);<p>サーバー側の非同期クエリは、通常のタイムアウトしきい値を超える可能性のある長時間実行される分析クエリや大規模データセットの処理、あるいはロードバランサー、APIゲートウェイ、プロキシなど、厳格なHTTPタイムアウトを強制するタイムアウトに敏感な環境で特に役立ちます。非同期クエリは、結果の取得から提出を切り離すことで接続切断を回避します。</p><h2>はじめに</h2><p>LINQ to ES|QLは次のバージョンから利用可能です。</p><ul><li><p><strong>Elastic.Clients.Elasticsearch v9.3.4</strong> (9.x ブランチ)</p></li><li><p><strong>Elastic.Clients.Elasticsearch v8.19.18</strong>（8.xブランチ）</p></li></ul><p>NuGetからのインストール：</p><p><code>dotnet add package Elastic.Clients.Elasticsearch</code></p><p>エントリーポイントは<code>client.Esql</code>にあります。</p><p>メソッド</p><p>戻り値</p><p>ユースケース</p><p>Query&lt;T&gt;(...)</p><p>IEnumerable&lt;T&gt;</p><p>同期実行</p><p>QueryAsync&lt;T&gt;(...)</p><p>IAsyncEnumerable&lt;T&gt;</p><p>非同期ストリーミング</p><p>CreateQuery&lt;T&gt;()</p><p>IEsqlQueryable&lt;T&gt;</p><p>高度な構成と検査</p><p>SubmitAsyncQueryAsync&lt;T&gt;(...)</p><p>EsqlAsyncQuery&lt;T&gt;</p><p>長時間実行されるサーバー側クエリ</p><p>クエリオプション、複数フィールドへのアクセス、ネストされたオブジェクト、複数値フィールドの処理など、機能の詳細については<a href="https://www.elastic.co/docs/reference/elasticsearch/clients/dotnet/linq-to-esql">LINQ to ES|QLのドキュメントを</a>参照してください。</p><h2>まとめ</h2><p>LINQ to ES|QLは、C# LINQの完全な表現力をElasticsearchのES|QLクエリ言語にもたらし、クエリ文字列を手作業で作成することなく、厳密に型付けされた構成可能なクエリを書くことができます。自動パラメーターキャプチャ、ストリーミングマテリアライゼーション、スタンドアロン変換から完全なElasticsearchクライアントまで拡張できる階層型パッケージアーキテクチャーにより、あらゆる規模の.NETアプリケーションに自然に適合します。最新のクライアントをインストールし、LINQ式をインデックスに向け、残りはプロバイダーに任せましょう。</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/linq-esql-c-elasticsearch-net-client</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/linq-esql-c-elasticsearch-net-client</guid>
    <category><![CDATA[ES|QL]]></category>
    <category><![CDATA[Vector Database]]></category>
    <dc:creator><![CDATA[Florian Bernd,Martijn Laarman]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltdfa35fbcbbf4959f/6a1705b9dc55de19a4e00d07/e54132e915217063e9ed0ec45059c6cfc38e31dd-1280x720.png" length="0" type="image/png"/>
    <pubDate>Wed, 01 Apr 2026 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[高速性と精度の対比：量子化ベクトル探索の再現率の測定]]></title>
    <description><![CDATA[Elasticsearchのベクトル検索における再現率を、最小限の設定で測定する方法について説明します。]]></description>
    <content:encoded><![CDATA[<p>誰もがベクトル検索が瞬時に行われることを望んでいますが、高次元ベクトルではデータ量が膨大になります。1,024次元のfloat-32ベクトルは1つのメモリを大量に消費し、他の何百万ものベクトルと比較すると計算量が多くなります。</p><p>これを解決するために、Elasticsearchのような検索エンジンは主に2つの最適化戦略を使用します。</p><ol><li><p><strong>近似検索（Hierarchical Navigable Small World [HNSW]）：</strong>すべての文書をスキャンする代わりに、ナビゲーショングラフを構築して、回答の可能性が高い近傍に素早くジャンプします。</p></li><li><p><strong>量子化：</strong>メモリ使用量を削減し、計算速度を向上させるために、ベクトルを圧縮します（例えば、32ビット浮動小数点数から8ビット整数、あるいは1ビットのバイナリ値へ）。</p></li></ol><p>しかし、最適化にはしばしば<strong>精度</strong>という代償が伴います。</p><p>「データを圧縮し、検索中にショートカットを取ると、最高の結果を見逃すのではないか？」「この最適化は検索エンジンの関連性を低下させるのではないか？」といった恐れは正当です。</p><p>Elasticの量子化が結果を低下させないことを証明するために、<a href="https://huggingface.co/datasets/fancyzhx/dbpedia_14"><strong>DBpedia-14</strong></a><a href="https://huggingface.co/datasets/fancyzhx/dbpedia_14"> </a>データセットを使用して再現可能なテストハーネスを構築し、Elasticsearchのデフォルトの最適化を使用する際に、速度と引き換えにどれだけの精度（具体的には再現率）を犠牲にしているかを正確に計算しました。</p><p>要約すると、それはおそらく想定よりもずっと少ないでしょう。<a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/fast_vs_accurate_measuring_the_recall_of_quantized_vector_search/vector_recall_notebook.ipynb">こちらのノートブック</a>をチェックして、ぜひご自身でお試しください。</p><h2><strong>定義（非専門家向け）</strong></h2><p>コードを見る前に、いくつかの用語について確認しておきましょう。</p><ul><li><p><strong>関連性対再現率：</strong><strong>関連性</strong> は主観的で（良いものが見つかったか？）、<strong>再現率</strong>は数学的なものです。データベース内にクエリと<em>完全</em>に一致する文書が10件あり、検索エンジンがそのうち9件を見つけた場合、再現率は90%（または0.9）です。</p></li><li><p><strong>完全一致検索（フラット）：</strong>総当たり法とも呼ばれ、検索エンジンはインデックス内のすべての文書をスキャンし、距離を計算します。</p><ul><li><p><em>長所：</em>100%完璧な再現率。</p></li><li><p><em>短所：</em>計算コストが高く、スケール時の処理速度が遅くなる。</p></li></ul></li><li><p><strong>近似探索（HNSW）：</strong>いわゆる「ショートカット」の方法。検索エンジンは <a href="https://www.elastic.co/search-labs/blog/hnsw-graph">HNSW</a> グラフを作成し、グラフを巡回して最も近い隣接点を探します。</p><ul><li><p><em>長所：</em>非常に高速で拡張性が高い。</p></li><li><p><em>短所：</em>グラフの探索が早すぎて停止すると、近傍を見逃す可能性がある。</p></li></ul></li></ul><h2><strong>実験：完全一致と近似探索の比較</strong></h2><p>再現率をテストするために、テキスト分類モデルのトレーニングと評価によく使用される、14のオントロジークラスにわたるタイトルと要約の大規模なデータセット<strong>DBPedia-14</strong>データセットを使用しました。具体的には、「Film」カテゴリに焦点を当てます。最適化された生産設定を、数学的に完璧な真値と比較したいと考えました。</p><p>この実験では、テキスト表現の業界ベンチマークをリードする最先端の多言語モデル<a href="https://www.elastic.co/search-labs/blog/jina-embeddings-v5-text">jina-embeddings-v5-text-small</a>モデルを使用しています。このモデルを選んだ理由は、高性能埋め込みの現在の標準となっているためです。Jina v5の優れた精度とElasticsearchのネイティブ量子化を組み合わせることで、計算効率が高く、検索品質にも妥協のない検索アーキテクチャを実証できます。</p><p>二重マッピングを使用したインデックスを設定し、同じテキストを同時に2つの異なるフィールドに取り込みました。</p><ol><li><p><strong><code>content.raw</code></strong>（タイプ: <code>flat</code>）。これにより、ElasticsearchはFloat32ベクトル全体の総当たりスキャンを実行することになります。これにより完全一致の結果が返され、ベースラインとして使用されます。</p></li><li><p><strong><code>content</code></strong>（タイプ： <code>semantic_text</code>）。デフォルトではHNSW + Better Binary Quantization（BBQ）を使用しています。これは、近似一致のための標準的かつ最適化された生産設定です。</p></li></ol><h3><strong>Recall@10 テスト</strong></h3><p>指標としては、Recall@10を使用しました。</p><p>50本のランダムな映画を選び、両方のフィールドに同じクエリを実行しました。</p><ul><li><p><strong>完全一致（フラット）</strong>検索で上位10個の近傍が ID [1, 2, 3... 10] であると示されている場合。</p></li><li><p>また、<strong>近似（HNSW）</strong>検索では、ID [1, 2, 3... 9, 99] が返されます。</p></li><li><p>上位10位のうち、9位を正しく特定できました。スコアは<strong>0.9</strong>です。</p></li></ul><p>こちらが使用したマッピングです。</p># The "Control Group": Forces exact brute-force scan
"raw": {
    "type": "semantic_text",
    "inference_id": ".jina-embeddings-v5-text-small",
    "index_options": {
        "dense_vector": {
            "type": "flat"
        }
    }
}<p><strong>結果：成功の「横ばい線」</strong></p><p>スケールテストを実行し、完全なデータセットを再読み込みし、1,000～40,000件の文書のインデックスサイズに対してテストしました。</p><p>再現率スコアに何が起こったかは以下のとおりです。</p><p>ドキュメント</p><p>Recall@10スコア</p><p>1,000</p><p>1.000 (100%)</p><p>5,000</p><p>0.998 (100%)</p><p>10,000</p><p>0.992 (99.4%)</p><p>20,000</p><p>0.999 (99.0％)</p><p>40,000</p><p>0.992 (98.8%)</p><p>結果は驚くほど安定していました。スケールアップしても、近似検索は総当たりの完全一致検索と<strong>99％超の確率</strong>で一致しました。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt8168a0a4946bade7/6a170e154a531b61b536a9eb/a4bfacb1d0cce6fdf6df0e1a9d4fc5d4007a66da-1999x1209.png" alt="ベクトル検索の安定性：再現率とインデックスサイズ" /><h2><strong>なぜこれほど上手くいったでしょう？</strong></h2><p>ベクトルをバイナリ値に圧縮すると、これよりも精度が低下すると考えられるかもしれません。その理由は、Elasticsearchが検索を処理する方法にあります。</p><p>今日のほとんどの埋め込みモデルは、大きなFloat32ベクトルを出力します。探索を効率的にするために、Elasticsearchは高次元ベクトルに対して量子化を使用します。具体的には、バージョン9.2以降、デフォルトで<a href="https://www.elastic.co/search-labs/blog/elasticsearch-9-1-bbq-acorn-vector-search">BBQ</a>を使用するようになりました。</p><p>BBQは<strong>再スコアリング</strong>メカニズムを採用しています。</p><ol><li><p><strong>トラバーサル：</strong>検索エンジンは圧縮された（量子化された）ベクトルを使ってHNSWグラフを高速に走査します。ベクトルが小さいため、効率的にオーバーサンプリングを行い、パフォーマンスを損なうことなく、より多くの候補リスト（例えば、類似性の高い上位100件の文書）を収集できます。</p></li><li><p><strong>再スコアリング：</strong>候補が見つかったら、その数件の文書について完全精度の値を取得して、最終的な正確なランキングを計算します。</p></li></ol><p>これにより、量子化による高速な処理と、最終的なソートにおける浮動小数点数の精度という、両方の利点を享受できます。</p><h2><strong>もっと良くできるでしょうか？</strong></h2><p>注目すべき点は、ここで確認している結果がデフォルト設定とデータのランダムサンプリングを使用していることです。これは高性能の出発点とお考えください。Jina v5は非常に優れていますが、これらの再現率スコアはすべてのデータセットに対して「万能な」保証ではありません。すべてのデータ収集には独自の特性があり、さらにパフォーマンスを向上させるために調整することは可能ですが、常に自分の特定のデータに対してベンチマーク設定を行い、どこまで性能を引き出せるかを確認する必要があります。</p><h2><strong>まとめ</strong></h2><p>これは非常に小規模なテストです。ただし、この演習の目的は埋め込みモデルやBBQを個別に測定することではありません。最小限のセットアップで、データセットの再現率を簡単に測定する方法を示すことです。</p><p>このテストを独自のデータで実行したい場合は、<a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/fast_vs_accurate_measuring_the_recall_of_quantized_vector_search/vector_recall_notebook.ipynb">こちらのノートブック</a>をチェックして試してみてください。</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/recall-vector-search-quantization</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/recall-vector-search-quantization</guid>
    <category><![CDATA[Vector Database]]></category>
    <dc:creator><![CDATA[Jeff Vestal]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt198c7085db96aa04/6a170e17cdacbfe88c7d2a86/09f03b9239d66c36763cdab3fafcdac207ff6d83-1280x720.png" length="0" type="image/png"/>
    <pubDate>Fri, 20 Mar 2026 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[ElasticsearchにおけるHNSWの適応的早期終了]]></title>
    <description><![CDATA[ElasticsearchにHNSWの新しい適応的早期終了戦略を導入します。]]></description>
    <content:encoded><![CDATA[<p>Elasticsearchは、<a href="https://www.elastic.co/search-labs/blog/hnsw-graph">Hierarchical Navigable Small World</a>（HNSW）アルゴリズムを使用して、近接グラフ上でベクトル検索を実行します。HNSWは、k近傍法（KNN） の結果の品質と関連コストの間で適切なトレードオフを提供することが知られています。</p><p>HNSWでは、グラフ内の候補ノードを反復的に拡張し、これまでに発見された最も近い近傍の制限されたセットを維持することで検索が進行します。各拡張にはコスト（ベクトル演算、ディスクへのランダムシークなど）がかかり、そのコストに対する限界効用は検索が進むにつれて減少する傾向があります。</p><p>HNSWグラフのトラバーサルを最適化する1つの方法は、新しい真の近傍を見つける周辺尤度が増加しない場合に検索を停止することです。このため、<a href="https://www.elastic.co/docs/reference/elasticsearch/index-settings/index-modules#index-dense-vector-hnsw-early-termination">Elasticsearch 9.2</a>では、新しい<a href="https://www.elastic.co/search-labs/blog/hnsw-knn-search-early-termination">早期終了メカニズム</a>を導入しました。これは、グラフノードを訪問しても一定回数連続して十分な数の新しい最近傍が提供されない場合に、検索プロセスを停止するものです。</p><p>この記事では、HNSWの前述の早期終了メカニズムを改良して、さまざまなデータセットやデータ分布に適したものにする方法について説明します。</p><h2><strong>HNSWでの早期終了</strong></h2><p>HNSW では、近接グラフ内の候補ノードを反復的に拡張し、これまでに発見された最も近い近傍の制限されたセットを維持して、グラフ全体を訪問するか、早期終了基準を満たすまで、検索が続行されます。</p><p>したがって、早期終了は必ずしも最適化ではなく、<strong>検索アルゴリズム自体の一部</strong>です。停止を決定する瞬間が、効率性と再現率のバランスを決定します。Elasticsearchでは、HNSWのクエリを早期終了させる方法がすでにいくつか存在します。</p><ul><li><p>固定された最大数のノードが訪問されます。</p></li><li><p>一定のタイムアウトに達した場合。</p></li></ul><p>これらのルールは単純かつ予測可能ですが、<strong>検索が実際に何をしているかにはほとんど関係がありません</strong>。また、これらは主に、クエリがエンドユーザーにとって妥当な時間内に完了することを確認するために使用されます。</p><p>前回の<a href="https://www.elastic.co/search-labs/blog/hnsw-knn-search-early-termination">ブログ投稿</a>ではHNSWにおける冗長性の概念を紹介しました。つまり、HNSWが新しい候補ノードを評価し続けても、さらに最も近い近傍が見つからない場合、冗長な計算が発生します。</p><h2><strong>忍耐度：努力ではなく進歩を測る</strong></h2><p><em>忍耐度</em>という概念は、<strong>努力ではなく進歩</strong>を中心に早期終了を再構築します。</p><p>次のように尋ねる代わりに</p><p>「何ステップ進んだ？」</p><p>新たに次のように問いかけます。</p><p>「希望を失うまでに受け入れられる無駄な計算はどれだけかな？」</p><p>HNSW検索では、通常、初期の探索によっ上位k候補セットの最高の改善がもたらされます。HNSWグラフ探索の最初のステップでは、アルゴリズムがクエリベクトルにますます近い近傍を検出し続けるため、近傍のセットは継続的に更新されます。時間が経ち、検索が収束するにつれて、これらの改善はまれになります。<a href="https://cs.uwaterloo.ca/~jimmylin/publications/Teofili_Lin_ECIR2025.pdf">忍耐度ベースの終了</a>はこのパターンを監視し、改善が一定期間停止した時点で検索を終了します。</p><p>実際には、HNSWグラフを訪問する際、候補ノードをホップしながらキューの飽和比も計算します。これは、最新のグラフノードを訪問中に変更されなかった最も近い近傍の割合（または最後の反復中に導入された新しい近傍の数の逆数）を測定します。このような比率が連続した反復処理で大きくなりすぎると、グラフの訪問を停止します。</p><p>概念的には、忍耐度はHNSWの検索を<strong>収穫逓減</strong>プロセスとして扱います。リターンが平坦になると、グラフの調査を継続してもほとんど利益は得られません。</p><p>この枠組みは、終了を恣意的な固定された制限ではなく、<em>観察可能な結果</em>に直接結び付けるため、強力です。</p><p>このスマートな早期終了手法を使用する利点は、HNSWグラフ探索では、ほぼ完璧な相対再現率を維持しながら、より少数のグラフノードを訪問する傾向があることです。</p><p>これを視覚化するために、FinancialQAとQuoraという2つのデータセットと、JinaV3とE5-smallというモデルで、忍耐度に基づく早期終了（ <em><code>et=static</code></em>とラベル付け）で取得した訪問ノードあたりの再現量を、デフォルトのHNSW動作（ <em><code>et=no</code></em>とラベル付け）と比較してプロットすることができます。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltfd0d692b9beb476a/6a170ef4dc55debf0be00e97/a9d07c5153ea64a2426c82487c36846030692bb9-1600x945.png" alt="HNSWの適応的早期終了 " /><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt93509c251a1b641e/6a170ef6dc55dea2b3e00e9b/dac56125c4b16d1b596c9876b6ca9ac7b2dc87fa-1600x944.png" alt="HNSWの適応的早期終了 es" /><h2><strong>静的しきい値とHNSWのダイナミクス</strong></h2><p>実際には、Elasticsearchでは<strong>静的しきい値</strong>を使用してこれが実装されます。1つのしきい値は、<strong>飽和しきい値</strong>、つまり、最適ではないと判断される飽和度の比率を指します。もう1つのしきい値は、最適ではないキュー飽和を維持しながら連続して訪問できるグラフノードの数、つまり<strong>忍耐しきい値</strong>を指します。</p><p>Elasticsearch 9.2でこの早期終了戦略を導入したとき、レイテンシーとメモリ消費の面でメリットを得ながら再現率を可能な限り高められるように、保守的なデフォルトを選択することにしました。このため、KNNクエリでは飽和しきい値を100%に、忍耐しきい値を <a href="https://www.elastic.co/docs/reference/query-languages/query-dsl/query-dsl-knn-query#knn-query-top-level-parameters:~:text=search%20request%20size.-,num_candidates,-(Optional%2C%20integer)%20The"><em><code>num_candidates</code></em></a> の（有界の）30%に設定しています。</p><p>多くのシナリオでは、これらの設定はうまく機能しますが、同じ数の近傍を要求する2つのクエリでは、収束動作が根本的に異なる可能性があります。あるクエリは密集した局所的な近傍に遭遇し、すぐに飽和します。他のクエリは競争力のある候補を見つけるまでに、長くまばらな経路を通過しなければなりません。後者は、効果的に処理するのが最も困難であることが判明しました。</p><p>その結果、次のようなことに気付くことがありました。</p><ul><li><p>簡単なクエリに対する過度の探索。</p></li><li><p>難しいクエリに対する時期尚早の終了。</p></li></ul><p>したがって、固定されたしきい値は収束に関する全体的な仮定をエンコードしますが、HNSWをさまざまなダイナミクスに適応させることができると考えました。</p><h2><strong>HNSWの早期終了を適応的に</strong></h2><p>適応的早期終了は、この問題に異なる角度からアプローチします。事前に定義された停止しきい値を強制する代わりに、アルゴリズムが<strong>検索のダイナミクス自体からいつ停止するかを推測します</strong>。</p><p>したがって、2つの連続した候補間のキュー飽和比を比較する代わりに、即時平滑化発見率 （クエリ<em>q</em>の最後の訪問<em>i</em>で導入された新しい隣接ノードの数）と、グラフ訪問中のそのような発見率の移動平均と標準偏差を導入することにしました（<a href="https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#Welford's_online_algorithm">ウェルフォードのアルゴリズム</a>を使用）。これらの発見率に関する統計はクエリごとに計算されるため、この情報をもとに各クエリの忍耐度を判断できます。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltdbfb1e123f1b026d/6a170ef7cf4f25d9bab2d216/1958be7ca4425ade66eaf621ada3533173183598-694x118.png" alt="" /><p>以前は静的であったしきい値は、発見率の統計に対して適応的になります。飽和しきい値はローリング平均と標準偏差の合計になり、一方で忍耐力は標準偏差に反比例して適応およびスケーリングされます。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt7d4d91f464a9dc9d/6a170ef8d7c0223420de656a/f7ee4a55c24853b657df26052b275e8bd76cf0f9-654x156.png" alt="" /><p>早期終了ルールは変わらず、飽和は即時発見率が適応飽和しきい値より低い場合に発生します。適応的忍耐度よりも大きい連続候補訪問回数にわたって飽和が継続する場合、グラフ訪問は停止します。</p><p>こうすることで、KNNクエリの <em><code>num_candidates</code></em> パラメーターに依存しない動作（早期終了に関係なく、常に設定されるか、デフォルトのままになる場合がある）が得られ、各クエリとベクトル分布に動的に適応しやすくなります。</p><p>適応型戦略（ <em><code>et=adaptive</code></em>とラベル付け）を使用したFinancialQAおよびQuoraでの訪問ノードあたりの再現率は、静的戦略（ <em><code>et=static</code></em> ）およびデフォルトのHNSW動作（ <em><code>et=no</code></em> ）と比較した場合、高くなっています。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blteab7ba53ae14da0e/6a170ef9961e69e072c4cfd5/2a906997d9a25d74c7038bd9661bc97581e7258e-1600x938.png" alt=" 適応戦略とデフォルトのHNSWの動作" /><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt6fb9e672d3698200/6a170efb67045b7b2b45c2ab/3a114911e232c351dbb814cea20e8b0f1415a717-1600x925.png" alt="" /><p>適応的早期終了はElasticsearch 9.3ではHNSWの高密度ベクトルフィールドに対してデフォルトでオンになっています（最終的には<a href="https://www.elastic.co/docs/reference/elasticsearch/index-settings/index-modules#index-dense-vector-hnsw-early-termination">同じインデックスレベルの</a>設定でオフにすることができます）。</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/hnsw-elasticsearch-adaptive-early-termination</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/hnsw-elasticsearch-adaptive-early-termination</guid>
    <category><![CDATA[Vector Database]]></category>
    <category><![CDATA[Elastic内部の実情]]></category>
    <dc:creator><![CDATA[Tommaso Teofili]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt27b746cc1995e6b7/6a170efda29299de8ad010c6/e6d3186f609dd56dc5ffe33d70fa9e5cfa05b51f-1280x720.png" length="0" type="image/png"/>
    <pubDate>Mon, 02 Mar 2026 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Elasticsearchのベクトル検索はOpenSearchの最大8倍高速]]></title>
    <description><![CDATA[OpenSearchとElasticsearchのフィルタリングされたベクトル検索ベンチマークを比較し、コンテキストエンジニアリングシステムにおいてベクトル検索のパフォーマンスが重要な理由を探ります。]]></description>
    <content:encoded><![CDATA[<h2>AIエージェントとコンテキストエンジニアリングにおいて検索速度が重要な理由</h2><p>2,000万件の文書コーパスを用いたベンチマークテストの結果、Elasticsearchはフィルタリングされたベクトル検索においてOpenSearchよりも最大8倍高いスループットを実現し、テストしたすべての構成においてより高いRecall@100を達成しました。コンテキストエンジニアリングに必要な要素は高速ベクトル取得だけではありません。ワークフローの反復につれて、チームはハイブリッド検索やフィルタリングなどの強力な関連性制御、操作の簡便性、予測可能なパフォーマンスも必要とするようになります。しかし、エージェントはリクエストごとに取得、推論、取得のループを何度も実行することが多いため、取得の遅延が乗数効果となり、ここでの改善はエンドツーエンドの応答性の向上とコスト削減に直接つながります。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt9daec868eed84658/6a170bef6234e0c322db1a19/d5a52a07773f0942c2baa732dacfe782aac0f415-1600x683.png" alt="OpenSearchとElasticsearchの比較：フィルタリングされたベクトル検索のスループットベンチマーク" /><p>コンテキストエンジニアリングにおいて、取得は一度きりのステップではありません。エージェントとアプリケーションは、クエリを洗練させ、事実を検証し、根拠に基づいたコンテキストを構築し、タスクを完了するために、取得 → 推論 → 取得といったループを繰り返し実行します。このパターンは、エージェント型ワークフローや反復型検索拡張生成（RAG）においてよく見られます。検索はユーザーリクエストごとに何度も呼び出される可能性があるため、応答に遅延が生じ、インフラコストが増加します。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt718c72fb858e4c98/6a170bf00e2e496e7241a132/54ac476ff20a3cf93484298c9ae47612c12fc110-800x417.png" alt="コンテキストエンジニアリングは、大規模なコンテキストプールを限定されたLLMコンテキストウィンドウに変換します。" /><h2>ベクトル検索のパフォーマンスが重要な理由</h2><p></p><p>「15インチのノートパソコンが入る、防水性があり、金曜日までに届く60ドル以下の機内持ち込み用バックパックが必要です」という質問に、店員が答える場面を想像してみてください。</p><p>実際の運用環境では、アシスタントがベクトルクエリを1回発行して停止することはほとんどありません。適切なコンテキストを構築するために検索ループを実行し、各ステップは通常、在庫状況、地域、出荷約束、ブランドルール、ポリシー適格性などのフィルターによって制約されます。</p><p><strong>ステップ1：意図を解釈し、制約に変換する。</strong></p><p>エージェントはリクエストを構造化されたフィルタとセマンティッククエリに変換します。例えば、次のようなものです。</p><ul><li><p>フィルター：在庫あり、ユーザーの郵便番号に配達可能、金曜日までに配達、価格60ドル未満、有効な出品</p></li><li><p>ベクトルクエリ：「機内持ち込み用バックパック 15インチノートパソコン対応 防水」</p></li></ul><p><strong>ステップ2：候補を取得し、絞り込む。</strong></p><p>適切な一致を見逃さないように、しばしばバリエーションを加えて検索を繰り返します。</p><ul><li><p>「旅行用バックパック 機内持ち込み可能 ノートパソコン用スリーブ」</p></li><li><p>「防水通勤用バックパック 15インチ」</p></li><li><p>「軽量 機内リュック」</p></li></ul><p>各クエリは同じ適格性フィルターを使用します。なぜなら、無関係な項目や利用できない項目を取得することは、コンテキストの無駄になるからです。</p><p><strong>ステップ3：詳細を確認し、リスクを軽減するために展開する。</strong></p><p>エージェントは、最終的な回答に影響を与える主要な属性を再度確認するために取得を行います。</p><ul><li><p>素材と耐水性に関する記述</p></li><li><p>寸法とノートパソコン収納部の適合性</p></li><li><p>返品ポリシーや保証の制約</p></li><li><p>在庫が少ない場合の代替オプション</p></li></ul><p>これは、取得、推論、取得、組み立てという複数段階のコンテキストエンジニアリングです。</p><h2>コンテキストエンジニアリングにおいてレイテンシと再現率が重要な理由</h2><p>これらのやり取りには、ユーザーセッションごとに数十回のフィルタリングされたデータ取得呼び出しが含まれる場合があります。それにより、呼び出しごとのレイテンシがエンドツーエンドの応答時間に直接的な乗数となり、低い再現率は追加の再試行を強制したり、エージェントが対象アイテムを見逃す原因となり、回答の質を低下させます。</p><p>要点：コンテキストエンジニアリングされたシステムでは、フィルタリングされた近似最近傍法（ANN）は単一のルックアップではありません。これは制約下での反復処理であるため、大規模言語モデル（LLM）が最も目立つ要素である場合でも、ベクトル検索のパフォーマンスはレイテンシ、スループット、コストにすぐに現れます。</p><h2>ベンチマーク</h2><h3>成果</h3><p>グラフ2では、各点が1つのテスト構成を表しています。最も良い結果は左上に表示され、これは低いレイテンシで高い再現率が得られることを意味します。Elasticsearchの結果はOpenSearchよりも常に左上に近く、同じワークロード設定下でより優れた速度と精度を示しています。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltb562b30a600cc8e1/6a170bf2cf4f253582b2d1b2/c50d1df00968cac18149a2799e6242fbe49b66a0-1600x990.png" alt=" グラフ2：再現率対平均レイテンシ（再スコア1）。" /><h4>いくつかの重要な洞察</h4><ul><li><p><code>s_n_r_value</code>: <code>size_numCandidates_rescoreOversample</code>の省略形（これらのテストではkとnumCandidatesはnumCandidatesと等しく設定されます）。例えば、 <code>100_500_1</code> size=100、numCandidates=500、k=500、再スコアオーバーサンプル=1 を意味します。</p></li><li><p>再現率：その構成における測定Recall@100</p></li><li><p>平均レイテンシ（ミリ秒）：クエリごとの平均エンドツーエンドレイテンシ</p></li><li><p>スループット：1秒あたりのクエリ数</p></li><li><p>再現率（％）：ElasticsearchとOpenSearchの相対的な再現率向上率（Elasticsearch - OpenSearch）/OpenSearch</p></li><li><p>レイテンシXs：OpenSearchの平均レイテンシをElasticsearchの平均レイテンシで割った値</p></li><li><p>スループットX：Elasticsearchのスループットをオープンサーチのスループットで割った値</p></li></ul><p>エンジン</p><p>`s_n_r_value`</p><p>リコール</p><p>平均レイテンシ（ミリ秒）</p><p>スループット</p><p>再現率（％）</p><p>レイテンシ Xs</p><p>スループット Xs</p><p>Elasticsearch</p><p>100_250_1</p><p>0.7704</p><p>25</p><p>534.75</p><p>9.70％</p><p>2.28</p><p>1.91</p><p>OpenSearch</p><p>100_250_1</p><p>0.7023</p><p>57.08</p><p>279.58</p><p></p><p></p><p></p><p>Elasticsearch</p><p>100_500_1</p><p>0.8577</p><p>25.42</p><p>524.14</p><p>7.20%</p><p>2.4</p><p>2</p><p>OpenSearch</p><p>100_500_1</p><p>0.8001</p><p>60.9</p><p>262.12</p><p></p><p></p><p></p><p>Elasticsearch</p><p>100_750_1</p><p>0.8947</p><p>29.67</p><p>528.09</p><p>5.72％</p><p>2.25</p><p>2.21</p><p>OpenSearch</p><p>100_750_1</p><p>0.8463</p><p>66.76</p><p>239.11</p><p></p><p></p><p></p><p>Elasticsearch</p><p>100_1000_1</p><p>0.9156</p><p>29.65</p><p>534.5</p><p>4.66％</p><p>2.46</p><p>2.44</p><p>OpenSearch</p><p>100_1000_1</p><p>0.8748</p><p>72.88</p><p>219.01</p><p></p><p></p><p></p><p>Elasticsearch</p><p>100_1500_1</p><p>0.9386</p><p>31.84</p><p>497.3</p><p>3.38％</p><p>2.71</p><p>2.68</p><p>OpenSearch</p><p>100_1500_1</p><p>0.9079</p><p>86.16</p><p>185.4</p><p></p><p></p><p></p><p>Elasticsearch</p><p>100_2000_1</p><p>0.9507</p><p>34.69</p><p>457.2</p><p>2.57%</p><p>2.98</p><p>2.96</p><p>OpenSearch</p><p>100_2000_1</p><p>0.9269</p><p>103.36</p><p>154.55</p><p></p><p></p><p></p><p>Elasticsearch</p><p>100_2500_1</p><p>0.9582</p><p>37.9</p><p>418.43</p><p>1.99％</p><p>3.28</p><p>3.26</p><p>OpenSearch</p><p>100_2500_1</p><p>0.9395</p><p>124.29</p><p>128.53</p><p></p><p></p><p></p><p>Elasticsearch</p><p>100_3000_1</p><p>0.9636</p><p>41.86</p><p>379.4</p><p>1.62％</p><p>3.46</p><p>3.44</p><p>OpenSearch</p><p>100_3000_1</p><p>0.9482</p><p>144.67</p><p>110.34</p><p></p><p></p><p></p><p>Elasticsearch</p><p>100_4000_1</p><p>0.9705</p><p>50.28</p><p>316.21</p><p>1.06%</p><p>3.87</p><p>3.85</p><p>OpenSearch</p><p>100_4000_1</p><p>0.9603</p><p>194.36</p><p>82.22</p><p></p><p></p><p></p><p>Elasticsearch</p><p>100_5000_1</p><p>0.9749</p><p>58.77</p><p>270.91</p><p>0.73%</p><p>4.43</p><p>4.41</p><p>OpenSearch</p><p>100_5000_1</p><p>0.9678</p><p>260.33</p><p>61.38</p><p></p><p></p><p></p><p>Elasticsearch</p><p>100_6000_1</p><p>0.9781</p><p>66.75</p><p>238.59</p><p>0.52%</p><p>4.91</p><p>4.89</p><p>OpenSearch</p><p>100_6000_1</p><p>0.973</p><p>327.44</p><p>48.81</p><p></p><p></p><p></p><p>Elasticsearch</p><p>100_7000_1</p><p>0.9804</p><p>74.64</p><p>213.49</p><p>0.38％</p><p>5.28</p><p>5.27</p><p>OpenSearch</p><p>100_7000_1</p><p>0.9767</p><p>394.24</p><p>40.53</p><p></p><p></p><p></p><p>Elasticsearch</p><p>100_8000_1</p><p>0.9823</p><p>82.28</p><p>193.59</p><p>0.27％</p><p>6.86</p><p>6.83</p><p>OpenSearch</p><p>100_8000_1</p><p>0.9797</p><p>564.14</p><p>28.33</p><p></p><p></p><p></p><p>Elasticsearch</p><p>100_9000_1</p><p>0.9837</p><p>90.08</p><p>176.96</p><p>0.16%</p><p>7.63</p><p>7.61</p><p>OpenSearch</p><p>100_9000_1</p><p>0.9821</p><p>687.25</p><p>23.25</p><p></p><p></p><p></p><p>Elasticsearch</p><p>100_10000_1</p><p>0.9848</p><p>97.64</p><p>163.31</p><p>0.08%</p><p>8.38</p><p>8.36</p><p>OpenSearch</p><p>100_10000_1</p><p>0.984</p><p>818.64</p><p>19.53</p><p></p><p></p><p></p><p>例えば、<code>100_9000_1</code>では、OpenSearchの平均取得時間687ミリ秒に対してElasticsearchは90ミリ秒であり、10ステップの取得ループでは約10×(687-90)=6秒の追加待機時間となります。 </p><p><a href="https://github.com/elastic/competitive-benchmarking-studies/tree/main/es-9.3-vs-os-3.5-vector-search/jingra/results/20260220">全ての結果を</a>ご覧ください。</p><h3>調査手法</h3><p>Pythonを使ってクエリを送信し、対応タイミングやその他の統計を追跡し、以下のクエリをエンジンに送信しました。ベクター検索エンジンのパフォーマンスは、そのコアパラメータ（考慮する候補の数、再スコアリングの積極性、返されるコンテキストの量など）をどのように調整するかによって決まることを覚えておいてください。これらの設定は、再現率（正解を見つける可能性）とレイテンシ（結果を得るまでの速さ）の両方に直接影響します。</p><p>ベンチマークでは、エージェント検索ループで通常調整するのと同じ候補、再スコア、結果サイズの設定を使用し、そのワークロード下でElasticsearchがどのように機能するかを測定しました。その後、同じ設定でOpenSearchをリファレンスとして実行しました。</p><p>OpenSearch</p>GET &lt;INDEX_NAME&gt;/_search
{
  "query": {
    "knn": {
      "&lt;DENSE_VECTOR_FIELD_NAME&gt;": {
        "vector": [...],
        "k": &lt;NUMBER_OF_CANDIDATES&gt;,
        "method_parameters": {
          "ef_search": &lt;NUMBER_OF_CANDIDATES&gt;
        },
        "rescore": {
          "oversample_factor": &lt;OVERSAMPLE&gt;
        },
        "filter": {
          &lt;SOME_FILTER&gt;
        }
      }
    }
  },
  "size": &lt;RESULT_SIZE&gt;,
  "_source": {
    "excludes": [
      "&lt;DENSE_VECTOR_FIELD_NAME&gt;"
    ]
  }
}<ul><li><p><code>"size": &lt;RESULT_SIZE&gt;</code>: クライアントに返されたヒット数。このベンチマークでは、Recall@100を計算するために、結果のサイズは100です。</p></li><li><p><code>"k": &lt;NUMBER_OF_CANDIDATES&gt;</code>: 最近傍候補の数。</p></li><li><p><code>"ef_search": &lt;NUMBER_OF_CANDIDATES&gt;</code>: 検査するベクトルの数。</p></li><li><p><code>"oversample_factor": &lt;OVERSAMPLE&gt;</code>: 再スコアリングを行う前に取得される候補ベクトルの数。</p></li></ul><p>Elasticsearch</p>GET &lt;INDEX_NAME&gt;/_search
{
  "query": {
    "knn": {
      "field": "&lt;DENSE_VECTOR_FIELD_NAME&gt;",
      "query_vector": [...],
      "k": &lt;NUMBER_OF_CANDIDATES&gt;,
      "num_candidates": &lt;NUMBER_OF_CANDIDATES&gt;,
      "rescore_vector": {
        "oversample": &lt;OVERSAMPLE&gt;
      },
      "filter": {
        &lt;SOME_FILTER&gt;
      }
    }
  },
  "size": &lt;RESULT_SIZE&gt;,
  "_source": {
    "excludes": [
      "&lt;DENSE_VECTOR_FIELD_NAME&gt;"
    ]
  }
}<ul><li><p><code>"size": &lt;RESULT_SIZE&gt;</code>: クライアントに返されたヒット数。このベンチマークでは、Recall@100を計算するために、結果のサイズは100です。</p></li><li><p><code>"k": &lt;NUMBER_OF_CANDIDATES&gt;</code>: 各シャードから返す最近傍の数。</p></li><li><p><code>"num_candidates": &lt;NUMBER_OF_CANDIDATES&gt;</code>: <code>knn</code>検索を実行する際にシャードごとに考慮する最近傍候補の数。</p></li><li><p><code>"oversample": &lt;OVERSAMPLE&gt;</code>: 再スコアリングを行う前に取得される候補ベクトルの数。</p></li></ul><p>例</p><p><code>Knn</code> クエリ（ <code>100_500_1</code> ）は次のようになります。</p><p>OpenSearch</p>GET search_catalog_128/_search
{
  "query": {
    "knn": {
      "search_catalog_embedding": {
        "vector": [...],
        "k": 500,
        "method_parameters": {
          "ef_search": 500
        },
        "rescore": {
          "oversample_factor": 1
        },
        "filter": {
          "term": {
            "valid": true
          }
        }
      }
    }
  },
  "size": 100,
  "_source": {
    "excludes": [
      "search_catalog_embedding"
    ]
  }
}<p>Elasticsearch</p>GET search_catalog_128/_search
{
  "query": {
    "knn": {
      "field": "search_catalog_embedding",
      "query_vector": [...],
      "k": 500,
      "num_candidates": 500,
      "rescore_vector": {
        "oversample": 1
      },
      "filter": {
        "term": {
          "valid": true
        }
      }
    }
  },
  "size": 100,
  "_source": {
    "excludes": [
      "search_catalog_embedding"
    ]
  }
}<p>Terraformスクリプト、Kubernetesマニフェスト、ベンチマークコードとともに、完全な構成はこの<a href="https://github.com/elastic/competitive-benchmarking-studies">リポジトリ</a>の<a href="https://github.com/elastic/competitive-benchmarking-studies/tree/main/es-9.3-vs-os-3.5-vector-search">es-9.3-vs-os-3.5-vector-search</a>フォルダで入手可能です。</p><h3>クラスターの設定</h3><p>私たちは、16 vCPUと64 GB RAMを備えた6台のe2-standard-16クラウドサーバーでテストを実行しました。各サーバーにおいて、検索エンジンノードを実行する各Kubernetesポッドに15個のvCPUと56GBのRAMを割り当て、そのうち28GBをJVMヒープ用に確保しました。</p><p>クラスターはElasticsearch 9.3.0とOpenSearch 3.5.0（Lucene 10.3.2）で実行しました。このベンチマークでは両方のシステムが同じLuceneバージョンを使用しているため、観測されたスループットとレイテンシの違いはLucene単独に起因するものではなく、各エンジンがフィルタリングされたk近傍法（kNN）による検索と再スコアリングをどのように統合し実行するかの違いを反映します。私たちは、3つのプライマリシャードと1つのレプリカ（合計6つのシャード、ノードごとに1つ）を持つ単一のインデックスを使用しました。</p><p>我々はまた、同じリージョン内の別のサーバーを使用してベンチマーククライアントを実行し、タイミング統計を収集しました。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt7c7bdd567395d5b7/6a170bf3c1e8a56ee2f882f6/f81002c9186e4c2d3e92f49d72418fee9860fc5e-761x401.png" alt="ElasticsearchとOpenSearchのベンチマーク用のクラスター設定" /><h3>データセット</h3><p></p><p>このベンチマークでは、2,000万件のドキュメントを含む大規模なeコマーススタイルのカタログ埋め込みデータセットを使用しました。これは、実世界のフィルタリングされたベクトル検索を大規模なスケールで反映するように設計されています。</p><p></p><p>各ドキュメントはカタログ項目を表し、以下を含みます。</p><p></p><ul><li><p>近似kNN検索に使用される128次元の密なベクトル埋め込み。</p></li><li><p>構造化されたメタデータフィールドを使用してフィルタリング（例：アイテムの有効性と可用性、およびその他のカタログ制約）を行うことで、適格なサブセット内でのみ最近傍を取得するという一般的な本番パターンを可能にします。</p></li></ul><p></p><p>このデータセットを選んだ理由は、本番環境におけるエージェント型システムやRAG型システムで見られる主要なパフォーマンス上の課題を捉えているからです。つまり、ベクトル類似性だけでは不十分であり、検索はフィルタによって制約されることが多く、システムはこれらの制約の下で高い再現率を維持しながらレイテンシを低く抑える必要があるということです。より小規模なQAスタイルのデータセットと比較すると、2000万件の文書コーパスは、フィルタリングされたANNシステムが実際に直面する規模と候補数によるプレッシャーをより適切に反映しています。</p><h2>まとめ</h2><p>現代のAIアーキテクチャー、特にコンテキストエンジニアリングを中心としたアーキテクチャーにおいては、ベクトル探索の速度は些細な実装上の問題ではなく、乗数です。エージェントとワークフローが取得 → 推論 → 取得を繰り返す際、検索パフォーマンスはエンドツーエンドのレイテンシ、スループット、モデルに供給されるコンテキストの品質を直接形作ります。</p><p>当社のベンチマークでは、Elasticsearchは、OpenSearchと比べて、より低いレイテンシで一貫して高い再現率を達成しました。これは、類似ベクトルの取得だけでなく、正確性が「適切なドキュメントを取得できること」に左右されるシナリオで顕著でした。制御されたデータセットではその差は明確であり、本番環境では、大量の検索呼び出しを通じてそれらの利点が蓄積され、応答性が向上し、容量余裕が増加し、インフラストラクチャーコストが削減されます。</p><h3>参考資料</h3><ol><li><p><a href="https://www.elastic.co/search-labs/blog/context-engineering-overview">コンテキストエンジニアリングとは何ですか？</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/series/context-engineering-hybrid-search-evolution">ハイブリッド検索とコンテキストエンジニアリングの進化</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/context-engineering-relevance-ai-agents-elasticsearch">AIエージェント向けコンテキストエンジニアリングにおける関連性の影響</a></p></li></ol>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/opensearch-vs-elasticsearch-filtered-vector-search</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/opensearch-vs-elasticsearch-filtered-vector-search</guid>
    <category><![CDATA[Vector Database]]></category>
    <dc:creator><![CDATA[Sachin Frayne]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt4b38e5114bbf098c/6a170bf560084b3a6c3c459d/fb7ee623925ca6696d643e437ce8efe5fe749079-1280x720.png" length="0" type="image/png"/>
    <pubDate>Wed, 25 Feb 2026 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[NVIDIA cuVSでElasticsearchのベクトルインデキシングを最大12倍高速化：GPUアクセラレーション 第2章]]></title>
    <description><![CDATA[ElasticsearchがGPUアクセラレーションによるベクトルインデキシングとNVIDIA cuVSを使用してほぼ12倍のインデキシングスループットを達成する方法をご覧ください。]]></description>
    <content:encoded><![CDATA[<p>今年の初め、ElasticはNVIDIAとの<a href="https://ir.elastic.co/news/news-details/2025/Elastic-Brings-Enterprise-Data-to-NVIDIA-AI-Factories/default.aspx">協業</a>を発表し、ElasticsearchにGPUアクセラレーションをもたらすために<a href="https://developer.nvidia.com/cuvs">NVIDIA cuVS</a>と統合しました。これは<a href="https://www.nvidia.com/en-us/on-demand/session/gtc25-S71286/">NVIDIA GTCのセッション</a>やさまざまな<a href="https://www.elastic.co/search-labs/blog/gpu-accelerated-vector-search-elasticsearch-nvidia">ブログ</a>で詳しく説明されています。この投稿は、NVIDIAのベクトル検索チームとの共同エンジニアリング作業の最新情報です。</p><h2>要約</h2><p>まず、現状をお伝えしましょう。Elasticsearchは、強力なベクトルデータベースとして確立され、大規模な類似性検索に対して豊富な特徴と強力なパフォーマンスを提供しています。スカラー量子化、Better Binary Quantization（<a href="https://www.elastic.co/search-labs/blog/better-binary-quantization-lucene-elasticsearch">BBQ</a>）、<a href="https://www.elastic.co/blog/accelerating-vector-search-simd-instructions">SIMD</a>ベクトル演算、<a href="https://www.elastic.co/search-labs/blog/diskbbq-elasticsearch-introduction">DiskBBQ</a>のようなよりディスク効率の高いアルゴリズムなどの機能により、すでにベクトルワークロードの管理に効率的かつ柔軟な選択肢を提供しています。</p><p>NVIDIA cuVSをベクトル検索タスク用の呼び出し可能なモジュールとして統合することで、ベクトルインデキシングのパフォーマンスと効率を大幅に向上させ、大規模なベクトルワークロードをより良くサポートすることを目指しています。</p><h2>課題</h2><p>高性能ベクトルデータベースを構築する上で最も困難な課題の一つは、ベクトルインデックス（<a href="https://arxiv.org/abs/1603.09320">HNSW</a>グラフ）を構築することです。インデックス構築は、すべてのベクトルが他の多数のベクトルと比較されるため、すぐに数百万、あるいは数十億の算術演算によって支配されるようになります。さらに、インデックスのライフサイクル操作、例えば圧縮やマージなどは、インデキシングの全体的な計算オーバーヘッドをさらに増加させる可能性があります。データ量と関連するベクトル埋め込みが指数関数的に増加するにつれ、大規模な並列処理と高スループットの数学演算用に構築された高速コンピューティングGPUは、これらのワークロードを処理するのに理想的な位置にあります。</p><h2>Elasticsearch-GPUプラグインの登場</h2><p><a href="https://developer.nvidia.com/cuvs">NVIDIA cuVS</a>は、GPUによるベクトル検索とデータクラスタリングのためのオープンソースCUDA-Xライブラリであり、AIおよび推奨ワークロード向けの高速インデックス構築と埋め込み検索を可能にします。</p><p>Elasticsearchは<a href="https://mvnrepository.com/artifact/com.nvidia.cuvs/cuvs-java">cuVSをcuvs-javaを通じて使用して</a>います。cuvs-javaはコミュニティが開発し、NVIDIAが保守するオープンソースライブラリです。cuvs-javaライブラリは軽量で、<a href="https://docs.rapids.ai/api/cuvs/nightly/c_api/">cuVS C API</a>をベースに<a href="https://openjdk.org/projects/panama/">Panama</a> Foreign Functionを使用して、cuVSの特徴をJavaらしい方法で公開しつつ、モダンな高性能を維持しています。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltc7fd7361099da05a/6a17e920be608670af00477f/5f6daa1eb07f704a6707d9e6b7ccb81d0abaa8c9-566x419.png" alt="ElasticsearchがNVIDIA cuVS、CPU、GPUインデックスでどのように機能するか" /><p>cuvs-javaライブラリは<a href="https://github.com/elastic/elasticsearch/pull/135545">新しいElasticsearchプラグイン</a>に統合されています。そのため、GPU上でのインデキシングを同じElasticsearchノードとプロセスで実行でき、外部のコードやハードウェアを提供する必要はありません。CUVsライブラリがインストールされていて、GPUが存在して構成されている場合、インデキシング中にElasticsearchはGPUを使用してベクターインデキシング処理を高速化します。ベクトルはGPUに提供され、GPUは<a href="https://arxiv.org/abs/2308.15136">CAGRA</a>グラフを構築します。その後、このグラフはHNSW形式に変換され、CPU上でのベクトル検索にすぐに利用可能になります。構築されたグラフの最終的な形式は、CPU上に構築されるものと同じです。これによりElasticsearchは、基盤となるハードウェアがサポートしている場合、GPUを活用して高スループットのベクトルインデックスを作成し、CPUのパワーを他のタスク（同時検索やデータ処理など）に解放することができます。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt485f55f29d6df5c4/6a17e922be6086dcf3004785/3ea255bd9bfd7983f78143c5eba999d2149d72be-671x356.png" alt="" /><h2>インデックス構築の加速</h2><p>ElasticsearchにGPUアクセラレーションを統合する一環として、cuvs-javaにいくつかの機能強化が行われ、効率的なデータのインプット/出力と関数呼び出しに焦点が当てられました。主要な機能強化は、<a href="https://github.com/rapidsai/cuvs/blob/2cf5fa7666d703dccbe655f8214656b0952bb69b/java/cuvs-java/src/main/java/com/nvidia/cuvs/CuVSMatrix.java">cuVSMatrix</a>を使用して、Javaヒープ、オフヒープ、またはGPUメモリに存在するベクトルを透過的にモデル化することです。これにより、データをメモリとGPU間で効率的に移動でき、潜在的に数十億のベクトルの不要なコピーを回避できます。</p><p>この基礎となるゼロコピー抽象化のおかげで、GPUメモリへの転送とグラフの取得の両方が直接実行できます。インデキシング中、ベクトルは最初にJavaヒープ上のメモリにバッファリングされ、その後GPUに送られてCagraグラフを構築します。その後、グラフはGPUから取得され、HNSW形式に変換され、ディスクに保存されます。</p><p>マージ時には、ベクトルはすでにディスクに格納されており、Javaヒープを完全にバイパスします。インデックスファイルはメモリマップされ、データは直接GPUメモリに転送されます。この設計は、float32やint8などのさまざまなビット幅にも簡単に対応し、他の量子化スキームにも自然に拡張できます。</p><h2>実際のパフォーマンス</h2><p>数字を見てみる前に、少し背景を説明しておきましょう。Elasticsearchのセグメントマージは通常、インデキシング中にバックグラウンドで自動的に実行されるため、分離してベンチマークをとることが難しくなります。再現可能な結果を得るために、制御された実験でforce-mergeを使用してセグメントのマージを明示的にトリガーしました。force-mergeはバックグラウンドマージと同じ基礎となるマージ操作を実行するので、実際のインデキシングワークロードでは正確な効果が異なる場合でも、そのパフォーマンスは期待される改善を示す有用な指標となります。</p><p>さて、数字を見てみましょう。</p><p>最初のベンチマーク結果は非常に有望です。ベンチマークは、ローカルに接続されたNVMeストレージを持つAWS <code>g6.4xlarge</code>インスタンスで実行しました。Elasticsearchのシングルノードは、デフォルトの最適なインデキシングスレッド数（各物理コアに1つずつの計8つ）を使用し、<a href="https://www.elastic.co/docs/reference/elasticsearch/index-settings/merge">マージスロットリング</a>（高速NVMeディスクではあまり適用されません）を無効にするように設定しました。</p><p>データセットには<a href="https://github.com/elastic/rally-tracks/blob/master/openai_vector/README.md">OpenAI Rallyベクトルトラック</a>から取得した1,536次元のベクトル260万個を<a href="https://github.com/elastic/elasticsearch/pull/137072">base64文字列</a>としてエンコードし、float32 <em>hnsw</em>としてインデックスして使用しました。すべてのシナリオにおいて、構築されたグラフは最大95%のリコールレベルを達成します。結果は以下となりました。</p><ul><li><p><strong>インデキシングのスループット：</strong>メモリ内バッファのフラッシュ中にグラフ構築を GPU に移動することで、スループットが約 12 倍向上します。</p></li><li><p><strong>強制マージ：</strong>インデキシングが完了した後、GPUはセグメントのマージを加速し続け、強制マージフェーズを約7倍高速化します。</p></li></ul><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltfea4ee13b5a3b10d/6a17e923e9ea879c6aa9c616/f60ea9ee5996e456f393ffd195ee7eada6e5a7c2-948x387.png" alt="" /><ul><li><p><strong>CPU使用率：</strong>グラフ構築をGPUにオフロードすると、平均およびピーク時のCPU使用率が大幅に削減されます。以下のグラフは、インデキシングとマージ中のCPU使用率を示しており、これらの操作をGPUで実行すると使用率がどれだけ低くなるかを強調しています。GPUインデキシング中のCPU使用率が低下すると、CPUサイクルが解放され、検索パフォーマンスの向上に向けることができます。</p></li></ul><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt80ff9c53f9b6884a/6a17e925445de9ee4b4d0187/5e680a5fc41700a877f3d8b2e5ce18ebd3f37a0b-1600x562.png" alt="" /><ul><li><p><strong>リコール：</strong>CPU実行とGPU実行の精度は実質的に同じですが、GPUで構築されたグラフのリコールはわずかに高くなります。</p></li></ul><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5cbe084eca27b8e4/6a17e926faa913317093c8b7/48a2b7758606bd321712b7d8378cd2640e652a4e-1384x544.png" alt="" /><h2>別の次元での比較：価格</h2><p>先ほどの比較では、意図的に同一のハードウェアが使用されており、唯一の違いはインデキシング中にGPUが使用されたかどうかでした。この設定は、生のコンピューティング効果を分離するのに役立ちますが、コストの観点から比較することもできます。</p><p>GPUアクセラレーション構成とほぼ同じ時間単価で、同等のCPUおよびメモリリソースの約2倍（32個のvCPU（AMD EPYC）と64GBのRAM）を備えたCPUのみのセットアップをプロビジョニングでき、インデキシングスレッドの数を2倍の16に増やすことができます。</p><p>比較を公平かつ一貫性のあるものにするために、このCPUのみの実験をAWS g6.8xlargeインスタンスで実行しました。GPUは明示的に無効になっています。これにより、GPUアクセラレーションとCPUのみのインデキシングのコストパフォーマンスのトレードオフを評価する際、他のすべてのハードウェア特性を一定に保つことができました。</p><p>予想どおり、より強力なCPUインスタンスでは、上記のセクションのベンチマークと比較してパフォーマンスが向上しています。しかし、このより強力なCPUインスタンスを元のGPUアクセラレーション結果と比較すると、GPUは、リコールレベル最大<strong>95%</strong>に達するグラフを構築しながら<strong>最大5倍</strong>のインデキシングスループット向上、<strong>最大6倍</strong>のフォースマージと、依然として大幅なパフォーマンス向上を提供します。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt94b5eb6f95ba307d/6a17e928abe0f255d4dfea35/8ffa58cae3ad175ef2932a351aeef4c34a1407b9-948x394.png" alt="" /><h2>結論</h2><p>エンドツーエンドのシナリオでは、NVIDIA cuVSによるGPUアクセラレーションにより、インデキシングのスループットが約12倍向上し、force-mergeのレイテンシが7倍減少し、CPU使用率が大幅に低下します。これは、ベクトルインデキシングとマージワークロードがGPUアクセラレーションから大きな恩恵を受けることを示しています。コスト調整後の比較では、GPUアクセラレーションは引き続き大幅なパフォーマンス向上をもたらし、インデキシングのスループットは約5倍、force-merge操作は6倍高速化されます。</p><p>GPUアクセラレーションによるベクトルインデキシングは、現在Elasticsearch 9.3の技術プレビューで計画されており、2026年初頭にリリース予定です。</p><p>続報をお楽しみに。</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/elasticsearch-gpu-accelerated-vector-indexing-nvidia</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/elasticsearch-gpu-accelerated-vector-indexing-nvidia</guid>
    <category><![CDATA[Vector Database]]></category>
    <dc:creator><![CDATA[Chris Hegarty,Hemant Malik,Corey Nolet,Manas Singh,Mithun Radhakrishnan,Mayya Sharipova,Lorenzo Dematte,Ben Frederickson]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt1248d51633bd75d9/6a17e92ae9ea8714b3a9c61a/08f7469a4daaf67b7c5999585aae179b6680c78d-896x746.png" length="0" type="image/png"/>
    <pubDate>Wed, 03 Dec 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Elasticsearch と SigLIP-2 による山頂のマルチモーダル探索 ]]></title>
    <description><![CDATA[SigLIP-2 埋め込みと Elasticsearch kNN ベクトル検索を使用して、テキストから画像、画像から画像へのマルチモーダル検索を実装する方法を学びます。プロジェクトの焦点: エベレスト トレッキングでアマ ダブラム山の山頂の写真を探す。]]></description>
    <content:encoded><![CDATA[<p>写真アルバムを意味で検索したいと思ったことはありませんか?「青いジャケットを着てベンチに座っている写真を見せてください」「エベレストの写真を見せてください」「日本酒と寿司」などのクエリを試してみてください。コーヒー（またはお好みの飲み物）を飲みながら、読み続けてください。このブログでは、マルチモーダル ハイブリッド検索アプリケーションの構築方法を紹介します。マルチモーダルとは、アプリが単語だけでなく、テキスト、画像、音声などさまざまな種類の入力を理解して検索できることを意味します。ハイブリッドとは、キーワード マッチング、kNN ベクトル検索、ジオフェンシングなどの技術を組み合わせて、より鮮明な結果を提供することを意味します。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltdfa1ec1ccd450e94/6a17da751d1b8308ee93e344/0ec6bbb45013846b59ee00d2bf73ee2182ee7392-1920x1080.gif" alt="エベレスト登山のさまざまな山頂の写真のライブラリ。" /><p>これを実現するために、Google の SigLIP-2 を使用して画像とテキストの両方のベクトル埋め込みを生成し、Elasticsearch ベクトル データベースに保存します。クエリ時に、検索入力、テキストまたは画像を埋め込みに変換し、高速 kNN ベクトル検索を実行して結果を取得します。この設定により、効率的なテキストから画像への検索、画像から画像への検索が可能になります。Streamlit UI は、テキストベースの検索を行ってアルバムから一致する写真を検索して表示するだけでなく、アップロードされた画像から山頂を識別し、フォトアルバムでその山の他の写真を表示できるフロントエンドを提供することで、このプロジェクトを実現します。また、検索精度を向上させるために実行した手順や、実用的なヒントやコツについても説明します。さらに詳しく調べるために、 <a href="https://github.com/navneet83/multimodal-mountain-peak-search">GitHub リポジトリ</a>と<a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/notebooks/multimodal_mountain_peak_search.ipynb">Colab ノートブック</a>を提供しています。</p><h2>始まり</h2><p>このブログ投稿は、エベレストベースキャンプトレッキングで撮ったアマダブラム山の写真を全部見せてほしいと私に頼んだ10歳の子供からインスピレーションを受けたものです。写真アルバムを精査しながら、私は他のいくつかの山頂を特定するよう求められましたが、そのうちのいくつかは名前がわかりませんでした。</p><p>それで、これは楽しいコンピューター ビジョン プロジェクトになるかもしれないというアイデアが浮かびました。私たちが達成したかったこと:</p><ul><li><p>山頂の写真を名前で検索する</p></li><li><p>画像から山頂の名前を推測し、写真アルバムで似たような山頂を見つける</p></li><li><p>概念クエリを機能させる（<em>人</em>、<em>川</em>、<em>祈りの旗</em><em>など）</em></p></li></ul><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltf82df9d7005fc3fe/6a17da78abe0f2e77bdfe8b9/e9d0d720a9b565d5b749bdc915068852d4f157ad-1200x1600.png" alt="アマ・ダブラム山 " /><h2>ドリームチームを結成: SigLIP-2、Elasticsearch、Streamlit</h2><p>これを機能させるには、テキスト (「Ama Dablam」) と画像 (私のアルバムの写真) の両方を、意味のある比較が可能なベクトル、つまり同じベクトル空間に変換する必要があることがすぐに明らかになりました。これを実行すると、検索は単に「最も近いものを見つける」だけになります。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5f80b69a9d5bd28a/6a17da7a4b055ddd1243209e/20e6f8b7d4fa48414f407ec200adbe00ee28d517-1536x1024.png" alt="SigLIP-2、Elasticsearch、Streamlit のドリームチーム。" /><p>画像の埋め込みを生成するために、多言語<a href="https://huggingface.co/blog/vlms-2025">ビジョン言語エンコーダー</a>を使用します。これにより、山の写真と「Ama Dablam」などのフレーズが同じベクトル空間に配置されます。</p><p>最近 Google がリリースした<a href="https://huggingface.co/blog/siglip2"><strong>SigLIP-2</strong></a>はここによく当てはまります。タスク固有のトレーニング (<strong>ゼロショット</strong>設定) なしで埋め込みを生成でき、ラベルのない写真や異なる名前と言語を持つピークなど、私たちのユースケースに適しています。テキストと画像のマッチングがトレーニングされているため、クエリ言語やスペルが異なっていても、トレッキング中の山の写真と短いテキストプロンプトは埋め込みとして近いものになります。</p><p>SigLIP-2 は、品質と速度のバランスが優れており、複数の入力解像度をサポートし、CPU と GPU の両方で実行されます。SigLIP-2 は、オリジナルの CLIP などの以前のモデルと比較して、屋外での写真撮影に対してより堅牢になるように設計されています。私たちのテストでは、SigLIP-2 は一貫して信頼できる結果を生成しました。また、サポートも非常に充実しており、このプロジェクトに最適な選択肢となっています。</p><p>次に、埋め込みとパワー検索を保存するためのベクトル データベースが必要です。画像埋め込みに対するコサイン kNN 検索をサポートするだけでなく、単一のクエリでジオフェンスとテキスト フィルターを適用することもサポートする必要があります。Elasticsearch はここで最適です。ベクトル (dense_vector フィールドの HNSW kNN) を非常に適切に処理し、テキスト、ベクトル、地理クエリを組み合わせたハイブリッド検索をサポートし、フィルタリングと並べ替えをすぐに使用できます。また、水平方向にも拡張できるため、数枚の写真から数千枚の写真まで簡単に拡張できます。公式の<a href="https://www.elastic.co/docs/reference/elasticsearch/clients/python">Elasticsearch Python クライアントは</a>、配管をシンプルに保ち、プロジェクトときれいに統合します。最後に、検索クエリを入力して結果を表示できる軽量のフロントエンドが必要です。簡単な Python ベースのデモには、Streamlit が最適です。ファイルのアップロード、レスポンシブな画像グリッド、並べ替えとジオフェンシングのためのドロップダウン メニューなど、必要な基本的な機能を提供します。簡単にクローンを作成してローカルで実行でき、Colab ノートブックでも動作します。</p><h2>実装</h2><h3>Elasticsearchのインデックス設計とインデックス戦略</h3><p>このプロジェクトでは、 <code>peaks_catalog</code>と<code>photos</code>の 2 つのインデックスを使用します。</p><h4>Peaks_catalogインデックス</h4><p>この索引は、エベレストベースキャンプトレッキング中に見える主要な山頂のコンパクトなカタログとして機能します。このインデックス内の各ドキュメントは、エベレスト山などの単一の山頂に対応しています。各山頂ドキュメントには、名前/エイリアス、オプションの緯度経度座標、および SigLIP-2 テキストプロンプト (+ オプションの参照画像) を組み合わせて構築された単一のプロトタイプ ベクトルが保存されます。</p><p><strong>インデックスマッピング:</strong></p><p>分野</p><p>タイプ</p><p>例</p><p>目的/注意事項</p><p>ベクトル/インデックス</p><p>id</p><p>キーワード</p><p>アマ・ダブラム</p><p>安定したスラッグ/ID</p><p>—</p><p>名前</p><p>テキスト + キーワードサブフィールド</p><p>["アマ・ダブラム"、"アマダブラム"]</p><p>エイリアス/多言語名; 正確なフィルターのためのnames.raw</p><p>—</p><p>ラトロン</p><p>ジオポイント</p><p>{"lat":27.8617,"lon":86.8614}</p><p>緯度/経度の組み合わせによるピーク GPS 座標 (オプション)</p><p>—</p><p>高度m</p><p>整数</p><p>6812</p><p>標高（オプション）</p><p>—</p><p>テキスト埋め込み</p><p>dense_vector</p><p>768</p><p>このピークのブレンドプロトタイプ（プロンプトとオプションで1～3枚の参照画像）</p><p>index:true、類似度:"cosine"、index_options: {type:"hnsw", m:16, ef_construction:128}</p><p>このインデックスは主に、画像から山頂を識別するなど、画像間の検索に使用されます。このインデックスは、テキストから画像への検索結果を強化するためにも使用されます。</p><p>要約すると、 <code>peaks_catalog</code>は「これは何の山ですか？」という質問を焦点を絞った最近傍問題に変換し、概念的理解を画像データの複雑さから効果的に分離します。</p><p><strong>peaks_catalog インデックスのインデックス戦略:</strong> EBC トレッキング中に見える最も目立つ山頂のリストを作成することから始めます。各山頂の地理的位置、名前、同義語、標高を<a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/data/peaks.yaml">yaml ファイル</a>に保存します。次のステップは、各ピークの<a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/embed_and_index_photos.py#L351">埋め込みを生成し</a>、それを<code>text_embed</code>フィールドに保存することです。堅牢な埋め込みを生成するために、次の手法を使用します。</p><ul><li><p>以下を使用してテキスト プロトタイプを作成します。</p><ul><li><p>山の名前</p></li><li><p><a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/embed_and_index_photos.py#L301">プロンプト アンサンブル</a>(複数の異なるプロンプトを使用して同じ質問に答える)、例:</p><ul><li><p>「ネパール、ヒマラヤ山脈の山頂{name}の自然写真」</p></li><li><p>「クンブ地域の{name}マーク的な山頂、高山の風景」</p></li><li><p>「 {name}山頂、雪、岩だらけの尾根」</p></li></ul></li><li><p>オプションの<a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/embed_and_index_photos.py#L333">反概念</a>（SigLIP-2 に一致しないものを指示する）: 「絵画、イラスト、ポスター、地図、ロゴ」の小さなベクトルを減算して、実際の写真に偏向させます。</p></li></ul></li><li><p>ピークの参照画像が提供されている場合は、オプションで<a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/embed_and_index_photos.py#L388C13-L388C29">画像プロトタイプを作成します</a>。</p></li></ul><p>次に、<a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/embed_and_index_photos.py#L392">テキストと画像のプロトタイプをブレンドして</a>、最終的な埋め込みを生成します。最後に、ドキュメントはすべての必須フィールドで<a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/embed_and_index_photos.py#L396">インデックス化され</a>ます。</p>def l2norm(v: np.ndarray) -&gt; np.ndarray:
    return v / (np.linalg.norm(v) + 1e-12)
def compute_blended_peak_vec(
        emb: Siglip2,
        names: List[str],
        peak_id: str,
        peaks_images_root: str,
        alpha_text: float = 0.5,
        max_images: int = 3,
) -&gt; Tuple[np.ndarray, int, int, List[str]]:
    """
    Build blended vector for a single peak.

    Returns:
      vec           : np.ndarray (L2-normalized)
      found_count   : number of reference images discovered
      used_count    : number of references used (&lt;= max_images)
      used_filenames: list of filenames used (for logging)
    """
    # 1) TEXT vector
    tv = embed_text_blend(emb, names)

    # 2) IMAGE refs: prefer folder by id; fallback to slug of the primary name
    root = Path(peaks_images_root)
    candidates = [root / peak_id]
    if names:
        candidates.append(root / slugify(names[0]))

    all_refs: List[Path] = []
    for c in candidates:
        if c.exists() and c.is_dir():
            all_refs = list_ref_images(c)
            if all_refs:
                break

    found = len(all_refs)
    used_list = all_refs[:max_images] if (max_images and found &gt; max_images) else all_refs
    used = len(used_list)

    img_v = embed_image_mean(emb, used_list) if used_list else None

    # 3) Blend TEXT and IMAGE vectors, clamp alpha to [0,1]
    a = max(0.0, min(1.0, float(alpha_text)))
    vec = l2norm(tv if img_v is None else (a * tv + (1.0 - a) * img_v)).astype("float32")
    return vec, found, used, [p.name for p in used_list]<p><code>peaks_catalog</code>インデックスからのサンプル ドキュメント:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt1219f5d0e39b512c/6a17da7c57726263161bcace/bc05fbd0c4f8d721d5170c28a3884a9eda80bb7d-1210x1132.png" alt="Elasticsearch の peaks_catalog インデックスからのサンプル ドキュメント。" /><h4>写真インデックス</h4><p>このプライマリ インデックスには、アルバム内のすべての写真に関する詳細情報が保存されます。各ドキュメントは 1 枚の写真を表し、次の情報が含まれています。</p><ul><li><p>フォトアルバム内の写真への相対パス。これを使用して、一致する画像を表示したり、検索 UI に画像を読み込んだりできます。</p></li><li><p>写真のGPSと時間情報。</p></li><li><p>SigLIP-2 によって生成された画像エンコーディング用の密なベクトル。</p></li><li><p><code>predicted_peaks</code> ピーク名でフィルタリングできます。<strong>インデックスマッピング</strong></p></li></ul><p>分野</p><p>タイプ</p><p>例</p><p>目的/注意事項</p><p>ベクター / インデックス</p><p>パス</p><p>キーワード</p><p>データ/画像/IMG_1234.HEIC</p><p>UI でサムネイル/フル画像を開く方法</p><p>—</p><p>クリップ画像</p><p>dense_vector</p><p>768</p><p>SigLIP-2画像埋め込み</p><p>index:true、類似度:"cosine"、index_options: {type:"hnsw", m:16, ef_construction:128}</p><p>予測ピーク</p><p>キーワード</p><p>["ama-dablam","pumori"]</p><p>インデックス時の上位Kの推測（安価なUXフィルター/ファセット）</p><p>—</p><p>GPS</p><p>ジオポイント</p><p>{"lat":27.96,"lon":86.83}</p><p>地理フィルターを有効にする</p><p>—</p><p>ショット時間</p><p>date</p><p>2023年10月18日09:41:00Z</p><p>撮影時間: 並べ替え/フィルター</p><p>—</p><p><strong>写真インデックスのインデックス戦略:</strong>アルバム内の写真ごとに、次の操作を実行します。
<a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/embed_and_index_photos.py#L526">画像メタデータから</a>画像<code>shot_time</code>と<code>gps</code>情報を抽出します。</p><ul><li><p><a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/embed_and_index_photos.py#L511">SigLIP-2 画像埋め込み</a>: 画像をモデルに渡し、ベクトルを L2 正規化します。埋め込みを<code>clip_image</code>フィールドに保存します。</p></li><li><p><a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/embed_and_index_photos.py#L519">ピークを予測し</a>、 <code>predicted_peaks</code>フィールドに保存します。これを行うには、まず前の手順で生成された写真の画像ベクトルを取得し、次に<code>peaks_catalog</code>インデックスの text_embed フィールドに対して簡単な kNN 検索を実行します。上位 3 ～ 4 つのピークを保持し、残りは無視します。</p></li><li><p>画像名とパスの<a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/embed_and_index_photos.py#L509">ハッシュ</a>を実行して<code>_id</code>フィールドを計算します。これにより、複数回実行した後に重複が発生しなくなります。</p></li></ul><p>写真のすべてのフィールドを決定したら、<a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/embed_and_index_photos.py#L530">一括</a>インデックスを使用して写真ドキュメントを一括でインデックスします。</p>def bulk_index_photos(
        es: Elasticsearch,
        images_root: str,
        photos_index: str = "photos",
        peaks_index: str = "peaks_catalog",
        topk_predicted: int = 5,
        batch_size: int = 200,
        refresh: str = "false",
) -&gt; None:
    """Walk a folder of images, embed + enrich, and bulk index to Elasticsearch."""
    root = Path(images_root)
    if not root.exists():
        raise SystemExit(f"Images root not found: {images_root}")

    emb = Siglip2()
    batch: List[Dict[str, Any]] = []
    n_indexed = 0

    for p in iter_images(root):
        rel = relpath_within(root, p)
        _id = id_for_path(rel)

        # 1) Image embedding (and reuse it for predicted_peaks)
        try:
            with Image.open(p) as im:
                ivec = emb.image_vec(im.convert("RGB")).astype("float32")
        except (UnidentifiedImageError, OSError) as e:
            print(f"[skip] {rel} — cannot embed: {e}")
            continue

        # 2) Predict top-k peak names
        try:
            top_names = predict_peaks(es, ivec.tolist(), peaks_index=peaks_index, k=topk_predicted)
        except Exception as e:
            print(f"[warn] predict_peaks failed for {rel}: {e}")
            top_names = []

        # 3) EXIF enrichment (safe)
        gps = get_gps_decimal(str(p))
        shot = get_shot_time(str(p))

        # 4) Build doc and stage for bulk
        doc = {"path": rel, "clip_image": ivec.tolist(), "predicted_peaks": top_names}
        if gps:
            doc["gps"] = gps
        if shot:
            doc["shot_time"] = shot

        batch.append(
            {"_op_type": "index", "_index": photos_index, "_id": _id, "_source": doc}
        )

        # 5) Periodic flush
        if len(batch) &gt;= batch_size:
            helpers.bulk(es, batch, refresh=refresh)
            n_indexed += len(batch)
            print(f"[photos] indexed {n_indexed} (last: {rel})")
            batch.clear()

    # Final flush
    if batch:
        helpers.bulk(es, batch, refresh=refresh)
        n_indexed += len(batch)
        print(f"[photos] indexed {n_indexed} total.")

    print("[done] photos indexing")<p>写真インデックスからのサンプルドキュメント:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt744b7e6326937cfc/6a17da7e6df731d3040a0da8/1dc1406ac2a97440b6804838795b3c2205c4c6b2-1080x1234.png" alt="Elasticsearch の写真インデックスからのサンプル ドキュメント。" /><p>要約すると、写真のインデックスは、アルバム内のすべての写真を格納する、高速でフィルタリング可能な kNN 対応のストアです。マッピングは意図的に最小限に抑えられており、すばやく取得し、きれいに表示し、結果を空間と時間で分割するのに十分な構造になっています。このインデックスは、両方の検索ユースケースに対応します。両方のインデックスを作成するための Python スクリプトは<a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/create_indices.py">ここに</a>あります。</p><p>以下の Kibana マップの視覚化では、写真アルバムのドキュメントが緑のドットで表示され、 <code>peaks_catalog</code>インデックスの山頂が赤い三角形で表示されています。緑のドットはエベレスト ベース キャンプのトレッキング トレイルとぴったり一致しています。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltb5bf016e8d9c3e84/6a17da80be608681f10045e6/1c75d0ed0ce53d28a94bf2f47a354e25581d2baf-1600x1402.png" alt="Kibana マップの視覚化では、写真アルバムのドキュメントが緑のドットで表示され、peaks_catalog インデックスの山頂が赤い三角形で表示されています。緑のドットは、エベレスト ベース キャンプのトレッキング トレイルとよく揃っています。" /><h2>検索ユースケース</h2><p><strong>名前による検索（テキストから画像へ）：</strong>この機能により、ユーザーはテキスト クエリを使用して山頂の写真（さらには「祈りの旗」のような抽象的な概念）を見つけることができます。これを実現するために、テキスト入力は SigLIP-2 を使用して<a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/query_by_peak_name.py#L87C5-L87C20">テキスト ベクトルに変換されます</a>。堅牢なテキストベクトル生成のために、 インデックスでテキスト埋め込みを作成するために使用したのと同じ戦略を採用しています。つまり、テキスト入力を小さな<code>peaks_catalog</code><a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/query_by_peak_name.py#L100"> プロンプトアンサンブル</a> と<a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/query_by_peak_name.py#L104"> 組み合わせ</a><a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/query_by_peak_name.py#L103"> 、小さな 反概念ベクトル</a> を減算し、<a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/query_by_peak_name.py#L104"> L2正規化</a> を適用して最終的なクエリベクトルを生成します。次に、 <code>photos.clip_image</code>フィールドで kNN<a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/query_by_peak_name.py#L140">クエリを</a>実行し、コサイン類似度に基づいて一致する上位のピークを取得して、最も近い画像を見つけます。オプションとして、クエリの一部として地理および日付<a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/query_by_peak_name.py#L152">フィルター</a>、および/または<code>photos.predicted_peaks</code>用語フィルターを適用することで、検索結果の関連性を高めることができます (以下のクエリ例を参照)。これにより、トレッキング中に実際には見えていない、似たような山頂を除外することができます。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt9bb9abf5ce64fcbb/6a17da81e8fbce20db3a17da/b5fac28ffdbedb820505365ca07df125cd01b939-946x370.png" alt="Elasticsearch でのマルチモーダルな名前による検索 (テキストから画像への検索) の仕組み。" /><p><strong>ジオフィルターを使用した Elasticsearch クエリ:</strong></p>POST photos/_search
{
  "knn": {
    "field": "clip_image",
    "query_vector": [ ... ],
    "k": 60,
    "num_candidates": 2000
  },
  "query": {
    "bool": {
      "filter": [
        { "geo_bounding_box": { "gps": { "top_left": "...", "bottom_right": "..." } } }
      ]
    }
  },
  "_source": ["path","predicted_peaks","gps","shot_time"]
}

Response (first two documents):
{
 "hits": {
   "total": {
     "value": 56,
     "relation": "eq"
   },
   "max_score": 0.5779596,
   "hits": [
     {
       "_index": "photos",
       "_id": "d01da3a1141981486c3493f6053c79e92a788463",
       "_score": 0.5779596,
       "_source": {
         "path": "IMG_2738.HEIC",
         "predicted_peaks": [
           "Pumori",
           "Kyajo Ri",
           "Khumbila",
           "Nangkartshang",
           "Kongde Ri"
         ],
         "gps": {
           "lat": 27.97116388888889,
           "lon": 86.82331111111111
         },
         "shot_time": "2023-11-03T08:07:13"
       }
     },
     {
       "_index": "photos",
       "_id": "c79d251f07adc5efaedc53561110a7fd78e23914",
       "_score": 0.5766071,
       "_source": {
         "path": "IMG_2761.HEIC",
         "predicted_peaks": [
           "Kyajo Ri",
           "Makalu",
           "Baruntse",
           "Cho Oyu",
           "Khumbila"
         ],
         "gps": {
           "lat": 27.975558333333332,
           "lon": 86.82515
         },
         "shot_time": "2023-11-03T08:51:08"
       }
     }
}<p><strong>画像による検索 (画像間):</strong>この機能を使用すると、写真内の山を識別し、写真アルバム内で同じ山の他の画像を見つけることができます。画像がアップロードされると、SigLIP-2 画像エンコーダーによって処理され、<a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/identify_from_picture_find_similar_peaks.py#L228">画像ベクトル</a>が生成されます。次に、 <code>peaks_catalog.text_embed</code>フィールドで<a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/identify_from_picture_find_similar_peaks.py#L234">kNN 検索を</a>実行し、最も一致するピーク名を特定します。次に、これらの一致するピーク名から<a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/identify_from_picture_find_similar_peaks.py#L257">テキスト ベクトルが生成され</a>、写真インデックスに対して別の<a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/identify_from_picture_find_similar_peaks.py#L263">kNN 検索が</a>実行され、対応する写真が検索されます。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltab9d16333e2a9e69/6a17da827f6f155448c099cc/3a3d5635bee7a222b95529dd7f9fbee016381610-1226x550.png" alt="Elasticsearch で画像によるマルチモーダル検索 (画像間検索) がどのように機能するかを説明します。" /><p><strong>Elasticsearchクエリ:</strong></p><p>ステップ1: 一致するピーク名を見つける</p>GET peaks_catalog/_search
{
 "knn": {
   "field": "text_embed",
   "query_vector": [...image-vector... ],
   "k": 3,
   "num_candidates": 500
 },
 "_source": [
   "id",
   "names",
   "latlon",
   "text_embed"
 ]
}


Response (first two documents):
{
 "took": 2,
 "timed_out": false,
 "_shards": {
   "total": 1,
   "successful": 1,
   "skipped": 0,
   "failed": 0
 },
 "hits": {
   "total": {
     "value": 3,
     "relation": "eq"
   },
   "max_score": 0.58039916,
   "hits": [
     {
       "_index": "peaks_catalog",
       "_id": "pumori",
       "_score": 0.58039916,
       "_source": {
         "id": "pumori",
         "names": [
           "Pumori",
           "Pumo Ri"
         ],
         "latlon": {
           "lat": 28.01472,
           "lon": 86.82806
         },
         "text_embed": [
                  ... embeddings...
         ]
       }
     },
     {
       "_index": "peaks_catalog",
       "_id": "kyajo-ri",
       "_score": 0.57942784,
       "_source": {
         "id": "kyajo-ri",
         "names": [
           "Kyajo Ri",
           "Kyazo Ri"
         ],
         "latlon": {
           "lat": 27.909167,
           "lon": 86.673611
         },
         "text_embed": [
           ... embeddings...
         ]
       }
     }
   ]
 }
}<p>ステップ 2: <code>photos</code>インデックスで検索を実行し、一致する画像を見つけます (テキストから画像への検索ユース ケースに示されているのと同じクエリ)。</p>POST photos/_search
{
 "knn": {
   "field": "clip_image",
   "query_vector": [ ...image-vector... ],
   "k": 30,
   "num_candidates": 2000
 },
 "_source": [
   "path",
   "gps",
   "shot_time",
   "predicted_peaks",
   "clip_image"
 ],
 "query": {
   "bool": {
     "filter": [
       {
         "term": {
           "predicted_peaks": "Pumori"
         }
       }
     ]
   }
 }
}


Response (first two documents):
{
 "hits": {
   "total": {
     "value": 56,
     "relation": "eq"
   },
   "max_score": 0.5779596,
   "hits": [
     {
       "_index": "photos",
       "_id": "d01da3a1141981486c3493f6053c79e92a788463",
       "_score": 0.5779596,
       "_source": {
         "path": "IMG_2738.HEIC",
         "predicted_peaks": [
           "Pumori",
           "Kyajo Ri",
           "Khumbila",
           "Nangkartshang",
           "Kongde Ri"
         ],
         "gps": {
           "lat": 27.97116388888889,
           "lon": 86.82331111111111
         },
         "shot_time": "2023-11-03T08:07:13"
       }
     },
     {
       "_index": "photos",
       "_id": "c79d251f07adc5efaedc53561110a7fd78e23914",
       "_score": 0.5766071,
       "_source": {
         "path": "IMG_2761.HEIC",
         "predicted_peaks": [
           "Kyajo Ri",
           "Makalu",
           "Baruntse",
           "Cho Oyu",
           "Khumbila"
         ],
         "gps": {
           "lat": 27.975558333333332,
           "lon": 86.82515
         },
         "shot_time": "2023-11-03T08:51:08"
       }
     }
}<h2>流線型のUI</h2><p>すべてをまとめるために、両方の検索ユースケースを実行できるシンプルな Streamlit UI を作成しました。左側のレールには、チェックボックスとミニマップ/ジオフィルターが付いた、スクロール可能なピークのリスト（ <code>photos.predicted_peaks</code>から集約）が表示されます。上部には、<strong>名前による検索</strong>ボックスと<strong>写真アップロードからの識別</strong>ボタンがあります。中央のペインには、kNN スコア、予測ピーク バッジ、キャプチャ時間を表示するレスポンシブなサムネイル グリッドがあります。各画像には、フル解像度のプレビューを表示するための<strong>画像表示</strong>ボタンが含まれています。</p><p><strong>画像をアップロードして検索:</strong>ピークを予測し、写真アルバムから一致するピークを見つけます。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd1fb2b0304a310d2/6a17da8425daab7cda08a0fa/dca540cbf5279e6d6102c5a0c0351ddd4ac91cda-1600x1112.png" alt="アマダブラム山の山頂をテキストから画像、画像から画像の両方でマルチモーダル検索できる、シンプルで合理化された UI。" /><p><strong>テキスト検索</strong>: アルバム内の一致するピークをテキストから検索します</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt496c1ae8f7886320/6a17da86abe0f2da48dfe8bd/b1e8618db746cd49ea4962d3dc73031387b975dd-1600x1166.png" alt="山頂ライブラリでテキスト検索を使用してエベレスト山の山頂を検索する方法。" /><h2>まとめ</h2><p><em><strong>アマ・ダブラムの</strong></em> <em>写真</em><em> だけ見せてもらえませんか ？ というところから始まりました。</em>小規模で実用的な<strong>マルチモーダル検索</strong>システムになりました。私たちは生のトレッキング写真を撮影し、それを<strong>SigLIP-2 埋め込み</strong>に変換し、 <strong>Elasticsearch</strong>を使用してベクトルに対して高速<strong>kNN を</strong>実行し、さらに単純な地理/時間フィルターを使用して<em>意味</em>によって適切な画像を浮かび上がらせました。途中で、私たちは 2 つのインデックス、つまり混合プロトタイプの小さな<code>peaks_catalog</code> (識別用) と、画像ベクトルと EXIF のスケーラブルな<code>photos</code>インデックス (検索用) で関心を分離しました。実用的かつ再現性があり、拡張も簡単です。</p><p>調整したい場合は、いくつかの設定を試すことができます。</p><ul><li><p><strong>クエリ時間の設定:</strong> <code>k</code> (返す近隣の数) と<code>num_candidates</code> (最終スコアリングの前に検索する範囲)。これらの設定については、<a href="https://www.elastic.co/search-labs/blog/elasticsearch-knn-and-num-candidates-strategies">こちらの</a>ブログで説明されています。</p></li><li><p><strong>インデックス時間の設定:</strong> <code>m</code> (グラフの接続性) および<code>ef_construction</code> (ビルド時間の精度とメモリ)。クエリの場合は、 <code>ef_search</code>も試してください。値が大きいほど、通常は、レイテンシを多少トレードオフして、リコール率が向上します。これらの設定の詳細については、<a href="https://www.elastic.co/search-labs/blog/hnsw-graph">このブログ</a>を参照してください。</p></li></ul><p>今後、<strong>マルチモーダル</strong>および<strong>多言語</strong>検索用のネイティブモデル/リランカーがElasticエコシステムにまもなく導入される予定です。これにより、画像/テキスト検索とハイブリッドランキングがさらに強化されるはずです<a href="https://ir.elastic.co/news/news-details/2025/Elastic-Completes-Acquisition-of-Jina-AI-a-Leader-in-Frontier-Models-for-Multimodal-and-Multilingual-Search/default.aspx?utm_source=chatgpt.com">。ir.elastic.co+1</a></p><p>これを自分で試してみたい場合は:</p><ul><li><p><strong>GitHub リポジトリ:</strong> <a href="https://github.com/navneet83/multimodal-mountain-peak-search"><em>https://github.com/navneet83/multimodal-mountain-peak-search</em></a></p></li><li><p><strong>Colab クイックスタート:</strong> <a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/notebooks/multimodal_mountain_peak_search.ipynb">https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/notebooks/multimodal_mountain_peak_search.ipynb</a></p></li></ul><p>これで私たちの旅は終わり、帰る時間になりました。これが役に立つことを願っています。これを壊した場合（または改善した場合）、何を変更したかをお聞かせください。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltdce2fff1569d2a8b/6a17da894b055dd1f24320a2/d324d1e1472f1bfbd8f25747f57bdeeb9c7f16b2-1600x1200.png" alt="" />]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/multimodal-search-siglip-2-elasticsearch</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/multimodal-search-siglip-2-elasticsearch</guid>
    <category><![CDATA[Vector Database]]></category>
    <category><![CDATA[ハイブリッド検索]]></category>
    <category><![CDATA[AI]]></category>
    <category><![CDATA[Python]]></category>
    <dc:creator><![CDATA[Navneet Kumar]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltccb66279debb05f9/6a17da8b63baffe228741b15/ffcf93358a7c5dadcea82faf3de460bf060d003c-1600x1200.png" length="0" type="image/png"/>
    <pubDate>Tue, 04 Nov 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[ハイブリッド検索再ランキングによる多言語埋め込みモデルの関連性の向上]]></title>
    <description><![CDATA[Cohere の再ランカーと Elasticsearch のハイブリッド検索を使用して、E5 多言語埋め込みモデルの検索結果の関連性を向上させる方法を学びます。]]></description>
    <content:encoded><![CDATA[<h2>はじめに</h2><p><a href="https://www.elastic.co/search-labs/blog/multilingual-embedding-model-deployment-elasticsearch">このシリーズの最後の部分</a>では、Elastic の事前トレーニング済み E5 モデル (および Hugging Face の他の多言語テキスト埋め込みモデル) のデプロイについて説明し、Elasticsearch と Kibana を使用してテキスト データから高密度のベクトル埋め込みを生成する方法について詳しく説明しました。このブログでは、これらの埋め込みの結果を調べ、多言語モデルを活用することの大きな利点を強調します。</p><p>インデックス<code>coco_multilingual</code>が作成されたので、検索を実行すると、参照用の「en」フィールドを含む複数の言語のドキュメントが表示されます。</p># GET coco_multilingual/_search
    {
       "_index": "coco_multilingual",
       "_id": "WAiXQJYBgf6odR9bLohZ",
       "_score": 1,
       "_source": {
         "description": "Ein Parkmeßgerät auf einer Straße mit Autos",
         "en": "A row of parked cars sitting next to parking meters.",
         "language": "de",
         "vector_description": {...}
       }
     },
     . . .<h2>英語で検索する</h2><p>英語で検索を実行して、どれくらいうまくいくか確認してみましょう。</p>GET coco_multi/_search
{
"size": 10,
"_source": [
  "description", "language", "en"
],
"knn": {
  "field": "vector_description.predicted_value",
  "k": 10,
  "num_candidates": 100,
  "query_vector_builder": {
    "text_embedding": {
      "model_id": ".multilingual-e5-small_linux-x86_64_search",
      "model_text": "query: kitty"
    }
  }
}
}{
       "_index": "coco_multi",
       "_id": "JQiXQJYBgf6odR9b6Yz0",
       "_score": 0.9334303,
       "_source": {
         "description": "Eine Katze, die in einem kleinen, gepackten Koffer sitzt.",
         "en": "A brown and white cat is in a suitcase.",
         "language": "de"
       }
     },
      {
       "_index": "coco_multi",
       "_id": "3AiXQJYBgf6odR9bFod6",
       "_score": 0.9281012,
       "_source": {
         "description": "Una bambina che tiene un gattino vicino a una recinzione blu.",
         "en": "A little girl holding a kitten next to a blue fence.",
         "language": "it"
       }
     },
     . . .<p>ここでは、クエリは一見単純に見えますが、内部的にはすべての言語のすべてのドキュメントにわたって「kitty」という単語の数値埋め込みを検索しています。また、ベクトル検索を実行しているため、「kitty」に関連する可能性のあるすべての単語を意味的に検索できます。「cat」、「kitten」、「feline」、「gatto」（イタリア語）、「mèo」（ベトナム語）、고양이（韓国語）、猫（中国語）などです。その結果、クエリが英語であっても、他のすべての言語でコンテンツを検索できるようになります。たとえば、「a kitty l <code>ying on something</code>を検索すると、イタリア語、オランダ語、ベトナム語のドキュメントも返されます。効率について話しましょう!</p><h2>他の言語でコンテンツを検索する</h2>GET coco_multi/_search
{  
 "size": 100,
 "_source": [
   "description", "language", "en"
 ],
 "knn": {
   "field": "vector_description.predicted_value",
   "k": 50,
   "num_candidates": 1000,
   "query_vector_builder": {
     "text_embedding": {
       "model_id": ".multilingual-e5-small_linux-x86_64_search",
       "model_text": "query: kitty lying on something"
     }
   }
 }
}{
 "description": "A black kitten lays on her side beside remote controls.",
 "en": "A black kitten lays on her side beside remote controls.",
 "language": "en"
},
{
 "description": "un gattino sdraiato su un letto accanto ad alcuni telefoni ",
 "en": "A black kitten lays on her side beside remote controls.",
 "language": "it"
},
{
 "description": "eine Katze legt sich auf ein ausgestopftes Tier",
 "en": "a cat lays down on a stuffed animal",
 "language": "de"
},
{
 "description": "Một chú mèo con màu đen nằm nghiêng bên cạnh điều khiển từ xa.",
 "en": "A black kitten lays on her side beside remote controls.",
 "language": "vi"
}
. . .<p>同様に、韓国語で「cat」（「고양이」）のキーワード検索を実行しても、意味のある結果が返されます。驚くべきことに、このインデックスには韓国語の文書がまったくありません。</p>GET coco_multi/_search
{
 "size": 100,
 "_source": [
   "description", "language", "en"
 ],
 "knn": {
   "field": "vector_description.predicted_value",
   "k": 50,
   "num_candidates": 1000,
   "query_vector_builder": {
     "text_embedding": {
       "model_id": ".multilingual-e5-small_linux-x86_64_search",
       "model_text": "query: 고양이"
     }
   }
 }
} {
       {
         "description": "eine Katze legt sich auf ein ausgestopftes Tier",
         "en": "a cat lays down on a stuffed animal",
         "language": "de"
       }
     },
     {
       {
         "description": "Một con chó và con mèo đang ngủ với nhau trên một chiếc ghế dài màu cam.",
         "en": "A dog and cat lying  together on an orange couch. ",
         "language": "vi"
       }
     },<p>これが機能するのは、埋め込みモデルが意味を共有セマンティック空間で表現し、インデックス付けされたキャプションとは異なる言語でのクエリでも関連する画像を取得できるためです。</p><h2>ハイブリッド検索と再ランキングによる関連性の高い検索結果の向上</h2><p>関連する結果が期待どおりに表示されたことを嬉しく思います。しかし、現実の世界では、たとえば、最も関連性の高い上位 5 ～ 10 件の結果に絞り込む必要がある e コマースや RAG アプリケーションでは、再ランク付けモデルを使用して最も関連性の高い結果を優先することができます。</p><p>ここで、ベトナム語で「猫の色は何色ですか？」と尋ねるクエリを実行すると、多くの結果が表示されますが、上位 1 つまたは 2 つが最も関連性が高いとは限りません。</p>GET coco_multi/_search
{
 "size": 20,
 "_source": [
   "description",
   "language",
   "en"
 ],
 "knn": {
   "field": "vector_description.predicted_value",
   "k": 20,
   "num_candidates": 1000,
   "query_vector_builder": {
     "text_embedding": {
       "model_id": ".multilingual-e5-small_linux-x86_64_search",
       "model_text": "query: con mèo màu gì?"
     }
   }
 }
}<p>結果にはすべて「猫」または何らかの形の色が言及されています。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt979f5944b1708042/6a17ef76420229ff6829f6aa/33e1e887dbbdd1066cfedc7375f5e3b46538529e-859x847.png" alt="" /><p>では、それを改善しましょう!<a href="https://cohere.com/blog/rerank-3pt5">Cohere</a>の多言語再ランク付けモデルを統合して、質問に対応する推論を改善しましょう。</p>PUT _inference/rerank/cohere_rerank
{
 "service": "cohere",
 "service_settings": {
   "api_key": "your_api_key",
   "model_id": "rerank-v3.5"
 },
 "task_settings": {
   "top_n": 10,
   "return_documents": true
 }
}


GET coco_multi/_search
{
"size": 10,
"_source": [
  "description",
  "language",
  "en"
],
"retriever": {
  "text_similarity_reranker": {
    "retriever": {
      "rrf": {
        "retrievers": [
          {
            "knn": {
              "field": "vector_description.predicted_value",
              "k": 50,
              "num_candidates": 100,
              "query_vector_builder": {
                "text_embedding": {
                  "model_id": ".multilingual-e5-small_linux-x86_64_search",
                  "model_text": "query: con mèo màu gì?" // English: What color is the cat?
                }
              }
            }
          }
        ],
        "rank_window_size": 100,
        "rank_constant": 0
      }
    },
    "field": "description",
    "inference_id": "cohere_rerank",
    "inference_text": "con mèo màu gì?"
  }
}
} {
       "_index": "coco_multi",
       "_id": "rQiYQJYBgf6odR9bBYyH",
       "_score": 1.5501487,
       "_source": {
         "description": "Hai cái điện thoại được đặt trên một cái chăn cạnh một con mèo con màu đen.",
         "en": "A black kitten lays on her side beside remote controls.",
         "language": "vi"
       }
     },
     {
       "_index": "coco_multi",
       "_id": "swiXQJYBgf6odR9b04uf",
       "_score": 1.5427427,
       "_source": {
         "description": "Một con mèo sọc nâu nhìn vào máy quay.", // Real translation: A brown striped cat looks at the camera 
         "en": "This cat is sitting on a porch near a tire.",
         "language": "vi"
       }
     },<p>これで、上位の結果により、アプリケーションは子猫の色が黒か茶色で縞模様であると自信を持って答えることができます。ここでさらに興味深いのは、ベクトル検索によって、元のデータセットの英語のキャプションの欠落が実際に検出されたことです。参照の英語翻訳ではその詳細が抜けていたにもかかわらず、茶色の縞模様の猫を見つけることができます。これがベクトル検索の威力です。</p><h2>まとめ</h2><p>このブログでは、多言語埋め込みモデルの有用性と、Elasticsearch を活用してモデルを統合し埋め込みを生成する方法、ハイブリッド検索と再ランク付けによって関連性と精度を効果的に向上させる方法について説明しました。独自の<a href="https://cloud.elastic.co/registration?onboarding_token=vectorsearch&amp;cta=cloud-registration&amp;tech=trial&amp;plcmt=article%20content&amp;pg=search-labs">クラウド クラスターを作成し、</a>選択した言語とデータセットで<a href="https://www.elastic.co/docs/explore-analyze/machine-learning/nlp/ml-nlp-e5">すぐに使用できる E5 モデルを使用して、多言語セマンティック検索を</a>試すことができます。</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/multilingual-embedding-model-hybrid-search-reranking</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/multilingual-embedding-model-hybrid-search-reranking</guid>
    <category><![CDATA[Vector Database]]></category>
    <category><![CDATA[運用]]></category>
    <dc:creator><![CDATA[Quynh Nguyen]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltf625e3f63fcd9f54/6a17ef7796142a61f8eb1bcd/d341b04acecc8eeec321f5404e1643447ecc8526-720x420.png" length="0" type="image/png"/>
    <pubDate>Mon, 03 Nov 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Elasticsearch に多言語埋め込みモデルをデプロイする]]></title>
    <description><![CDATA[Elasticsearch でベクトル検索とクロスリンガル検索を行うために、e5 多言語埋め込みモデルをデプロイする方法を学びます。]]></description>
    <content:encoded><![CDATA[<h2>はじめに</h2><p>世界中のユーザーがいる世界では、言語間情報検索 (CLIR) が非常に重要です。CLIR を使用すると、検索を 1 つの言語に限定するのではなく、<em>あらゆる</em>言語で情報を検索できるため、ユーザー エクスペリエンスが向上し、操作が効率化されます。電子商取引の顧客が自分の言語で商品を検索でき、事前にデータをローカライズする必要なく、適切な結果が表示されるグローバル市場を想像してみてください。あるいは、情報源が別の言語であっても、学術研究者がニュアンスや複雑さを含めて母国語で論文を検索できる場所です。</p><p>多言語テキスト埋め込みモデルを使用すると、まさにそれが実現できます。埋め込みは、テキストの意味を数値ベクトルとして表現する方法です。これらのベクトルは、同様の意味を持つテキストが高次元空間内で互いに近く配置されるように設計されています。多言語テキスト埋め込みモデルは、特に、異なる言語間で同じ意味を持つ単語やフレーズを同様のベクトル空間にマッピングするように設計されています。</p><p>オープンソースの Multilingual E5 のようなモデルは、多くの場合、対照学習などの手法を使用して、大量のテキスト データでトレーニングされます。このアプローチでは、モデルは類似した意味を持つテキストのペア (肯定的なペア) と類似しない意味を持つテキストのペア (否定的なペア) を区別することを学習します。モデルは、正のペア間の類似性が最大化され、負のペア間の類似性が最小化されるように、生成するベクトルを調整するようにトレーニングされます。多言語モデルの場合、このトレーニング データには、相互に翻訳された異なる言語のテキスト ペアが含まれており、モデルが複数の言語の共有表現空間を学習できるようになります。結果として得られる埋め込みは、クエリの言語に関係なく、テキスト埋め込み間の類似性を使用して関連するドキュメントを検索するクロスリンガル検索を含むさまざまな NLP タスクに使用できます。</p><h2>多言語ベクター検索のメリット</h2><ul><li><p><strong>ニュアンス</strong>: ベクター検索は、キーワードのマッチングを超えて、意味を捉えることに優れています。これは、文脈や言語の微妙なニュアンスを理解する必要があるタスクにとって非常に重要です。</p></li><li><p><strong>クロスリンガル理解</strong>: クエリとドキュメントが異なる語彙を使用している場合でも、言語間で効果的な情報検索を可能にします。</p></li><li><p><strong>関連性</strong>: クエリとドキュメント間の概念的な類似性に焦点を当てることで、より関連性の高い結果を提供します。</p></li></ul><p>たとえば、さまざまな国における「ソーシャル メディアが政治的議論に与える影響」を研究している学術研究者を考えてみましょう。ベクトル検索を使用すると、「l'impatto dei social media sul discorso politico」（イタリア語）または「ảnh hưởng của mạng xã hội đối với diễn ngôn chính trị」（ベトナム語）などのクエリを入力し、英語、スペイン語などで関連する論文を見つけることができます。他のインデックス付き言語。これは、ベクトル検索では、正確なキーワードを含む論文だけでなく、ソーシャル メディアの政治への影響の<em>概念</em>について議論している論文も特定されるためです。これにより、研究の幅と深さが大幅に向上します。</p><h2>使い始める</h2><p>Elasticsearch を使用して CLIR を設定する方法 (すぐに使用できる E5 モデルを使用) を次に示します。複数の言語の画像キャプションを含む<a href="https://huggingface.co/datasets/romrawinjp/multilingual-coco">オープンソースの多言語 COCO データセットを</a>使用して、2 種類の検索を視覚化します。</p><ol><li><p>1つの英語データセット上の他の言語のクエリと検索用語、および</p></li><li><p>複数の言語のドキュメントを含むデータセットに対する複数の言語でのクエリ。</p></li></ol><p>次に、ハイブリッド検索と再ランキングの力を活用して、検索結果をさらに改善します。</p><h2>要件</h2><ul><li><p>Python 3.6以上</p></li><li><p>Elasticsearch 8以上</p></li><li><p>Elasticsearch Pythonクライアント: pip install elasticsearch</p></li></ul><h2>データセット</h2><p><a href="https://huggingface.co/datasets/romrawinjp/multilingual-coco">COCO データセット</a>は、大規模なキャプション データセットです。データセット内の各画像には複数の異なる言語でキャプションが付けられており、言語ごとに複数の翻訳が利用可能です。デモンストレーションの目的で、各翻訳を個別のドキュメントとしてインデックス化し、参照用に最初の利用可能な英語の翻訳もインデックス化します。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltfc7e508e9a7dffe8/6a17f3e2b1e113249579f394/d4f0632529c71a22fbdecf21c9f4f0bb64b8e69c-1600x567.png" alt="" /><h3>ステップ1: 多言語COCOデータセットをダウンロードする</h3><p>ブログを簡素化し、理解しやすくするために、ここでは、単純な API 呼び出しを使用して、restval の最初の 100 行をローカル JSON ファイルに読み込みます。あるいは、HuggingFace のライブラリ データセットを使用して、完全なデータセットまたはデータセットのサブセットを読み込むこともできます。</p>import requests
import json
import os
### Download multilingual coco dataset into a json file (for easy viewing)
### Here we are retrieving first 100 rows for this example
### Alternatively, you can use `datasets` library from Hugging Face
url = "https://datasets-server.huggingface.co/rows?dataset=romrawinjp%2Fmultilingual-coco&amp;config=default&amp;split=restval&amp;offset=0&amp;length=100"
response = requests.get(url)


if response.status_code == 200:
   data = response.json()
   output_file = "multilingual_coco_sample.json" 
   ### Loading the downloaded content into a json file locally
   with open(output_file, "w", encoding="utf-8") as f:
       json.dump(data, f, indent=4, ensure_ascii=False)
   print(f"Data successfully downloaded and saved to {output_file}")
else:
   print(f"Failed to download data: {response.status_code}")
   print(response.text)<p>データが JSON ファイルに正常に読み込まれると、次のようなものが表示されます。</p><p><code>Data successfully downloaded and saved to multilingual_coco_sample.json</code></p><h3>ステップ2: (Elasticsearchを起動) Elasticsearchでデータをインデックスする</h3><p>a) ローカル Elasticsearch サーバーを起動します。</p><p>b) Elasticsearch クライアントを起動します。</p>from elasticsearch import Elasticsearch
from getpass import getpass


# Initialize Elasticsearch client
es = Elasticsearch(getpass("Host: "), api_key=getpass("API Key: "))


index_name = "coco"


# Create the index if it doesn't exist
if not es.indices.exists(index=index_name):
   es.indices.create(index=index_name, body=mapping)<p>c) インデックスデータ</p># Load the JSON data
with open('./multilingual_coco_sample.json', 'r') as f:
   data = json.load(f)


rows = data["rows"]
# List of languages to process
languages = ["en", "es", "de", "it", "vi", "th"]


# For each image, we will process each individual caption as its own document
bulk_data = []
for data in rows:
   row = data["row"]
   image = row.get("image")
   image_url = image["src"]


   # Process each language
   for lang in languages:
       # Skip if language not present in this row
       if lang not in row:
           continue


       # Get all descriptions for this language
 # along with first available English caption for reference
       descriptions = row[lang]
       first_eng_caption = row["en"][0]


       # Prepare bulk indexing data
       for description in descriptions:
           if description == "":
               continue
           # Add index operation
           bulk_data.append(
               {"index": {"_index": index_name}}
           )
           # Add document
           bulk_data.append({
               "language": lang,
               "description": description,
               "en": first_eng_caption,
               "image_url": image_url,
           })


# Perform bulk indexing
if bulk_data:
   try:
       response = es.bulk(operations=bulk_data)
       if response["errors"]:
           print("Some documents failed to index")
       else:
           print(f"Successfully bulk indexed {len(bulk_data)} documents")
   except Exception as e:
       print(f"Error during bulk indexing: {str(e)}")


print("Indexing complete!")<p>データがインデックスされると、次のようなものが表示されます。</p><p><code>Successfully bulk indexed 4840 documents</code></p><p><code>Indexing complete!</code></p><h3>ステップ3: E5トレーニング済みモデルをデプロイする</h3><p>Kibanaで、スタック管理 &gt;<strong>トレーニング済みモデル</strong>ページに移動し、.multilingual-e5-small_linux-x86_64の<strong>デプロイを</strong>クリックします。オプション。この E5 モデルは、linux-x86_64 向けに最適化された小型の多言語モデルで、そのまま使用できます。「デプロイ」をクリックすると、デプロイ設定または vCPU 構成を調整できる画面が表示されます。簡単にするために、デフォルト オプションを使用し、適応型リソースを選択します。これにより、使用状況に応じてデプロイメントが自動的にスケーリングされます。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltfbc09867063a8e6f/6a17f3e3148009a295b4889d/95cd8f352425d1db2d04b00c3c88d1e71d1ef19a-1600x440.png" alt="" /><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt264a2016341e9b6f/6a17f3f0e8fbce18de3a1aa7/1599d99949dda8267acc58f400a403a3af5373ef-1600x655.png" alt="" /><p>オプションとして、他のテキスト埋め込みモデルを使用することもできます。たとえば、BGE-M3 を使用するには、 <a href="https://www.elastic.co/docs/reference/elasticsearch/clients/eland/machine-learning#ml-nlp-pytorch">Elastic の Eland Python クライアント</a>を使用して HuggingFace からモデルをインポートできます。</p>export MODEL_ID="bge-m3"
export HUB_MODEL_ID="BAAI/bge-m3"
export CLOUD_ID={{CLOUD_ID}}
export ES_API_KEY={{API_KEY}}
docker run -it --rm docker.elastic.co/eland/eland \
eland_import_hub_model --cloud-id $CLOUD_ID --es-api-key $ES_API_KEY --hub-model-id $HUB_MODEL_ID --es-model-id $MODEL_ID --task-type text_embedding --start<p>次に、「トレーニング済みモデル」ページに移動し、インポートしたモデルを必要な構成でデプロイします。</p><h3>ステップ4: デプロイされたモデルを使用して元のデータをベクトル化または埋め込みを作成する</h3><p>埋め込みを作成するには、まずテキストを取得して推論テキスト埋め込みモデルに通すことができる取り込みパイプラインを作成する必要があります。これは、Kibana のユーザー インターフェースまたは Elasticsearch の API を通じて実行できます。</p><p><strong>Kibana インターフェース経由でこれを行うには</strong>、トレーニング済みモデルをデプロイした後、 <strong>[テスト]</strong>ボタンをクリックします。これにより、生成された埋め込みをテストおよびプレビューできるようになります。<code>coco</code>の新しいデータビューを作成しますインデックスを作成し、データ ビューを新しく作成した coco データ ビューに設定し、フィールドを<code>description</code>に設定します。これは、埋め込みを生成するフィールドだからです。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt24a2b9a9a5111bbc/6a17f3f13e9e452c7fba15d5/cfe189e13dc118d325e7fb90bdace0c912e29f51-1088x1600.png" alt="" /><p>それは素晴らしいですね！これで、取り込みパイプラインの作成に進み、元のドキュメントのインデックスを再作成し、パイプラインに渡して、埋め込みを含む新しいインデックスを作成できます。これを実現するには、 <strong>「パイプラインの作成」を</strong>クリックします。これにより、埋め込みの作成に必要なプロセッサが自動的に入力され、パイプラインの作成プロセスがガイドされます。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte39c5ad52702103d/6a17f3f3e9ea87dd05a9c734/1e043c1c3279b66fbdf19c06b41e76e613043998-1600x1126.png" alt="" /><p>ウィザードでは、データの取り込みと処理中に障害を処理するために必要なプロセッサを自動的に入力することもできます。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt63e002a79d177cf4/6a17f3f596142a3e91eb1c48/8804d31b4f869078e3b2245040bbb0ab1720a94a-1600x1084.png" alt="" /><p>それでは、取り込みパイプラインを作成しましょう。パイプラインに<code>coco_e5</code>という名前を付けます。パイプラインが正常に作成されたら、ウィザードで元のインデックス付きデータを新しいインデックスに再インデックスすることで、パイプラインをすぐに使用して埋め込みを生成できます。プロセスを開始するには、 <strong>「再インデックス」</strong>をクリックします。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt39243d9ad1779fdf/6a17f3f696142a13eaeb1c4c/e34b1b18f5b24420d4581fe4d657c569926c2023-1600x1126.png" alt="" /><h2>より複雑な構成の場合は、Elasticsearch API を使用できます。</h2><p>一部のモデルでは、モデルのトレーニング方法により、埋め込みを生成する前に実際の入力の先頭または末尾に特定のテキストを追加する必要がある場合があります。そうしないと、パフォーマンスが低下します。</p><p>たとえば、e5 の場合、モデルは入力テキストが「passage: {content of passage} 」に続くことを想定します。これを実現するために、取り込みパイプラインを活用しましょう。新しい取り込みパイプライン<strong>vectorize_descriptions を</strong>作成します。このパイプラインでは、新しい一時的な<code>temp_desc</code>フィールドを作成し、 <code>description</code>テキストの先頭に「passage:」を追加し、モデルで<code>temp_desc</code>を実行してテキスト埋め込みを生成し、 <code>temp_desc</code>を削除します。</p>PUT _ingest/pipeline/vectorize_descriptions
{
"description": "Pipeline to run the descriptions text_field through our inference text embedding model",
"processors": [
 {
   "set": {
     "field": "temp_desc",
     "value": "passage: {{description}}"
   }
 },
 {
   "inference": {     
"field_map": {
       "temp_desc": "text_field"
     },
     "model_id": ".multilingual-e5-small_linux-x86_64_search",
     "target_field": "vector_description"
   }
 },
 {
   "remove": {
     "field": "temp_desc"
   }
 }
]
}<p>さらに、生成されたベクトルに使用する<a href="https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/dense-vector#dense-vector-quantization">量子化の種類</a>を指定することもできます。デフォルトでは、Elasticsearch は<code>int8_hnsw</code>を使用しますが、ここでは各次元を 1 ビットの精度に削減する<a href="https://www.elastic.co/search-labs/blog/better-binary-quantization-lucene-elasticsearch">Better Binary Quantization</a> (または<code>bqq_hnsw</code> ) を使用します。これにより、精度は犠牲になりますが、メモリ フットプリントが 96% (または 32 倍) 削減されます。後で再ランク付けを使用して精度の低下を改善することが分かっているため、この量子化タイプを選択しています。</p><p>そのためには、 <strong>coco_multi</strong>という名前の新しいインデックスを作成し、マッピングを指定します。ここでの魔法は vector_description フィールドにあり、そこで<strong>index_options</strong>のタイプを<strong>bbq_hnsw</strong>に指定します。</p>PUT coco_multi
{
 "mappings": {
   "properties": {
     "description": {
       "type": "text"
     },
     "en": {
       "type": "text"
     },
     "image_url": {
       "type": "keyword"
     },
     "language": {
       "type": "keyword"
     },
     "vector_description.predicted_value": {
       "type": "dense_vector",
       "dims": 384,
       "index": "true",
       "similarity": "cosine",
       "index_options": {
         "type": "bbq_hnsw" 
       }
     }
   }
 }
}<p>これで、説明フィールドを「ベクトル化」または埋め込みを作成する取り込みパイプラインを使用して、元のドキュメントを新しいインデックスに再インデックスできます。</p>POST _reindex?wait_for_completion=false
{
 "source": {
   "index": "coco"
 },
 "dest": {
   "index": "coco_multilingual",
   "pipeline": "vectorize_descriptions"
 }
}<p>以上です！Elasticsearch と Kibana を使用して多言語モデルを正常にデプロイし、Kibana ユーザー インターフェースまたは Elasticsearch API を使用して Elastic でデータにベクトル埋め込みを作成する方法を段階的に学習しました。このシリーズの第 2 部では、多言語モデルを使用した場合の結果とニュアンスについて説明します。その間、独自の<a href="https://cloud.elastic.co/registration?onboarding_token=vectorsearch&amp;cta=cloud-registration&amp;tech=trial&amp;plcmt=article%20content&amp;pg=search-labs">クラウド クラスターを作成し</a>、選択した言語とデータセットで<a href="https://www.elastic.co/docs/explore-analyze/machine-learning/nlp/ml-nlp-e5">すぐに使用できる E5 モデルを使用して多言語セマンティック検索を</a>試すことができます。</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/multilingual-embedding-model-deployment-elasticsearch</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/multilingual-embedding-model-deployment-elasticsearch</guid>
    <category><![CDATA[Vector Database]]></category>
    <category><![CDATA[運用]]></category>
    <dc:creator><![CDATA[Quynh Nguyen]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt59254226694f93a6/6a17f3f81480098988b488a1/8f2aa7bebb6b2f701e274ba7282273f9ab4abed6-720x432.png" length="0" type="image/png"/>
    <pubDate>Wed, 22 Oct 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[ベクター検索フィルタリング: 関連性を保つ]]></title>
    <description><![CDATA[クエリに最も類似した結果を見つけるためにベクトル検索を実行するだけでは不十分です。検索結果を絞り込むには、フィルタリングが必要になることがよくあります。この記事では、Elasticsearch と Apache Lucene でのベクトル検索のフィルタリングの仕組みについて説明します。]]></description>
    <content:encoded><![CDATA[<p>関連する結果を見つけるには、ベクター検索だけでは不十分です。検索結果を絞り込み、無関係な結果を除外するのに役立つフィルタリング基準を使用することは非常に一般的です。</p><p>ベクトル検索でのフィルタリングの仕組みを理解すると、パフォーマンスとリコールのトレードオフのバランスをとるのに役立ちます。また、フィルタリングの使用時にベクトル検索のパフォーマンスを高めるために使用される最適化のいくつかを知ることもできます。</p><h2>なぜフィルタリングするのですか?</h2><p>ベクトル検索は、大規模なデータセット内で関連情報を検索する方法に革命をもたらし、クエリと意味的に類似する項目を発見できるようになりました。</p><p>ただし、類似アイテムを見つけるだけでは十分ではありません。多くの場合、特定の基準や属性に基づいて検索結果を絞り込む必要があります。</p><p>電子商取引ストアで商品を検索していると想像してください。純粋なベクター検索では視覚的に類似したアイテムが表示される場合がありますが、価格帯、ブランド、在庫状況、または顧客評価でフィルタリングすることもできます。フィルタリングがなければ、類似した製品が大量に表示され、探しているものを正確に見つけることが難しくなります。</p><p>フィルタリングにより、検索結果を正確に制御できるようになり、取得された項目が意味的に一致するだけでなく、必要な要件をすべて満たすことが保証されます。これにより、より正確で効率的、そしてユーザーフレンドリーな検索エクスペリエンスが実現します。</p><p>Elasticsearch と Apache Lucene が優れているのはこの点です。さまざまなデータ タイプにわたって効果的なフィルタリングを使用することが、他のベクター データベースとの主な違いの 1 つです。</p><h2>正確なベクトル検索のためのフィルタリング</h2><p>正確なベクトル検索を実行するには、主に 2 つの方法があります。</p><ul><li><p>dense_vector フィールドに<code>flat</code>インデックス タイプを使用します。これにより、 <code>knn</code>検索では近似検索ではなく正確な検索が使用されるようになります。</p></li><li><p>ベクトル関数を使用してスコアを計算する<a href="https://www.elastic.co/docs/reference/query-languages/query-dsl/query-dsl-script-score-query#vector-functions">script_score クエリ</a>を使用します。これはどのインデックス タイプでも使用できます。</p></li></ul><p>正確なベクトル検索を実行すると、すべてのベクトルがクエリと比較されます。このシナリオでは、フィルターを通過するベクトルのみを比較する必要があるため、フィルタリングによってパフォーマンスが向上します。</p><p>いずれにしてもすべてのベクトルが考慮されるため、結果の品質には影響しません。興味のない結果を事前にフィルタリングするだけで、操作の数を減らすことができます。</p><p>これは非常に重要です。適用したフィルターによって少数のドキュメントが生成される場合、近似検索ではなく正確な検索を実行するとパフォーマンスが向上する可能性があるためです。</p><p>経験則としては、フィルターを通過するドキュメントが 10,000 個未満の場合は完全一致検索を使用します。<a href="https://www.elastic.co/search-labs/blog/better-binary-quantization-lucene-elasticsearch">BBQ</a>インデックスは比較が非常に高速なので、ベース インデックスが 10 万未満の場合は、完全一致検索を使用するのが合理的です。詳細については、<a href="https://www.elastic.co/search-labs/blog/knn-exact-vs-approximate-search">このブログ投稿</a>をご覧ください。</p><p>フィルターが常に非常に制限的である場合は、HNSW ベースのインデックス タイプではなく<code>flat</code>インデックス タイプを使用して、近似検索ではなく完全検索に重点を置いたインデックス作成を検討してください。詳細については、 <a href="https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/dense-vector#dense-vector-params">index_options のプロパティ</a>を参照してください。</p><h2>近似ベクトル検索のためのフィルタリング</h2><p>近似ベクトル検索を実行する場合、結果の精度とパフォーマンスをトレードオフします。HNSW のようなベクトル検索データ構造は、数百万のベクトルのおおよその最近傍を効率的に検索します。計算コストのかかるベクトル比較を最小限に抑えて、最も類似したベクトルを取得することに重点を置いています。</p><p>つまり、他のフィルタリング属性はベクター データの一部ではないということです。さまざまなデータ タイプには、用語辞書、投稿リスト、ドキュメント値など、検索やフィルタリングに効率的な独自のインデックス構造があります。</p><p>これらのデータ構造はベクトル検索メカニズムとは別であるため、ベクトル検索にフィルタリングをどのように適用すればよいでしょうか?フィルターには、ベクター検索の後にフィルターを適用する (ポストフィルタリング) か、ベクター検索の前にフィルターを適用する (プレフィルタリング) という 2 つのオプションがあります。</p><p>それぞれの選択肢には長所と短所があります。詳しく見ていきましょう！</p><h3>ポストフィルタリング</h3><p>ポストフィルタリングは、ベクトル検索が完了した後にフィルターを適用します。これは、最も類似した上位 k 個のベクトル結果が見つかった後にフィルターが適用されることを意味します。</p><p>明らかに、結果にフィルターを適用した後、 k 件未満の結果が返される可能性があります。もちろん、ベクトル検索 (より高い k 値) からより多くの結果を取得できますが、フィルターを適用した後に k 以上の結果が得られるかどうかはわかりません。</p><p>ポストフィルタリングの利点は、ベクトル検索の実行時の動作を変更しないことです。つまり、ベクトル検索はフィルタリングを認識しません。ただし、取得される結果の最終的な数は変わります。</p><p>以下は、 <a href="https://www.elastic.co/docs/reference/query-languages/query-dsl/query-dsl-knn-query">knn クエリ</a>を使用したポストフィルタリングの例です。フィルタリング句が knn クエリとは別であることを確認します。</p>{
  "query": {
    "bool": {
      "must": {
        "knn": {
          "field": "image-vector",
          "query_vector": [54, 10, -2],
          "k": 5,
          "num_candidates": 50
        }
      },
      "filter": {
        "term": {
          "file-type": "png"
        }
      }
    }
  }
}<p><a href="https://www.elastic.co/docs/reference/elasticsearch/rest-apis/filter-search-results#post-filter">post-filter</a>を使用した knn 検索では、ポストフィルタリングも利用できます。</p>{
  "knn": {
    "field": "image-vector",
    "query_vector": [54, 10, 2],
    "k": 5,
    "num_candidates": 50
  },
  "post_filter": {
    "term": {
      "file-type": "png"
    }
  }
}<p>knn 検索では明示的なポストフィルター セクションを使用する必要があることに注意してください。ポストフィルターを使用しない場合、knn 検索はポストフィルターを実行する代わりに<a href="https://www.elastic.co/docs/solutions/search/vector/knn#_combine_approximate_knn_with_other_features">、最近傍の結果を他のクエリまたはフィルターと組み合わせます</a>。</p><h3>プレフィルタリング</h3><p>ベクトル検索の前にフィルターを適用すると、まずフィルターを満たすドキュメントが取得され、次にその情報がベクトル検索に渡されます。</p><p>Lucene は<a href="https://github.com/apache/lucene/blob/7a60d7ce92392181e137361336e5196bd486cdd9/lucene/core/src/java/org/apache/lucene/util/BitSet.java">BitSets</a>を使用して、フィルター条件を満たすドキュメントを効率的に保存します。次に、ベクトル検索は条件を満たすドキュメントを考慮して、HNSW グラフを走査します。候補を結果に追加する前に、それが有効なドキュメントの BitSet に含まれているかどうかを確認します。</p><p>ただし、有効なドキュメントでない場合でも、候補を調査してクエリと比較する必要があります。HNSW の有効性は、グラフ内のベクトル間の接続に依存します。つまり、候補の探索を停止すると、その近傍もスキップされる可能性があることを意味します。</p><p>ガソリンスタンドに行くために車を運転するのを想像してください。ガソリンスタンドのない道路を無視すると、目的地にたどり着く可能性は低くなります。他の道はあなたにとって必要なものではないかもしれませんが、目的地まであなたを<em>繋いで</em>くれます。HNSW グラフ上のベクトルも同様です。</p><p>したがって、プレフィルタリングを適用すると、フィルタを適用しない場合よりもパフォーマンスが低下します。検索で訪れる<em>すべての</em>ベクトルに対して作業を実行し、フィルターに一致しないベクトルを破棄する必要があります。私たちは、トップ k の結果を得るために、より多くの作業とより多くの時間をかけています。</p><p>以下は、Elasticsearch クエリ DSL での事前フィルタリングの例です。フィルタリング句が knn セクションの一部になっていることを確認します。</p>{
  "knn": {
    "field": "image-vector",
    "query_vector": [54, 10, -2],
    "k": 5,
    "num_candidates": 50,
    "filter": {
      "term": {
        "file-type": "png"
      }
    }
  }
}<p>事前フィルタリングは、 <a href="https://www.elastic.co/docs/solutions/search/vector/knn#knn-search-filter-example">knn 検索</a>と<a href="https://www.elastic.co/docs/reference/query-languages/query-dsl/query-dsl-knn-query#knn-query-filtering">knn クエリの</a>両方で利用できます。</p>{
  "query": {
    "knn": {
      "field": "image-vector",
      "query_vector": [-5, 9, -12],
      "k": 5,
      "filter": {
        "term": {
          "file-type": "png"
        }
      }
    }
  }
}<h4>プレフィルタリングの最適化</h4><p>プレフィルタリングのパフォーマンスを確保するために適用できる最適化がいくつかあります。</p><p>フィルターの制限が厳しい場合は、完全一致検索に切り替えることができます。比較するベクトルが少ない場合は、フィルターを満たす少数のドキュメントに対して正確な検索を実行する方が高速です。</p><p>これは、 <a href="https://github.com/apache/lucene/blob/eb876b618da5d04c1ad14b04a48321638318493a/lucene/core/src/java/org/apache/lucene/search/AbstractKnnVectorQuery.java#L218">Lucene</a>および Elasticsearch で自動的に適用される最適化です。</p><p>別の最適化方法では、フィルターの条件を満たさないベクトルを無視します。代わりに、このメソッドは、フィルターを通過するフィルター処理されたベクトルの近傍をチェックします。このアプローチでは、フィルタリングされたベクトルは考慮されず、現在のパスに接続されたベクトルの探索が継続されるため、比較の回数が効果的に削減されます。</p><p>このアルゴリズムは ACORN-1 であり、そのプロセスについては<a href="https://www.elastic.co/search-labs/blog/filtered-hnsw-knn-search">こちらのブログ記事</a>で詳しく説明されています。</p><h2>ドキュメントレベルのセキュリティを使用したフィルタリング</h2><p><a href="https://www.elastic.co/docs/deploy-manage/users-roles/cluster-or-deployment-auth/controlling-access-at-document-field-level#document-level-security">ドキュメント レベル セキュリティ (DLS)</a>は、ユーザー ロールが取得できるドキュメントを指定する Elasticsearch 機能です。</p><p>DLS はクエリを使用して実行されます。ロールにはインデックスに関連付けられたクエリを持たせることができ、これにより、そのロールに属するユーザーがインデックスから取得できるドキュメントを効果的に制限できます。</p><p>ロール クエリは、<a href="https://github.com/elastic/elasticsearch/blob/c3a1cb34294e902a9f46d7e840ea09965019f456/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/accesscontrol/SecurityIndexReaderWrapper.java#L92">それに一致するドキュメントを取得する</a>ためのフィルターとして使用され、BitSet としてキャッシュされます。この BitSet は、基盤となる Lucene リーダーをラップするために使用されるため、クエリから返されたドキュメントのみが<em>ライブで</em>あると見なされます。つまり、それらのドキュメントはインデックス上に存在し、削除されていません。</p><p>knn クエリを実行するために<a href="https://github.com/apache/lucene/blob/a211d30097a8e3264d3ef073a054bd31eb847231/lucene/core/src/java/org/apache/lucene/search/AbstractKnnVectorQuery.java#L196">リーダーからライブ ドキュメントが取得される</a>ため、ユーザーが利用できるドキュメントのみが考慮されます。プレフィルターがある場合は、DLS ドキュメントが<a href="https://github.com/apache/lucene/blob/a211d30097a8e3264d3ef073a054bd31eb847231/lucene/core/src/java/org/apache/lucene/search/AbstractKnnVectorQuery.java#L204">それに追加され</a>ます。</p><p>つまり、DLS フィルタリングは、近似ベクトル検索のプレフィルタとして機能し、同じパフォーマンスへの影響と最適化を実現します。</p><p>完全一致検索を使用した DLS には、任意のフィルターを適用する場合と同じ利点があります。つまり、DLS から取得されるドキュメントが少ないほど、完全一致検索のパフォーマンスが向上します。DLS によって返されるドキュメントの数も考慮してください。DLS ロールの制限が非常に厳しい場合は、近似検索ではなく完全検索の使用を検討してください。</p><h2>ベンチマーク</h2><p>Elasticsearch では、ベクトル検索フィルタリングが効率的であることを確認したいと考えています。当社には、さまざまな<a href="https://elasticsearch-benchmarks.elastic.co/#tracks/so_vector/nightly/default/90d">フィルタリングを使用して近似ベクトル検索を実行するベクトル フィルタリング用の特定のベンチマーク</a>があり、ベクトル検索で関連する結果を可能な限り高速に取得し続けることができるようにします。</p><p>ACORN-1 が導入されたときの<a href="https://elasticsearch-benchmark-analytics.elastic.co/app/dashboards#/view/43b63e80-5ba2-11ed-aede-a742809feed4?_g=(refreshInterval:(pause:!t,value:60000),time:(from:'2025-05-28T01:27:58.456Z',to:'2025-06-30T13:53:26.430Z'))&amp;_a=()">改善点</a>を確認します。ベクトルの 2% のみがフィルターを通過するテストでは、クエリの待機時間は元の期間の 55% に短縮されます。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt820cb0b715cf291c/6a17e227dbb4ff8d49fb5615/3eac3748a33376fc97d957364a5c1f5108d5c58b-1023x896.png" alt="" /><h2>まとめ</h2><p>フィルタリングは検索の不可欠な部分です。ベクトル検索でフィルタリングのパフォーマンスを確保し、トレードオフと最適化を理解することが、効率的で正確な検索の成否を左右します。</p><p>フィルタリングはベクトル検索のパフォーマンスに影響します。</p><ul><li><p>フィルタリングを使用すると、正確な検索が高速になります。フィルタリングが十分に制限されている場合は、近似検索ではなく完全検索の使用を検討する必要があります。これは Elasticsearch での自動最適化です。</p></li><li><p>事前フィルタリングを使用すると近似検索が遅くなります。事前フィルタリングを使用すると、検索速度は遅くなりますが、フィルターに一致する上位 k 件の結果を取得できます。</p></li><li><p>ポストフィルタリングでは、フィルターの適用時にフィルターによってフィルタリングされる可能性があるため、必ずしも上位 k 件の結果が取得されるわけではありません。</p></li></ul><p>フィルタリングをお楽しみください！</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/vector-search-filtering</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/vector-search-filtering</guid>
    <category><![CDATA[Vector Database]]></category>
    <category><![CDATA[Lucene]]></category>
    <category><![CDATA[Elastic内部の実情]]></category>
    <dc:creator><![CDATA[Carlos Delgado]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt39ede1736e0f1456/6a17e2282f4a5c5031fa8843/03b1dd4c7bda4fbabd8e374bc2e4f12d5be6ef5f-1600x1150.png" length="0" type="image/png"/>
    <pubDate>Wed, 03 Sep 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Elasticsearch フィールド タイプへの埋め込みのマッピング: semantic_text、dense_vector、sparse_vector]]></title>
    <description><![CDATA[semantic_text、dense_vector、sparse_vector をどのように、いつ使用するか、またそれらが埋め込み生成とどのように関係するかについて説明します。]]></description>
    <content:encoded><![CDATA[<p>情報検索の関連性と精度を向上させるために埋め込みを使用することは、長年にわたって大幅に増加しています。Elasticsearch などのツールは、密ベクトル、疎ベクトル、セマンティック テキストなどの特殊なフィールド タイプを通じてこのタイプのデータをサポートするように進化してきました。ただし、良好な結果を得るには、利用可能な Elasticsearch フィールド タイプ ( <code>semantic_text</code> 、 <code>dense_vector</code> 、 <code>sparse_vector</code> ) に埋め込みを適切にマッピングする方法を理解することが重要です。</p><p>この記事では、これらのフィールド タイプ、それぞれのフィールド タイプをいつ使用するか、インデックス作成時とクエリ時の両方で埋め込み生成と使用戦略にどのように関連するかについて説明します。</p><h2>密ベクトル型</h2><p>Elasticsearch の<code>dense_vector</code>フィールド タイプは、ほぼすべての次元が関連する、テキスト、画像、音声などのデータの数値表現である密なベクトルを格納するために使用されます。これらのベクトルは、OpenAI、Cohere、Hugging Face などのプラットフォームによって提供される埋め込みモデルを使用して生成され、他のドキュメントと正確な用語を共有していない場合でも、データの全体的な意味をキャプチャするように設計されています。</p><p>Elasticsearch では、使用されるモデルに応じて、密なベクトルは最大 4096 次元を持つことができます。たとえば、all-MiniLM-L6-v2 モデルは 384 次元のベクトルを生成しますが、OpenAI の text-embedding-ada-002 は 1536 次元のベクトルを生成します。</p><p><code>dense_vector</code>フィールドは、事前に生成されたベクトルの使用、カスタム類似度関数の適用、外部モデルとの統合など、より高度な制御が必要な場合に、この種の埋め込みを格納するためのデフォルトのタイプとして一般的に採用されています。</p><h3>dense_vector 型をいつ、なぜ使用するのでしょうか?</h3><p>高密度ベクトルは、文、段落、またはドキュメント全体の間の意味的な類似性を捉えるのに最適です。同じ用語を共有していなくても、テキストの全体的な意味を比較することが目的の場合、非常に効果的です。</p><p>密ベクトル フィールドは、OpenAI、Cohere、Hugging Face などのプラットフォームによって提供されるモデルを使用した外部埋め込み生成パイプラインが既にあり、これらのベクトルを手動で保存およびクエリするだけの場合に最適です。このタイプのフィールドは、埋め込みモデルとの高い互換性と、生成およびクエリの完全な柔軟性を提供し、検索中にベクトルがどのように生成、インデックス付け、使用されるかを制御できます。</p><p>さらに、ランキングロジックを調整する必要がある場合には、k-NN や script_score などのクエリを使用して、さまざまな形式のセマンティック検索をサポートします。これらの可能性により、高密度ベクトルは、RAG (検索拡張生成)、推奨システム、類似性に基づくパーソナライズされた検索などのアプリケーションに最適です。</p><p>最後に、このフィールドでは、 <code>cosineSimilarity</code> 、 <code>dotProduct</code> 、 <code>l2norm</code>などの関数を使用して関連性ロジックをカスタマイズし、ユースケースのニーズに応じてランキングを調整できます。 </p><p>柔軟性、カスタマイズ性、および上記のような高度なユースケースとの互換性を必要とする人にとって、高密度ベクターは依然として最適なオプションです。</p><h3>密ベクター型のクエリを使用するにはどうすればいいですか?</h3><p><strong><code>dense_vector</code></strong>として定義されたフィールドの検索では、k 近傍クエリが使用されます。このクエリは、密なベクトルがクエリ ベクトルに最も近いドキュメントを見つける役割を果たします。以下は、密なベクトル フィールドに k-NN クエリを適用する方法の例です。</p>{
  "knn": {
    "field": "my_dense_vector",
    "k": 10,
    "num_candidates": 50,
    "query_vector": [/* vector generated by model */]
  }
}<p>k-NN クエリに加えて、ドキュメントのスコアリングをカスタマイズする必要がある場合は、script_score クエリを使用して、 <strong>cosineSimilarity、dotProduct、l2norm</strong>などのベクトル比較関数と組み合わせて、より制御された方法で関連性を計算することもできます。例を参照してください:</p>{
"script_score": {
    "query": { "match_all": {} },
    "script": {
      "source": "cosineSimilarity(params.query_vector,
'my_dense_vector') + 1.0",
      "params": {
        "query_vector": [/* vector */]
      }
    }
  }
}<p>さらに詳しく知りたい場合は、 <a href="https://www.elastic.co/jp/search-labs/blog/vector-search-set-up-elasticsearch">「Elasticsearch でベクトル検索を設定する方法」の記事を参照することをお勧めします。</a></p><p></p><h2>スパースベクトル型</h2><p><strong><code>sparse_vector</code></strong>フィールド タイプは、ほとんどの値がゼロで、少数の項のみに重要な重みがある数値表現であるスパース ベクトルを格納するために使用されます。このタイプのベクトルは、SPLADE や ELSER (Elastic Learned Sparse EncodeR) などの用語ベースのモデルで一般的です。</p><h3>スパースベクトル型をいつ、なぜ使用するのでしょうか?</h3><p>スパースベクトルは、意味的インテリジェンスを犠牲にすることなく、語彙的により正確な検索が必要な場合に最適です。テキストをトークン/値のペアとして表現し、関連する重みを持つ最も関連性の高い用語のみを強調表示することで、明確さ、制御性、効率性を実現します。</p><p>このタイプのフィールドは、テキスト内の相対的な重要度に基づいて各トークンに異なる重みを割り当てる ELSER モデルや SPLADE モデルなどの用語に基づいてベクトルを生成する場合に特に役立ちます。</p><p>クエリ内の特定の単語の影響を制御したい場合には、スパースベクター型を使用すると、用語の重みを手動で調整して、結果のランキングを最適化できます。</p><p>主な利点としては、ドキュメントが関連性があると判断された理由を明確に理解できるため検索の透明性が高く、また、すべての次元を保存する密なベクトルとは異なり、ゼロ以外の値を持つトークンのみが保存されるためストレージ効率が高くなる点が挙げられます。</p><p>さらに、スパース ベクトルはハイブリッド検索戦略の理想的な補完であり、稠密ベクトルと組み合わせて語彙の精度と意味の理解を組み合わせることもできます。</p><h3>スパースベクトル型のクエリを使用するにはどうすればいいですか?</h3><p><strong><code>sparse_vector</code></strong>クエリを使用すると、トークン/値形式のクエリ ベクトルに基づいてドキュメントを検索できます。以下のクエリの例を参照してください。</p>{
  "query": {
    "sparse_vector": {
      "field": "field_sparse",
      "query_vector": {
        "token1": 0.6,
        "token2": 0.2,
        "token3": 0.9
      }
    }
  }
}<p>トレーニング済みのモデルを使用する場合は、クエリ テキストをスパース ベクトルに自動的に変換する推論エンドポイントを使用できます。</p>{
  "query": {
    "sparse_vector": {
      "field": "field_sparse",
      "inference_id": "the inference ID to produce the token/weights",
      "query": "search text"
    }
  }
}<p>このトピックをさらに詳しく調べるには、 <a href="https://www.elastic.co/jp/search-labs/blog/sparse-vector-embedding">「トレーニング済み ML モデルによるスパース ベクトル埋め込みの理解」</a>を読むことをお勧めします。</p><h2>セマンティックテキストタイプ</h2><p><strong><code>semantic_text</code></strong>フィールド タイプは、Elasticsearch でセマンティック検索を使用する最もシンプルで簡単な方法です。推論エンドポイントを通じて、インデックス作成時とクエリ時の両方で埋め込み生成を自動的に処理します。つまり、ベクトルを手動で生成したり保存したりする手間がかかりません。</p><h3>セマンティックテキストはいつ、なぜ使用するのでしょうか?</h3><p><code>semantic_text</code>フィールドは、最小限の技術的労力で、ベクトルを手動で処理することなく開始したい場合に最適です。このフィールドは、埋め込み生成やベクトル検索マッピングなどの手順を自動化し、セットアップをより速く便利にします。</p><p><strong>シンプルさと抽象化</strong>を重視する場合は、<strong>マッピング、埋め込み生成、および取り込みパイプラインを手動で構成する複雑さが排除される</strong>ため、 <code>semantic_text</code>使用を検討する必要があります。推論モデルを選択するだけで、残りの作業は Elasticsearch が処理します。</p><p>主な利点としては、インデックス作成とクエリの両方で実行される<strong>自動埋め込み生成と</strong>、選択した推論モデルをサポートするように事前構成された、<strong>すぐに使用できるマッピング</strong>が挙げられます。</p><p>さらに、このフィールドは<strong>長いテキストの自動分割 (テキスト チャンク) をネイティブにサポート</strong>しており、大きなテキストをそれぞれ独自の埋め込みを持つ小さな段落に分割できるため、検索の精度が向上します。これにより、特にセマンティック検索の基礎となるエンジニアリングを扱うことなく価値を迅速に提供したいチームにとって、生産性が大幅に向上します。</p><p>ただし、 <code>semantic_text</code>スピードとシンプルさを提供しますが、このアプローチにはいくつかの制限があります。Elasticsearch の推論エンドポイントとして利用できる限り、市場標準モデルの使用が可能になります。ただし、 <code>dense_vector</code>フィールドの場合のように、<strong>外部で生成された埋め込みはサポートされません</strong>。</p><p>ベクトルの生成方法をより細かく制御する必要がある場合、独自の埋め込みを使用する場合、または高度な戦略のために複数のフィールドを組み合わせる必要がある場合は、 <code>dense_vector</code>フィールドと<code>sparse_vector</code>フィールドによって、よりカスタマイズされたシナリオやドメイン固有のシナリオに必要な柔軟性が得られます。</p><h3>セマンティックテキストタイプのクエリの使用方法</h3><p><strong><code>semantic_text</code></strong>より前は、埋め込みの種類 (密または疎) に応じて異なるクエリを使用する必要がありました。スパースフィールドには<code>sparse_vector</code>クエリが使用されましたが、 <code>dense_vector</code>フィールドには KNN クエリが必要でした。</p><p>セマンティック テキスト タイプでは、<a href="https://www.elastic.co/jp/docs/reference/query-languages/query-dsl/query-dsl-semantic-query">セマンティック クエリを使用して検索が実行されます。セマンティック クエリ</a>は、クエリ ベクトルを自動的に生成し、それをインデックス付けされたドキュメントの埋め込みと比較します。<strong><code>semantic_text</code></strong>タイプでは、クエリを埋め込むための推論エンドポイントを定義できますが、何も指定されていない場合は、インデックス作成時に使用されたのと同じエンドポイントがクエリに適用されます。</p>{
  "query": {
    "semantic": {
      "field": "semantic_text_field",
      "query": "search text"
    }
  }
}<p>詳細については、 <a href="https://www.elastic.co/jp/search-labs/blog/semantic-search-simplified-semantic-text">「Elasticsearch の新しい semantic_text マッピング: セマンティック検索の簡素化」という</a>記事を読むことをお勧めします。</p><h2>まとめ</h2><p>Elasticsearch で埋め込みをマッピングする方法を選択するときは、ベクトルを生成する方法とベクトルに対して必要な制御レベルを理解することが重要です。シンプルさを求める場合、セマンティック テキスト フィールドを使用すると、自動かつスケーラブルなセマンティック検索が可能になり、多くの初期使用ケースに最適です。より高度な制御、微調整されたパフォーマンス、またはカスタム モデルとの統合が必要な場合は、密ベクトル フィールドと疎ベクトル フィールドが必要な柔軟性を提供します。</p><p>理想的なフィールド タイプは、ユース ケース、利用可能なインフラストラクチャ、機械学習スタックの成熟度によって異なります。最も重要なのは、Elastic が最新の、適応性に優れた検索システムを構築するためのツールを提供していることです。</p><h2>参照資料</h2><ul><li><p><a href="https://www.elastic.co/jp/guide/en/elasticsearch/reference/current/semantic-text.html">セマンティックテキストフィールドタイプ</a></p></li><li><p><a href="https://www.elastic.co/jp/guide/en/elasticsearch/reference/current/sparse-vector.html">スパースベクトル場型</a></p></li><li><p><a href="https://www.elastic.co/jp/guide/en/elasticsearch/reference/current/dense-vector.html">密ベクトル場型</a></p></li><li><p><a href="https://www.elastic.co/jp/guide/en/elasticsearch/reference/current/query-dsl-semantic-query.html">セマンティッククエリ</a></p></li><li><p><a href="https://www.elastic.co/jp/guide/en/elasticsearch/reference/current/query-dsl-sparse-vector-query.html">スパースベクトルクエリ</a></p></li><li><p><a href="https://www.elastic.co/jp/guide/en/elasticsearch/reference/current/knn-search.html">kNN検索</a></p></li><li><p><a href="https://www.elastic.co/jp/search-labs/blog/semantic-search-simplified-semantic-text">Elasticsearchの新しいsemantic_textマッピング：セマンティック検索の簡素化</a></p></li><li><p><a href="https://www.elastic.co/jp/search-labs/blog/sparse-vector-embedding">学習済み ML モデルによるスパースベクトル埋め込みの理解</a></p></li></ul>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/mapping-embeddings-to-elasticsearch-field-types</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/mapping-embeddings-to-elasticsearch-field-types</guid>
    <category><![CDATA[Vector Database]]></category>
    <category><![CDATA[マッピング]]></category>
    <dc:creator><![CDATA[Andre Luiz]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt72cd3c2601b22886/6a17083b0c4857259901a9dc/f98fdff837db55b466780c0bae672aa6f6c3a966-1200x628.png" length="0" type="image/png"/>
    <pubDate>Tue, 13 May 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[ユースケースにBetter Binary Quantization (BBQ)を実装する方法]]></title>
    <description><![CDATA[ユースケースで Better Binary Quantization (BBQ) を実装する理由とその方法について説明します。]]></description>
    <content:encoded><![CDATA[<p>ベクトル検索は、テキストのセマンティック検索や、画像、ビデオ、オーディオの類似性検索を実装する際の基盤を提供します。ベクトル検索では、ベクトルはデータの数学的表現であり、そのサイズは膨大で、場合によっては遅くなることがあります。Better Binary Quantization (以下、BBQ と呼びます) は、ベクトルの圧縮方法として機能します。これにより、ベクトルを縮小して検索と処理を高速化しながら、適切な一致を見つけることができます。この記事では、BBQ と、ベクトルを自動的に再スコアリングする量子化インデックスにのみ使用可能なフィールドである rescore_vector について説明します。</p><p>この記事で説明したすべての完全なクエリと出力は<a href="https://github.com/elastic/elasticsearch-labs/tree/main/supporting-blog-content/how-and-why-bbq">、Elasticsearch Labs コード リポジトリ</a>で確認できます。</p><h2>ユースケースで Better Binary Quantization (BBQ) を実装する理由は何ですか?</h2>注: BBQ の背後にある数学の仕組みを詳しく理解するには、以下の<a href="https://www.elastic.co/jp/search-labs/blog/bbq-implementation-into-use-case#further-learning">「さらに学ぶ」セクション</a>をご覧ください。このブログでは、実装に重点を置いています。<p>数学は興味深いものですが、ベクトル検索がなぜ正確であり続けるのかを完全に理解したい場合には重要です。結局のところ、現在のベクトル検索アルゴリズムではデータの読み取り速度によって制限されることが判明しているため、これはすべて圧縮に関することです。したがって、そのデータすべてをメモリに収めることができれば、ストレージから読み取る場合と比べて速度が大幅に向上します (<a href="https://sre.google/static/pdf/rule-of-thumb-latency-numbers-letter.pdf">メモリは SSD よりも約 200 倍高速です</a>)。</p><p>いくつか留意すべき点があります:</p><ul><li><p><a href="https://arxiv.org/pdf/1603.09320">HNSW</a> (Hierarchical Navigable Small World) などのグラフベースのインデックスは、ベクター検索では最も高速です。</p><ul><li><p>HNSW: 効率的な高次元類似性検索を可能にする多層グラフ構造を構築する近似最近傍検索アルゴリズム。</p></li></ul></li></ul><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt760bd95c206bfa8f/6a17e2ad505ac393f7ad8a95/590f3b3c72a76023a38a0436cd9ff90a9f80e936-1964x1262.png" alt="HNSW: 効率的な高次元類似性検索を可能にする多層グラフ構造を構築する近似最近傍検索アルゴリズム。" /><ul><li><p>HNSW の速度は、基本的にメモリ、または最悪の場合、ストレージからのデータ読み取り速度によって制限されます。</p><ul><li><p>理想的には、保存されているすべてのベクトルをメモリにロードできるようにする必要があります。</p></li></ul></li><li><p>埋め込みモデルは通常、浮動小数点数ごとに 4 バイトの float32 精度のベクトルを生成します。</p></li><li><p>そして最後に、ベクトルや次元の数によっては、すべてのベクトルを保存するためのメモリがすぐに不足する可能性があります。</p></li></ul><p>これを当然のこととして考えると、それぞれが数百、数千の次元を持つ可能性のある数百万、数十億のベクトルを取り込み始めると、すぐに問題が発生することがわかります。「<a href="https://www.elastic.co/jp/search-labs/blog/bbq-implementation-into-use-case#approximate-numbers-on-the-compression-ratios">圧縮率のおおよその数値</a>」というセクションでは、おおよその数値が示されています。</p><h2>始めるには何が必要ですか?</h2><p>始めるには、次のものが必要です。</p><ul><li><p>Elastic Cloud またはオンプレミスを使用している場合は、Elasticsearch のバージョン 8.18 以降が必要になります。BBQ は 8.16 で導入されましたが、この記事では 8.18 で導入された<code>vector_rescore</code>使用します。</p></li><li><p>さらに、クラスター内に<a href="https://www.elastic.co/jp/guide/en/elasticsearch/reference/8.18/ml-settings.html">機械学習 (ML) ノード</a>があることも確認する必要があります。(注: モデルをロードするには最低 4 GB の ML ノードが必要ですが、完全な本番環境のワークロードには、はるかに大きなノードが必要になる可能性があります。)</p></li><li><p>Serverless を使用している場合は、ベクターに最適化されたインスタンスを選択する必要があります。</p></li><li><p>また、ベクター データベースに関する基本的な知識も必要になります。Elastic のベクトル検索の概念にまだ慣れていない場合は、まず次のリソースを確認することをお勧めします。</p><ul><li><p><a href="https://www.elastic.co/jp/search-labs/blog/elastic-vector-database-practical-example">弾性ベクトルデータベースのナビゲート</a></p></li><li><p><a href="https://www.elastic.co/jp/blog/retrieval-augmented-generation-explained">検索拡張生成の背後にある大きなアイデア</a></p></li></ul></li></ul><h2>より良いバイナリ量子化（BBQ）実装</h2><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt18df00df95ff2ca7/6a17e2af414c6411989450df/4d388078495566f0527e931e0c2e38facdce83c6-1503x748.png" alt="Elasticsearch バーベキュー実装。" /><p>このブログをシンプルに保つために、組み込み関数が利用可能な場合はそれを使用します。この場合、機械学習ノード上の Elasticsearch 内で直接実行される<a href="https://www.elastic.co/jp/guide/en/machine-learning/8.17/ml-nlp-e5.html"><code>.multilingual-e5-small</code></a>ベクトル埋め込みモデルがあります。<code>text_embedding</code>モデルを、任意の埋め込みツール ( <a href="https://www.elastic.co/jp/guide/en/elasticsearch/reference/8.18/infer-service-openai.html">OpenAI</a> 、 <a href="https://www.elastic.co/jp/guide/en/elasticsearch/reference/8.18/infer-service-google-ai-studio.html">Google AI Studio</a> 、 <a href="https://www.elastic.co/jp/guide/en/elasticsearch/reference/8.18/infer-service-cohere.html">Cohere</a>など) に置き換えることができることに注意してください。希望するモデルがまだ統合されていない場合は、<a href="https://www.elastic.co/jp/guide/en/elasticsearch/reference/8.18/bring-your-own-vectors.html">独自の密なベクトル埋め込みも使用</a>できます。</p><p>まず、特定のテキストのベクトルを生成するための推論エンドポイントを作成する必要があります。これらのコマンドはすべて、Kibana <a href="https://www.elastic.co/jp/guide/en/kibana/8.18/console-kibana.html">Dev Tools コンソール</a>から実行します。このコマンドは<code>.multilingual-e5-small</code>をダウンロードします。まだ存在しない場合はエンドポイントが設定されます。実行には 1 分ほどかかる場合があります。予想される出力は、Outputs フォルダー内のファイル<a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/how-and-why-bbq/Outputs/01-create-an-inference-endpoint-output.json">01-create-an-inference-endpoint-output.json</a>で確認できます。 </p>PUT _inference/text_embedding/my_e5_model
{
  "service": "elasticsearch",
  "service_settings": {
    "num_threads": 1,
    "model_id": ".multilingual-e5-small",
    "adaptive_allocations": {
      "enabled": true,
      "min_number_of_allocations": 1
    }
  }
}<p>これが返されると、モデルが設定され、次のコマンドを使用してモデルが期待どおりに動作するかどうかをテストできます。期待される出力は、Outputs フォルダー内のファイル<a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/how-and-why-bbq/Outputs/02-embed-text-output.json">02-embed-text-output.json</a>で確認できます。</p>POST _inference/text_embedding/my_e5_model
{
  "input": "my awesome piece of text"
}<p>トレーニング済みのモデルがどのノードにも割り当てられないという問題が発生した場合は、モデルを手動で起動する必要がある場合があります。</p>POST _ml/trained_models/.multilingual-e5-small/deployment/_start<p>ここで、埋め込みモデルからの出力と一致するように、標準テキスト フィールド ( <code>my_field</code> ) と 384 次元の密なベクトル フィールド ( <code>my_vector</code> ) の 2 つのプロパティを持つ新しいマッピングを作成しましょう。<code>index_options.type to bbq_hnsw</code>もオーバーライドします。予想される出力は、Outputs フォルダー内のファイル<a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/how-and-why-bbq/Outputs/03-create-byte-qauntized-index-output.json">03-create-byte-qauntized-index-output.json</a>で確認できます。</p>PUT bbq-my-byte-quantized-index
{
  "mappings": {
    "properties": {
      "my_field": {
        "type": "text"
      },
      "my_vector": {
        "type": "dense_vector",
        "dims": 384,
        "index_options": {
          "type": "bbq_hnsw"
        }
      }
    }
  }
}<p>Elasticsearch がベクトルを生成するようにするには、 <a href="https://www.elastic.co/jp/guide/en/elasticsearch/reference/8.18/ingest.html">Ingest Pipeline</a>を利用できます。このパイプラインには、エンドポイント ( <code>model_id</code> )、ベクトルを作成する<code>input_field</code> 、およびそれらのベクトルを格納する<code>output_field</code>の 3 つが必要です。以下の最初のコマンドは、内部で<a href="https://www.elastic.co/jp/guide/en/elasticsearch/reference/current/inference-apis.html">推論サービス</a>を使用する推論取り込みパイプラインを作成し、2 番目のコマンドはパイプラインが正しく動作していることをテストします。予想される出力は、Outputs フォルダー内のファイル<a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/how-and-why-bbq/Outputs/04-create-and-simulate-ingest-pipeline-output.json">04-create-and-simulate-ingest-pipeline-output.json</a>で確認できます。</p>PUT _ingest/pipeline/my_inference_pipeline
{
  "processors": [
    {
      "inference": {
        "model_id": "my_e5_model",
        "input_output": [
          {
            "input_field": "my_field",
            "output_field": "my_vector"
          }
        ]
      }
    }
  ]
}

POST _ingest/pipeline/my_inference_pipeline/_simulate
{
  "docs": [
    {
      "_source": {
        "my_field": "my awesome text field"
      }
    }
  ]
}<p>これで、以下の最初の 2 つのコマンドを使用してドキュメントを追加し、3 番目のコマンドで検索が機能することをテストする準備が整いました。予想される出力は、Outputs フォルダー内のファイル<a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/how-and-why-bbq/Outputs/05-bbq-index-output.json">05-bbq-index-output.json</a>で確認できます。</p>PUT bbq-my-byte-quantized-index/_doc/1?pipeline=my_inference_pipeline
{
    "my_field": "my awesome text field"
}

PUT bbq-my-byte-quantized-index/_doc/2?pipeline=my_inference_pipeline
{
    "my_field": "some other sentence"
}

GET bbq-my-byte-quantized-index/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "knn": {
            "field": "my_vector",
            "query_vector_builder": {
              "text_embedding": {
                "model_id": "my_e5_model",
                "model_text": "my awesome search field"
              }
            },
            "k": 10,
            "num_candidates": 100
          }
        }
      ]
    }
  },
  "_source": [
    "my_field"
  ]
}<p><a href="https://www.elastic.co/jp/search-labs/blog/better-binary-quantization-lucene-elasticsearch#lucene-benchmarking">この投稿</a>で推奨されているように、圧縮の利点を活用しながら高い再現精度を維持するのに役立つため、大量のデータに拡張する場合は、再スコアリングとオーバーサンプリングが推奨されます。Elasticsearch バージョン 8.18 以降では、 <a href="https://www.elastic.co/jp/guide/en/elasticsearch/reference/8.18/knn-search.html#dense-vector-knn-search-rescoring">rescore_vector</a>を使用してこの方法で実行できます。予想される出力は、Outputs フォルダー内のファイル<a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/how-and-why-bbq/Outputs/06-bbq-search-8-18-output.json">06-bbq-search-8-18-output.json</a>にあります。</p>GET bbq-my-byte-quantized-index/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "knn": {
            "field": "my_vector",
            "query_vector_builder": {
              "text_embedding": {
                "model_id": "my_e5_model",
                "model_text": "my awesome search field"
              }
            },
            "rescore_vector": {
              "oversample": 3
            },
            "k": 10,
            "num_candidates": 100
          }
        }
      ]
    }
  },
  "_source": [
    "my_field"
  ]
}<p>これらのスコアは、生データで得られるスコアと比べてどうでしょうか?上記のすべてを<code>index_options.type: hnsw</code>を使ってもう一度実行すると、スコアが非常に似ていることがわかります。予想される出力は、Outputs フォルダーのファイル<a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/how-and-why-bbq/Outputs/07-raw-vector-output.json">07-raw-vector-output.json</a>で確認できます。</p>PUT my-raw-vector-index
{
  "mappings": {
    "properties": {
      "my_field": {
        "type": "text"
      },
      "my_vector": {
        "type": "dense_vector",
        "dims": 384,
        "index_options": {
          "type": "hnsw"
        }
      }
    }
  }
}

PUT my-raw-vector-index/_doc/1?pipeline=my_inference_pipeline
{
    "my_field": "my awesome text field"
}

PUT my-raw-vector-index/_doc/2?pipeline=my_inference_pipeline
{
    "my_field": "some other sentence"
}

GET my-raw-vector-index/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "knn": {
            "field": "my_vector",
            "query_vector_builder": {
              "text_embedding": {
                "model_id": "my_e5_model",
                "model_text": "my awesome search field"
              }
            },
            "k": 10,
            "num_candidates": 100
          }
        }
      ]
    }
  },
  "_source": [
    "my_field"
  ]
}<h2>圧縮比のおおよその数値</h2><p>ベクトル検索を使用する場合、ストレージとメモリの要件がすぐに大きな課題になる可能性があります。次の内訳は、さまざまな量子化手法によってベクター データのメモリ フットプリントがいかに劇的に削減されるかを示しています。</p><p>ベクトル（V）</p><p>寸法（D）</p><p>生（V x D x 4）</p><p>int8 (V x (D x 1 + 4))</p><p>int4 (V x (D x 0.5 + 4))</p><p>バーベキュー（V×（D×0.125+4））</p><p>10,000,000</p><p>384</p><p>14.31GB</p><p>3.61GB</p><p>1.83GB</p><p>0.58GB</p><p>50,000,000</p><p>384</p><p>71.53GB</p><p>18.07GB</p><p>9.13GB</p><p>2.89GB</p><p>1億</p><p>384</p><p>143.05GB</p><p>36.14GB</p><p>18.25GB</p><p>5.77GB</p><h2>まとめ</h2><p>BBQ は、精度を犠牲にすることなくベクター データを圧縮するために適用できる最適化です。これはベクトルをビットに変換することで機能し、データを効果的に検索できるようにし、AI ワークフローを拡張して検索を高速化し、データ ストレージを最適化できるようにします。</p><h2>さらなる学習</h2><p>バーベキューについてさらに詳しく知りたい場合は、次のリソースをぜひチェックしてください。</p><ul><li><p><a href="https://www.elastic.co/jp/search-labs/blog/better-binary-quantization-lucene-elasticsearch">LuceneとElasticsearchにおけるバイナリ量子化（BBQ）</a></p></li><li><p><a href="https://www.elastic.co/jp/search-labs/blog/bit-vectors-elasticsearch-bbq-vs-pq">より良いバイナリ量子化（BBQ）と積量子化</a></p></li><li><p><a href="https://www.elastic.co/jp/search-labs/blog/optimized-scalar-quantization-elasticsearch">最適化されたスカラー量子化：さらに優れたバイナリ量子化</a></p></li><li><p><a href="https://www.youtube.com/watch?v=04NzMt2Nigc">より良いバイナリ量子化（BBQ）：バイトからBBQへ、より良いベクトル検索の秘密 by Ben Trent</a></p></li></ul>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/bbq-implementation-into-use-case</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/bbq-implementation-into-use-case</guid>
    <category><![CDATA[Vector Database]]></category>
    <category><![CDATA[基本]]></category>
    <dc:creator><![CDATA[Sachin Frayne,Jessica Garson]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt3dd0495b536b2615/6a17e2b0414c6488459450e3/66842055367cdd795532b01c167f2a4b03dc65e3-1200x628.png" length="0" type="image/png"/>
    <pubDate>Wed, 23 Apr 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Elasticsearch BBQ vs. OpenSearch FAISS: ベクトル検索パフォーマンス比較]]></title>
    <description><![CDATA[Elasticsearch BBQ と OpenSearch FAISS のパフォーマンス比較。]]></description>
    <content:encoded><![CDATA[<p><strong>バイナリ量子化によるベクトル検索: BBQ を使用した Elasticsearch は、 FAISS を使用した OpenSearch よりも 5 倍高速です</strong>。Elastic はコミュニティから、特にセマンティック検索/ベクター検索の領域における Elasticsearch と OpenSearch のパフォーマンスの違いを明確にしてほしいというリクエストを受けており、明確でデータに基づいた比較を提供するためにこれらのパフォーマンス テストを実施しました。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blta8792b5284e7a553/6a1709b9961e69a084c4cee6/7f4f8d08f7bee188423e4e65f0caefc7e34f0355-1600x681.png" alt="Elasticsearch BBQ vs. OpenSearch FAISS - 速度とスループットの再現率の比較" /><h2>バイナリ量子化対決</h2><p>高次元ベクトルを元の形式で保存すると、メモリを大量に消費する可能性があります。量子化技術はこれらのベクトルをコンパクトな表現に圧縮し、メモリフットプリントを大幅に削減します。その後、検索は圧縮された空間で実行されるため、計算の複雑さが軽減され、特に大規模なデータセットでは検索が高速化されます。</p><p>Elastic は、Lucene を最高のパフォーマンスを発揮するベクター エンジンにすることに注力しています。Elasticsearch 8.16 では Lucene をベースに<a href="https://www.elastic.co/jp/search-labs/blog/better-binary-quantization-lucene-elasticsearch">Better Binary Quantization</a> (BBQ) を導入し、8.18 および 9.0 でさらに進化させました。BBQ は、float32 次元をビットに削減する<a href="https://www.elastic.co/jp/search-labs/blog/optimized-scalar-quantization-elasticsearch">スカラー量子化</a>の新しいアプローチに基づいて構築されており、高いランキング品質を維持しながら約 95% のメモリ削減を実現します。</p><p>一方、OpenSearch は、nmslib (現在は非推奨)、Lucene、FAISS といった複数のベクター エンジンを使用します。<a href="https://www.elastic.co/jp/search-labs/blog/elasticsearch-opensearch-vector-search-performance-comparison">以前のブログ</a>では、ベクトル検索について Elasticsearch と OpenSearch を比較しました。3 つの異なるデータセットを使用し、両方の製品でさまざまなエンジンと構成の組み合わせをテストしました。</p><p>このブログでは、現在両方の製品で利用可能なバイナリ量子化アルゴリズムに焦点を当てています。<a href="https://github.com/elastic/rally-tracks/edit/master/openai_vector">openai_vector</a> Rally トラックを使用して、Elasticsearch と BBQ および<a href="https://opensearch.org/docs/latest/search-plugins/knn/knn-vector-quantization/#binary-quantization"> OpenSearch と FAISS のバイナリ量子化を テストしました。</a></p><p>主な目的は、同じレベルの再現率で両方のソリューションのパフォーマンスを評価することでした。<em>リコールと</em>はどういう意味ですか?リコールは、検索システムによって関連結果がどれだけ正常に取得されたかを測定する指標です。</p><p>この評価では、recall@k が特に重要です。ここで、 <em>k は</em>検討される上位の結果の数を表します。したがって、 <strong>Recall@10</strong> 、 <strong>Recall@50、および Recall@100 は、</strong>それぞれ上位 10、50、および 100 件の取得された項目内に表示される実際の関連結果の数を測定します。再現率は 0 ～ 1 (または 0% ～ 100% の精度) のスケールで表されます。これは重要なことです。なぜなら、ここでは再現率が常に 1 (100%) となる正確な KNN ではなく、近似 KNN (ANN) について話しているためです。</p><p><em>k</em>の各値に対して、最終的なランキングを適用する前に考慮される候補の数である<em>n</em>も指定しました。つまり、Recall@10、Recall@50、および Recall@100 の場合、システムは最初にバイナリ量子化アルゴリズムを使用して<em>n</em>個の候補を取得し、次にそれらをランク付けして、上位<em>k 個</em>の結果に期待される関連項目が含まれているかどうかを判断します。</p><p><em>n を</em>制御することで、効率と精度のトレードオフを分析できます。通常、 <em>n</em>が大きいほど、ランキングに使用できる候補が増えるためリコール<strong>は増加します</strong>が、レイテンシも<strong>増加し</strong>、スループット<strong>は低下します</strong>。逆に、 <em>n</em>を小さくすると検索速度は上がりますが、初期セットに含まれる関連候補が少なすぎるとリコール率が低下する可能性があります。</p><p>この比較では、Elasticsearch は同一の設定で OpenSearch よりも低いレイテンシと高いスループットを示しました。</p><h2>方法論</h2><p>完全な構成、Terraform スクリプト、Kubernetes マニフェスト、および特定の Rally トラックは、この<a href="https://github.com/elastic/elasticsearch-opensearch-vector-performance/tree/bbq">リポジトリ</a>の<a href="https://github.com/elastic/elasticsearch-opensearch-vector-performance/tree/bbq/rally-custom/custom_tracks/elasticsearch/openai_vector_bq"><em>openai_vector_bq</em></a>で入手できます。</p><p>以前のベンチマークと同様に、次のもので構成される Kubernetes クラスターを使用しました。</p><ul><li><p>Elasticsearch 9.0 用の 1 つのノードプール（3 台の<code>e2-standard-32</code>マシン、128 GB の RAM、32 個の CPU）</p></li><li><p>OpenSearch 2.19 用の 1 つのノード プール (3 台の<code>e2-standard-32</code>マシン (128 GB RAM、32 個の CPU))</p></li><li><p>Rally 用の 1 つのノードプール（2 台の<code>e2-standard-4</code>マシン、16 GB の RAM と 4 つの CPU）</p></li></ul><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt934e988e96a5849f/6a1709bbacf08838f9be9ac1/169bb6033f6eebfd1b177b3446bf916fde4ee5c5-1600x856.png" alt="Elasticsearch BBQ vs Opensearch FAISS方法論のセットアップ" /><p>Elasticsearch クラスター バージョン 9.0 を 1 つと OpenSearch クラスター バージョン 2.19 を 1 つセットアップしました。</p><p>Elasticsearch と OpenSearch は両方ともまったく同じ設定でテストされました。 つまり、OpenAI の<a href="https://openai.com/blog/new-and-improved-embedding-model"> text-embedding-ada-002 モデルを使用して生成された埋め込みが強化された</a><a href="https://huggingface.co/datasets/BeIR/nq"> NQ データ セットからの</a> 250 万のドキュメントを使用する、いくつかの変更 を<a href="https://github.com/elastic/elasticsearch-opensearch-vector-performance/commit/b97d5d95c22c8cf862f2030964524bdd156a5da3"> 加え た</a><a href="https://github.com/elastic/rally-tracks/edit/master/openai_vector"> openai_vector</a> Rally トラック を使用しました。</p>{
  "source-file": "open_ai_corpus-initial-indexing.json.bz2",
  "document-count": 2580961,
  "compressed-bytes": 32076749416,
  "uncompressed-bytes": 90263571686
}<p>結果には、8 つの同時クライアントを使用して検索操作を実行し、さまざまなリコール レベル (recall@10、recall@50、recall@100) で測定されたレイテンシとスループットが報告されています。単一のシャードを使用し、レプリカは使用しませんでした。</p><p>我々は以下のkn-rescoreの組み合わせを実行した。10-2000-2000、または<em>k:10</em> 、 <em>n:2000</em> 、 <em>rescore:2000 は</em>、2000 件の結果に対して再スコアを適用して、n 件の候補 (2000) のうち上位 k 件 (10) を取得します (これは「オーバーサンプル係数」が 1 の場合に相当します)。各検索はウォームアップとして 1,000 回実行され、10,000 回実行されました。</p><p></p><p><u><strong>リコール@10</strong></u></p><ul><li><p>10-40-40</p></li><li><p>10-50-50</p></li><li><p>10-100-100</p></li><li><p>10-200-200</p></li><li><p>10-500-500</p></li><li><p>10-750-750</p></li><li><p>10-1000-1000</p></li><li><p>10-1500-1500</p></li><li><p>2000年10月</p></li></ul><p><u><strong>リコール@50</strong></u></p><ul><li><p>50-150-150</p></li><li><p>50-200-200</p></li><li><p>50-250-250</p></li><li><p>50-500-500</p></li><li><p>50-750-750</p></li><li><p>50-1000-1000</p></li><li><p>50-1200-1200</p></li><li><p>50-1500-1500</p></li><li><p>50-2000-2000</p></li></ul><p><u><strong>リコール@100</strong></u></p><ul><li><p>100-200-200</p></li><li><p>100-250-250</p></li><li><p>100-300-300</p></li><li><p>100-500-500</p></li><li><p>100-750-750</p></li><li><p>100-1000-1000</p></li><li><p>100-1200-1200</p></li><li><p>100-1500-1500</p></li><li><p>100-2000-2000</p></li></ul><p>ベンチマークを再現するために、rally-elasticsearch と rally-opensearch の両方の Kubernetes マニフェストには、関連するすべての変数が ConfigMap で外部化されており、<a href="https://github.com/elastic/elasticsearch-opensearch-vector-performance/blob/bbq/k8s/rally-openai_vector-es-bq.yml">こちら</a>(ES) と<a href="https://github.com/elastic/elasticsearch-opensearch-vector-performance/blob/bbq/k8s/rally-openai_vector-os-bq.yml">こちら</a>(OS) から入手できます。<em>search_ops</em>パラメータは、k、n、rescore の任意の組み合わせをテストするようにカスタマイズできます。</p><h3>OpenSearch Rallyの設定</h3><p><code>/k8s/rally-openai_vector-os-bq.yml</code></p>apiVersion: v1
kind: ConfigMap
metadata:
  name: rally-params-os
  labels:
    app: rally-opensearch
data:
  user-tags.json: |
    {
      "product": "OpenSearch",
      "product-version": "OpenSearch-2.19.0",
      "product-label": "OpenSearch-2.19-faiss",
      "benchmark-run": "19-feb-recall@100"
    }
  track-params.json: |
    {
      "mapping_type": "vectors-only-mapping-with-docid",
      "standalone_search_clients": 8,
      "standalone_search_iterations": 5000,
      "ann_threshold": 0,
      "vector_mode": "on_disk",
      "compression_level": "32x",
      "vector_method_name": "hnsw",
      "vector_method_engine": "faiss",
      "search_ops": [
        [100, 200, 200],
        [100, 250, 250],
        [100, 300, 300],
        [100, 500, 500],
        [100, 750, 750],
        [100, 1000, 1000],
        [100, 1200, 1200],
        [100, 1500, 1500],
        [100, 2000, 2000]
      ]
    }<h3>Opensearchインデックス設定</h3><p>ConfigMap の変数はインデックス構成で使用され、一部のパラメータは変更されません。OpenSearch における 1 ビット量子化は<a href="https://opensearch.org/docs/latest/search-plugins/knn/knn-vector-quantization/#binary-quantization">、圧縮レベルを「32x」に設定する</a>ことによって設定されます。</p><p><code>index-vectors-only-mapping-with-docid-mapping.json</code></p>{
  "settings": {
    {% if preload_pagecache %}
    "index.store.preload": [
      "vec", "vex", "vem", "veq", "veqm", "veb", "vebm"
    ],
    {% endif %}
    "index.number_of_shards": {{ number_of_shards | default(1) }},
    "index.number_of_replicas": {{ number_of_replicas | default(0) }},
    "index.knn": true,
    "index.knn.advanced.approximate_threshold": {{ ann_threshold | default(15000) }}
  },
  "mappings": {
    "dynamic": false,
    "properties": {
      "docid": {
        "type": "keyword"
      },
      "emb": {
        "type": "knn_vector",
        "dimension": 1536,
        "space_type": "innerproduct",
        "data_type": "float",
        "mode": {{ vector_mode | default("in_memory") | tojson }},
        "compression_level": {{ compression_level | default("32x") | tojson }},
        "method": {
          "name": {{ vector_method_name | default("hnsw") | tojson }},
          "engine": {{ vector_method_engine | default("faiss") | tojson }},
          "parameters": {
            "ef_construction": 100,
            "m": 16
          }
        }
      }
    }
  }
}<h3>Elasticsearch Rallyの設定</h3><p><code>/k8s/rally-openai_vector-es-bq.yml</code></p>apiVersion: v1
kind: ConfigMap
metadata:
  name: rally-params-es
  labels:
    app: rally-elasticsearch
data:
  user-tags.json: |
    {
      "product": "Elasticsearch",
      "product-version": "Elasticsearch-9.0.0-ade01164",
      "product-label": "Elasticsearch-9.0-BBQ",
      "benchmark-run": "19-feb-recall@100"
    }
  track-params.json: |
    {
      "mapping_type": "vectors-only-mapping-with-docid",
      "standalone_search_clients": 8,
      "standalone_search_iterations": 5000,
      "vector_index_type": "bbq_hnsw",
      "search_ops": [
        [100, 200, 200],
        [100, 250, 250],
        [100, 300, 300],
        [100, 500, 500],
        [100, 750, 750],
        [100, 1000, 1000],
        [100, 1200, 1200],
        [100, 1500, 1500],
        [100, 2000, 2000]
      ]
    }<h3>Elasticsearchインデックス設定</h3><p><code>index-vectors-only-mapping-with-docid-mapping.json</code></p>{
  "settings": {
    {# non-serverless-index-settings-marker-start #}
    {%- if build_flavor != "serverless" or serverless_operator == true -%}
    {% if preload_pagecache %}
    "index.store.preload": [ "vec", "vex", "vem", "veq", "veqm", "veb", "vebm" ],
    {% endif %}
    "index.number_of_shards": {{ number_of_shards | default(1) }},
    "index.number_of_replicas": {{ number_of_replicas | default(0) }}
    {%- endif -%}
    {# non-serverless-index-settings-marker-end #}
  },
  "mappings": {
    "dynamic": false,
    "properties": {
      "docid": {
        "type": "keyword"
      },
      "emb": {
        "type": "dense_vector",
        "element_type": "float",
        "dims": 1536,
        "index": true,
        "similarity": "dot_product",
        "index_options": {
          "type": {{ vector_index_type | default("bbq_hnsw") | tojson }},
          "ef_construction": 100,
          "m": 16
        }
      }
    }
  }
}<h2>成果</h2><p>結果を解釈する方法は複数あります。レイテンシとスループットの両方について、リコールの各レベルで簡略化されたグラフと詳細なグラフをプロットしました。各指標について「高いほど良い」と考えると、違いが簡単にわかります。ただし、レイテンシはマイナス（低い方が実際には良い）であり、スループットはプラスです。簡略化されたグラフでは、 <strong>(リコール / レイテンシ) * 10000</strong> (単に「速度」と呼びます) と<strong>リコール * スループット</strong>を使用しました。したがって、両方のメトリックは、速度とスループットが高いほど優れていることを意味します。始めましょう。</p><h3>10回想 - 簡略化</h3><p>この再現レベルでは、Elasticsearch BBQ は OpenSearch FAISS よりも最大<strong>5 倍高速</strong>(平均で 3.9 倍高速) で、平均で<strong>3.2 倍のスループット</strong>を実現します。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt3de6b3460127a8ae/6a1709bcb339d55975769f6f/d580ad53e8974bd3aa75957c413a0136c4e465c5-1600x681.png" alt="Elasticsearch BBQは、OpenSearch FAISSの速度とスループットと比較して、最大5倍（平均3.9倍）高速で、平均3.2倍のスループットを実現しています。Recall@10" /><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd47045013cac5e9d/6a1709be0e2e49599a41a088/18edce667fe36ab95033264ef8df6f352dda2425-2044x866.png" alt="Elasticsearch BBQ は OpenSearch FAISS よりも最大 5 倍高速 (平均 3.9 倍高速) で、平均 3.2 倍のスループットを実現します。" /><h4>リコール@10 - 詳細</h4><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt0ecb02d4a2967dbd/6a1709bf14b270c04fe3c5c6/a7459b87e679f4ad963d0e2f1685499b40f6f050-1600x799.png" alt="詳細な再現率@10レイテンシ比較 Elasticsearch BBQ vs Opensearch FAISS" /><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt182da309ad6cfd39/6a1709c166c4f99077f8bfd8/c6036f55a13377654d296eb3148c7199e1965475-1600x799.png" alt="Elasticsearch BBQ と Opensearch FAISS の詳細なリコール@10 スループット比較。" /><p></p><p>タスク</p><p>レイテンシ平均</p><p>スループット平均</p><p>平均再現率</p><p>Elasticsearch-9.0-BBQ</p><p>10-100-100</p><p>11.70</p><p>513.58</p><p>0.89</p><p>Elasticsearch-9.0-BBQ</p><p>10-1000-100</p><p>27.33</p><p>250.55</p><p>0.95</p><p>Elasticsearch-9.0-BBQ</p><p>10-1500-1500</p><p>35.93</p><p>197.26</p><p>0.95</p><p>Elasticsearch-9.0-BBQ</p><p>10-200-200</p><p>13.33</p><p>456.16</p><p>0.92</p><p>Elasticsearch-9.0-BBQ</p><p>2000年10月</p><p>44.27</p><p>161.40</p><p>0.95</p><p>Elasticsearch-9.0-BBQ</p><p>10-40-40</p><p>10.97</p><p>539.94</p><p>0.84</p><p>Elasticsearch-9.0-BBQ</p><p>10-50-50</p><p>11時00分</p><p>535.73</p><p>0.85</p><p>Elasticsearch-9.0-BBQ</p><p>10-500-500</p><p>19.52</p><p>341.45</p><p>0.93</p><p>Elasticsearch-9.0-BBQ</p><p>10-750-750</p><p>22.94</p><p>295.19</p><p>0.94</p><p>OpenSearch-2.19-faiss</p><p>10-100-100</p><p>35.59</p><p>200.61</p><p>0.94</p><p>OpenSearch-2.19-faiss</p><p>10-1000-1000</p><p>156.81</p><p>58.30</p><p>0.96</p><p>OpenSearch-2.19-faiss</p><p>10-1500-1500</p><p>181.79</p><p>42.97</p><p>0.96</p><p>OpenSearch-2.19-faiss</p><p>10-200-200</p><p>47.91</p><p>155.16</p><p>0.95</p><p>OpenSearch-2.19-faiss</p><p>2000年10月</p><p>232.14</p><p>31.84</p><p>0.96</p><p>OpenSearch-2.19-faiss</p><p>10-40-40</p><p>27.55</p><p>249.25</p><p>0.92</p><p>OpenSearch-2.19-faiss</p><p>10-50-50</p><p>28.78</p><p>245.14</p><p>0.92</p><p>OpenSearch-2.19-faiss</p><p>10-500-500</p><p>79.44</p><p>97.06</p><p>0.96</p><p>OpenSearch-2.19-faiss</p><p>10-750-750</p><p>104.19</p><p>75.49</p><p>0.96</p><h3>50歳の思い出 - 簡略化</h3><p>この再現率では、Elasticsearch BBQは<strong>最大5倍（平均4.2倍）高速で</strong>、平均<strong>3.9倍のスループット</strong>を実現しています。OpenSearch FAISSよりも。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt486683f2c7a1d677/6a1709c2a6c2b995bde796a3/3189ffb330948b35854eeea9ae317d4846c14972-1600x681.png" alt="Recal @50 ベクトルパフォーマンス比較 Elasticsearch BBQ vs Opensearch FAISS" /><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltba830c6741784682/6a1709c4a292990627d00ff7/607383d674dcf0b8f94bfb1a450063f52fcbeb15-2060x876.png" alt="Elasticsearch BBQ は OpenSearch FAISS よりも最大 5 倍高速 (平均 4.2 倍高速) で、平均 3.9 倍のスループットを実現します。" /><h4>詳細な結果 - 50 回のリコール</h4><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt821775e7f87e5ede/6a1709c6a6c2b90af3e796a7/ebfffe0b776aad31dd03d315cfbf5aa098b41226-1600x789.png" alt="Recall@50 Elasticsearch BBQ と Opensearch FAISS のレイテンシ結果を振り返る" /><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt4f47784ed1017c15/6a1709c7e8fbce0fbe39fbf5/ae20cf870a65c400a2112bbad62eb56e244f549a-1600x799.png" alt="Recall@50 Elasticsearch BBQとOpensearch FAISSスループット結果" /><p></p><p>タスク</p><p>レイテンシ平均</p><p>スループット平均</p><p>平均再現率</p><p>Elasticsearch-9.0-BBQ</p><p>50-1000-1000</p><p>25.71</p><p>246.44</p><p>0.95</p><p>Elasticsearch-9.0-BBQ</p><p>50-1200-1200</p><p>28.81</p><p>227.85</p><p>0.95</p><p>Elasticsearch-9.0-BBQ</p><p>50-150-150</p><p>13.43</p><p>362.90</p><p>0.90</p><p>Elasticsearch-9.0-BBQ</p><p>50-1500-1500</p><p>33.38</p><p>202.37</p><p>0.95</p><p>Elasticsearch-9.0-BBQ</p><p>50-200-200</p><p>12.99</p><p>406.30</p><p>0.91</p><p>Elasticsearch-9.0-BBQ</p><p>50-2000-2000</p><p>42.63</p><p>163.68</p><p>0.95</p><p>Elasticsearch-9.0-BBQ</p><p>50-250-250</p><p>14.41</p><p>373.21</p><p>0.92</p><p>Elasticsearch-9.0-BBQ</p><p>50-500-500</p><p>17.15</p><p>341.04</p><p>0.93</p><p>Elasticsearch-9.0-BBQ</p><p>50-750-750</p><p>31.25</p><p>248.60</p><p>0.94</p><p>OpenSearch-2.19-faiss</p><p>50-1000-1000</p><p>125.35</p><p>62.53</p><p>0.96</p><p>OpenSearch-2.19-faiss</p><p>50-1200-1200</p><p>143.87</p><p>54.75</p><p>0.96</p><p>OpenSearch-2.19-faiss</p><p>50-150-150</p><p>43.64</p><p>130.01</p><p>0.89</p><p>OpenSearch-2.19-faiss</p><p>50-1500-1500</p><p>169.45</p><p>46.35</p><p>0.96</p><p>OpenSearch-2.19-faiss</p><p>50-200-200</p><p>48.05</p><p>156.07</p><p>0.91</p><p>OpenSearch-2.19-faiss</p><p>50-2000-2000</p><p>216.73</p><p>36.38</p><p>0.96</p><p>OpenSearch-2.19-faiss</p><p>50-250-250</p><p>53.52</p><p>142.44</p><p>0.93</p><p>OpenSearch-2.19-faiss</p><p>50-500-500</p><p>78.98</p><p>97.82</p><p>0.95</p><p>OpenSearch-2.19-faiss</p><p>50-750-750</p><p>103.20</p><p>75.86</p><p>0.96</p><h3>リコール@100</h3><p>この再現レベルでは、Elasticsearch BBQ は OpenSearch FAISS よりも<strong>最大 5 倍高速</strong>(平均 4.6 倍高速) で、平均で<strong>3.9 倍のスループット</strong>を実現します。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt380eb9165343a7b3/6a1709c9acf0882669be9ac5/d3f29db64cbde9956de1fa3ae64a75f15141a2bb-1600x681.png" alt="Elasticsearch BBQ VS Opensearch FAISS の結果を 100 回思い出す" /><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt505ce8aa70898390/6a1709cb1949f73a37e7a9bb/10aff7f8c61fdac895b9ba9c5342baf239ba3ffc-2072x864.png" alt="Elasticsearch BBQとOpensearch FAISSのレイテンシとスループットパフォーマンスの比較" /><h4>詳細な結果 - 再現率 100</h4><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltb2a73e29940e9d6b/6a1709cccf4f257615b2d138/8790fdf9512b850447f6875fb69969f6f1d4da5f-1600x799.png" alt="詳細なレイテンシ結果 - リコール率 100 Elasticsearch BBQ vs Opensearch FAISS" /><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte2c91ca21da03d96/6a1709ce0c4857d3fc01aa39/d47032fac6c288cd4eedd9f25001e417b2fa9d65-1600x787.png" alt="詳細なスループット結果 - Elasticsearch BBQ と Opensearch FAISS を 100 で再現。" /><p></p><p>タスク</p><p>レイテンシ平均</p><p>スループット平均</p><p>平均再現率</p><p>Elasticsearch-9.0-BBQ</p><p>100-1000-1000</p><p>27.82</p><p>243.22</p><p>0.95</p><p>Elasticsearch-9.0-BBQ</p><p>100-1200-1200</p><p>31.14</p><p>224.04</p><p>0.95</p><p>Elasticsearch-9.0-BBQ</p><p>100-1500-1500</p><p>35.98</p><p>193.99</p><p>0.95</p><p>Elasticsearch-9.0-BBQ</p><p>100-200-200</p><p>14.18</p><p>403.86</p><p>0.88</p><p>Elasticsearch-9.0-BBQ</p><p>100-2000-2000</p><p>45.36</p><p>159.88</p><p>0.95</p><p>Elasticsearch-9.0-BBQ</p><p>100-250-250</p><p>14.77</p><p>433.06</p><p>0.90</p><p>Elasticsearch-9.0-BBQ</p><p>100-300-300</p><p>14.61</p><p>375.54</p><p>0.91</p><p>Elasticsearch-9.0-BBQ</p><p>100-500-500</p><p>18.88</p><p>340.37</p><p>0.93</p><p>Elasticsearch-9.0-BBQ</p><p>100-750-750</p><p>23.59</p><p>285.79</p><p>0.94</p><p>OpenSearch-2.19-faiss</p><p>100-1000-1000</p><p>142.90</p><p>58.48</p><p>0.95</p><p>OpenSearch-2.19-faiss</p><p>100-1200-1200</p><p>153.03</p><p>51.04</p><p>0.95</p><p>OpenSearch-2.19-faiss</p><p>100-1500-1500</p><p>181.79</p><p>43.20</p><p>0.96</p><p>OpenSearch-2.19-faiss</p><p>100-200-200</p><p>50.94</p><p>131.62</p><p>0.83</p><p>OpenSearch-2.19-faiss</p><p>100-2000-2000</p><p>232.53</p><p>33.67</p><p>0.96</p><p>OpenSearch-2.19-faiss</p><p>100-250-250</p><p>57.08</p><p>131.23</p><p>0.87</p><p>OpenSearch-2.19-faiss</p><p>100-300-300</p><p>62.76</p><p>120.10</p><p>0.89</p><p>OpenSearch-2.19-faiss</p><p>100-500-500</p><p>84.36</p><p>91.54</p><p>0.93</p><p>OpenSearch-2.19-faiss</p><p>100-750-750</p><p>111.33</p><p>69.95</p><p>0.94</p><h2>バーベキューの改良</h2><p>BBQ は最初のリリース以来長い道のりを歩んできました。Elasticsearch 8.16 では、比較のために、8.16 のベンチマーク実行を現在のものと並べて含めており、それ以降、リコールとレイテンシーがどのように改善されたかを確認できます。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt9f1f7ae1bb425a3a/6a1709cf67045bde8e45c192/45a0acfe5985bff28ccada76ec4eca190fe65f72-1600x799.png" alt="Elasticsearch 9.0 BBQのレイテンシリコールの改善をElasticsearch 8.16 BBQとベンチマーク" /><p>Elasticsearch 8.18 および 9.0 では、ベクトルを量子化するためのコア アルゴリズムを書き直しました。つまり、8.16 の BBQ は良かったのですが、最新バージョンはさらに良くなっています。それについては<a href="https://www.elastic.co/jp/search-labs/blog/optimized-scalar-quantization-elasticsearch">ここ</a>と<a href="https://www.elastic.co/jp/search-labs/blog/scalar-quantization-optimization">ここで</a>読むことができます。つまり、すべてのベクトルは、最適化されたスカラー分位数を通じて個別に量子化されます。その結果、ユーザーはパフォーマンスを犠牲にすることなくベクトル検索の精度向上の恩恵を受けることができ、Elasticsearch のベクトル検索はさらに強力になります。</p><h2>まとめ</h2><p>Elasticsearch BBQ と OpenSearch FAISS のパフォーマンス比較では、Elasticsearch はベクトル検索において OpenSearch を大幅に上回り、さまざまなレベルのリコールで平均最大 5 倍のクエリ速度と 3.9 倍のスループットを達成しています。</p><p>主な調査結果は次のとおりです。</p><ul><li><p><strong>Recall@10</strong> : Elasticsearch BBQ は、OpenSearch FAISS と比較して最大 5 倍高速 (平均 3.9 倍高速) で、平均 3.2 倍のスループットを実現します。</p></li><li><p><strong>Recall@50</strong> : Elasticsearch BBQ は、OpenSearch FAISS と比較して最大 5 倍高速 (平均で 4.2 倍高速) で、平均で 3.9 倍のスループットを実現します。</p></li><li><p><strong>Recall@100</strong> : Elasticsearch BBQ は、OpenSearch FAISS と比較して最大 5 倍高速 (平均で 4.6 倍高速) で、平均で 3.9 倍のスループットを実現します。</p></li></ul><p>これらの結果は、特に高次元ベクトル検索シナリオにおける Elasticsearch BBQ の効率性とパフォーマンスの利点を強調しています。Elasticsearch 8.16 で導入された Better Binary Quantization (BBQ) 技術は、高いランキング品質を維持しながらメモリを大幅に削減 (約 95%) するため、大規模なベクトル検索アプリケーションに最適です。</p><p>Elastic では、RAG (Retrieval Augmented Generation) を含む検索および取得ユースケースに最適なベクター データベースを提供するために、Apache Lucene と Elasticsearch を改良するための革新を絶えず続けています。<a href="https://www.elastic.co/jp/search-labs/blog/optimized-scalar-quantization-elasticsearch">最近の進歩</a>によりパフォーマンスが劇的に向上し、Lucene 10 から得た成果を基にして、ベクター検索が以前よりも高速かつスペース効率が高くなりました。このブログは、その革新のもう一つの例です。</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/elasticsearch-bbq-vs-opensearch-faiss</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/elasticsearch-bbq-vs-opensearch-faiss</guid>
    <category><![CDATA[Vector Database]]></category>
    <dc:creator><![CDATA[Ugo Sangiorgi]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd76ada1eebfe05cd/6a1709d1a29299ed29d00ffb/796de4829e29566f1f3efa2482f5c3e54b31b1d6-1536x1024.png" length="0" type="image/png"/>
    <pubDate>Tue, 15 Apr 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Google Cloud の Vertex AI Platform のネイティブ基盤となる Elasticsearch ベクトル データベース]]></title>
    <description><![CDATA[Google Cloud の Vertex AI 向けの初のサードパーティ製ネイティブ グラウンディング エンジンである Elasticsearch を使用して、Gemini モデルをエンタープライズ データにグラウンディングすることで、カスタム GenAI エクスペリエンスを構築する方法をご覧ください。]]></description>
    <content:encoded><![CDATA[<p>Elasticは、Elasticsearchベクターデータベースがネイティブサポートされた情報検索エンジンとしてGoogle CloudのVertex AIプラットフォームに統合され、ユーザーがElasticsearchの高度なAI搭載セマンティックおよびハイブリッド検索機能を使用してGoogleのGeminiモデルのマルチモーダルな強みを活用できるようになることを発表します。</p><p>開発者は、統一されたジャーニー内で RAG アプリケーションを作成し、ローコードで柔軟な方法でプライベート データに基づいたチャット エクスペリエンスを実現できるようになりました。顧客や社内従業員向けの AI エージェントを構築する場合でも、ソフトウェア内で LLM 生成を活用する場合でも、Vertex AI プラットフォームでは、最小限の構成で Elasticsearch の関連性を簡単に利用できます。この統合により、本番環境のユースケースで Gemini モデルをより簡単に、より迅速に導入できるようになり、GenAI を PoC から実際のシナリオへと推進できます。</p><p>このブログでは、Elasticsearch を Google Cloud の Vertex AI プラットフォームと統合して、シームレスなデータグラウンディングと完全にカスタマイズ可能な GenAI アプリケーションの構築を実現する方法について説明します。どのようにするか見てみましょう。</p><h2>Google Cloud の Vertex AI と Gemini モデルは Elasticsearch によってデータに基づいています</h2><p>GenAI アプリケーションを作成するために Vertex AI サービスとツールを活用するユーザーは、新しい「Grounding」オプションにアクセスして、プライベート データを会話のやり取りに自動的に取り入れることができるようになりました。Elasticsearch は現在この機能の一部となっており、両方から使用できます。</p><ul><li><p>Vertex AI <a href="https://cloud.google.com/vertex-ai/generative-ai/docs/model-reference/inference">LLM API は</a>、生成時に Google の Gemini モデルを直接強化します (推奨)。</p></li><li><p><a href="https://cloud.google.com/generative-ai-app-builder/docs/grounded-gen">Grounded Generation API は</a>、Vertex AI Agent Builder エコシステムでエージェント エクスペリエンスを構築する代わりに使用されます。</p></li></ul><p>この統合により、最もダウンロードされ、導入されている<a href="https://www.elastic.co/jp/elasticsearch/vector-database">ベクターデータベースで</a>ある Elasticsearch が、社内のエンド カスタマー対応チャットで必要な場所に適切なエンタープライズ データを提供します。これは、GenAI をビジネス プロセスに実際に導入する上で非常に重要です。</p><p>前述の API により、開発者はこの新しい提携機能をコードに採用できるようになります。ただし、迅速なエンジニアリングとテストは、アプリケーション開発において依然として重要なステップであり、初期の発見の場として機能します。これをサポートするために、Elasticsearch は Vertex AI Studio コンソール ツール内でユーザーが簡単に評価できるように設計されています。</p><p>以下に示すように、UI の「Grounding のカスタマイズ」タブ内で、必要なパラメータ (検索するインデックス、取得するドキュメントの数、必要な検索テンプレート) を使用して Elastic エンドポイントを構成するには、いくつかの簡単な手順を実行するだけです (これを機能させるには、UI と以下のコード例に「ApiKey」という単語を含む API キーを入力する必要があることに注意してください)。これで、プライベートな知識を使って生成する準備が整いました。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt7d313968acca1fea/6a17e624b1e113278079f250/69b3d979d18fa90742d9397f57975c586edf5d9f-1003x710.gif" alt="Elasticsearch を活用したデータに基づく Google Cloud の Vertex AI と Gemini モデル" /><h2>GenAIアプリケーションを簡単に本番環境に対応</h2><p>Elastic と Google Cloud は、開発者中心で包括的かつ楽しいエクスペリエンスを提供するために連携しています。LLM と Grounding Generation API の両方で Elastic にネイティブに接続することで、Vertex AI 上で GAI アプリケーションを構築する際の複雑さとオーバーヘッドが軽減され、不要な追加 API やデータ オーケストレーションが回避され、1 回の統合呼び出しでグラウンディングが可能になります。</p><p>両方のシナリオでどのように機能するかを見てみましょう。</p><p>最初の例は LLM API を使用して実行されます。</p>curl -X POST \
  -H "Authorization: Bearer $(gcloud auth print-access-token)" \
  -H "Content-Type: application/json" \https://us-central1-aiplatform.googleapis.com/v1beta1/projects/&lt;PROJECT_ID&gt;/locations/us-central1/publishers/google/models/gemini-2.0-flash-001:generateContent \
  -d '
{
  "contents": [
    {
      "role": "user",
      "parts": [
        {
          "text": "What's my company car policy?"
        }
      ]
    }
  ],
  "tools": [{
    "retrieval": {
      "externalApi": {
        "api_spec": "ELASTIC_SEARCH",
    "endpoint": "https://&lt;my-elastic-cluster&gt;.gcp.elastic-cloud.com:9243",
    "apiAuth": {
      "apiKeyConfig": {
            "apiKeyString": "ApiKey &lt;API_KEY&gt;"
      }
    },
    "elasticSearchParams": {
      "index": "&lt;my-index&gt;",
      "searchTemplate": "&lt;my-search-template&gt;"
    }
      }
    }
  }]
}<p>上記の例では、Gemini 2.0 Flash へのコンテンツ生成を要求する API の<code>retrieval</code>フィールドを使用して、要求の検索エンジンをコンテキストに応じて設定できます。<code>api_spec</code>を「ELASTIC_SEARCH」に設定すると、API キーやクラスターエンドポイント (Elastic クラスターへのリクエストをルーティングするために必要)、データを取得するインデックス、検索ロジックに使用する検索テンプレートなどの追加の構成パラメータが使用できるようになります。</p><p>同様に、Grounding Generation API を使用して<code>groundingSpec</code>パラメータを設定することで、同じ結果を得ることができます。</p>curl -X POST -H "Authorization: Bearer $(gcloud auth print-access-token)" -H "Content-Type: application/json" https://us-discoveryengine.googleapis.com/v1alpha/projects/&lt;PROJECT_ID&gt;/locations/global:generateGroundedContent -d '
{
  "contents": [{
    "role": "user",
    "parts": [{
      "text": "What do I need to patch a hole in my drywall?"
    }]
  }],
  "groundingSpec": {
    "groundingSources": [{
      "elasticSource": {
        "endpoint": "https://&lt;my-elastic-cluster&gt;.gcp.elastic-cloud.com:9243",
        "index": "&lt;my-index&gt;",
        "searchTemplate": "&lt;my-search-template",
        "apiKey": "projects/&lt;PROJECT_ID&gt;/secrets/api-key/versions/latest"
      }
    }]
  }
}
'<p>どちらのアプローチでも、レスポンスには、クエリをサポートするために、Elasticsearch で見つかった最も関連性の高いプライベート ドキュメントと、関連する接続されたデータ ソースが含まれた回答が提供されます。</p><p>ただし、シンプルであることは、特定のニーズやユースケースを満たすためのパーソナライゼーションの欠如と混同しないでください。これを念頭に置いて、検索構成をシナリオに完全に適応させることができるように設計しました。</p><h2>指先で完全にカスタマイズ可能な検索:検索テンプレート</h2><p>検索シナリオを最大限にカスタマイズするために、Google Cloud と連携して、当社の有名な<a href="https://www.elastic.co/jp/guide/en/elasticsearch/reference/current/search-template.html">検索テンプレート</a>をベースにしたエクスペリエンスを構築しました。Elasticsearch 検索テンプレートは、動的で再利用可能かつ保守可能な検索クエリを作成するための優れたツールです。クエリ構造を事前に定義して再利用することができます。これらは、開発時間を節約し、エラーの可能性を減らすため、異なるパラメータを使用して同様のクエリを実行する場合に特に役立ちます。テンプレートには変数のプレースホルダーを含めることができるため、クエリは動的になり、さまざまな検索要件に適応できるようになります。</p><p>Vertex AI API と Elasticsearch をグラウンディングに使用する場合は、上記のコード スニペットに示すように、検索ロジックが実装され Elasticsearch にプッシュダウンされる目的の検索テンプレートを参照する必要があります。Elastic のパワー ユーザーは、検索アプローチを非同期的に管理、構成、更新し、特定のインデックス、モデル、データに合わせてカスタマイズできます。その操作は、Vertex AI ユーザー、ウェブ アプリ開発者、AI エンジニアにとって完全に透過的な方法で実行でき、グラウンディング API でテンプレートの名前を指定するだけで済みます。</p><p>この設計により完全なカスタマイズが可能になり、Elasticsearch の広範な検索機能を Google Cloud AI 環境で利用できるようになり、モジュール性、透明性、そして Elastic に精通していない開発者にとっても使いやすさが確保されます。</p><p>BM25 検索、セマンティック検索、または 2 つの間のハイブリッド アプローチが必要なときはいつでも (<a href="https://www.elastic.co/jp/guide/en/elasticsearch/reference/current/retrievers-overview.html">リトリーバー</a>をすでに調べましたか?)単一の検索 API 呼び出しで構成可能な検索テクニックを使用すれば、検索テンプレートでカスタム ロジックを定義でき、Vertex AI はそれを自動的に活用できます。</p><p>これは、ベクトルと結果を管理するために選択した埋め込みおよび再ランク付けモデルにも適用されます。ユースケースに応じて、Elastic の ML ノードでモデルをホストしたり、推論 API を介してサードパーティのサービスエンドポイントを使用したり、オンプレミスでローカルモデルを実行したりすることができます。これは検索テンプレートを介して実行可能であり、次のセクションでその仕組みを見ていきます。</p><h2>参照テンプレートから始めて、独自のテンプレートを作成しましょう</h2><p>すぐに使い始められるように、初期リファレンスとして使用できる互換性のある検索テンプレートのサンプル セットを用意しました。その後、それを基にしてカスタムの検索テンプレートを変更および構築できます。</p><ul><li><p>ELSER モデルによるセマンティック検索 (スパースベクトルとチャンキング)</p></li><li><p>e5多言語モデルによるセマンティック検索（密ベクトルとチャンキング）</p></li><li><p>Vertex AI テキスト埋め込みモデルを使用したハイブリッド検索</p></li></ul><p>これらは、この<a href="https://github.com/elastic/elasticsearch-labs/tree/main/supporting-blog-content/Cloud-Vertex-AI/search-templates">GitHub リポジトリ</a>で見つけることができます。</p><p>1 つの例を見てみましょう。製品カタログに Google Cloud の Vertex AI API を使用して埋め込みを作成します。まず、以下に示すように Elasticsearch で検索テンプレートを作成する必要があります。</p>PUT _scripts/google-template-knn
{
  "script": {
    "lang": "mustache",
    "source": {
      "_source": {
        "excludes": [ "title_embedding", "description_embedding", "images" ]
      },
        "size": "{{num_hits}}",
          "knn" : [
          { 
            "field": "description_embedding",
            "k": 5,
            "num_candidates": 10,
            "query_vector_builder": {
              "text_embedding": {
                "model_id": "googlevertexai_embeddings_004",
                "model_text": "{{query}}"
              }
            },
            "boost": 0.4
          },
          {
            "field": "title_embedding",
            "k": 5,
            "num_candidates": 10,
            "query_vector_builder": {
              "text_embedding": {
                "model_id": "googlevertexai_embeddings_004",
                "model_text": "{{query}}"
            }
          },
          "boost": 0.6
          }
          ]
    }  
  }
}<p>この例では、1 回の検索内で 2 つのフィールド ( <code>title_embedding</code> – 製品の名前を含むベクトル フィールド – と<code>description_embedding</code> – 製品の説明の表現を含むベクトル フィールド) に対して KNN 検索を実行します。</p><p><code>excludes</code>構文を利用すると、LLM に不要なフィールドが返されることを回避できます。不要なフィールドが返されると、処理中にノイズが発生し、最終的な回答の品質に影響を及ぼす可能性があります。この例では、ベクターと画像の URL を含むフィールドを除外しました。</p><p>ベクトルは、以前は次のように定義されていた Vertex AI 埋め込み API <code>googlevertexai_embeddings_004</code>への推論エンドポイントを介して送信された入力に対して、クエリ時にオンザフライで作成されます。</p>PUT /_inference/text_embedding/googlevertexai_embeddings_004
{
    "service": "googlevertexai",
    "service_settings": {
        "service_account_json": "&lt;your_service_account_key&gt;",
        "model_id": "text-embedding-004",
        "location": "us-central1",
        "project_id": "&lt;your_gcp_project&gt;"
    }
}<p>Elastic の推論 API の使用方法に関する追加情報については、<a href="https://www.elastic.co/jp/guide/en/elasticsearch/reference/current/inference-apis.html">こちらを</a>ご覧ください。</p><p>これで、テンプレート検索をテストする準備が整いました。</p>GET product-catalog-with-embeddings/_search/template
{
  "id": "google-template-knn",
  "params": {
    "query": "What do I need to patch a hole in my drywall?",
    "index_name": "product-catalog-with-embeddings",
    "num_hits": 3
  }
}<p><code>params</code>フィールドは、テンプレート スクリプトで二重中括弧内に設定した変数を置き換えます。現在、Vertex AI LLM および Grounded Generation API は、次の入力変数を Elastic に送信できます。</p><ul><li><p>「クエリ」 - 検索対象となるユーザークエリ</p></li><li><p>「index_name」 - 検索するインデックスの名前</p></li><li><p>「num_hits」 - 最終出力で取得したいドキュメントの数</p></li></ul><p>出力例は次のとおりです。</p>{
        "_index": "product-catalog-with-embeddings",
        "_id": "9ZQCm5IBcrGI1ivqV-f_",
        "_score": 0.4925191,
        "_ignored": [
          "description.keyword",
          "images.keyword"
        ],
        "_source": {
          "description": "DAP Eclipse Rapid Wall Repair Patch is a new, revolutionary product solution for repairing drywall damage. No more waiting for spackling to dry or messy sanding. DAP Eclipse allows you to patch drywall damage and paint immediately, allowing you to finish your project faster. This all-in-1, mess free solution not only provides a permanent, long-lasting repair but also superior impact resistance for areas that may see reoccurring impact, such as behind a door.",
          "availability": "InStock",
          "model_id": "googlevertexai_embeddings_004",
          "title": "4 in. Eclipse Wall Repair Patch (2-Pack)",
          "url": "https://www.myDIYwebsite.com/p/DAP-4-in-Eclipse-Wall-Repair-Patch-2-Pack-7079809164/317967195",
          "price": 23.96,
          "product_id": 317967195,
          "currency": "USD",
          "brand": "DAP"
        }<p>上記のクエリは、Google Cloud の Vertex AI が、以前に作成した検索テンプレートを参照するときに、バックグラウンドで Elasticsearch 上で実行するクエリとまったく同じです。Gemini モデルは、出力ドキュメントを使用して回答を根拠付けます。「乾式壁の補修には何が必要ですか?」と質問すると、一般的な提案ではなく、チャット エージェントが具体的な製品を提供します。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte88a0c34242df6fa/6a17e6263e03d704504f2c2e/8c8b1374da7e9b2c17df8758acb448eed5b74e2d-1473x913.png" alt="GoogleのVertex AIプラットフォームでプロンプトを作成する" /><h2>ElasticとGoogle CloudによるエンドツーエンドのGenAIジャーニー</h2><p>Elastic は Google Cloud と提携して、本番環境対応のエンドツーエンドの GenAI エクスペリエンスとソリューションを作成します。先ほど見てきたように、Elastic は Vertex AI プラットフォームの UI と SDK に直接統合された最初の ISV であり、シームレスで基盤のある Gemini モデル プロンプトとエージェントが当社のベクトル検索機能を使用して実行できるようになります。さらに、Elastic は<a href="https://www.elastic.co/jp/guide/en/elasticsearch/reference/current/infer-service-google-vertex-ai.html">Vertex AI</a>および<a href="https://www.elastic.co/jp/guide/en/elasticsearch/reference/current/infer-service-google-ai-studio.html">Google AI Studio</a>の埋め込み、再ランク付け、補完モデルと統合し、Google Cloud 環境を離れることなくベクトルを作成およびランク付けして、<a href="https://cloud.google.com/responsible-ai?hl=en">責任ある AI の</a>原則を保証します。マルチモーダルアプローチをサポートすることで、さまざまなデータ形式にわたるアプリケーションを共同で促進します。</p><p><a href="https://www.elastic.co/jp/search-labs/blog/vertex-ai-elasticsearch-playground-fast-rag-apps">Playground</a>を介して GenAI 検索コードを調整、テスト、エクスポートできます。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt6a978835a5418eb9/6a17e628fbc5f8abff491a6a/b1fc48dba1713cd01c4d7f3296587d0c4e6e7e0c-862x651.png" alt="ElasticとGoogle CloudによるエンドツーエンドのGenAIジャーニー " /><p>しかし、検索アプリの構築だけではありません。ElasticはGeminiモデルを活用して、 <a href="https://www.elastic.co/jp/blog/elastic-google-vertex-ai-integration">Elastic AIアシスタント、攻撃検出、自動インポート機能</a>などのIT運用を強化し、セキュリティアナリストやSREの低価値タスクによる日々の疲労を軽減し、ビジネスの改善に集中できるようにします。Elastic では、 <a href="https://www.elastic.co/jp/guide/en/integrations/current/gcp_vertexai.html">Vertex AI の使用状況を包括的に監視し</a>、応答時間、トークン、リソースなどのメトリックとログを追跡して、最適なパフォーマンスを確保することもできます。私たちは協力して、データの取り込みと埋め込み生成からハイブリッド検索によるグラウンディングまで、GenAI のライフサイクル全体を管理し、LLM を活用したアクションによって GenAI ツールの堅牢な観測性とセキュリティを確保します。</p><h2>さらに詳しく調べて試してみてください!</h2><p>これを試してみませんか？この機能は現在、Google Cloud プロジェクトで GA になっています。</p><p>Elastic Search AI Platform を使い始めて、その機能を試す最も簡単な方法の一つは、まだお試しいただいていない場合、 <a href="https://cloud.elastic.co/registration">Elastic Cloud の無料トライアル</a>を利用するか、 <a href="https://console.cloud.google.com/marketplace/product/elastic-prod/elastic-cloud?pli=1">Google Cloud Marketplace</a>からサブスクライブすることです。</p><p><em>この投稿で説明されている機能のリリースとタイミングは、Elastic の独自の裁量により決定されます。現在利用できない機能は、予定どおりに提供されないか、まったく提供されない可能性があります。Elastic、Elasticsearch および関連するマークは、米国およびその他の国における Elasticsearch NV の商標、ロゴ、または登録商標です。その他すべての会社名および製品名は、それぞれの所有者の商標、ロゴ、または登録商標です。</em></p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/elasticsearch-google-cloud-vertex-ai-native-grounding</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/elasticsearch-google-cloud-vertex-ai-native-grounding</guid>
    <category><![CDATA[Vector Database]]></category>
    <dc:creator><![CDATA[Valerio Arvizzigno]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt973959b32f2070d6/6a17e62a6317304ec1585a5e/d1f1c8860f1f0b989ad698a882f869de7284ab78-1200x628.png" length="0" type="image/png"/>
    <pubDate>Wed, 09 Apr 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[HNSWグラフのマージを高速化する]]></title>
    <description><![CDATA[複数の HNSW グラフを構築する際のオーバーヘッドを削減するために、特にグラフのマージにかかるコストを削減するために私たちが行ってきた作業について説明します。]]></description>
    <content:encoded><![CDATA[<p>以前、複数の<a href="https://www.elastic.co/jp/search-labs/blog/hnsw-graph"> HNSW グラフを</a> 検索する必要がある場合に生じるいくつかの課題と、その課題をどのように軽減できるか<a href="https://www.elastic.co/jp/search-labs/blog/multi-graph-vector-search"> について説明しました 。</a>当時、私たちは計画していたさらなる改善点についても触れました。この投稿はその作業の集大成です。</p><p>なぜ複数のグラフを使用するのかと疑問に思うかもしれません。これは、Lucene のアーキテクチャ上の選択である不変セグメントによる副作用です。ほとんどのアーキテクチャ上の選択と同様に、長所と短所があります。たとえば、最近 Serverless Elasticsearch が GA になりました。この文脈において、私たちは不変セグメントから、効率的なインデックス レプリケーションや、インデックスとクエリの計算を分離して個別に自動スケーリングする機能など、非常に大きなメリットを得ています。ベクトル量子化の場合、セグメントのマージにより、パラメータを更新してデータ特性に適合させることができます。これに沿って、データ特性を測定し、インデックスの選択を再検討する機会を持つことで得られる他の利点もあると考えています。</p><p>この投稿では、複数の HNSW グラフを構築する際のオーバーヘッドを大幅に削減し、特にグラフのマージにかかるコストを削減するために私たちが行ってきた作業について説明します。</p><h3>背景</h3><p>管理可能な数のセグメントを維持するために、Lucene はセグメントをマージする必要があるかどうかを定期的に確認します。これは、現在のセグメント数が、基本セグメント サイズとマージ ポリシーによって決定されるターゲット セグメント数を超えているかどうかを確認することになります。カウントを超過すると、Lucene は制約に違反しながらセグメントのグループを結合します。このプロセスについては<a href="https://blog.mikemccandless.com/2011/02/visualizing-lucenes-segment-merges.html">他の場所で</a>詳しく説明されています。</p><p>Lucene は、書き込み増幅の対数的増加を実現するため、同様のサイズのセグメントを結合することを選択します。ベクトル インデックスの場合、書き込み増幅はベクトルがグラフに挿入される回数です。Luceneはセグメントを約10個のグループにまとめ、マージしようとします。その結果、ベクトルはグラフに約挿入されます。ここで、 はインデックス ベクトル数、 予想される基本セグメント ベクトル数です。対数増加のため、巨大なインデックスの場合でも書き込み増幅は 1 桁になります。ただし、グラフのマージに費やされる合計時間は、書き込み増幅に直線的に比例します。</p><p>HNSW グラフをマージする際には、最大セグメントのグラフを保持し、他のセグメントのベクトルをそのグラフに挿入するという、小さな最適化をすでに行っています。これが上記の 9/10 要因の理由です。以下では、マージするすべてのグラフの情報を使用することで、どのように大幅に改善できるかを示します。</p><h3>HNSW グラフのマージ</h3><p>これまでは、最大のグラフを保持し、他のグラフからベクトルを挿入し、それらを含むグラフは無視していました。以下で利用する重要な洞察は、破棄する各 HNSW グラフには、それに含まれるベクトルに関する重要な近接情報が含まれているということです。この情報を使用して、少なくとも一部のベクトルの挿入を高速化したいと考えています。</p><p>これは任意のマージポリシーを構築するために使用できるアトミック操作であるため、小さなグラフを大きなグラフに挿入する問題に焦点を当てます。</p><p>戦略は、 の頂点のサブセットを見つけて、大きなグラフに挿入することです。次に、小さなグラフ内のこれらの頂点の接続性を使用して、残りの頂点の挿入を高速化します。以下では、小さいグラフと大きいグラフの頂点 の隣接点をそれぞれ と で表します。図式的にプロセスは次のとおりです。</p><p><code>MERGE-HNSW</code></p><p><code>Inputs </code><code> and </code></p><p><code>1</code><code>Find </code><code> to insert into </code><code> using COMPUTE-JOIN-SET</code>
<code>2</code><code>Insert each vertex </code><code> into </code>
<code>3</code><code>for </code><code> do</code>
<code>4</code>
<code>5</code>
<code>6</code><code>FAST-SEARCH-LAYER</code>
<code>7</code><code>SELECT-NEIGHBORS-HEURISTIC</code>
<code>8</code></p><p>以下で説明する手順 (1 行目) を使用して、セットを計算します。次に、標準の HNSW 挿入手順 (2 行目) を使用して、 内のすべての頂点を大きなグラフに挿入します。挿入していない各頂点については、挿入した隣接頂点と、大きなグラフ内でのその隣接頂点を見つけます (4 行目と 5 行目)。このセットでシードされた<code>FAST-SEARCH-LAYER</code>手順 (6 行目) を使用して、HNSW<a href="https://arxiv.org/pdf/1603.09320">論文</a>から<code>SELECT-NEIGHBORS-HEURISTIC</code>の候補を検索します (7 行目)。実際には、 <code>SEARCH-LAYER</code> <code>INSERT</code>メソッド (論文のアルゴリズム 1) の候補セットを見つけるために置き換えていますが、それ以外は変更されていません。最後に、先ほど挿入した頂点を (行 8) に追加します。</p><p>これが機能するためには、 内のすべての頂点が内に少なくとも 1 つの隣接頂点を持つ必要があることは明らかです。実際、 内のすべての頂点に対して、  (あるに対して) が最大のレイヤー接続性であることが必要です。実際の HNSW グラフでは、頂点次数がかなり広がっていることがわかります。下の図は、Lucene HNSW グラフの最下層の頂点次数の典型的な累積密度関数を示しています。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltc815e1a9c3bb0d06/6a17e178ec0f89308a5a6564/44f001b3a1bc6627e172fed5c52a02cfdcb4cd66-1324x898.png" alt="HNSWグラフ: 頂点次数分布の例" /><p>に固定値を使用することと、それを頂点次数の関数にすることを検討しました。この2番目の選択肢は、グラフの品質への影響を最小限に抑えながら大幅なスピードアップにつながるため、次の方法を採用しました。</p><p>定義により、  | は小さなグラフの頂点の次数に等しいことに注意してください。下限が 2 の場合、次数が 2 未満のすべての頂点が挿入されます。</p><p>単純な計算の議論によれば、 慎重に選択すれば、約に直接挿入するだけでよいことがわかります。具体的には、グラフの端の頂点の 1 つをに挿入すると、グラフのエッジに色を付けます。すると、 内に少なくとも 個の 隣接頂点を持つためには、少なくとも 個の 辺を色付けする必要があることがわかります。さらに、我々は</p><p>ここで、 小さなグラフ内の平均頂点次数です。J内の各頂点に対して、最大での辺を色付けします。したがって、色付けすると予想されるエッジの総数は最大でとなります。慎重に選択することで、この辺の数に近い色を塗ることができると期待しており、すべての頂点をカバーするには満たす必要がある。</p><p>これは、 を意味します。</p><p><code>SEARCH-LAYER</code>が実行時間を支配すると、マージ時間を最大高速化できる可能性があります。書き込み増幅の対数的増加を考慮すると、非常に大きなインデックスの場合でも、通常は 1 つのグラフを構築する場合と比べて構築時間が 2 倍になるだけです。</p><p>この戦略のリスクは、グラフの品質が損なわれることです。最初は何も実行しない<code>FAST-SEARCH-LAYER</code>を試しました。これにより、特に単一のセグメントにマージするときに、レイテンシの関数としてのリコールが影響を受ける程度にグラフの品質が低下することがわかりました。次に、グラフの限定的な検索を使用して、さまざまな代替案を検討しました。結局、最も効果的な選択は最も単純なものでした。<code>SEARCH-LAYER</code>を使用しますが、 <code>ef_construction</code>は低くします。このパラメータ化により、優れた品質のグラフを実現しながらも、マージ時間を平均で 30% 強短縮することができました。</p><h3>結合セットの計算</h3><p>適切な結合セットを見つけることは、HNSW グラフ カバー問題として定式化できます。貪欲ヒューリスティックは、最適なグラフカバーを近似するためのシンプルで効果的なヒューリスティックです。私たちが採用するアプローチでは、ゲインが減少する順に頂点を 1 つずつ選択してに追加します。ゲインは次のように定義されます。</p><p>ここで、 におけるベクトル の近傍の数を表し、 は 指示関数である。ゲインには、 に追加した頂点の数の変化、つまりが含まれます。これは、カバーされていない頂点を追加することで目標に近づくためです。中央のオレンジ色の頂点のゲイン計算を下の図に示します。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt7a353d56fa09af5f/6a17e17a2f4a5c33f7fa8825/fe11cd55a7d94d9e0b0f4d47ea309a99075c355d-540x474.png" alt="HNSWグラフの結合集合Jに追加する頂点ゲイン" /><p>各頂点に対して次の状態を維持します。</p><ol><li><p>古くなっても、</p></li><li><p>そのゲイン 、</p></li><li><p>内の隣接頂点の数はと表され、</p></li><li><p>タイブレークに使用される範囲 [0,1] 内の乱数。</p></li></ol><p>結合セットを計算するための疑似コードは次のとおりです。</p><p><code>COMPUTE-JOIN-SET</code></p><p><code>Inputs </code></p><p><code>1</code>
<code>2</code>
<code>3</code><code>for </code><code> do</code>
<code>4</code>
<code>5</code>
<code>6</code>
<code>7</code><code>while </code><code> do
8</code><code> maximum gain vertex in </code>
<code>9</code><code>Remove the state for </code><code> from </code>
<code>10</code><code>if </code><code> is not stale then</code>
<code>11</code>
<code>12</code>
<code>13</code><code>for </code><code> do</code>
<code>14</code><code>mark </code><code> as stale if </code>
<code>15</code><code>mark neighbors of </code><code> stale if </code><code>
16</code><code>
17</code><code>else
18</code><code>
19</code><code>if </code><code> then
20</code><code>
21</code><code>return </code></p><p>まず 1 行目から 5 行目で状態を初期化します。</p><p>メイン ループの各反復では、最初に最大ゲイン頂点 (行 8) を抽出し、同点の場合はランダムに決定します。変更を加える前に、頂点のゲインが古くなっているかどうかを確認する必要があります。特に、 に頂点を追加するたびに、他の頂点のゲインに影響を与えます。</p><ol><li><p>すべての隣接ノードはに追加の隣接ノードを持つため、ゲインは変化する可能性がある（14行目）</p></li><li><p>いずれかの隣接セルが完全にカバーされている場合、その隣接セルのゲインはすべて変化する可能性があります（14～16行目）</p></li></ol><p>ゲインは遅延方式で再計算されるため、頂点をに挿入する場合にのみ、その頂点のゲインを再計算します (18 ～ 20 行目)。ゲインは常に減少するだけなので、挿入すべき頂点を見逃すことは決してありません。</p><p>いつ終了するかを決定するには、 に追加した頂点の合計増加を追跡するだけでよいことに注意してください。さらに、 の場合、少なくとも 1 つの頂点のゲインはゼロではないため、常に進歩します。</p><h3>成果</h3><p>私たちは、サポートされている 3 つの距離メトリック (ユークリッド、コサイン、内積) をカバーする 4 つのデータセットで実験を実行しました。</p><ol><li><p>quora-E5-small: 522931文書、384次元、コサイン類似度を使用、</p></li><li><p>cohere-wikipedia-v2: 100万文書、768次元、コサイン類似度を使用。</p></li><li><p>要点: 100万文書、960次元、ユークリッド距離を使用、</p></li><li><p>cohere-wikipedia-v3: 100 万件のドキュメント、1024 次元、最大内積を使用します。</p></li></ol><p>各データセットに対して、2 つの量子化レベルを評価します。</p><ol><li><p>int8 – 次元ごとに1バイトの整数を使用し、</p></li><li><p>BBQ – 次元ごとに 1 ビットを使用します。</p></li></ol><p>最後に、各実験について、2 つの検索深度で検索品質を評価し、インデックスを構築した後と、単一のセグメントに強制的にマージした後を調べます。</p><p>要約すると、グラフの品質を維持しながら、インデックス作成とマージの速度を一貫して大幅に向上させ、すべてのケースで検索パフォーマンスを実現できます。</p><h4>実験1: int8量子化</h4><p>ベースラインから候補、つまり提案された変更までの平均的な高速化は次のとおりです。</p><p>インデックス時間の高速化: <strong>1.28</strong> </p><p>強制マージの高速化: <strong>1.72</strong></p><p>これは実行時間の次の内訳に対応する。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt8422be122ef1a9c9/6a17e17bbe6086522e00467d/5f9f6d487b4c74c4998aa47465912c1a9743e928-734x479.png" alt="ベースラインと候補のマージ戦略のインデックスとマージ時間" /><p>完全性のために正確な時間は</p><p></p><p>索引</p><p></p><p>マージ</p><p></p><p>データセット</p><p>ベースライン</p><p>候補者</p><p>構築</p><p>候補者</p><p>quora-E5-small</p><p>112.41秒</p><p>81.55秒</p><p>113.81秒</p><p>70.87秒</p><p>ウィキコヒアv2</p><p>158.1秒</p><p>122.95秒</p><p>425.20秒</p><p>239.28秒</p><p>要旨</p><p>141.82秒</p><p>119.26秒</p><p>536.07秒</p><p>279.05秒</p><p>ウィキコヒアv3</p><p>211.86秒</p><p>168.22秒</p><p>654.97秒</p><p>414.12秒</p><p>以下に、複数のセグメントを持つインデックス（すべてのベクトルをインデックスした後のデフォルトのマージ戦略の最終結果）と単一セグメントへの強制マージ後のインデックスの、2 つの検索深度（recall@10 と recall@100）と、単一セグメントへの強制マージ後の、候補（破線）とベースラインを比較したリコール対レイテンシのグラフを示します。曲線が高く、左に寄っているほど良好であり、これは、より低いレイテンシでより高い再現性を意味します。</p><p>ご覧のとおり、複数のセグメント インデックスの場合、候補は Cohere v3 データセットの方が優れており、他のすべてのデータセットではわずかに劣りますが、ほぼ同等です。単一セグメントに統合した後、リコール曲線はすべてのケースでほぼ同じになります。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltf674622469bf6a90/6a17e17dfbc5f874a84919c9/9195297f19f90b172d832ba56bdada7b0d8a768b-985x392.png" alt="インデックス構築後のレイテンシと@10および@100の関係を思い出してください" /><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltde89f7e94ac823f4/6a17e17fe9ea876429a9c507/c866f32d579ae2b841e92ca733dd29c0e214ac6b-986x386.png" alt="単一セグメントにマージした後のレイテンシと@10および@100のリコール" /><h4>実験2：BBQ量子化</h4><p>ベースラインから候補までの平均的な高速化は次のとおりです。</p><p>インデックス時間の高速化: <strong>1.33</strong> </p><p>強制マージの高速化: <strong>1.34</strong></p><p>これは実行時間の次の内訳に対応する。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5b0eb305222fa5c2/6a17e18125daab514f08a18c/11be0cf6b63480dc703409329357ea4847b55051-740x415.png" alt="ベースラインと候補のマージ戦略のインデックスとマージ時間" /><p>完全性のために正確な時間は</p><p></p><p>索引</p><p></p><p>マージ</p><p></p><p>データセット</p><p>ベースライン</p><p>候補者</p><p>構築</p><p>候補者</p><p>quora-E5-small</p><p>70.71秒</p><p>58.25秒</p><p>59.38秒</p><p>40.15秒</p><p>ウィキコヒアv2</p><p>203.08秒</p><p>142.27秒</p><p>107.27秒</p><p>85.68秒</p><p>要旨</p><p>110.35秒</p><p>105.52秒</p><p>323.66秒</p><p>202.2秒</p><p>ウィキコヒアv3</p><p>313.43秒</p><p>190.63秒</p><p>165.98秒</p><p>159.95秒</p><p>複数のセグメント インデックスの場合、ベースラインがわずかに優れている cohere v2 を除き、ほぼすべてのデータセットで候補の方が優れています。単一セグメントインデックスの場合、リコール曲線はすべてのケースでほぼ同じです。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltb12174abeadbcdd1/6a17e1822f4a5c4cc2fa8829/7da1be27bab5a7f5f9c9a1882ea35761a4a0ba5c-973x383.png" alt="インデックス構築後のレイテンシと@10および@100の関係を思い出してください" /><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltf1b88d4f7fec9905/6a17e184414c64a8de9450b4/a26af30a56c55a12addee7e4fb7e8f606f366668-979x386.png" alt="@10と@100を1つのセグメントに統合した場合のレイテンシを思い出してください" /><h3>まとめ</h3><p>このブログで説明したアルゴリズムは、今後リリースされる Lucene 10.2 およびそれに基づく Elasticsearch リリースで利用できるようになります。ユーザーは、これらの新しいバージョンで改善されたマージ パフォーマンスと短縮されたインデックス構築時間を活用できるようになります。この変更は、Lucene と Elasticsearch をベクター検索とハイブリッド検索に高速かつ効率的にするための継続的な取り組みの一環です。</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/hnsw-graphs-speed-up-merging</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/hnsw-graphs-speed-up-merging</guid>
    <category><![CDATA[Vector Database]]></category>
    <category><![CDATA[Lucene]]></category>
    <dc:creator><![CDATA[Thomas Veasey,Mayya Sharipova]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt9fca6a01d0541cfb/6a17e187033c8d46486bb0e1/49a6c880f5dedd0fa502ece5be124824ee218cc0-1792x1024.png" length="0" type="image/png"/>
    <pubDate>Mon, 07 Apr 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Elasticsearchでの後期相互作用モデルのスケーリング - パート 2]]></title>
    <description><![CDATA[この記事では、後期相互作用ベクトルを大規模な本番環境のワークロードに適したものにするための技術を探ります。これには、ディスク容量の使用を減らし、計算効率を向上させることが含まれます。]]></description>
    <content:encoded><![CDATA[<p><a href="https://www.elastic.co/search-labs/blog/elastiacsearch-colpali-document-search">前回のColPaliブログ</a>では、Elasticsearchを使ってビジュアル検索アプリケーションを作成する方法を探りました。ColPaliなどのモデルがアプリケーションにもたらす価値に主に焦点を当てましたが、E5などのバイエンコーダーを使ったベクトル検索に比べてパフォーマンス上の欠点があります。</p><p>このブログでは、<a href="https://www.elastic.co/search-labs/blog/elastiacsearch-colpali-document-search">パート1</a>の例を基に、後期相互作用ベクトルを大規模な本番ワークロードに対応させるために、さまざまなテクニックとElasticsearchの強力なベクトル検索ツールキットをどのように使用するかを説明します。</p><p>完全なコード例は<a href="https://github.com/elastic/elasticsearch-labs/tree/main/supporting-blog-content/colpali">GitHub</a>にあります。</p><h2>後期相互作用モデルの課題</h2><p>ColPaliは、インデックス内のドキュメントに対してページごとに1000を超えるベクトルを作成します。</p><p>これにより、後期相互作用ベクトルを扱う際に2つの課題が生じます。</p><ol><li><p>ディスク容量：これらのベクトルをすべてディスクに保存すると、大量のストレージ使用量が発生し、規模が大きくなるとコストが高くなります。</p></li><li><p>計算：ドキュメントのランキングを行う際に、<code>maxSimDotProduct()</code>比較を使用すると、各ドキュメントのすべてのベクトルをクエリのN個のベクトルと比較する必要があります。</p></li></ol><p>これらの問題に対処するためのいくつかの手法を見てみましょう。</p><h2>後期相互作用モデルを最適化する技術</h2><h3>ビットベクトル</h3><p>ディスク容量を減らすために、画像をビットベクトルに圧縮することができます。Pythonの簡単な関数を使用して、マルチベクトルをビットベクトルに変換できます。</p>def to_bit_vectors(embeddings: list) -&gt; list:
    return [
        np.packbits(np.where(np.array(embedding) &gt; 0, 1, 0))
        .astype(np.int8)
        .tobytes()
        .hex()
        for embedding in embeddings
    ]<p>この関数の基本的な概念は単純で、0より大きい値は1に、0より小さい値は0になり、これが0と1の配列となり、それをビットベクトルを表す16進数の文字列に変換するというものです。</p><p>インデックスマッピングでは、<code>element_type</code>パラメータを<code>bit</code>に設定します。</p>mappings = {
    "mappings": {
        "properties": {
            "col_pali_vectors": {
                "type": "rank_vectors",
                "element_type": "bit"
            }
        }
    }
}

es.indices.create(index=INDEX_NAME, body=mappings)<p>新しいビットベクトルをすべてインデックスに書き込んだら、次のコードを使ってビットベクトルをランク付けします。</p>query = "What do companies use for recruiting?"
query_vector = to_bit_vectors(create_col_pali_query_vectors(query))
es_query = {
    "_source": False,
    "query": {
        "script_score": {
            "query": {
                "match_all": {}
            },
            "script": {
                "source": "maxSimInvHamming(params.query_vector, 'col_pali_vectors')",
                "params": {
                    "query_vector": query_vector
                }
            }
        }
    },
    "size": 5
}<img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltcaf0c8f999d68701/6a17f46696142a6f46eb1c56/51b989446e4099745971e1eac27d147a78d13e0a-1600x480.png" alt="" /><p>精度を少し犠牲にすることで、ハミング距離（<code>maxSimInvHamming(...)</code>）を使用することができます。これにより、ビットマスク、SIMDなどの最適化を活用できます。ビットベクトルとハミング距離について詳しくは<a href="https://www.elastic.co/search-labs/blog/bit-vectors-in-elasticsearch">ブログをご覧ください</a>。</p><p>あるいは、クエリベクトルをビットベクトルに変換せずに、完全な忠実度の後期相互作用ベクトルで検索することもできます。</p>query = "What do companies use for recruiting?"
query_vector = create_col_pali_query_vectors(query)
es_query = {
    "_source": False,
    "query": {
        "script_score": {
            "query": {
                "match_all": {}
            },
            "script": {
                "source": "maxSimDotProduct(params.query_vector, 'col_pali_vectors')",
                "params": {
                    "query_vector": query_vector
                }
            }
        }
    },
    "size": 5
}<img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt2fa6c89fb451b6e0/6a17f468af47b65da9cde0d9/89f3c795c6dc44b2f7d46801320288316b47e1b2-1600x488.png" alt="ビットベクトルを用いた後期相互作用モデルの最適化結果" /><p>これにより、非対称類似関数を用いてベクトルを比較します。</p><p></p><p>2つのビットベクトル間の通常のハミング距離について考えてみましょう。以下のドキュメントベクトル<em>D</em>と</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd4b6ece1ec4dedac/6a17f4694b055d248143234e/366a70a23e37d403788327b7aefc873fd4482f5e-1235x86.png" alt="" /><p>クエリベクトル<em>Q</em>があると仮定します。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt7721df9c8f0d84f1/6a17f46b3e03d77cf54f2dcb/978ec31e0afbc0eaebf548a015b628c0cad84625-1247x84.png" alt="" /><p></p><p>シンプルなバイナリ量子化では、ベクトル<em>D</em>を <code>10101101</code>に、<em>Q</em>を<code>11111011</code>に変換します。ハミング距離を見つけるには、直接ビット計算が必要ですが、これは非常に高速です。この場合、ハミング距離は <code>01010110</code>であり、ビット数は4です。したがって、得点はそのハミング距離の逆数になります。類似するベクトルほどハミング距離は小さくなるため、これを反転すると類似するベクトルに高いスコアが付けられるようになります。特にここで、スコアは1/4 = <code>0.25</code>となります。</p><p>ただし、各次元の大きさが失われることに注意してください。<code>1</code>は<code>1</code>です。ですから、<em>Qについては</em>、<code>0.01</code> と<code>0.79</code> の違いはなくなります。単純に<code>&gt;0</code>に従って量子化しているので、Qベクトルが量子化されない小さなトリックを実行できます。これではビット単位の超高速演算はできませんが、Dが量子化されたままなので、ストレージコストは低く抑えられます。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blted50315518e2d599/6a17f46c414c6463709452d3/508e8498ba969534d2a0131d431c75735fc27cb9-1399x611.png" alt="" /><p>つまり、これにより<em>Q</em>で提供される情報が保持され、距離推定の品質が向上し、ストレージが小さく抑えられます。</p><p>ビットベクトルを使用することで、ディスク容量とクエリ時の計算負荷を大幅に節約できます。しかし、できることはまだあります。</p><h3>平均ベクトル</h3><p>数十万、数百万のドキュメントにわたって検索をスケールするには、ビットベクトルがもたらすパフォーマンスの恩恵でさえ十分ではないでしょう。このようなタイプのワークロードにスケールするには、ElasticsearchのHNSWインデックス構造をベクトル検索に活用したいと思うでしょう。</p><p>ColpAliはドキュメントごとに約1000ベクトルを生成しますが、これは多すぎ、HNSWグラフに追加することはできません。したがって、ベクトルの数を減らす必要があります。これを行うには、画像を埋め込むときにColPaliによって生成されたすべてのドキュメントベクトルの平均を取得して、ドキュメントの意味の単一の表現を作成します。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltc053425fbfcf8de8/6a17f46f148009faf1b488aa/7c3b4dffb70bd95f67deb35f8c01e73c5286c2ab-1476x1102.png" alt="すべての遅延相互作用ベクトルの平均ベクトル" /><p>現時点では、これはElastic自体では不可能であり、Elasticsearchに取り込む前にベクトルを前処理する必要があります。 </p><p>Logstashまたは取り込みパイプラインでこれを行うことができますが、ここでは単純なPython関数を使用します。</p>def to_avg_vector(vectors):
    vectors_array = np.array(vectors)
    
    avg_vector = np.mean(vectors_array, axis=0)
    
    norm = np.linalg.norm(avg_vector)
    if norm &gt; 0:
        normalized_avg_vector = avg_vector / norm
    else:
        normalized_avg_vector = avg_vector

    return normalized_avg_vector.tolist()<p>また、ドット積類似度を使用できるようにベクトルを正規化しています。</p><p>ColPaliのベクトルをすべて平均ベクトルに変換したら、それらをdense_vectorフィールドにインデックスできます。</p>mappings = {
    "mappings": {
        "properties": {
            "avg_vector": {
                "type": "dense_vector",
                "dims": 128,
                "index": True,
                "similarity": "dot_product"
            },
            "col_pali_vectors": {
                "type": "rank_vectors",
                "element_type": "bit"
            }
        }
    }
}

es.indices.create(index=INDEX_NAME, body=mappings)<p>これによって、後期相互作用ベクトルとともにさらに多くの情報が保存されるため、合計ディスク使用量が増加することを考慮する必要があります。さらに、HNSWグラフを保持するために追加のRAMを使用して、何十億ものベクトルにわたって検索をスケールできるようにします。RAMの使用を減らすために、人気の <a href="https://www.elastic.co/search-labs/blog/optimized-scalar-quantization-elasticsearch">BBQ機能</a>を活用できます。その結果、通常では不可能な膨大なデータセットに対して高速な検索結果が得られます。</p><p>knnクエリで検索するだけで最も関連性の高いドキュメントを見つけられるようになりました。</p>query = "What do companies use for recruiting?"
query_vector = to_avg_vector(create_col_pali_query_vectors(query))
es_query = {
    "_source": False,
    "knn": {
        "field": "avg_vector",
        "query_vector": query_vector,
        "k": 10,
        "num_candidates": 100
    },
    "size": 5
}<img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltf4ac6b7fd444f935/6a17f471a29299d76ad02dc0/a500f9907020f032c3b1c7a48ed8c759dc2a1dd4-1600x498.png" alt="" /><p>以前最高だったマッチは残念ながら3位に落ちました。</p><p>この問題を解決するために、多段階の取得を行うことができます。最初の段階では、knnクエリを使って数百万件の文書からクエリに最適な候補を検索します。第2段階では、ColPaliの後期相互作用ベクトルの忠実度が高い上位k（ここでは10）のみを再ランクしています。</p>query = "What do companies use for recruiting?"
col_pali_vector = create_col_pali_query_vectors(query)
avg_vector = to_avg_vector(col_pali_vector)
es_query = {
  "_source": False,
  "retriever": {
    "rescorer": {
      "retriever": {
        "knn": {
          "field": "avg_vector",
          "query_vector": avg_vector,
          "k": 10,
          "num_candidates": 100
        }
      },
      "rescore": {
        "window_size": 10,
        "query": {
          "rescore_query": {
            "script_score": {
              "query": {
                "match_all": {}
              },
              "script": {
                "source": "maxSimDotProduct(params.query_vector, 'col_pali_vectors')",
                "params": {
                  "query_vector": col_pali_vector
                }
              }
            }
          }
        }
      }
    }
  },
  "size": 5
}<img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt265edd5f37be40b7/6a17f4733e9e45583dba15e6/797f490860af87fcf29f34668a5c4511419c6fd0-1600x501.png" alt="後期相互作用モデルを最適化するための平均ベクトル使用の結果" /><p>ここでは、8.18で導入された<a href="https://www.elastic.co/guide/en/elasticsearch/reference/8.18/retriever.html#rescorer-retriever">リスコアリトリーバー</a>を使用して結果を再ランク付けしています。再採点後、ベストマッチが再び1位になったことがわかります。 </p><p>注：本番環境では、maxSim関数が以前として相対的に高パフォーマンスであるため、10よりはるかに高いkを使うことができます。</p><h3>トークンプーリング</h3><p>トークンプーリングは、白い背景のパッチなどの冗長な情報をプールすることで、マルチベクトル埋め込みのシーケンスの長さを短縮します。この技術により、ページのシグナルの大部分を維持しながら埋め込みの数を減らすことができます。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt3d643e6ac908f640/6a17f4754b055d3783432352/09eae0b768f4b450e555d7e53e57561bd52de2c0-1412x1056.png" alt="後期相互作用モデルの最適化のためのトークンプーリング" /><p>トークンプーリングは、クラスタリングアルゴリズムを使用して、ドキュメント内の類似したトークン埋め込みをクラスターにグループ化することによって機能します。次に、各クラスターのベクトルの平均を計算して、単一の集約表現を作成します。この集約されたベクトルはグループ内の元のトークンを置き換え、ドキュメントシグナルを大幅に失うことなくベクトルの合計数を削減します。</p><p>ColPaliの論文では、ほとんどのデータセットに対して初期プールファクター値を3とすることを提案しており、これにより元のパフォーマンスの97.8%を保持しつつ、ベクトルの総数を66.7%削減しています。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt2d78a15c6944a1ea/6a17f477414c64a2929452d9/343cc9eaf54af8c7a125d4115838f3c7d5659de0-1600x1007.png" alt="後期相互作用モデル最適化のためのプール係数" /><p>しかし、注意も必要です。非常に密度が高く、テキストが多く、余白がほとんどない「Shift」データセットは、プールファクターが増加するにつれてパフォーマンスが急速に低下します。</p><p>プールされたベクトルを作成するには、colpali_engineライブラリを利用できます。</p>from colpali_engine.compression.token_pooling import HierarchicalTokenPooler

pooler = HierarchicalTokenPooler(pool_factor=3) # test on your data for a good pool_factor

def pool_vectors(embedding: list) -&gt; list:
    tensor = torch.tensor(embedding).unsqueeze(0)
    pooled = pooler.pool_embeddings(tensor)
    return pooled.squeeze(0).tolist()<p>これで、次元が約66.7%削減されたベクトルが得られました。通常通りにインデックス化し、<code>maxSimDotProduct()</code>機能で検索することができます。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltc460c14031814ede/6a17f4794202292bf629f72d/2412c5db7d79a590b96d42fe01f140c42f010612-1600x481.png" alt="後期相互作用モデルの結果" /><p>結果の精度が若干犠牲になりますが、良好な検索結果を得ることができます。</p><p>ヒント：pool_factorを高くすれば（100-200）、平均的なベクトルソリューションとここで説明したソリューションの中間を取ることもできます。ドキュメントあたり5-10ベクトル程度であれば、HNSWインデックスを活用するためにネストされたフィールドでインデックスを作成することが可能になります。</p><h2>クロスエンコーダー、後期相互作用とバイエンコーダーの比較</h2><p>これまでに学んだことを踏まえて、ColPaliやColBERTなどの後期相互作用モデルを他のAI検索手法と比較すると、どのような位置づけになるでしょうか。</p><p>maxSim関数はクロスエンコーダーと比較して安価ですが、クエリとドキュメントのペアごとに2つのベクトルを比較するだけのバイエンコーダーを使用したベクトル検索よりも、依然として多くの比較と計算を必要とします。 </p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltbfd97f3bba3e5b17/6a17f47be31791b0052d5943/75e1fc9e601aa7a6e88f137565c1919166c7a71c-1480x458.png" alt="クロスエンコーダー、後期相互作用モデルとバイエンコーダーの比較" /><p>このため、後期相互作用モデルについては、一般的にトップkの検索結果のリランキングにのみ使用することを推奨します。また、フィールドタイプの名前であるrank_vectorsにもこれを反映しています。</p><p>では、クロスエンコーダーはどうでしょうか。クエリ時に実行するコストが安いため、後期相互作用モデルの方が優れているのでしょうか。これもまた、状況によります。クロスエンコーダーは一般的に高品質な結果を生成しますが、クエリとドキュメントのペアが変換器モデルを通して完全に処理する必要があるため、多くの計算リソースを必要とします。また、ベクトルのインデキシングを必要とせず、ステートレスな方法で動作できるという利点もあります。その結果は次のようになります。</p><ul><li><p>使用ディスク容量の削減</p></li><li><p>よりシンプルなシステム</p></li><li><p>検索結果の品質向上</p></li><li><p>レイテンシが高いため、深いリランキングが困難</p></li></ul><p>一方、後期相互作用モデルでは、この計算の一部をインデックス時にオフロードできるため、クエリのコストが削減されます。その代償として、ベクトルをインデックスする必要があり、インデックスパイプラインがより複雑になり、これらのベクトルを保存するためにより多くのディスク領域が必要になります。</p><p>特にColPaliの場合、画像には大量のデータが含まれているため、画像からの情報の分析は非常に高価です。この場合、クエリ時にこの情報を評価するとリソースが大量に消費され、処理が遅くなるため、トレードオフはColPaliなどの後期相互作用モデルを使用する方に傾きます。 </p><p>ColBertのように、ほとんどのクロスエンコーダーのようにテキストデータを処理する後期相互作用モデル（例：elastic-rerank-v1）では、ディスクの節約とシンプルさのメリットを享受するためにクロスエンコーダーを使用する方が有利になる可能性があります。</p><p>ユースケースに合わせてこれらの長所と短所を比較検討し、最適な検索アプリケーションを構築するためにElasticsearchが提供するさまざまなツールを試してみることをお勧めします。</p><h2>まとめ</h2><p>このブログでは、ColpAliのような後期相互作用モデルをElasticsearchの大規模ベクトル検索用に最適化するさまざまな手法を紹介しました。後期相互作用モデルは検索効率とランキング品質の間の強力なバランスを実現しますが、ストレージと計算に関連する課題ももたらします。</p><p>これらの課題に対処するために、私たちは次の点を検討しました。</p><ul><li><p>ハミング距離や非対称最大類似度などの効率的な類似度計算を活用しながら、ディスク容量を大幅に削減する<strong>ビットベクトル</strong>。</p></li><li><p>複数の埋め込みを単一の密な表現に圧縮する<strong>平均ベクトル</strong>。これにより、HNSW インデックスによる効率的な検索が可能になります。</p></li><li><p>冗長な埋め込みを賢明にマージしながら意味の整合性を保守し、クエリ時の演算処理の負担を軽減する<strong>トークンプーリング</strong>。</p></li></ul><p>Elasticsearchは、ニーズに基づいて検索アプリケーションをカスタマイズおよび最適化するための強力なツールキットを提供します。検索速度、ランキング品質、ストレージ効率のどれを優先するかに関係なく、これらのツールとテクニックを使用すると、実際のアプリケーションのニーズに応じてパフォーマンスと品質のバランスをとることができます。</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/late-interaction-model-colpali-scale</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/late-interaction-model-colpali-scale</guid>
    <category><![CDATA[関連性]]></category>
    <category><![CDATA[Vector Database]]></category>
    <dc:creator><![CDATA[Peter Straßer,Benjamin Trent]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt97a536033e6b0a56/6a17f47dfbc5f88c86491c0d/c780b78a07573f2df1cfef8b29a7109f839b0ab3-1200x628.png" length="0" type="image/png"/>
    <pubDate>Thu, 20 Mar 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[NVIDIA による Elasticsearch の GPU アクセラレーションによるベクトル検索の探究: 第 1 章]]></title>
    <description><![CDATA[NVIDIA cuVS を活用したこのコラボレーションにより、開発者は Elasticsearch のベクトル検索に GPU アクセラレーションを利用できるようになります。]]></description>
    <content:encoded><![CDATA[<p>Elastic Engineering 組織では、ここしばらくベクトル データベースのパフォーマンスの最適化に取り組んできました。私たちの使命は、Lucene と Elasticsearch を最高のベクター データベースにすることです。ハードウェア アクセラレーションの<a href="https://www.elastic.co/jp/blog/accelerating-vector-search-simd-instructions">CPU SIMD 命令</a>により、新しいベクトル データ圧縮技術革新 ( <a href="https://www.elastic.co/jp/search-labs/blog/better-binary-quantization-lucene-elasticsearch">Better Binary Quantization、別名 BBQ</a> ) が導入され、さらに BBQ に対するアルゴリズム アプローチを更新して期待を上回るメリットがもたらされ、<a href="https://www.elastic.co/jp/search-labs/blog/filtered-hnsw-knn-search">フィルター処理された HNSW も高速化されました</a>。要点は、私たちがより速く、より良く、より効率的(より?)なものを構築しているということです。開発者が RAG-gedy の問題を解決するためのベクター データベースです。</p><p>効率を一切犠牲にしないという当社の使命の一環として、NVIDIA GPU という興味深いコンピューター チップを使った高速化の可能性を模索しています。このチップについて聞いたことがあるかもしれません (本当に聞いたことありませんか?)。</p><p>パフォーマンスを重視する場合、指数関数的に多くのデータをインデックスする方法、そこから洞察を取得する方法、ML モデルが関係する場合にそれを実行する方法など、検討すべき問題領域がいくつかあります。GPU があれば、利用可能なメリットをすべて引き出すことができるはずです。</p><p>この記事では、Elasticsearch での GPU アクセラレーションによるベクトル検索について探りながら、NVIDIA ベクトル検索チームとのコラボレーションについて詳しく説明します。この作業により、開発者が実際の Elasticsearch 搭載アプリで GPU と CPU を組み合わせて使用できるユースケースが実現します。楽しい時間です！</p><h2>Elasticsearch GPU</h2><p>Elasticsearch エンジニアリング チームが、ベクトル検索アルゴリズムのバインディングを公開する、開発者向けのオープンソース cuVS Java API エクスペリエンスの構築に協力していることをお知らせします。この作業は、パナマFFIでのこれまでの経験を活用しています。Elasticsearch と Apache Lucene は、インデックス作成中にグラフを構築するために NVIDIA cuVS API を使用します。さて、話が先に進んでしまいましたが、少し戻ってみましょう。</p><p>このコラボレーションの中心となるのは、オープンソースの C++ ライブラリである<a href="https://developer.nvidia.com/cuvs">NVIDIA cuVS</a>です。より高いスループット、より低いレイテンシ、より速いインデックス構築時間を実現することで、ベクター検索に GPU アクセラレーションをもたらすことを目指しています。しかし、Elasticsearch と Apache Lucene は Java で書かれています。これはどのように動作するのでしょうか?</p><p><a href="https://github.com/SearchScale/lucene-cuvs">lucene-cuvs</a>と Elastic-NVIDIA-SearchScale のコラボレーションを導入して、これを Lucene エコシステムに導入し、Elasticsearch での GPU アクセラレーションによるベクトル検索を探索します。最近の NVIDIA cuVS 25.02 リリースでは、cuVS 用の Java API が追加されました。新しい API は実験段階であり、今後も進化していきますが、現在は使用可能です。「Java からネイティブ関数の呼び出しは遅いのではないだろうか?」という疑問が生じるかもしれません。もうない！バインディングには新しい<a href="https://openjdk.org/projects/panama/">Panama FFI</a> (Foreign Function Interface) を使用しており、Java からネイティブ ダウンコールへのオーバーヘッドが最小限に抑えられています。</p><p>私たちはしばらくの間<a href="https://www.elastic.co/jp/search-labs/blog/lucene-and-java-moving-forward-together">、Elasticsearch と Lucene で Panama FFI</a>を使用してきました。すごいですね！でも…いつも「でも」ってあるじゃないですか。FFI には、Java バージョン間での可用性に関する課題があります。私たちは、cuVS API を Java 21 にコンパイルし、その実装を Java 22 をターゲットとしたマルチリリース jar 内にカプセル化することで、この問題を解決しました。これにより、cuVS Java を Lucene および Elasticsearch で直接使用できるようになります。</p><p>さて、cuVS Java API が手に入ったので、他に何が必要でしょうか?</p><h2>CPUの2つのアルゴリズムの物語</h2><p>Elasticsearch は、スケーラブルな近似 KNN 検索のための<a href="https://arxiv.org/abs/1603.09320">HNSW アルゴリズム</a>をサポートしています。ただし、GPU を最大限に活用するために、GPU が提供する高度な並列処理向けに特別に設計された別のアルゴリズム、 <a href="https://arxiv.org/pdf/2308.15136">CAGRA [</a> <a href="https://arxiv.org/pdf/2308.15136"><strong>C</strong></a> <a href="https://arxiv.org/pdf/2308.15136"><em>UDA</em></a> <a href="https://arxiv.org/pdf/2308.15136"><strong>A</strong></a> <a href="https://arxiv.org/pdf/2308.15136"><em>NN</em></a> <a href="https://arxiv.org/pdf/2308.15136"><strong>GRA</strong></a> <a href="https://arxiv.org/pdf/2308.15136"><em>ph</em></a> <a href="https://arxiv.org/pdf/2308.15136">]</a>を使用します。</p><p>CAGRA のサポートを追加する方法に入る前に、Elasticsearch と Lucene が「コーデック形式」を通じてインデックス データにアクセスする方法を見てみましょう。これは、</p><ol><li><p>ディスク上の表現、</p></li><li><p>データの読み書きのためのインターフェース</p></li><li><p>Lucene のセグメントベースのアーキテクチャを処理するための仕組み。</p></li></ol><p>私たちは、cuVS Java API を内部的に使用して GPU 上でインデックス作成と検索を行う新しい KNN (k 近傍法)<a href="https://lucene.apache.org/core/10_1_0/core/org/apache/lucene/codecs/KnnVectorsFormat.html">ベクトル形式</a>を実装しています。ここから、Elasticsearch のマッピングを通じてこのコーデック タイプをインデックス内のフィールド タイプに「plumb」します。その結果、バックアップ インデックスが CAGRA グラフを使用しているか、HNSW グラフを使用しているかに関係なく、既存の KNN クエリは引き続き機能します。もちろん、ここでは多くの詳細について触れていませんが、それについては今後のブログで取り上げる予定です。以下は、GPU アクセラレーション Elasticsearch の高レベルアーキテクチャです。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltb6197b631a34f8b6/6a170b0da6c2b9e60ce7970b/be6b7356c03df4dee7230625c2c9af3b019f93be-756x510.png" alt="" /><p>この新しいコーデック形式のデフォルトは CAGRA です。ただし、CPU 上での検索のために CAGRA グラフを HNSW グラフに変換することもサポートされています。</p><h2>GPUでのインデックス作成と検索：いくつかの「コア」な決定を下す</h2><p>Elasticsearch Serverless のステートレス<a href="https://www.elastic.co/jp/search-labs/blog/stateless-your-new-state-of-find-with-elasticsearch">アーキテクチャ</a>ではインデックス作成と検索が分離されており、責任の区分が明確になっています。私たちは、これらの独立した責任をそれぞれ果たすために最適なハードウェア プロファイルを選択します。</p><p>ユーザーは主に次の 2 つの展開戦略を検討すると予想されます。</p><ol><li><p>GPU でのインデックス作成と検索: インデックス作成中に CAGRA グラフを構築し、検索中に使用します。これは、非常に低遅延の検索が必要な場合に最適です。</p></li><li><p>GPU でインデックスを作成し、CPU で検索する: インデックス作成中に、CAGRA グラフを構築し、それを HNSW グラフに変換します。HNSW グラフはインデックスに保存され、後で CPU で検索に使用できます。</p></li></ol><p>この柔軟性により、さまざまな展開モデルが提供され、コストとパフォーマンスのトレードオフが可能になります。たとえば、インデックス作成サービスでは、検索には低電力の CPU を使用しながら、GPU を使用してグラフをタイムリーに効率的に構築およびマージできます。</p><h2>ElasticsearchにおけるGPUアクセラレーションによるベクトル検索の計画は以下のとおりです。</h2><p>私たちは、コストとパフォーマンスのバランスをとるためのさまざまなノブを提供し、パフォーマンスの向上と展開戦略の柔軟性をユーザーに提供することを楽しみにしています。この作業が詳しく発表された<a href="https://www.nvidia.com/gtc/session-catalog/?tab.catalogallsessionstab=16566177511100015Kus&amp;search=Lucene#/">NVIDIA GTC 2025 セッションはこちらです</a>。</p><p>素晴らしいコラボレーションを実現してくれた NVIDIA と SearchScale のエンジニアリング チームに感謝します。今後のブログでは、実装の詳細とパフォーマンス分析をさらに詳しく検討します。好奇心の帽子をしっかり握ってください🎩!</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/gpu-accelerated-vector-search-elasticsearch-nvidia</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/gpu-accelerated-vector-search-elasticsearch-nvidia</guid>
    <category><![CDATA[Vector Database]]></category>
    <dc:creator><![CDATA[Chris Hegarty,Hemant Malik]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt298e839e708ca11c/6a170b0fb339d560c2769fc2/38bc0377a6adce7eae0099f61902fdbbe644eb4a-1440x960.jpg" length="0" type="image/jpeg"/>
    <pubDate>Wed, 19 Mar 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[ベクトル探索の簡単な紹介]]></title>
    <description><![CDATA[この記事は、セマンティック検索とも呼ばれるベクトル検索の複雑さと、それが Elasticsearch でどのように実装されるかを詳しく説明する 3 部構成の記事の第 1 部です。]]></description>
    <content:encoded><![CDATA[<p>この記事は、セマンティック検索とも呼ばれるベクトル検索の複雑さと、それが Elasticsearch でどのように実装されるかを詳しく説明する 3 部構成の記事の第 1 部です。</p><p>この最初の部分では、埋め込みベクトルの基本とベクトル検索が内部でどのように機能するかについて、一般的な紹介を提供することに重点を置いています。</p><p>最初の記事で学んだ知識をすべて活用して、第<a href="https://www.elastic.co/search-labs/blog/vector-search-set-up-elasticsearch">2 部で</a>は Elasticsearch でベクトル検索を設定する方法を詳しく説明します。</p><p><a href="https://www.elastic.co/search-labs/blog/hybrid-search-elasticsearch">パート 3</a>では、パート 1 と 2 で学んだ内容を活用し、その知識を基にして、Elasticsearch で強力なハイブリッド検索クエリを作成する方法を詳しく検討します。</p><p>この記事の本題に入る前に、時間をさかのぼって、セマンティック検索の重要な概念であるベクトルの歴史を少し振り返ってみましょう。</p><h2>ベクトルは新しいものではない</h2><p>2022年11月にChatGPTが登場して以来、「ベクトル検索」について聞いたり読んだりしない日はないということは、誰もが同意するでしょう。これはどこにでも存在し、広く普及しているため、つい最近登場した最先端技術であるという印象を受けることがよくありますが、実際にはこの技術は 60 年以上前から存在しているのです。このテーマに関する研究は 1960 年代半ばに始まり、最初の研究論文は 1978 年に情報検索の専門家である Gerard Salton 氏とコーネル大学の同僚によって発表されました。Salton の密ベクトル モデルと疎ベクトル モデルに関する研究は、現代のベクトル検索テクノロジの根幹を成しています。</p><p>過去 20 年間で、彼の研究に基づいたさまざまな<a href="https://db-engines.com/en/ranking/vector+dbms/all">ベクトル DBMS が</a>作成され、市場に投入されました。これらには、2019 年に<a href="https://issues.apache.org/jira/browse/LUCENE-9004">ベクター検索の取り組み</a>を開始した Apache Lucene プロジェクトを搭載した Elasticsearch が含まれます。</p><p>ベクトルは現在どこにでも存在し、広く普及しているため、ベクトルを扱う前に、まずその基礎となる理論と内部の仕組みをしっかり理解することが重要です。その詳細に入る前に、語彙検索とベクトル検索の違いを簡単に確認して、それらの違いと相互補完性について理解を深めましょう。</p><h2>ベクトル検索と語彙検索</h2><p>ベクトル検索を紹介する簡単な方法は、おそらく慣れている従来の語彙検索と比較することです。ベクトル検索 (セマンティック検索とも呼ばれます) と語彙検索の動作は大きく異なります。字句検索は、Elasticsearch で長年使用されてきた種類の検索です。簡単にまとめると、インデックスされてクエリされた内容の本当の意味を理解しようとするのではなく、ユーザーが<strong>クエリに</strong>入力した単語のリテラルまたはその変形（語幹、同義語など）を、TF-IDFなどの類似性アルゴリズムを使用して以前にデータベースにインデックスされたすべてのリテラルと語彙的に一致させることに多大な努力を払います。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltfbb8e150865a5ec8/6a17e0c725daab50f408a16e/a4f3eba19599e5330e9b0d29ecdbbeac211cdab0-1600x583.png" alt="語彙検索の例" /><p>ご覧のとおり、左上の 3 つのドキュメントはトークン化され、分析されています。次に、結果の用語は転置インデックスにインデックス付けされ、分析された用語がそれらを含むドキュメント ID に単純にマッピングされます。すべての用語は 1 回だけ存在し、どのドキュメントでも共有されないことに注意してください。「nice german teacher」を検索すると、いずれもクエリの真の意味を捉えていないにもかかわらず、さまざまなスコアで 3 つのドキュメントすべてが一致します。</p><p>下の図 2 に見られるように、多義語や同音異義語、つまり綴りは同じだが<strong>意味が異なる</strong>単語 (right、palm、bat、mean など) を扱う場合はさらに複雑になります。3 つの異なる意味を持つ「right」という単語を取り上げ、何が起こるかを見てみましょう。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltea0cc829ff2c8029/6a17e0c92f4a5c8107fa8811/ebb9cc1be03475bbf68269a8dcea5f08bcda0b64-1594x754.png" alt="語彙検索による同音異義語の検索" /><p><em>「I'm not right」</em>を検索すると、最初に返された結果とはまったく逆の意味を持つ文書が返されます。まったく同じ用語を検索しても、順序を変えて意味を変えた場合（たとえば、 <em>「右折する」</em>と<em>「右折する」）、</em>まったく同じ結果（つまり、3 番目のドキュメント「右折する」）が返されます。確かに、クエリは過度に単純化されており、フレーズ マッチングなどのより高度なクエリは使用されていませんが、これは、語彙検索ではインデックスされている内容と検索されている内容の背後にある真の意味を理解していないことを示しています。それが明確でなくても心配しないでください。3 番目の記事でこの例を再度取り上げ、この場合にベクトル検索がどのように役立つかを確認します。</p><p>語彙検索を正当に評価するには、<strong>構造化</strong>データのインデックス作成方法 (マッピング、テキスト分析、取り込みパイプラインなど) とクエリの作成方法 (巧みに作成された DSL クエリ、クエリ用語分析など) を制御できる場合、語彙検索エンジンで驚くべき成果を上げることができ、それに疑問の余地はありません。Elasticsearch の語彙検索機能に関する実績は実に素晴らしいものです。過去数年間にそれが達成したこと、そして語彙検索の分野をどれだけ普及させ、改善したかは、実に注目すべきことです。</p><p>ただし、自由形式のテキストで質問する必要があるユーザーに対して、<a href="https://www.elastic.co/what-is/unstructured-data"><strong>非構造化</strong></a><a href="https://www.elastic.co/what-is/unstructured-data">データ</a>(画像、ビデオ、音声、生のテキストなど) のクエリをサポートするタスクを課せられる場合、語彙検索では不十分です。さらに、クエリがテキストではなく、画像である場合もあります。これについては後ほど説明します。このような状況で語彙検索が不十分な主な理由は、非構造化データは構造化データと同じようにインデックス付けもクエリもできないことです。非構造化データを扱う場合には、<strong>セマンティクスが</strong>重要になります。セマンティクスとはどういう意味ですか?とても簡単に言えば、意味です！</p><p>画像検索エンジン（Google Image Search や Lens など）の簡単な例を見てみましょう。画像をドラッグ アンド ドロップすると、Google セマンティック検索エンジンが検索した画像に最も類似した画像を見つけて返します。下の図 3 では、左側にジャーマン シェパードの写真が表示され、右側には取得されたすべての類似写真が表示されています。最初の結果は、提供された写真と同じ写真 (つまり、最も類似した写真) です。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt3e0a0fb3c300537a/6a17e0cbe8fbce69d13a185d/c0dff24d4379141abd1038c9c4b5577fd2dca802-1600x657.png" alt="セマンティック検索の例: 画像の検索" /><p>これは人間にとっては単純かつ論理的に聞こえますが、コンピューターにとっては全く別の話です。これを可能にするのがベクトル検索です。世界が最近目撃したように、ベクトル検索によって解き放たれる力は莫大です。ではボンネットを開けて、下に何が隠れているか見てみましょう。</p><h2>埋め込みベクトル</h2><p>前に説明したように、語彙検索エンジンを使用すると、テキストなどの構造化データは、用語の実際の意味に関係なく、検索時に一致できる用語に簡単にトークン化できます。ただし、非構造化データは、大きなバイナリ オブジェクト (画像、ビデオ、オーディオなど) などのさまざまな形式をとることができるため、同じトークン化プロセスにはまったく適していません。さらに、セマンティック検索の目的は、データが表す意味に基づいて検索できるようにデータをインデックス化することです。どうすればそれを達成できるでしょうか?答えは2つの言葉です:<strong>機械学習です</strong>!もっと正確に言えば、ディープラーニングです!</p><p><strong>ディープラーニングは</strong>、データの真の意味を段階的に抽出できる複数の処理層で構成された人工ニューラル ネットワークに基づくモデルに依存する機械学習の特定の領域です。これらのニューラル ネットワーク モデルの動作方法は、人間の脳に大きく影響を受けています。下の図 4 は、入力層と出力層、および複数の隠し層を持つニューラル ネットワークの概要を示しています。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt8ca5fd8f0e61d939/6a17e0cd7f6f152a67c09a53/aeea3bab3c29e1c591a9c9082f2e73e7d3990ef1-1600x1116.png" alt="ベクトル探索におけるニューラルネットワーク層" /><p>ニューラル ネットワークの真の偉業は、単一の非構造化データを、<strong>埋め込みベクトル</strong>または単に<strong>埋め込み</strong>と呼ばれる浮動小数点値のシーケンスに変換できることです。人間は、ベクトルを 2 次元または 3 次元の空間で視覚化すると、ベクトルが何であるかをかなりよく理解できます。ベクトルの各成分は、2D xy 平面または 3D xyz 空間内の座標を表します。</p><p>ただし、ニューラル ネットワーク モデルが動作する埋め込みベクトルは、数百または数千の次元を持つ場合があり、単純に多次元空間内の点を表します。各ベクトル次元は、非構造化データの<strong>機能</strong>または特性を表します。これを、画像を 2048 次元の埋め込みベクトルに変換するディープラーニング モデルで説明しましょう。このモデルは、図 3 で使用したジャーマン シェパードの写真を、下の表に示す埋め込みベクトルに変換します。最初と最後の 3 つの要素のみが表示されていますが、テーブルにはさらに 2,042 の列/ディメンションがあることに注意してください。</p><p></p><p>赤い</p><p>犬です</p><p>青空</p><p>…</p><p>グラスなし</p><p>ジャーマンシェパード</p><p>木</p><p>ジャーマンシェパード 埋め込み</p><p>0.0121</p><p>0.9572</p><p>0.8735</p><p>…</p><p>0.1198</p><p>0.9712</p><p>0.0512</p><p>各列はモデルの次元であり、基礎となるニューラル ネットワークがモデル化しようとする機能または特性を表します。モデルに与えられた各入力は、その入力が 2048 次元のそれぞれにどの程度類似しているかに応じて特徴付けられます。したがって、埋め込みベクトルの各要素の値は、その入力と特定の次元の<strong>類似性</strong>を示します。この例では、モデルが犬とジャーマンシェパードの間に高い類似性があること、また青空が存在することを検出したことがわかります。</p><p>語彙検索では用語が一致するか一致しないかのどちらかになりますが、ベクトル検索では、非構造化データがモデルでサポートされている各次元とどの程度<em>類似している</em>かをより正確に把握できます。そのため、埋め込みベクトルは、非構造化データの優れたセマンティック表現として機能します。</p><h2>秘密のソース</h2><p>非構造化データがディープラーニング ニューラル ネットワークによって、多数の次元に沿ったデータの類似性を捉える埋め込みベクトルに細分化される仕組みがわかったので、それらのベクトルのマッチングがどのように機能するかを理解する必要があります。答えは非常に簡単であることがわかりました。互いに<strong>近い</strong>埋め込みベクトルは、<strong>意味的に類似した</strong>データ部分を表します。したがって、ベクトル データベースをクエリする場合、検索入力 (画像、テキストなど) は、まず、すべての非構造化データのインデックス作成に使用されたのと同じモデルを使用して埋め込みベクトルに変換され、最終的な目標はそのクエリ ベクトルに<strong>最も近い隣接ベクトルを</strong>見つけることです。したがって、必要なのは、クエリ ベクトルとデータベースにインデックスが付けられた既存のすべてのベクトルとの間の「距離」または「類似性」を測定する方法を見つけることだけです。</p><h3>距離と類似性</h3><p>幸いなことに、2 つのベクトル間の距離を測定することは、ベクトル演算のおかげで簡単に解決できる問題です。それでは、Elasticsearch などの最新のベクトル検索データベースでサポートされている、最も人気のある距離と類似度の関数を見てみましょう。警告、これから数学の話になります!</p><h4>L1距離</h4><p>2 つのベクトル x と y の L1 距離 (マンハッタン距離とも呼ばれる) は、それらのすべての要素のペアワイズ絶対差を合計することによって測定されます。明らかに、距離 d が小さいほど、2 つのベクトルは近くなります。式は以下のように非常にシンプルです。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt2df2e9bc20883491/6a17e0ce033c8d006a6bb0bf/2b17bcedfbedde61117a3e7970af55bc62318c6c-312x102.png" alt="ベクトル探索におけるL1距離式" /><p>視覚的に、L1 距離は以下の図 5 のように表すことができます。</p><p></p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltb90a33e6b1cbe00b/6a17e0d0414c64eb76945096/52075441892151536ed216081817a8e852566daa-474x464.png" alt="2つのベクトル間のL1距離を視覚化する" /><p>2 つのベクトル x と y、たとえば x = (1, 2) と y = (4, 3) を取ると、両方のベクトルの L1 距離は | 1 - 4 | + | 2 - 3 | = 4 になります。</p><h4>L2距離</h4><p>2 つのベクトル x と y の L2 距離 (ユークリッド距離とも呼ばれます) は、まずそのすべての要素のペアワイズ差の 2 乗を合計し、その結果の平方根を取ることによって測定されます。基本的には 2 点間の最短経路 (斜辺とも呼ばれます) です。L1と同様に、距離dが小さいほど、2つのベクトルは近くなります。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltae9b63fb593ae225/6a17e0d1af47b68379cddea8/b7675aa41f4f381e954c21dacd23a51a2dde6780-384x112.png" alt="ベクトル探索におけるL2距離" /><p>L2距離は以下の図6に示されています。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt32d913b46e07e79d/6a17e0d27f6f1582f7c09a57/bac99d08d6cf8a3a387a8acdc2e8f67357dad235-448x456.png" alt="2つのベクトル間のL2距離を視覚化する" /><p>L1距離に使用したのと同じ2つのサンプルベクトルxとyを再利用すると、L2距離はとして計算できます。10 の平方根を取ると 3.16 になります。</p><p></p><h4>ライン距離</h4><p>2 つのベクトル x と y の Linf (L 無限大) 距離は、チェビシェフ距離またはチェス盤距離とも呼ばれ、任意の 2 つの要素間の最長距離、または軸/次元の 1 つに沿って測定された最長距離として定義されます。式は非常にシンプルで、次のようになります。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt863411e15de16fa3/6a17e0d4505ac30939ad8a48/179ea6c6520a59acd97638313a2fb1b105e2ffed-436x82.png" alt="ベクトル探索におけるLinf距離の式" /><p>Linf 距離の表現を以下の図 7 に示します。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt863ee7b51fd5fc15/6a17e0d56864a4772db686af/b75540d793cdc0645244d77dc36da9d5734ccbac-548x560.png" alt="2つのベクトル間のLinf距離" /><p>ここでも、同じ 2 つのサンプル ベクトル x と y を取ると、L 無限大距離を max ( | 1 - 4 | 、 | 2 - 3 | ) = max (3, 1) = 3 として計算できます。</p><h4>コサイン類似度</h4><p>L1、L2、Linf とは対照的に、コサイン類似度は 2 つのベクトル x と y 間の距離を測定するのではなく、それらの相対的な角度、つまり両方がほぼ同じ方向を向いているかどうかを測定します。類似度が高いほど、2 つのベクトルは「近い」ことになります。式も非常にシンプルで、次のようになります。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt61d55341a6457ab7/6a17e0d7e9ea872b4ea9c4e2/931b6b90f0ee83e63a66496d06d6b4cef3affddd-304x72.png" alt="ベクトル検索におけるコサイン類似度の式" /><p>2 つのベクトル間のコサイン類似度を表す方法を以下の図 8 に示します。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt50eb6399510b5380/6a17e0d83e9e45302cba139a/52092e8b5d366178e50a35f8c954ee8eaca76965-582x586.png" alt="2つのベクトル間のコサイン類似度" /><p>さらに、コサイン値は常に [-1, 1] の範囲内にあるため、図 9 に示すように、-1 は反対の類似性 (つまり、両方のベクトル間の角度が 180°)、0 は無関係の類似性 (つまり、角度が 90°)、1 は同一 (つまり、角度が 0°) を意味します。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt785e195c75a5089e/6a17e0da1d1b8355bc93e398/fd2690a845b89b69ff202bd04192893638538aec-1600x456.png" alt="ベクトル探索におけるコサイン類似度スペクトル" /><p>
もう一度、同じサンプルベクトル x と y を再利用し、上記の式を使用してコサイン類似度を計算してみましょう。まず、両方のベクトルのドット積をとして計算します。次に、両方のベクトルの長さ（大きさとも呼ばれます）を掛けます： 最後に、ドット積を乗算した長さ 10 / 11.18034 = 0.894427 (つまり、角度 26°) で割ります。これは 1 に非常に近いため、両方のベクトルは非常に似ていると見なすことができます。</p><h4>ドット積類似度</h4><p>コサイン類似度の欠点の 1 つは、2 つのベクトル間の角度のみが考慮され、大きさ (長さ) は考慮されないことです。つまり、2 つのベクトルがほぼ同じ方向を指していても、一方が他方よりもはるかに長い場合、両方とも類似していると見なされます。ドット積類似度 (スカラーまたは内積とも呼ばれる) は、ベクトルの角度と大きさの両方を考慮することで類似度を改善し、より正確な類似度メトリックを提供します。</p><p>ドット積類似度を計算するために、2 つの同等の式が使用されます。1 つ目は、先ほどコサイン類似度の分子で見たものと同じです。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt7f1c3369f8628457/6a17e0db1d1b832a1893e39c/52e2723926ab27cd96b688e805fae15f607073c8-482x104.png" alt="ベクトル検索におけるドット積類似度式" /><p>2 番目の式は、両方のベクトルの長さにそれらの間の角度の余弦を掛けるだけです。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt136dd2a749c2398a/6a17e0dc6df73108a80a0e1f/dc9b11fc67dd748d9f1f29b735f4726138cb7d39-452x70.png" alt="ベクトル検索における内積類似度公式の簡略化" /><p>ドット積の類似性は、以下の図 10 に視覚化されています。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltb2c095e1c1948752/6a17e0dd6df731e30b0a0e23/1bda38cf82a1e1037f44b6e9657602c9efe1c0a6-558x574.png" alt="2つのベクトル間のドット積類似度" /><p>最後にもう一度、サンプルの x ベクトルと y ベクトルを取り、最初の式を使用して、(1  4) + (2  3) = 10 として、先ほどコサイン類似度を計算したのと同じように、ドット積類似度を計算します。</p><p>2 番目の式を使用して、両方のベクトルの長さを掛けます: これに両方のベクトル間の角度 26° のコサインを掛けると、11.18034  cos(26°) = 10 になります。</p><p>注目すべき点は、すべてのベクトルが最初に<strong>正規化されて</strong>いる場合（つまり、長さが 1 の場合）、ドット積類似度はコサイン類似度（|x| |y| = 1 であるため）、つまり両方のベクトル間の角度のコサインとまったく同じになることです。後で説明するように、ベクトルを正規化することは、ベクトルの大きさを無関係にして類似性が角度のみに焦点を当てるようにするために採用する良い方法です。また、インデックス作成時およびクエリ時の距離計算も高速化されますが、これは数十億のベクトルを操作するときに大きな問題になる可能性があります。</p><h3>簡単な要約</h3><p>さて、これまで非常に多くの情報を見てきましたが、ここで少し立ち止まって、これまでの状況を簡単に振り返ってみましょう。私たちは次のことを学びました…</p><ul><li><p>…セマンティック検索は、非構造化データを多次元埋め込みベクトルに変換することに優れたディープラーニング ニューラル ネットワーク モデルに基づいています。</p></li><li><p>…モデルの各次元は、非構造化データの機能または特性を表します。</p></li><li><p>…埋め込みベクトルは、特定の非構造化データが各次元にどの程度類似しているかを表す類似度値のシーケンス（各次元に 1 つずつ）です。</p></li><li><p>… 2 つのベクトルが「近い」ほど（つまり、最も近い隣り合うベクトルほど）、意味的に類似した概念を表していることになります。</p></li><li><p>…距離関数 (L1、L2、Linf) を使用すると、2 つのベクトルがどれだけ近いかを測定できます。</p></li><li><p>…相似関数 (コサインとドット積) を使用すると、2 つのベクトルが同じ方向にどれだけ進んでいるかを測定できます。</p></li></ul><p></p><p>さて、最後に掘り下げる必要があるのは、ベクター検索エンジンそのものです。クエリが送られてくると、まずクエリがベクトル化され、次にベクトル検索エンジンがそのクエリ ベクトルに最も近い隣接ベクトルを見つけます。クエリ ベクトルとデータベース内のすべてのベクトル間の距離または類似性を測定するブルート フォース方式は、データ セットが小さい場合には有効ですが、ベクトルの数が増えるとすぐに不十分になります。言い換えれば、数百万、数十億、あるいは数兆ものベクトルをインデックス化し、クエリベクトルの最近傍を妥当な時間内に見つけるにはどうすればよいでしょうか。そこで私たちは賢くなって、ベクトルをインデックスする最適な方法を見つけ出し、精度をあまり落とさずにできるだけ早く最近傍に焦点を絞る必要があります。</p><h3>ベクトル探索アルゴリズムと技術</h3><p>長年にわたり、さまざまな研究チームが、非常に巧妙なベクトル検索アルゴリズムの開発に多大な労力を費やしてきました。ここでは、主なものを簡単に紹介します。使用ケースに応じて、他のものよりも適しているものがあります。</p><h4>線形探索</h4><p>先ほど、クエリ ベクトルをデータベース内に存在するすべてのベクトルと比較するブルート フォース手法について説明した際に、線形検索、つまりフラット インデックスについて簡単に触れました。小さなデータセットではうまく機能する可能性がありますが、ベクトルと次元の数が増えるとパフォーマンスが急激に低下します (O(n) の複雑度)。</p><p>幸いなことに、<strong>近似最近傍法</strong>(ANN) と呼ばれるより効率的なアプローチがあります。この方法では、埋め込みベクトル間の距離が事前に計算され、類似のベクトルが、たとえばクラスター、ツリー、ハッシュ、グラフなどを使用して、互いに近くなるように保存および整理されます。このようなアプローチは、通常 100% の精度を保証しないため、「近似」と呼ばれます。最終的な目標は、類似のベクトルが含まれる可能性が最も高い領域のみに焦点を当てるために、<strong>検索範囲をできるだけ早くできるだけ小さく</strong>するか、<strong>ベクトルの次元を減らす</strong>ことです。</p><h4>K次元ツリー</h4><p>K 次元ツリー (KD ツリー) は、k 次元空間にポイントを格納するバイナリ検索ツリーの一般化であり、検索空間をベクトルがインデックス付けされた小さな左ツリーと右ツリーに連続的に二分することによって機能します。検索時には、アルゴリズムは、クエリ ベクトル (図 11 の赤い点) の周囲のいくつかのツリー ブランチを訪問するだけで、最も近い近傍 (図 11 の緑の点) を見つけることができます。k 個を超える近傍が要求された場合、アルゴリズムがさらに多くの近傍を見つけるまで黄色の領域が拡張されます。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt55e01707cd9fca57/6a17e0dfbe60866a90004653/e21af2d613112279089b6fa2166359233b10019d-829x860.png" alt="ベクトル探索におけるKD木アルゴリズム" /><p>KD ツリー アルゴリズムの最大の利点は、一部のローカライズされたツリー ブランチのみに素早く焦点を当てることができるため、ほとんどのベクトルを考慮から除外できることです。ただし、次元数が増えると、低次元空間よりも多くの分岐を訪問する必要があるため、このアルゴリズムの効率は低下します。</p><h4>逆ファイルインデックス</h4><p>逆ファイルインデックス (IVF) アプローチも、互いに近いベクトルを共有の重心に割り当てる<strong>空間分割</strong>アルゴリズムです。2D 空間では、図 12 に示すように、ボロノイ図でこれを視覚化するのが最適です。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt7b7e4fe6deff5ef3/6a17e0e17b54f940eb8b381c/33ddaaa818ab2f2a5fc87982c82c2a34cb849e33-640x640.png" alt="2次元空間における逆ファイルインデックスのボロノイ表現 " /><p>上記の 2D 空間は 20 個のクラスターに分割されており、それぞれのクラスターの重心は黒い点で示されています。空間内のすべての埋め込みベクトルは、それらの重心が最も近いクラスターに割り当てられます。検索時に、アルゴリズムはまず、クエリ ベクトルに最も近い重心を見つけることで、焦点を当てるクラスターを判断し、次に、その領域と、必要に応じて周囲の領域に焦点を絞って、最も近い近傍を見つけます。</p><p>このアルゴリズムは、高次元空間で使用する場合、KD ツリーと同じ問題が発生します。これは次元の呪いと呼ばれ、空間の体積が非常に大きくなり、すべてのデータがまばらに見え、より正確な結果を得るために必要なデータの量が指数関数的に増加する場合に発生します。データがまばらな場合、これらの空間分割アルゴリズムでデータをクラスターに整理することが難しくなります。幸いなことに、この問題を軽減する他のアルゴリズムとテクニックが存在します。以下に詳細を説明します。</p><h4>量子化</h4><p>量子化は<strong>圧縮</strong>ベースのアプローチであり、埋め込みベクトルの精度を下げることでデータベースの合計サイズを削減できます。これは、浮動小数点ベクトル値を整数値に変換することにより<strong>、スカラー量子化 (SQ)</strong>を使用して実現できます。これにより、データベースのサイズが 8 分の 1 に縮小されるだけでなく、メモリ消費も減少し、検索時のベクトル間の距離計算が高速化されます。</p><p>もう 1 つの手法は<strong>積量子化 (PQ) と呼ばれ、</strong>最初に空間を低次元のサブスペースに分割し、次にクラスタリング アルゴリズム (k 平均法に類似) を使用して各サブスペース内で互いに近いベクトルをグループ化します。</p><p>量子化は<strong>次元削減</strong>とは異なることに注意してください。次元削減では次元数が削減され、ベクトルが単に短くなります。</p><h4>階層的にナビゲート可能なスモールワールド（HNSW）</h4><p>名前だけ読むと複雑に思えても心配しないでください。実際はそうではありません。つまり、Hierarchical Navigable Small Worlds は、非常に人気があり効率的な多層グラフベースのアルゴリズムです。Apache Lucene を含むさまざまなベクター データベースで使用されます。HNSW の概念的表現を以下の図 13 に示します。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt7f4f4a8e393c8d53/6a17e0e3e8fbcefa193a1861/189ef9a8bec476e379222c454644ba4c1f952085-1400x840.png" alt="階層的にナビゲート可能なスモールワールド（HNSW）" /><p>最上層には、ベクトル間のリンクが最も長い少数のベクトルのグラフ、つまり、類似性が最も低い接続されたベクトルのグラフが表示されます。下の層に進んでいくと、見つかるベクトルの数が増え、グラフの密度が増し、ベクトル同士がどんどん接近していきます。最下層では、すべてのベクトルを見つけることができ、最も類似したベクトルが互いに最も近く配置されます。</p><p>検索時に、アルゴリズムは任意のエントリ ポイントの最上位レイヤーから開始し、クエリ ベクトル (灰色の点で表示) に最も近いベクトルを見つけます。次に、1 つ下のレイヤーに移動して、上のレイヤーに残したのと同じベクトルから始めて、同じプロセスを繰り返します。これを 1 レイヤーずつ続けて、最下層に到達してクエリ ベクトルに最も近い近傍を見つけます。</p><h4>局所性依存ハッシュ（LSH）</h4><p>これまでに紹介した他のすべてのアプローチと同様に、局所性に敏感なハッシュは、検索速度を向上させるために検索スペースを大幅に削減することを目指しています。この技術では、類似性情報を保持したまま埋め込みベクトルがハッシュ値に変換されるため、検索空間は最終的に、トラバースする必要のあるグラフやツリーではなく、検索できる単純なハッシュ テーブルになります。ハッシュベースの方法の主な利点は、任意の（大きな）数の次元を含むベクトルを固定サイズのハッシュにマッピングできるため、精度をあまり犠牲にすることなく検索時間が大幅に短縮されることです。</p><p>一般的にデータのハッシュ方法、特にベクトルの埋め込み方法は多種多様ですが、この記事ではそれぞれの詳細については説明しません。従来のハッシュ方法では、通常、非常に類似しているように見えるデータに対して非常に異なるハッシュが生成されます。埋め込みベクトルは浮動小数点値で構成されているため、ベクトル演算で互いに非常に近いと考えられる 2 つのサンプル浮動小数点値 (たとえば、0.73 と 0.74) を取り、いくつかの一般的なハッシュ関数を実行してみましょう。以下の結果を見ると、一般的なハッシュ関数では入力間の類似性を保持していないことは明らかです。</p><p>ハッシュ関数</p><p>0.73</p><p>0.74</p><p>MD5</p><p>1342129d04cd2924dd06cead4cf0a3ca</p><p>0aec1b15371bd979cfa66b0a50ebecc5</p><p>SHA1</p><p>49d2c3e0e44bff838e1db571a121be5ea874e8d9</p><p>a534e76482ade9d9fe4bff3035a7f31f2f363d77</p><p>SHA256</p><p>99d03fc3771fe6848d675339fc49eeb1cb8d99a12e6358173336b99a2ec530ea</p><p>5ecbc825ba5c16856edfdaf0abc5c6c41d0d8a9c508e34188239521dc7645663</p><p>従来のハッシュ方式では、類似したデータ間の<em>ハッシュ衝突を最小限に抑えよ</em>うとしますが、局所性に敏感なハッシュの主な目的は、まさにその逆、つまり、類似したデータが高い確率で同じバケット内に含まれるように<em>ハッシュ衝突を最大化する</em>ことです。そうすることで、多次元空間内で互いに近接する埋め込みベクトルは、同じバケットに含まれる固定サイズの値にハッシュされます。LSH ではハッシュされたベクトルの近接性を維持できるため、この手法はデータのクラスタリングや最近傍検索に非常に便利です。</p><p>すべての面倒な作業は、ハッシュを計算する必要があるインデックス作成時に発生しますが、検索時には、最も近い埋め込みベクトルを含むバケットを検索するためにクエリ ベクトルをハッシュするだけで済みます。候補バケットが見つかると、通常は 2 回目のラウンドが実行され、クエリ ベクトルに最も近い隣接ベクトルが識別されます。</p><h2>まとめましょう</h2><p>ベクトル検索を紹介するために、この記事ではかなり広範囲にわたる内容を扱う必要がありました。語彙検索とベクトル検索の違いを比較した後、ディープラーニング ニューラル ネットワーク モデルが非構造化データのセマンティクスを捕捉し、その意味を高次元埋め込みベクトル (モデルの各次元に沿ったデータの類似性を表す浮動小数点数のシーケンス) に変換する方法を学びました。また、ベクトル検索と語彙検索は競合するものではなく、補完的な情報検索手法であることも注目に値します (このシリーズの第 3 部でハイブリッド検索について詳しく説明するときに説明します)。</p><p>その後、ベクトル検索の基本的な構成要素である距離 (および類似度) 関数を導入しました。これにより、2 つのベクトルの近接性を測定し、それらが表す概念の類似性を評価できるようになります。</p><p>最後に、ツリー、グラフ、クラスター、ハッシュをベースにした、最も人気のあるさまざまなベクトル検索アルゴリズムとテクニックを確認しました。その目的は、線形ブルート フォース検索のように空間全体を調べなくても、多次元空間の特定の領域をすばやく絞り込み、最も近い近傍を見つけることです。</p><p>この記事が気に入ったら、ぜひこのシリーズの他の部分も読んでみてください。</p><ul><li><p><a href="https://www.elastic.co/search-labs/blog/vector-search-set-up-elasticsearch">パート2：Elasticsearchでベクトル検索を設定する方法</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/hybrid-search-elasticsearch">パート3: Elasticsearchを使用したハイブリッド検索</a></p></li></ul>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/introduction-to-vector-search</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/introduction-to-vector-search</guid>
    <category><![CDATA[Vector Database]]></category>
    <dc:creator><![CDATA[Valentin Crettaz]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt70da374b8dc0c490/6a17e0e46864a473aab686b3/63eea8ea95b49e7241e539f65bf5aa3bb8823fff-1200x628.png" length="0" type="image/png"/>
    <pubDate>Thu, 06 Feb 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[AIエージェント開発におけるMicrosoftセマンティックカーネル向けElasticsearch Vector Store Connectorの使い方]]></title>
    <description><![CDATA[Microsoft Semantic Kernel は、AI エージェントを簡単に構築し、最新の AI モデルを C#、Python、または Java コードベースに統合できる軽量のオープンソース開発キットです。Semantic Kernel Elasticsearch Vector Store Connector のリリースにより、AI エージェントの構築に Semantic Kernel を使用する開発者は、Semantic Kernel の抽象化を引き続き使用しながら、Elasticsearch をスケーラブルなエンタープライズ グレードのベクター ストアとしてプラグインできるようになりました。]]></description>
    <content:encoded><![CDATA[<p><a href="https://learn.microsoft.com/en-us/semantic-kernel/overview/">Microsoft Semantic Kernel</a> チームと連携して、<a href="https://learn.microsoft.com/en-us/semantic-kernel/overview/"> Microsoft Semantic</a> Kernel (.NET) ユーザー向けに<a href="https://github.com/elastic/semantic-kernel-net/"> Semantic Kernel Elasticsearch Vector Store Connector が利用可能になったことを発表します。</a>セマンティック カーネルは、ベクター ストアからのより関連性の高いデータ駆動型の応答を使用して大規模言語モデル (LLM) を強化する機能など、エンタープライズ グレードの AI エージェントの構築を簡素化します。Semantic Kernel は、Elasticsearch などの Vector Stores と対話するためのシームレスな抽象化レイヤーを提供し、レコードのコレクションの作成、一覧表示、削除や、個々のレコードのアップロード、取得、削除などの重要な機能を提供します。</p><p><a href="https://learn.microsoft.com/en-us/semantic-kernel/concepts/vector-store-connectors/out-of-the-box-connectors/elasticsearch-connector?pivots=programming-language-csharp">すぐに使用できるセマンティック カーネル Elasticsearch ベクター ストア コネクタは、</a>セマンティック カーネル<a href="https://learn.microsoft.com/en-us/semantic-kernel/concepts/vector-store-connectors/?pivots=programming-language-csharp#the-vector-store-abstraction">ベクター ストアの抽象化</a>をサポートしており、開発者は AI エージェントの構築時に Elasticsearch をベクター ストアとしてプラグインすることが非常に簡単になります。</p><p>Elasticsearch はオープンソース コミュニティに強固な基盤を持ち、最近<a href="https://www.elastic.co/blog/elasticsearch-is-open-source-again">AGPL ライセンスを</a>採用しました。これらのツールは、オープンソースの Microsoft Semantic Kernel と組み合わせることで、強力なエンタープライズ対応ソリューションを提供します。このコマンド<code>curl -fsSL https://elastic.co/start-local | sh </code> (詳細については<a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/run-elasticsearch-locally.html">start-local</a>を参照) を実行して数分で Elasticsearch を起動し、ローカルで開始できます。その後、AI エージェントを本番稼働させながら、<a href="https://cloud.elastic.co/registration?onboarding_token=vectorsearch&amp;utm_source=semantickernel&amp;utm_content=documentation">クラウドホスト バージョン</a>または<a href="https://www.elastic.co/guide/en/elasticsearch/reference/8.16/install-elasticsearch.html">セルフホスト</a>バージョンに移行できます。</p><p>このブログでは、Semantic Kernel を使用する際に<a href="https://github.com/elastic/semantic-kernel-net/">Semantic Kernel Elasticsearch Vector Store Connector を</a>使用する方法について説明します。コネクタの Python バージョンは将来提供される予定です。</p><h2>高レベルのシナリオ: Semantic Kernel と Elasticsearch を使用した RAG アプリの構築</h2><p>次のセクションでは例を見ていきます。大まかに言うと、ユーザーの質問を入力として受け取り、回答を返す RAG (Retrieval Augmented Generation) アプリケーションを構築しています。LLM として Azure OpenAI (<a href="https://devblogs.microsoft.com/semantic-kernel/introducing-new-ollama-connector-for-local-models/">ローカル LLM</a>も使用可能)、ベクター ストアとして Elasticsearch、すべてのコンポーネントを結び付けるフレームワークとして Semantic Kernel (.net) を使用します。</p><p>RAG アーキテクチャに精通していない場合は、次の記事で簡単に概要を把握できます: <a href="https://www.elastic.co/search-labs/blog/retrieval-augmented-generation-rag">https://www.elastic.co/search-labs/blog/retrieval-augmented-generation-rag</a> 。</p><p>回答は、Elasticsearch vectorstore から取得され、質問に関連するコンテキストが入力する LLM によって生成されます。応答には、LLM によってコンテキストとして使用されたソースも含まれます。</p><h3>RAGの例</h3><p>この具体的な例では、社内のホテル データベースに保存されているホテルについてユーザーが質問できるアプリケーションを構築します。ユーザーは例えばさまざまな基準に基づいて特定のホテルを検索したり、ホテルのリストを要求したりできます。</p><p>サンプル データベースでは、100 件のエントリを含む<a href="https://github.com/elastic/semantic-kernel-net/blob/main/Elastic.SemanticKernel.Playground/hotels.csv">ホテルのリスト</a>を生成しました。コネクタのデモをできるだけ簡単に試せるように、サンプル サイズは意図的に小さくなっています。実際のアプリケーションでは、特に非常に大量のデータを扱う場合、Elasticsearch コネクタは `InMemory` ベクトル ストア実装などの他のオプションよりも優位性を発揮します。</p><p>完全なデモ アプリケーションは、Elasticsearch ベクター ストア コネクタ<a href="https://github.com/elastic/semantic-kernel-net/tree/main/Elastic.SemanticKernel.Playground">リポジトリ</a>にあります。</p><p>まず、必要な NuGet パッケージと using ディレクティブをプロジェクトに追加することから始めましょう。</p>dotnet add package "Elastic.Clients.Elasticsearch" -v 8.16.2
dotnet add package "Elastic.SemanticKernel.Connectors.Elasticsearch" -v 0.1.2
dotnet add package "Microsoft.Extensions.Hosting" -v 9.0.0
dotnet add package "Microsoft.SemanticKernel.Connectors.AzureOpenAI" -v 1.30.0
dotnet add package "Microsoft.SemanticKernel.PromptTemplates.Handlebars" -v 1.30.0using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;

using Elastic.Clients.Elasticsearch;
using Elastic.Transport;

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.VectorData;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Data;
using Microsoft.SemanticKernel.Embeddings;
using Microsoft.SemanticKernel.PromptTemplates.Handlebars;<p>これで、データ モデルを作成し、セマンティック カーネル固有の属性を指定して、ストレージ モデル スキーマとテキスト検索のヒントを定義できるようになりました。</p>/// &lt;summary&gt;
/// Data model for storing a "hotel" with a name, a description, a  description embedding and an optional reference link.
/// &lt;/summary&gt;
public sealed record Hotel
{
	[VectorStoreRecordKey]
	public required string HotelId { get; set; }

	[TextSearchResultName]
	[VectorStoreRecordData(IsFilterable = true)]
	public required string HotelName { get; set; }

	[TextSearchResultValue]
	[VectorStoreRecordData(IsFullTextSearchable = true)]
	public required string Description { get; set; }

	[VectorStoreRecordVector(Dimensions: 1536, DistanceFunction.CosineSimilarity, IndexKind.Hnsw)]
	public ReadOnlyMemory&lt;float&gt;? DescriptionEmbedding { get; set; }

	[TextSearchResultLink]
	[VectorStoreRecordData]
	public string? ReferenceLink { get; set; }
}<p>ストレージ モデル スキーマ属性 (`VectorStore*`) は、Elasticsearch Vector Store Connector の実際の使用に最も関連しています。具体的には次のようになります。</p><p></p><ul><li><p><code>VectorStoreRecordKey</code> レコード クラスのプロパティを、ベクトル ストアにレコードが格納されるキーとしてマークします。</p></li><li><p><code>VectorStoreRecordData</code> レコード クラスのプロパティを 'data' としてマークします。</p></li><li><p><code>VectorStoreRecordVector</code> レコード クラスのプロパティをベクトルとしてマークします。</p></li></ul><p>これらの属性はすべて、ストレージ モデルをさらにカスタマイズするために使用できるさまざまなオプション パラメーターを受け入れます。たとえば、 <code>VectorStoreRecordKey </code>の場合、異なる距離関数や異なるインデックス タイプを指定することが可能です。</p><p>テキスト検索属性 ( <code>TextSearch*</code> ) は、この例の最後のステップで重要になります。これらについては後ほど説明します。</p><p>次のステップでは、セマンティック カーネル エンジンを初期化し、コア サービスへの参照を取得します。実際のアプリケーションでは、サービス コレクションに直接アクセスするのではなく、<a href="https://learn.microsoft.com/en-us/dotnet/core/extensions/dependency-injection">依存性注入を</a>使用する必要があります。同じことがハードコードされた構成とシークレットにも当てはまります。これらは、代わりに<a href="https://learn.microsoft.com/en-us/dotnet/core/extensions/configuration">構成プロバイダー</a>を使用して読み取る必要があります。</p>var builder = Host.CreateApplicationBuilder(args);

// Register AI services.
var kernelBuilder = builder.Services.AddKernel();

kernelBuilder.AddAzureOpenAIChatCompletion("gpt-4o", "https://my-service.openai.azure.com", "my_token");

kernelBuilder.AddAzureOpenAITextEmbeddingGeneration("ada-002", "https://my-service.openai.azure.com", "my_token");

// Register text search service.
kernelBuilder.AddVectorStoreTextSearch&lt;Hotel&gt;();

// Register Elasticsearch vector store.
var elasticsearchClientSettings = new ElasticsearchClientSettings(new Uri("https://my-elasticsearch-instance.cloud"))
    .Authentication(new BasicAuthentication("elastic", "my_password"));

kernelBuilder.AddElasticsearchVectorStoreRecordCollection&lt;string, Hotel&gt;("skhotels", elasticsearchClientSettings);

// Build the host.
using var host = builder.Build();

// For demo purposes, we access the services directly without using a DI context.

var kernel = host.Services.GetService&lt;Kernel&gt;()!;
var embeddings = host.Services.GetService&lt;ITextEmbeddingGenerationService&gt;()!;
var vectorStoreCollection = host.Services.GetService&lt;IVectorStoreRecordCollection&lt;string, Hotel&gt;&gt;()!;

// Register search plugin.
var textSearch = host.Services.GetService&lt;VectorStoreTextSearch&lt;Hotel&gt;&gt;()!;
kernel.Plugins.Add(textSearch.CreateWithGetTextSearchResults("SearchPlugin"));<p><code>vectorStoreCollection</code>サービスを使用してコレクションを作成し、いくつかの<a href="https://github.com/elastic/semantic-kernel-net/blob/main/Elastic.SemanticKernel.Playground/hotels.csv">デモ レコード</a>を取り込むことができるようになりました。</p>await vectorStoreCollection.CreateCollectionIfNotExistsAsync();

// CSV format: ID;Hotel Name;Description;Reference Link
var hotels = (await File.ReadAllLinesAsync("hotels.csv"))
    .Select(x =&gt; x.Split(';'));

foreach (var chunk in hotels.Chunk(25))
{
    var descriptionEmbeddings = await embeddings.GenerateEmbeddingsAsync(chunk.Select(x =&gt; x[2]).ToArray());
    
    for (var i = 0; i &lt; chunk.Length; ++i)
    {
        var hotel = chunk[i];
        await vectorStoreCollection.UpsertAsync(new Hotel
        {
            HotelId = hotel[0],
            HotelName = hotel[1],
            Description = hotel[2],
            DescriptionEmbedding = descriptionEmbeddings[i],
            ReferenceLink = hotel[3]
        });
    }
}<p>これは、セマンティック カーネルが、複雑なベクトル ストアの使用を、いくつかの単純なメソッド呼び出しにまで削減する方法を示しています。</p><p>内部的には、Elasticsearch に新しいインデックスが作成され、必要なすべてのプロパティ マッピングが作成されます。その後、データ セットは完全に透過的にストレージ モデルにマッピングされ、最終的にインデックスに保存されます。以下は Elasticsearch でのマッピングの様子です。</p>{
  "mappings": {
    "properties": {
      "descriptionEmbedding": {
        "dims": 1536,
        "index": true,
        "index_options": {
          "type": "hnsw"
        },
        "similarity": "cosine",
        "type": "dense_vector"
      },
      "hotelName": {
        "type": "keyword"
      },
      "description": {
        "type": "text"
      }
    }
  }
}<p><code>embeddings.GenerateEmbeddingsAsync()</code>は、構成された Azure AI Embeddings Generation サービスを透過的に呼び出しました。</p><p>このデモの最後のステップでは、さらに多くの魔法が観察できます。</p><p><code>InvokePromptAsync</code>を 1 回呼び出すだけで、ユーザーがデータについて質問したときに、次のすべての操作が実行されます。</p><p>1.ユーザーの質問の埋め込みが生成される</p><p>2. ベクトルストアで関連するエントリを検索する</p><p>3. クエリの結果はプロンプトテンプレートに挿入されます</p><p>4. 最終プロンプトの形式で実際のクエリがAIチャット補完サービスに送信されます。</p>// Invoke the LLM with a template that uses the search plugin to
// 1. get related information to the user query from the vector store
// 2. add the information to the LLM prompt.
var response = await kernel.InvokePromptAsync(
    promptTemplate: """
                    Please use this information to answer the question:
                    {{#with (SearchPlugin-GetTextSearchResults question)}}
                      {{#each this}}
                        Name: {{Name}}
                        Value: {{Value}}
                        Source: {{Link}}
                        -----------------
                      {{/each}}
                    {{/with}}
                    
                    Include the source of relevant information in the response.

                    Question: {{question}}
                    """,
    arguments: new KernelArguments
    {
        { "question", "Please show me all hotels that have a rooftop bar." },
    },
    templateFormat: "handlebars",
    promptTemplateFactory: new HandlebarsPromptTemplateFactory());<p>以前データ モデルで定義した<code>TextSearch*</code>属性を覚えていますか?これらの属性により、プロンプト テンプレート内の対応するプレースホルダーを使用できるようになります。これらのプレースホルダーには、ベクター ストア内のエントリからの情報が自動的に入力されます。</p><p>「屋上バーがあるホテルをすべて教えてください。」という質問に対する最終的な回答は次のとおりです。</p>Console.WriteLine(response.ToString());

// &gt; The hotel that has a rooftop bar is Skyline Suites. You can find more information about this hotel [here](https://example.com/yz567).<p>答えは、hotels.csvの次のエントリを正しく参照しています。</p>9;
Skyline Suites;
Offering panoramic city views from every suite, this hotel is perfect for those who love the urban landscape. Enjoy luxurious amenities, a rooftop bar, and close proximity to attractions. Luxurious and contemporary.;
https://example.com/yz567<p>この例は、Microsoft Semantic Kernel を使用すると、よく考えられた抽象化によって複雑さが大幅に軽減され、非常に高いレベルの柔軟性が実現されることを示しています。たとえば、コードの 1 行を変更するだけで、コードの他の部分をリファクタリングすることなく、使用されているベクトル ストアまたは AI サービスを置き換えることができます。</p><p>同時に、このフレームワークは、`InvokePrompt` 関数やテンプレート、検索プラグイン システムなどの膨大な高レベル機能を提供します。</p><p>完全なデモ アプリケーションは、Elasticsearch ベクター ストア コネクタ リポジトリにあります。</p><h2>Elasticsearchで他に何ができるのか</h2><ul><li><p><a href="https://www.elastic.co/search-labs/blog/semantic-search-simplified-semantic-text">Elasticsearchの新しいsemantic_textマッピング：セマンティック検索の簡素化</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/semantic-reranking-with-retrievers">Elasticsearch におけるセマンティックリランキング（リトリーバー使用）</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-1">高度なRAGテクニックパート1：データ処理</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2">高度なRAGテクニックパート2：クエリとテスト</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/elasticsearch-rag-with-llama3-opensource-and-elastic">Llama 3オープンソースとElasticでRAGを構築する</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/local-rag-agent-elasticsearch-langgraph-llama3">LangGraph、LLaMA3、Elasticsearchベクターストアを使用してローカルエージェントをゼロから構築するチュートリアル</a></p></li></ul><h2>Elasticsearch とセマンティックカーネル: 次は何?</h2><ul><li><p>.NET で GenAI アプリケーションを構築する際に、Elasticsearch ベクター ストアを Semantic Kernel に簡単にプラグインする方法を示しました。次回の Python 統合にご期待ください。</p></li><li><p>Semantic Kernel は<a href="https://www.elastic.co/search-labs/tutorials/search-tutorial/vector-search/hybrid-search">ハイブリッド検索</a>などの高度な検索機能の抽象化を構築するため、Elasticsearch Connect を使用すると、.NET 開発者は Semantic Kernel を使用しながらそれらを簡単に実装できるようになります。</p></li></ul>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/elasticsearch-connector-microsoft-semantic-kernel</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/elasticsearch-connector-microsoft-semantic-kernel</guid>
    <category><![CDATA[AI]]></category>
    <category><![CDATA[.NET]]></category>
    <category><![CDATA[Vector Database]]></category>
    <dc:creator><![CDATA[Florian Bernd,Srikanth Manvi]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt2d8725035e86f8a8/6a17fe447f6f1564f8c09d74/0564fe794e4c66d0507317822d7aa71826183d20-1311x762.jpg" length="0" type="image/jpeg"/>
    <pubDate>Fri, 06 Dec 2024 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[電子商取引の製品カタログでハイブリッド検索を使用する方法]]></title>
    <description><![CDATA[ファセット、プロモーション、パーソナライゼーション、行動分析を活用し、ハイブリッド検索を使用して e コマース製品カタログを構築する方法を学びます。]]></description>
    <content:encoded><![CDATA[<p>この記事では、全文検索とベクトル検索の結果を組み合わせたハイブリッド検索を実装する方法を説明します。これら 2 つのアプローチを統合することにより、ハイブリッド検索では、両方の検索戦略の長所を活用して、結果の幅が広がります。</p><p>ハイブリッド検索の統合に加えて、検索ソリューションをさらに堅牢にする機能を追加する方法も紹介します。これには、ファセットやパーソナライズされた製品プロモーションが含まれます。さらに、Elastic の Behavioral Analytics ツールを使用して、ユーザーインタラクションをキャプチャし、貴重な洞察を生成する方法も紹介します。</p><p>この実装では、ユーザーが検索結果を表示して操作できるようにするインターフェースと、情報を返す API の両方を構築する方法について説明します。ソースコードを含むリポジトリにアクセスするには、以下のリンクを参照してください。</p><ul><li><p><a href="https://github.com/elastic/elasticsearch-labs/tree/main/supporting-blog-content/hybrid-search-for-an-e-commerce-product-catalogue/product-store-search">https://github.com/elastic/elasticsearch-labs/tree/main/supporting-blog-content/hybrid-search-for-an-e-commerce-product-catalogue/product-store-search</a></p></li><li><p><a href="https://github.com/elastic/elasticsearch-labs/tree/main/supporting-blog-content/hybrid-search-for-an-e-commerce-product-catalogue/app-product-store">https://github.com/elastic/elasticsearch-labs/tree/main/supporting-blog-content/hybrid-search-for-an-e-commerce-product-catalogue/app-product-store</a> </p></li></ul><p>このガイドは、インデックスの作成からファセットや結果のパーソナライズなどの高度な機能の実装まで、いくつかのステップに分かれています。最後には、電子商取引のシナリオで使用できる強力な検索ソリューションが完成します。</p><h2>電子商取引ハイブリッド検索の環境設定</h2><p>実装を始める前に、環境を設定する必要があります。Elasticsearch を管理するには、Elastic Cloud 上のサービスまたはコンテナ化されたソリューションを使用するように選択できます。コンテナ化を選択した場合、Docker Compose 経由の構成はこのリポジトリにあります: <a href="https://github.com/andreluiz1987/product-store-search/blob/main/docker/docker-compose.yml">docker-compose.yml</a> 。</p><h2>インデックス作成と製品カタログの取り込み</h2><p>インデックスは、名前、説明、写真、カテゴリ、タグなどのフィールドを含む化粧品のカタログに基づいて作成されます。「名前」や「説明」などの全文検索に使用されるフィールドは<code>text</code>としてマッピングされますが、「カテゴリ」や「ブランド」などの集計に使用されるフィールドはファセットを有効にするために<code>keyword</code>としてマッピングされます。</p><p>「説明」フィールドは、製品に関する詳細なコンテキストを提供するため、ベクター検索に使用されます。このフィールドは、説明のベクトル表現を格納する<code>dense_vector,</code>として定義されます。</p><p>インデックス マッピングは次のようになります。</p>{
   "mappings":{
      "properties":{
         "id":{
            "type":"keyword"
         },
         "brand":{
            "type":"text",
            "fields":{
               "keyword":{
                  "type":"keyword"
               }
            }
         },
         "name":{
            "type":"text"
         },
         "price":{
            "type":"float"
         },
         "price_sign":{
            "type":"keyword"
         },
         "currency":{
            "type":"keyword"
         },
         "image_link":{
            "type":"keyword"
         },
         "description":{
            "type":"text"
         },
         "description_embeddings":{
            "type":"dense_vector",
            "dims":384
         },
         "rating":{
            "type":"keyword"
         },
         "category":{
            "type":"keyword"
         },
         "product_type":{
            "type":"keyword"
         },
         "tag_list":{
            "type":"keyword"
         }
      }
   }
}<p>インデックスを作成するためのスクリプトは<a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/hybrid-search-for-an-e-commerce-product-catalogue/product-store-search/infra/create_index.py">ここに</a>あります。</p><h2>埋め込み生成</h2><p>製品の説明をベクトル化するために、モデル all-MiniLM-L6-v2 を使用します。この場合、アプリケーションはインデックス作成前に埋め込みを生成する必要があります。別のオプションとしては、モデルを Elasticsearch クラスターにインポートすることですが、このローカル環境では、アプリケーション内で直接ベクトル化を実行することを選択しました。</p><p>インデックスの作成には、 <a href="https://www.kaggle.com/datasets/shivd24coder/cosmetic-brand-products-dataset">Kaggle</a>で利用可能な化粧品データセットを使用し、データ取り込みの効率を向上させるためにバッチ処理を使用しました。この同じ取り込み段階で、「description」フィールドの埋め込みを生成し、新しいフィールド「description_embeddings」にインデックスを付けます。</p><p>完全なデータ取り込みプロセスは、リポジトリで利用可能な<strong>Jupyter Notebook</strong>を通じて直接実行できます。このノートブックには、データがどのように読み取られ、処理され、Elasticsearch にインデックス付けされるかについてのステップバイステップのガイドが用意されており、簡単に複製および実験を行うことができます。</p><p>次のリンクからノートブックにアクセスできます: <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/hybrid-search-for-an-e-commerce-product-catalogue/product-store-search/ingestion/ingestion.ipynb">Ingestion Notebook。</a></p><h2>ハイブリッド検索の実装</h2><p>それでは、ハイブリッド検索を実装してみましょう。キーワードベースの検索では、「name」、「category」、「description」フィールドをターゲットとする<a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-multi-match-query.html">multi_match</a>クエリを使用します。これにより、これらのいずれかのフィールドに検索用語が含まれるドキュメントが取得されるようになります。</p><p>ベクトル検索には、 <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/knn-search.html">KNN クエリ</a>を使用します。クエリを実行する前に検索用語をベクトル化する必要があり、これは入力用語をベクトル化するメソッドを使用して実行されます。取り込み時に使用されたのと同じモデルが検索用語にも使用されることに注意してください。</p><p>両方の検索の組み合わせは、<a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/rrf.html">相互ランク融合 (RRF)</a>アルゴリズムを使用して実現されます。このアルゴリズムは、2 つのクエリの結果を結合し、ノイズを減らすことで検索精度を向上させます。RRF を使用すると、キーワードベースの検索とベクトル検索の両方を連携させることができ、ユーザーのクエリの理解が向上します。</p>query = {
   "retriever": {
       "rrf": {
           "retrievers": [
               {
                   "standard": {
                       "query": organic_query['query']
                   }
               },
               {
                   "knn": {
                       "field": "description_embeddings",
                       "query_vector": vector,
                       "k": 5,
                       "num_candidates": 20
                   }
               }
           ],
           "rank_window_size": 20,
           "rank_constant": 5
       }
   },
   "_source": organic_query['_source']
}<h3>結果の比較: キーワード検索とハイブリッド検索</h3><p>ここで、従来のキーワード検索とハイブリッド検索の結果を比較してみましょう。キーワード検索で「乾燥肌用ファンデーション」を検索すると、次のような結果が表示されます。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltcb5405df787bc8f5/6a17023e961e697ca1c4cdb4/52e717aa3c9c1fadfadb639f5fb77cf8e47e3b34-1600x1021.png" alt="結果の比較: キーワード検索とハイブリッド検索" /><ol><li><p><strong>レブロン カラーステイ メイクアップ (普通肌/乾燥肌用)説明</strong>: レブロン カラーステイ メイクアップは、軽い処方で、固まったり、色落ちしたり、擦り落ちたりしない、長持ちするカバー力を提供します。タイムリリーステクノロジーを採用した、オイルフリーの水分バランスフォーミュラは、特に普通肌または乾燥肌に継続的に水分を補給するように作られています。特徴：メイクアップは快適なつけ心地で、最大24時間持続します。ミディアムからフルカバー。美しい色合いが揃っています。
</p></li><li><p><strong>メイベリン ドリームスムースムースファンデーション説明</strong>：気に入る理由ユニークなクリームホイップファンデーションが、100%赤ちゃんのような滑らかな肌を実現します。14時間、うるおいを保ち、肌荒れや乾燥を防ぎます。軽いつけ心地で、完璧な保湿効果を発揮します。シームレスにブレンドされ、一日中フレッシュな使い心地です。オイルフリー、無香料、皮膚科医テスト済み、アレルギーテスト済み、ノンコメドジェニック。毛穴を詰まらせません。安全です。敏感肌用。</p></li></ol><p><strong>分析</strong>：「乾燥肌用ファンデーション」を検索した場合、検索語のキーワードと製品のタイトルおよび説明が完全に一致する結果が得られました。ただし、この一致が必ずしも最良の選択を反映するとは限りません。たとえば、<strong>レブロン カラーステイ メイクアップ ノーマル / ドライ スキンは、</strong>特に乾燥肌向けに作られているので、良い選択肢です。オイルフリーでありながら、持続的な水分補給を実現する処方になっています。対照的に、<strong>メイベリン ドリーム スムース ムース ファンデーション</strong>も入手しました。オイルフリーで保湿効果もあると謳っていますが、オイルフリーの製品は乾燥肌に必要な水分補給よりも皮脂のコントロールに重点を置く傾向があるため、一般的に脂性肌や混合肌の方におすすめです。これは、乾燥肌の人々の特定のニーズを完全に満たさない製品が返される可能性がある、キーワードベースの検索の限界を浮き彫りにしています。</p><p>ここで、ハイブリッド アプローチを使用して同じ検索を実行すると、次のようになります。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt412e38b327cf6a4f/6a17024066c4f94c52f8beb9/13b562a5120619355ba0861976102e208964a49f-1600x1021.png" alt="ハイブリッドアプローチを使用して検索を実行する" /><ol><li><p><strong>CoverGirl アウトラスト ステイ ルミナス ファンデーション クリーミー ナチュラル (820):説明</strong>: CoverGirl アウトラスト ステイ ルミナス ファンデーションは、みずみずしい仕上がりとほのかな輝きを実現するのに最適です。オイルフリーでべたつかない処方で、肌に自然な輝きを与え、一日中持続します。この一日中使えるファンデーションは、完璧なカバー力を提供しながら肌に潤いを与えます。<strong>分析:</strong>この製品は、乾燥肌のユーザーにとって重要な保湿を重視しているため、関連性があります。「肌に潤いを与える」と「みずみずしい仕上がり」という言葉は、乾燥肌用のファンデーションを探しているユーザーの意図と一致しています。ベクター検索では、水分補給の概念を理解し、それを乾燥肌に対応するファンデーションの必要性に結び付けたと考えられます。
</p></li><li><p><strong>レブロン カラーステイ メイクアップ (普通肌/乾燥肌用):説明:</strong>レブロン カラーステイ メイクアップは、軽い処方で、固まったり、色落ちしたり、擦り落ちたりしない、長持ちするカバー力を提供します。タイムリリーステクノロジーを採用したこのオイルフリーの水分バランスフォーミュラは、普通肌や乾燥肌に継続的に水分を補給するために特別に配合されています。<strong>分析:</strong>この製品は、普通肌または乾燥肌向けに作られていることを明記しており、乾燥肌のユーザーのニーズに直接応えています。「モイスチャーバランス処方」と継続的な水分補給は、乾燥肌向けのファンデーションを探している人に最適です。ベクター検索では、キーワードの一致だけでなく、水分補給に重点が置かれ、ターゲット層として乾燥肌が具体的に言及されていたため、この結果が正常に取得されました。
</p></li><li><p><strong>セラム ファンデーション説明:</strong>セラム ファンデーションは、軽量で中程度のカバー力を持つフォーミュラで、21 色にわたる幅広い色調が揃っています。これらのファンデーションは、適度なカバー力があり、自然な仕上がりで、非常に軽い美容液のような感触です。粘度は非常に低く、付属のポンプ、または必要に応じて別途購入できるオプションのガラス製スポイトを使用して注入します。<strong>分析:</strong>この場合、説明では、自然な感触の軽い美容液ファンデーションを強調しています。これは、乾燥肌の人のニーズに合致しています。乾燥肌の方は、優しく、保湿性があり、ケーキ状にならない仕上がりの製品を求めることが多いからです。ベクター検索では、軽量で自然なカバー力と美容液のようなテクスチャーという幅広い文脈が拾われたと思われます。これらは保湿性と快適な使用感に関連し、「乾燥肌」という言葉は明示的には使われていませんが、乾燥肌との関連性が感じられます。</p></li></ol><h2>ファセット実装</h2><p>ファセットは、検索結果を効率的に絞り込み、フィルタリングするために不可欠であり、特に電子商取引などの多種多様な製品を扱うシナリオでは、ユーザーに焦点を絞ったナビゲーションを提供します。ユーザーはカテゴリ、ブランド、価格などの属性に基づいて結果を調整できるため、検索の精度が向上します。この機能を実装するために、インデックス作成段階で<code>keyword</code>として定義された<code>category</code>フィールドと<code>brand</code>フィールドに対して<a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-bucket-terms-aggregation.html">用語の集計を</a>使用します。</p>    query = build_query(term, categories, product_types, brands)
    query["aggs"] = {
        "product_types": {"terms": {"field": "product_type"}},
        "categories": {"terms": {"field": "category"}},
        "brands": {"terms": {"field": "brand.keyword"}}
    }<p>実装の完全なコードは<a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/hybrid-search-for-an-e-commerce-product-catalogue/product-store-search/api/api.py#L144">ここに</a>あります。</p><p>「乾燥肌用ファンデーション」の検索結果を以下に示します。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt31080652a60d8600/6a1702416234e0af7ddb18e7/e8907af359c021eaa38ec7a6a53f10c325ee8ade-1146x1248.png" alt="「乾燥肌用ファンデーション」の検索結果" /><h2>結果のカスタマイズ: 固定されたクエリ</h2><p>場合によっては、検索結果で特定の商品を宣伝すると有益なことがあります。このため、特定の商品を結果の上部に表示できる<a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-pinned-query.html"><strong>Pinned Queries</strong></a>を使用します。以下では、商品を宣伝せずに「Foundation」という用語を検索します。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt31e75c815262993e/6a170243509168ae42e1b95d/9396c4ed358e7a68eed47f17dd6915d0bad492aa-1600x1157.png" alt="商品を宣伝せずに「ファンデーション」という用語を検索する" /><p>この例では、「グルテンフリー」のタグが付いた商品を宣伝できます。製品 ID を使用することで、検索結果で製品が優先されるようになります。具体的には、<strong>セラムファンデーション</strong>（ID：1043）、<strong>カバレッジファンデーション</strong>（ID：1042）、<strong>リアリスト インビジブルセッティングパウダー</strong>（ID：1039）の3商品をプロモーションします。</p>{
   "query":{
      "pinned":{
         "ids":[
            "1043",
            "1042",
            "1039"
         ],
         "organic":{
            "bool":{
               "must":[
                  {
                     "multi_match":{
                        "query":"foundation",
                        "fields":[
                           "name",
                           "category",
                           "description"
                        ]
                     }
                  }
               ]
            }
         }
      }
   }
}<p>特定の製品 ID を使用することで、クエリ結果で製品が優先されるようになります。クエリ構造には、上部に「固定」する必要がある製品 ID のリスト (この場合は ID 1043、1042、および 1039) が含まれており、残りの結果は、フィールド「名前」、「カテゴリ」、「説明」のテキスト クエリなどの条件の組み合わせを使用して、検索の自然な流れに従います。この方法により、通常の関連性に基づいて残りの検索を維持しながら、アイテムを制御された方法で宣伝してそれらの可視性を確保することが可能になります。</p><p>以下に、プロモーション対象製品を使用したクエリ実行の結果を示します。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt53a6d5cbef227f16/6a1702458b73cb68f0189ef3/8c1a547bfe31360c405eae0891c666635051a51b-1600x1039.png" alt="プロモーション対象商品に対するクエリ実行の結果" /><p>完全なクエリ コードは<a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/hybrid-search-for-an-e-commerce-product-catalogue/product-store-search/api/api.py#L112">ここに</a>あります。</p><h2>行動分析による検索行動の分析</h2><p>これまでに、検索結果の関連性を向上させ、製品の発見性を高めるための機能をすでに追加してきました。ここで、ユーザーの検索行動を分析し、結果の有無や検索結果のクリックなどのパターンを識別する機能を追加して、検索ソリューションを完成させます。そのためには、Elastic が提供する<strong>行動分析</strong>機能を使用します。これにより、わずか数ステップでユーザーの検索行動を監視および分析し、検索エクスペリエンスを最適化するための貴重な洞察を得ることができます。</p><h3>行動分析コレクションの作成</h3><p>最初のアクションは、すべての動作分析イベントを受信するコレクションを作成することです。コレクションを作成するには、 <strong>「検索」 &gt; 「行動分析」</strong>で Kibana インターフェースにアクセスします。以下の例では、 <code>tracking-search</code>という名前のコレクションを作成しました。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt9b4cb5480ab0bfef/6a1702474a531b189936a7e7/c05e533212a3690c5ea9f2d5226cbcdc901c482a-1600x1009.png" alt="行動分析 - コレクションの命名" /><h3>インターフェースに行動分析を統合する</h3><p><a href="https://github.com/elastic/elasticsearch-labs/tree/main/supporting-blog-content/hybrid-search-for-an-e-commerce-product-catalogue/app-product-store">フロントエンド</a>アプリケーションは JavaScript で開発されており、Behavioral Analytics を統合するには、Elastic の公式ドキュメントに記載されている手順に従って、 <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/behavioral-analytics-start.html#behavioral-analytics-start-ui-integration-js-client"><strong>Behavioral Analytics JavaScript Tracker を</strong></a>インストールします。</p><h3>JavaScriptトラッカーの実装</h3><p>ここで、トラッカー クライアントをアプリケーションにインポートし、メソッド<code>trackPageView</code> 、 <code>trackSearch</code> 、 <code>trackSearchClick</code>を使用してユーザー操作をキャプチャします。</p><p><strong>免責事項</strong>: 当社はユーザーのインタラクションデータを収集するためにツールを使用していますが、 <strong>GDPR</strong>への準拠を確実にするために不可欠です。これは、どのようなデータが収集され、どのように使用されるかをユーザーに明確に通知し、追跡をオプトアウトするオプションを提供することを意味します。さらに、収集した情報を保護し、データへのアクセスや削除などのユーザーの権利を尊重するために、強力なセキュリティ対策を実施し、すべての手順が GDPR の原則に準拠していることを確認する必要があります。
</p><p><strong>ステップ1: トラッカーインスタンスの作成</strong></p><p>まず、インタラクションを監視するトラッカー インスタンスを作成します。この構成では、ターゲット エンドポイント、コレクション名、API キーを定義します。</p>createTracker({
  endpoint: "https://endpoint:443",
  collectionName: "tracking-search",
  apiKey: "api-key"
});<p><strong>ステップ2: ページビューのキャプチャ</strong></p><p>ページビューを追跡するには、 <code>trackPageView</code>イベントを設定します。</p>    trackPageView({
      page: {
        title: "home-page"
      },
    });<p><code>trackPageView</code>イベントの詳細については、この<a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/behavioral-analytics-event-reference.html#behavioral-analytics-event-reference-pageview-fields">ドキュメント</a>を参照してください。</p><p><strong>ステップ3: 検索クエリのキャプチャ</strong></p><p>ユーザーの検索アクションを監視するには、 <code>trackSearch</code>メソッドを使用します。</p>      trackSearch({
        search: {
          query: searchTerm,
          results: {
            items: documents,
            total_results: response.data.length,
          },
        },
      });<p>ここでは検索用語と検索結果を収集しています。</p><p><strong>ステップ4: 検索結果のクリックを追跡する</strong></p><p>最後に、検索結果のクリックをキャプチャするために、 <code>trackSearchClick</code>メソッドを使用します。</p>trackSearchClick({
      document: { id: product.id, index: "products-catalog"},
      search: {
        query: searchTerm,
        page: {
          current: 1,
          size: products.length,
        },
        results: {
          items: documents,
          total_results: products.length,
        },
        search_application: "app-product-store"
      },
    });<p>クリックされたドキュメントの ID、検索用語、検索結果に関する情報も収集します。</p><h3>Kibanaでデータを分析する</h3><p>ユーザーインタラクションイベントがキャプチャされるようになったので、検索アクションに関する貴重なデータを取得できるようになりました。Kibana は、行動分析ツールを使用して、この行動データを視覚化して分析します。結果を表示するには、 <strong>「検索」&gt;「行動分析」&gt;「マイコレクション」</strong>に移動するだけで、キャプチャされたイベントの概要が表示されます。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltdab4f4507c5a4732/6a17024860084b34ca3c4411/e219c4af2e01b0ccc1b9079459a07832835abc75-1600x1155.png" alt="Kibanaでデータを分析する" /><p>この概要では、インターフェースに統合された各アクションでキャプチャされたイベントの概要を示します。この情報から、ユーザーの検索行動に関する貴重な洞察を得ることができます。ただし、特定のシナリオに関連性の高いメトリクスを使用してカスタマイズされたダッシュボードを作成したい場合、Kibana はダッシュボードを構築するための強力なツールを提供しており、メトリクスのさまざまな視覚化を作成できます。</p><p>以下に、時間の経過に伴う最も検索された用語、結果を返さなかったクエリ、最も検索された用語を強調表示するワード クラウド、最後に検索アクセスの発生元を識別する地理的な視覚化などを監視するための視覚化とグラフをいくつか作成しました。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt04ced4406436f942/6a17024a5091685116e1b961/84a2c39dab77a3f9366c258a3a45d2cbc7df125e-1600x689.png" alt="監視するための視覚化とグラフ" /><h2>まとめ</h2><p>この記事では、キーワード検索とベクター検索を組み合わせたハイブリッド検索ソリューションを実装し、ユーザーにとってより正確で関連性の高い結果を提供します。また、ファセットやピン留めされたクエリによる結果のパーソナライズなどの追加機能を使用して、より完全で効率的な検索エクスペリエンスを実現する方法についても検討しました。</p><p>さらに、Elastic の<strong>Behavioral Analytics を</strong>統合して、検索エンジンとのやり取り中のユーザー行動をキャプチャして分析しました。<code>trackPageView</code> 、 <code>trackSearch</code> 、 <code>trackSearchClick</code>などのメソッドを使用することで、検索クエリ、検索結果のクリック、ページビューを監視し、検索行動に関する貴重な分析情報を生成することができました。</p><h2>参照資料</h2><p>データセット</p><p><a href="https://www.kaggle.com/datasets/shivd24coder/cosmetic-brand-products-dataset">https://www.kaggle.com/datasets/shivd24coder/コスメティックブランド製品データセット</a></p><p>トランス</p><p><a href="https://huggingface.co/sentence-transformers/all-MiniLM-L6-v2">https://huggingface.co/sentence-transformers/all-MiniLM-L6-v2</a></p><p>相互ランク融合</p><p><a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/rrf.html">https://www.elastic.co/guide/en/elasticsearch/reference/current/rrf.html</a></p><p><a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/retriever.html#rrf-retriever">https://www.elastic.co/guide/en/elasticsearch/reference/current/retriever.html#rrf-retriever</a></p><p>Knnクエリ</p><p><a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/knn-search.html">https://www.elastic.co/guide/en/elasticsearch/reference/current/knn-search.html</a></p><p>固定されたクエリ</p><p><a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-pinned-query.html">https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-pinned-query.html</a></p><p>行動分析API</p><p><a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/behavioral-analytics-apis.html">https://www.elastic.co/guide/en/elasticsearch/reference/current/behavioral-analytics-apis.html</a></p><p>https://www.elastic.co/guide/en/elasticsearch/reference/current/behavioral-analytics-overview.html</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/hybrid-search-ecommerce</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/hybrid-search-ecommerce</guid>
    <category><![CDATA[Vector Database]]></category>
    <dc:creator><![CDATA[Andre Luiz]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt63c711d1bf9b2501/6a17024c66c4f9c2b7f8bebd/05578fc595a12f6b1ebf88a10a2a31e9971b545e-1200x628.png" length="0" type="image/png"/>
    <pubDate>Tue, 12 Nov 2024 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[BlazorとElasticsearchを使用した検索アプリの構築]]></title>
    <description><![CDATA[Blazor と Elasticsearch を使用して検索アプリケーションを構築する方法と、ハイブリッド検索に Elasticsearch .NET クライアントを使用する方法を学習します。]]></description>
    <content:encoded><![CDATA[<p>この記事では、C# スキルを活用して Blazor と Elasticsearch を使用した検索アプリケーションを構築する方法を学習します。<a href="https://www.elastic.co/guide/en/elasticsearch/client/net-api/current/introduction.html">Elasticsearch .NET</a>クライアントを使用して、<a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/full-text-queries.html">フルテキスト</a>、<a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/semantic-search.html">セマンティック</a>、<a href="https://www.elastic.co/search-labs/tutorials/search-tutorial/vector-search/hybrid-search">ハイブリッド</a>検索クエリを実行します。</p><p><strong>注:</strong> Elasticsearch C# クライアント<a href="https://www.elastic.co/guide/en/elasticsearch/client/net-api/7.17/nest.html">NEST</a>の古いバージョンに精通している場合は、NEST クライアントの廃止と新機能に関するこちらの<a href="https://www.elastic.co/search-labs/blog/net-client-evolution">ブログ投稿を</a>お読みください。<em>NESTは.NETクライアントの前世代であり、</em><code>Elastic.Clients.Elasticsearch package</code></p><p></p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltca8d1da68641fac0/6a17f65763173044d4585bfb/18f890286ca122cd97286c09ebf740208b802d0b-650x395.png" alt="Blazor アプリの構築: Blazor ダイアグラムを使用した esre" /><ul><li><p><a href="https://www.elastic.co/search-labs/blog/search-app-with-esre-blazor#what-is-blazor?">Blazor とは何ですか?</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/search-app-with-esre-blazor#what-is-esre?">ESRE とは何ですか?</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/search-app-with-esre-blazor#configuring-elser">ELSER の設定</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/search-app-with-esre-blazor#indexing-data">データのインデックス作成</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/search-app-with-esre-blazor#building-the-app-with-blazor-&amp;-elasticsearch">建築申請</a></p></li></ul><h2>Blazor とは何ですか?</h2><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt91278ae422883e28/6a17f6592f4a5c3213fa8a95/622741915d016b68bf94f742332d10d736d60052-707x461.png" alt="Blazorサーバー" /><p><a href="https://dotnet.microsoft.com/en-us/apps/aspnet/web-apps/blazor">Blazor は</a>、開発者がクライアントまたはサーバー上で実行される Web アプリケーションを構築できるようにするために Microsoft によって作成された、オープン ソースの HTML、CSS、および C# ベースの Web フレームワークです。Blazor を使用すると、再利用可能なコンポーネントを作成してアプリケーションをより速く構築することもできます。開発者は、同じファイル内に C# で HTML ビューとアクションを構築できるため、読みやすくクリーンなコードを維持できます。さらに、Blazor Hybrid を使用すると、.NET コードを介してネイティブ プラットフォーム機能にアクセスするネイティブ モバイル アプリを構築できます。</p><p>Blazor を優れたフレームワークにする機能の一部を以下に示します。</p><ul><li><p>サーバー側とクライアント側のレンダリングオプション</p></li><li><p>再利用可能なUIコンポーネント</p></li><li><p>SignalRによるリアルタイム更新</p></li><li><p>組み込みの状態管理</p></li><li><p>組み込みルーティングシステム</p></li><li><p>強力な型付けとコンパイル時のチェック</p></li></ul><h3>Blazor を選ぶ理由</h3><p>Blazor は他のフレームワークやライブラリに比べていくつかの利点があります。開発者はクライアント コードとサーバー コードの両方に C# を使用できるため、強力な型指定とコンパイル時のチェックが可能になり、信頼性が向上します。.NET エコシステムとシームレスに統合され、.NET ライブラリとツールの再利用を可能にし、強力なデバッグ サポートを提供します。</p><h2>ESRE とは何ですか?</h2><p><a href="https://www.elastic.co/elasticsearch/elasticsearch-relevance-engine">Elasticsearch Relevance Engine ™ (ESRE)</a>は、強力な Elasticsearch 検索エンジンをベースに機械学習と人工知能を使用して<a href="https://www.elastic.co/guide/en/esre/current/learn.html">検索アプリケーションを構築するためのツール</a>セットです。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltf313ba986de01b93/6a17d851505ac306fcad8975/4c0f2645ed1c27fe3ef61a1a9126adadfd8d5368-721x421.png" alt="エスレ" /><p>ESREについて詳しく知りたい方は、<a href="https://www.elastic.co/search-labs/blog/introducing-elasticsearch-relevance-engine-esre">こちらの</a>ブログ記事をご覧ください。</p><h2>ELSER の設定</h2><p>Elastic の<a href="https://www.elastic.co/elasticsearch/elasticsearch-relevance-engine">ESRE</a>機能を活用するために、モデルプロバイダーとして<a href="https://www.elastic.co/guide/en/machine-learning/current/ml-nlp-elser.html">ELSER を</a>使用します。</p><p><em>ElasticsearchのELSERモデルを使用するには、PlatinumまたはEnterpriseライセンスと、最低4GBの専用機械学習（ML）ノードが必要です。詳細は</em><a href="https://www.elastic.co/guide/en/machine-learning/8.15/ml-nlp-elser.html#elser-req"><em>こちらをご覧ください。</em></a></p><p>まず推論エンドポイントを作成します。</p>PUT _inference/sparse_embedding/my-elser-model
{
  "service": "elser",
  "service_settings": {
    "num_allocations": 1,
    "num_threads": 1
  }
}<p>ELSER を初めて使用する場合は、モデルがバックグラウンドで読み込まれるときに 502 Bad Gateway エラーが発生する可能性があります。Kibana の<code>Machine Learning &gt; Trained Models</code>でモデルのステータスを確認できます。デプロイが完了したら、次のステップに進むことができます。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltee295a3516338302/6a17f65b414c640deb94531d/a7ff94b72d337cde892165d744b9f42fba702a87-1440x649.png" alt="訓練済みモデルのチェック" /><h2>データのインデックス作成</h2><p><a href="https://github.com/elastic/elasticsearch-labs/tree/main/supporting-blog-content/esre-with-blazor/books.zip">ここから</a>データセットをダウンロードし、Kibana を使用してデータをインポートできます。これを行うには、ホームページにアクセスして「データのアップロード」をクリックします。次に、ファイルをアップロードして<code>Import</code>をクリックします。最後に、 <code>Advanced</code>タブに移動して、次のマッピングを貼り付けます。</p>{
   "properties":{
      "authors":{
         "type":"keyword"
      },
      "categories":{
         "type":"keyword"
      },
      "longDescription":{
         "type":"semantic_text",
         "inference_id":"my-elser-model",
         "model_settings":{
            "task_type":"sparse_embedding"
         }
      },
      "pageCount":{
         "type":"integer"
      },
      "publishedDate":{
         "type":"date"
      },
      "shortDescription":{
         "type":"text"
      },
      "status":{
         "type":"keyword"
      },
      "thumbnailUrl":{
         "type":"keyword"
      },
      "title":{
         "type":"text"
      }
   }
}<img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt0f07300647edd2ca/6a17f65dfaa913172a93ca0c/1aea0b9c51e275f89339f5d463fcaef799fc3943-1235x1083.png" alt="データのインポート" /><p>セマンティック クエリとフルテキスト クエリを実行できるインデックスを作成します。<a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/semantic-text.html">semantic_text</a>フィールド タイプは、データのチャンク化と埋め込みを処理します。として インデックス付けしていることに注意してください 。フィールドを<em> と text` の</em><em> 両方としてインデックス付けしたい場合は、 copy_to を使用できます</em><em> 。</em><em><code>longDescription</code></em><em><code>semantic_text</code></em><em><code>semantic_text</code></em></p><h2>BlazorとElasticsearchを使ったアプリ構築</h2><h3>APIキー</h3><p>最初に行う必要があるのは、Elasticsearch へのリクエストを認証するための API キーを作成することです。API キーは読み取り専用であり、 <code>books-blazor</code>インデックスのクエリのみに許可される必要があります。</p>POST /_security/api_key
{
  "name": "books-blazor-key",
  "role_descriptors": {
    "books-blazor-reader": {
      "indices": [
        {
          "names": ["books-blazor"],
          "privileges": ["read"]
        }
      ]
    }
  }
}<p>次のような画面が表示されます。
</p>{
  "id": "XXXXXXXXXXXXXXXXXXXXXXXX",
  "name": "books-blazor-key",
  "api_key": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
  "encoded": "XXXXXXXXXXXXXXXXXXXXXXXX=="
}<p>後で必要になるので、 <code>encoded</code>応答フィールドの値を保存します。<a href="https://www.elastic.co/cloud/">Elastic Cloud</a>で実行している場合は、Cloud ID も必要になります。(<a href="https://www.elastic.co/search-labs/tutorials/install-elasticsearch/elastic-cloud#finding-your-cloud-id">こちらで</a>確認できます)。</p><h4>Blazorプロジェクトの作成</h4><p>まず Blazor をインストールし、<a href="https://dotnet.microsoft.com/en-us/learn/aspnet/blazor-tutorial/install">公式の手順</a>に従ってサンプル プロジェクトを作成します。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltb5c20b896fe1e98e/6a17f65f6317301566585bff/f5720867d960c91fd1b3c4ae9be174e062e6b2fc-975x830.png" alt="Blazor チュートリアル - Blazor プロジェクトの作成" /><p>プロジェクトを作成すると、フォルダー構造とファイルは次のようになります。</p>BlazorApp/
|-- BlazorApp.csproj
|-- BlazorApp.sln
|-- Program.cs
|-- appsettings.Development.json
|-- appsettings.json
|-- Properties/
|   `-- launchSettings.json
|-- Components/
|   |-- App.razor
|   |-- Routes.razor
|   |-- _Imports.razor
|   |-- Layout/
|   |   |-- MainLayout.razor
|   |   |-- MainLayout.razor.css
|   |   |-- NavMenu.razor
|   |   `-- NavMenu.razor.css
|   `-- Pages/
|       |-- Counter.razor
|       |-- Error.razor
|       |-- Home.razor
|       `-- Weather.razor
|-- wwwroot/
|-- bin/
`-- obj/ <p>テンプレートアプリケーションには<a href="https://blog.getbootstrap.com/2021/08/04/bootstrap-5-1-0/">Bootstrap v5.1.0</a>が含まれていますスタイリング用。</p><p><a href="https://www.elastic.co/guide/en/elasticsearch/client/net-api/8.0/installation.html">Elasticsearch .NET</a>クライアントをインストールしてプロジェクトのセットアップを完了します。</p>dotnet add package Elastic.Clients.Elasticsearch<p>この手順を完了すると、ページは次のようになります。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt3b90e394ded7ff56/6a17f660faa913d49f93ca10/ef9d2186c2cf49e78a307c4aa69c51632e84578d-940x529.png" alt="Blazor の hello world" /><h3>フォルダ構造</h3><p>ここで、フォルダーを次のように整理します。</p>BlazorApp/
|-- Components/
|   |-- Pages/
|   |   |-- Search.razor
|   |   `-- Search.razor.css
|   `-- Elasticsearch/
|       |-- SearchBar.razor
|       |-- Results.razor
|       `-- Facet.razor
|-- Models/
|   |-- Book.cs
|   `-- Response.cs
`-- Services/
    `-- ElasticsearchService.cs<p>ファイルの説明:</p><ul><li><p>Components/Pages/Search.razor: 検索バー、結果、フィルターを含むメイン ページ。</p></li><li><p>コンポーネント/ページ/Search.razor.css:ページ スタイル。</p></li><li><p>Components/Elasticsearch/SearchBar.razor: 検索バー コンポーネント。</p></li><li><p>Components/Elasticsearch/Results.razor: 結果コンポーネント。</p></li><li><p>Components/Elasticsearch/Facet.razor: フィルター コンポーネント。</p></li><li><p>Components/Svg/GlassIcon.razor: 検索アイコン。</p></li><li><p>Components/_Imports.razor: これにより、すべてのコンポーネントがインポートされます。</p></li><li><p>Models/Book.cs: ブックフィールドのスキーマを保存します。</p></li><li><p>Models/Response.cs: 検索結果、ファセット、ヒット数の合計を含む応答スキーマを保存します。</p></li><li><p>Services/ElasticsearchService.cs: Elasticsearch サービス。Elasticsearch への接続とクエリを処理します。</p></li></ul><h4>初期設定</h4><p>まずはクリーンアップから始めましょう。</p><p>ファイルを削除します:</p><ul><li><p>コンポーネント/ページ/Counter.razor</p></li><li><p>コンポーネント/ページ/Weather.razor</p></li><li><p>コンポーネント/ページ/ホーム.razor</p></li><li><p>コンポーネント/レイアウト/NavMenu.razor</p></li><li><p>コンポーネント/レイアウト/NavMenu.razor.css</p></li></ul><p><code>/Components/_Imports.razor</code>ファイルを確認してください。次のインポートが必要です。</p>@using System.Net.Http
@using System.Net.Http.Json
@using Microsoft.AspNetCore.Components.Forms
@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.AspNetCore.Components.Web
@using static Microsoft.AspNetCore.Components.Web.RenderMode
@using Microsoft.AspNetCore.Components.Web.Virtualization
@using Microsoft.JSInterop
@using BlazorApp
@using BlazorApp.Components<h4>プロジェクトにElasticを統合する</h4><p>次に、Elasticsearch コンポーネントをインポートします。</p>@using System.Net.Http
@using System.Net.Http.Json
@using Microsoft.AspNetCore.Components.Forms
@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.AspNetCore.Components.Web
@using static Microsoft.AspNetCore.Components.Web.RenderMode
@using Microsoft.AspNetCore.Components.Web.Virtualization
@using Microsoft.JSInterop
@using BlazorApp
@using BlazorApp.Components
@using BlazorApp.Components.Elasticsearch @* &lt;--- Add this line *@<p>アプリケーションのスペースを増やすために、 <code>/Components/Layout/MainLayout.razor</code>ファイルからデフォルトのサイドバーを削除します。</p>@inherits LayoutComponentBase

&lt;div class="page"&gt;
    &lt;main&gt;
        &lt;article class="content"&gt;
            @Body
        &lt;/article&gt;
    &lt;/main&gt;
&lt;/div&gt;

&lt;div id="blazor-error-ui"&gt;
    An unhandled error has occurred.
    &lt;a href="" class="reload"&gt;Reload&lt;/a&gt;
    &lt;a class="dismiss"&gt;🗙&lt;/a&gt;
&lt;/div&gt;<img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt441012c5aeac85f8/6a17f661ec0f8949f15a67ac/55bfff607f9167a532f83ad074b479364a346c6e-816x472.png" alt="ナビゲーションバーをレイアウトから削除しました" /><p>次に、<a href="https://learn.microsoft.com/en-us/aspnet/core/security/app-secrets?view=aspnetcore-8.0&amp;tabs=linux#secret-manager">ユーザー シークレット</a>の Elasticsearch 資格情報を入力します。</p>dotnet user-secrets init
dotnet user-secrets set ElasticsearchCloudId "your Cloud ID"
dotnet user-secrets set ElasticsearchApiKey "your API Key"<p>このアプローチを使用すると、.Net 8 は機密データをプロジェクト フォルダー外の別の場所に保存し、 <code>IConfiguration</code>インターフェースを使用してアクセスできるようになります。これらの変数は、同じユーザー シークレットを使用するすべての .Net プロジェクトで使用できます。</p><p>次に、 <code>Program.cs</code>ファイルを変更してシークレットを読み取り、Elasticsearch クライアントをマウントします。</p><p>まず、必要なライブラリをインポートします。</p>using BlazorApp.Services;
using Elastic.Clients.Elasticsearch;
using Elastic.Transport;<ul><li><p>BlazorApp.Services: Elasticsearch サービスが含まれます。</p></li><li><p>Elastic.Clients.Elasticsearch: Elasticsearch クライアント .Net 8 ライブラリをインポートします。</p></li><li><p>Elastic.Transport: Elasticsearch トランスポート ライブラリをインポートします。これにより、ApiKey クラスを使用してリクエストを認証できるようになります。</p></li></ul><p>次に、 <code>var app = builder.Build()</code>行の前に次のコードを挿入します。</p>// Initialize the Elasticsearch client.
builder.Services.AddScoped(sp =&gt;
{
    // Getting access to the configuration service to read the Elasticsearch credentials.
    var configuration = sp.GetRequiredService&lt;IConfiguration&gt;();
    var cloudId = configuration["ElasticsearchCloudId"];
    var apiKey = configuration["ElasticsearchApiKey"];

    if (string.IsNullOrEmpty(cloudId) || string.IsNullOrEmpty(apiKey))
    {
        throw new InvalidOperationException(
            "Elasticsearch credentials are missing in configuration."
        );
    }

    var settings = new ElasticsearchClientSettings(cloudId, new ApiKey(apiKey)).EnableDebugMode();
    return new ElasticsearchClient(settings);
});<p>このコードは、ユーザー シークレットから Elasticsearch 資格情報を読み取り、Elasticsearch クライアント インスタンスを作成します。</p><p>ElasticSearch クライアントの初期化後、次の行を追加して Elasticsearch サービスを登録します。</p>builder.Services.AddScoped&lt;ElasticsearchService&gt;();<p>次のステップでは、 <code>/Services/ElasticsearchService.cs</code>ファイルに検索ロジックを構築します。</p><p>まず、必要なライブラリとモデルをインポートします。</p>using BlazorApp.Models;
using Elastic.Clients.Elasticsearch;
using Elastic.Clients.Elasticsearch.QueryDsl;<p>次に、クラス<code>ElasticsearchService</code> 、コンストラクター、および変数を追加します。</p>namespace BlazorApp.Services
{
    public class ElasticsearchService
    {
        private readonly ElasticsearchClient _client;

        // The logger is used to log information, warnings and errors about the Elasticsearch service and requests.
        private readonly ILogger&lt;ElasticsearchService&gt; _logger;

        public ElasticsearchService(
            ElasticsearchClient client,
            ILogger&lt;ElasticsearchService&gt; logger
        )
        {
            _client = client ?? throw new ArgumentNullException(nameof(client));
            _logger = logger;
        }
    }
}<h4>検索の設定</h4><p>それでは、検索ロジックを構築してみましょう。</p>private static Action&lt;RetrieverDescriptor&lt;BookDoc&gt;&gt; BuildHybridQuery(
    string searchTerm,
    Dictionary&lt;string, List&lt;string&gt;&gt; selectedFacets
)
{
    var filters = BuildFilters(selectedFacets);

    return retrievers =&gt;
        retrievers.Rrf(rrf =&gt;
            rrf.RankWindowSize(50)
                .RankConstant(20)
                .Retrievers(
                    retrievers =&gt;
                        retrievers.Standard(std =&gt;
                            std.Query(q =&gt;
                                q.Bool(b =&gt;
                                    b.Must(m =&gt;
                                            m.MultiMatch(mm =&gt;
                                                mm.Query(searchTerm)
                                                    .Fields(
                                                        new[]
                                                        {
                                                            "title",
                                                            "shortDescription",
                                                        }
                                                    )
                                            )
                                        )
                                        .Filter(filters.ToArray())
                                )
                            )
                        ),
                    retrievers =&gt;
                        retrievers.Standard(std =&gt;
                            std.Query(q =&gt;
                                q.Bool(b =&gt;
                                    b.Must(m =&gt;
                                            m.Semantic(sem =&gt;
                                                sem.Field("longDescription")
                                                    .Query(searchTerm)
                                            )
                                        )
                                        .Filter(filters.ToArray())
                                )
                            )
                        )
                )
        );
}

public static List&lt;Action&lt;QueryDescriptor&lt;BookDoc&gt;&gt;&gt; BuildFilters(
    Dictionary&lt;string, List&lt;string&gt;&gt; selectedFacets
)
{
    var filters = new List&lt;Action&lt;QueryDescriptor&lt;BookDoc&gt;&gt;&gt;();

    if (selectedFacets != null)
    {
        foreach (var facet in selectedFacets)
        {
            foreach (var value in facet.Value)
            {
                var field = facet.Key.ToLower();
                if (!string.IsNullOrEmpty(field))
                {
                    filters.Add(m =&gt; m.Term(t =&gt; t.Field(new Field(field)).Value(value)));
                }
            }
        }
    }

    return filters;
}<ul><li><p><code>BuildFilters</code> ユーザーが選択したファセットを使用して、検索クエリのフィルターを構築します。</p></li><li><p><code>BuildHybridQuery</code> 全文検索とセマンティック検索を組み合わせた<a href="https://www.elastic.co/search-labs/tutorials/search-tutorial/vector-search/hybrid-search">ハイブリッド検索</a>クエリを構築します。</p></li></ul><p>次に、検索メソッドを追加します。</p>public async Task&lt;ElasticResponse&gt; SearchBooksAsync(
    string searchTerm,
    Dictionary&lt;string, List&lt;string&gt;&gt; selectedFacets
)
{
    try
    {
        _logger.LogInformation($"Performing search for: {searchTerm}");

        // Retrieve the hybrid query with filters applied.
        var retrieverQuery = BuildHybridQuery(searchTerm, selectedFacets);

        var response = await _client.SearchAsync&lt;BookDoc&gt;(s =&gt;
            s.Index("elastic-blazor-books")
                .Retriever(retrieverQuery)
                .Aggregations(aggs =&gt;
                    aggs.Add("Authors", agg =&gt; agg.Terms(t =&gt; t.Field(p =&gt; p.Authors)))
                        .Add(
                            "Categories",
                            agg =&gt; agg.Terms(t =&gt; t.Field(p =&gt; p.Categories))
                        )
                        .Add("Status", agg =&gt; agg.Terms(t =&gt; t.Field(p =&gt; p.Status)))
                )
        );

        if (response.IsValidResponse)
        {
            _logger.LogInformation($"Found {response.Documents.Count} documents");

            var hits = response.Total;
            var facets =
                response.Aggregations != null
                    ? FormatFacets(response.Aggregations)
                    : new Dictionary&lt;string, Dictionary&lt;string, long&gt;&gt;();

            var elasticResponse = new ElasticResponse
            {
                TotalHits = hits,
                Documents = response.Documents.ToList(),
                Facets = facets,
            };

            return elasticResponse;
        }
        else
        {
            _logger.LogWarning($"Invalid response: {response.DebugInformation}");
            return new ElasticResponse();
        }
    }
    catch (Exception ex)
    {
        _logger.LogError(ex, "Error performing search");
        return new ElasticResponse();
    }
}

public static Dictionary&lt;string, Dictionary&lt;string, long&gt;&gt; FormatFacets(
    Elastic.Clients.Elasticsearch.Aggregations.AggregateDictionary aggregations
)
{
    var facets = new Dictionary&lt;string, Dictionary&lt;string, long&gt;&gt;();

    foreach (var aggregation in aggregations)
    {
        if (
            aggregation.Value
            is Elastic.Clients.Elasticsearch.Aggregations.StringTermsAggregate termsAggregate
        )
        {
            var facetName = aggregation.Key;
            var facetDictionary = ConvertFacetDictionary(
                termsAggregate.Buckets.ToDictionary(b =&gt; b.Key, b =&gt; b.DocCount)
            );
            facets[facetName] = facetDictionary;
        }
    }

    return facets;
}

private static Dictionary&lt;string, long&gt; ConvertFacetDictionary(
    Dictionary&lt;Elastic.Clients.Elasticsearch.FieldValue, long&gt; original
)
{
    var result = new Dictionary&lt;string, long&gt;();
    foreach (var kvp in original)
    {
        result[kvp.Key.ToString()] = kvp.Value;
    }
    return result;
}<ul><li><p><code>SearchBooksAsync</code>: ハイブリッド クエリを使用して検索を実行し、ファセットを構築するための集計が含まれた結果を返します。</p></li><li><p><code>FormatFacets</code>: 集計応答を辞書にフォーマットします。</p></li><li><p><code>ConvertFacetDictionary</code>: ファセット辞書をより読みやすい形式に変換します。</p></li></ul><p>次のステップでは、検索ページの結果として出力される Elasticsearch クエリの<code>hits</code>で返されるデータを表すモデルを作成します。</p><p>まず、ファイル<code>/Models/Book.cs</code>を作成し、次の内容を追加します。</p>namespace BlazorApp.Models
{
    public class BookDoc
    {
        public string? Title { get; set; }
        public int? PageCount { get; set; }
        public string? PublishedDate { get; set; }
        public string? ThumbnailUrl { get; set; }
        public string? ShortDescription { get; set; }
        public LongDescription? LongDescription { get; set; }
        public string? Status { get; set; }
        public List&lt;string&gt;? Authors { get; set; }
        public List&lt;string&gt;? Categories { get; set; }
    }

    public class LongDescription
    {
        public string? Text { get; set; }
    }
}<p>次に、 <code>/Models/Response.cs</code>ファイルに Elastic レスポンスを設定し、次のコードを追加します。</p>namespace BlazorApp.Models
{
    public class ElasticResponse
    {
        public ElasticResponse()
        {
            Documents = new List&lt;BookDoc&gt;();
            Facets = new Dictionary&lt;string, Dictionary&lt;string, long&gt;&gt;();
        }

        public long TotalHits { get; set; }
        public List&lt;BookDoc&gt; Documents { get; set; }
        public Dictionary&lt;string, Dictionary&lt;string, long&gt;&gt; Facets { get; set; }
    }
}<h4>基本的なUIの設定</h4><p>次に、SearchBar コンポーネントを追加します。ファイル<code>/Components/Elasticsearch/SearchBar.razor</code>に次の内容を追加します。</p>@using System.Threading.Tasks

&lt;form @onsubmit="SubmitSearch"&gt;
  &lt;div class="input-group mb-3"&gt;
    &lt;input type="text" @bind-value="searchTerm" class="form-control" placeholder="Enter search term..." /&gt;
    &lt;button type="submit" class="btn btn-primary input-btn"&gt;
      &lt;span class="input-group-svg"&gt;
        Search
      &lt;/span&gt;
    &lt;/button&gt;
  &lt;/div&gt;
&lt;/form&gt;

@code {
  [Parameter]
  public EventCallback&lt;string&gt; OnSearch { get; set; }

  private string searchTerm = "";

  private async Task SubmitSearch()
  {
    await OnSearch.InvokeAsync(searchTerm);
  }
}<p>このコンポーネントには、検索バーと検索を実行するためのボタンが含まれています。</p><p>Blazor は、同じファイル内で C# コードを使用して HTML を動的に生成できるようにすることで、優れた柔軟性を提供します。</p><p>その後、ファイル<code>/Components/Elasticsearch/Results.razor</code>で、検索結果を表示する結果コンポーネントを構築します。</p>@using BlazorApp.Models

@if (SearchResults != null &amp;&amp; SearchResults.Any())
{
  &lt;div class="row"&gt;
  @foreach (var result in SearchResults)
    {
      &lt;div class="col-12 mb-3"&gt;
        &lt;div class="card"&gt;
          &lt;div class="row g-0"&gt;
            &lt;div class="col-md-3 image-container"&gt;
              @if (!string.IsNullOrEmpty(result?.ThumbnailUrl))
              {
                &lt;img src="@result?.ThumbnailUrl" class="img-fluid rounded-start" alt="Thumbnail"&gt;
              }
              else
              {
                &lt;div class="placeholder"&gt;
                  @result?.Title
                &lt;/div&gt;
              }
            &lt;/div&gt;

            &lt;div class="col-md-9"&gt; &lt;!-- Adjusted to use the remaining 75% --&gt;
              &lt;div class="card-body"&gt;
                &lt;h4 class="card-title"&gt;
                  @result?.Title
                &lt;/h4&gt;

                &lt;div class="details-container"&gt;
                  &lt;div class=""&gt;

                    @if (result?.Authors?.Any() == true)
                    {
                      &lt;p class="card-text p-first"&gt;
                        Authors: &lt;small class="text-muted"&gt;@string.Join(", ", result.Authors)&lt;/small&gt;
                      &lt;/p&gt;
                    }

                    @if (result?.Categories?.Any() == true)
                    {
                      &lt;p class="card-text p-second"&gt;
                        Categories: &lt;small class="text-muted"&gt;@string.Join(", ", result.Categories)&lt;/small&gt;
                      &lt;/p&gt;
                    }
                  &lt;/div&gt;
                  &lt;div class="numPages-status"&gt;
                    @if (result?.PageCount != null)
                    {
                      &lt;p class="card-text p-first"&gt;
                        Pages: &lt;small class="text-muted"&gt;@result.PageCount&lt;/small&gt;
                      &lt;/p&gt;
                    }

                    @if (result?.Status != null)
                    {
                      &lt;p class="card-text p-second"&gt;
                        Status: &lt;small class="text-muted"&gt;@result.Status&lt;/small&gt;
                      &lt;/p&gt;
                    }
                  &lt;/div&gt;
                &lt;/div&gt;

                &lt;div class="long-text-container"&gt;
                  &lt;p class="card-text"&gt;&lt;small class="text-muted"&gt;@result?.LongDescription?.Text&lt;/small&gt;&lt;/p&gt;
                &lt;/div&gt;
                @if (!string.IsNullOrEmpty(result?.PublishedDate))
                {
                  &lt;div class="date-container"&gt;
                    &lt;p class="card-text"&gt;
                      Published Date: &lt;small class="text-muted small-date"&gt;@FormatDate(result.PublishedDate)&lt;/small&gt;
                    &lt;/p&gt;
                  &lt;/div&gt;
                }
              &lt;/div&gt;
            &lt;/div&gt;
          &lt;/div&gt;
        &lt;/div&gt;
      &lt;/div&gt;
    }
  &lt;/div&gt;
}
else if (SearchResults != null)
{
  &lt;p&gt;No results found.&lt;/p&gt;
}

@code {
  [Parameter]
  public List&lt;BookDoc&gt; SearchResults { get; set; } = new List&lt;BookDoc&gt;();

  private string FormatDate(string? date)
  {
    if (DateTime.TryParse(date, out DateTime parsedDate))
    {
      return parsedDate.ToString("MMMM dd, yyyy");
    }
    return "";
  }
}<p>最後に、検索結果をフィルタリングするためのファセットを作成する必要があります。</p><p><em>注: ファセットとは、製品タイプ、価格帯、ブランドなど、特定の属性やカテゴリに基づいて検索結果を絞り込むことができるフィルターです。これらのフィルターは通常、クリック可能なオプション（多くの場合チェックボックス）として表示され、ユーザーが検索を絞り込み、関連性の高い結果を見つけやすくします。Elasticsearchでは、ファセットは</em><a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations.html"><em>集計を</em></a>使用して作成されます<em>。</em></p><p>ファイル<code>/Components/Elasticsearch/Facet.razor</code>に次のコードを配置してファセットを設定します。</p>@if (Facets != null)
{
  &lt;div class="facets-container"&gt;
  @foreach (var facet in Facets)
    {
      &lt;h3&gt;@facet.Key&lt;/h3&gt;
      @foreach (var option in facet.Value)
      {
        &lt;div&gt;
          &lt;input type="checkbox" checked="@IsFacetSelected(facet.Key, option.Key)"
            @onclick="() =&gt; ToggleFacet(facet.Key, option.Key)" /&gt;
          @option.Key (@option.Value)
        &lt;/div&gt;
      }
    }
  &lt;/div&gt;
}


@code {
  [Parameter]
  public Dictionary&lt;string, Dictionary&lt;string, long&gt;&gt;? Facets { get; set; }

  [Parameter]
  public EventCallback&lt;Dictionary&lt;string, List&lt;string&gt;&gt;&gt; OnFacetChanged { get; set; }

  private Dictionary&lt;string, List&lt;string&gt;&gt; selectedFacets = new();

  private void ToggleFacet(string facetName, string facetValue)
  {
    if (!selectedFacets.TryGetValue(facetName, out var facetValues))
    {
      facetValues = selectedFacets[facetName] = new List&lt;string&gt;();
    }

    if (!facetValues.Remove(facetValue))
    {
      facetValues.Add(facetValue);
    }

    OnFacetChanged.InvokeAsync(selectedFacets);
  }

  private bool IsFacetSelected(string facetName, string facetValue)
  {
    return selectedFacets.ContainsKey(facetName) &amp;&amp; selectedFacets[facetName].Contains(facetValue);
  }
}<p>このコンポーネントは、 <code>author</code> 、 <code>categories</code> 、および<code>status</code>フィールドの<code>terms</code>集計を読み取り、Elasticsearch に送り返すフィルターのリストを生成します。</p><p>さて、すべてをまとめてみましょう。</p><p><code>/Components/Pages/Search.razor</code>ファイル内:</p>@page "/"
@rendermode InteractiveServer
@using BlazorApp.Models
@using BlazorApp.Services
@inject ElasticsearchService ElasticsearchService
@inject ILogger&lt;Search&gt; Logger

&lt;PageTitle&gt;Search&lt;/PageTitle&gt;

&lt;div class="top-row px-4 "&gt;

    &lt;div class="searchbar-container"&gt;
        &lt;h4&gt;Semantic Search with Elasticsearch and Blazor&lt;/h4&gt;

        &lt;SearchBar OnSearch="PerformSearch" /&gt;
    &lt;/div&gt;

    &lt;a href="https://www.elastic.co/search-labs/esre-with-blazor" target="_blank"&gt;About&lt;/a&gt;
&lt;/div&gt;

&lt;div class="px-4"&gt;

    &lt;div class="search-details-container"&gt;
        &lt;p role="status"&gt;Current search term: @currentSearchTerm&lt;/p&gt;
        &lt;p role="status"&gt;Total results: @totalResults&lt;/p&gt;
    &lt;/div&gt;

    &lt;div class="results-facet-container"&gt;
        &lt;div class="facets-container"&gt;
            &lt;Facet Facets="facets" OnFacetChanged="OnFacetChanged" /&gt;
        &lt;/div&gt;
        &lt;div class="results-container"&gt;
            &lt;Results SearchResults="searchResults" /&gt;
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;

@code {
    private string currentSearchTerm = "";
    private long totalResults = 0;
    private List&lt;BookDoc&gt; searchResults = new List&lt;BookDoc&gt;();
    private Dictionary&lt;string, Dictionary&lt;string, long&gt;&gt; facets = new Dictionary&lt;string, Dictionary&lt;string, long&gt;&gt;();
    private Dictionary&lt;string, List&lt;string&gt;&gt; selectedFacets = new Dictionary&lt;string, List&lt;string&gt;&gt;();

    protected override async Task OnInitializedAsync()
    {
        await PerformSearch();
    }

    private async Task PerformSearch(string searchTerm = "")
    {
        try
        {
            currentSearchTerm = searchTerm;

            var response = await ElasticsearchService.SearchBooksAsync(currentSearchTerm, selectedFacets);
            if (response != null)
            {
                searchResults = response.Documents;
                facets = response.Facets;
                totalResults = response.TotalHits;
            }
            else
            {
                Logger.LogWarning("Search response is null.");
            }

            StateHasChanged();
        }
        catch (Exception ex)
        {
            Logger.LogError(ex, "Error performing search.");
        }
    }

    private async Task OnFacetChanged(Dictionary&lt;string, List&lt;string&gt;&gt; newSelectedFacets)
    {
        selectedFacets = newSelectedFacets;
        await PerformSearch(currentSearchTerm);
    }
}<p>私たちのページは機能しています!</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltc750e55fff43de4b/6a17f6636864a4ab08b68931/5f0f25cb029a34df7f577a9f307278226ed07c3f-816x473.png" alt="Blazorページの例" /><p>ご覧のとおり、ページは機能しますが、スタイルが欠けています。見た目をもっと整理してレスポンシブにするために、CSS を追加してみましょう。</p><p>レイアウト スタイルの置き換えを始めましょう。<code>Components/Layout/MainLayout.razor.css</code>ファイル内:</p>.page {
  position: relative;
  display: flex;
  flex-direction: column;
}

main {
  flex: 1;
}

#blazor-error-ui {
  background: lightyellow;
  bottom: 0;
  box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2);
  display: none;
  left: 0;
  padding: 0.6rem 1.25rem 0.7rem 1.25rem;
  position: fixed;
  width: 100%;
  z-index: 1000;
}

#blazor-error-ui .dismiss {
  cursor: pointer;
  position: absolute;
  right: 0.75rem;
  top: 0.5rem;
}<p><code>Components/Pages/Search.razor.css</code>ファイルで検索ページのスタイルを追加します。</p>.input-group .input-group-svg {
  background: transparent;
  border: transparent;
  pointer-events: none;
}

.results-facet-container {
  display: flex;
  margin-top: 1rem;
  overflow-x: auto;
}

.search-details-container {
  display: flex;
  justify-content: space-between;
  margin-top: 1rem;
}

.searchbar-container {
  padding-top: 2rem;
  display: flex;
  flex-direction: column; 
  flex-grow: 1;
  height: 100%;
  max-width: 100%; 
}

.searchbar-container h4 {
  margin: 0;
}

.top-row {
  margin-top: -1.1rem;
  position: relative; 
  background-color: hsl(216, 29%, 67%);
  border-bottom: 1px solid #d6d5d5;
  display: flex;
  align-items: center;
  height: 100%;
  padding: 0 1rem;
}

.top-row a {
  margin-left: auto;
  margin-top: -4rem; 
  color: #000000;
  text-decoration: none;
}

.top-row a:hover {
  text-decoration: underline;
}

@media (max-width: 640.98px) {
  .top-row {
    justify-content: space-between;
  }

  .top-row ::deep a,
  .top-row ::deep .btn-link {
    margin-left: 0;
  }
}

@media (min-width: 641px) {
  .top-row.auth ::deep a:first-child {
    flex: 1;
    text-align: right;
    width: 0;
  }

  .top-row,
  article {
    padding-left: 2rem !important;
    padding-right: 1.5rem !important;
  }
}<p>ページの見た目が良くなり始めました:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt3ecc49f404f18591/6a17f6654b055d959b432384/3dadd7ade2dd9070300a4e0514a4da2ae9cc9fb9-817x473.png" alt="検索ページにスタイルを追加した後の Blazor ページ" /><p>最後に仕上げましょう:</p><p>次のファイルを作成します。</p><ul><li><p>コンポーネント/Elasticsearch/Facet.razor.css</p></li><li><p>コンポーネント/Elasticsearch/Results.razor.css</p></li></ul><p><code>Facet.razor.css</code>のスタイルを追加します:</p>.facets-container {
  font-size: 15px;
  margin-right: 4rem;
  overflow-x: auto;
  white-space: nowrap;
  max-width: 300px;
}

.results-facet-container {
  display: flex;
  margin-top: 1rem;
  overflow-x: auto;
}

.results-facet-container &gt; * {
  flex-shrink: 0;
}<p><code>Results.razor.css</code>の場合:</p>.image-container {
  display: flex;
  justify-content: center;
  align-items: center;
  height: 100%;
  padding: 1rem;
  box-sizing: border-box;
}

.image-container img {
  max-width: 100%;
  height: auto;
  border-radius: 0.5rem;
}

.placeholder {
  display: flex;
  justify-content: center;
  align-items: center;
  height: 100%;
  width: 100%;
  background-color: #f0f0f0;
  border: 1px solid #ccc;
  font-size: 0.9rem;
  color: #888;
  text-align: center;
  padding: 1rem;
  border-radius: 0.5rem;
}

.card-body {
  padding: 1rem;
}

.details-container {
  display: flex;
  justify-content: space-between;
  padding: 1.5rem 0;
}

.date-container {
  margin-top: 1rem;
  display: flex;
  justify-content: flex-end;
}

.date-container .small-date {
  font-weight: bold;
}<p>最終結果:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt78bbd8e6f9e23988/6a17f6664b055d2ed8432388/3e68ec4a38775fdfbeaa1a990e6a1e11dfb081d8-816x472.png" alt="Blazor アプリページの構築の最終結果" /><p>アプリケーションを実行するには、次のコマンドを使用できます。</p><p><code>dotnet watch</code></p><p>やったね！検索バーを使用して Elasticsearch インデックス内の書籍を検索し、著者、カテゴリ、ステータス別に結果をフィルタリングできるようになりました。</p><h3>全文検索とセマンティック検索の実行</h3><p>デフォルトでは、アプリは<a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-multi-match-query.html"> 全文検索</a> と <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-semantic-query.html">セマンティック検索の</a><a href="https://www.elastic.co/search-labs/tutorials/search-tutorial/vector-search/hybrid-search"> 両方を使用して ハイブリッド検索を</a> 実行します。フルテキスト検索用とセマンティック検索用の 2 つの別個のメソッドを作成し、いずれかのメソッドを選択してユーザーの入力に基づいてクエリを構築することで、検索ロジックを変更できます。</p><p><code>/Services/ElasticsearchService.cs</code>ファイルの<code>ElasticsearchService</code>クラスに次のメソッドを追加します。</p>private static Action&lt;QueryDescriptor&lt;BookDoc&gt;&gt; BuildSemanticQuery(
    string searchTerm,
    Dictionary&lt;string, List&lt;string&gt;&gt; selectedFacets
)
{
    var filters = BuildFilters(selectedFacets);

    return query =&gt;
        query.Bool(b =&gt;
            b.Must(m =&gt; m.Semantic(sem =&gt; sem.Field("longDescription").Query(searchTerm)))
                .Filter(filters.ToArray())
        );
}

private static Action&lt;QueryDescriptor&lt;BookDoc&gt;&gt; BuildMultiMatchQuery(
    string searchTerm,
    Dictionary&lt;string, List&lt;string&gt;&gt; selectedFacets
)
{
    var filters = BuildFilters(selectedFacets);

    if (string.IsNullOrEmpty(searchTerm))
    {
        return query =&gt; query.Bool(b =&gt; b.Filter(filters.ToArray()));
    }

    return query =&gt;
        query.Bool(b =&gt;
            b.Should(m =&gt;
                    m.MultiMatch(mm =&gt;
                        mm.Query(searchTerm).Fields(new[] { "title", "shortDescription" })
                    )
                )
                .Filter(filters.ToArray())
        );
}<p>どちらのメソッドも<code>BuildHybridQuery</code>メソッドと同様に動作しますが、全文検索またはセマンティック検索のみを実行します。</p><p>選択した検索方法を使用するように<code>SearchBooksAsync</code>メソッドを変更できます。</p>public async Task&lt;ElasticResponse&gt; SearchBooksAsync(
    string searchTerm,
    Dictionary&lt;string, List&lt;string&gt;&gt; selectedFacets
)
{
    try
    {
        _logger.LogInformation($"Performing search for: {searchTerm}");
        
        // Modify the query builder to use the selected search method.
        var multiMatchQuery = BuildMultiMatchQuery(searchTerm, selectedFacets); // For full text search
        var semanticQuery = BuildSemanticQuery(searchTerm, selectedFacets); // For semantic search

        // In this case we will not use retrievers, but you can add them if you want to use them.
        var response = await _client.SearchAsync&lt;BookDoc&gt;(s =&gt;
            s.Index("elastic-blazor-books")
                .Query(multiMatchQuery) // Change this line to use different search methods, for example: .Query(semanticQuery) for semantic search
                .Aggregations(aggs =&gt;
                    aggs.Add("Authors", agg =&gt; agg.Terms(t =&gt; t.Field(p =&gt; p.Authors)))
                        .Add(
                            "Categories",
                            agg =&gt; agg.Terms(t =&gt; t.Field(p =&gt; p.Categories))
                        )
                        .Add("Status", agg =&gt; agg.Terms(t =&gt; t.Field(p =&gt; p.Status)))
                )
        );

        if (response.IsValidResponse)
        {
            _logger.LogInformation($"Found {response.Documents.Count} documents");

            var hits = response.Total;
            var facets =
                response.Aggregations != null
                    ? FormatFacets(response.Aggregations)
                    : new Dictionary&lt;string, Dictionary&lt;string, long&gt;&gt;();

            var elasticResponse = new ElasticResponse
            {
                TotalHits = hits,
                Documents = response.Documents.ToList(),
                Facets = facets,
            };

            return elasticResponse;
        }
        else
        {
            _logger.LogWarning($"Invalid response: {response.DebugInformation}");
            return new ElasticResponse();
        }
    }
    catch (Exception ex)
    {
        _logger.LogError(ex, "Error performing search");
        return new ElasticResponse();
    }
}<p>完全な申請書は<a href="https://github.com/elastic/elasticsearch-labs/tree/main/supporting-blog-content/esre-with-blazor">ここから</a>ご覧いただけます</p><h2>まとめ</h2><p>Blazor は、C# を使用して Web アプリケーションを構築できる効果的なフレームワークです。Elasticsearch は、検索アプリケーションを構築できる強力な検索エンジンです。両方を組み合わせることで、ESRE のパワーを活用して、<a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/semantic-search.html">セマンティック検索エクスペリエンス</a>を短期間で実現し、堅牢な検索アプリケーションを簡単に構築できます。</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/search-app-with-esre-blazor</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/search-app-with-esre-blazor</guid>
    <category><![CDATA[Vector Database]]></category>
    <category><![CDATA[.NET]]></category>
    <dc:creator><![CDATA[Gustavo Llermaly]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte7424ac5f0b223b4/6a17f668414c641971945323/7ba0d6bec908bfcae966b7f38626fabd682c6f3d-1200x628.png" length="0" type="image/png"/>
    <pubDate>Wed, 09 Oct 2024 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Elasticsearchを埋め込みストアとして使用したLangChain4j]]></title>
    <description><![CDATA[LangChain4j (LangChain for Java) には埋め込みストアとして Elasticsearch があります。これを使用して、プレーン Java で RAG アプリケーションを構築する方法を説明します。]]></description>
    <content:encoded><![CDATA[<p>
<a href="https://www.elastic.co/search-labs/blog/langchain4j-llm-integration-introduction">前回の投稿</a>では、LangChain4j とは何か、またどのように使用するかについて説明しました。</p><ul><li><p>LLMと<code>ChatLanguageModel</code>を実装して議論する <code>ChatMemory</code></p></li><li><p>チャット履歴をメモリに保持して、LLMとの以前の議論の文脈を思い出す</p></li></ul><p>このブログ投稿では、次の方法について説明します。</p><ul><li><p>テキスト例からベクトル埋め込みを作成する</p></li><li><p>ベクトル埋め込みをElasticsearch埋め込みストアに保存する </p></li><li><p>類似ベクトルを検索</p></li></ul><h2>埋め込みを作成する</h2><p>埋め込みを作成するには、使用する<code>EmbeddingModel</code>を定義する必要があります。たとえば、<a href="https://www.elastic.co/search-labs/blog/langchain4j-llm-integration-introduction">前回の投稿</a>で使用したのと同じミストラル モデルを使用できます。それは ollama で実行されていました:</p>EmbeddingModel model = OllamaEmbeddingModel.builder()
  .baseUrl(ollama.getEndpoint())
  .modelName(MODEL_NAME)
  .build();<p>モデルはテキストからベクトルを生成できます。ここで、モデルによって生成された次元の数を確認できます。</p>Logger.info("Embedding model has {} dimensions.", model.dimension());
// This gives: Embedding model has 4096 dimensions.<p>テキストからベクトルを生成するには、以下を使用します。</p>Response&lt;Embedding&gt; response = model.embed("A text here");<p>または、テキスト、価格、発売日などに基づいてフィルタリングできるようにメタデータも提供したい場合は、 <code>Metadata.from()</code>使用できます。たとえば、ここではゲーム名をメタデータ フィールドとして追加しています。</p>TextSegment game1 = TextSegment.from("""
    The game starts off with the main character Guybrush Threepwood stating "I want to be a pirate!"
    To do so, he must prove himself to three old pirate captains. During the perilous pirate trials, 
    he meets the beautiful governor Elaine Marley, with whom he falls in love, unaware that the ghost pirate 
    LeChuck also has his eyes on her. When Elaine is kidnapped, Guybrush procures crew and ship to track 
    LeChuck down, defeat him and rescue his love.
""", Metadata.from("gameName", "The Secret of Monkey Island"));
Response&lt;Embedding&gt; response1 = model.embed(game1);
TextSegment game2 = TextSegment.from("""
    Out Run is a pseudo-3D driving video game in which the player controls a Ferrari Testarossa 
    convertible from a third-person rear perspective. The camera is placed near the ground, simulating 
    a Ferrari driver's position and limiting the player's view into the distance. The road curves, 
    crests, and dips, which increases the challenge by obscuring upcoming obstacles such as traffic 
    that the player must avoid. The object of the game is to reach the finish line against a timer.
    The game world is divided into multiple stages that each end in a checkpoint, and reaching the end 
    of a stage provides more time. Near the end of each stage, the track forks to give the player a 
    choice of routes leading to five final destinations. The destinations represent different 
    difficulty levels and each conclude with their own ending scene, among them the Ferrari breaking 
    down or being presented a trophy.
""", Metadata.from("gameName", "Out Run"));
Response&lt;Embedding&gt; response2 = model.embed(game2);<p>このコードを実行する場合は、 <a href="https://github.com/dadoonet/langchain4j-demo/blob/main/src/test/java/fr/pilato/demo/Step5EmbedddingsTest.java">Step5EmbedddingsTest.java</a>クラスをチェックアウトしてください。</p><h2>ベクトルを保存するためにElasticsearchを追加する</h2><p>LangChain4j はメモリ内の埋め込みストアを提供します。これは簡単なテストを実行するのに便利です:</p>EmbeddingStore&lt;TextSegment&gt; embeddingStore = new InMemoryEmbeddingStore&lt;&gt;();
embeddingStore.add(response1.content(), game1);
embeddingStore.add(response2.content(), game2);<p>しかし、明らかに、このデータストアはすべてをメモリに保存し、サーバーに無限のメモリがないため、これよりもはるかに大きなデータセットでは機能しません。そのため、代わりに、定義上「弾性」があり、データに合わせてスケールアップおよびスケールアウトできる Elasticsearch に埋め込みを保存することができます。そのためには、Elasticsearch をプロジェクトに追加しましょう。</p>&lt;dependency&gt;
  &lt;groupId&gt;dev.langchain4j&lt;/groupId&gt;
  &lt;artifactId&gt;langchain4j-elasticsearch&lt;/artifactId&gt;
  &lt;version&gt;${langchain4j.version}&lt;/version&gt;
&lt;/dependency&gt;

&lt;dependency&gt;
  &lt;groupId&gt;org.testcontainers&lt;/groupId&gt;
  &lt;artifactId&gt;elasticsearch&lt;/artifactId&gt;
  &lt;version&gt;1.20.1&lt;/version&gt;
  &lt;scope&gt;test&lt;/scope&gt;
&lt;/dependency&gt;<p>お気づきのとおり、Elasticsearch TestContainers モジュールもプロジェクトに追加したので、テストから Elasticsearch インスタンスを起動できます。</p>// Create the elasticsearch container
ElasticsearchContainer container =
  new ElasticsearchContainer("docker.elastic.co/elasticsearch/elasticsearch:8.15.0")
    .withPassword("changeme");

// Start the container. This step might take some time...
container.start();

// As we don't want to make our TestContainers code more complex than
// needed, we will use login / password for authentication.
// But note that you can also use API keys which is preferred.
final CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials("elastic", "changeme"));

// Create a low level Rest client which connects to the elasticsearch container.
client = RestClient.builder(HttpHost.create("https://" + container.getHttpHostAddress()))
  .setHttpClientConfigCallback(httpClientBuilder -&gt; {
    httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider);
    httpClientBuilder.setSSLContext(container.createSslContextFromCa());
    return httpClientBuilder;
  })
  .build();

// Check the cluster is running
client.performRequest(new Request("GET", "/"));<p>Elasticsearch を埋め込みストアとして使用するには、LangChain4j のインメモリ データストアから Elasticsearch データストアに切り替えるだけです。</p>EmbeddingStore&lt;TextSegment&gt; embeddingStore =
  ElasticsearchEmbeddingStore.builder()
    .restClient(client)
    .build();
embeddingStore.add(response1.content(), game1);
embeddingStore.add(response2.content(), game2);<p>これにより、Elasticsearch の<code>default</code>インデックスにベクトルが保存されます。インデックス名をより意味のあるものに変更することもできます。</p>EmbeddingStore&lt;TextSegment&gt; embeddingStore =
  ElasticsearchEmbeddingStore.builder()
    .indexName("games")
    .restClient(client)
    .build();
embeddingStore.add(response1.content(), game1);
embeddingStore.add(response2.content(), game2);<p>このコードを実行する場合は、 <a href="https://github.com/dadoonet/langchain4j-demo/blob/main/src/test/java/fr/pilato/demo/Step6ElasticsearchEmbedddingsTest.java">Step6ElasticsearchEmbedddingsTest.java</a>クラスをチェックアウトしてください。</p><h2>類似ベクトルを検索</h2><p>類似のベクトルを検索するには、まず、以前使用したのと同じモデルを使用して、質問をベクトル表現に変換する必要があります。すでにそれを行ったので、もう一度これを行うのは難しくありません。この場合、メタデータは必要ないことに注意してください。</p>String question = "I want to pilot a car";
Embedding questionAsVector = model.embed(question).content();<p>質問をこのように表現して検索リクエストを作成し、埋め込みストアに最初の上位ベクトルを見つけるように依頼することができます。</p>EmbeddingSearchResult&lt;TextSegment&gt; result = embeddingStore.search(
  EmbeddingSearchRequest.builder()
    .queryEmbedding(questionAsVector)
    .build());<p>これで、結果を反復処理して、メタデータから取得したゲーム名やスコアなどの情報を出力できます。</p>result.matches().forEach(m -&gt; Logger.info("{} - score [{}]",
  m.embedded().metadata().getString("gameName"), m.score()));<p>予想どおり、最初のヒットは「Out Run」になります。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt7ca0dcfdb1a9c94f/6a170291cf4f256938b2d017/140b6a962e5edbb4870419250e30bfb815b0d73e-640x480.gif" alt="アウトラン" />Out Run - score [0.86672974]
The Secret of Monkey Island - score [0.85569763]<p>このコードを実行する場合は、 <a href="https://github.com/dadoonet/langchain4j-demo/blob/9ec4b1d4c7c69821f143ddf272bbfed273c67b14/src/test/java/fr/pilato/demo/Step7SearchForVectorsTest.java#L110-L129">Step7SearchForVectorsTest.java</a>クラスをチェックアウトしてください。 </p><h2>舞台裏</h2><p>Elasticsearch Embedding ストアのデフォルト構成では、バックグラウンドで<a href="https://www.elastic.co/guide/en/elasticsearch/reference/8.15/query-dsl-knn-query.html">近似 kNN クエリが</a>使用されます。</p>POST games/_search
{
  "query" : {
    "knn": {
      "field": "vector",
      "query_vector": [-0.019137882, /* ... */, -0.0148779955]
    }
  }
}<p>ただし、埋め込みストアにデフォルトの構成 ( <code>ElasticsearchConfigurationKnn</code> ) とは別の構成 ( <code>ElasticsearchConfigurationScript</code> ) を提供することでこれを変更できます。</p>EmbeddingStore&lt;TextSegment&gt; embeddingStore =
  ElasticsearchEmbeddingStore.builder()
    .configuration(ElasticsearchConfigurationScript.builder().build())
    .indexName("games")
    .restClient(client)
    .build();<p><code>ElasticsearchConfigurationScript</code>実装は、<a href="https://www.elastic.co/guide/en/elasticsearch/reference/8.15/query-dsl-script-score-query.html"><code>script_score</code></a><a href="https://www.elastic.co/guide/en/elasticsearch/reference/8.15/query-dsl-script-score-query.html"> </a><a href="https://www.elastic.co/guide/en/elasticsearch/reference/8.15/query-dsl-script-score-query.html#vector-functions-cosine"><code>cosineSimilarity</code></a><a href="https://www.elastic.co/guide/en/elasticsearch/reference/8.15/query-dsl-script-score-query.html#vector-functions-cosine">関数</a> を使用して、バックグラウンドで クエリ を実行します。</p><p>基本的に、電話をかけるときは:</p>EmbeddingSearchResult&lt;TextSegment&gt; result = embeddingStore.search(
  EmbeddingSearchRequest.builder()
    .queryEmbedding(questionAsVector)
    .build());<p>これにより、次のコードが呼び出されます。</p>POST games/_search
{
  "query": {
    "script_score": {
      "script": {
        "source": "(cosineSimilarity(params.query_vector, 'vector') + 1.0) / 2",
        "params": {
          "queryVector": [-0.019137882, /* ... */, -0.0148779955]
        }
      }
    }
  }
}<p>この場合、結果は「順序」の点では変化せず、スコアのみが調整されます。これは、 <code>cosineSimilarity</code>呼び出しでは近似値を使用せず、一致するベクトルごとにコサインを計算するためです。</p>Out Run - score [0.871952]
The Secret of Monkey Island - score [0.86380446]<p>このコードを実行する場合は、 <a href="https://github.com/dadoonet/langchain4j-demo/blob/9ec4b1d4c7c69821f143ddf272bbfed273c67b14/src/test/java/fr/pilato/demo/Step7SearchForVectorsTest.java#L132-L155">Step7SearchForVectorsTest.java</a>クラスをチェックアウトしてください。</p><h2>まとめ</h2><p>テキストから埋め込みを簡単に生成する方法と、2 つの異なるアプローチを使用して Elasticsearch で最も近い近傍を保存および検索する方法について説明しました。</p><ul><li><p>デフォルトの<code>ElasticsearchConfigurationKnn</code>オプションを使用して、近似高速<code>knn</code>クエリを使用する</p></li><li><p>正確だが遅い<code>script_score</code>クエリを<code>ElasticsearchConfigurationScript</code>オプションとともに使用する</p></li></ul><p>次のステップでは、ここで学んだことを基に、完全な RAG アプリケーションを構築します。</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/langchain4j-elasticsearch-embedding-store</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/langchain4j-elasticsearch-embedding-store</guid>
    <category><![CDATA[Java]]></category>
    <category><![CDATA[AI]]></category>
    <category><![CDATA[Vector Database]]></category>
    <dc:creator><![CDATA[David Pilato]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltfc873b86c76d1798/6a170293acf088f666be99b3/abd8a4a809064101c037af66b87f28e5ecde03b0-1474x645.jpg" length="0" type="image/jpeg"/>
    <pubDate>Tue, 08 Oct 2024 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[高度なRAGテクニックパート2：クエリとテスト]]></title>
    <description><![CDATA[RAG のパフォーマンスを向上させる可能性のあるテクニックについて議論し、実装します。パート 2/2。高度な RAG パイプラインのクエリとテストに焦点を当てます。]]></description>
    <content:encoded><![CDATA[<p><em>すべてのコードは</em><a href="https://github.com/elastic/elasticsearch-labs/tree/advanced-rag-techniques/supporting-blog-content/advanced-rag-techniques"><em> 、Searchlabs リポジトリの advanced-rag-techniques</em></a><em> ブランチに あります 。</em></p><p>高度な RAG テクニックに関する記事のパート 2 へようこそ。<a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-1">このシリーズのパート 1</a>では、高度な RAG パイプラインのデータ処理コンポーネントを設定、説明、実装しました。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt9a4691874a19d8da/6a170b3f47d49c99f22d8a24/72b51ba2ae5e5977b56e5b915674753d6cfd0e56-1440x840.jpg" alt="高度なRAGパイプライン" /><p>この部分では、実装のクエリとテストを進めていきます。早速始めましょう！</p><h3>目次</h3><ul><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#searching-and-retrieving,-generating-answers">検索と取得、回答の生成</a></p><ul><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#enriching-queries-with-synonyms">同義語によるクエリの強化</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#hyde-hypothetical-document-embedding">HyDE（仮想文書埋め込み）</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#hybrid-search">ハイブリッド検索</a></p></li></ul></li><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#experiments">実験</a></p><ul><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#summary-of-results">結果の要約</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#test-1-who-audits-elastic">テスト 1: Elastic を監査するのは誰ですか?</a></p><ul><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#advancedrag">アドバンスドRAG</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#simplerag">シンプルラグ</a></p></li></ul></li><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#test-2--total-revenue-2023">テスト2：2023年の総収入</a></p><ul><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#advancedrag-1">アドバンスドRAG</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#simplerag-1">シンプルラグ</a></p></li></ul></li><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#test-3-what-product-does-growth-primarily-depend-on-how-much">テスト 3: 成長は主にどの製品に依存しますか?いくら？</a></p><ul><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#advancedrag-2">アドバンスドRAG</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#simplerag-2">シンプルラグ</a></p></li></ul></li><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#test-4-describe-employee-benefit-plan">テスト4: 従業員福利厚生制度の説明</a></p><ul><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#advancedrag-3">アドバンスドRAG</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#simplerag-3">シンプルラグ</a></p></li></ul></li><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#test-5-which-companies-did-elastic-acquire">テスト 5: Elastic が買収した企業はどれですか?</a></p><ul><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#advancedrag-4">アドバンスドRAG</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#simplerag-4">シンプルラグ</a></p></li></ul></li></ul></li><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#conclusion">まとめ</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#appendix">付記</a></p><ul><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#prompts">プロンプト</a></p><ul><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#rag-question-answering-prompt">RAG質問回答プロンプト</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#elastic-query-generator-prompt">弾性クエリジェネレータプロンプト</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#potential-questions-generator-prompt">潜在的な質問ジェネレータプロンプト</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#hyde-generator-prompt">HyDEジェネレータプロンプト</a></p></li></ul></li><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#sample-hybrid-search-query">ハイブリッド検索クエリのサンプル</a></p></li></ul></li></ul><h2>検索と取得、回答の生成</h2><p>最初のクエリ、理想的には主に年次報告書に記載されている情報を尋ねてみましょう。いかがでしょうか:</p>Who audits Elastic?"
<p>ここで、クエリを強化するためにいくつかのテクニックを適用してみましょう。</p><h3>同義語によるクエリの強化</h3><p>まず、クエリの文言の多様性を高めて、Elasticsearch クエリに簡単に処理できる形式に変えてみましょう。GPT-4o の助けを借りて、クエリを OR 句のリストに変換します。次のプロンプトを書いてみましょう:</p>
ELASTIC_SEARCH_QUERY_GENERATOR_PROMPT = '''
You are an AI assistant specialized in generating Elasticsearch query strings. Your task is to create the most effective query string for the given user question. This query string will be used to search for relevant documents in an Elasticsearch index.

Guidelines:
1. Analyze the user's question carefully.
2. Generate ONLY a query string suitable for Elasticsearch's match query.
3. Focus on key terms and concepts from the question.
4. Include synonyms or related terms that might be in relevant documents.
5. Use simple Elasticsearch query string syntax if helpful (e.g., OR, AND).
6. Do not use advanced Elasticsearch features or syntax.
7. Do not include any explanations, comments, or additional text.
8. Provide only the query string, nothing else.

For the question "What is Clickthrough Data?", we would expect a response like:
clickthrough data OR click-through data OR click through rate OR CTR OR user clicks OR ad clicks OR search engine results OR web analytics

AND operator is not allowed. Use only OR.

User Question:
[The user's question will be inserted here]

Generate the Elasticsearch query string:
'''
<p>GPT-4o をクエリに適用すると、基本クエリの同義語と関連語彙が生成されます。</p>'audits elastic OR 
elasticsearch audits OR 
elastic auditor OR 
elasticsearch auditor OR 
elastic audit firm OR 
elastic audit company OR 
elastic audit organization OR 
elastic audit service'
<p><code>ESQueryMaker</code>クラスでは、クエリを分割する関数を定義しました。</p>def parse_or_query(self, query_text: str) -&gt; List[str]:
    # Split the query by 'OR' and strip whitespace from each term
    # This converts a string like "term1 OR term2 OR term3" into a list ["term1", "term2", "term3"]
    return [term.strip() for term in query_text.split(' OR ')]
<p>その役割は、この OR 句の文字列を取得して用語のリストに分割し、主要なドキュメント フィールドで複数の一致を実行できるようにすることです。</p>["original_text", 'keyphrases', 'potential_questions', 'entities']
<p>最終的にこのクエリに至ります:</p> 'query': {
    'bool': {
        'must': [
            {
                'multi_match': {
                'query': 'audits Elastic Elastic auditing Elastic audit process Elastic compliance Elastic security audit Elasticsearch auditing Elasticsearch compliance Elasticsearch security audit',
                'fields': [
                    'original_text',
                'keyphrases',
                'potential_questions',
                'entities'
                ],
                'type': 'best_fields',
                'operator': 'or'
                }
            }
      ]
<p>これにより、元のクエリよりも多くのベースがカバーされ、同義語を忘れたために検索結果を見逃すリスクが軽減されることが期待されます。しかし、私たちにはもっとできることがある。</p><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#table-of-contents">トップに戻る</a></p><h3>HyDE（仮想文書埋め込み）</h3><p>今回は<a href="https://arxiv.org/abs/2212.10496">HyDE を</a>実装するために、再び GPT-4o を活用しましょう。</p><p>HyDE の基本的な前提は、仮想ドキュメント (元のクエリに対する回答が含まれる可能性のある種類のドキュメント) を生成することです。文書の事実性や正確性は問題ではありません。それを念頭に置いて、次のプロンプトを書いてみましょう。</p>HYDE_DOCUMENT_GENERATOR_PROMPT = '''
You are an AI assistant specialized in generating hypothetical documents based on user queries. Your task is to create a detailed, factual document that would likely contain the answer to the user's question. This hypothetical document will be used to enhance the retrieval process in a Retrieval-Augmented Generation (RAG) system.

Guidelines:
1. Carefully analyze the user's query to understand the topic and the type of information being sought.
2. Generate a hypothetical document that:
   a. Is directly relevant to the query
   b. Contains factual information that would answer the query
   c. Includes additional context and related information
   d. Uses a formal, informative tone similar to an encyclopedia or textbook entry
3. Structure the document with clear paragraphs, covering different aspects of the topic.
4. Include specific details, examples, or data points that would be relevant to the query.
5. Aim for a document length of 200-300 words.
6. Do not use citations or references, as this is a hypothetical document.
7. Avoid using phrases like "In this document" or "This text discusses" - write as if it's a real, standalone document.
8. Do not mention or refer to the original query in the generated document.
9. Ensure the content is factual and objective, avoiding opinions or speculative information.
10. Output only the generated document, without any additional explanations or meta-text.

User Question:
[The user's question will be inserted here]

Generate a hypothetical document that would likely contain the answer to this query:
'''
<p>ベクトル検索は通常、コサインベクトルの類似度に基づいて行われるため、クエリをドキュメントに一致させるのではなく、ドキュメントをドキュメントに一致させることでより良い結果を達成できるというのが HyDE の前提です。</p><p>私たちが重視するのは、構造、フロー、用語です。あまり事実ではない。GPT-4o は次のような HyDE ドキュメントを出力します。</p>'Elastic N.V., the parent company of Elastic, the organization known for developing Elasticsearch, is subject to audits to ensure financial accuracy, 
regulatory compliance, and the integrity of its financial statements. The auditing of Elastic N.V. is typically conducted by an external, 
independent auditing firm. This is common practice for publicly traded companies to provide stakeholders with assurance regarding the company\'s 
financial position and operations.\n\nThe primary external auditor for Elastic is the audit firm Ernst &amp; Young LLP (EY). Ernst &amp; Young is one of the 
four largest professional services networks in the world, commonly referred to as the "Big Four" audit firms. These firms handle a substantial number 
of audits for major corporations around the globe, ensuring adherence to generally accepted accounting principles (GAAP) and international financial 
reporting standards (IFRS).\n\nThe audit process conducted by EY involves several steps. Initially, the auditors perform a risk assessment to identify 
areas where misstatements due to error or fraud could occur. They then design audit procedures to test the accuracy and completeness of financial statements,
 which include examining financial transactions, assessing internal controls, and reviewing compliance with relevant laws and regulations. Upon completion of 
 the audit, Ernst &amp; Young issues an audit report, which includes the auditor’s opinion on whether the financial statements are free from material misstatement 
 and are presented fairly in accordance with the applicable financial reporting framework.\n\nIn addition to external audits by firms like Ernst &amp; Young, 
 Elastic may also be subject to internal audits. Internal audits are performed by the company’s own internal auditors to evaluate the effectiveness of internal 
 controls, risk management, and governance processes.\n\nOverall, the auditing process plays a crucial role in maintaining the transparency and reliability of 
 Elastic\'s financial information, providing confidence to investors, regulators, and other stakeholders.'
<p>これはかなり信憑性があり、インデックスを作成したい種類のドキュメントに最適な候補のように見えます。これを埋め込み、ハイブリッド検索に使用します。</p><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#table-of-contents">トップに戻る</a></p><h3>ハイブリッド検索</h3><p>これが私たちの検索ロジックの中核です。語彙検索コンポーネントは、生成された OR 句の文字列になります。高密度ベクトル コンポーネントには、HyDE ドキュメント (検索ベクトルとも呼ばれます) が埋め込まれます。KNN を使用して、検索ベクトルに最も近い候補ドキュメントをいくつか効率的に識別します。デフォルトでは、語彙検索コンポーネントを<em>TF-IDF と BM25 によるスコアリング</em>と呼びます。最後に、語彙スコアと密なベクトルスコアは、 <a href="https://arxiv.org/abs/2407.01219">Wang ら</a>が推奨する 30/70 比率を使用して結合されます。</p>def hybrid_vector_search(self, index_name: str, query_text: str, query_vector: List[float], 
                         text_fields: List[str], vector_field: str, 
                         num_candidates: int = 100, num_results: int = 10) -&gt; Dict:
    """
    Perform a hybrid search combining text-based and vector-based similarity.

    Args:
        index_name (str): The name of the Elasticsearch index to search.
        query_text (str): The text query string, which may contain 'OR' separated terms.
        query_vector (List[float]): The query vector for semantic similarity search.
        text_fields (List[str]): List of text fields to search in the index.
        vector_field (str): The name of the field containing document vectors.
        num_candidates (int): Number of candidates to consider in the initial KNN search.
        num_results (int): Number of final results to return.

    Returns:
        Dict: A tuple containing the Elasticsearch response and the search body used.
    """
    try:
        # Parse the query_text into a list of individual search terms
        # This splits terms separated by 'OR' and removes any leading/trailing whitespace
        query_terms = self.parse_or_query(query_text)

        # Construct the search body for Elasticsearch
        search_body = {
            # KNN search component for vector similarity
            "knn": {
                "field": vector_field,  # The field containing document vectors
                "query_vector": query_vector,  # The query vector to compare against
                "k": num_candidates,  # Number of nearest neighbors to retrieve
                "num_candidates": num_candidates  # Number of candidates to consider in the KNN search
            },
            "query": {
                "bool": {
                    # The 'must' clause ensures that matching documents must satisfy this condition
                    # Documents that don't match this clause are excluded from the results
                    "must": [
                        {
                            # Multi-match query to search across multiple text fields
                            "multi_match": {
                                "query": " ".join(query_terms),  # Join all query terms into a single space-separated string
                                "fields": text_fields,  # List of fields to search in
                                "type": "best_fields",  # Use the best matching field for scoring
                                "operator": "or"  # Match any of the terms (equivalent to the original OR query)
                            }
                        }
                    ],
                    # The 'should' clause boosts relevance but doesn't exclude documents
                    # It's used here to combine vector similarity with text relevance
                    "should": [
                        {
                            # Custom scoring using a script to combine vector and text scores
                            "script_score": {
                                "query": {"match_all": {}},  # Apply this scoring to all documents that matched the 'must' clause
                                "script": {
                                    # Script to combine vector similarity and text relevance
                                    "source": """
                                    # Calculate vector similarity (cosine similarity + 1)
                                    # Adding 1 ensures the score is always positive
                                    double vector_score = cosineSimilarity(params.query_vector, params.vector_field) + 1.0;
                                    # Get the text-based relevance score from the multi_match query
                                    double text_score = _score;
                                    # Combine scores: 70% vector similarity, 30% text relevance
                                    # This weighting can be adjusted based on the importance of semantic vs keyword matching
                                    return 0.7 * vector_score + 0.3 * text_score;
                                    """,
                                    # Parameters passed to the script
                                    "params": {
                                        "query_vector": query_vector,  # Query vector for similarity calculation
                                        "vector_field": vector_field  # Field containing document vectors
                                    }
                                }
                            }
                        }
                    ]
                }
            }
        }

        # Execute the search request against the Elasticsearch index
        response = self.conn.search(index=index_name, body=search_body, size=num_results)
        # Log the successful execution of the search for monitoring and debugging
        logger.info(f"Hybrid search executed on index: {index_name} with text query: {query_text}")
        # Return both the response and the search body (useful for debugging and result analysis)
        return response, search_body
    except Exception as e:
        # Log any errors that occur during the search process
        logger.error(f"Error executing hybrid search on index: {index_name}. Error: {e}")
        # Re-raise the exception for further handling in the calling code
        raise e
<p>最後に、RAG 関数を組み立てることができます。クエリから回答までの RAG の流れは次のようになります。</p><ol><li><p>クエリを OR 句に変換します。</p></li><li><p>HyDE ドキュメントを生成して埋め込みます。</p></li><li><p>両方をハイブリッド検索への入力として渡します。</p></li><li><p>上位 n 件の結果を取得し、最も関連性の高いスコアが LLM のコンテキスト メモリ内で「最新」になるように結果を逆にします (逆パッキング)。逆パッキングの例: クエリ:「Elasticsearch クエリ最適化手法」取得されたドキュメント (関連性の高い順): LLM コンテキストの順序を逆にする: 順序を逆にすることで、最も関連性の高い情報 (1) がコンテキストの最後に表示され、回答生成中に LLM からより多くの注目を受ける可能性が高くなります。</p><ol><li><p>「ブールクエリを使用して、複数の検索条件を効率的に組み合わせます。」</p></li><li><p>「クエリの応答時間を改善するためのキャッシュ戦略を実装します。」</p></li><li><p>「インデックス マッピングを最適化して、検索パフォーマンスを高速化します。」</p></li><li><p>「インデックス マッピングを最適化して、検索パフォーマンスを高速化します。」</p></li><li><p>「クエリの応答時間を改善するためのキャッシュ戦略を実装します。」</p></li><li><p>「ブールクエリを使用して、複数の検索条件を効率的に組み合わせます。」</p></li></ol></li><li><p>生成のためにコンテキストを LLM に渡します。</p></li></ol>def get_context(index_name, 
                match_query, 
                text_query, 
                fields, 
                num_candidates=100, 
                num_results=20, 
                text_fields=["original_text", 'keyphrases', 'potential_questions', 'entities'], 
                embedding_field="primary_embedding"):

    embedding=embedder.get_embeddings_from_text(text_query)

    results, search_body = es_query_maker.hybrid_vector_search(
        index_name=index_name,
        query_text=match_query,
        query_vector=embedding[0][0],
        text_fields=text_fields,
        vector_field=embedding_field,
        num_candidates=num_candidates,
        num_results=num_results
    )

    # Concatenates the text in each 'field' key of the search result objects into a single block of text.
    context_docs=['\n\n'.join([field+":\n\n"+j['_source'][field] for field in fields]) for j in results['hits']['hits']]

    # Reverse Packing to ensure that the highest ranking document is seen first by the LLM.
    context_docs.reverse()
    return context_docs, search_body

def retrieval_augmented_generation(query_text):
    match_query= gpt4o.generate_query(query_text)
    fields=['original_text']

    hyde_document=gpt4o.generate_HyDE(query_text)

    context, search_body=get_context(index_name, match_query, hyde_document, fields)

    answer= gpt4o.basic_qa(query=query_text, context=context)
    return answer, match_query, hyde_document, context, search_body

<p>クエリを実行して回答を取得してみましょう。</p>According to the context, Elastic N.V. is audited by an independent registered public accounting firm, PricewaterhouseCoopers (PwC). 
This information is found in the section titled "report of independent registered public accounting firm," which states:

"We have audited the accompanying consolidated balance sheets of Elastic N.V. [...] / s / pricewaterhouseco."
<p>ニース。そうです。</p><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#table-of-contents">トップに戻る</a></p><h2>実験</h2><p>今答えなければならない重要な質問があります。これらの実装に多大な労力と追加の複雑さを投資することで、何が得られましたか?</p><p>少し比較してみましょう。私たちが実装した RAG パイプラインと、私たちが行った機能強化のないベースライン ハイブリッド検索を比較したものです。小規模な一連のテストを実行して、大きな違いが見られるか確認します。ここで実装した RAG を AdvancedRAG と呼び、基本パイプラインを SimpleRAG と呼びます。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltf605c8246989df32/6a1711178b73cbc61d18a11d/8da40067835ab8b4dc12fe52a51a6c26858ad32f-1440x1095.jpg" alt="シンプルなRAGパイプライン" /><h4>結果の要約</h4><p>この表は、両方の RAG パイプラインの 5 つのテストの結果をまとめたものです。回答の詳細と品質に基づいて各方法の相対的な優位性を判断しましたが、これは完全に主観的な判断です。実際の回答はこの表の下に再現されていますので、ご参照ください。それでは、彼らの成果を見てみましょう!</p><p>SimpleRAG は質問 1 と 5 に答えることができませんでした。AdvancedRAG は質問 2、3、4 についても非常に詳しく説明しました。詳細度が増したことにより、AdvancedRAG の回答の質が優れていると判断しました。</p><p>テスト</p><p>質問</p><p>高度なRAGパフォーマンス</p><p>SimpleRAG パフォーマンス</p><p>AdvancedRAG レイテンシー</p><p>SimpleRAG レイテンシ</p><p>勝者</p><p>1</p><p>Elastic を監査するのは誰ですか?</p><p>監査人として PwC を正しく特定しました。</p><p>監査人を識別できませんでした。</p><p>11.6秒</p><p>4.4秒</p><p>アドバンスドRAG</p><p>2</p><p>2023年の総収益はいくらでしたか?</p><p>正しい収益数値を提供しました。前年度の収益に関する追加のコンテキストを含めました。</p><p>正しい収益数値を提供しました。</p><p>13.3秒</p><p>2.8秒</p><p>アドバンスドRAG</p><p>3</p><p>成長は主にどの製品に依存しますか?いくら？</p><p>Elastic Cloud が主要な推進力であることを正しく認識しました。全体的な収益コンテキストとより詳しい詳細が含まれています。</p><p>Elastic Cloud が主要な推進力であることを正しく認識しました。</p><p>14.1秒</p><p>12.8秒</p><p>アドバンスドRAG</p><p>4</p><p>従業員福利厚生プランの説明</p><p>退職金制度、健康プログラム、その他の福利厚生について包括的に説明しました。異なる年ごとの具体的な寄付金額が含まれています。</p><p>報酬、退職金制度、職場環境、Elastic Cares プログラムなどの福利厚生の概要をわかりやすく説明しました。</p><p>26.6秒</p><p>11.6秒</p><p>アドバンスドRAG</p><p>5</p><p>Elastic が買収した企業はどれですか?</p><p>レポートに記載されている最近の買収 (CmdWatch、Build Security、Optimyze) を正しくリストしました。いくつかの取得日と購入価格を提供しました。</p><p>提供されたコンテキストから関連情報を取得できませんでした。</p><p>11.9秒</p><p>2.7秒</p><p>アドバンスドRAG</p><h4>テスト 1: Elastic を監査するのは誰ですか?</h4><p>アドバンスドRAG</p>According to the context, Elastic N.V. is audited by an independent 
registered public accounting firm, PricewaterhouseCoopers (PwC). 
This information is found in the section titled "report of 
independent registered public accounting firm," which states:
"We have audited the accompanying consolidated balance sheets 
of Elastic N.V. [...] / s / pricewaterhouseco."
<p>シンプルラグ</p>According to the provided context, no specific auditing firm 
or company is mentioned as the auditor for Elastic. 
The context provided talks about various features, strengths, 
deployment options, and other details related to Elastic and 
its operations but does not specify which auditing firm is 
responsible for auditing Elastic's financial statements or 
internal controls.
<p><strong>要約</strong>: SimpleRAGはPWCを監査人として特定しなかった</p><p>そうですね、それは実はかなり驚きました。これは SimpleRAG 側の検索失敗のようです。監査に関連する文書は取得されませんでした。次のテストでは難易度を少し下げてみましょう。</p><h4>テスト2：2023年の総収入</h4><p>アドバンスドRAG</p>### Total Revenue for 2023

According to the provided context, the total revenue for the 
year ended April 30, 2023, was **$1,068,989,000**. This 
information can be found several times in the provided text. 
Specifically, one part states:

"The following table sets forth our results of operations 
for the periods presented in dollars and as a percentage of 
our total revenue. ... year ended April 30, 2023, ... 
total revenue 1,068,989".

Additionally, it is mentioned here as well:

"For the years ended April 30, 2023, 2022, and 2021, 
representing year-over-year growth of 24% for the year 
ended April 30, 2023... total revenue $1,068,989".

Therefore, the total revenue for 2023 is $1.069 billion.
<p>シンプルラグ</p>According to the context provided, the total revenue for the fiscal year ended April 30, 2023, is:

$1,068,989,000 (or approximately $1.1 billion).
<p><strong>要約</strong>: 両RAGとも正解: 2023年の総収益は1,068,989,000ドル</p><p>二人ともここにいました。AdvancedRAG がより広範囲の文書を入手したように思われますか?確かに、答えはより詳細で、前年からの情報が組み込まれています。私たちが行った機能強化を考えると、それは予想されることですが、判断するには時期尚早です。</p><p>難易度を上げてみましょう。</p><h4>テスト 3: 成長は主にどの製品に依存しますか?いくら？</h4><p>アドバンスドRAG</p>### Product Growth Primarily Depends On:
According to the context, "our revenue is derived primarily from 
subscriptions of search, observability and security built into 
the elastic stack." The document mentions various strategies and 
factors contributing to the company’s growth which include investment 
in "elastic cloud," "elastic stack," and the conversion of free 
users to paid subscribers.

### Quantitative Data:
- **Revenue**: The document states, "Our total revenue was $1.1 billion, 
$862.4 million, and $608.5 million for the years ended April 30, 2023, 
2022, and 2021, respectively."
- **Subscription Revenue**: "Subscriptions accounted for 92%, 93%, 
and 93% of our total revenue for the years ended April 30, 2023, 
2022, and 2021, respectively."
- **Elastic Cloud Contribution**: "For the years ended April 30, 2023, 
2022, and 2021, Elastic Cloud contributed 40%, 35%, and 27% of our 
total revenue, respectively."
- **Customer Growth**: The number of customers increased from 
"over 15,000 as of April 30, 2021," to "approximately 20,200 as of 
April 30, 2023."

### Summary:
Product growth for the company primarily depends on the "Elastic Stack" 
(which includes search, observability, and security solutions) and 
particularly the "Elastic Cloud." The Elastic Cloud's growing contribution 
to revenue highlights its significance, which grew from 27% in 2021 to 40% in 2023.
<p>シンプルラグ</p>According to the provided context, growth primarily depends on **Elastic Cloud**, 
which is described as the company's "family of cloud-based offerings." 
The context also emphasizes the significance of Elastic Cloud in achieving 
the company's long-term growth potential and its role in increasing subscription revenue.

From the context:

- "Increasing adoption of Elastic Cloud... is an important growth opportunity 
for our business. Organizations are increasingly looking for hosted 
deployment alternatives with reduced administrative burdens."
- "We believe that offering Elastic Cloud is important for achieving 
our long-term growth potential, and we expect Elastic Cloud’s contribution 
to our subscription revenue to continue to increase."

In terms of the contribution to total revenue, here are the specific figures provided:

- Elastic Cloud contributed 40% of the total revenue for the year ended April 30, 2023.
- Elastic Cloud contributed 35% of the total revenue for the year ended April 30, 2022.
- Elastic Cloud contributed 27% of the total revenue for the year ended April 30, 2021.

This progression shows a clear upward trend in the importance and 
contribution of Elastic Cloud to the company's overall revenue.
<p><strong>概要</strong>: 両方の RAG は、Elastic Cloud を主要な成長原動力として正しく認識しました。ただし、AdvancedRAG では、サブスクリプション収益と顧客の増加を考慮したより詳細な情報が含まれており、Elastic の他の製品についても明示的に言及されています。</p><h4>テスト4: 従業員福利厚生制度の説明</h4><p>アドバンスドRAG</p>### Employee Benefit Plan Overview

The provided context outlines various elements of the employee benefit plan at 
Elastic, often referred to as "Elasticians." Here are the key components 
described in the texts:

1. **401(k) Plan**:
   - **Coverage**: The 401(k) plan is available to substantially all U.S. 
   employees who meet minimum age and service requirements.
   - **Contributions**: Elastic makes contributions to the 401(k) plan up to 
   6% of the participating employee’s W-2 earnings and wages.
   - **Expenses**: For the fiscal years ended April 30, Elastic recorded 
   expenses of $17.9 million (2023), $15.2 million (2022), and $11.4 million (2021) 
   related to the 401(k) plan.
   - **Defined-Contribution Plans in Other Countries**: Elastic has 
   defined-contribution plans in various other countries and recorded respective 
   expenses of $9.4 million (2023), $7.2 million (2022), and $5.1 million (2021).

2. **Stock-Based Compensation**:
   - **Types of Awards**: Stock options, restricted stock units (RSUs), 
   and shares under the Employee Stock Purchase Plan (ESPP).
   - **Fair Value Measurement**: Fair value of these stock awards is 
   measured using models like Black-Scholes.
   - **Employee Stock Purchase Plan (2022 ESPP)**: 
     - Started in 2022, it allows employees to acquire ordinary 
     shares at a discount (85% of the market value at the beginning 
     or end of the offering period).
     - Offering periods are approximately six months long.

3. **Total Rewards Compensation**:
   - **Components**: Includes cash compensation as well as equity awards, 
   reflecting a comprehensive interest in the well-being of the employees.
   - **Programs**: Market competitive medical and dental programs, 
   mental health, and holistic well-being programs.
   - **Paid Time Off**: Features 16 weeks of paid leave for new 
   parents and life-planning benefits.

4. **Retirement and Income Protection Plans**:
   - **401(k) Match**: Elastic provides a dollar-for-dollar 
   match up to 6% of eligible earnings up to a plan-limit 
   maximum for U.S.-based employees.
   - **Similar Plans Outside the U.S.**: Competitive retirement 
   plans available for employees based outside the United States.

5. **Flexible Work Environment**: 
   - **Distributed Workforce**: Elastic operates with a global 
   distributed workforce model, allowing employees to work remotely 
   and providing a broad candidate pool.
   
6. **Learning and Development**:
   - **Programs**: Access to on-demand video-based learning, 
   leadership development programs, and other specific training 
   to develop managers and leaders.

7. **Employee Engagement and Surveys**:
   - **Mechanisms**: Annual employee engagement survey and 
   mid-year pulse survey.
   - **Objectives**: To monitor morale, develop action plans, 
   and maintain high engagement scores.

8. **Onboarding Program**:
   - **X-School**: An extensive new-hire orientation program to 
   help new employees meet peers and learn about the company's 
   products and solutions.

In summary, Elastic's employee benefit plan encompasses a broad 
spectrum of financial, health, well-being, and professional development 
benefits designed to support and enhance the lives of its employees. 
The plan is both competitive and comprehensive, aiming to attract,
retain, and engage its global workforce.
<p>シンプルラグ</p>The employee benefit plan at the Company includes several key 
components aimed at enhancing the overall well-being and 
professional development of employees, referred to as "Elasticians." 

1. **Compensation and Benefits:**
   - The Company provides market-competitive compensation 
   packages which typically include cash compensation and equity awards.
   - Comprehensive medical and dental programs are offered, 
   emphasizing mental health and holistic well-being.
   - Paid time off programs feature:
     - 16 weeks of paid leave for new parents.
     - Life-planning benefits.
     - Travel reimbursements for certain healthcare services.

2. **Retirement and Income Protection Plans:**
   - In the United States, employees are eligible for a 401k 
   plan with a dollar-for-dollar match by the Company up to 6% 
   of eligible earnings, subject to a plan limit.
   - Similar competitive retirement and income protection plans 
   are available for employees outside the United States.

3. **Work Environment:**
   - The Company offers a flexible work environment, allowing most 
   employees to work from anywhere as often as they would like.
   - Physical offices around the world are available for employees 
   who prefer to work on-site or need a space for face-to-face interactions.

4. **Community Involvement:**
   - The Elastic Cares Program supports charitable organizations 
   that matter to employees on both local and global levels.
   - The program includes:
     - Donation matching.
     - Providing the Company's technology for free to certain 
     nonprofit organizations.
     - Volunteer time off initiative.
<p><strong>概要</strong>: AdvancedRAG では、米国に拠点を置く従業員向けの 401K プランや、米国外の定義拠出金プランについてさらに詳しく取り上げています。また、健康と幸福の計画についても言及していますが、SimpleRAG が言及している Elastic Cares プログラムについては触れられていません。</p><h4>テスト 5: Elastic が買収した企業はどれですか?</h4><p>アドバンスドRAG</p>Elastic acquired several companies over the years to enhance its 
technology and market presence. According to the provided context, 
the specific acquisitions include:

1. **CmdWatch Security Inc.**: Acquired on September 17, 2021. 
The total purchase consideration was $77.8 million.
2. **Build Security Ltd.**: Acquired on September 2, 2021. The 
combined purchase price for Build Security Ltd. and another acquisition was $57.2 million.
3. **Optimyze Cloud Inc.**: Acquired on November 1, 2021. Part 
of the combined purchase price mentioned above with Build Security Ltd.
4. **Endgame, Inc.**: Although the context mentions an amended 
and restated stock incentive plan, it indicates that Endgame, Inc. was 
associated with Elastic, suggesting it could have been acquired at some point.

These acquisitions are part of Elastic's strategy to 
enhance the technology underlying its security and 
observability offerings.
<p>シンプルラグ</p>The provided context does not specify any companies that Elastic has acquired. 
Therefore, based on the context, there is no information available about the companies acquired by Elastic.
<p><strong>概要</strong>: SimpleRAG は買収に関する関連情報を取得せず、回答に失敗しました。AdvancedRAG は、レポートに記載されている主要な買収である CmdWatch、Build Security、Optimyze を正しくリストしています。</p><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#table-of-contents">トップに戻る</a></p><h2>まとめ</h2><p>私たちのテストによると、私たちの高度な技術により、提示される情報の範囲と深さが拡大し、RAG 回答の品質が向上する可能性があるようです。</p><p>さらに、 <code>Which companies did Elastic acquire?</code>や<code>Who audits Elastic</code>などのあいまいな表現の質問に対して、AdvancedRAG では正しく回答されましたが、SimpleRAG では正しく回答されなかったため、信頼性が向上する可能性があります。</p><p>ただし、5 件中 3 件では、ハイブリッド検索のみを組み込んだ基本的な RAG パイプラインで、重要な情報のほとんどを捉えた回答を生成できたという点に留意する価値があります。</p><p>データ準備フェーズとクエリフェーズに LLM が組み込まれているため、AdvancedRAG のレイテンシは通常、SimpleRAG の 2 ～ 5 倍になることに注意してください。これは大きなコストであるため、AdvancedRAG は、応答品質がレイテンシーよりも優先される状況にのみ適している可能性があります。</p><p>データ準備段階で Claude Haiku や GPT-4o-mini などの小型で安価な LLM を使用すると、大きなレイテンシ コストを軽減できます。回答生成用の高度なモデルを保存します。</p><p>これは Wang らの研究結果と一致しています。結果が示すように、行われた改善は比較的漸進的です。つまり、シンプルなベースライン RAG を使用すると、安価で高速でありながら、適切な最終製品にほぼ到達できます。私にとっては、それは興味深い結論です。速度と効率が重要となるユースケースでは、SimpleRAG が賢明な選択です。パフォーマンスを最大限に引き出す必要があるユースケースでは、AdvancedRAG に組み込まれたテクニックが解決策となる可能性があります。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt56b7067a9d41d5a8/6a171119acf0886fb4be9c45/ea811706b6adc4731d90b925a9fefa0ac15901b4-1440x1060.jpg" alt="王パイプライン" /><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#table-of-contents">トップに戻る</a></p><h2>付記</h2><h3>プロンプト</h3><h4>RAG質問回答プロンプト</h4><p>クエリとコンテキストに基づいて LLM に回答を生成させるためのプロンプト。</p>BASIC_RAG_PROMPT = '''
You are an AI assistant tasked with answering questions based primarily on the provided context, while also drawing on your own knowledge when appropriate. Your role is to accurately and comprehensively respond to queries, prioritizing the information given in the context but supplementing it with your own understanding when beneficial. Follow these guidelines:

1. Carefully read and analyze the entire context provided.
2. Primarily focus on the information present in the context to formulate your answer.
3. If the context doesn't contain sufficient information to fully answer the query, state this clearly and then supplement with your own knowledge if possible.
4. Use your own knowledge to provide additional context, explanations, or examples that enhance the answer.
5. Clearly distinguish between information from the provided context and your own knowledge. Use phrases like "According to the context..." or "The provided information states..." for context-based information, and "Based on my knowledge..." or "Drawing from my understanding..." for your own knowledge.
6. Provide comprehensive answers that address the query specifically, balancing conciseness with thoroughness.
7. When using information from the context, cite or quote relevant parts using quotation marks.
8. Maintain objectivity and clearly identify any opinions or interpretations as such.
9. If the context contains conflicting information, acknowledge this and use your knowledge to provide clarity if possible.
10. Make reasonable inferences based on the context and your knowledge, but clearly identify these as inferences.
11. If asked about the source of information, distinguish between the provided context and your own knowledge base.
12. If the query is ambiguous, ask for clarification before attempting to answer.
13. Use your judgment to determine when additional information from your knowledge base would be helpful or necessary to provide a complete and accurate answer.

Remember, your goal is to provide accurate, context-based responses, supplemented by your own knowledge when it adds value to the answer. Always prioritize the provided context, but don't hesitate to enhance it with your broader understanding when appropriate. Clearly differentiate between the two sources of information in your response.

Context:
[The concatenated documents will be inserted here]

Query:
[The user's question will be inserted here]

Please provide your answer based on the above guidelines, the given context, and your own knowledge where appropriate, clearly distinguishing between the two:
'''
<h4>弾性クエリジェネレータプロンプト</h4><p>同義語を使用してクエリを拡充し、OR 形式に変換するように要求します。</p>ELASTIC_SEARCH_QUERY_GENERATOR_PROMPT = '''
You are an AI assistant specialized in generating Elasticsearch query strings. Your task is to create the most effective query string for the given user question. This query string will be used to search for relevant documents in an Elasticsearch index.

Guidelines:
1. Analyze the user's question carefully.
2. Generate ONLY a query string suitable for Elasticsearch's match query.
3. Focus on key terms and concepts from the question.
4. Include synonyms or related terms that might be in relevant documents.
5. Use simple Elasticsearch query string syntax if helpful (e.g., OR, AND).
6. Do not use advanced Elasticsearch features or syntax.
7. Do not include any explanations, comments, or additional text.
8. Provide only the query string, nothing else.

For the question "What is Clickthrough Data?", we would expect a response like:
clickthrough data OR click-through data OR click through rate OR CTR OR user clicks OR ad clicks OR search engine results OR web analytics

AND operator is not allowed. Use only OR.

User Question:
[The user's question will be inserted here]

Generate the Elasticsearch query string:
'''
<h4>潜在的な質問ジェネレータプロンプト</h4><p>潜在的な質問の生成を促し、ドキュメントのメタデータを充実させます。</p>RAG_QUESTION_GENERATOR_PROMPT = '''
You are an AI assistant specialized in generating questions for Retrieval-Augmented Generation (RAG) systems. Your task is to analyze a given document and create 10 diverse questions that would effectively test a RAG system's ability to retrieve and synthesize information from this document.

Guidelines:
1. Thoroughly analyze the entire document.
2. Generate exactly 10 questions that cover various aspects and levels of complexity within the document's content.
3. Create questions that specifically target:
   a. Key facts and information
   b. Main concepts and ideas
   c. Relationships between different parts of the content
   d. Potential applications or implications of the information
   e. Comparisons or contrasts within the document
4. Ensure questions require answers of varying lengths and complexity, from simple retrieval to more complex synthesis.
5. Include questions that might require combining information from different parts of the document.
6. Frame questions to test both literal comprehension and inferential understanding.
7. Avoid yes/no questions; focus on open-ended questions that promote comprehensive answers.
8. Consider including questions that might require additional context or knowledge to fully answer, to test the RAG system's ability to combine retrieved information with broader knowledge.
9. Number the questions from 1 to 10.
10. Output only the ten questions, without any additional text, explanations, or answers.

Document:
[The document content will be inserted here]

Generate 10 questions optimized for testing a RAG system based on this document:
'''
<h4>HyDEジェネレータプロンプト</h4><p>HyDEを使用して仮想文書を生成するためのプロンプト</p>HYDE_DOCUMENT_GENERATOR_PROMPT = '''
You are an AI assistant specialized in generating hypothetical documents based on user queries. Your task is to create a detailed, factual document that would likely contain the answer to the user's question. This hypothetical document will be used to enhance the retrieval process in a Retrieval-Augmented Generation (RAG) system.

Guidelines:
1. Carefully analyze the user's query to understand the topic and the type of information being sought.
2. Generate a hypothetical document that:
   a. Is directly relevant to the query
   b. Contains factual information that would answer the query
   c. Includes additional context and related information
   d. Uses a formal, informative tone similar to an encyclopedia or textbook entry
3. Structure the document with clear paragraphs, covering different aspects of the topic.
4. Include specific details, examples, or data points that would be relevant to the query.
5. Aim for a document length of 200-300 words.
6. Do not use citations or references, as this is a hypothetical document.
7. Avoid using phrases like "In this document" or "This text discusses" - write as if it's a real, standalone document.
8. Do not mention or refer to the original query in the generated document.
9. Ensure the content is factual and objective, avoiding opinions or speculative information.
10. Output only the generated document, without any additional explanations or meta-text.

User Question:
[The user's question will be inserted here]

Generate a hypothetical document that would likely contain the answer to this query:
'''
<h3>ハイブリッド検索クエリのサンプル</h3>{'knn': {'field': 'primary_embedding',
  'query_vector': [0.4265527129173279,
   -0.1712949573993683,
   -0.042020395398139954,
   ...],
  'k': 100,
  'num_candidates': 100},
 'query': {'bool': {'must': [{'multi_match': {'query': 'audits Elastic Elastic auditing Elastic audit process Elastic compliance Elastic security audit Elasticsearch auditing Elasticsearch compliance Elasticsearch security audit',
      'fields': ['original_text',
       'keyphrases',
       'potential_questions',
       'entities'],
      'type': 'best_fields',
      'operator': 'or'}}],
   'should': [{'script_score': {'query': {'match_all': {}},
      'script': {'source': '\n                                        double vector_score = cosineSimilarity(params.query_vector, params.vector_field) + 1.0;\n                                        double text_score = _score;\n                                        return 0.7 * vector_score + 0.3 * text_score;\n                                        ',
       'params': {'query_vector': [0.4265527129173279,
         -0.1712949573993683,
         -0.042020395398139954,
        ...],
        'vector_field': 'primary_embedding'}}}}]}},
 'size': 10}
]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2</guid>
    <category><![CDATA[Vector Database]]></category>
    <category><![CDATA[AI]]></category>
    <dc:creator><![CDATA[Han Xiang Choong]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltf605c8246989df32/6a1711178b73cbc61d18a11d/8da40067835ab8b4dc12fe52a51a6c26858ad32f-1440x1095.jpg" length="0" type="image/jpeg"/>
    <pubDate>Thu, 15 Aug 2024 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[高度なRAGテクニックパート1：データ処理]]></title>
    <description><![CDATA[RAG のパフォーマンスを向上させる可能性のあるテクニックについて議論し、実装します。パート 1/2。高度な RAG パイプラインのデータ処理と取り込みのコンポーネントに焦点を当てます。]]></description>
    <content:encoded><![CDATA[<p><em>これは、高度なRAGテクニックを探るパート1です。</em><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2"><em>パート2はこちらをクリックしてください！</em></a></p><p>最近の論文<a href="https://arxiv.org/abs/2407.01219">「検索拡張生成におけるベスト プラクティスの探求」では、</a> RAG のベスト プラクティスのセットに収束することを目的として、さまざまな RAG 強化手法の有効性を経験的に評価しています。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt671704ff06a4011d/6a170b3ea929cf2d19ae09d8/dafa7250e7c4ead4d9b4aed7c407509131929749-1440x572.png" alt="王氏が推奨するRAGパイプライン" /><p>提案されたベストプラクティスのいくつか、つまり検索の品質を向上させることを目的としたベストプラクティス<strong>（センテンスチャンキング、HyDE、リバースパッキング）</strong>を実装します。</p><p>簡潔にするために、効率性の向上に重点を置いた手法<strong>(クエリの分類と要約)</strong>は省略します。</p><p>また、ここでは取り上げなかったものの、個人的には便利で興味深いと思われるいくつかのテクニック<strong>(メタデータの包含、複合マルチフィールドの埋め込み、クエリの強化) も</strong>実装します。</p><p>最後に、検索結果と生成された回答の品質がベースラインと比較して向上したかどうかを確認するための短いテストを実行します。さあ始めましょう！</p><h2>RAGの概要</h2><p>RAG は、外部の知識ベースから情報を取得して生成された回答を充実させることで、LLM を強化することを目的としています。ドメイン固有の情報を提供することで、LLM はトレーニング データの範囲外のユース ケースに迅速に適応できます。微調整よりも大幅にコストが安く、最新の状態に保つのも簡単になります。</p><p>RAG の品質を向上させるための対策は、通常、次の 2 つの点に重点を置いています。</p><ol><li><p>ナレッジベースの品質と明確さを向上します。</p></li><li><p>検索クエリの範囲と特定性を向上させます。</p></li></ol><p>これら 2 つの対策により、LLM が関連する事実や情報にアクセスできる可能性が高まり、幻覚を起こしたり、古くなったり無関係になったりする可能性のある独自の知識を利用したりする可能性が低くなるという目標が達成されます。</p><p>方法の多様性を数文で説明するのは困難です。わかりやすくするために、すぐに実装に移りましょう。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt9a4691874a19d8da/6a170b3f47d49c99f22d8a24/72b51ba2ae5e5977b56e5b915674753d6cfd0e56-1440x840.jpg" alt="高度なRAGパイプライン" /><h3>目次</h3><ul><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-1#overview">ご紹介</a></p><ul><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-1#table-of-contents">目次</a></p></li></ul></li><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-1#set-up">設定</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-1#ingesting-processing-and-embedding-documents">ドキュメントの取り込み、処理、埋め込み</a>  </p><ul><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-1#data-ingestion">データインジェスト</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-1#sentence-level-token-wise-chunking">文レベル、トークン単位のチャンキング</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-1#metadata-inclusion-and-generation">メタデータの包含と生成</a> </p><ul><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-1#keyphrases-extracted-by-textrank">TextRankによって抽出されたキーフレーズ</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-1#potential-questions-generated-by-gpt-4o">GPT-4oによって生成される潜在的な質問</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-1#entities-extracted-by-spacy">Spacyによって抽出されたエンティティ</a></p></li></ul></li><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-1#composite-multi-field-embeddings">複合多体埋め込み</a></p><ul><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-1#indexing-to-elastic">Elasticへのインデックス</a></p></li></ul></li></ul></li><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-1#cat-break">猫の休憩</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-1#appendix">付記</a></p><ul><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-1#definitions">定義</a></p></li></ul></li></ul><h2>設定</h2><p><em>すべてのコードは</em><a href="https://github.com/elastic/elasticsearch-labs/tree/advanced-rag-techniques/supporting-blog-content/advanced-rag-techniques"><em> Searchlabs リポジトリに</em></a><em> あります 。</em></p><p>まずは第一に。次のものが必要になります:</p><ol><li><p>弾力性のあるクラウドの展開</p></li><li><p>LLM API - このノートブックでは、Azure OpenAI 上の GPT-4o デプロイメントを使用しています。</p></li><li><p>Python バージョン 3.12.4 以降</p></li></ol><p><a href="https://github.com/elastic/elasticsearch-labs/blob/advanced-rag-techniques/supporting-blog-content/advanced-rag-techniques/main.ipynb">main.ipynb ノートブックからすべてのコードを実行します。</a></p><p>リポジトリを git clone し、supporting-blog-content/advanced-rag-techniques に移動して、次のコマンドを実行します。</p># Create a new virtual environment named 'rag_env'
python -m venv rag_env

# Activate the virtual environment (for Unix-based systems)
source rag_env/bin/activate

# (For Windows)
.\rag_env\Scripts\activate

# Install packages listed in requirements.txt
pip install -r requirements.txt
<p>完了したら、 <em>.env</em>を作成します。ファイルを開き、次のフィールドに入力します ( <a href="https://github.com/elastic/elasticsearch-labs/blob/advanced-rag-techniques/supporting-blog-content/advanced-rag-techniques/.env.example"><em>.env.example</em></a>で参照されます)。有益なコメントをくれた共著者の Claude-3.5 に感謝します。</p># Elastic Cloud: Found in the 'Deployment' page of your Elastic Cloud 
# console
ELASTIC_CLOUD_ENDPOINT=""
ELASTIC_CLOUD_ID=""

# Elastic Cloud: Created during deployment setup or in 'Security' 
# settings
ELASTIC_USERNAME=""
ELASTIC_PASSWORD=""

# Elastic Cloud: The name of the index you created in Kibana or via API
ELASTIC_INDEX_NAME=""

# Azure AI Studio: Found in 'Keys and Endpoint' section of your Azure 
# OpenAI resource
AZURE_OPENAI_KEY_1=""
AZURE_OPENAI_KEY_2=""
AZURE_OPENAI_REGION=""
AZURE_OPENAI_ENDPOINT=""

# Azure AI Studio: Found in 'Deployments' section of your Azure OpenAI 
# resource
AZURE_OPENAI_DEPLOYMENT_NAME=""

# Using BAAI/bge-small-en-v1.5 because I think it is a good balance of 
# resource efficiency and performance. 
HUGGINGFACE_EMBEDDING_MODEL="BAAI/bge-small-en-v1.5"
<p>次に、取り込むドキュメントを選択し、ドキュメント フォルダーに配置します。この記事では、 <a href="https://s201.q4cdn.com/217177842/files/doc_downloads/OtherDocuments/2023/AnnualMeeting/Annual-Report-Fiscal-Year-2023.pdf">Elastic NV Annual Report 2023 を</a>使用します。これは非常に難しくて密度の高いドキュメントであり、RAG テクニックのストレス テストに最適です。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte292dc6030d496cc/6a170b40dc55de9b03e00dfc/e513b9d67adac43da794c25a5969b893127bbbe3-1440x395.jpg" alt="Elastic 年次報告書 2023" /><p>準備が整いましたので、摂取に移りましょう。<em>main.ipynb</em>を開き、最初の 2 つのセルを実行して、すべてのパッケージをインポートし、すべてのサービスを初期化します。</p><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-1#table-of-contents">トップに戻る</a></p><h2>ドキュメントの取り込み、処理、埋め込み</h2><h3>データインジェスト</h3><ul><li><p><em>個人的なメモ: LlamaIndex の便利さに驚いています。LLM や LlamaIndex が登場する前の昔、さまざまな形式のドキュメントを取り込むには、あらゆる場所から難解なパッケージを収集する、骨の折れる作業でした。今では、関数呼び出しは 1 つに減りました。野生。</em></p></li></ul><p><code>SimpleDirectoryReader</code>は<code>directory_path.</code>内のすべてのドキュメントをロードします。 <code>.pdf</code>ファイルの場合は、ドキュメント オブジェクトのリストを返します。このリストは、操作しやすいように Python 辞書に変換します。</p># llamaindex_processor.py
from llama_index.core import SimpleDirectoryReader

class LlamaIndexProcessor:
   def __init__(self):
       pass 
   
   def load_documents(self, directory_path):
       ''' 
       Load all documents in directory
       '''
       reader = SimpleDirectoryReader(input_dir=directory_path)
       return reader.load_data()

# main.ipynb
llamaindex_processor=LlamaIndexProcessor()
documents=llamaindex_processor.load_documents('./documents/')
documents=[dict(doc_obj) for doc_obj in documents]
<p>各辞書には、 <code>text</code>フィールドにキー コンテンツが含まれています。また、ページ番号、ファイル名、ファイル サイズ、タイプなどの便利なメタデータも含まれています。</p>{
  'id_': '5f76f0b3-22d8-49a8-9942-c2bbab14f63f',
  'metadata': {'page_label': '5',
   'file_name': 'Elastic_NV_Annual-Report-Fiscal-Year-2023.pdf',
   'file_path': '/Users/han/Desktop/Projects/truckasaurus/documents/Elastic_NV_Annual-Report-Fiscal-Year-2023.pdf',
   'file_type': 'application/pdf',
   'file_size': 3724426,
   'creation_date': '2024-07-27',
   'last_modified_date': '2024-07-27'},
   'text': 'Table of Contents\nPage\nPART I\nItem 1. Business 3\n15 Item 1A. Risk Factors\nItem 1B. Unresolved Staff Comments 48\nItem 2. Properties 48\nItem 3. Legal Proceedings 48\nItem 4. Mine Safety Disclosures 48\nPART II\nItem 5. Market for Registrant's Common Equity, Related Stockholder Matters and Issuer Purchases of \nEquity Securities49\nItem 6. [Reserved] 49\nItem 7. Management's Discussion and Analysis of Financial Condition and Results of Operations 50\nItem 7A. Quantitative and Qualitative Disclosures About Market Risk 64\nItem 8. Financial Statements and Supplementary Data 66\nItem 9. Changes in and Disagreements With Accountants on Accounting and Financial Disclosure 100\n100\n101Item 9A. Controls and Procedures\nItem 9B. Other Information\nItem 9C. Disclosure Regarding Foreign Jurisdictions That Prevent Inspections 101\nPART III\n102\n102\n102\n102Item 10. Directors, Executive Officers and Corporate Governance\nItem 11. Executive Compensation\nItem 12. Security Ownership of Certain Beneficial Owners and Management, and Related Stockholder Matters  \nItem 13. Certain Relationships and Related Transactions, and Director Independence\nItem 14. Principal Accountant Fees and Services 102\nPART IV\n103\n105Item 15. Exhibits and Financial Statement Schedules  \nItem 16. Form 10-K Summary\nSignatures 106\ni',
   ...
}
<p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-1#table-of-contents">トップに戻る</a></p><h3>文レベル、トークン単位のチャンキング</h3><p>最初にやるべきことは、ドキュメントを標準的な長さのチャンクに削減することです (一貫性と管理性を確保するため)。埋め込みモデルには、固有のトークン制限 (処理できる最大入力サイズ) があります。トークンはモデルが処理するテキストの基本単位です。情報の損失（コンテンツの切り捨てや省略）を防ぐために、これらの制限を超えないテキストを提供する必要があります（長いテキストを短いセグメントに分割する）。</p><p>チャンク化はパフォーマンスに大きな影響を与えます。理想的には、各チャンクは自己完結的な情報を表し、単一のトピックに関するコンテキスト情報をキャプチャします。チャンク化の方法には、文書を単語数で分割する単語レベルのチャンク化と、LLM を使用して論理ブレークポイントを識別するセマンティック チャンク化があります。</p><p>単語レベルのチャンキングは安価で高速かつ簡単ですが、文が分割され、コンテキストが壊れるリスクがあります。セマンティック チャンキングは、特に 116 ページの Elastic 年次レポートのようなドキュメントを扱う場合には、時間がかかり、コストも高くなります。</p><p>中道的なアプローチを選択しましょう。文レベルのチャンキングは依然としてシンプルですが、単語レベルのチャンキングよりもコンテキストをより効果的に保持でき、コストも大幅に削減され、処理速度も速くなります。さらに、周囲のコンテキストの一部をキャプチャし、段落を分割することによる影響を軽減するために、スライディング ウィンドウを実装します。</p># chunker.py 

import uuid
import re


class Chunker: 
    def __init__(self, tokenizer):
        self.tokenizer = tokenizer 
    
    def split_into_sentences(self, text):
        """Split text into sentences."""
        return re.split(r'(?&lt;=[.!?])\s+', text)
 
    def sentence_wise_tokenized_chunk_documents(self, documents, chunk_size=512, overlap=20, min_chunk_size=50):
        '''
        1. Split text into sentences.
        2. Tokenize using the provided tokenizer method.
        3. Build chunks up to the chunk_size limit.
        4. Create an overlap based on tokens - to preserve context.
        5. Only keep chunks that meet the minimum token size requirement.
        '''
        chunked_documents = []

        for doc in documents:
            sentences = self.split_into_sentences(doc['text'])
            tokens = []
            sentence_boundaries = [0]

            # Tokenize all sentences and keep track of sentence boundaries
            for sentence in sentences:
                sentence_tokens = self.tokenizer.encode(sentence, add_special_tokens=True)
                tokens.extend(sentence_tokens)
                sentence_boundaries.append(len(tokens))

            # Create chunks
            chunk_start = 0
            while chunk_start &lt; len(tokens):
                chunk_end = chunk_start + chunk_size

                # Find the last complete sentence that fits in the chunk
                sentence_end = next((i for i in sentence_boundaries if i &gt; chunk_end), len(tokens))
                chunk_end = min(chunk_end, sentence_end)

                # Create the chunk
                chunk_tokens = tokens[chunk_start:chunk_end]

                # Check if the chunk meets the minimum size requirement
                if len(chunk_tokens) &gt;= min_chunk_size:
                    # Create a new document object for this chunk
                    chunk_doc = {
                        'id_': str(uuid.uuid4()),
                        'chunk': chunk_tokens,
                        'original_text': self.tokenizer.decode(chunk_tokens),
                        'chunk_index': len(chunked_documents),
                        'parent_id': doc['id_'],
                        'chunk_token_count': len(chunk_tokens)
                    }

                    # Copy all other fields from the original document
                    for key, value in doc.items():
                        if key != 'text' and key not in chunk_doc:
                            chunk_doc[key] = value

                    chunked_documents.append(chunk_doc)

                # Move to the next chunk start, considering overlap
                chunk_start = max(chunk_start + chunk_size - overlap, chunk_end - overlap)

        return chunked_documents

# main.ipynb 
# Initialize Embedding Model
HUGGINGFACE_EMBEDDING_MODEL = os.environ.get('HUGGINGFACE_EMBEDDING_MODEL')
embedder=EmbeddingModel(model_name=HUGGINGFACE_EMBEDDING_MODEL)

# Initialize Chunker
chunker=Chunker(embedder.tokenizer)
<p><code>Chunker</code>クラスは埋め込みモデルのトークナイザーを受け取り、テキストをエンコードおよびデコードします。ここで、20 個のトークンが重なり合う、それぞれ 512 個のトークンのチャンクを構築します。これを実行するには、テキストを文に分割し、それらの文をトークン化してから、トークン制限に違反することなく追加できなくなるまで、トークン化された文を現在のチャンクに追加します。</p><p>最後に、埋め込みのために文章を元のテキストにデコードし、 <code>original_text</code>というフィールドに保存します。チャンクは<code>chunk</code>というフィールドに保存されます。ノイズ（つまり、無駄なドキュメント）を減らすために、長さが 50 トークン未満のドキュメントは破棄されます。</p><p>これをドキュメント上で実行してみましょう。</p>chunked_documents=chunker.sentence_wise_tokenized_chunk_documents(documents, chunk_size=512)
<p>そして、次のようなテキストのチャンクが返されます。</p>print(chunked_documents[4]['original_text'])

[CLS] the aggregate market value of the ordinary shares held by non - affiliates of the registrant, 
based on the closing price of the shares of ordinary shares on the new york stock exchange on 
october 31, 2022 ( the last business day of the registrant 's second fiscal quarter ), was 
approximately $ 6. 1 billion. [SEP] [CLS] as of may 31, 2023, the registrant had 97, 390, 886 
ordinary shares, par value €0. 01 per share, outstanding. [SEP] [CLS] documents incorporated by 
reference portions of the registrant 's definitive proxy statement relating to the registrant 's 2
023 annual general meeting of shareholders are incorporated by reference into part iii of this annual 
...
...
<p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-1#table-of-contents">トップに戻る</a></p><h3>メタデータの包含と生成</h3><p>ドキュメントをチャンクに分割しました。次は、データを充実させる段階です。追加のメタデータを生成または抽出したい。この追加のメタデータは、検索パフォーマンスに影響を与え、強化するために使用できます。</p><p>ドキュメントのリスト (Python 辞書) とプロセッサ関数のリストを受け取る役割を持つ<code>DocumentEnricher</code>クラスを定義します。これらの関数はドキュメントの<code>original_text</code>列を実行し、その出力を新しいフィールドに保存します。</p><p>まず、 <a href="https://github.com/elastic/elasticsearch-labs/blob/advanced-rag-techniques/supporting-blog-content/advanced-rag-techniques/nltk_processor.py">TextRank</a>を使用してキーフレーズを抽出します。TextRank は、単語間の関係に基づいて重要度をランク付けすることにより、テキストから主要なフレーズと文を抽出するグラフベースのアルゴリズムです。</p><p>次に、 <a href="https://github.com/elastic/elasticsearch-labs/blob/advanced-rag-techniques/supporting-blog-content/advanced-rag-techniques/llm.py">GPT-4oを使用してpotential_questionsを生成します</a>。</p><p>最後に、<a href="https://spacy.io/"> Spacy</a> <a href="https://github.com/elastic/elasticsearch-labs/blob/advanced-rag-techniques/supporting-blog-content/advanced-rag-techniques/entity_extractor.py">を使用して エンティティを抽出します</a> 。</p><p>それぞれのコードは非常に長くて複雑なので、ここで再現することは控えます。ご興味があれば、以下のコード サンプルにファイルがマークされています。</p><p>データ拡充を実行してみましょう:</p># documentenricher.py
from tqdm import tqdm

class DocumentEnricher:

    def __init__(self):
        pass 

    def enrich_document(self, documents, processors, text_col='text'):
        for doc in tqdm(documents, desc="Enriching documents using processors: "+str(processors)): 
            for (processor, field) in processors: 
                metadata=processor(doc[text_col])
                if isinstance(metadata, list):
                    metadata='\n'.join(metadata)
                doc.update({field: metadata})
 
# main.ipynb
# Initialize processor classes 
nltkprocessor=NLTKProcessor() // nltk_processor.py
entity_extractor=EntityExtractor() // entity_extractor.py
gpt4o = LLMProcessor(model='gpt-4o') // llm.py

# Initialize LLM
documentenricher=DocumentEnricher()

# Create new fields in the documents - These are the outputs of the processor functions.
processors=[
    (nltkprocessor.textrank_phrases, "keyphrases"),
    (gpt4o.generate_questions, "potential_questions"),
    (entity_extractor.extract_entities, "entities")
    ]

# .enrich_document() will modify chunked_docs in place. 
# To view the results, we'll print chunked_docs in the next few cells!
documentenricher.enrich_document(chunked_docs, text_col='original_text', processors=processors)
<p>結果を見てみましょう:</p><h4>TextRankによって抽出されたキーフレーズ</h4><p>これらのキーフレーズは、チャンクの中核トピックの代わりとなります。クエリがサイバーセキュリティに関係する場合、このチャンクのスコアは向上します。</p>print(chunked_documents[25]['keyphrases'])

'elastic agent stop', 'agent stop malware', 
'stop malware ransomware', 'malware ransomware environment', 
'ransomware environment wide', 'environment wide visibility', 
'wide visibility threat', 'visibility threat detection', 
'sep cl key', 'cl key feature'
<h4>GPT-4oによって生成される潜在的な質問</h4><p>これらの潜在的な質問はユーザーのクエリと直接一致する可能性があり、スコアの向上につながります。GPT-4o に、現在のチャンクにある情報を使用して回答できる質問を生成するように指示します。</p>print(chunked_documents[25]['potential_questions'])

1. What are the primary functions that Elastic Agent provides in terms of cybersecurity?
2. Describe how Logstash contributes to data management within an IT environment.
3. List and explain any key features of Logstash mentioned in the document.
4. How does Elastic Agent enhance environment-wide visibility in threat detection?
5. What capabilities does Logstash offer for handling data beyond simple collection?
6. In what ways does the document suggest that Elastic Agent stops malware and ransomware?
7. Can you identify any relationships between the functionalities of Elastic Agent and Logstash in an integrated environment?
8. What implications might the advanced threat detection capabilities of Elastic Agent have for organizational security policies?
9. Compare and contrast the roles of Elastic Agent and Logstash based on their described functions.
10. How might the centralized collection ability of Logstash support the threat detection capabilities of Elastic Agent?
<h4>Spacyによって抽出されたエンティティ</h4><p>これらのエンティティはキーフレーズと同様の目的を果たしますが、キーフレーズ抽出では見逃される可能性のある組織や個人の名前を取得します。</p>print(chunked_documents[29]['entities'])

'appdynamics', 'apm data', 'azure sentinel', 
'microsoft', 'mcafee', 'broadcom', 'cisco', 
'dynatrace', 'coveo', 'lucidworks'
<p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-1#table-of-contents">トップに戻る</a></p><h3>複合多体埋め込み</h3><p>追加のメタデータでドキュメントを充実させたので、この情報を活用して、より堅牢でコンテキストを認識した埋め込みを作成できます。</p><p>プロセスの現在のポイントを確認しましょう。各ドキュメントには 4 つの興味深いフィールドがあります。</p>{
    "chunk": "...",
    "keyphrases": "...", 
    "potential_questions": "...", 
    "entities": "..." 
}
<p>各フィールドはドキュメントのコンテキストに関する異なる視点を表し、LLM が重点を置くべき重要な領域を強調する可能性があります。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt84cb328fce6aae23/6a170b42964cea3e4408bbc4/aea1f513009a0c7c8545a79fad8f072a5bcae24c-1440x1067.jpg" alt="RAG のメタデータ強化パイプライン" /><p>計画としては、これらの各フィールドを埋め込み、複合埋め込みと呼ばれる埋め込みの加重合計を作成することです。</p><p>運が良ければ、この複合埋め込みにより、検索動作を制御する別の調整可能なハイパーパラメータが導入されるだけでなく、システムがよりコンテキストを認識できるようになります。</p><p>まず、main.ipynb ノートブックの先頭にインポートされたローカルに定義された埋め込みモデルを使用して、各フィールドを埋め込み、各ドキュメントを更新します。</p># EmbeddingModel defined in embedding_model.py
embedder=EmbeddingModel(model_name=HUGGINGFACE_EMBEDDING_MODEL)

cols_to_embed=['keyphrases', 'potential_questions', 'entities']

embedding_cols=[]
for col in cols_to_embed:
    # Works on text input
    embedding_col=embedder.embed_documents_text_wise(chunked_documents, text_field=col)
    embedding_cols.append(embedding_col)
# Works on token input
embedding_col=embedder.embed_documents_token_wise(chunked_documents, token_field="chunk")
embedding_cols.append(embedding_col)
<p>各埋め込み関数は埋め込みのフィールドを返します。これは、 <code>_embedding</code>という接尾辞が付いた元の入力フィールドです。</p><p>複合埋め込みの重みを定義しましょう。</p>embedding_cols=[
                'keyphrases_embedding',
                'potential_questions_embedding',
                'entities_embedding',
                'chunk_embedding']
combination_weights=[
                    0.1,
                    0.15,
                    0.05,
                    0.7
                ]
<p>重み付けにより、ユースケースとデータの品質に基づいて各コンポーネントに優先順位を割り当てることができます。直感的に言えば、これらの重み付けの大きさは、各コンポーネントの意味的価値に依存します。チャンクテキスト自体が圧倒的に豊富なので、重み付けを 70% に割り当てます。エンティティは組織名や人名のリストだけなので最も小さいので、重み付けを 5% に割り当てます。これらの値の正確な設定は、ユースケースごとに経験的に決定する必要があります。</p><p>最後に、重み付けを適用し、複合埋め込みを作成する関数を記述しましょう。スペースを節約するために、コンポーネントの埋め込みもすべて削除します。</p>from tqdm import tqdm 
def combine_embeddings(objects, embedding_cols, combination_weights, primary_embedding='primary_embedding'):
    # Ensure the number of weights matches the number of embedding columns
    assert len(embedding_cols) == len(combination_weights), "Number of embedding columns must match number of weights"
    
    # Normalize weights to sum to 1
    weights = np.array(combination_weights) / np.sum(combination_weights)
    
    for obj in tqdm(objects, desc="Combining embeddings"):
        # Initialize the combined embedding
        combined = np.zeros_like(obj[embedding_cols[0]])
        
        # Compute the weighted sum
        for col, weight in zip(embedding_cols, weights):
            combined += weight * np.array(obj[col])
        
        # Add the new combined embedding to the object
        obj.update({primary_embedding:combined.tolist()})
        
        # Remove the original embedding columns
        for col in embedding_cols:
            obj.pop(col, None)

combine_embeddings(chunked_documents, embedding_cols, combination_weights)
<p>これで書類の処理は完了です。次のようなドキュメント オブジェクトのリストが作成されました。</p>{ 'id_': '7fe71686-5cd0-4831-9e79-998c6dbeae0c', 'chunk': [2312, 14613, ...], 'original_text': 'if an emerging growth company, indicate by check mark if the registrant has elected not to use the extended ...', 'chunk_index': 3, 'chunk_token_count': 399, 'metadata': {'page_label': '3', 'file_name': 'Elastic_NV_Annual-Report-Fiscal-Year-2023.pdf', ... 'keyphrases': 'sep cl unk\ncheck mark registrant\ncl unk indicate\nunk indicate check\nindicate check mark\nprincipal executive office\naccelerate filer unk\ncompany unk emerge\nunk emerge growth\nemerge growth company', 'potential_questions': '1. What are the different types of registrant statuses mentioned in the document?\n2. Under what section of the Sarbanes-Oxley Act must registrants file a report on the effectiveness of their internal ...', 'entities': 'the effe ctiveness of\nsection 13\nSEP\nUNK\nsection 21e\n1934\n1933\nu. s. c.\nsection 404\nsection 12\nal', 'primary_embedding': [-0.3946287803351879, -0.17586839850991964, ...] }
<h4>Elasticへのインデックス</h4><p>ドキュメントを Elastic Search に一括アップロードしてみましょう。この目的のために、私はずっと前に<a href="https://github.com/elastic/elasticsearch-labs/blob/advanced-rag-techniques/supporting-blog-content/advanced-rag-techniques/elastic_helpers.py"><code>elastic_helpers.py</code></a>で Elastic Helper 関数のセットを定義しました。これは非常に長いコードなので、関数呼び出しに注目してみましょう。</p><p><code>es_bulk_indexer.bulk_upload_documents</code> Elasticsearch の便利な動的マッピングを活用して、辞書オブジェクトの任意のリストで動作します。</p># Initialize Elasticsearch
ELASTIC_CLOUD_ID = os.environ.get('ELASTIC_CLOUD_ID')
ELASTIC_USERNAME = os.environ.get('ELASTIC_USERNAME')
ELASTIC_PASSWORD = os.environ.get('ELASTIC_PASSWORD')
ELASTIC_CLOUD_AUTH = (ELASTIC_USERNAME, ELASTIC_PASSWORD)
es_bulk_indexer = ESBulkIndexer(cloud_id=ELASTIC_CLOUD_ID, credentials=ELASTIC_CLOUD_AUTH)
es_query_maker = ESQueryMaker(cloud_id=ELASTIC_CLOUD_ID, credentials=ELASTIC_CLOUD_AUTH)

# Define Index Name
index_name=os.environ.get('ELASTIC_INDEX_NAME')


# Create index and bulk upload 
index_exists = es_bulk_indexer.check_index_existence(index_name=index_name)
if not index_exists:
    logger.info(f"Creating new index: {index_name}")
    es_bulk_indexer.create_es_index(es_configuration=BASIC_CONFIG, index_name=index_name)

success_count = es_bulk_indexer.bulk_upload_documents(
    index_name=index_name, 
    documents=chunked_documents, 
    id_col='id_',
    batch_size=32
)
<p>Kibana にアクセスして、すべてのドキュメントがインデックスされていることを確認します。全部で224個あるはずです。こんなに大きな文書にしては悪くないですね!</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt8efeface6effe01d/6a170b447d8d67652870e72a/1b3b07f6b98ceb65f6594ce4be83c5b0ed7e7cf9-1440x1380.jpg" alt="インデックスキバナ" /><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-1#table-of-contents">トップに戻る</a></p><h2>猫の休憩</h2><p>ちょっと休憩しましょう。記事がちょっと重いのはわかっています。私の猫を見てください:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltc1db5595f71c12ff/6a170b450e2e49940241a0fe/baca4eb52b801b21ced97352cc55462f0a12d6b0-969x996.jpg" alt="ハンパイプライン" /><p>愛らしい。帽子がなくなってしまったので、彼女がそれを盗んでどこかに隠したのではないかと半分疑っています :(</p><p>ここまで来られたことおめでとうございます :)</p><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2">パート 2</a>では、RAG パイプラインのテストと評価についてご紹介します。</p><h2>付記</h2><h3>定義</h3><p><strong>1. 文のチャンキング</strong></p><ul><li><p>RAG システムでテキストをより小さな意味のある単位に分割するために使用される前処理手法。</p></li><li><p><em>プロセス：</em> </p><ol><li><p>入力: 大きなテキストブロック（例: 文書、段落）</p></li><li><p>出力: 小さなテキストセグメント (通常は文または小さな文のグループ)</p></li></ol></li><li><p><em>目的：</em> </p><ul><li><p>きめ細やかでコンテキストに特化したテキストセグメントを作成する</p></li><li><p>より正確なインデックス作成と検索が可能</p></li><li><p>RAGシステムで取得した情報の関連性を向上</p></li></ul></li><li><p><em>特徴:</em> </p><ul><li><p>セグメントは意味的に意味がある</p></li><li><p>独立してインデックスを作成し、検索できる</p></li><li><p>多くの場合、独立した理解可能性を確保するためにある程度の文脈が保持される</p></li></ul></li><li><p><em>メリット：</em> </p><ul><li><p>検索精度の向上</p></li><li><p>RAGパイプラインのより集中的な拡張を可能にします</p></li></ul></li></ul><p><strong>2. HyDE（仮想文書埋め込み）</strong></p><ul><li><p>LLM を使用して、RAG システムでのクエリ拡張用の仮想ドキュメントを生成する手法。</p></li><li><p><em>プロセス：</em>  </p><ol><li><p>LLMへの入力クエリ</p></li><li><p>LLMはクエリに答える仮説文書を生成する</p></li><li><p>生成されたドキュメントを埋め込む</p></li><li><p>ベクトル検索に埋め込みを使用する</p></li></ol></li><li><p><em>主な違い:</em> </p><ul><li><p>従来のRAG: クエリとドキュメントを一致させる</p></li><li><p>HyDE: 文書を文書と照合する</p></li></ul></li><li><p><em>目的：</em> </p><ul><li><p>特に複雑または曖昧なクエリの検索パフォーマンスを向上します</p></li><li><p>短いクエリよりも豊富な意味コンテキストをキャプチャする</p></li></ul></li><li><p><em>メリット：</em> </p><ul><li><p>LLMの知識を活用してクエリを拡張する</p></li><li><p>検索された文書の関連性が向上する可能性がある</p></li></ul></li><li><p><em>課題:</em> </p><ul><li><p>追加のLLM推論が必要となり、レイテンシとコストが増加する</p></li><li><p>パフォーマンスは生成された仮想文書の品質に依存する</p></li></ul></li></ul><p><strong>3. 逆パッキング</strong></p><ul><li><p>RAG システムで、検索結果を LLM に渡す前に並べ替えるために使用される手法。</p></li><li><p><em>プロセス：</em> </p><ol><li><p>検索エンジン (Elasticsearch など) は、関連性の高い順にドキュメントを返します。</p></li><li><p>順序は逆になり、最も関連性の高いドキュメントが最後に配置されます。</p></li></ol></li><li><p><em>目的：</em> </p><ul><li><p>LLM の新しさバイアスを利用します。LLM は、それぞれのコンテキストにおける最新の情報に重点を置く傾向があります。</p></li><li><p>LLM のコンテキスト ウィンドウ内で最も関連性の高い情報が「最新」であることを保証します。</p></li></ul></li><li><p><em>例:</em>元の順序: [最も関連性の高い順、2番目に関連性の高い順、3番目に関連性の高い順、...] 逆の順序: [...、3番目に関連性の高い順、2番目に関連性の高い順、最も関連性の高い順]</p></li></ul><p><strong>4. クエリの分類</strong></p><ul><li><p>クエリに RAG が必要かどうか、または LLM によって直接回答できるかどうかを判断して、RAG システムの効率を最適化する手法。</p></li><li><p><em>プロセス：</em> </p><ol><li><p>使用中の LLM に固有のカスタム データセットを開発する</p></li><li><p>特殊な分類モデルをトレーニングする</p></li><li><p>モデルを使用して受信したクエリを分類する</p></li></ol></li><li><p><em>目的：</em> </p><ul><li><p>不要なRAG処理を回避することでシステム効率を向上</p></li><li><p>最も適切な応答メカニズムにクエリを直接送信する</p></li></ul></li><li><p><em>要件：</em> </p><ul><li><p>LLM固有のデータセットとモデル</p></li><li><p>精度を維持するための継続的な改良</p></li></ul></li><li><p><em>メリット：</em> </p><ul><li><p>単純なクエリの計算オーバーヘッドを削減</p></li><li><p>非RAGクエリの応答時間を改善する可能性がある</p></li></ul></li></ul><p><strong>5. 要約</strong></p><ul><li><p>RAG システムで検索された文書を圧縮する手法。</p></li><li><p><em>プロセス：</em> </p><ol><li><p>関連文書を取得する</p></li><li><p>各文書の簡潔な要約を生成する</p></li><li><p>RAG パイプラインでは完全なドキュメントではなく要約を使用する</p></li></ol></li><li><p><em>目的：</em> </p><ul><li><p>重要な情報に焦点を当ててRAGのパフォーマンスを向上させる</p></li><li><p>関連性の低いコンテンツからのノイズや干渉を減らす</p></li></ul></li><li><p><em>メリット：</em> </p><ul><li><p>LLM回答の関連性が向上する可能性がある</p></li><li><p>コンテキスト制限内でより多くのドキュメントを含めることができます</p></li></ul></li><li><p><em>課題:</em> </p><ul><li><p>要約時に重要な詳細が失われるリスク</p></li><li><p>要約生成のための追加の計算オーバーヘッド</p></li></ul></li></ul><p><strong>6. メタデータの包含</strong></p><ul><li><p>追加のコンテキスト情報でドキュメントを充実させる手法。</p></li><li><p><em>メタデータの種類:</em>  </p><ul><li><p>キーフレーズ</p></li><li><p>タイトル</p></li><li><p>日付</p></li><li><p>著者詳細</p></li><li><p>宣伝文句</p></li></ul></li><li><p><em>目的：</em> </p><ul><li><p>RAGシステムで利用可能なコンテキスト情報を増やす</p></li><li><p>LLMに文書の内容と関連性をより明確に理解させる</p></li></ul></li><li><p><em>メリット：</em> </p><ul><li><p>検索精度が向上する可能性がある</p></li><li><p>LLMの文書有用性を評価する能力を強化する</p></li></ul></li><li><p><em>実装：</em> </p><ul><li><p>文書の前処理中に実行できる</p></li><li><p>追加のデータ抽出または生成手順が必要になる場合があります</p></li></ul></li></ul><p><strong>7. 複合多体埋め込み</strong></p><ul><li><p>異なるドキュメント コンポーネントごとに個別の埋め込みを作成する RAG システム用の高度な埋め込み手法。</p></li><li><p><em>プロセス：</em> </p><ol><li><p>関連するフィールド（例：タイトル、キーフレーズ、宣伝文句、メインコンテンツ）を特定する</p></li><li><p>各フィールドごとに個別の埋め込みを生成する</p></li><li><p>これらの埋め込みを結合または保存して検索に使用します</p></li></ol></li><li><p><em>標準的なアプローチとの違い:</em> </p><ul><li><p>従来型: ドキュメント全体の単一の埋め込み</p></li><li><p>複合: さまざまなドキュメントの側面に対応する複数の埋め込み</p></li></ul></li><li><p><em>目的：</em> </p><ul><li><p>よりニュアンス豊かで文脈を考慮した文書表現を作成する</p></li><li><p>文書内のより多様なソースから情報を取得する</p></li></ul></li><li><p><em>メリット：</em> </p><ul><li><p>曖昧なクエリや多面的なクエリのパフォーマンスが向上する可能性があります</p></li><li><p>検索時にさまざまな文書の側面をより柔軟に重み付けできます</p></li></ul></li><li><p><em>課題:</em> </p><ul><li><p>埋め込みストレージと検索プロセスの複雑さが増す</p></li><li><p>より洗練されたマッチングアルゴリズムが必要になる場合があります</p></li></ul></li></ul><p><strong>8. クエリエンリッチメント</strong></p><ul><li><p>元のクエリを関連用語で拡張し、検索範囲を広げる手法。</p></li><li><p><em>プロセス：</em> </p><ol><li><p>元のクエリを分析する</p></li><li><p>同義語や意味的に関連するフレーズを生成する</p></li><li><p>クエリに以下の追加用語を追加します</p></li></ol></li><li><p><em>目的：</em> </p><ul><li><p>文書コーパス内の潜在的な一致の範囲を拡大する</p></li><li><p>特定の言語や専門用語を含むクエリの検索パフォーマンスを向上</p></li></ul></li><li><p><em>メリット：</em> </p><ul><li><p>元の検索語句と完全に一致しない関連文書を取得する可能性がある</p></li><li><p>クエリとドキュメント間の語彙の不一致を克服するのに役立ちます</p></li></ul></li><li><p><em>課題:</em> </p><ul><li><p>慎重に実装しないとクエリドリフトのリスクがある</p></li><li><p>検索プロセスにおける計算オーバーヘッドが増加する可能性がある</p></li></ul></li></ul><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-1#table-of-contents">トップに戻る</a></p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-1</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-1</guid>
    <category><![CDATA[Vector Database]]></category>
    <category><![CDATA[AI]]></category>
    <dc:creator><![CDATA[Han Xiang Choong]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt9a4691874a19d8da/6a170b3f47d49c99f22d8a24/72b51ba2ae5e5977b56e5b915674753d6cfd0e56-1440x840.jpg" length="0" type="image/jpeg"/>
    <pubDate>Wed, 14 Aug 2024 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[OpenSearchとElasticsearchの違い：ベクトル検索のパフォーマンス比較]]></title>
    <description><![CDATA[Elasticsearchは、ベクトル検索においてOpenSearchよりもデフォルトのままで2倍から12倍高速です]]></description>
    <content:encoded><![CDATA[<p><a href="https://www.elastic.co/search-labs/blog/elasticsearch-opensearch-vector-search-performance-comparison#up-to-12x-faster-out-of-the-box">まとめ：Elasticsearchは最大12倍高速</a> - Elasticでは、特にセマンティック検索/ベクトル検索のレルムにおけるElasticsearchとOpenSearch のパフォーマンスの違いを明確にしてほしいというリクエストをコミュニティから多数受けていました。そこで、このパフォーマンステストを実施して、明確でデータに基づく比較を提供しました。曖昧さはなく、ユーザーに伝えるための単純な事実です。結論としては、<strong>Elasticsearchはベクトル検索においてOpenSearchより最大12倍高速</strong>であるため、必要な計算リソースが少なくなります。これは、ElasticがLuceneを検索および取得ユースケースに最適なベクトルデータベースとして統合することに重点を置いていることを反映しています。</p><p>ベクトル検索は、特にAIや機械学習のフィールドで、類似性検索の方法に革命をもたらしています。ベクトル埋め込みモデルの採用が増えるにつれ、何百万もの高次元ベクトルを効率的に検索する機能が重要になっています。</p><p>ベクトルデータベースの実現方法に関して、ElasticとOpenSearchは著しく異なるアプローチを採用しています。Elasticは、Apache LuceneとElasticsearchがベクトル検索アプリケーションにおけるトップクラスの選択肢となるべく、これらの最適化に多額の投資を行ってきました。対照的に、OpenSearchは焦点を広げ、他のベクトル検索実装を統合し、Luceneの対象範囲を超えて模索を行っています。Elasticは戦略的にLuceneへと焦点を合わせており、弊社版Elasticsearchにおいて高度に統合されたサポートを提供できるため、各コンポーネントが他のコンポーネントの機能を補完および増幅できるという特徴が強化されています。</p><p>このブログでは、Elasticsearch 8.14とOpenSearch 2.14の詳細な比較を、異なる構成とベクトルエンジンを考慮して紹介します。このパフォーマンス分析では、Elasticsearch がベクトル検索操作に優れたプラットフォームであることが証明されました。今後追加予定の<a href="https://www.elastic.co/search-labs/blog/vector-similarity-computations-ludicrous-speed">機能により</a>その差は<a href="https://www.elastic.co/search-labs/blog/elasticsearch-lucene-vector-database-gains">さらに</a>拡大するでしょう。OpenSearchと比較すると、すべてのベンチマークトラックで優れた結果を示し、<strong>平均で2倍から12倍高速なパフォーマンスを実現しました</strong>。これは、<code>so_vector</code>（2Mベクトル、768D）、<code>openai_vector</code>（2.5Mベクトル、1536D）、および<code>dense_vector</code>（10Mベクトル、96D）を含む、さまざまなベクトル量と次元を使用したシナリオ全体で行われました。これらはすべて、Google Cloudで必要なインフラストラクチャーをプロビジョニングするためのTerraformスクリプトと、テストを実行するためのKubernetesマニフェストと共に、<a href="https://github.com/elastic/elasticsearch-opensearch-vector-performance">このリポジトリ</a>で利用可能です。</p><p>このブログで詳述されている結果は、<a href="https://www.elastic.co/blog/elasticsearch-opensearch-performance-gap">以前に公開され、サードパーティによって検証された調査</a>の結果を補完するものであり、最も一般的な検索分析操作（テキストクエリ、並べ替え、範囲、日付ヒストグラム、用語フィルタリング）では、ElasticsearchがOpenSearchよりも40%–140%高速であることが示されています。これに、別の差別化要因であるベクトル検索を追加します。</p><h2>デフォルトの設定で最大12倍高速</h2><p>4つのベクトルデータセットにわたる重点的なベンチマークには、さまざまなサイズ、次元、構成を考慮した近似KNN検索と正確なKNN検索の両方が含まれ、合計<code>40.189.820</code>のキャッシュされていない検索要求がありました。結果：<strong>Elasticsearchはベクトル検索においてOpenSearchより最大12倍高速である</strong>ため、必要な計算リソースが少なくなります。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt34b83c6eba3bcb6e/6a17d727dbb4ff18d6fb54fb/cdb26e91f085b90e9b12aeb8fee53b04d365ecae-1440x1156.webp" alt="p90平均" /><p>図1：ElasticsearchおよびOpenSearchにおけるさまざまなタスクについて、ANNと厳密なKNNをグループ化した図。</p><p><code>knn-10-100</code>のようなグループは、およびのKNN検索を意味します。HNSWベクトル検索では、はクエリベクトルから取得する最近傍の数を決定します。結果として検索する類似ベクトルの数を指定します。は各セグメントで取得する候補ベクトルの数を設定します。候補の数を増やすと精度は向上しますが、より多くの計算リソースが必要になります。</p><p>また、さまざまな量子化手法とエンジン固有の最適化を活用してテストしました。各トラック、タスク、ベクトルエンジンの詳細な結果を以下に示します。</p><h2>厳密なKNNと近似KNN</h2><p>さまざまなデータセットとユースケースを扱う場合、ベクトル検索に適したアプローチは異なります。このブログでは、<code>knn-10-100</code>のように<code>knn-*</code>と記載されているすべてのタスクは<strong>近似KNN</strong>を使用し、<code>script-score-*</code>は<strong>正確なKNN</strong>を参照していますが、それらの違いは何で、なぜ重要なのでしょうか。</p><p>基本的に、より大規模なデータセットを扱う場合、優れた拡張性のある近似K-最近傍法（ANN）が推奨されます。フィルタリング処理が必要な場合のある、より小規模なデータセットの場合、厳密なKNN法が最適です。</p><p>正確なKNNでは、ブルートフォース法を使用して、データセット内の1つのベクトルと他のすべてのベクトルとの間の距離を計算します。次に、これらの距離を順位付けして、の最も近い近傍を見つけます。この方法では正確な一致が保証されますが、大規模で高次元のデータセットでは拡張性の課題があります。ただし、正確なKNNが必要なケースは数多くあります。</p><ul><li><p><strong>再スコアリング</strong>：語彙検索または意味検索の後にベクトルベースの再スコアリングを行うシナリオでは、正確なKNNが不可欠です。例えば、製品検索エンジンでは、テキストクエリ（キーワードやカテゴリなど）に基づいて最初の検索結果をフィルタリングし、フィルタリングされたアイテムに関連付けられたベクトルを使用して、より正確な類似性評価を行うことができます。</p></li><li><p><strong>パーソナライゼーション</strong>：多数のユーザーを扱う際、それぞれが比較的少数（100万個など）の個別のベクトルで表される場合、ユーザー固有のメタデータ（例：user_id）でインデックスをソートし、ベクトルを使用したブルートフォーススコアリングが効率的になります。このアプローチにより、個々のユーザーの好みに合わせた正確なベクトル比較に基づいて、パーソナライズされたレコメンデーションやコンテンツ配信が可能になります。</p></li></ul><p>したがって、厳密なKNNでは、ベクトルの類似性に基づく最終的な順位付けと推奨が正確なものとなり、ユーザーの好みに合わせて調整されるようになります。</p><p>一方、近似KNN（またはANN）は、特に大規模で高次元のデータセットにおいて、厳密なKNNよりも高速かつ効率的にデータ検索を行う方法を採用しています。ANNは、クエリとすべての点の間の正確な最も近い距離を測定することにより計算やスケーリングの問題につながるブルートフォース的なアプローチではなく、特定の手法を使用してデータセットにおける検索可能なベクトルのインデックスと次元を効率的に再構築します。これにより若干の不正確さが生じる可能性がありますが、検索処理速度が大幅に向上するため、大規模なデータセットを処理するための効果的な代替手段となります。</p><p>このブログでは、<code>knn-*</code>として記載されているすべてのタスクは<code>knn-10-100</code>のように<strong>近似KNN</strong>を使用し、<code>script-score-*</code>は<strong>正確なKNN</strong>を参照します。</p><h2>テスト手法</h2><p>ElasticsearchとOpenSearchはBM25検索操作のAPIにおいては似ていますが、後者が前者のフォークであるため、フォーク後に導入されたベクトル検索には当てはまりません。OpenSearchはアルゴリズムに関してElasticsearchとは異なるアプローチを取り、<code>lucene</code>とは別に<code>nmslib</code>と<code>faiss</code>の2つのエンジンを導入しました。それぞれに特定の構成と制限があります（例：OpenSearchの<code>nmslib</code>では、多くのユースケースで不可欠な機能であるフィルターが許可されていません）。</p><p>3つのエンジンはいずれも、階層型ナビゲート可能スモールワールド（HNSW）アルゴリズムを使用しています。このアルゴリズムは近似最近傍を検索するのに効率的で、特に高次元データを扱う際に強力です。<code>faiss</code>は第2のアルゴリズム<code>ivf</code>もサポートしていますが、データセットの事前トレーニングが必要なため、ここではHNSWのみに焦点を当てます。HNSWの中心的な考え方は、データを複数の接続されたグラフのレイヤーに整理し、各レイヤーがデータセットの異なる粒度を表すことです。検索は最も粗いビューの最上位レイヤーから始まり、ベースレベルに到達するまで、より細かいレイヤーへと進みます。</p><p>公平なテストの場を確保するために、両方の検索エンジンは制御された環境内で同一の条件下でテストされました。適用された方法は、Elasticsearch、OpenSearch、Rally専用のノードプールを使用した<a href="https://www.elastic.co/blog/elasticsearch-opensearch-performance-gap#testing-methodology">以前に公開されたパフォーマンス比較</a>と似ています。<a href="https://github.com/elastic/elasticsearch-opensearch-vector-performance/blob/main/terraform/main.tf">terraformスクリプト</a>は（すべてのソースとともに）Kubernetesクラスターを提供するために利用可能です。</p><ul><li><p>Elasticsearch用の3台の<code>e2-standard-32</code>マシン（128GB RAM、32CPU）を持つ1つのNodeプール</p></li><li><p>OpenSearch用の3台の<code>e2-standard-32</code>マシン（128GB RAM、32CPU）を持つ1つのNodeプール</p></li><li><p>Rally用の2台の<code>t2a-standard-16</code>マシン（64GB RAM、16CPU）を持つ1つのNodeプール</p></li></ul><p>各「トラック」（またはテスト）を、異なるエンジン、異なる設定、異なるベクトルタイプを含む各設定に対して10回実行しました。各トラックには、トラックにより1000回から10000回まで繰り返すタスクがあります。ネットワークのタイムアウトなどでトラック内のタスクの1つが失敗した場合はすべてのタスクを無視したため、すべての結果は問題なく開始して終了したトラックを表します。すべてのテスト結果は統計的に検証されており、改善が偶然ではないことが保証されています。</p><h2>詳細な調査結果</h2><p>平均レイテンシではなく99パーセンタイルを使用して比較するのはなぜでしょうか。特定の地域の平均住宅価格の例を想像してみましょう。平均価格によれば高価な地域となっていても、よく調べてみると、ほとんどの住宅の価値ははるかに低く、わずかな高級物件が平均値を膨らませていることがわかるかもしれません。これは、平均価格がその地域の住宅価値の全範囲を正確に表現できない可能性があることを示しています。同じようなことが、応答時間についても言えます。平均値が重要な問題を隠してしまうことがあるのです。</p><h4>タスク</h4><ul><li><p>近似KNN（k:10 n:50）</p></li><li><p>近似KNN（k:10 n:100）</p></li><li><p>近似KNN（k:100 n:1000）</p></li><li><p>近似KNN（k:10 n:50、キーワードフィルター併用）</p></li><li><p>近似KNN（k:10 n:100、キーワードフィルター併用）</p></li><li><p>近似KNN（k:100 n:1000、キーワードフィルター併用）</p></li><li><p>近似KNN（k:10 n:100、インデックス作成と同時実行）</p></li><li><p>厳密なKNN（スクリプトスコア）</p></li></ul><h4>ベクトルエンジン</h4><ul><li><p><code>lucene</code> （ElasticsearchとOpenSearchにおいて、両方ともバージョン9.10）</p></li><li><p><code>faiss</code> （OpenSearchにおいて）</p></li><li><p><code>nmslib</code> （OpenSearchにおいて）</p></li></ul><h4>ベクトルの種類</h4><ul><li><p><code>hnsw</code> （ElasticsearchとOpenSearchにおいて）</p></li><li><p><code>int8_hnsw</code> Elasticsearch内（自動8ビット量子化を備えたHNSW：<a href="https://www.elastic.co/search-labs/blog/evaluating-scalar-quantization">リンク</a>）</p></li><li><p><code>sq_fp16 hnsw </code>OpenSearch内（自動16ビット量子化を備えたHNSW：<a href="https://opensearch.org/docs/2.14/search-plugins/knn/knn-vector-quantization#faiss-16-bit-scalar-quantization">リンク</a>）</p></li></ul><h4>すぐに使える同時セグメント検索</h4><p>ご存知かと思いますが、LuceneはJavaで記述された高性能なテキスト検索エンジンライブラリであり、Elasticsearch、OpenSearch、Solrなどの多くの検索プラットフォームのバックボーンとして機能します。Luceneは本質的に、データをセグメントに整理します。セグメントは本質的には自己完結型のインデックスであり、Luceneがより効率的に検索を実行できるようにします。そのため、Luceneベースの検索エンジンに検索を発行すると、検索はそれらのセグメントで順次または並行して実行されます。</p><p>OpenSearchは同時セグメント検索をオプションのフラグとして導入しましたが、デフォルトでは使用されません。特別なインデックス設定<code>index.search.concurrent_segment_search.enabled</code>を使用して有効にする必要があります。詳細は<a href="https://opensearch.org/docs/latest/search-plugins/concurrent-segment-search/">こちら</a>をご覧ください。ただし、いくつかの<a href="https://opensearch.org/docs/latest/search-plugins/concurrent-segment-search/#other-considerations">制限</a>があります。</p><p>一方、Elasticsearchは<a href="https://github.com/elastic/elasticsearch/pull/101230">すぐに使用できる状態</a>でセグメントを同時に検索するため、このブログでの比較では、さまざまなベクトルエンジンとベクトルタイプに加えて、さまざまな構成も考慮に入れます。</p><ul><li><p>Elasticsearch ootb：同時セグメント検索検索を備えたデフォルト設定のElasticsearch</p></li><li><p>OpenSearch ootb：同時セグメント検索が有効でない状態</p></li><li><p>OpenSearch css：同時セグメント検索が有効な状態</p></li></ul><p>それでは、テストした各ベクトルデータセットの詳細な結果を見てみましょう。</p><h2>250万ベクトル、1536次元（openai_vector）</h2><p>最も単純なトラックで、かつ次元の点でも最大の<a href="https://github.com/elastic/rally-tracks/edit/master/openai_vector">openai_vector</a>から始めます。これは、OpenAIの<a href="https://openai.com/blog/new-and-improved-embedding-model">text-embedding-ada-002モデル</a>を使用して生成された埋め込みで強化された<a href="https://huggingface.co/datasets/BeIR/nq">NQデータセット</a>を使用します。これは、近似KNNのみをテストし、タスクが5つしかないため、最も単純です。スタンドアロン（インデキシングなし）でのテストと、インデキシングと並行してのテストが可能で、単一クライアントと8つの同時クライアントを使用します。</p><h3>タスク</h3><ul><li><p><strong>standalone-search-knn-10-100-multiple-clients</strong>：8クライアントで250万ベクトルを同時に検索、k: 10およびn:100</p></li><li><p><strong>standalone-search-knn-100-1000-multiple-clients</strong>：8クライアントで250万ベクトルを同時に検索、k: 100およびn:1000</p></li><li><p><strong>standalone-search-knn-10-100-single-client</strong>：単一クライアントで250万ベクトルを検索、k: 10およびn:100</p></li><li><p><strong>standalone-search-knn-100-1000-single-client</strong>：単一クライアントで250万ベクトルを検索、k: 100およびn:1000</p></li><li><p><strong>parallel-documents-indexing-search-knn-10-100</strong>：250万ベクトルを検索しながら追加の10万ドキュメントをインデキシング、k:10およびn:100</p></li></ul><p>p99の平均パフォーマンスの概要は次のとおりです。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltf5791848eb0c12fb/6a17d72925daab9cae08a09e/eea0b2b49c690baada3e09d6968e513bfffe51a9-1440x318.webp" alt="openai_vectorテーブル" /><p>ここで、 :10、:100でベクトル検索とインデックス作成（つまり読み取り+書き込み）を実行した場合、ElasticsearchはOpenSearchよりも<strong>3～8倍高速</strong>であり、kとnが同一のインデックス作成なしの場合には<strong>2～3倍高速</strong>であることがわかりました。:100 および:1000（<em>standalone-search-knn-100-1000-single-client</em>および<em>standalone-search-knn-100-1000-multiple-clients</em>）の場合、Elasticsearchは平均して OpenSearch より<strong>2倍から7倍</strong>高速です。</p><p>詳細な結果には、比較された具体的なケースとベクトルエンジンが示されています。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltdbe41d3187ced7ec/6a17d72a445de951c44cff4c/a7a761ed631d3e6211beb83d9d93d752d10123c9-1440x1728.webp" alt="openai_vector" /><h4>リコール</h4><p></p><p>knn-recall-10-100</p><p>knn-recall-100-1000</p><p>Elasticsearch-8.14.0@lucene-hnsw</p><p>0.969485</p><p>0.995138</p><p>Elasticsearch-8.14.0@lucene-int8_hnsw</p><p>0.781445</p><p>0.784817</p><p>OpenSearch-2.14.0@lucene-hnsw</p><p>0.96519</p><p>0.995422</p><p>OpenSearch-2.14.0@faiss</p><p>0.984154</p><p>0.98049</p><p>OpenSearch-2.14.0@faiss-sq_fp16</p><p>0.980012</p><p>0.97721</p><p>OpenSearch-2.14.0@nmslib</p><p>0.982532</p><p>0.99832</p><h2>1,000万ベクトル、96次元（dense_vector）</h2><p><a href="https://github.com/elastic/rally-tracks/tree/master/dense_vector">dense_vector</a>には、1,000万ベクトルと96次元があります。これは<a href="https://big-ann-benchmarks.com/">Yandex DEEP1B</a>画像データセットに基づいています。データセットは、「sample data」ファイル<code>learn.350M.fbin</code>の最初の1,000万ベクトルから作成されます。検索操作では、「query data」ファイルクエリのベクトルを使用します。<code>public.10K.fbin</code>。</p><p>ElasticsearchとOpenSearchの両方がこのデータセットで非常に優れたパフォーマンスを発揮します。特に、通常読み取り専用のインデックスで実行される<a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-forcemerge.html">force merge</a>の後では顕著です。これは、インデックスをデフラグして検索用の単一の「テーブル」を作成するのと似ています。</p><h3>タスク</h3><p>各タスクは100リクエストでウォームアップされ、その後1000リクエストが測定されます。</p><ul><li><p><strong>knn-search-10-100</strong>: 1000万ベクトルを検索、k: 10およびn:100</p></li><li><p><strong>knn-search-100-1000</strong>：1000万ベクトルを検索、k: 100およびn:1000</p></li><li><p><strong>knn-search-10-100-force-merge</strong>：強制マージ後の1000万ベクトルの検索、k: 10およびn:100</p></li><li><p><strong>knn-search-100-1000-force-merge</strong>：強制マージ後の1000万ベクトルの検索、k: 100およびn:1000</p></li><li><p><strong>knn-search-100-1000-concurrent-with-indexing</strong>：1000万ベクトルを検索しながら<a href="https://github.com/elastic/rally-tracks/blob/master/dense_vector/challenges/default.json#L76C36-L76C37">データセットの5%を</a>更新、k: 100およびn:1000</p></li><li><p><strong>script-score-query</strong>：<a href="https://github.com/elastic/rally-tracks/blob/master/dense_vector/queries.json">特定の2000ベクトル</a>の正確なKNN検索。</p></li></ul><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt4629d06af85eb96c/6a17d72c6864a423e7b685dc/174995e0a2156d86359cdb7aa446dfaae6312ea4-1440x316.webp" alt="dense_vector" /><p>ElasticsearchとOpenSearchはどちらも近似KNNに対して良好なパフォーマンスを示しました。<em>knn-search-100-1000-force-merge</em>および<em>knn-search-10-100-force-merge</em>でインデックスが結合されている場合（つまり、セグメントが1つだけの場合）、<code>nmslib</code>および<code>faiss</code>を使用すると、いずれも約15ミリ秒で非常に近い値であるにもかかわらず、OpenSearchのパフォーマンスは他よりも優れています。</p><p>ただし、インデックスに複数のセグメントがある場合（インデックスがドキュメントの更新を受け取る典型的な状況）において、<em>knn-search-10-100</em>と<em>knn-search-100-1000</em>では、Elasticsearchは遅延を約7ミリ秒と16ミリ秒に保ちますが、他のすべてのOpenSearchエンジンはより遅くなります。</p><p>また、インデックスが検索され、同時に書き込まれる場合（<em>knn-search-100-1000-concurrent-with-indexing</em>）、Elasticsearchはレイテンシを15ミリ秒以下に保守し（13.8ミリ秒）、OpenSearchの出荷時（49.3ミリ秒）よりも<strong>4倍近く高速</strong>であり、同時セグメント検索が有効な場合でも（17.9ミリ秒）、依然として高速ですが、有意差があるほどではありません。</p><p>正確なKNNに関しては、その差はさらに大きくなります。Elasticsearch は OpenSearch よりも<strong>6 倍高速です</strong>（約 260 ミリ秒対約 1600 ミリ秒）。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt254f43bcaa3dbfc2/6a17d72ddbb4ffc780fb54ff/17aec6be31117440bc4d1f99984aed95df1c4f6b-1440x1728.webp" alt="dense_vector" /><h4>リコール</h4><p></p><p>knn-recall-10-100</p><p>knn-recall-100-1000</p><p>Elasticsearch-8.14.0@lucene-hnsw</p><p>0.969843</p><p>0.996577</p><p>Elasticsearch-8.14.0@lucene-int8_hnsw</p><p>0.775458</p><p>0.840254</p><p>OpenSearch-2.14.0@lucene-hnsw</p><p>0.971333</p><p>0.996747</p><p>OpenSearch-2.14.0@faiss</p><p>0.9704</p><p>0.914755</p><p>OpenSearch-2.14.0@faiss-sq_fp16</p><p>0.968025</p><p>0.913862</p><p>OpenSearch-2.14.0@nmslib</p><p>0.9674</p><p>0.910303</p><h2>200万ベクトル、768次元（so_vector）</h2><p>この<a href="https://github.com/elastic/rally-tracks/tree/master/so_vector">トラック</a>、<code>so_vector</code>、は2022年4月21日にダウンロードされた<a href="https://archive.org/download/stackexchange/stackoverflow.com-Posts.7z">StackOverflowの投稿のダンプ</a>から派生しています。質問文書のみが含まれており、回答を表す文書はすべて削除されています。各質問タイトルは、文変換モデル<a href="https://huggingface.co/sentence-transformers/multi-qa-mpnet-base-cos-v1">multi-qa-mpnet-base-cos-v1</a>を使用してベクトルにエンコードされました。このデータセットには最初の200万件の質問が含まれています。</p><p>前のトラックとは異なり、ここでの各ドキュメントには、フィルタリングとハイブリッド検索を備えた近似KNNなどのテスト機能をサポートするためのベクトル以外のフィールドも含まれています。OpenSearchの<code>nmslib</code>は<a href="https://opensearch.org/docs/latest/search-plugins/knn/filter-search-knn/#k-nn-search-with-filters">フィルターをサポートしていない</a>ため、このテストには含まれていません。</p><h3>タスク</h3><p>各タスクは100件のリクエストに対してウォームアップされ、その後100件のリクエストが測定されます。テストには16種類の検索タイプ*2つの異なるk値*3つの異なるn値が含まれているため、簡潔にするためにタスクがグループ化されていることに注意してください。</p><ul><li><p><strong>knn-10-50</strong>：フィルターなしで200万ベクトルを検索、k:10およびn:50</p></li><li><p><strong>knn-10-50-filtered</strong>：200万ベクトルを<a href="https://github.com/elastic/rally-tracks/blob/master/so_vector/operations/default.json">フィルターを使用して</a>検索、k:10およびn:50</p></li><li><p><strong>knn-10-50-after-force-merge</strong>：200万ベクトルを強制マージ後にフィルターを使用して検索、k:10およびn:50</p></li><li><p><strong>knn-10-100</strong>：フィルターなしで200万ベクトルを検索、k:10およびn:100</p></li><li><p><strong>knn-10-100-filtered</strong>：200万ベクトルを<a href="https://github.com/elastic/rally-tracks/blob/master/so_vector/operations/default.json">フィルターを使用して</a>検索、k:10およびn:100</p></li><li><p><strong>knn-10-100-after-force-merge</strong>：200万ベクトルを強制マージ後にフィルターを使用して検索、k:10およびn:100</p></li><li><p><strong>knn-100-1000</strong>：フィルターなしで200万ベクトルを検索、k:100およびn:1000</p></li><li><p><strong>knn-100-1000-filtered</strong>：200万ベクトルを<a href="https://github.com/elastic/rally-tracks/blob/master/so_vector/operations/default.json">フィルターを使用して</a>検索、k:100およびn:1000</p></li><li><p><strong>knn-100-1000-after-force-merge</strong>：200万ベクトルを強制マージ後にフィルターを使用して検索、k:100およびn:1000</p></li><li><p><strong>exact-knn</strong>：<a href="https://github.com/elastic/rally-tracks/blob/master/so_vector/operations/default.json#L56">フィルターあり、フィルターなしの</a>正確なKNN検索。</p></li></ul><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt8b44e1306af35877/6a17d72f577262aca11bca3e/d4ed2982d55370cad4b2048b23ce97caa55017c0-1440x316.webp" alt="so_vectorテーブル" /><p>このテストでは、ElasticsearchはOpenSearchよりも<strong>一貫して高速で</strong>あり、OpenSearchの方が高速なのは2つのケースのみで、その差はそれほど大きくありません（<em>knn-10-100</em>と<em>knn-100-1000</em>）。<em>knn-10-50</em>、<em>knn-10-100</em>、<em>knn-100-1000</em>をフィルターと組み合わせて使用するタスクでは、最大<strong>7倍</strong>（112ミリ秒対803ミリ秒）の差が見られます。</p><p>当然ながら、<em>knn-10-50-after-force-merge</em>、<em>knn-10-100-after-force-merge</em>、knn-100-1000-after-force-mergeで証明されているように、両方のソリューションのパフォーマンスは「<em>強制マージ」後に均等になるようです。</em>これらのタスクでは、 <code>faiss</code>の方が高速です。</p><p>Exact KNNのパフォーマンスは今回も大きく異なり、ElasticsearchはOpenSearchより<strong>13倍高速</strong>です（約385ミリ秒対約5262ミリ秒）。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltf80ccc7c2df84559/6a17d7314b055de00d43203f/615cb9228eb05ddd2e9512b3a6a5bc88d4088a1a-1440x1440.webp" alt="so_vector" /><h4>リコール</h4><p></p><p>knn-recall-10-100</p><p>knn-recall-100-1000</p><p>knn-recall-10-50</p><p>Elasticsearch-8.14.0@lucene-hnsw</p><p>1</p><p>1</p><p>1</p><p>Elasticsearch-8.14.0@lucene-int8_hnsw</p><p>1</p><p>0.986667</p><p>1</p><p>OpenSearch-2.14.0@lucene-hnsw</p><p>1</p><p>1</p><p>1</p><p>OpenSearch-2.14.0@faiss</p><p>1</p><p>1</p><p>1</p><p>OpenSearch-2.14.0@faiss-sq_fp16</p><p>1</p><p>1</p><p>1</p><p>OpenSearch-2.14.0@nmslib</p><p>0.9674</p><p>0.910303</p><p>0.976394</p><h2>ElasticsearchとLuceneが明確な勝者に</h2><p>Elasticでは、Apache LuceneとElasticsearchを絶え間なく革新し、RAG（Retrieval-Augmented Generation）を含む検索および取得のユースケースに最適なベクトルデータベースを提供できるようにしています。最近の進歩により、パフォーマンスが大幅に改善され、ベクトル検索が<a href="https://search-labs.elastic.co/search-labs/blog/elasticsearch-lucene-vector-database-gains">以前よりも高速かつスペース効率的</a>になりました。これはLucene 9.10からの成果を基に構築されています。このブログでは、最新バージョンを比較すると、ElasticsearchがOpenSearchよりも最大12倍高速であることを示す調査を紹介しました。</p><p>両方の製品が同じバージョンのLucene（<a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/release-notes-8.14.0.html">Elasticsearch 8.14リリースノート</a>および<a href="https://github.com/opensearch-project/OpenSearch/blob/2.14/release-notes/opensearch.release-notes-2.14.0.md">OpenSearch2.14 リリースノート</a>）を使用していることは注目に値します。</p><p>Elasticのイノベーションのペースは、オンプレミスおよびElastic Cloudのお客様だけでなく、<a href="https://www.elastic.co/search-labs/blog/stateless-your-new-state-of-find-with-elasticsearch">ステートレスプラットフォーム</a>を使用しているお客様にもさらに多くの成果をもたらします。<a href="https://www.elastic.co/search-labs/blog/int4-scalar-quantization-in-lucene">int4へのスカラー量子化</a>のサポートなどの機能は、<a href="https://www.elastic.co/search-labs/blog/evaluating-scalar-quantization">int8のテスト</a>と同様に、顧客が再現率を大幅に低下させることなくこれらの手法を利用できるように、厳格なテストを経て提供されます。</p><p>AIおよび機械学習アプリケーションの急増により、ベクトル検索の効率は現代の検索エンジンでは交渉の余地のない機能になりつつあります。大量で複雑なベクトルデータの要求に対応できる強力な検索エンジンを探している組織にとって、Elasticsearchは決定的な答えです。</p><p>確立されたプラットフォームを拡張する場合でも、新しいプロジェクトを開始する場合でも、ベクトル検索のニーズに合わせてElasticsearchを統合することは、具体的かつ長期的なメリットをもたらす戦略的な動きです。実証済みのパフォーマンス上の優位性を備えたElasticsearchは、検索における次のイノベーションの波を支える態勢が整っています。</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/elasticsearch-opensearch-vector-search-performance-comparison</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/elasticsearch-opensearch-vector-search-performance-comparison</guid>
    <category><![CDATA[Vector Database]]></category>
    <category><![CDATA[Lucene]]></category>
    <dc:creator><![CDATA[Ugo Sangiorgi]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5d70b25967c2194e/6a17d732b1e11383f879f0ca/13c3c0053e2968fb835ba2f90f34bec3a011b5c0-880x592.webp" length="0" type="image/webp"/>
    <pubDate>Wed, 26 Jun 2024 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[ベクトル類似度測定とスコアリング]]></title>
    <description><![CDATA[Elasticsearchにおけるベクトル類似度尺度とスコアリングについて、L1距離、L2距離、コサイン類似度、ドット積類似度、最大内積類似度などを含めて検討します。]]></description>
    <content:encoded><![CDATA[<p>フリーテキストを検索する必要が生じ、Ctrl+F / Cmd+F では不十分になった場合、通常、次に思い浮かぶ論理的な選択肢は語彙検索エンジンの使用です。語彙検索エンジンは、検索対象のテキストを分析し、検索時に一致可能な用語にトークン化することに優れていますが、インデックス付けおよび検索対象のテキストの真の意味を理解し、意味を成すという点では、通常、不十分です。</p><p>まさにここでベクター検索エンジンが活躍します。同じテキストをインデックス化して、そのテキストが表す意味と、類似または関連する意味を持つ他の概念との関係の両方に基づいて検索できるようにすることができます。</p><p>このブログでは、ベクトルがテキストの意味を伝えるための優れた数学的概念である理由について簡単に触れたいと思います。次に、近隣ベクトルの検索、つまり類似の意味を持つベクトルの検索と、それらのスコア付けの方法について、Elasticsearch がサポートするさまざまな類似性テクニックについて詳しく説明します。</p><h2>ベクトル埋め込みとは何ですか？</h2><p>この記事では、ベクトル埋め込みの複雑な部分については詳しく説明しません。このトピックをさらに詳しく調べたい場合、または続行する前に入門書が必要な場合は、<a href="https://www.elastic.co/jp/what-is/vector-embedding">次のガイド</a>を確認することをお勧めします。</p><p>簡単に言えば、ベクトル埋め込みは機械学習プロセスを通じて得られる（例えばあらゆる種類の非構造化入力データ (生のテキスト、画像、ビデオ、音声など) を、意味と関係性を持つ数値データに変換する AI (人工知能) です。非構造化データの種類によって、各データの種類を「理解」するようにトレーニングされたさまざまな種類の機械学習モデルが必要です。</p><p>各ベクトルは、特定のデータ部分を多次元空間内の点として配置し、その位置はモデルがデータを特徴付けるために使用する一連の機能を表します。次元の数は機械学習モデルによって異なりますが、通常は数百から数千の範囲です。たとえば、 <a href="https://platform.openai.com/docs/guides/embeddings">OpenAI Embeddings モデルは</a>1536 次元を誇りますが、 <a href="https://docs.cohere.com/reference/embed">Cohere Embeddings モデルは</a>382 から 4096 次元の範囲になります。Elasticsearch の dense_vector フィールド タイプは、最新リリース時点で最大 4096 次元をサポートします。</p><p>ベクトル埋め込みの本当の特徴は、同様の意味を共有するデータ ポイントが空間内で互いに接近していることです。もう 1 つの興味深い点は、ベクトル埋め込みがデータ ポイント間の関係を捉えるのにも役立つことです。</p><h2>ベクトルをどのように比較するのでしょうか?</h2><p>非構造化データは機械学習モデルによって、多数の次元に沿ったデータの類似性を捉えるベクトル埋め込みに細分化されることがわかったので、次にそれらのベクトルのマッチングがどのように機能するかを理解する必要があります。答えは非常に簡単であることがわかりました。</p><p>互いに<strong>近い</strong>ベクトル埋め込みは<strong>、意味的に類似した</strong>データ部分を表します。したがって、ベクトル データベースをクエリする場合、検索入力 (画像、テキストなど) は、すべての非構造化データのインデックス作成に使用されたのと同じ機械学習モデルを使用して、まずベクトル埋め込みに変換され、最終的な目標はそのクエリ ベクトルに<strong>最も近い隣接ベクトル</strong>を見つけることです。したがって、必要なのは、クエリ ベクトルと、データベースにインデックスが付けられた既存のすべてのベクトルとの間の「距離」または「類似性」を測定する方法を見つけることだけです。とても簡単です。</p><h2>距離、類似性、スコアリング</h2><p>幸いなことに、ベクトル演算のおかげで、2 つのベクトル間の距離または類似性を測定することは簡単に解決できる問題です。それでは、Elasticsearch でサポートされている最も人気のある距離と類似度の関数を見てみましょう。警告、この先は数学です!</p><p>始める前に、スコアリングについて簡単に見てみましょう。実際のところ、Lucene ではスコアは正の値のみが許可されます。これから紹介するすべての距離関数と類似度関数は、2 つのベクトルがどれだけ近いか、または類似しているかの尺度を生成しますが、それらの生の数値は負になる可能性があるため、スコアとして使用するのに適することはほとんどありません。このため、最終スコアは、スコアが正になり、スコアが大きいほどランキングが高くなる（つまり、ベクトルが近い）ように、距離または類似度の値から導き出す必要があります。</p><h3>L1距離</h3><p>2 つのベクトルとの L1 距離 (マンハッタン距離とも呼ばれます) は、それらのすべての要素のペアワイズ絶対差を合計することによって測定されます。明らかに、距離が小さいほど、2つのベクトルは近くなります。L1距離の式（1）は、以下に示すように非常に単純です。</p><p>視覚的に、L1 距離は以下の図 (赤) のように表すことができます。</p><p>次の2つのベクトルとのL1距離を計算するととなる。</p><p><strong>重要:</strong> L1<a href="https://www.elastic.co/jp/guide/en/elasticsearch/reference/current/query-dsl-script-score-query.html#vector-functions-l1"> </a>距離関数は、<code>script_score</code> DSL クエリを使用した<a href="https://www.elastic.co/jp/guide/en/elasticsearch/reference/8.11/dense-vector.html#dense-vector-params"> 正確なベクトル検索(別名、ブルート</a> フォース検索) でのみサポートされ、<a href="https://www.elastic.co/jp/guide/en/elasticsearch/reference/8.11/knn-search.html#approximate-knn"><code>knn</code></a><a href="https://www.elastic.co/jp/guide/en/elasticsearch/reference/8.11/knn-search.html#approximate-knn"> 検索オプション</a> または<a href="https://www.elastic.co/jp/guide/en/elasticsearch/reference/current/query-dsl-knn-query.html"><code>knn</code></a><a href="https://www.elastic.co/jp/guide/en/elasticsearch/reference/current/query-dsl-knn-query.html"> DSL クエリ を使用した 近似</a> kNN 検索 ではサポートされないことに注意してください。</p><h3>L2距離</h3><p>2 つのベクトルとの L2 距離 (ユークリッド距離とも呼ばれます) は、まずそれらのすべての要素のペアワイズ差の 2 乗を合計し、次にその結果の平方根をとることによって測定されます。基本的には 2 点間の最短経路です。L1と同様に、距離が小さいほど、2つのベクトルは近くなります。</p><p>L2 距離は、下の画像で赤で示されています。</p><p> 距離に使用したのと同じ 2 つのサンプル ベクトル と 距離を として計算できるようになります。</p><p>スコアリングに関しては、2 つのベクトル間の距離が小さいほど、それらのベクトルは近い (つまり、類似している) と言えます。したがって、スコアを導き出すには、距離の測定を逆転させて、最小の距離で最高のスコアが得られるようにする必要があります。L2距離を用いた場合のスコアの計算方法は、以下の式（3）のようになります。</p><p>前の例のサンプルベクトルを再利用すると、スコアはになります。互いに非常に近い 2 つのベクトルのスコアは 1 に近づきますが、互いに非常に離れた 2 つのベクトルのスコアは 0 に近づく傾向があります。</p><p>L1 距離関数と L2 距離関数についてまとめると、これらを比較する良い例えは、A と B をニューヨーク市マンハッタンの 2 つの建物として考えることです。A から B まで行くタクシーは L1 パス (街路や大通り) に沿って走行する必要がありますが、鳥はおそらく L2 パス (直線) を使用するでしょう。</p><h3>コサイン類似度</h3><p>L1 および L2 とは対照的に、コサイン類似度は 2 つのベクトルと間の距離を測定するのではなく、それらの相対的な角度、つまり両方がほぼ同じ方向を向いているかどうかを測定します。類似度が高いほど、2 つのベクトル間の角度が小さくなり、したがって、それらのベクトルは「近く」なり、伝えられる意味は「似ている」ことになります。</p><p>これを説明するために、野外でそれぞれ異なる方向を見ている 2 人の人物を考えてみます。下の図では、青い人物はベクトルで示される方向を向いており、赤い人物はベクトルの方向を向いています。視線を同じ方向に向けるほど（つまり、ベクトルが近づくほど）、青と赤の領域で表された視野の重なり合う部分が大きくなります。視野がどの程度重なるかがコサイン類似度です。ただし、人物 B は人物 A よりも遠くを見ていることに注意してください (つまり、ベクトルが長い)。人 B は地平線の遠くの山を眺めているかもしれませんが、人 A は近くの木を眺めているかもしれません。コサイン類似度の場合、角度だけが関係するため、コサイン類似度は役割を果たしません。</p><p>それでは、コサイン類似度を計算してみましょう。式（4）は非常に単純で、分子は2つのベクトルのドット積で構成され、分母にはベクトルの大きさ（つまり長さ）の積が含まれます。</p><p>と間のコサイン類似度は、それらの間の角度の尺度として以下の画像に示されています (赤で表示)。</p><p>これらのコサイン類似度値が具体的に何を意味するのかを説明するために、少し回り道をしてみましょう。下のコサイン関数を示す画像に見られるように、値は常に範囲内で振動します。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltc0e4eb98ebb64cf3/6a17da287b54f983818b3783/e31145284b8c27d0bd9e3d2831f811388134fd83-1188x272.png" alt="余弦関数" /><p>2 つのベクトルが類似しているとみなされるためには、その角度が可能な限り鋭角で、理想的には角度に近く、完全な類似度がになる必要があることに注意してください。つまり、ベクトルは...</p><ol><li><p>...互いに<strong>近づくと</strong>、角度の余弦は近づきます（つまり、 に近づきます）。</p></li></ol><ol><li><p>...<strong>無関係ですが</strong>、角度の余弦は近づきます（つまり、 に近づきます）。</p></li></ol><ol><li><p>...<strong>反対に</strong>、その角度の余弦はに近づきます（つまり、 に近づきます）。</p></li></ol><p>2つのベクトル間のコサイン類似度を計算する方法がわかり、結果の値をどのように解釈するかもよくわかったので、同じサンプルベクトルとを再利用し、先ほど見た式（4）を使用してコサイン類似度を計算できます。</p><p>コサイン類似度は となり、これは よりも に近いため、 2 つのベクトルは<strong> 多少類似して</strong> いる、つまり完全に類似しているわけではないが、完全に無関係でもなく、明らかに反対の意味を持っていないことを意味します。</p><p>任意のコサイン類似度値から正のスコアを導き出すには、次の式（5）を使用する必要があります。この式は、 範囲内で振動するコサイン類似度値をの範囲内のスコアに変換します。</p><p>したがって、サンプルベクトルとのスコアは次のようになります:  。</p><h3>ドット積類似度</h3><p>コサイン類似度の欠点の 1 つは、2 つのベクトル間の角度のみが考慮され、大きさは考慮されないことです。つまり、2 つのベクトルがほぼ同じ方向を指していても、一方が他方よりもはるかに長い場合、両方とも類似していると見なされます。ドット積類似度 (スカラー類似度または内積類似度とも呼ばれる) は、ベクトルの角度と大きさの両方を考慮することで類似度を改善し、より正確な類似度メトリックを提供します。ベクトルの大きさを無関係にするために、ドット積類似性では、まずベクトルを正規化する必要があります。そのため、最終的には単位長さ 1 のベクトルのみを比較することになります。</p><p>先ほどと同じ 2 人の人物でもう一度これを説明してみます。ただし今回は、彼らを円形の部屋の中央に配置し、彼らの視界の範囲 (つまり、部屋の半径) がまったく同じになるようにします。コサイン類似度と同様に、同じ方向を向くほど（つまり、ベクトルが近づくほど）、視野の重なり合う部分が多くなります。しかし、コサイン相似とは逆に、両方のベクトルの長さは同じで、両方の領域の表面積も同じなので、2 人の人物は同じ距離にあるまったく同じ画像を見ていることになります。これら 2 つの領域がどの程度重なり合っているかは、それらのドット積の類似性を示します。</p><p>ドット積の類似度の式を紹介する前に、ベクトルを正規化する方法を簡単に見てみましょう。これは非常に簡単で、2 つの簡単な手順で実行できます。</p><ol><li><p>ベクトルの大きさを計算する</p></li><li><p>各成分を1で得られた大きさで割ります。</p></li></ol><p>例として、ベクトルを取ります。コサイン類似度を確認したときに見たように、その大きさを計算できます。つまり、 。次に、ベクトルの各成分をその大きさで割ると、次の正規化ベクトルが得られます。</p><p>2番目のベクトルに対して同じプロセスを実行すると、次の正規化されたベクトルが生成されます。</p><p>ドット積類似度式を導くために、正規化されたベクトルと間のコサイン類似度を式(4)を用いて以下のように計算することができる。</p><p>そして、両方の正規化されたベクトルの大きさがなったので、ドット積の相似式（6）は単純に、両方の正規化されたベクトルのドット積になります。</p><p>下の図では、正規化されたベクトルとを示しており、1 つのベクトルを他のベクトルに投影することで、それらのドット積の類似性を示しています (赤で表示)。</p><p>新しい式（6）を使用すると、2つの正規化されたベクトルのドット積類似度を計算することができ、当然のことながら、コサインの類似度とまったく同じ類似度値が得られます。</p><p>ドット積類似性を活用する場合、ベクトルに浮動小数点値が含まれているかバイト値が含まれているかに応じて、スコアの計算方法が異なります。前者の場合、スコアはコサイン類似度の場合と同じ方法で以下の式（7）を使用して計算されます。</p><p>しかし、ベクトルがバイト値で構成されている場合、スコアリングの計算方法は以下の式（8）に示すように少し異なります。ここで、 ベクトルの次元数です。</p><p>また、正確なスコアを生成するための制約の 1 つは、クエリ ベクトルを含むすべてのベクトルの長さが同じである必要があるが、必ずしも 1 である必要はないということです。</p><h3>最大内積類似度</h3><p>リリース 8.11 以降では、ベクトルを正規化する必要がないという点で、ドット積類似度よりも制約が少ない新しい類似度関数があります。この主な理由は<a href="https://www.elastic.co/jp/search-labs/blog/lucene-bringing-maximum-inner-product-to-lucene">以下の記事</a>で詳しく説明されていますが、簡単にまとめると、特定のデータセットはベクトルの正規化にあまり適しておらず (例: <a href="https://www.elastic.co/jp/search-labs/blog/elasticsearch-cohere-embeddings-support">Cohere 埋め込み</a>)、正規化すると関連性の問題が発生する可能性があります。</p><p>最大内積類似度を計算する式は、ドット積の式とまったく同じです（6）。変わるのは、以下の式(9)に示すように、類似性が正か負かによって式が変わる区分関数を使用して最大内積類似度をスケーリングしてスコアを計算する方法です。</p><p>この区分関数は、 区間内のすべての負の最大内積類似度値と、 区間内のすべての正の値をスケーリングします。</p><h2>要約すれば</h2><p>数学的に言えば、これはかなり大変な話ですが、役に立つと思われるポイントをいくつか挙げておきます。</p><p>どの類似度関数を使用できるかは、最終的にはベクトル埋め込みが正規化されているかどうかによって決まります。ベクトルがすでに正規化されている場合、またはデータ セットがベクトルの正規化に依存しない場合 (つまり、関連性が損なわれない場合)、ベクトルを正規化してドット積類似度を使用できます。これは、各ベクトルの長さを計算する必要がないため、コサイン積類似度よりもはるかに高速に計算できるためです。何百万ものベクトルを比較する場合、それらの計算量は非常に多くなることがあります。</p><p>ベクトルが正規化されていない場合は、次の 2 つのオプションがあります。</p><ol><li><p>ベクトルを正規化できない場合はコサイン類似度を使用する</p></li><li><p>ベクトルの大きさが意味を持つため、それをスコアリングに反映させたい場合には、新しい最大内積類似度を使用します（例：Cohere埋め込み）。</p></li></ol><p>この時点で、ベクトル埋め込み間の距離または類似性を計算し、そのスコアを導き出す方法が理解できるはずです。この記事がお役に立てれば幸いです。</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/vector-similarity-measures-and-scoring</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/vector-similarity-measures-and-scoring</guid>
    <category><![CDATA[Vector Database]]></category>
    <dc:creator><![CDATA[Valentin Crettaz]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte5a3e9d39849d3ed/6a17da31a2929960a3d02b3f/4d9e89678798b9de68357b5cc06dbbd8b9c6e5e8-1440x823.webp" length="0" type="image/webp"/>
    <pubDate>Mon, 13 May 2024 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[AI盗作：Elasticsearchによる盗作検出]]></title>
    <description><![CDATA[ここでは、NLP モデルと Vector Search のユースケースに焦点を当て、Elasticsearch を使用して AI の盗用をチェックする方法を説明します。]]></description>
    <content:encoded><![CDATA[<p>盗作には、<strong>直接的な</strong>盗用（コンテンツの一部または全体をコピーする）と<strong>言い換えれば言い換え</strong>（著者の作品のいくつかの単語やフレーズを変更して言い換える）があります。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltb6e139760e56ec98/6a171147dc55de0ad2e00edf/5d7073187fda829438aeec8d3a1194a5bea2ba57-1440x347.png" alt="" /><p>インスピレーションと言い換えには違いがあります。コンテンツを読んでインスピレーションを得て、たとえ同じような結論に達したとしても、自分の言葉でそのアイデアを探求することは可能です。</p><p>盗作は長い間議論されてきたテーマですが、コンテンツの制作と公開が加速するにつれて、盗作は関連性を保ち、継続的な課題となっています。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltc2af1678cb04506b/6a171149cf4f251c2ab2d257/a0b9a98d729db09dae0a79315c001e6763c12704-1400x1016.png" alt="" /><p>この課題は、盗作チェックが頻繁に行われる書籍、学術研究、司法文書に限定されません。それは新聞やソーシャルメディアにも及ぶ可能性があります。</p><p>情報が豊富で出版物に簡単にアクセスできるようになった今、スケーラブルなレベルで盗作を効果的にチェックするにはどうすればよいでしょうか?</p><p>大学、政府機関、企業はさまざまなツールを使用していますが、単純な<a href="https://www.elastic.co/search-labs/lexical-and-semantic-search-with-elasticsearch">語彙検索で</a>直接的な盗用を効果的に検出できる一方で、<strong>言い換えられたコンテンツを特定することが主な課題です。</strong></p><h2>生成AIによる盗作検出</h2><p>生成 AI によって新たな課題が生まれます。AI によって生成されたコンテンツは、コピーされた場合に盗作とみなされますか?</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt8a1ed1b6fa3f1a56/6a17114b4a531bc40736aa69/9345b28d6d27c37469bc38e823c41780b4eabfe5-1440x875.png" alt="" /><p>たとえば、 <a href="https://openai.com/">OpenAI の</a><a href="https://openai.com/policies/terms-of-use">利用規約</a>では、OpenAI はユーザー向けに API によって生成されたコンテンツに対する著作権を主張しないことが規定されています。この場合、生成 AI を使用する個人は、生成されたコンテンツを引用なしで自由に使用できます。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltbc2e08ab5b808e38/6a17114dab7f086cbadb9f93/e1f415f69a247666f02ddc81468944920c874cd7-968x814.png" alt="" /><p>しかし、効率性を向上させるために生成 AI を使用することが受け入れられるかどうかは、依然として議論の余地があります。</p><p>OpenAIは盗作検出に貢献しようと<a href="https://huggingface.co/roberta-base-openai-detector">検出モデル</a>を開発したが、後にその精度が十分に高くないことを認めた。</p><p><em>「これは単独の検出では精度が十分ではなく、より効果を上げるにはメタデータベースのアプローチ、人間の判断、一般の教育と組み合わせる必要があると考えています。」</em></p><p>課題は依然として残っていますが、利用できるツールが増えたことにより、言い換えられたコンテンツや AI コンテンツの場合でも盗作を検出するための選択肢が増えています。</p><h2>Elasticsearchで盗作を検出する</h2><p>これを認識して、このブログでは、メタデータ検索を超えて、自然言語処理 (NLP) モデルとベクトル検索、盗作検出を使用したもう 1 つのユース ケースを検討します。</p><p>これは<a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/plagiarism-detection-with-elasticsearch/plagiarism_detection_es.ipynb"> Python の例</a> で実証されており、NLP 関連の記事を含む<a href="https://www.sbert.net/"> SentenceTransformers</a> の<a href="https://sbert.net/datasets/emnlp2016-2018.json"> データセット を活用しています。</a>以前に Elasticsearch にインポートされた<a href="https://huggingface.co/sentence-transformers/all-mpnet-base-v2">テキスト埋め込みモデル</a>で生成された「要約」埋め込みを考慮して「意味的テキスト類似性」を実行することにより、要約の盗用をチェックします。さらに、AI によって生成されたコンテンツ (AI 盗作) を識別するために、OpenAI によって開発された<a href="https://huggingface.co/roberta-base-openai-detector">NLP モデル</a>も Elasticsearch にインポートされました。</p><p>次の図はデータ フローを示しています。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt0464f88d3ed12070/6a17114fab7f084905db9f97/1ad89c98a2f42a497548ca3947749bad54ec1172-1440x880.png" alt="" /><p><a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/inference-processor.html">推論プロセッサ</a> を使用した<a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/ingest.html"> 取り込みパイプラインの</a> 実行中に、「abstract」段落は 768 次元のベクトル「abstract_vector.predicted_value」にマッピングされます。</p><p>マッピング：</p>"abstract_vector.predicted_value": { # Inference results field
"type": "dense_vector", 
"dims": 768, # model embedding_size
"index": "true", 
"similarity": "dot_product" # When indexing vectors for approximate kNN search, you need to specify the similarity function for comparing the vectors.
<p>ベクトル表現間の類似性は、「類似性」<a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/dense-vector.html#dense-vector-params">パラメータ</a>を使用して定義されるベクトル類似性メトリックを使用して測定されます。</p><p><a href="https://en.wikipedia.org/wiki/Cosine_similarity">コサイン</a>はデフォルトの類似度メトリックであり、「(1 + cosine(クエリ、ベクトル)) / 2」として計算されます。元のベクトルを保持する必要があり、事前に正規化できない場合を除き、コサイン類似度を実行する最も効率的な方法は、すべてのベクトルを単位長さに正規化することです。これにより、検索中に余分なベクトルの長さの計算を実行することを回避でき、代わりに 'dot_product' を使用できます。</p><p>この同じパイプラインでは、<a href="https://huggingface.co/roberta-base-openai-detector">テキスト分類モデル</a>を含む別の推論プロセッサが、コンテンツがおそらく人間によって書かれた「本物」か、おそらく AI によって書かれた「偽物」かを検出し、各ドキュメントに「openai-detector.predicted_value」を追加します。</p><p>取り込みパイプライン:</p>client.ingest.put_pipeline( 
    id="plagiarism-checker-pipeline",
    processors = [
    {
      "inference": { #for ml models - to infer against the data that is being ingested in the pipeline
        "model_id": "roberta-base-openai-detector", #text classification model id
        "target_field": "openai-detector", # Target field for the inference results
        "field_map": { #Maps the document field names to the known field names of the model.
        "abstract": "text_field" # Field matching our configured trained model input. 
        }
      }
    },
    {
      "inference": {
        "model_id": "sentence-transformers__all-mpnet-base-v2", #text embedding model id
        "target_field": "abstract_vector", # Target field for the inference results
        "field_map": {
        "abstract": "text_field" # Field matching our configured trained model input. Typically for NLP models, the field name is text_field.
        }
      }
    }
    
  ]
)
<p>クエリ時に、同じテキスト埋め込みモデルを使用して、「query_vector_builder」オブジェクト内のクエリ「model_text」のベクトル表現も生成されます。</p><p>k 最近傍 (kNN) 検索は、類似度メトリックによって測定されたクエリ ベクトルに最も近い k ベクトルを見つけます。</p><p>各ドキュメントの _score は類似性から導き出され、スコアが大きいほどランキングが高くなります。これは、ドキュメントが意味的に類似していることを意味します。結果として、3 つの可能性を出力します。スコアが 0.9 を超える場合は「高い類似性」を考慮し、スコアが 0.7 未満の場合は「低い類似性」、それ以外の場合は「中程度の類似性」を考慮します。ユースケースに応じて、_score のどのレベルが盗作とみなされるかを決定するために、さまざまなしきい値を柔軟に設定できます。</p><p>さらに、テキスト分類が実行され、テキスト クエリ内の AI によって生成された要素もチェックされます。</p><p>クエリ:</p>from elasticsearch import Elasticsearch
from elasticsearch.client import MlClient

#duplicated text - direct plagiarism test

model_text = 'Understanding and reasoning about cooking recipes is a fruitful research direction towards enabling machines to interpret procedural text. In this work, we introduce RecipeQA, a dataset for multimodal comprehension of cooking recipes. It comprises of approximately 20K instructional recipes with multiple modalities such as titles, descriptions and aligned set of images. With over 36K automatically generated question-answer pairs, we design a set of comprehension and reasoning tasks that require joint understanding of images and text, capturing the temporal flow of events and making sense of procedural knowledge. Our preliminary results indicate that RecipeQA will serve as a challenging test bed and an ideal benchmark for evaluating machine comprehension systems. The data and leaderboard are available at http://hucvl.github.io/recipeqa.'

response = client.search(index='plagiarism-checker', size=1,
    knn={
        "field": "abstract_vector.predicted_value",
        "k": 9,
        "num_candidates": 974,
        "query_vector_builder": { #The 'all-mpnet-base-v2' model is also employed to generate the vector representation of the query in a 'query_vector_builder' object.
            "text_embedding": {
                "model_id": "sentence-transformers__all-mpnet-base-v2",
                "model_text": model_text
            }
        }
    }
)

for hit in response['hits']['hits']:
    score = hit['_score']
    title = hit['_source']['title']
    abstract = hit['_source']['abstract']
    openai = hit['_source']['openai-detector']['predicted_value']
    url = hit['_source']['url']

    if score &gt; 0.9:
        print(f"\nHigh similarity detected! This might be plagiarism.")
        print(f"\nMost similar document: '{title}'\n\nAbstract: {abstract}\n\nurl: {url}\n\nScore:{score}\n\n")

        if openai == 'Fake':
            print("This document may have been created by AI.\n")

    elif score &lt; 0.7:
        print(f"\nLow similarity detected. This might not be plagiarism.")

        if openai == 'Fake':
            print("This document may have been created by AI.\n")

    else:
        print(f"\nModerate similarity detected.")
        print(f"\nMost similar document: '{title}'\n\nAbstract: {abstract}\n\nurl: {url}\n\nScore:{score}\n\n")

        if openai == 'Fake':
            print("This document may have been created by AI.\n")

ml_client = MlClient(client)

model_id = 'roberta-base-openai-detector' #open ai text classification model

document = [
    {
        "text_field": model_text
    }
]

ml_response = ml_client.infer_trained_model(model_id=model_id, docs=document)

predicted_value = ml_response['inference_results'][0]['predicted_value']

if predicted_value == 'Fake':
    print("\nNote: The text query you entered may have been generated by AI.\n")
<p>アウトプット：</p>High similarity detected! This might be plagiarism.

Most similar document: 'RecipeQA: A Challenge Dataset for Multimodal Comprehension of Cooking Recipes'

Abstract: Understanding and reasoning about cooking recipes is a fruitful research direction towards enabling machines to interpret procedural text. In this work, we introduce RecipeQA, a dataset for multimodal comprehension of cooking recipes. It comprises of approximately 20K instructional recipes with multiple modalities such as titles, descriptions and aligned set of images. With over 36K automatically generated question-answer pairs, we design a set of comprehension and reasoning tasks that require joint understanding of images and text, capturing the temporal flow of events and making sense of procedural knowledge. Our preliminary results indicate that RecipeQA will serve as a challenging test bed and an ideal benchmark for evaluating machine comprehension systems. The data and leaderboard are available at[ http://hucvl.github.io/recipeqa](http://hucvl.github.io/recipeqa).

url:[http://aclweb.org/anthology/D18-1166](http://aclweb.org/anthology/D18-1166)

Score:1.0
<p>この例では、データセットの「抽象」値の 1 つをテキスト クエリ「model_text」として使用した後、盗用が特定されました。類似度スコアは 1.0 であり、類似度が高いこと、<strong>つまり直接的な盗用であること</strong>を示しています。ベクトル化されたクエリとドキュメントは、予想どおり AI 生成コンテンツとして認識されませんでした。</p><p>クエリ:</p>#similar text - paraphrase plagiarism test 

model_text = 'Comprehending and deducing information from culinary instructions represents a promising avenue for research aimed at empowering artificial intelligence to decipher step-by-step text. In this study, we present CuisineInquiry, a database for the multifaceted understanding of cooking guidelines. It encompasses a substantial number of informative recipes featuring various elements such as headings, explanations, and a matched assortment of visuals. Utilizing an extensive set of automatically crafted question-answer pairings, we formulate a series of tasks focusing on understanding and logic that necessitate a combined interpretation of visuals and written content. This involves capturing the sequential progression of events and extracting meaning from procedural expertise. Our initial findings suggest that CuisineInquiry is poised to function as a demanding experimental platform.'
<p>アウトプット：</p>High similarity detected! This might be plagiarism.

Most similar document: 'RecipeQA: A Challenge Dataset for Multimodal Comprehension of Cooking Recipes'

Abstract: Understanding and reasoning about cooking recipes is a fruitful research direction towards enabling machines to interpret procedural text. In this work, we introduce RecipeQA, a dataset for multimodal comprehension of cooking recipes. It comprises of approximately 20K instructional recipes with multiple modalities such as titles, descriptions and aligned set of images. With over 36K automatically generated question-answer pairs, we design a set of comprehension and reasoning tasks that require joint understanding of images and text, capturing the temporal flow of events and making sense of procedural knowledge. Our preliminary results indicate that RecipeQA will serve as a challenging test bed and an ideal benchmark for evaluating machine comprehension systems. The data and leaderboard are available at[ http://hucvl.github.io/recipeqa](http://hucvl.github.io/recipeqa).

url:[http://aclweb.org/anthology/D18-1166](http://aclweb.org/anthology/D18-1166)

Score:0.9302529

Note: The text query you entered may have been generated by AI.
<p>テキストクエリ「model_text」を、類似した単語の繰り返しを最小限に抑えながら同じメッセージを伝える AI 生成テキストで更新すると、検出された類似度は依然として高かったものの、スコアは 1.0 ではなく 0.9302529 になりました<strong>(言い換え盗用)</strong> 。AIによって生成されたこのクエリが検出されることも予想されました。</p><p>最後に、テキスト クエリ「model_text」を、これらのドキュメントの要約ではない Elasticsearch に関するテキストと見なすと、検出された類似度は 0.68991005 となり、考慮されたしきい値によると類似度が低いことが示されました。</p><p>クエリ:</p>#different text - not a plagiarism

model_text = 'Elasticsearch provides near real-time search and analytics for all types of data.'
<p>アウトプット：</p>Low similarity detected. This might not be plagiarism.
<p>AI によって生成されたテキスト クエリでは、言い換えや直接コピーされたコンテンツの場合と同様に、盗用が正確に識別されましたが、盗用検出の状況を把握するには、さまざまな側面を認識する必要があります。</p><p>AI 生成コンテンツの検出という観点から、私たちは価値ある貢献を果たすモデルを検討しました。ただし、単独の検出には固有の限界があり、精度を高めるには他の方法を組み込む必要があることを認識することが重要です。</p><p>テキスト埋め込みモデルの選択によってもたらされる変動性も、もうひとつの考慮事項です。異なるデータセットでトレーニングされたさまざまなモデルでは、さまざまなレベルの類似性が得られ、生成されたテキスト埋め込みの重要性が強調されます。</p><p>最後に、これらの例では、ドキュメントの要約を使用しました。ただし、盗作検出には大きな文書が関係することが多く、テキストの長さの問題に対処することが不可欠です。テキストがモデルのトークン制限を超えることはよくあり、埋め込みを構築する前にチャンクに分割する必要があります。これを処理する<a href="https://www.elastic.co/guide/en/elasticsearch/reference/8.11/knn-search.html#nested-knn-search">実用的なアプローチ</a>としては、dense_vector を使用したネストされた構造を利用することが挙げられます。</p><h2>まとめ</h2><p>このブログでは、特に言い換えられたコンテンツや AI によって生成されたコンテンツにおける盗作の検出の課題と、この目的のために意味的なテキストの類似性とテキスト分類をどのように使用できるかについて説明しました。</p><p>これらの方法を組み合わせることで、AI によって生成されたコンテンツ、直接的な盗用と言い換えられた盗用を正常に識別した盗用検出の例を示しました。</p><p>主な目的は検出を簡素化するフィルタリング システムを確立することですが、検証には人間による評価が依然として不可欠です。</p><p>意味的テキスト類似性と NLP についてさらに詳しく知りたい場合は、次のリンクもご覧ください。</p><ul><li><p><a href="https://www.elastic.co/what-is/semantic-search">セマンティック検索とは？</a></p></li><li><p><a href="https://www.elastic.co/what-is/natural-language-processing">自然言語処理 (NLP) とは何ですか?</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/lexical-and-semantic-search-with-elasticsearch">Elasticsearch による語彙検索と意味検索</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/chunking-via-ingest-pipelines">大規模なドキュメントをインジェストパイプラインでチャンク化し、ネストされたベクトルと組み合わせることで、簡単にパッセージを検索できます。</a></p></li></ul>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/ai-plagiarism-checker-with-elasticsearch</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/ai-plagiarism-checker-with-elasticsearch</guid>
    <category><![CDATA[Vector Database]]></category>
    <category><![CDATA[Python]]></category>
    <dc:creator><![CDATA[Priscilla Parodi]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt68a5bc2434a9b03b/6a1711510e2e49a09641a22a/83e05cd4f81799fbb7b7950ed87600e825ec81e9-1024x1024.png" length="0" type="image/png"/>
    <pubDate>Tue, 19 Dec 2023 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[ElasticsearchとGoを使用して、ホリネズミ狩りをハイブリッド検索]]></title>
    <description><![CDATA[Elasticsearch と Elasticsearch Go クライアントを使用してキーワード検索とベクター検索を組み合わせることでハイブリッド検索を実現する方法を学びます。]]></description>
    <content:encoded><![CDATA[<p>このシリーズの前回の記事では、Elasticsearch Go クライアントを<a href="https://www.elastic.co/search-labs/blog/perform-text-queries-with-the-elasticsearch-go-client">従来のキーワード検索</a>と<a href="https://www.elastic.co/search-labs/blog/perform-vector-search-with-the-elasticsearch-go-client">ベクター検索</a>に使用する方法を説明しました。この第 3 部では、ハイブリッド検索について説明します。<a href="https://www.elastic.co/elasticsearch/">Elasticsearch</a> と<a href="https://www.elastic.co/guide/en/elasticsearch/client/go-api/current/index.html"> Elasticsearch</a> Go クライアント を使用して、ベクトル検索とキーワード検索の両方を組み合わせる方法の<a href="https://github.com/carlyrichmond/gopher-hunting-elasticsearch"> 例を</a> 紹介します。</p><h2>要件</h2><p>このシリーズのパート 1 と同様に、この例では次の前提条件が必要です。</p><ol><li><p>Goバージョン1.21以降のインストール</p></li><li><p><a href="https://go.dev/doc/code">Go ドキュメント</a>に記載されている推奨構造とパッケージ管理を使用して、独自の Go リポジトリを作成します。</p></li><li><p>独自の Elasticsearch クラスターを作成し、Wikipedia<a href="https://github.com/carlyrichmond/gopher-hunting-elasticsearch#sources"> のフレンドリーな</a><a href="https://en.wikipedia.org/wiki/Gopher"> Gopher</a> を含む、 げっ歯類ベースのページ セットを設定します。</p></li></ol><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt6a436c42996e5172/6a1704cf0c48573d1c01a957/34fa81a9b4c292634c719b8303a9b6b7506d7920-1440x662.png" alt="Wikipedia Gopherページ" /><h2>Elasticsearchへの接続</h2><p>繰り返しになりますが、私たちの例では、Go クライアントが提供する<a href="https://www.elastic.co/guide/en/elasticsearch/client/go-api/current/typedapi.html">Typed API</a>を使用します。クエリに対して安全な接続を確立するには、次のいずれかを使用してクライアントを構成する必要があります。</p><ol><li><p>Elastic Cloud を利用する場合の Cloud ID と API キー</p></li><li><p>クラスターURL、ユーザー名、パスワード、証明書</p></li></ol><p>Elastic Cloud にあるクラスターに接続すると、次のようになります。</p>func GetElasticsearchClient() (*elasticsearch.TypedClient, error) {
	var cloudID = os.Getenv("ELASTIC_CLOUD_ID")
	var apiKey = os.Getenv("ELASTIC_API_KEY")

	var es, err = elasticsearch.NewTypedClient(elasticsearch.Config{
		CloudID: cloudID,
		APIKey:  apiKey,
		Logger:  &amp;elastictransport.ColorLogger{os.Stdout, true, true},
	})

	if err != nil {
		return nil, fmt.Errorf("unable to connect: %w", err)
	}

	return es, nil
}
<p>後続のセクションで示すように、 <code>client</code>接続は検索に使用できます。</p><h2>ハイブリッド検索の手動ブースティング</h2><p>検索アルゴリズムのセットを組み合わせる場合、従来のアプローチでは、各クエリ タイプを強化するために定数を手動で構成していました。具体的には、クエリごとに係数が指定され、結合された結果セットが予想されるセットと比較され、クエリのリコールが決定されます。次に、いくつかの要因セットを繰り返し、希望する状態に最も近いものを選択します。</p><p>たとえば、 <code>0.8</code>倍の係数でブーストされた単一のテキスト検索クエリと、 <code>0.2</code>倍の係数が低い knn クエリを組み合わせるには、次の例に示すように、両方のクエリ タイプで<code>Boost</code>フィールドを指定します。</p>func HybridSearchWithBoost(client *elasticsearch.TypedClient, term string) ([]Rodent, error) {
	var k = 10
	var numCandidates = 10
	var knnBoost float32 = 0.2
	var queryBoost float32 = 0.8

	res, err := client.Search().
		Index("vector-search-rodents").
		Knn(types.KnnSearch{
			Field:         "text_embedding.predicted_value",
			Boost:         &amp;knnBoost,
			K:             &amp;k,
			NumCandidates: &amp;numCandidates,
			QueryVectorBuilder: &amp;types.QueryVectorBuilder{
				TextEmbedding: &amp;types.TextEmbedding{
					ModelId:   "sentence-transformers__msmarco-minilm-l-12-v3",
					ModelText: term,
				},
			}}).
		Query(&amp;types.Query{
			Match: map[string]types.MatchQuery{
				"title": {
					Query: term,
					Boost: &amp;queryBoost,
				},
			},
		}).
		Do(context.Background())

	if err != nil {
		return nil, err
	}

	return getRodents(res.Hits.Hits)
}
<p>各クエリの<code>Boost</code>オプションで指定された係数がドキュメント スコアに追加されます。一致クエリのスコアを knn クエリよりも大きな係数で増加させることにより、キーワード クエリの結果の重み付けがより大きくなります。</p><p>手動によるブースティングの課題は、特に検索の専門家でない場合、望ましい結果セットにつながる要因を見つけ出すために調整が必要になることです。ランダムな値を試してみて、希望する結果セットに近づくかどうかを調べるだけです。</p><h2>ハイブリッド検索とGoクライアントにおける相互ランク融合</h2><p><a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/rrf.html">Reciprocal Rank Fusion</a> (RRF) は、Elasticsearch 8.9 のハイブリッド検索のテクニカル プレビューとしてリリースされました。チューニングに関連する学習曲線を短縮し、結果セットを最適化するための要素の実験にかかる時間を短縮することを目的としています。</p><p>RRF では、以下のアルゴリズムでスコアをブレンドしてドキュメント スコアが再計算されます。</p>score := 0.0
// q is a query in the set of queries (vector and keyword search)
for _, q := range queries {
    // result(q) is the results 
    if document in result(q) {
        // k is a ranking constant (default 60)
        // rank(result(q), d) is the document's rank within result(q) 
        // range from 1 to the window_size (default 100)
        score +=  1.0 / (k + rank(result(q), d))
    }
}

return score
<p>RRF を使用する利点は、Elasticsearch 内で適切なデフォルト値を利用できることです。ランキング定数<code>k</code>デフォルトは<code>60</code>です。大規模なデータ セットを検索するときに、返されるドキュメントの関連性とクエリ パフォーマンスの間のトレードオフを提供するために、検討される各クエリの結果セットのサイズは<code>window_size</code>の値に制限されます。この値は、<a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/rrf.html#rrf-api">ドキュメント</a>で説明されているように、デフォルトで<code>100</code>になります。</p><p><code>k</code> また、 <code>windows_size</code> 、以下の例のように、Go クライアントの<code>Rank</code>メソッド内の<code>Rrf</code>構成内で構成することもできます。</p>func HybridSearchWithRRF(client *elasticsearch.TypedClient, term string) ([]Rodent, error) {
	var k = 10
	var numCandidates = 10

	// Minimum required window size for the default result size of 10
	var windowSize int64 = 10
	var rankConstant int64 = 42

	res, err := client.Search().
		Index("vector-search-rodents").
		Knn(types.KnnSearch{
			Field:         "text_embedding.predicted_value",
			K:             &amp;k,
			NumCandidates: &amp;numCandidates,
			QueryVectorBuilder: &amp;types.QueryVectorBuilder{
				TextEmbedding: &amp;types.TextEmbedding{
					ModelId:   "sentence-transformers__msmarco-minilm-l-12-v3",
					ModelText: term,
				},
			}}).
		Query(&amp;types.Query{
			Match: map[string]types.MatchQuery{
				"title": {Query: term},
			},
		}).
		Rank(&amp;types.RankContainer{
			Rrf: &amp;types.RrfRank{
				WindowSize:   &amp;windowSize,
				RankConstant: &amp;rankConstant,
			},
		}).
		Do(context.Background())

	if err != nil {
		return nil, err
	}

	return getRodents(res.Hits.Hits)
}
<h2>まとめ</h2><p>ここでは、 <a href="https://www.elastic.co/guide/en/elasticsearch/client/go-api/current/index.html">Elasticsearch Go クライアント</a>を使用して Elasticsearch でベクトル検索とキーワード検索を組み合わせる方法について説明しました。</p><p>このシリーズのすべてのコードについては、 <a href="https://github.com/carlyrichmond/gopher-hunting-elasticsearch">GitHub リポジトリ</a>をご覧ください。まだご覧になっていない方は、このシリーズのすべてのコードについては<a href="https://www.elastic.co/search-labs/blog/perform-text-queries-with-the-elasticsearch-go-client">パート 1</a>と<a href="https://www.elastic.co/search-labs/blog/perform-vector-search-with-the-elasticsearch-go-client">パート 2</a>をご覧ください。</p><p><em>楽しいホリネズミ狩りを！</em></p><h2>各種資料</h2><ol><li><p><a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/index.html">Elasticsearchガイド</a></p></li><li><p><a href="https://www.elastic.co/guide/en/elasticsearch/client/go-api/current/index.html">Elasticsearch Goクライアント</a></p></li><li><p><a href="https://www.elastic.co/what-is/vector-search">ベクトル検索とは何ですか?| 弾性</a></p></li><li><p><a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/rrf.html">相互ランク融合</a></p></li></ol>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/hybrid-search-with-the-elasticsearch-go-client</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/hybrid-search-with-the-elasticsearch-go-client</guid>
    <category><![CDATA[Vector Database]]></category>
    <category><![CDATA[Go]]></category>
    <dc:creator><![CDATA[Carly Richmond,Laurent Saint-Félix]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltdcae959696d55a9b/6a1704d5ab7f085a56db9d7c/491ef9efbb30b253e1d9e3b7f816a9a23c8f5264-721x420.jpg" length="0" type="image/jpeg"/>
    <pubDate>Thu, 02 Nov 2023 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Elasticsearch Goクライアントを使用してElasticsearchでベクトル検索を実行する]]></title>
    <description><![CDATA[実際の例を通して、Elasticsearch Go クライアントを使用して Elasticsearch でベクトル検索を実行する方法を学びます。]]></description>
    <content:encoded><![CDATA[<p>Go を含むあらゆるプログラミング言語でソフトウェアを構築することは、生涯にわたる学習に取り組むことです。Carly は大学時代や仕事を通じて、ベクトル検索の最新かつ最高の実装を含む、数多くのプログラミング言語やテクノロジーに携わってきました。しかし、それだけでは十分ではありませんでした!それで最近カーリーも囲碁を始めました。</p><p>動物、プログラミング言語、そして親しみやすい著者と同じように、検索もさまざまな方法の進化を遂げており、独自の検索ユースケースに応じてどれを選択するかを決めるのは難しい場合があります。このブログでは、ベクトル検索の概要と、<a href="https://www.elastic.co/elasticsearch/"> Elasticsearch</a> および<a href="https://www.elastic.co/guide/en/elasticsearch/client/go-api/current/index.html"> Elasticsearch Go</a> クライアント を使用した各アプローチの<a href="https://github.com/carlyrichmond/gopher-hunting-elasticsearch"> 例を</a> 紹介します。これらの例では、Elasticsearch と Go のベクトル検索を使用して、ホリネズミを見つけて、その食べ物を特定する方法を説明します。</p><h2>要件</h2><p>この例を実行するには、次の前提条件が満たされていることを確認してください。</p><ol><li><p>Goバージョン1.21以降のインストール</p></li><li><p>独自のGoリポジトリを作成する</p></li><li><p>独自の Elasticsearch クラスターを作成し、Wikipedia のフレンドリーな<a href="https://en.wikipedia.org/wiki/Gopher">Gopher</a>を含む一連のげっ歯類ベースのページを設定します。</p></li></ol><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt6a436c42996e5172/6a1704cf0c48573d1c01a957/34fa81a9b4c292634c719b8303a9b6b7506d7920-1440x662.png" alt="Wikipedia Gopherページ" /><h2>Elasticsearchへの接続</h2><p>この例では、Go クライアントが提供する<a href="https://www.elastic.co/guide/en/elasticsearch/client/go-api/current/typedapi.html">Typed API</a>を利用します。クエリに対して安全な接続を確立するには、次のいずれかを使用してクライアントを構成する必要があります。</p><ol><li><p>Elastic Cloud を利用する場合のクラウド ID と API キー。</p></li><li><p>クラスター URL、ユーザー名、パスワード、証明書。</p></li></ol><p>Elastic Cloud にあるクラスターに接続すると、次のようになります。</p>func GetElasticsearchClient() (*elasticsearch.TypedClient, error) {
	var cloudID = os.Getenv("ELASTIC_CLOUD_ID")
	var apiKey = os.Getenv("ELASTIC_API_KEY")

	var es, err = elasticsearch.NewTypedClient(elasticsearch.Config{
		CloudID: cloudID,
		APIKey:  apiKey,
		Logger:  &amp;elastictransport.ColorLogger{os.Stdout, true, true},
	})

	if err != nil {
		return nil, fmt.Errorf("unable to connect: %w", err)
	}

	return es, nil
}
<p>後続のセクションに示すように、 <code>client</code>接続はベクトル検索に使用できます。</p><h2>ベクトル検索</h2><p>ベクトル検索は、検索問題をベクトルを使用した数学的な比較に変換することでこの問題を解決しようとします。ドキュメント埋め込みプロセスには、モデルを使用してドキュメントを高密度ベクトル表現、つまり単純な数値のストリームに変換する追加の段階があります。このアプローチの利点は、画像や音声などのテキスト以外のドキュメントをクエリと一緒にベクトルに変換して検索できることです。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltc4b3b9b2ad1e8f46/6a1704d06234e0800fdb1927/b113358093e367358d684f7aaf0a6684ebb2d0dd-1440x653.png" alt="ベクトル探索図" /><p>簡単に言えば、ベクトル検索はベクトル距離の計算のセットです。下の図では、クエリ<code>Go Gopher</code>のベクトル表現がベクトル空間内のドキュメントと比較され、最も近い結果 (定数<code>k</code>で示される) が返されます。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5c736ecfc49bb038/6a1704d2cf4f25bcb6b2d075/54a17a4b41029644e7c66f33d428e8d01a6f4ce3-1184x743.png" alt="Gopherベクトル空間の例" /><p>ドキュメントの埋め込みを生成するために使用するアプローチに応じて、ホリネズミが何を食べるかを調べる方法が 2 つあります。</p><h3>アプローチ1: 独自のモデルを持ち込む</h3><p>Platinum ライセンスでは、モデルをアップロードし、推論 API を使用して Elasticsearch 内で埋め込みを生成できます。モデルの設定には 6 つのステップがあります。</p><ol><li><p>モデル リポジトリからアップロードする PyTorch モデルを選択します。この例では、Hugging Face の<a href="https://huggingface.co/sentence-transformers/msmarco-MiniLM-L-12-v3">sentence-transformers/msmarco-MiniLM-L-12-v3</a>を使用して埋め込みを生成します。</p></li><li><p>Elasticsearch クラスターとタスク タイプ<code>text_embeddings</code>の資格情報を使用して<a href="https://www.elastic.co/guide/en/elasticsearch/client/eland/current/overview.html">、Python 用の Eland Machine Learning クライアント</a>でモデルを Elastic にロードします。Eland がインストールされていない場合は、以下に示すように<a href="https://www.elastic.co/guide/en/machine-learning/current/ml-nlp-import-model.html#ml-nlp-import-docker">Docker を使用してインポート手順を実行</a>できます。</p></li></ol>docker run -it --rm --network host \
    docker.elastic.co/eland/eland \
    eland_import_hub_model \
      --cloud-id $ELASTIC_CLOUD_ID \
      --es-api-key $ELASTIC_API_KEY \
      --hub-model-id sentence-transformers/msmarco-MiniLM-L-12-v3 \
      --task-type text_embedding
<ol><li><p>アップロードしたら、サンプル ドキュメントを使用してモデル<code>sentence-transformers__msmarco-minilm-l-12-v3</code>をすぐにテストし、埋め込みが期待どおりに生成されることを確認します。</p></li></ol><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt8a399e1f50ecfdaf/6a1704d4ab7f08935edb9d78/fbbdde621361a487eb07304bed029c228d0f7aa6-1440x789.png" alt="Elastic Test トレーニング済みモデルの例" /><ol><li><p>推論プロセッサを含む取り込みパイプラインを作成します。これにより、アップロードされたモデルを使用してベクター表現を生成できるようになります。</p></li></ol>PUT _ingest/pipeline/search-rodents-vector-embedding-pipeline
{
  "processors": [
    {
      "inference": {
        "model_id": "sentence-transformers__msmarco-minilm-l-12-v3",
        "target_field": "text_embedding",
        "field_map": {
          "body_content": "text_field"
        }
      }
    }
  ]
}
<ol><li><p>各ドキュメントに対して生成されたベクトル埋め込みを格納するための、タイプ<code>dense_vector</code>のフィールド<code>text_embedding.predicted_value</code>を含む新しいインデックスを作成します。</p></li></ol>PUT vector-search-rodents
{
  "mappings": {
    "properties": {
      "text_embedding.predicted_value": {
        "type": "dense_vector",
        "dims": 384,
        "index": true,
        "similarity": "cosine"
      },
      "text": {
        "type": "text"
      }
    }
  }
}
<ol><li><p>新しく作成された取り込みパイプラインを使用してドキュメントのインデックスを再作成し、各ドキュメントの追加フィールド<code>text_embedding.predicted_value</code>としてテキスト埋め込みを生成します。</p></li></ol>POST _reindex
{
  "source": {
    "index": "search-rodents"
  },
  "dest": {
    "index": "vector-search-rodents",
    "pipeline": "search-rodents-vector-embedding-pipeline"
  }
}
<p>次の例に示すように、新しいインデックス<code>vector-search-rodents</code>を使用して、同じ検索 API で<code>Knn</code>オプションを使用できるようになりました。</p>func VectorSearch(client *elasticsearch.TypedClient, term string) ([]Rodent, error) {
  var k = 10
	var numCandidates = 10

	res, err := client.Search().
		Index("vector-search-rodents").
		Knn(types.KnnSearch{
      # Field in document containing vector
			Field:         "text_embedding.predicted_value",
      # Number of neighbors to return
			K:             &amp;k,
      # Number of candidates to evaluate in comparison
			NumCandidates: &amp;numCandidates,
      # Generate query vector using the same model used in the inference processor
			QueryVectorBuilder: &amp;types.QueryVectorBuilder{
				TextEmbedding: &amp;types.TextEmbedding{
					ModelId:   "sentence-transformers__msmarco-minilm-l-12-v3",
					ModelText: term,
				},
			}}).Do(context.Background())

	if err != nil {
		return nil, fmt.Errorf("error in rodents vector search: %w", err)
	}

	return getRodents(res.Hits.Hits)
}
<p>アンマーシャリングによる JSON 結果オブジェクトの変換は、キーワード検索の例とまったく同じ方法で実行されます。定数<code>K</code>と<code>NumCandidates</code>使用すると、返される隣接ドキュメントの数と、シャードごとに考慮する候補の数を設定できます。候補の数を増やすと結果の精度は上がりますが、比較が多く実行されるためクエリの実行時間が長くなることに注意してください。</p><p>クエリ<code>What do Gophers eat?</code>を使用してコードを実行すると、返される結果は以下と同様になり、以前のキーワード検索とは異なり、Gopher の記事に要求された情報が含まれていることが強調表示されます。</p>[
  {ID:64f74ecd4acb3df024d91112 Title:Gopher - Wikipedia Url:https://en.wikipedia.org/wiki/Gopher} 
  {ID:64f74ed34acb3d71aed91fcd Title:Squirrel - Wikipedia Url:https://en.wikipedia.org/wiki/Squirrel} 
  //Other results omitted
]
<h3>アプローチ2：ハグフェイス推論API</h3><p>もう 1 つのオプションは、Elasticsearch の外部で同じ埋め込みを生成し、ドキュメントの一部として取り込むことです。このオプションは Elasticsearch 機械学習ノードを使用しないため、無料利用枠で実行できます。</p><p>Hugging Face は、無料で使用できるレート制限付きの<a href="https://huggingface.co/docs/api-inference/index">推論 API を</a>公開しています。アカウントと API トークンを使用すると、実験やプロトタイピングを開始するために同じ埋め込みを手動で生成できます。実稼働環境での使用は推奨されません。同様のアプローチを使用して、ローカルで独自のモデルを呼び出して埋め込みを生成したり、有料の API を使用したりすることもできます。</p><p>以下の関数<code>GetTextEmbeddingForQuery</code>では、クエリ文字列に対して推論 API を使用して、エンドポイントへの<code>POST</code>リクエストから返されるベクトルを生成します。</p>// HuggingFace text embedding helper
func GetTextEmbeddingForQuery(term string) []float32 {
    // HTTP endpoint
    model := "sentence-transformers/msmarco-minilm-l-12-v3"
    posturl := fmt.Sprintf("https://api-inference.huggingface.co/pipeline/feature-extraction/%s", model)

    // JSON body
    body := []byte(fmt.Sprintf(`{
        "inputs": "%s",
        "options": {"wait_for_model":True}
    }`, term))

    // Create a HTTP post request
    r, err := http.NewRequest("POST", posturl, bytes.NewBuffer(body))

    if err != nil {
        log.Fatal(err)
        return nil
    }

    token := os.Getenv("HUGGING_FACE_TOKEN")
    r.Header.Add("Authorization", fmt.Sprintf("Bearer %s", token))

    client := &amp;http.Client{}
    res, err := client.Do(r)
    if err != nil {
        panic(err)
    }

    defer res.Body.Close()

    var post []float32
    derr := json.NewDecoder(res.Body).Decode(&amp;post)

    if derr != nil {
        log.Fatal(derr)
        return nil
    }

    return post
}
<p>結果の<code>[]float32</code>型のベクトルは、 <code>QueryVectorBuilder</code>オプションを使用する代わりに<code>QueryVector</code>として渡され、以前に Elastic にアップロードされたモデルが活用されます。</p>func VectorSearchWithGeneratedQueryVector(client *elasticsearch.TypedClient, term string) ([]Rodent, error) {
	vector, err := GetTextEmbeddingForQuery(term)
	if err != nil {
		return nil, err
	}

	if vector == nil {
		return nil, fmt.Errorf("unable to generate vector: %w", err)
	}

  var k = 10
	var numCandidates = 10

	res, err := client.Search().
		Index("vector-search-rodents").
		Knn(types.KnnSearch{
      # Field in document containing vector
			Field:         "text_embedding.predicted_value",
      # Number of neighbors to return
			K:             &amp;k,
      # Number of candidates to evaluate in comparison
			NumCandidates: &amp;numCandidates,
      # Query vector returned from Hugging Face inference API
			QueryVector:   vector,
		}).
		Do(context.Background())

	if err != nil {
		return nil, err
	}

	return getRodents(res.Hits.Hits)
}
<p><code>K</code>と<code>NumCandidates</code>オプションは2つのオプションに関係なく同じままであり、埋め込みを生成するために同じモデルを使用しているため、同じ結果が生成されることに注意してください。</p><h2>まとめ</h2><p>ここでは、 <a href="https://www.elastic.co/guide/en/elasticsearch/client/go-api/current/index.html">Elasticsearch Go クライアント</a>を使用して Elasticsearch でベクトル検索を実行する方法について説明しました。このシリーズのすべてのコードについては、 <a href="https://github.com/carlyrichmond/gopher-hunting-elasticsearch">GitHub リポジトリ</a>をご覧ください。<a href="https://www.elastic.co/search-labs/blog/hybrid-search-with-the-elasticsearch-go-client">パート 3</a>に進み、<a href="https://www.elastic.co/search-labs/blog/perform-text-queries-with-the-elasticsearch-go-client">パート 1</a>で説明した Go のキーワード検索機能とベクトル検索を組み合わせる方法の概要を確認します。</p><p>それまでは、楽しいホリネズミ狩りを！</p><h2>各種資料</h2><ol><li><p><a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/index.html">Elasticsearchガイド</a></p></li><li><p><a href="https://www.elastic.co/guide/en/elasticsearch/client/go-api/current/index.html">Elasticsearch Goクライアント</a></p></li><li><p><a href="https://www.elastic.co/what-is/vector-search">ベクトル検索とは何ですか?| 弾性</a></p></li></ol>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/perform-vector-search-with-the-elasticsearch-go-client</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/perform-vector-search-with-the-elasticsearch-go-client</guid>
    <category><![CDATA[Vector Database]]></category>
    <category><![CDATA[Go]]></category>
    <dc:creator><![CDATA[Carly Richmond,Laurent Saint-Félix]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltdcae959696d55a9b/6a1704d5ab7f085a56db9d7c/491ef9efbb30b253e1d9e3b7f816a9a23c8f5264-721x420.jpg" length="0" type="image/jpeg"/>
    <pubDate>Wed, 01 Nov 2023 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Elasticsearch による語彙検索と意味検索]]></title>
    <description><![CDATA[このブログでは、語彙検索と意味検索に焦点を当て、Elasticsearch を使用して情報を取得するためのさまざまなアプローチについて説明します。]]></description>
    <content:encoded><![CDATA[<p>検索は、検索クエリまたは複合クエリに基づいて最も関連性の高い情報を検索するプロセスであり、関連する検索結果はこれらのクエリに最も一致するドキュメントです。検索にはさまざまな課題と方法がありますが、最終的な目標は、<strong>質問に対する最適な回答を見つけることです</strong>。</p><p>この目標を考慮して、このブログ投稿では、Elasticsearch を使用して情報を取得するためのさまざまなアプローチを検討し、特にテキスト検索（<strong>語彙検索とセマンティック検索）に焦点を当てます。</strong></p><h2>要件</h2><p>これを実現するために、eコマース製品情報をシミュレートするために生成された<a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/lexical-and-semantic-search-with-elasticsearch/products-ecommerce.json">データセット</a>でのさまざまな検索シナリオを示す Python の例を提供します。</p><p>このデータセットには 2,500 を超える製品が含まれており、それぞれに説明が付いています。これらの製品は 76 の異なる製品カテゴリに分類されており、各カテゴリには以下に示すようにさまざまな数の製品が含まれています。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt34415151c00ced2b/6a17d8710b0bed6c9bdd342c/4104466050f3024b6bcaf382da2a702650f62227-1440x708.png" alt="" /><p><em>ツリーマップの視覚化 - カテゴリ.キーワードの上位 22 個の値 (製品カテゴリ)</em></p><p>セットアップには次のものが必要です:</p><ul><li><p>Python 3.6以降</p></li><li><p><a href="https://www.elastic.co/guide/en/elasticsearch/client/python-api/current/index.html">Elastic Pythonクライアント</a></p></li><li><p>Elastic 8.8 以降のデプロイメント、8GB メモリの機械学習ノード</p></li><li><p><a href="https://www.elastic.co/guide/en/machine-learning/8.9/ml-nlp-elser.html">Elastic Learned Sparse EncodeR</a>モデルは、Elastic にプリロードされており、デプロイメントにインストールされて起動します。</p></li></ul><p>Elastic Cloud を使用します。<a href="https://cloud.elastic.co/registration?onboarding_token=vectorsearch&amp;cta=cloud-registration&amp;tech=trial&amp;plcmt=article%20content&amp;pg=search-labs">無料トライアルを</a>ご利用いただけます。</p><p>このブログ記事で提供されている検索クエリに加えて、 <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/lexical-and-semantic-search-with-elasticsearch/ecommerce_dense_sparse_project.ipynb">Python ノートブックで</a>は次のプロセスをガイドします。</p><ul><li><p>Pythonクライアントを使用してElasticデプロイメントへの接続を確立する</p></li><li><p>Elasticsearchクラスターにテキスト埋め込みモデルをロードする</p></li><li><p>特徴ベクトルと密ベクトルのインデックスを作成するためのマッピングを使用してインデックスを作成します。</p></li><li><p>テキスト埋め込みとテキスト拡張のための推論プロセッサを備えた取り込みパイプラインを作成する</p></li></ul><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt0e95406ece8f08d9/6a17d8731d1b83d32e93e2f4/54a9a490a3b0cf1b9c2228bee8eddd3f566bd435-1418x1102.png" alt="" /><h2>語彙検索 - スパース検索</h2><p>テキスト クエリに基づいて Elasticsearch がドキュメントの関連性をランク付けする従来の方法では、<strong> 語彙検索用のスパース</strong> モデルである<a href="https://en.wikipedia.org/wiki/Okapi_BM25"> BM25 モデルの</a> Lucene 実装が使用されます。この方法は、正確な用語の一致を探すという、テキスト検索の従来のアプローチに従います。</p><p>この検索を可能にするために、Elasticsearch はテキスト分析を実行して<strong>テキスト フィールド</strong>データを検索可能な形式に変換します。</p><p><strong>テキスト分析</strong>は、検索に関連するトークンを抽出するプロセスを管理する一連のルールである<a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/analyzer-anatomy.html">アナライザー</a>によって実行されます。アナライザーには、<a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/analysis-tokenizers.html">トークナイザー</a>が 1 つだけ必要です。トークナイザーは文字のストリームを受け取り、それを個々のトークン (通常は個々の単語) に分割します。以下に例を示します。</p><h3>語彙検索のための文字列トークン化</h3>#Performs text analysis on a string and returns the resulting tokens.

# Define the text to be analyzed
text = "Comfortable furniture for a large balcony"

# Define the analyze request
request_body = {
  "analyzer": "standard",
  "text": text
}

# Perform the analyze request
response = client.indices.analyze(analyzer=request_body["analyzer"], text=request_body["text"])

# Extract and display the analyzed tokens
tokens = [token["token"] for token in response["tokens"]]
print("Analyzed Tokens:", tokens)
<p>出力</p>Analyzed Tokens: ['comfortable', 'furniture', 'for', 'a', 'large', 'balcony']
<p>この例では、デフォルトのアナライザーである<a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/analysis-standard-analyzer.html">標準</a>アナライザーを使用しています。これは、英語の文法に基づいたトークン化を提供するため、ほとんどのユースケースに適しています。トークン化により、個々の用語での一致が可能になりますが、各トークンは文字どおりに一致します。</p><p>検索エクスペリエンスをカスタマイズしたい場合は、別の<a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/analysis-analyzers.html">組み込みアナライザーを</a>選択できます。たとえば、<a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/analysis-stop-analyzer.html">ストップ アナライザーを</a>使用するようにコードを更新すると、ストップワードの削除がサポートされ、文字以外の文字でテキストがトークンに分割されます。</p>...
# Define the analyze request
request_body = {
  "analyzer": "stop",
  "text": text
}
...
<p>出力</p>Analyzed Tokens: ['comfortable', 'furniture', 'large', 'balcony']
<p><a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/analysis-charfilters.html">組み込みアナライザーがニーズを満たさない場合は、ゼロ個以上の 文字フィルター</a> 、<a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/analysis-tokenizers.html"> トークナイザー</a> 、およびゼロ個以上の <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/analysis-tokenfilters.html">トークン</a> フィルター の適切な組み合わせを使用する<a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/analysis-custom-analyzer.html"> カスタム アナライザー</a> を作成できます。</p>"analyzer":  {

  "my_analyzer": {

    "type": "custom", #For custom analyzers, use a type of custom or omit the type parameter.

    "tokenizer": "standard", #Built-in or customized tokenizer

    "filter": ["lowercase", "synonym"] #Built-in or customized token filters
  }
}
<p>トークナイザーとトークン フィルターを組み合わせた上記の例では、テキストは、<a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/analysis-lowercase-tokenfilter.html"> </a><a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/analysis-synonym-tokenfilter.html#:~:text=Elasticsearch%20will%20use%20the%20token,applied%20to%20the%20synonym%20entries.">同義語トークン フィルター によって処理される前に、</a> 小文字フィルター によって小文字化されます。</p><h2>語彙マッチング</h2><p><a href="https://www.elastic.co/blog/practical-bm25-part-2-the-bm25-algorithm-and-its-variables">BM25 は</a>、用語の頻度と重要度に基づいて、特定の検索クエリに対するドキュメントの関連性を測定します。</p><p>以下のコードは、<a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-match-query.html"> 一致</a> クエリを実行し、<em> 「ecommerce-search」</em> インデックスの<em> 「description」</em> <strong>フィールド値と検索クエリ</strong><em><strong> 「 Comfortable furniture for a large balcony 」</strong></em><strong> を考慮して最大</strong> 2 つのドキュメントを検索します。</p><p>このクエリに一致すると見なされるドキュメントの基準を絞り込むと、精度が向上します。ただし、より具体的な結果を得るには、変動に対する許容度が低くなるというデメリットがあります。</p># BM25

response = client.search(size=2,
index="ecommerce-search",
query= {
  "match": {
    "description" : {  
      "query": "Comfortable furniture for a large balcony",
      "analyzer": "stop"
    }
  }
}
)

hits = response['hits']['hits']

if not hits:
  print("No matches found")

else:
  for hit in hits:
    score = hit['_score']
    product = hit['_source']['product']
    category = hit['_source']['category']
    description = hit['_source']['description']
    print(f"\nScore: {score}\nProduct: {product}\nCategory: {category}\nDescription: {description}\n")
<p>出力</p>Score: 15.607948
Product: Barbie Dreamhouse
Category: Toys
Description: is a classic Barbie playset with multiple rooms, furniture, a large balcony, a pool, and accessories. It allows kids to create their dream Barbie world.

Score: 9.137739
Product: Comfortable Rocking Chair
Category: Indoor Furniture
Description: enjoy relaxing moments with this comfortable rocking chair. Its smooth motion and cushioned seat make it an ideal piece of furniture for unwinding.
<p>出力を分析すると、最も関連性の高い結果は「<em> おもちゃ</em> 」カテゴリの「バービードリームハウス 」製品であり、その説明には「<em> 家具</em> 」、「<em> 大型」</em> 、「バルコニー 」という用語が含まれているため関連性が高く、説明に検索クエリに一致する用語が 3 つ含まれている唯一の製品であり、説明に「バルコニー」 という用語が含まれているのもこの製品のみです。</p><p>2 番目に関連性の高い製品は、「 屋内用家具」に分類される「快適なロッキングチェア 」で、その説明には「<em> 快適な</em> 」および「<em> 家具</em> 」という用語が含まれています。データセット内のこの検索クエリの少なくとも 2 つの用語に一致する製品は 3 つだけであり、この製品はそのうちの 1 つです。</p><p><em>「快適」という</em>言葉は 105 製品の説明に登場し、 <em>「家具」という</em>言葉は、<em>おもちゃ</em>、<em>屋内用家具、屋外用家具、および「犬と猫の用品とおもちゃ」という 4 つのカテゴリーの 4 製品の説明に登場しています。</em></p><p>ご覧のとおり、クエリを考慮すると最も関連性の高い製品はおもちゃであり、2 番目に関連性の高い製品は屋内用家具です。これらのドキュメントが一致する理由を知るためにスコア計算に関する詳細な情報が必要な場合は、 <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/search-explain.html"><em>explain</em></a> __query パラメータを true に設定できます。</p><p>両方の結果が最も関連性の高いものであるにもかかわらず、このデータセット内のドキュメントの数と用語の出現の両方を考慮すると、クエリ「<em>大きなバルコニー用の快適な家具</em>」の背後にある意図は、おもちゃや室内用家具などを除いた、実際の大きなバルコニー用の家具を検索することです。</p><p>語彙検索は比較的<strong>単純かつ高速</strong>ですが、ユーザーの意図やクエリを必ずしも知らずにすべての用語と同義語を知ることが常に可能であるとは限らないため、限界があります。自然言語の使用においてよく見られる現象は<strong>語彙の不一致</strong>です。<a href="https://dl.acm.org/doi/abs/10.1145/32206.32212">調査</a>によると、平均して<strong>80% の確率で</strong>、異なる人々 (同じ分野の専門家) が同じものを異なる名前で呼ぶことがわかっています。</p><p>これらの制限により、意味的知識を組み込んだ他のスコアリング モデルを探すことになります。自然言語のような連続的な入力トークンの処理に優れたトランスフォーマーベースのモデルは、ドキュメントとクエリの両方の数学的表現を考慮して、検索の根本的な意味を捉えます。これにより、テキストの高密度でコンテキストを認識したベクトル表現が可能になり、関連するコンテンツを見つけるための洗練された方法である<strong>セマンティック検索</strong>が強化されます。</p><h2>セマンティック検索 - 高密度検索</h2><p>このコンテキストでは、データを意味のあるベクトル値に変換した後、 <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/knn-search.html">k 最近傍 (kNN)</a>検索アルゴリズムを使用して、データセット内でクエリ ベクトルに最も類似したベクトル表現を検索します。Elasticsearch は、kNN 検索に、<a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/knn-search.html#exact-knn">正確なブルート フォース kNN</a>と<a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/knn-search.html#approximate-knn">近似 kNN</a> (ANN とも呼ばれる) の 2 つの方法をサポートしています。</p><p>ブルートフォース kNN は正確な結果を保証しますが、大規模なデータセットでは適切に拡張できません。近似 kNN は、パフォーマンスを向上させるために精度をある程度犠牲にして、近似最近傍を効率的に見つけます。</p><p>Lucene の kNN 検索と高密度ベクトル インデックスのサポートにより、Elasticsearch は階層的ナビゲート可能スモール ワールド (HNSW) アルゴリズムを活用し、さまざまな<a href="http://ann-benchmarks.com/">ann ベンチマーク データセット</a>にわたって強力な検索パフォーマンスを発揮します。以下のサンプルコードを使用して、Python で近似 kNN 検索を実行できます。</p><h3>近似kNNによるセマンティック検索</h3># KNN - approximate kNN

response = client.search(index='ecommerce-search', size=2,
knn={
  "field": "description_vector.predicted_value",
  "k": 50, # Number of nearest neighbors to return as top hits.
#The optimal value of k is dependent on the data. It can vary in different scenarios.

  "num_candidates": 500, # Number of nearest neighbor candidates to consider per shard.

#Increasing num_candidates tends to improve the accuracy of the final k results.

  "query_vector_builder": { # Object indicating how to build a query_vector. kNN search enables you to perform semantic search by using a previously deployed text embedding model, the steps for this process are demonstrated in the Python notebook.
    "text_embedding": { 
      "model_id": "sentence-transformers__all-mpnet-base-v2", # Text embedding model id
      "model_text": "Comfortable furniture for a large balcony" # Query
    }
  }
}
)

for hit in response['hits']['hits']:
        
  score = hit['_score']
  product = hit['_source']['product']
  category = hit['_source']['category']
  description = hit['_source']['description']
  print(f"\nScore: {score}\nProduct: {product}\nCategory: {category}\nDescription: {description}\n")
<p>このコード ブロックは、Elasticsearch の kNN を使用して、製品データセットの「<em> description</em><em> 」フィールドの埋め込みを考慮した「 大きなバルコニー用の快適な家具</em> 」のベクトル化されたクエリ (query_vector_build) に類似した説明を持つ最大 2 つの製品を返します。</p><p>製品の埋め込みは、以前はパイプラインに取り込まれたデータに対して推論するための<em>「</em> <a href="https://huggingface.co/sentence-transformers/all-mpnet-base-v2"><em>all-mpnet-base-v2</em></a> <em>」</em>テキスト埋め込みモデルを含む推論プロセッサを使用して、取り込みパイプラインで生成されていました。</p><p>このモデルは、 <em>「</em> <a href="https://github.com/UKPLab/sentence-transformers/blob/master/docs/package_reference/sentence_transformer/evaluation.md"><em>sentence_transformers.evaluation</em></a> <em>」</em>を使用した事前学習済みモデルの評価に基づいて選択されました。トレーニング中にさまざまなクラスを使用してモデルを評価します。「all-mpnet-base-v2」モデルは<a href="https://www.sbert.net/docs/pretrained_models.html">、Sentence-Transformers ランキング</a>で最高の平均パフォーマンスを示し、 <a href="https://huggingface.co/spaces/mteb/leaderboard">Massive Text Embedding Benchmark (MTEB)</a>リーダーボードでも好位置を獲得しました。このモデルは<a href="https://huggingface.co/microsoft/mpnet-base">、Microsoft/mpnet ベースの</a>モデルを事前トレーニングし、10 億の文のペアのデータセットで微調整されており、文を 768 次元の密なベクトル空間にマッピングします。</p><p>あるいは、特にドメイン固有のデータに合わせて微調整されたモデルなど、使用できる他のモデルも多数あります。</p><p>出力</p>Score: 0.79207325
Product: Patio Sofa Set with Ottoman
Category: Outdoor Furniture
Description: is a versatile and comfortable patio sofa set, including a sofa, ottoman, and coffee table, great for outdoor lounging.

Score: 0.7836937
Product: Patio Sofa Set with Canopy
Category: Outdoor Furniture
Description: is a luxurious and comfortable patio sofa set with a canopy, providing shade and style for outdoor lounging.
<p><em>出力は、選択したモデル、</em><a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/knn-search.html#knn-search-filter-example"><em> フィルター</em></a><em> 、および</em><a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/knn-search.html#tune-approximate-knn-for-speed-accuracy"><em> おおよその kNN チューニング</em></a><em> によって異なる場合があります 。</em></p><p>kNN 検索結果は両方とも「 <em>Outdoor Furniture</em> 」カテゴリにありますが、クエリの一部として「 <em>outdoor</em> 」という単語が明示的に言及されておらず、コンテキストにおけるセマンティクスの理解の重要性が強調されています。</p><p>高密度ベクトル検索にはいくつかの利点があります。</p><ul><li><p>セマンティック検索の有効化</p></li><li><p>非常に大規模なデータセットを処理できるスケーラビリティ</p></li><li><p>幅広いデータタイプを処理できる柔軟性</p></li></ul><p>しかし、<strong>高密度ベクトル探索には独自の課題もあります</strong>。</p><ul><li><p>ユースケースに適した埋め込みモデルの選択</p></li><li><p>モデルが選択されると、ドメイン固有のデータセットでのパフォーマンスを最適化するためにモデルを微調整する必要がある場合があり、このプロセスにはドメイン専門家の関与が求められる。</p></li><li><p>さらに、高次元ベクトルのインデックス作成は計算コストが高くなる可能性がある。</p></li></ul><h2>セマンティック検索 - 学習されたスパース検索</h2><p>別のアプローチとして、セマンティック検索を実行する別の方法である学習済みスパース検索を検討してみましょう。</p><p>スパースモデルとして、数十年にわたる最適化の恩恵を受けている Elasticsearch の Lucene ベースの転置インデックスを活用します。ただし、このアプローチは、BM25 などの語彙スコアリング関数を使用して同義語を単純に追加するだけにとどまりません。代わりに、より深い言語スケールの知識を使用して学習した関連性を組み込み、関連性を最適化します。</p><p><a href="https://www.elastic.co/guide/en/machine-learning/current/ml-nlp-elser.html">Elastic Learned Sparse Encoder は</a>、検索クエリを拡張して元のクエリには存在しない関連用語を含めることにより、以下の例に示すように、<strong>スパース ベクトル埋め込みを改善します</strong>。</p><h3>Elastic Learned Sparse Encoder によるスパースベクトル検索</h3># Elastic Learned Sparse Encoder

response = client.search(index='ecommerce-search', size=2,
query={
  "text_expansion": {
    "ml.tokens": {
      "model_id":"elser_model",
      "model_text":"Comfortable furniture for a large balcony"                
    }
  }
}
)

for hit in response['hits']['hits']:

  score = hit['_score']
  product = hit['_source']['product']
  category = hit['_source']['category']
  description = hit['_source']['description']
  print(f"\nScore: {score}\nProduct: {product}\nCategory: {category}\nDescription: {description}\n")
<p>出力</p>Score: 14.405318
Product: Garden Lounge Set with Side Table
Category: Garden Furniture
Description: is a comfortable and stylish garden lounge set, including a sofa, chairs, and a side table for outdoor relaxation.

Score: 14.281318
Product: Rattan Patio Conversation Set
Category: Outdoor Furniture
Description: is a stylish and comfortable outdoor furniture set, including a sofa, two chairs, and a coffee table, all made of durable rattan material.
<p>この場合の結果には、「<em> 屋外用家具</em> 」に非常に類似した製品を提供する「<em> ガーデン家具</em> 」カテゴリが含まれます。</p><p>「ml.tokens」を分析すると、学習済みスパース検索によって生成されたトークンを含む「rank_features」フィールドを見ると、生成されたさまざまなトークンの中に、「<em>リラックス</em>」（快適）、「<em>ソファ</em>」（家具）、「<em>屋外</em>」（バルコニー）など、検索クエリの一部ではないものの、意味的には関連している用語があることがわかります。</p><p>以下の画像は、用語拡張ありとなしの両方で、クエリとともにこれらの用語の一部を強調表示しています。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt7e869985347b19a7/6a17d875e31791dc2c2d56a1/dd86607fce6137d843a3ec002390eaa988b432f9-1440x502.png" alt="" /><p>ご覧のとおり、このモデルはコンテキスト認識型の検索を提供し、より解釈しやすい結果を提供しながら語彙の不一致の問題を軽減するのに役立ちます。ドメイン固有の再トレーニングが適用されない場合でも、高密度ベクトル モデルよりも優れたパフォーマンスを発揮します。</p><h2>ハイブリッド検索: 語彙検索と意味検索を組み合わせた関連性の高い結果</h2><p>検索に関しては、普遍的な解決策はありません。これらの検索方法にはそれぞれ長所がありますが、課題もあります。ユースケースに応じて、最適なオプションは変わる場合があります。多くの場合、複数の検索方法間で最良の結果が補完的になります。したがって、関連性を高めるために、それぞれの方法の長所を組み合わせることを検討します。</p><p><strong>ハイブリッド検索を</strong>実装する方法は複数あります。線形結合、各スコアへの重み付け、重みの指定が不要な逆ランク融合 (RRF) などです。</p><h3>Elasticsearch: 語彙検索と意味検索の両方の長所を活用</h3># BM25 + Elastic Learned Sparse Encoder (Linear Combination)

response = client.search(index='ecommerce-search', size=2,

query= {
  "bool": {
    "should": [
    {
      "match": {
        "description" : {  
          "query": "A dining table and comfortable chairs for a large balcony",
          "boost": 1
        }
      }
    },                   
    {
      "text_expansion": {
        "ml.tokens": {
          "model_id": "elser_model",
          "model_text": "A dining table and comfortable chairs for a large balcony",
          "boost": 1
        }
      }
     }
    ]
  }
}
)

# The boost value is 1 for the text expansion and match query. This means that the relevance score of the results of these queries are not boosted. You can specify a boost value to give a weight to each score in the sum. The scores will be calculated as: score = boost value * match_score + boost value * text_expansion_score

for hit in response['hits']['hits']:

  score = hit['_score']
  product = hit['_source']['product']
  category = hit['_source']['category']
  description = hit['_source']['description']
  print(f"\nScore: {score}\nProduct: {product}\nCategory: {category}\nDescription: {description}\n")
<p>このコードでは、「<em>大きなバルコニー用のダイニング テーブルと快適な椅子</em>」という値を持つ 2 つのクエリを使用してハイブリッド検索を実行しました。検索語として「<em>家具</em>」を使用する代わりに、探しているものを指定しており、両方の検索で同じフィールド値「説明」を考慮しています。ランキングは、BM25 スコアと ELSER スコアに等しい重み付けをした線形結合によって決定されます。</p><p>出力</p>Score: 31.628141
Product: Garden Dining Set with Swivel Rockers
Category: Garden Furniture
Description: is a functional and comfortable garden dining set, including a table and chairs with swivel rockers for easy movement.

Score: 31.334227
Product: Garden Dining Set with Swivel Chairs
Category: Garden Furniture
Description: is a functional and comfortable garden dining set, including a table and chairs with swivel seats for convenience.
<p>以下のコードでは、クエリに同じ値を使用しますが、逆ランク融合法を使用して BM25 (クエリ パラメータ) と kNN (knn パラメータ) のスコアを結合し、ドキュメントを結合してランク付けします。</p># BM25 + KNN (RRF)

response = client.search(index='ecommerce-search', size=2,
query={
  "bool": {
    "should": [
    {
      "match": {
        "description": {
        "query": "A dining table and comfortable chairs for a large balcony"
        }
      }
    }
    ]
  }
},
knn={
  "field": "description_vector.predicted_value",
  "k": 50,
  "num_candidates": 500,
  "query_vector_builder": {
    "text_embedding": {
      "model_id": "sentence-transformers__all-mpnet-base-v2",
      "model_text": "A dining table and comfortable chairs for a large balcony"
    }
  }
},
rank={
  "rrf": { # Reciprocal rank fusion
    "window_size": 50, # This value determines the size of the individual result sets per query.
    "rank_constant": 20 # This value determines how much influence documents in individual result sets per query have over the final ranked result set.
  }
}
)

for hit in response['hits']['hits']:
        
  rank = hit['_rank']
  category = hit['_source']['category']
  product = hit['_source']['product']
  description = hit['_source']['description']
  print(f"\nRank: {rank}\nProduct: {product}\nCategory: {category}\nDescription: {description}\n")
<p><em>RRF 機能はテクニカル プレビュー段階です。GA の前に構文が変更される可能性があります。</em></p><p>出力</p>Rank: 1
Product: Patio Dining Set with Bench
Category: Outdoor Furniture
Description: is a spacious and functional patio dining set, including a dining table, chairs, and a bench for additional seating.

Rank: 2
Product: Garden Dining Set with Swivel Chairs
Category: Garden Furniture
Description: is a functional and comfortable garden dining set, including a table and chairs with swivel seats for convenience.
<p>ここでは、異なるフィールドと値を使用することもできます。これらの例のいくつかは、 <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/lexical-and-semantic-search-with-elasticsearch/ecommerce_dense_sparse_project.ipynb">Python ノートブック</a>で利用できます。</p><p>ご覧のとおり、Elasticsearch を使用すると、従来の語彙検索とベクトル検索（疎かでも密かでも）の両方の利点を活用でき、目標を達成し<strong>て質問に対する最適な回答を見つけることができます。</strong></p><p>ここで説明したアプローチについてさらに学習したい場合は、次のブログが役立ちます。</p><ul><li><p><a href="https://www.elastic.co/blog/improving-information-retrieval-elastic-stack-hybrid">Elastic Stackでの情報検索の改善：ハイブリッド検索</a></p></li><li><p><a href="https://www.elastic.co/blog/vector-search-elasticsearch-rationale">Elasticsearchのベクトル検索：設計の背後にある理論的根拠</a></p></li><li><p><a href="https://www.elastic.co/blog/lexical-ai-powered-search-elastic-vector-database">Elasticのベクターデータベースで語彙検索とAIを活用した検索を最大限に活用する方法</a></p></li><li><p><a href="https://www.elastic.co/blog/may-2023-launch-sparse-encoder-ai-model">Elastic Learned Sparse Encoder のご紹介: セマンティック検索のための Elastic の AI モデル</a></p></li><li><p><a href="https://www.elastic.co/blog/may-2023-launch-information-retrieval-elasticsearch-ai-model">Elastic Stackでの情報検索の改善: 新しい検索モデルElastic Learned Sparse Encoderのご紹介</a></p></li></ul><p>Elasticsearch は、ベクター検索を構築するために必要なすべてのツールとともに、ベクター データベースを提供します。</p><ul><li><p>Elasticsearch<a href="https://www.elastic.co/elasticsearch/vector-database">ベクターデータベース</a></p></li><li><p>Elasticによる<a href="https://www.elastic.co/enterprise-search/vector-search">ベクトル検索のユース</a>ケース</p></li></ul><h2>まとめ</h2><p>このブログ記事では、Elasticsearch を使用して情報を取得するためのさまざまなアプローチについて検討し、特にテキスト、語彙、意味の検索に焦点を当てました。これを実証するために、eコマース製品情報を含むデータセットを使用してさまざまな検索シナリオを紹介する Python の例を示しました。</p><p>BM25 を使用した従来の語彙検索をレビューし、語彙の不一致などの利点と課題について議論しました。この問題を克服するために、意味的知識を取り入れることの重要性を強調しました。さらに、セマンティック検索を可能にする高密度ベクトル検索について説明し、高次元ベクトルのインデックス作成時の計算コストなど、この検索方法に関連する課題についても説明しました。</p><p>一方、スパースベクトルは圧縮率が非常に高いことを説明しました。そこで、元のクエリには存在しない関連用語を含めるように検索クエリを拡張する Elastic の Learned Sparse Encoder について説明しました。</p><p>検索に関しては、万能の解決策は存在しません。それぞれの検索方法には長所と課題があります。そこで、ハイブリッド検索の概念についても議論しました。</p><p>ご覧のとおり、Elasticsearch を使用すると、従来の語彙検索とベクトル検索の両方の長所を活用できます。</p><p>始める準備はできましたか?利用可能な<a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/lexical-and-semantic-search-with-elasticsearch/ecommerce_dense_sparse_project.ipynb">Python ノートブック</a>を確認し、 <a href="https://cloud.elastic.co/registration?onboarding_token=vectorsearch&amp;cta=cloud-registration&amp;tech=trial&amp;plcmt=article%20content&amp;pg=search-labs">Elastic Cloud の無料トライアル</a>を開始してください。</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/lexical-and-semantic-search-with-elasticsearch</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/lexical-and-semantic-search-with-elasticsearch</guid>
    <category><![CDATA[Vector Database]]></category>
    <category><![CDATA[Python]]></category>
    <category><![CDATA[クエリー言語]]></category>
    <dc:creator><![CDATA[Priscilla Parodi]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltbd7e961f594005e7/6a17d80f033c8d981f6bb009/d240bfef29e9d432069059b312dd044eb76eec6c-1440x840.png" length="0" type="image/png"/>
    <pubDate>Tue, 03 Oct 2023 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Elasticsearch の NLP とベクトル検索によるチャットボット機能の強化]]></title>
    <description><![CDATA[ベクトル検索と NLP がどのように機能してチャットボットの機能を強化するかを探り、Elasticsearch がどのようにプロセスを促進するかを確認します。]]></description>
    <content:encoded><![CDATA[<p>会話型インターフェースは以前から存在しており、顧客サービス、情報検索、タスク自動化など、さまざまなタスクを支援する手段としてますます人気が高まっています。通常、音声アシスタントやメッセージング アプリを通じてアクセスされるこれらのインターフェースは、ユーザーが質問をより効率的に解決できるように人間の会話をシミュレートします。</p><p>テクノロジーの進歩に伴い、チャットボットはユーザーにパーソナライズされたエクスペリエンスを提供しながら、より複雑なタスクを迅速に処理するために使用されるようになりました。自然言語処理 (NLP) により、チャットボットはユーザーの言語を処理し、メッセージの意図を識別し、そこから関連情報を抽出できるようになります。たとえば、固有表現抽出では、テキストを一連のカテゴリに分類して重要な情報を抽出します。感情分析は感情的なトーンを識別し、質問応答はクエリに対する「答え」を識別します。NLP の目標は、アルゴリズムが人間の言語を処理し、大量のテキストの中から関連する文章を見つけたり、テキストを要約したり、新しい独自のコンテンツを生成するなど、これまでは人間だけが実行できたタスクを実行できるようにすることです。</p><p>これらの高度な NLP 機能は、<a href="https://www.elastic.co/what-is/vector-search">ベクトル検索</a>と呼ばれるテクノロジーに基づいて構築されています。Elastic は、ベクトル検索、正確な<a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/knn-search.html#knn-search">k 最近傍 (kNN) 検索と近似 k 最近傍検索の</a>実行、および NLP をネイティブにサポートしており、Elasticsearch で直接カスタム モデルまたは<a href="https://www.elastic.co/guide/en/machine-learning/current/ml-nlp-model-ref.html#ml-nlp-model-ref">サードパーティ モデル</a>を使用できます。</p><p>このブログ記事では、ベクトル検索と NLP がどのように機能してチャットボットの機能を強化するかを説明し、Elasticsearch がどのようにそのプロセスを促進するかを説明します。まず、ベクトル検索の概要を簡単に説明します。</p><h2>ベクトル検索</h2><p>人間は書かれた言語の意味と文脈を理解できますが、機械は同じことはできません。ここでベクトルが登場します。テキストをベクトル表現（テキストの意味の数値表現）に変換することで、マシンはこの制限を克服できます。従来の検索と比較すると、キーワードや頻度に基づく語彙検索に依存するのではなく、ベクトルでは数値に対して定義された演算を使用してテキスト データの処理が可能になります。</p><p>これにより、ベクトル検索では、クエリベクトルが与えられた場合に類似性を表す「埋め込み空間」内の距離を使用して、類似の概念またはコンテキストを共有するデータを見つけることができます。データが類似している場合、対応するベクトルも同様になります。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt53a615a8ba0ac931/6a17d795fbc5f8b257491910/08542abf8108aace288745b1aca8579b476ddc1b-1440x618.png" alt="" /><p>ベクトル検索は NLP アプリケーションで利用されるだけでなく、画像やビデオの処理など、非構造化データが関係するさまざまな他の領域でも使用されます。</p><p>チャットボット フローでは、ユーザーのクエリに対して複数のアプローチが可能であり、その結果、情報検索を改善してユーザー エクスペリエンスを向上させるさまざまな方法が存在します。それぞれの選択肢には独自の利点と欠点があるため、利用可能なデータとリソース、トレーニング時間（該当する場合）、および予想される精度を考慮することが重要です。次のセクションでは、質問応答 NLP モデルのこれらの側面について説明します。</p><h2>質問応答</h2><p>質問応答 (QA) モデルは、自然言語で尋ねられた質問に答えるように設計された NLP モデルの一種です。ユーザーが、ドキュメント内に既存のターゲット回答がなく、複数のリソースから回答を推測する必要がある質問がある場合、生成型 QA モデルが役立ちます。ただし、これらのモデルは計算コストが高く、ドメイン関連のトレーニングに大量のデータが必要になる可能性があり、この方法はドメイン外の質問を処理するのに特に価値があるにもかかわらず、状況によっては実用的ではない可能性があります。</p><p>一方、ユーザーが特定のトピックについて質問し、実際の回答がドキュメント内に存在する場合は、抽出型 QA モデルを使用できます。これらのモデルは、ソース ドキュメントから回答を直接抽出し、透明性と検証性に優れた結果を提供するため、質問にシンプルかつ効率的に回答したい企業や組織にとって、より実用的なオプションとなります。</p><p>以下の例は、 <a href="https://huggingface.co/deepset/minilm-uncased-squad2">Hugging Face で利用可能で</a>Elasticsearch にデプロイされた、事前トレーニング済みの抽出 QA モデルを使用して、特定のコンテキストから回答を抽出する方法を示しています。</p>POST _ml/trained_models/deepset__minilm-uncased-squad2/deployment/_infer
{
    "docs": [{"text_field": "Canvas is a data visualization and presentation application within Kibana. With Canvas, live data can be pulled directly from Elasticsearch and combined with colors, images, text, and other customized options to create dynamic, multi-page displays."}],
    "inference_config": {"question_answering": {"question": "What is Kibana Canvas?"}}
}


{
  "predicted_value": "a data visualization and presentation application",
  "start_offset": 10,
  "end_offset": 59,
  "prediction_probability": 0.28304219431376443
}
<p><a href="https://www.elastic.co/guide/en/machine-learning/current/ml-nlp-deploy-models.html">トレーニング済みのモデルをデプロイします。</a></p><p><a href="https://www.elastic.co/guide/en/machine-learning/current/ml-nlp-ner-example.html#ex-ner-ingest">推論取り込みパイプラインにモデルを追加します。</a></p><p>ユーザーのクエリを処理して情報を取得する方法はさまざまであり、非構造化データを扱う場合には、複数の言語モデルとデータ ソースを使用することが効果的な代替手段となります。これを説明するために、選択されたドキュメントから抽出されたデータを考慮してクエリに回答するために使用されるチャットボットのデータ処理の例を示します。</p><h2>チャットボットのデータ処理：NLPとベクトル検索</h2><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blta418a9c54bb16cf9/6a17d7975772624b371bca43/c2d1a2f110e937b1d3e5df0d5caac3c906c98fb0-1440x748.png" alt="" /><p>上記のように、チャットボットのデータ処理は 3 つの部分に分けられます。</p><ul><li><p><strong>ベクター処理:</strong>この部分はドキュメントをベクター表現に変換します。</p></li><li><p><strong>ユーザー入力処理:</strong>この部分では、ユーザークエリから関連情報を抽出し、セマンティック検索とハイブリッド検索を実行します。</p></li><li><p><strong>最適化:</strong>この部分には監視が含まれており、チャットボットの信頼性、最適なパフォーマンス、優れたユーザー エクスペリエンスを確保するために重要です。</p></li></ul><h2>ベクトル処理</h2><p><strong>処理</strong>部分では、まず各ドキュメントの構成要素を決定し、各要素をベクトル表現に変換します。これらの表現は、さまざまなデータ形式に対して作成できます。</p><p>埋め込みを計算するために使用できる方法は、事前トレーニング済みのモデルやライブラリなど、さまざまあります。</p><p>これらの表現に対する検索と取得の有効性は、既存のデータと、使用される方法の品質と関連性に依存することに注意することが重要です。</p><p>ベクトルが計算されると、 <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/dense-vector.html">dense_vector</a>フィールド タイプを使用して Elasticsearch に保存されます。</p>PUT &lt;target&gt;
{
  "mappings": {
    "properties": {
      "doc_part_vector": {
        "type": "dense_vector",
        "dims": 3
      },
      "doc_part" : {
        "type" : "keyword"
      }
    }
  }
}
<h2>チャットボットのユーザー入力処理</h2><p><strong>ユーザー</strong>側では、質問を受け取った後、先に進む前にそこから可能な限りすべての情報を抽出すると便利です。これはユーザーの意図を理解するのに役立ちます。この場合、それを支援するために<a href="https://huggingface.co/dslim/bert-base-NER">名前付きエンティティ認識モデル (NER)</a>を使用しています。NER は、名前付きエンティティを識別し、事前定義されたエンティティ カテゴリに分類するプロセスです。</p>POST _ml/trained_models/dslim__bert-base-ner/deployment/_infer
{
  "docs": { "text_field": "How many people work for Elastic?"}
}


{
  "predicted_value": "How many people work for [Elastic](ORG&amp;Elastic)?",
  "entities": [
    {
      "entity": "Elastic",
      "class_name": "ORG",
      "class_probability": 0.4993975435876747,
      "start_pos": 25,
      "end_pos": 32
    }
  ]
}
<p>必須のステップではありませんが、構造化データや上記または別の NLP モデルの結果を使用してユーザーのクエリを分類することで、<a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/knn-search.html#knn-search-filter-example">フィルター</a>を使用して kNN 検索を制限できます。これにより、処理する必要があるデータの量が削減され、パフォーマンスと精度が向上します。</p>    "filter": {
      "term": {
        "org": "Elastic"
      }
    }
<h2>セマンティック検索とハイブリッド検索</h2><p>プロンプトはユーザーのクエリから生成され、チャットボットは変動性と曖昧性を持つ人間の言語を処理する必要があるため、<a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/knn-search.html#semantic-search">セマンティック検索は</a>最適です。Elasticsearch では、クエリ文字列と<a href="https://huggingface.co/sentence-transformers/msmarco-MiniLM-L-12-v3">埋め込みモデル</a>の ID を query_vector_builder オブジェクトに渡すことで、セマンティック検索を 1 ステップで実行できます。これにより、クエリがベクトル化され、kNN<a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/search-search.html">検索</a>が実行され、クエリに意味的に最も近い上位 k 個の一致が取得されます。</p>POST /&lt;target&gt;/_search
{
  "knn": {
    "field": "doc_part_vector",
    "k": 5,
    "num_candidates": 20,
    "query_vector_builder": {
      "text_embedding": {
        "model_id": "&lt;text-embedding-model-id&gt;",
        "model_text": "&lt;query_string&gt;"
      }
    }
  }
 }
<p><a href="https://www.elastic.co/guide/en/machine-learning/8.7/ml-nlp-text-emb-vector-search-example.html">エンドツーエンドの例: テキスト埋め込みモデルを展開し、セマンティック検索に使用する方法。</a>Elasticsearch は、<strong>疎モデルである</strong>Okapi BM25 の Lucene 実装を使用してテキストクエリの関連性をランク付けし、<strong>密モデルはセマンティック検索</strong>に使用されます。ベクトル一致とテキスト <strong>クエリから取得された</strong><a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/knn-search.html#_combine_approximate_knn_with_other_features"><strong> 一致の両方の長所</strong></a><strong> を組み合わせる には 、</strong> ハイブリッド検索 を実行できます。</p>POST &lt;target&gt;/_search
{
  "query": {
          "match": {
            "content": {
              "query": "&lt;query_string&gt;"
            }
        }
  },
  "knn": {
    "field": "doc_part_vector",
    "query_vector_builder": {
      "text_embedding": {
    "model_id": "&lt;text-embedding-model-id&gt;",
     "model_text": "&lt;query_string&gt;"
      }
    },
    "filter": {
      "term": {
        "org": "Elastic"
      }
    }
  }
}
<h3>疎モデルと密モデルの両方を組み合わせると、多くの場合、最良の結果が得られます。</h3><p>通常、スパース モデルは短いクエリと特定の用語で優れたパフォーマンスを発揮しますが、密なモデルはコンテキストと関連付けを活用します。これらの方法がどのように比較され、相互に補完し合うかについて詳しく知りたい場合は、ここで、検索用に特別にトレーニングされた 2 つの高密度モデルに対して BM25 をベンチマークします。</p><p>最も関連性の高い結果は通常、ユーザーに最初に提供される回答になります。_scoreは、返されたドキュメントの<strong>関連性</strong>を判断するために使用される数値です。</p><h2>チャットボットの最適化</h2><p>チャットボットのユーザー エクスペリエンス、パフォーマンス、信頼性を向上させるには、ハイブリッド スコアリングを適用することに加えて、次のアプローチを組み込むことができます。<strong>感情分析:</strong>ダイアログが展開されるときにユーザーのコメントや反応を認識できるように、<a href="https://huggingface.co/distilbert-base-uncased-finetuned-sst-2-english">感情分析モデル</a>を組み込むことができます。</p>POST _ml/trained_models/distilbert-base-uncased-finetuned-sst-2-english/deployment/_infer
{
  "docs": { "text_field": "That was not my question!"}
}


{
  "predicted_value": "NEGATIVE",
  "prediction_probability": 0.980080439016437
}
<p><a href="https://www.elastic.co/blog/chatgpt-elasticsearch-openai-meets-private-data"><strong>GPT の機能</strong></a><strong>:</strong>全体的なエクスペリエンスを向上させる代わりに、Elasticsearch の検索関連性と OpenAI の GPT 質問応答機能を組み合わせ、 <a href="https://platform.openai.com/docs/guides/chat">Chat Completion API</a>を使用して、上位 k 件のドキュメントをコンテキストとして考慮し、ユーザーモデルが生成した応答を返すことができます。<em>プロンプト:&lt;user_question&gt; 「このドキュメント のみを使用して、この質問&lt;top_search_result&gt; に回答してください」</em></p><p><strong>可観測性:</strong>あらゆるチャットボットのパフォーマンスを確保することは非常に重要であり、これを実現するには監視が不可欠な要素です。チャットボットのやり取りをキャプチャするログに加えて、応答時間、待ち時間、その他の関連するチャットボットのメトリックを追跡することが重要です。これにより、パターンや傾向を特定し、異常を検出することさえ可能になります。Elastic <a href="https://www.elastic.co/blog/monitor-openai-api-gpt-models-opentelemetry-elastic">Observability</a>ツールを使用すれば、こうした情報を収集・分析できます。</p><h2>まとめ</h2><p>このブログ記事では、NLP とベクトル検索とは何かを説明し、ドキュメントのベクトル表現から抽出されたデータを考慮してユーザーのクエリに応答するために使用されるチャットボットの例を詳しく説明します。</p><p>実証されているように、NLP とベクトル検索を使用すると、チャットボットは構造化されたターゲット データを超えた複雑なタスクを実行できます。これには、複数のデータ ソースと形式をコンテキストとして使用して推奨事項を作成し、特定の製品またはビジネス関連のクエリに回答するとともに、パーソナライズされたユーザー エクスペリエンスを提供することも含まれます。</p><p>ユースケースは、顧客からの問い合わせに対応して顧客サービスを提供することから、開発者のクエリを支援し、ステップバイステップのガイダンスを提供したり、推奨事項を提案したり、タスクを自動化したりすることまで多岐にわたります。目標と既存のデータに応じて、他のモデルや方法を活用してさらに優れた結果を達成し、全体的なユーザー エクスペリエンスを向上させることもできます。</p><p>このトピックに関して役立つと思われるリンクをいくつか紹介します。</p><ol><li><p><a href="https://www.elastic.co/blog/how-to-deploy-natural-language-processing-nlp-getting-started">自然言語処理（NLP）の導入方法：はじめに</a></p></li><li><p><a href="https://www.elastic.co/blog/overview-image-similarity-search-in-elastic">Elasticsearchにおける画像類似検索の概要</a></p></li><li><p><a href="https://www.elastic.co/blog/chatgpt-elasticsearch-openai-meets-private-data">ChatGPTとElasticsearch: OpenAIとプライベートデータの出会い</a></p></li><li><p><a href="https://www.elastic.co/blog/monitor-openai-api-gpt-models-opentelemetry-elastic">Monitor OpenAI API and GPT models with OpenTelemetry and Elastic（OpenTelemetryとElasticを利用してOpenAI APIとGPTモデルを監視）</a></p></li><li><p><a href="https://www.elastic.co/blog/why-technology-leaders-need-vector-search">ITリーダーが検索エクスペリエンスを向上させるためにベクトル検索を必要とする5つの理由</a></p></li></ol><p>Elasticsearch に NLP とネイティブベクトル検索を組み込むことで、そのスピード、スケーラビリティ、検索機能を活用し、構造化データか非構造化データかを問わず大量のデータを処理できる、非常に効率的で効果的なチャットボットを作成できます。</p><p>始める準備はできましたか?<a href="https://cloud.elastic.co/registration?onboarding_token=vectorsearch&amp;cta=cloud-registration&amp;tech=trial&amp;plcmt=article%20content&amp;pg=search-labs">Elastic Cloud の無料トライアル</a>を開始してください。</p><p><em>このブログ投稿では、それぞれの所有者が所有および運営するサードパーティ製の生成 AI ツールを使用したり、参照したりする場合があります。Elastic はサードパーティ製ツールを一切管理しておらず、そのコンテンツ、操作、使用、またそのようなツールの使用によって発生する損失や損害については一切責任を負いません。個人情報、機密情報、秘密情報を扱う AI ツールを使用する場合は注意してください。送信したデータは AI のトレーニングやその他の目的で使用される場合があります。お客様が提供する情報が安全に保管され、機密性が保たれるという保証はありません。生成 AI ツールを使用する前に、プライバシー慣行と利用規約をよく理解しておく必要があります。</em></p><p><em>Elastic、Elasticsearch および関連するマークは、米国およびその他の国における Elasticsearch NV の商標、ロゴ、または登録商標です。その他すべての会社名および製品名は、それぞれの所有者の商標、ロゴ、または登録商標です。</em></p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/enhancing-chatbot-capabilities-with-nlp-and-vector-search-in-elasticsearch</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/enhancing-chatbot-capabilities-with-nlp-and-vector-search-in-elasticsearch</guid>
    <category><![CDATA[Vector Database]]></category>
    <dc:creator><![CDATA[Priscilla Parodi]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5c545fc80b6d79d6/6a170214839dfad776dcfd6f/d968e646240cd3ef7c79b5124d562a5f951d812b-1440x840.png" length="0" type="image/png"/>
    <pubDate>Wed, 21 Jun 2023 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[ベクトル場を用いたテキスト類似性検索]]></title>
    <description><![CDATA[この投稿では、テキスト埋め込みと Elasticsearch の新しい dense_vector タイプを使用して類似性検索をサポートする方法について説明します。]]></description>
    <content:encoded><![CDATA[<p>Elasticsearch は、<a href="https://www.elastic.co/about/history-of-elasticsearch">レシピ検索エンジン</a>として誕生して以来、高速で強力な全文検索を提供するように設計されました。このような背景から、テキスト検索の改善は、ベクターに関する私たちの継続的な取り組みの重要な動機となっています。Elasticsearch 7.0 では、高次元ベクトル用の実験的なフィールドタイプを導入し、7.3 リリースでは、ドキュメント スコアリングでこれらのベクトルを使用するためのサポートが追加されました。</p><p>この投稿では、テキスト類似性検索と呼ばれる特定の手法に焦点を当てています。このタイプの検索では、ユーザーは短いフリーテキストクエリを入力し、ドキュメントはクエリとの類似性に基づいてランク付けされます。テキストの類似性は、さまざまな使用例で役立ちます。</p><ul><li><p><strong>質問回答:</strong>よくある質問のコレクションが与えられた場合、ユーザーが入力した質問に類似した質問を見つけます。</p></li><li><p><strong>記事検索:</strong>研究記事のコレクションで、ユーザーのクエリに密接に関連するタイトルの記事を返します。</p></li><li><p><strong>画像検索:</strong>キャプション付きの画像のデータセットで、ユーザーの説明に類似したキャプションを持つ画像を検索します。</p></li></ul><p>類似性検索の直接的なアプローチは、クエリと共有する単語の数に基づいてドキュメントをランク付けすることです。しかし、共通する単語がほとんどない場合でも、ドキュメントがクエリと類似している可能性があります。より堅牢な類似性の概念では、構文と<a href="https://en.wikipedia.org/wiki/Semantic_similarity">意味の</a>内容も考慮されます。</p><p>自然言語処理 (NLP) コミュニティは、単語や文章を数値ベクトルとしてエンコードするテキスト埋め込みと呼ばれる手法を開発しました。これらのベクトル表現は、テキストの言語コンテンツをキャプチャするように設計されており、クエリとドキュメント間の類似性を評価するために使用できます。</p><p>この投稿では、テキスト埋め込みと Elasticsearch の dense_vector タイプを使用して類似性検索をサポートする方法について説明します。まず、埋め込み技術の概要を説明し、次に Elasticsearch を使用した類似性検索の簡単なプロトタイプを段階的に説明します。</p><strong>注:</strong>検索でテキスト埋め込みを使用することは、複雑かつ進化を続ける領域です。このブログは、特定のアーキテクチャや実装を推奨するものではありません。<a href="https://www.elastic.co/what-is/vector-search">ベクター検索</a>の力を利用して検索エクスペリエンスを強化する方法を学ぶには、ここから始めてください。<h2>テキスト埋め込みとは何ですか?</h2><p>さまざまな種類のテキスト埋め込みと、従来の検索アプローチとの比較を詳しく見てみましょう。</p><h3>単語埋め込み</h3><p><a href="https://en.wikipedia.org/wiki/Word_embedding">単語埋め込み</a>モデルは、単語を密な数値ベクトルとして表現します。これらのベクトルは、単語の意味特性を捉えることを目的としています。つまり、ベクトルが近い単語は、意味の点で類似しているはずです。適切な埋め込みでは、ベクトル空間の方向は単語の意味のさまざまな側面に結び付けられます。たとえば、「カナダ」のベクトルは、ある方向では「フランス」に近く、別の方向では「トロント」に近くなる可能性があります。</p><p>NLP および検索コミュニティは、かなり長い間、単語のベクトル表現に興味を抱いてきました。過去数年間、多くの従来のタスクがニューラル ネットワークを使用して再検討され、単語埋め込みへの関心が再び高まりました。<a href="https://papers.nips.cc/paper/5021-distributed-representations-of-words-and-phrases-and-their-compositionality.pdf">word2vec</a>や<a href="https://nlp.stanford.edu/pubs/glove.pdf">GloVe</a>など、いくつかの成功した単語埋め込みアルゴリズムが開発されました。これらのアプローチでは、大規模なテキスト コレクションを利用し、各単語が出現するコンテキストを調べてベクトル表現を決定します。</p><ul><li><p>word2vec Skip-gram モデルは、ニューラル ネットワークをトレーニングして、文中の単語の周囲のコンテキスト ワードを予測します。ネットワークの内部重みによって単語の埋め込みが与えられます。</p></li><li><p>GloVe では、単語の類似性は、他の文脈上の単語と一緒に出現する頻度によって決まります。このアルゴリズムは、単語の共起カウントに基づいて単純な線形モデルをトレーニングします。</p></li></ul><p>多くの研究グループが、Wikipedia や Common Crawl などの大規模なテキスト コーパスで事前トレーニングされたモデルを配布しており、ダウンロードして下流のタスクに組み込むのが便利になっています。事前トレーニング済みのバージョンが直接使用される場合もありますが、特定のターゲット データセットとタスクに合わせてモデルを調整すると役立つ場合があります。これは通常、事前トレーニング済みのモデルに対して「微調整」ステップを実行することによって実現されます。</p><p>単語埋め込みは非常に堅牢かつ効果的であることが証明されており、機械翻訳や感情分類などの NLP タスクでは、個々のトークンの代わりに埋め込みを使用するのが一般的になっています。</p><h3>文の埋め込み</h3><p>最近では、研究者たちは単語だけでなく、より長いテキストセクションを表す埋め込み技術に注目し始めています。現在のアプローチのほとんどは、複雑なニューラル ネットワーク アーキテクチャに基づいており、意味情報の取得を支援するためにトレーニング中にラベル付けされたデータを組み込むこともあります。</p><p>一度トレーニングされると、モデルは文を受け取り、文脈内の各単語のベクトルと文全体のベクトルを生成できるようになります。単語埋め込みと同様に、多くのモデルの事前トレーニング済みバージョンが利用可能であり、ユーザーはコストのかかるトレーニング プロセスを省略できます。トレーニング プロセスは大量のリソースを消費する可能性がありますが、モデルの呼び出しははるかに軽量です。文埋め込みモデルは通常、リアルタイム アプリケーションの一部として使用できるほど高速です。</p><p>一般的な文埋め込み手法としては、 <a href="https://arxiv.org/abs/1705.02364">InferSent</a> 、 <a href="https://arxiv.org/abs/1803.11175">Universal Sentence Encoder</a> 、 <a href="https://arxiv.org/abs/1802.05365">ELMo</a> 、 <a href="https://arxiv.org/abs/1810.04805">BERT</a>などがあります。単語や文の埋め込みの改善は活発に研究されている分野であり、今後さらに強力なモデルが導入される可能性があります。</p><h3>従来の検索アプローチとの比較</h3><p>従来の情報検索では、テキストを数値ベクトルとして表現する一般的な方法は、語彙内の単語ごとに 1 つの次元を割り当てることです。テキストのベクトルは、語彙内の各用語が出現する回数に基づいて決定されます。テキストを表現するこの方法は、文の構造を考慮せずに単語の出現回数を単純に数えるため、「bag of words」と呼ばれることがよくあります。</p><p>テキスト埋め込みは、いくつかの重要な点で従来のベクトル表現と異なります。</p><ul><li><p>エンコードされたベクトルは密度が高く、比較的低次元であり、多くの場合 100 次元から 1,000 次元の範囲になります。対照的に、Bag of Words ベクトルはスパースであり、50,000 以上の次元で構成できます。埋め込みアルゴリズムは、テキストの意味をモデル化する一環として、テキストを低次元空間にエンコードします。理想的には、同義語やフレーズは、新しいベクトル空間で同様の表現になります。</p></li><li><p>文の埋め込みでは、ベクトル表現を決定するときに単語の順序を考慮できます。たとえば、「tune in」というフレーズは、「in tune」とはまったく異なるベクトルとしてマッピングされる場合があります。</p></li><li><p>実際には、文の埋め込みはテキストの大きなセクションにはうまく一般化されないことがよくあります。これらは通常、短い段落よりも長いテキストを表すために使用されません。</p></li></ul><h2>類似性検索に埋め込みを使用する</h2><p>質問と回答の大きなコレクションがあったとしましょう。ユーザーは質問をすることができ、私たちはユーザーが回答を見つけやすくするために、コレクション内で最も類似した質問を取得したいと考えています。</p><p>テキスト埋め込みを使用すると、類似の質問を取得できるようになります。</p><ul><li><p>インデックス作成中に、各質問は文埋め込みモデルに渡され、数値ベクトルが生成されます。</p></li><li><p>ユーザーがクエリを入力すると、同じ文埋め込みモデルが実行され、ベクトルが生成されます。回答をランク付けするために、各質問とクエリ ベクトル間のベクトル類似度を計算します。埋め込みベクトルを比較する場合、<a href="https://en.wikipedia.org/wiki/Cosine_similarity">コサイン類似度を</a>使用するのが一般的です。</p></li></ul><p><a href="https://github.com/jtibshirani/text-embeddings">このリポジトリには、</a> Elasticsearch でこれをどのように実現できるかの簡単な例が示されています。メイン スクリプトは、 <a href="https://github.com/elastic/rally-tracks/tree/master/so">StackOverflow データセット</a>から約 20,000 件の質問にインデックスを付け、ユーザーがデータセットに対してフリーテキスト クエリを入力できるようにします。</p><p>スクリプトの各部分については後ほど詳しく説明しますが、まずはいくつかの例の結果を見てみましょう。多くの場合、この方法は、クエリとインデックス付けされた質問の間に強い単語の重複がない場合でも類似性を捉えることができます。</p><ul><li><p>「ファイルを圧縮」と入力すると、「フォルダとファイルの圧縮/解凍」が返されます。</p></li><li><p>「何かが IP であるかどうかを判断する」は、「文字列が IP であるかホスト名であるかを判断する方法」を返します。</p></li><li><p>「バイトを倍精度浮動小数点数に変換する」は「Pythonでバイトを浮動小数点数に変換する」を返します。</p></li></ul><h3>実装の詳細</h3><p><a href="https://github.com/jtibshirani/text-embeddings/blob/blog/src/main.py">スクリプトは、</a> TensorFlow に埋め込みモデルをダウンロードして作成することから始まります。Google の Universal Sentence Encoder を選択しましたが、他の多くの埋め込み方法を使用することもできます。スクリプトは追加のトレーニングや微調整を行わずに、埋め込みモデルをそのまま使用します。</p><p>次に、質問のタイトル、タグ、およびベクトルとしてエンコードされた質問のタイトルのマッピングを含む Elasticsearch インデックスを作成します。</p>"mappings": {
"properties": {
"title": {
"type": "text"
},
"title_vector": {
"type": "dense_vector",
"dims": 512
}
"tags": {
"type": "keyword"
},
...
}
}
<p>dense_vector のマッピングでは、ベクトルに含まれる次元の数を指定する必要があります。title_vector フィールドにインデックスを作成するとき、Elasticsearch はマッピングで指定されたのと同じ数のディメンションがあるかどうかを確認します。</p><p>ドキュメントのインデックスを作成するには、質問のタイトルを埋め込みモデルに渡して数値配列を取得します。この配列は、title_vector フィールドのドキュメントに追加されます。</p><p>ユーザーがクエリを入力すると、テキストは最初に同じ埋め込みモデルに渡され、パラメータ query_vector に保存されます。7.3 以降、Elasticsearch はネイティブ スクリプト言語で<a href="https://www.elastic.co/guide/en/elasticsearch/reference/7.6/query-dsl-script-score-query.html#vector-functions">cosineSimilarity 関数</a>を提供します。したがって、ユーザーのクエリとの類似性に基づいて質問をランク付けするには、script_score クエリを使用します。</p>{
"script_score": {
"query": {"match_all": {}},
"script": {
"source": "cosineSimilarity(params.query_vector, 'title_vector') + 1.0",
"params": {"query_vector": query_vector}
}
}
}
<p>新しいクエリごとに<a href="https://www.elastic.co/guide/en/elasticsearch/reference/7.6/modules-scripting-using.html#prefer-params">スクリプト () が再コンパイルされるのを避ける</a>ために、クエリ ベクトルをスクリプト パラメータとして渡すようにします。Elasticsearch では負のスコアが許可されないため、コサイン類似度に 1 を追加する必要があります。</p><p>|<strong>注:</strong>このブログ投稿では、元々、Elasticsearch 7.3 で使用可能だった<a href="https://www.elastic.co/guide/en/elasticsearch/reference/7.3/query-dsl-script-score-query.html#vector-functions">ベクトル関数の別の構文</a>を使用していましたが、7.6 では非推奨になりました。|</p><h3>重要な制限事項</h3><p>script_score クエリは、制限的なクエリをラップし、返されるドキュメントのスコアを変更するように設計されています。ただし、match_all クエリを提供しているため、スクリプトはインデックス内のすべてのドキュメントに対して実行されます。これは、Elasticsearch におけるベクトル類似性の現在の制限です。ベクトルはドキュメントのスコアリングには使用できますが、最初の検索手順では使用できません。ベクトル類似性に基づく検索のサポートは、<a href="https://github.com/elastic/elasticsearch/issues/42326">現在進行中の作業</a>の重要な領域です。</p><p>すべてのドキュメントをスキャンすることを回避し、高速なパフォーマンスを維持するために、match_all クエリをより選択的なクエリに置き換えることができます。検索に使用する適切なクエリは、特定のユースケースによって異なる可能性があります。</p><p>上記ではいくつか有望な例を見てきましたが、結果にはノイズが多く直感的でない場合もあることに注意することが重要です。例えば、「ファイルを圧縮する」では、「部分的な.csproj」にも高いスコアが割り当てられます。ファイル」と「.pyc を回避する方法」ファイルですか？また、メソッドが予期しない結果を返す場合、その問題をデバッグする方法が必ずしも明確であるとは限りません。各ベクトル要素の意味は不透明であることが多く、解釈可能な概念に対応していません。単語の重複に基づく従来のスコアリング手法を使用すると、「なぜこのドキュメントのランクが高いのか」という質問に答えるのが簡単になることがよくあります。</p><p>前述のように、このプロトタイプは埋め込みモデルをベクトル フィールドで使用する方法の例として提供されており、本番環境で使用できるソリューションとして提供されているわけではありません。新しい検索戦略を開発するときは、一致クエリなどの強力なベースラインと比較しながら、独自のデータでそのアプローチがどのように機能するかをテストすることが重要です。確実な結果を得るには、ターゲット データセットの埋め込みモデルを微調整したり、単語レベルのクエリ拡張などの埋め込みを組み込むさまざまな方法を試したりするなど、戦略に大きな変更を加える必要がある場合があります。</p><h2>結論</h2><p>埋め込み技術は、テキストの言語コンテンツを取得する強力な方法を提供します。埋め込みをインデックス化し、ベクトル距離に基づいてスコアリングすることで、単語レベルの重複を超えた類似性の概念を使用してドキュメントを比較できます。</p><p>ベクター フィールド タイプに基づいた機能をさらに導入することを楽しみにしています。検索にベクトルを使用するのは、微妙で発展途上の領域です。いつものように、 <a href="https://github.com/elastic/elasticsearch">Github</a>や<a href="https://discuss.elastic.co/">ディスカッション フォーラム</a>で、皆さんの使用事例や体験談をお聞かせください。</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/text-similarity-search-with-vectors-in-elasticsearch</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/text-similarity-search-with-vectors-in-elasticsearch</guid>
    <category><![CDATA[Vector Database]]></category>
    <dc:creator><![CDATA[Julie Tibshirani]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt91384bd99b05cd28/6a17e7e01d1b835cc593e467/c633ed737add7d22a7d65b3ca5c56480ef3d8b2c-720x420.jpg" length="0" type="image/jpeg"/>
    <pubDate>Thu, 06 Oct 2022 00:00:00 GMT</pubDate>
  </item>
  </channel>
</rss>