Spanner ベクトル検索のスタートガイド

1. はじめに

Spanner は、フルマネージドで水平スケーリングが可能な、グローバルに分散されたデータベース サービスで、リレーショナルと非リレーショナルの両方の運用ワークロードに最適です。

Spanner にはベクトル検索のサポートが組み込まれています。これにより、正確な K 最近傍(KNN)機能または近似最近傍(ANN)機能を利用して、生成 AI アプリケーションで類似検索またはセマンティック検索を実行し、検索拡張生成(RAG)を大規模に実装できます。

Spanner のベクトル検索クエリは、運用データに対する他のクエリと同様に、トランザクションが commit されるとすぐに最新のリアルタイム データを返します。

このラボでは、Spanner を活用してベクトル検索を実行し、SQL を使用して VertexAI の Model Garden からエンベディング モデルと LLM モデルにアクセスするために必要な基本機能の設定について説明します。

アーキテクチャは次のようになります。

d179a760add7adc0.png

この基礎知識をもとに、ScaNN アルゴリズムに基づくベクトル インデックスを作成する方法と、セマンティック ワークロードをスケーリングする必要がある場合に APPROX 距離関数を使用する方法を学びます。

作成するアプリの概要

このラボでは、次の作業を行います。

  • Spanner インスタンスの作成
  • Spanner のデータベース スキーマを設定して、VertexAI のエンベディング モデルと LLM モデルと統合する
  • 小売データセットを読み込む
  • データセットに対して類似性検索クエリを発行する
  • LLM モデルにコンテキストを提供して、プロダクト固有のレコメンデーションを生成する。
  • スキーマを変更してベクトル インデックスを作成します。
  • 新しく作成したベクトル インデックスを利用するようにクエリを変更します。

学習内容

  • Spanner インスタンスの設定方法
  • VertexAI と統合する方法
  • Spanner を使用してベクトル検索を行い、小売データセット内の類似商品を見つける方法
  • ANN 検索を使用してベクトル検索ワークロードをスケーリングするようにデータベースを準備する方法。

必要なもの

  • 請求先アカウントに接続されている Google Cloud プロジェクト。
  • ウェブブラウザ(ChromeFirefox など)

2. 設定と要件

プロジェクトを作成する

Google アカウント(Gmail または Google Apps)をお持ちでない場合は、1 つ作成する必要があります。Google Cloud Platform コンソール(console.cloud.google.com)にログインし、新しいプロジェクトを作成します。

すでにプロジェクトが存在する場合は、コンソールの左上にあるプロジェクト選択プルダウン メニューをクリックします。

6c9406d9b014760.png

[新しいプロジェクト] をクリックします。] ボタンをクリックし、新しいプロジェクトを作成します。

949d83c8a4ee17d9.png

まだプロジェクトが存在しない場合は、次のような最初のプロジェクトを作成するためのダイアログが表示されます。

870a3cbd6541ee86.png

続いて表示されるプロジェクト作成ダイアログでは、新しいプロジェクトの詳細を入力できます。

6a92c57d3250a4b3.png

プロジェクト ID を忘れないようにしてください。プロジェクト ID は、すべての Google Cloud プロジェクトを通じて一意の名前にする必要があります(上記の名前はすでに使用されているため使用できません)。以降、この Codelab では PROJECT_ID と呼びます。

次に、Google Cloud リソースを使用して Spanner API を有効にするために、Cloud Console で課金を有効化します(まだ行っていない場合)。

15d0ef27a8fbab27.png

この Codelab をすべて実行しても費用はかかりませんが、より多くのリソースを使用する場合や実行したままにする場合は、コストが高くなる可能性があります(このドキュメントの最後にある「クリーンアップ」セクションをご覧ください)。Google Cloud Spanner の料金については、こちらをご覧ください。

Google Cloud Platform の新規ユーザーの皆さんには、$300 の無料トライアルをご利用いただけます。その場合は、この Codelab を完全に無料でご利用いただけます。

Google Cloud Shell の設定

Google Cloud と Spanner はノートパソコンからリモートで操作でき、この Codelab では、Google Cloud Shell(Cloud 上で動作するコマンドライン環境)を使用します。

この Debian ベースの仮想マシンには、必要な開発ツールがすべて揃っています。永続的なホーム ディレクトリが 5 GB 用意されており、Google Cloud で稼働するため、ネットワークのパフォーマンスと認証が大幅に向上しています。つまり、この Codelab に必要なのはブラウザだけです(はい、Chromebook で動作します)。

  1. Cloud コンソールから Cloud Shell を有効にするには、「Cloud Shell をアクティブにする」アイコン gcLMt5IuEcJJNnMId-Bcz3sxCd0rZn7IzT_r95C8UZeqML68Y1efBG_B0VRp7hc7qiZTLAF-TXD7SsOadxn8uadgHhaLeASnVS3ZHK39eOlKJOgj9SJua_oeGhMxRrbOg3qigddS2A をクリックします(環境のプロビジョニングと接続には少し時間がかかります)。

JjEuRXGg0AYYIY6QZ8d-66gx_Mtc-_jDE9ijmbXLJSAXFvJt-qUpNtsBsYjNpv2W6BQSrDc1D-ARINNQ-1EkwUhz-iUK-FUCZhJ-NtjvIEx9pIkE-246DomWuCfiGHK78DgoeWkHRw

Screen Shot 2017-06-14 at 10.13.43 PM.png

Cloud Shell に接続すると、すでに認証が完了しており、プロジェクトに各自の PROJECT_ID が設定されていることがわかります。

gcloud auth list

コマンド出力

Credentialed accounts:
 - <myaccount>@<mydomain>.com (active)
gcloud config list project

コマンド出力

[core]
project = <PROJECT_ID>

なんらかの理由でプロジェクトが設定されていない場合は、次のコマンドを実行します。

gcloud config set project <PROJECT_ID>

PROJECT_ID が見つからない場合は、設定手順で使用した ID を確認するか、Cloud コンソール ダッシュボードで確認します。

158fNPfwSxsFqz9YbtJVZes8viTS3d1bV4CVhij3XPxuzVFOtTObnwsphlm6lYGmgdMFwBJtc-FaLrZU7XHAg_ZYoCrgombMRR3h-eolLPcvO351c5iBv506B3ZwghZoiRg6cz23Qw

Cloud Shell では、デフォルトで環境変数もいくつか設定されます。これらの変数は、以降のコマンドを実行する際に有用なものです。

echo $GOOGLE_CLOUD_PROJECT

コマンド出力

<PROJECT_ID>

Spanner API を有効にする

gcloud services enable spanner.googleapis.com

概要

このステップでは、プロジェクトを設定し(まだ作成していない場合)、Cloud Shell をアクティブにして、必要な API を有効にします。

次のステップ

次に、Spanner のインスタンスとデータベースを設定します。

3. Spanner のインスタンスとデータベースを作成する

Spanner インスタンスを作成する

このステップでは、この Codelab 用に Spanner インスタンスを設定します。そのためには、Cloud Shell を開いて次のコマンドを実行します。

export SPANNER_INSTANCE_ID=retail-demo
gcloud spanner instances create $SPANNER_INSTANCE_ID \
--config=regional-us-central1 \
--description="spanner AI retail demo" \
--nodes=1

コマンド出力:

$ gcloud spanner instances create $SPANNER_INSTANCE_ID \
--config=regional-us-central1 \
--description="spanner AI retail demo" \
--nodes=1
Creating instance...done.  

データベースを作成する

インスタンスを実行したら、データベースを作成できます。Spanner では、1 つのインスタンスで複数のデータベースを使用できます。

データベースはスキーマを定義する場所です。また、データベースにアクセスできるユーザーの制御、カスタム暗号化の設定、オプティマイザーの構成、保持期間の設定を行うこともできます。

データベースを作成するには、ここでも gcloud コマンドライン ツールを使用します。

export SPANNER_DATABASE=cymbal-bikes
gcloud spanner databases create $SPANNER_DATABASE \
 --instance=$SPANNER_INSTANCE_ID

コマンド出力:

$ gcloud spanner databases create $SPANNER_DATABASE \
 --instance=$SPANNER_INSTANCE_ID
Creating database...done.

概要

このステップでは、Spanner のインスタンスとデータベースを作成しました。

次のステップ

次に、Spanner のスキーマとデータを設定します。

4. Cymbal のスキーマとデータを読み込む

Cymbal スキーマを作成する

スキーマを設定するには、Spanner Studio に移動します。

3e1a0fed928b33cf.png

スキーマは 2 つの部分からなりますまず、products テーブルを追加します。このステートメントをコピーして、空のタブに貼り付けます。

スキーマの場合は、次の DDL をコピーしてボックスに貼り付けます。

CREATE TABLE products (
categoryId INT64 NOT NULL,
productId INT64 NOT NULL,
productName STRING(MAX) NOT NULL,
productDescription STRING(MAX) NOT NULL,
productDescriptionEmbedding ARRAY<FLOAT32>,
createTime TIMESTAMP NOT NULL OPTIONS (
allow_commit_timestamp = true
),
inventoryCount INT64 NOT NULL,
priceInCents INT64,
) PRIMARY KEY(categoryId, productId);

次に、[run] ボタンをクリックし、スキーマが作成されるまで数秒待ちます。

次に、2 つのモデルを作成し、VertexAI モデルのエンドポイントに構成します。

1 つ目のモデルはテキストからエンベディングを生成するために使用されるエンベディング モデルで、2 つ目のモデルは Spanner のデータに基づいてレスポンスを生成するために使用される LLM モデルです。

次のスキーマを Spanner Studio の新しいタブに貼り付けます。

CREATE MODEL EmbeddingsModel INPUT(
content STRING(MAX),
) OUTPUT(
embeddings STRUCT<statistics STRUCT<truncated BOOL, token_count FLOAT32>, values ARRAY<FLOAT32>>,
) REMOTE OPTIONS (
endpoint = '//aiplatform.googleapis.com/projects/<PROJECT_ID>/locations/us-central1/publishers/google/models/text-embedding-004'
);


CREATE MODEL LLMModel INPUT(
prompt STRING(MAX),
) OUTPUT(
content STRING(MAX),
) REMOTE OPTIONS (
endpoint = '//aiplatform.googleapis.com/projects/<PROJECT_ID>/locations/us-central1/publishers/google/models/gemini-pro',
default_batch_size = 1
);

次に、[run] ボタンをクリックし、モデルが作成されるまで数秒待ちます。

Spanner Studio の左側のペインに、次のテーブルとモデルが表示されます。

62455aa4b0e839d9.png

データを読み込む

ここで、いくつかの商品をデータベースに挿入する必要があります。Spanner Studio で新しいタブを開き、次の挿入ステートメントをコピーして貼り付けます。

INSERT INTO products (categoryId, productId, productName, productDescription, createTime, inventoryCount, priceInCents)
VALUES (1, 1, "Cymbal Helios Helmet", "Safety meets style with the Cymbal children's bike helmet. Its lightweight design, superior ventilation, and adjustable fit ensure comfort and protection on every ride. Stay bright and keep your child safe under the sun with Cymbal Helios!", PENDING_COMMIT_TIMESTAMP(), 100, 10999),
(1, 2, "Cymbal Sprout", "Let their cycling journey begin with the Cymbal Sprout, the ideal balance bike for beginning riders ages 2-4 years. Its lightweight frame, low seat height, and puncture-proof tires promote stability and confidence as little ones learn to balance and steer. Watch them sprout into cycling enthusiasts with Cymbal Sprout!", PENDING_COMMIT_TIMESTAMP(), 10, 13999),
(1, 3, "Cymbal Spark Jr.", "Light, vibrant, and ready for adventure, the Spark Jr. is the perfect first bike for young riders (ages 5-8). Its sturdy frame, easy-to-use brakes, and puncture-resistant tires inspire confidence and endless playtime. Let the spark of cycling ignite with Cymbal!", PENDING_COMMIT_TIMESTAMP(), 34, 13900),
(1, 4, "Cymbal Summit", "Conquering trails is a breeze with the Summit mountain bike. Its lightweight aluminum frame, responsive suspension, and powerful disc brakes provide exceptional control and comfort for experienced bikers navigating rocky climbs or shredding downhill. Reach new heights with Cymbal Summit!", PENDING_COMMIT_TIMESTAMP(), 0, 79999),
(1, 5, "Cymbal Breeze", "Cruise in style and embrace effortless pedaling with the Breeze electric bike. Its whisper-quiet motor and long-lasting battery let you conquer hills and distances with ease. Enjoy scenic rides, commutes, or errands with a boost of confidence from Cymbal Breeze!", PENDING_COMMIT_TIMESTAMP(), 72, 129999),
(1, 6, "Cymbal Trailblazer Backpack", "Carry all your essentials in style with the Trailblazer backpack. Its water-resistant material, multiple compartments, and comfortable straps keep your gear organized and accessible, allowing you to focus on the adventure. Blaze new trails with Cymbal Trailblazer!", PENDING_COMMIT_TIMESTAMP(), 24, 7999),
(1, 7, "Cymbal Phoenix Lights", "See and be seen with the Phoenix bike lights. Powerful LEDs and multiple light modes ensure superior visibility, enhancing your safety and enjoyment during day or night rides. Light up your journey with Cymbal Phoenix!", PENDING_COMMIT_TIMESTAMP(), 87, 3999),
(1, 8, "Cymbal Windstar Pump", "Flat tires are no match for the Windstar pump. Its compact design, lightweight construction, and high-pressure capacity make inflating tires quick and effortless. Get back on the road in no time with Cymbal Windstar!", PENDING_COMMIT_TIMESTAMP(), 36, 24999),
(1, 9,"Cymbal Odyssey Multi-Tool","Be prepared for anything with the Odyssey multi-tool. This handy gadget features essential tools like screwdrivers, hex wrenches, and tire levers, keeping you ready for minor repairs and adjustments on the go. Conquer your journey with Cymbal Odyssey!", PENDING_COMMIT_TIMESTAMP(), 52, 999),
(1, 10,"Cymbal Nomad Water Bottle","Stay hydrated on every ride with the Nomad water bottle. Its sleek design, BPA-free construction, and secure lock lid make it the perfect companion for staying refreshed and motivated throughout your adventures. Hydrate and explore with Cymbal Nomad!", PENDING_COMMIT_TIMESTAMP(), 42, 1299);

[run] ボタンをクリックしてデータを挿入します。

概要

このステップでは、スキーマを作成し、いくつかの基本的なデータを cymbal-bikes データベースに読み込みました。

次のステップ

次に、エンベディング モデルと統合して商品説明のエンベディングを生成するとともに、テキスト検索リクエストをエンベディングに変換して関連する商品を検索します。

5. エンベディングを操作する

商品説明のベクトル エンベディングを生成する

商品に対して類似検索を機能させるには、商品説明のエンベディングを生成する必要があります。

スキーマ内に EmbeddingsModel が作成された場合、これは単純な UPDATE DML ステートメントです。

UPDATE products p1
SET productDescriptionEmbedding =
(SELECT embeddings.values from ML.PREDICT(MODEL EmbeddingsModel,
(SELECT productDescription as content FROM products p2 where p2.productId=p1.productId)))
WHERE categoryId=1;

[run] ボタンをクリックして、商品の説明を更新します。

この例では、SQL クエリを使用して自然言語検索リクエストを提供します。このクエリは、検索リクエストをエンベディングに変換し、前の手順で生成された商品説明の保存されたエンベディングに基づいて、類似した結果を検索します。

-- Use Spanner's vector search, and integration with embedding and LLM models to
-- return items that are semantically relevant and available in inventory based on
-- real-time data.


SELECT productName, productDescription, inventoryCount, COSINE_DISTANCE(
productDescriptionEmbedding,
(   SELECT embeddings.values
FROM ML.PREDICT(
MODEL EmbeddingsModel,
(SELECT "I'd like to buy a starter bike for my 3 year old child" as content)
)
)
) as distance
FROM products
WHERE inventoryCount > 0
ORDER BY distance
LIMIT 5;

[run] ボタンをクリックして、類似の商品を見つけます。結果は次のようになります。

672e111753077fcf.png

クエリでは、在庫のある商品のみに関心がある(inventoryCount > 0)など、追加のフィルタが使用されていることに注意してください。

概要

このステップでは、Spanner と VertexAI のモデルとのインテグレーションを活用して、SQL を使用して商品説明のエンベディングと検索リクエストのエンベディングを作成しました。また、ベクトル検索を実行して、検索リクエストに一致する類似商品を見つけました。

次のステップ

次に、検索結果を使用して LLM にフィードし、商品ごとにカスタム レスポンスを生成しましょう。

6. LLM を操作する

Spanner を使用すると、VertexAI から提供される LLM モデルと簡単に統合できます。これにより、デベロッパーはアプリケーションにロジックを実行させることなく、SQL を使用して LLM と直接やり取りできます。

たとえば、ユーザー "I'd like to buy a starter bike for my 3 year old child". からの前の SQL クエリの結果は次のとおりです。

次のプロンプトを使用して、各結果に対して商品がユーザーに適しているかどうかのレスポンスを返そうとしています。

"Answer with ‘Yes' or ‘No' and explain why: Is this a good fit for me? I'd like to buy a starter bike for my 3 year old child"

使用できるクエリは次のとおりです。

-- Use an LLM to analyze this list and provide a recommendation on whether each
-- product is a good fit for the user. We use the vector search and real time
-- inventory data to first filter the products to reduce the size of the prompt to
-- the LLM.
SELECT productName, productDescription, inventoryCount, content AS LLMResponse
FROM ML.PREDICT(
MODEL LLMModel,
(   SELECT
inventoryCount,
productName,
productDescription,
CONCAT(
"Answer with Yes' or No' and explain why: Is this a good fit for me?",
"I'd like to buy a starter bike for my 3 year old child \n",
"Product Name: ", productName, "\n",
"Product Description:", productDescription) AS prompt,
FROM products
WHERE inventoryCount > 0
ORDER by COSINE_DISTANCE(
productDescriptionEmbedding,
(   SELECT embeddings.values
FROM ML.PREDICT(
MODEL EmbeddingsModel,
( SELECT "I'd like to buy a starter bike for my 3 year old child" as content)
)
)
) LIMIT 5
),
STRUCT(256 AS maxOutputTokens)
);

[run] ボタンをクリックしてクエリを発行します。結果は次のようになります。

35878cd0f88f1470.png

最初の製品は、商品説明に記載されている年齢層から 3 歳(2 ~ 4 歳)向けです。他の製品は適していません。

概要

このステップでは、LLM を使用して、ユーザーからのプロンプトに対する基本的な回答を生成しました。

次のステップ

次に、ANN を使用してベクトル検索をスケーリングする方法を学びましょう。

7. ベクトル検索のスケーリング

前述のベクトル検索の例では、exact-KNN ベクトル検索を使用していました。これは、Spanner データの特定のサブセットをクエリできる場合に便利です。この種のクエリは、高度にパーティショニング可能であると言われています。

高度にパーティショニング可能なワークロードがなく、データが大量にある場合は、ScaNN アルゴリズムを活用した ANN ベクトル検索を使用して検索パフォーマンスを向上させます。

Spanner でこれを行うには、次の 2 つのことを行う必要があります。

  • ベクトル インデックスを作成する
  • APPROX 距離関数を使用するようクエリを変更します。

ベクトル インデックスを作成する

このデータセットにベクトル インデックスを作成するには、まず productDescriptionEmbeddings 列を変更して各ベクトルの長さを定義する必要があります。ベクトルの長さを列に追加するには、元の列を削除してから再作成する必要があります。

ALTER TABLE `products` DROP COLUMN `productDescriptionEmbedding`;
ALTER TABLE
  `products` ADD COLUMN `productDescriptionEmbedding` ARRAY<FLOAT32>(vector_length=>768);

次に、前に実行した Generate Vector embedding ステップから再度エンベディングを作成します。

UPDATE products p1
SET productDescriptionEmbedding =
(SELECT embeddings.values from ML.PREDICT(MODEL EmbeddingsModel,
(SELECT productDescription as content FROM products p2 where p2.productId=p1.productId)))
WHERE categoryId=1;

列が作成されたら、インデックスを作成します。

CREATE VECTOR INDEX ProductDescriptionEmbeddingIndex
    ON products(productDescriptionEmbedding)
    WHERE productDescriptionEmbedding IS NOT NULL
OPTIONS (
 distance_type = 'COSINE'
);

新しいインデックスを使用する

新しいベクトル インデックスを使用するには、前のエンベディング クエリを少し変更する必要があります。

元のクエリは次のとおりです。

SELECT productName, productDescription, inventoryCount, COSINE_DISTANCE(
productDescriptionEmbedding,
(   SELECT embeddings.values
FROM ML.PREDICT(
MODEL EmbeddingsModel,
(SELECT "I'd like to buy a starter bike for my 3 year old child" as content)
)
)
) as distance
FROM products
WHERE inventoryCount > 0
ORDER BY distance
LIMIT 5;

次の変更を行う必要があります。

  • 新しいベクトル インデックスにインデックス ヒントを使用する: @{force_index=ProductDescriptionEmbeddingIndex}
  • COSINE_DISTANCE 関数呼び出しを APPROX_COSINE_DISTANCE に変更します。以下の最後のクエリでも JSON オプションが必要です。
  • ML.PREDICT 関数からエンベディングを個別に生成する。
  • エンベディングの結果を最終的なクエリにコピーします。

エンベディングを生成する

-- Generate the prompt embeddings
SELECT embeddings.values
FROM ML.PREDICT(
  MODEL EmbeddingsModel,
   (SELECT "I'd like to buy a starter bike for my 3 year old child" as content)
  )
)

クエリの結果をハイライト表示してコピーします。

1b43c5ae4ef9ab68.png

次に、コピーしたエンベディングを貼り付けて、次のクエリの <VECTOR> を置き換えます。

-- Embedding query now using the vector index


SELECT productName, productDescription, inventoryCount, 
  APPROX_COSINE_DISTANCE(productDescriptionEmbedding, array<float32>[@VECTOR], options => JSON '{\"num_leaves_to_search\": 10}')
FROM products @{force_index=ProductDescriptionEmbeddingIndex}
WHERE productDescriptionEmbedding IS NOT NULL AND inventoryCount > 0
ORDER BY distance
LIMIT 5;

次のようになります。

12397107ec49c491.png

概要

このステップでは、スキーマを変換してベクトル インデックスを作成しました。次に、ベクトル インデックスを使用して ANN 検索を行うようにエンベディング クエリを書き換えました。これは、ベクトル検索のワークロードをスケーリングするためにデータが増加する場合に重要なステップです。

次のステップ

次はクリーンアップです。

8. クリーンアップ(省略可)

クリーンアップするには、Cloud コンソールの Cloud Spanner セクションに移動し、Codelab で作成した retail-demo インスタンスを削除します。

41cbc1a84b3588d5.png

9. 完了

これで、Spanner の組み込みベクトル検索を使用した類似検索の実行が完了しました。さらに、SQL を使用して直接生成 AI 機能を提供するために、エンベディング モデルと LLM モデルを扱うのがいかに簡単であるかを確認しました。

最後に、ベクトル検索のワークロードをスケーリングするために、ScaNN アルゴリズムに基づく ANN 検索を実行するプロセスを学びました。

次のステップ

Spanner の厳密最近傍(KNN ベクトル検索)機能の詳細については、https://cloud.google.com/spanner/docs/find-k-nearest-neighbors をご覧ください。

Spanner の近似最近傍探索(ANN ベクトル検索)機能の詳細については、https://cloud.google.com/spanner/docs/find-approximate-nearest-neighbors をご覧ください。

Spanner の VertexAI インテグレーションを使用して SQL でオンライン予測を実行する方法の詳細については、https://cloud.google.com/spanner/docs/ml をご覧ください。