1. はじめに
Spanner は、リレーショナル ワークロードと非リレーショナル ワークロードの両方の運用ワークロードに適した、フルマネージドで水平スケール可能なグローバルに分散されたデータベース サービスです。
Spanner にはベクトル検索が組み込まれているため、厳密最近傍法(KNN)または近似最近傍法(ANN)のいずれかの機能を活用して、類似性検索やセマンティック検索を実行し、生成 AI アプリケーションに検索拡張生成(RAG)を実装できます。
Spanner のベクトル検索クエリは、オペレーショナル データに対する一般的なクエリと同じように、トランザクションがコミットされると同時に、リアルタイムで最新のデータを返します。
このラボでは、Spanner を使用してベクトル検索を実行し、SQL を使用して Vertex AI の Model Garden からエンベディング モデルと LLM モデルにアクセスするために必要な基本機能を設定します。
アーキテクチャは次のようになります。
これらの基礎を踏まえて、ScaNN アルゴリズムを基盤とするベクトル インデックスを作成し、セマンティック ワークロードをスケーリングする必要がある場合に APPROX 距離関数を使用する方法について学習します。
作成するアプリの概要
このラボでは、次の作業を行います。
- Spanner インスタンスの作成
- VertexAI のエンベディング モデルと LLM モデルと統合するように Spanner のデータベース スキーマを設定する
- 小売データセットを読み込む
- データセットに対して類似性検索クエリを実行する
- LLM モデルにコンテキストを提供して、プロダクト固有のおすすめを生成します。
- スキーマを変更してベクトル インデックスを作成します。
- 新しく作成したベクトル インデックスを活用するようにクエリを変更します。
学習内容
- Spanner インスタンスを設定する方法
- VertexAI と統合する方法
- Spanner を使用してベクトル検索を実行し、小売データセット内の類似アイテムを見つける方法
- ANN 検索を使用してベクトル検索ワークロードをスケーリングできるようにデータベースを準備する方法。
必要なもの
2. 設定と要件
プロジェクトを作成する
Google アカウント(Gmail または Google Apps)をお持ちでない場合は、1 つ作成する必要があります。Google Cloud Platform のコンソール(console.cloud.google.com)にログインし、新しいプロジェクトを作成します。
すでにプロジェクトが存在する場合は、コンソールの左上にあるプロジェクト選択プルダウン メニューをクリックします。
表示されたダイアログで [NEW PROJECT] ボタンをクリックして、新しいプロジェクトを作成します。
まだプロジェクトが存在しない場合は、次のような最初のプロジェクトを作成するためのダイアログが表示されます。
続いて表示されるプロジェクト作成ダイアログでは、新しいプロジェクトの詳細を入力できます。
プロジェクト ID を忘れないようにしてください。プロジェクト ID は、すべての Google Cloud プロジェクトを通じて一意の名前にする必要があります(上記の名前はすでに使用されているため使用できません)。以降、この Codelab では PROJECT_ID と呼びます。
次に、Google Cloud リソースを使用し、Spanner API を有効にするために、Developers Console で課金を有効にする必要があります。
この Codelab の操作をすべて行って、費用が生じたとしても、少額です。Google Cloud Spanner の料金については、こちらをご覧ください。
Google Cloud Platform の新規ユーザーの皆さんには、$300 の無料トライアルをご利用いただけます。その場合は、この Codelab を完全に無料でご利用いただけます。
Google Cloud Shell の設定
Google Cloud と Spanner はノートパソコンからリモートで操作でき、この Codelab では、Google Cloud Shell(Cloud 上で動作するコマンドライン環境)を使用します。
この Debian ベースの仮想マシンには、必要な開発ツールがすべて用意されています。仮想マシンは Google Cloud で稼働し、永続的なホーム ディレクトリが 5 GB 用意されているため、ネットワークのパフォーマンスと認証が大幅に向上しています。つまり、この Codelab に必要なのはブラウザだけです(Chromebook でも動作します)。
- Cloud Console から Cloud Shell を有効にするには、[Cloud Shell をアクティブにする]
をクリックします(環境のプロビジョニングと接続に若干時間を要します)。
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 コンソール ダッシュボードで確認します。
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 に移動します。
このスキーマには 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 モデル エンドポイントに構成します。
最初のモデルは、テキストからエンベディングを生成するために使用されるエンベディング モデルです。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 の左側のペインに、次のテーブルとモデルが表示されます。
データを読み込む
次に、データベースに商品を挿入します。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
ボタンをクリックして、類似商品を探します。結果は次のようになります。
在庫のある商品のみを対象とする(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
ボタンをクリックしてクエリを実行します。結果は次のようになります。
最初の商品は、商品の説明に記載されている年齢層(2 ~ 4 歳)が 3 歳に適しているため、3 歳の子供に適しています。他の製品は適切ではありません。
概要
このステップでは、LLM を使用して、ユーザーからのプロンプトに対する基本的なレスポンスを生成しました。
次のステップ
次に、ANN を使用してベクトル検索をスケーリングする方法について説明します。
7. ベクトル検索のスケーリング
これまでのベクトル検索の例では、正確な 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)
)
)
クエリの結果を選択してコピーします。
次に、コピーしたエンベディングを貼り付けて、次のクエリの <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;
次のようになります。
概要
このステップでは、スキーマを変換してベクトル インデックスを作成しました。次に、ベクトル インデックスを使用して ANN 検索を実行するようにエンベディング クエリを書き換えました。これは、データが増加してベクトル検索ワークロードをスケーリングする際に重要なステップです。
次のステップ
次に、クリーンアップを行います。
8. クリーンアップ(省略可)
クリーンアップするには、Cloud Console の Cloud Spanner セクションに移動して、Codelab で作成した 'retail-demo
' インスタンスを削除します。
9. 完了
これで、Spanner の組み込みベクトル検索を使用して類似度検索を正常に実行できました。また、エンベディング モデルと LLM モデルを操作して、SQL を使用して生成 AI 機能を直接提供する方法についても説明しました。
最後に、ベクトル検索ワークロードをスケーリングするために 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 をご覧ください。