本記事では、Lazuliでソフトウェア開発者として働くメンバーがラスベガス現地で参加したGoogle Cloud Next '24でデータベースについて学んだこと、そしていくつかの基準を用いて当社のニーズに適したものを選ぶ方法について共有したいと思います。
こんにちは、Lazuliの中村です。私はLazuliでソフトウェアエンジニアとして働いています。本記事ではラスベガスで参加したGoogle Cloud Next ’24で得た学びをまとめています。
Google Cloud ’24のオープニングキーノートでは、小売業界におけるさまざまなユースケースと例が紹介されました。その中には、自然言語入力や動画URLに基づいて関連製品を提示するオンライン衣料品店のデモや、チャットを通じて製品購入をサポートするデモが含まれていました。これらのデモから、これまで以上により簡単にベクトル検索が実装できるようになったことのメッセージを強く感じました。
そこで今回は、新しくプレビューリリースされたFirestoreのベクトル検索機能を使い、自然言語入力に基づいて関連製品を提案する製品のレコメンデーション機能を実装してみます。
ベクトル検索とは?
ベクトル検索は、データをベクトル(数値の配列)として表現し、その類似性に基づいて検索を行う手法です。従来のキーワードベースの検索とは異なり、ベクトル検索は意味的な類似性を捉えることができる点が特徴です。例えば、製品説明をベクトル化し、ユーザーの自然言語によるクエリとの類似性を計算することで、製品データに基づいてユーザーの意図に合致する製品を推薦することが可能になります。
ベクトル検索の詳細については、過去に開催されたGoogle Cloud Next Tokyo ’23のセッションが非常に参考になりました。
実装したもの
自然言語による入力を受け取り、関連する5つの製品を表示するレコメンド機能作りました。
実装内容
- ベクトル化のための製品説明の作成
- 製品説明のベクトル化
- データをFirestoreに登録
- ベクトル検索用のインデックス作成
- 自然言語を使用したベクトル検索の実行
1. ベクトル化のための製品説明の作成
サンプルの製品データとして、BigQuery上で公開されているeコマースサイト用のフェイクデータセットを使用しました。
データはCSV形式でエクスポートし、Firestoreに登録するためのデモデータを作成しました。
ベクトル検索では、検索クエリと製品説明の間の意味的な類似性を捉えることが重要となりますが、このデータセットには製品名、コスト以外に、自然言語入力との類似性を見つけるのに役立つ意味のあるデータは含まれていません。そこで、Gemini Pro 1.0モデルを使用して、各製品名から擬似的に製品説明を生成しました。
func generateProductInfo(ctx context.Context, products string) (string, error) {
prompt := fmt.Sprintf(`
You are an AI assistant that generates dummy data for an online shop. The product data is as follows:
'''csv
id,cost,category,name,brand,retail_price,department,sku,distribution_center_id
%s
'''
Please create a product description for each item in a single line of about 100-150 characters, considering the product's features, materials, season, target audience, size, etc.`, products)
resp, _ := model.GenerateContent(ctx, genai.Text(prompt))
content := resp.Candidates[0].Content.Parts[0].(genai.Text)
return string(content), nil
}
現実の利用シーンでは、自社が保有するプロダクトデータを含む、様々なデータソースから収集した情報を利用したり、出力された説明文が適切なモノか精査するプロセスが必要となります。
今回はデモとしての説明文作成のため、簡易なプロンプトにより説明文を生成しました。
2. 製品説明のベクトル化
続いて、生成した擬似説明を embedding モデルを使用してベクトル化します。今回は VertexAI のテキスト用の Embedding モデルを使用しました。VertexAI で利用可能な Embedding モデルの一覧はこちらから確認できます。
func generateEmbbedingValue(ctx context.Context, text string) ([]float32, error) {
vertexAIClient, _ := genai.NewClient(ctx, option.WithAPIKey(os.Getenv("API_KEY")))
embedder := vertexAIClient.EmbeddingModel("embedding-001") // Embedding のモデルを指定
resp, _ := embedder.EmbedContent(ctx, genai.Text(text))
return resp.Embedding.Values, nil
}
3. データをFirestoreに登録
Firestore SDKを使用してデータを登録しました。
import { Firestore, FieldValue } from "@google-cloud/firestore";
import fs from "fs";
const db = new Firestore({ projectId: process.env.PJ_ID, databaseId: process.env.DB_ID,});
const addDocuments = async () => {
// 読み込む製品データCSVファイルには生成されたベクトルが含まれています
const records = parseToJson(fs.readFileSync("products.csv"));
for (const record of records) {
// 文字列から配列にベクトルデータをパースしてフォーマット
const vector = parseVector(rowVector);
// 製品データとベクトルフィールドを持つ新しいドキュメントオブジェクトを作成
const doc = { ...record, embedding_field: FieldValue.vector(vector) };
await db.collection(process.env.COLLECTION_NAME).add(doc);
}
};
2025/5/22 現在、Firestore へのベクトル登録は Python または JavaScript 用の SDK によりサポートされています。(このことを知らなかったため、Go による説明文生成を実装しました。)
4. ベクトル検索用のインデックス作成
ベクトル検索を有効にするため、複合インデックスを作成する必要があります。
以下の gcloud CLI を使用してインデックスを生成できます。
gcloud alpha firestore indexes composite create \\
--collection-group={collection-group} \\
--query-scope=COLLECTION \\
--field-config field-path=field,vector-config='{"dimension":"768", "flat": "{}"}' \\
--database={database-id}
複合インデックスを使用する場合、ストレージ及び読み取りに対して追加の課金が発生します。特に、読み取り操作では、複合インデックスを使用した検索の対象ドキュメント数に基づいて料金が計算されます。
2024/5/22 現在、通常のインデックスでは、1,000ドキュメントあたり1回の読み取り操作の料金となりますが、ベクトル検索の場合は、100ドキュメントあたり1回の読み取り操作として計算されます。そのため、大規模なデータセットに対してベクトル検索を行う場合、インデックスのサイズが検索コストに与える影響が大きくなります。
5. 自然言語を使用したベクトル検索の実行
ユーザーの入力テキストをそのままベクトル化し、ベクトル検索クエリの入力として使用することで、商品データのベクトル検索ができます。
ベクトルデータの登録と同様、Python または JavaScript 用の SDK によりベクトル検索がサポートされています。
import { Firestore, FieldValue, VectorQuery, VectorQuerySnapshot } from "@google-cloud/firestore";
import { GoogleGenerativeAI } from "@google/generative-ai";
const genAI = new GoogleGenerativeAI(process.env.API_KEY ?? "");
const model = genAI.getGenerativeModel({model: "embedding-001"});
const searchDoc = async (message: string) => {
const result = await model.embedContent(message);
const vectorQuery: VectorQuery = db.collection("products").findNearest(
"embedding_field",
FieldValue.vector(result.embedding.values),
{ limit: 5, distanceMeasure: "COSINE" }
);
const vectorQuerySnapshot: VectorQuerySnapshot = await vectorQuery.get();
const res: any[] = [];
vectorQuerySnapshot.forEach((doc) => res.push(doc.data()));
return res;
};
まとめ
Gemini と Firestore のベクトル検索機能を組み合わせることで、自然言語を使用した商品のレコメンド機能のデモを簡単に実装することができました。
データの整理、生成AIにより生成されたデータ(本デモでは疑似説明文)の精査、採用するEmbeddingモデルの検討、ベクトル検索時の入力値の調整等、多くの工程が必要となるものの、
Firestore のようなマネージドなデータベース上でベクトル検索が利用可能になったことにより、ベクトル検索機能追加が、これまで以上に容易になりました。
参考資料
Youtube
Google Cloud Next Tokyo ’23 session about vector search: https://www.youtube.com/watch?v=7XI45ll8fqQ
Firestore Vector Search Documentation: https://firebase.google.com/docs/firestore/vector-search
Firestore Pricing: https://cloud.google.com/firestore/pricing?hl=ja
Google’s Embedding Models: https://ai.google.dev/gemini-api/docs/models/gemini?hl=en#embedding
本記事は以下の”Implementing a Recommendation Feature Using Firestore’s Vector Search”を翻訳したものになります。