1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

semantic_textを使ってElasticsearch 8.15以降で簡単にベクトル検索を実装する

Last updated at Posted at 2024-12-01

はじめに

ベクトル検索は目的のドキュメントをキーワードマッチ以外の意味的な近さで見つけたり、テキスト以外のマルチモーダルな画像や音声などの情報の検索に使ったりといったことが可能で、特にここ1・2年は生成AIと組み合わせたRAGというユースケースの登場で非常に広く利用されるようになりました。ただElasticsearchでベクトル検索を利用する場合、最初のセットアップをするまでに必要な工程が煩雑であることは否めません。ベクトル検索の詳しい手順は他の記事で書いていますのでそちらを参照してください。

そこでElasticsearchではバージョン8.15からsemantic_textというフィールド型を導入しました。このsemantic_textを利用するとベクトル検索のセットアップが格段に容易になりますので、この記事ではその方法を紹介したいと思います。

バージョン8.16の時点で、semantic_textフィールドはbetaです。また利用にはPratinum Licenseが必要です。参照

semantic_textフィールドの特徴

semantic_textフィールドは、テキストデータに対するセマンティック(意味)検索実現のために特化したフィールドタイプです。これまでベクトル検索を利用してセマンティック検索を実現しようと思うと、インデックスに対してdense_vectorフィールドを定義し、利用するモデルが出力する次元数のベクトルを保存できるようにセットアップするなどの、比較的複雑な設定が必要でした。この定義にはある程度利用するMLモデルがどのようなものであるかの技術的な知識が求められます。しかしsemantic_textフィールドを使うと、Inference Endpoint経由でどのMLモデル(エンドポイント)を利用するかを決めるだけで、適切な形でテキストをembeddingし、インデックスに保存したり検索したりすることができます。

また、それぞれのモデルは1回でembeddingできるテキストの大きさに上限があり、大きなテキストを扱うにはチャンキングという、テキストを小さなチャンクに分割するテクニックを独自に実装する必要がありました。しかしsemantic_textフィールドを利用すると、このチャンキングもElasticsearchが自動で行ってくれるようになります。

実装の流れ

semantic_textフィールドを利用した検索機能実装の流れは以下のようになります。

  1. Inference Endpointを作成
  2. Index定義
  3. データ投入
  4. 検索実行

このように書くとほとんど通常の検索と同じで実装できそうなことがわかりますね。それぞれ見ていきましょう。

Inference Endpointの作成

Elasticsearchの上でテキストデータをベクトルデータにembeddingするには、適切なMLモデルをElasticsearchのMLノードにデプロイする必要があります。具体的に利用したいモデルがHugging Faceで公開されている場合は、そのモデルをElandを使ってまずElasticsearchにアップロードする必要がありますが、ここではElasticがデフォルトでサポートしているE5モデルを利用することにします。E5モデルは日本語を含む多言語に対してembeddingをすることのできる大変便利なモデルです。Hugging Faceからのモデルアップロードについてはこちらの記事を参照してください。

モデルがElasticsearchにデプロイされたら、Create inference APIを利用してInference Endpointを作成します。

PUT _inference/text_embedding/my-e5-model
{
  "service": "elasticsearch",
  "service_settings": {
    "num_allocations": 1,
    "num_threads": 1,
    "model_id": ".multilingual-e5-small" 
  }
}

これでmy-e5-modelという名前のエンドポイントが作成されました。試しにこのエンドポイントを使って日本語テキストをembeddingしてみましょう。

POST _inference/text_embedding/my-e5-model
{
  "input": "試しにこのエンドポイントを使って日本語テキストをembeddingしてみましょう。"
}

すると以下のような結果が得られるはずです。

{
  "text_embedding": [
    {
      "embedding": [
        0.09948257,
        -0.040724847,
        -0.01904885,
        (...)
      ]
    }
  ]
}

Index定義とデータの投入

エンドポイントが作成できたら、これを使ってsemantic_textフィールドを定義します。今回は単に"content"というフィールドを持つだけのドキュメントを扱うことにしましょう。

Index定義は以下のようになります。

PUT semantic_text_test
{
  "mappings": {
    "properties": {
      "content": {
        "type": "text",
        "copy_to": "content_semantic"
      },
      "content_semantic": {
        "type": "semantic_text",
        "inference_id": "my-e5-model"
      }
    }
  }
}

content_semanticフィールドにsemantic_textフィールドタイプを適用しています。したがってこのcontent_semanticフィールドを利用してセマンティック検索が実現できます。このフィールドに対して直接データを投入しても良いのですが、そうすると元の本文の情報が失われてしまうため、まずはtext型のcontentフィールドに対してデータをIngestして、そこからcopy_toを使ってテキストデータをcontent_semanticフィールドにコピーしています。

実際にデータを投入してみましょう。

POST semantic_text_test/_doc/1
{
    "content": """# はじめに
ベクトル検索は目的のドキュメントをキーワードマッチ以外の意味的な近さで見つけたり、テキスト以外のマルチモーダルな画像や音声などの情報の検索に使ったりといったことが可能で、特にここ1・2年は生成AIと組み合わせたRAGというユースケースの登場で非常に広く利用されるようになりました。
    """
}

内容を確認します。

GET semantic_text_test/_search

結果

{
  "hits": {
    "hits": [
      {
        "_index": "semantic_text_test",
        "_id": "1",
        "_score": 1,
        "_source": {
          "content_semantic": {
            "inference": {
              "inference_id": "my-e5-model",
              "model_settings": {
                "task_type": "text_embedding",
                "dimensions": 384,
                "similarity": "cosine",
                "element_type": "float"
              },
              "chunks": [
                {
                  "text": """# はじめに
ベクトル検索は目的のドキュメントをキーワードマッチ以外の意味的な近さで見つけたり、テキスト以外のマルチモーダルな画像や音声などの情報の検索に使ったりといったことが可能で、特にここ1・2年は生成AIと組み合わせたRAGというユースケースの登場で非常に広く利用されるようになりました。
    """,
                  "embeddings": [
                    0.01843889,
                    -0.04999397,
                    -0.049595766,
                    -0.042462178,
                    0.08888733,
                    (...)
                  ]
                }
              ]
            }
          },
          "content": """# はじめに
ベクトル検索は目的のドキュメントをキーワードマッチ以外の意味的な近さで見つけたり、テキスト以外のマルチモーダルな画像や音声などの情報の検索に使ったりといったことが可能で、特にここ1・2年は生成AIと組み合わせたRAGというユースケースの登場で非常に広く利用されるようになりました。
    """
        }
      }
    ]
  }
}

embedding結果がchunksという配列に含まれていることがわかります。適切にチャンキングされた上でembeddingされてインデックスされているようですね。

検索

semantic_textフィールドに対する検索にはsemanticクエリーが利用できます。

GET semantic_text_test/_search
{
  "query" : {
    "semantic": {
      "field": "content_semantic", 
      "query": "イメージを見つける方法" 
    }
  }
}

とても自然な書き方なので、この記事で初めてElasticsearchのベクトル検索を実装する方には何の驚きもないかもしれないですが(それは良いことなのですが)、これまではベクトル検索が実現できるようになったときにはknnクエリーを使う必要があり、外部でembeddingした数値の配列を渡したり、あるいはここでクエリーをembeddingするためのモデル名を書いたりする必要があったことを考えると、かなりAPIも洗練されてきたように感じます。

ちなみに上記と同等のクエリーをknnで書くと以下のようになります。

GET semantic_text_test/_search
{
  "query": {
    "nested": {
      "path": "content_semantic.inference.chunks",
      "query": {
        "knn": {
          "field": "content_semantic.inference.chunks.embeddings",
          "query_vector_builder": {
            "text_embedding": {
              "model_id": "my-e5-model",
              "model_text": "イメージを見つける方法"
            }
          }
        }
      }
    }
  }
}

おわりに

この記事ではsemantic_textフィールドを使った検索について紹介しました。これまで比較的設定の複雑だったベクトル検索が、かなり簡単に利用できるようになったことがわかると思います。Elasticsearch 8.15以降で利用できますので、ぜひ活用してください。

ただ、生成AIの影響でNLP周りの機能についてはElasitcsearchでも活発に開発が続いています。semantic_textフィールドも8.16の時点でまだbetaです。新しいバージョンでは新機能が追加されたり、より効率的な実装・設定方法が提供されたりすることがありますので、ぜひElastic Search labsブログなどをウォッチしてキャッチアップしてください。

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?