はじめに
Google CloudのBigQueryでマルチモーダルエンベディングが導入されています。
例としてテキストから画像検索が挙がっていましたが、画像から画像検索がなかったのでPythonを駆使して試してみました。
前準備
検索するためのデータベースをBigQuery上に事前に作成しておきます。
画像の用意
検索したい画像をCloud Storageにアップロードします。
今回はImagen3に作ってもらった10枚の画像を使用してみます。
BigQueryの外部接続の作成
「外部データソースへの接続」から「Vertex AI リモートモデル、リモート関数、BigLake(Cloud リソース)」を選び、接続IDを入力します。
オブジェクトテーブルの作成
BigQueryのオブジェクトテーブルを使用することで、非構造データを扱うことができるようになります。
以下のクエリを実行します。
CREATE OR REPLACE EXTERNAL TABLE
`DATASET.object-table`
WITH CONNECTION `LOCATION.CONNNECTION_ID`
OPTIONS
( object_metadata = 'SIMPLE',
uris = ['gs://BUCKET_NAME/*']
);
実行が完了すると、検索したい画像のオブジェクトテーブルが作成されます。
接続のサービスアカウントに権限付与
作成された外部接続の詳細を開くと、サービスアカウントIDが記載されています。
このサービスアカウントIDに対して、以下のロールを付与します。
- Vertex AI ユーザー
- Storage オブジェクト閲覧者
エンベディングモデルの追加
multimodalembedding@001
エンドポイントを使用する BigQuery モデルを作成します。
以下のクエリを実行します。
CREATE OR REPLACE MODEL
DATASET.multimodal_embedding_model REMOTE
WITH CONNECTION `LOCATION.CONNNECTION_ID`
OPTIONS (endpoint = 'multimodalembedding@001')
エンベディングの生成
ML.GENERATE_EMBEDDING 関数を使用することで、エンベディングを生成できます。
以下のクエリを実行します。
CREATE OR REPLACE TABLE `DATASET.object-table_embeddings`
AS
SELECT * FROM ML.GENERATE_EMBEDDING(
MODEL `DATASET.multimodal_embedding_model`,
TABLE `DATASET.object-table`)
WHERE content_type = 'image/jpeg'
実行が完了すると、DATASET.object-table_embeddings
テーブルができています。
プレビューで中身を見てみると、画像のエンベディングされた結果が格納されています。
画像で画像を検索する
検索したい画像
以下の画像に似た画像を検索してみたいと思います。

検索
Pythonを使用します。
まず画像をマルチモーダルエンベディングでエンベディングに変換します。
import vertexai
from vertexai.vision_models import Image, MultiModalEmbeddingModel
vertexai.init(project=PROJECT_ID, location=LOCATION)
model = MultiModalEmbeddingModel.from_pretrained("multimodalembedding@001")
image = Image.load_from_file("/path/image.png")
embeddings = model.get_embeddings(
image=image,
dimension=1408,
)
その後、BigQueryのVECTOR_SEARCH
関数を使用したクエリをPython上から実行します。
BigQuery上で画像を読み込むには、オブジェクトテーブルをまた作成する必要があります。
今回はSQLクエリ上にエンベディング結果を埋め込むことで、別途オブジェクトテーブルを用意しないようにしました。
from google.cloud import bigquery
client = bigquery.Client()
client = bigquery.Client(project=PROJECT_ID)
query = f'''
CREATE OR REPLACE TABLE `DATASET.vector_search_results` AS
WITH search AS(
SELECT {embeddings.image_embedding} as ml_generate_embedding_result
)
SELECT base.uri AS gcs_uri, distance
FROM
VECTOR_SEARCH(
TABLE `DATASET.object-table_embeddings`,
'ml_generate_embedding_result',
TABLE search,
'ml_generate_embedding_result',
top_k => 3);
'''
result = client.query(query)
この実行が終わると、BigQuery上に検索結果のテーブルが作成されます。
そのテーブルに対して、検索結果を表示するコードを実行します。
import io
from PIL import Image
import matplotlib.pyplot as plt
import tensorflow as tf
def printImages(results):
image_results_list = list(results)
amt_of_images = len(image_results_list)
fig, axes = plt.subplots(nrows=amt_of_images, ncols=2, figsize=(20, 20))
fig.tight_layout()
fig.subplots_adjust(hspace=0.5)
for i in range(amt_of_images):
gcs_uri = image_results_list[i][0]
text = image_results_list[i][1]
f = tf.io.gfile.GFile(gcs_uri, 'rb')
stream = io.BytesIO(f.read())
img = Image.open(stream)
axes[i, 0].axis('off')
axes[i, 0].imshow(img)
axes[i, 1].axis('off')
axes[i, 1].text(0, 0, text, fontsize=10)
plt.show()
query = """
SELECT * FROM `DATASET.vector_search_results`
ORDER BY distance;
"""
printImages(client.query(query))
検索画像とそのベクトルの距離が表示されます。
ベクトルの距離が近いほど、入力画像と近いものを指しています。
一番上に色味と形状が似ている画像がきていて、靴という共通点で検索の上位に来ていることが分かります!
おわりに
今回BigQueryのマルチモーダルエンベディングを使用して、画像から画像検索を試してみました。
検索用のオブジェクトテーブルを使わないで、SQLのWITH句とPythonを駆使して実現させました。
BigQueryでマルチモーダルのベクトル検索が簡単に行えるので、クロスモダリティの検索の幅が広がりそうです!