BigQuery リモート関数を使用して、SQL クエリで Vertex AI Visual Question & Answering(VQA)に質問する

1. はじめに

概要

BigQuery リモート関数を使用すると、SQL と JavaScript 以外の言語で関数を実装できます。また、BigQuery のユーザー定義関数で許可されていないライブラリやサービスを使用して関数を実装することもできます。BigQuery リモート関数は、Cloud Run 関数Cloud Run を直接統合します。SQL クエリ内で BigQuery リモート関数を呼び出すには、1 つ以上の列を入力として受け取り、単一の値を出力として返します。

Cloud Run 関数は軽量なコンピューティング ソリューションで、デベロッパーはサーバーやランタイム環境を管理せずに、HTTPS を使用してトリガーしたり、CloudEvents に応答したりできる単一目的のスタンドアロン関数を作成できます。Cloud Run functions は、Node.js、Python、Go、Java、.NET、Ruby、PHP をサポートしています。

この Codelab では、Vertex AI の Visual Question & Answering(VQA)を使用して、Cloud Storage に保存されている画像に関する質問の回答を取得する BigQuery リモート関数を作成する方法について説明します。SQL クエリは、BigQuery のテーブルから画像の URI を取得します。次に、BigQuery リモート関数を使用して、画像の URI を Cloud Run 関数に送信します。この関数は、画像に関する VQA から回答を返します。

イラスト

5832020184ccf2b2.png

開発の観点から、この Codelab で行う手順は次のとおりです。

  1. Cloud Run 関数で HTTP エンドポイントを作成する
  2. CLOUD_RESOURCE タイプの接続を作成する
  3. Cloud Storage バケットの BigQuery オブジェクト テーブルを作成する
  4. リモート関数を作成する
  5. 他のユーザー定義関数と同様に、クエリ内でリモート関数を使用します。

学習内容

  • Python で HTTP Cloud Run 関数を作成する方法
  • SQL クエリ内で BigQuery リモート関数を作成して使用する方法
  • BigQuery オブジェクト テーブルを作成する方法
  • Vertex AI SDK for Python を使用して Visual Question Answering(VQA)を使用する方法

2. 設定と要件

前提条件

Cloud Shell をアクティブにする

  1. Cloud Console で、[Cloud Shell をアクティブにする] d1264ca30785e435.png をクリックします。

cb81e7c8e34bc8d.png

Cloud Shell を初めて起動する場合は、内容を説明する中間画面が表示されます。中間画面が表示された場合は [続行] をクリックします。

d95252b003979716.png

Cloud Shell のプロビジョニングと接続に少し時間がかかる程度です。

7833d5e1c5d18f54.png

この仮想マシンには、必要な開発ツールがすべて用意されています。5 GB の永続的なホーム ディレクトリが用意されており、Google Cloud で稼働するため、ネットワークのパフォーマンスと認証が大幅に向上しています。この Codelab での作業のほとんどは、ブラウザから実行できます。

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

  1. Cloud Shell で次のコマンドを実行して、認証されたことを確認します。
gcloud auth list

コマンド出力

 Credentialed Accounts
ACTIVE  ACCOUNT
*       <my_account>@<my_domain.com>

To set the active account, run:
    $ gcloud config set account `ACCOUNT`
  1. Cloud Shell で次のコマンドを実行して、gcloud コマンドがプロジェクトを認識していることを確認します。
gcloud config list project

コマンド出力

[core]
project = <PROJECT_ID>

上記のようになっていない場合は、次のコマンドで設定できます。

gcloud config set project <PROJECT_ID>

コマンド出力

Updated property [core/project].

3. ローカル環境変数を設定する

このコードでは、この Codelab で使用する gcloud コマンドを読みやすくするために、環境変数をいくつか作成します。

PROJECT_ID=$(gcloud config get-value project)

# Cloud Function variables
FUNCTION_NAME="imagen-vqa"
FUNCTION_REGION="us-central1"

# Cloud Function variables
BUCKET_NAME=$PROJECT_ID-imagen-vqa

# BigQuery variables
DATASET_ID="remote_function_codelab"
TABLE_NAME="images"
BQ_REGION="US"
CONNECTION_ID="imagen_vqa_connection"

4. Cloud Run 関数を作成する

BigQuery リモート関数を作成するには、まず Cloud Run 関数を使用して HTTP エンドポイントを作成する必要があります。エンドポイントは、単一の HTTP POST リクエストで行のバッチを処理し、バッチの結果を HTTP レスポンスとして返す必要があります。

この Cloud Run 関数は、SQL クエリから入力として画像ストレージ URI と質問プロンプトを受け取り、Visual Question & Answering(VQA)から回答を返します。

この Codelab では、Vertex AI SDK for Python を使用した python311 ランタイムの例を使用します。

関数のソースコードを作成する

まず、ディレクトリを作成し、そのディレクトリに移動します。

mkdir imagen-vqa && cd $_

次に、requirements.txt ファイルを作成します。

google-cloud-aiplatform[preview]
google-cloud-storage
functions-framework==3.*

次に、main.py ソースファイルを作成します。

from vertexai.preview.vision_models import ImageQnAModel
from vertexai.preview.vision_models import Image
from flask import jsonify
from google.cloud import storage
from urllib.parse import urlparse
import functions_framework

# This is the entry point for the cloud function
@functions_framework.http
def imagen_vqa(request):
    try:
        # See if you can parse the incoming JSON
        return_value = []
        request_json = request.get_json()
        # This grabs the input into the function as called from the SQL function 
        calls = request_json['calls']
        for call in calls:
            # We call the VQA function here in another function defined below
            ai_result = vqa(call)
            # The result to BigQuery is in the order it was prepared in 
            return_value.append(ai_result[0])
        # Prepare the response back to BigQuery
        return_json = jsonify( { "replies": return_value } )
        return return_json
    except Exception as e:
        return jsonify( { "errorMessage": str(e) } ), 400

# Helper function to split apart the GCS URI 
def decode_gcs_url(url):
    # Read the URI and parse it
    p = urlparse(url)
    bucket = p.netloc
    file_path = p.path[0:].split('/', 1)
    # Return the relevant objects (bucket, path to object)
    return bucket, file_path[1]
    
# We can't use the image load from local file since it expects a local path
# We use a GCS URL and get the bytes of the image 
def read_file(object_path):
    # Parse the path
    bucket, file_path = decode_gcs_url(object_path)
    storage_client = storage.Client()
    bucket = storage_client.bucket(bucket)
    blob = bucket.blob(file_path)
    # Return the object as bytes
    return blob.download_as_bytes()

# This is the function that calls the VQA function
def vqa (parameters):
    # This is the model we want to use
    image_qna_model = ImageQnAModel.from_pretrained("imagetext@001")
    # The location is the first parameter 
    image_loc = parameters[0]
    # Get the bytes 
    image_bytes = read_file(image_loc)
    # Load the bytes into the Image handler
    input_image = Image(image_bytes)
    # Ask the VQA the question
    results = image_qna_model.ask_question(
        image=input_image,
        # The prompt was the second parameter
        question=parameters[1],
        number_of_results=1
    )
    return results

Cloud Run 関数をデプロイする

これで、python311 ランタイム用の Cloud Run 関数をデプロイできます。

Cloud Run 関数を Cloud Run に直接デプロイするには、次のコマンドを実行します。

gcloud beta run deploy $FUNCTION_NAME \
      --source . \
      --function imagen_vqa \
      --region $FUNCTION_REGION \
      --no-allow-unauthenticated

Cloud Functions 第 2 世代としてデプロイする場合は、次のコマンドを使用します。

gcloud functions deploy $FUNCTION_NAME \
--gen2 \
--region=$FUNCTION_REGION \
--runtime=python311 \
--trigger-http \
--source=. \
--no-allow-unauthenticated

関数の URL を環境変数として保存し、後で使用できます。

ENDPOINT_URL="$(gcloud beta run services describe $FUNCTION_NAME --region $FUNCTION_REGION --format='value(status.url)')"

5. Cloud Storage バケットを作成する

まず、画像を保存する Cloud Storage バケットを作成します。

gcloud storage buckets create gs://$BUCKET_NAME

次に、VQA が使用する画像をアップロードします。この Codelab では、VQA ドキュメントサンプル画像を使用します。

Cloud Storage の Cloud コンソールを使用して、画像をバケットに直接アップロードできます。または、次のコマンドを実行して、サンプル画像を現在の Cloud Shell ディレクトリにダウンロードすることもできます。

wget -O image.jpg -o /dev/null https://unsplash.com/photos/QqN25A3iF9w/download?ixid=M3wxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNjk1NzYxMjY2fA&force=true

を Cloud Storage バケットにアップロードします。

gcloud storage cp image.jpg gs://$BUCKET_NAME

6. BigQuery Cloud Resource 接続を作成する

BigQuery は CLOUD_RESOURCE 接続を使用して、Cloud Functions の関数とやり取りします。次のコマンドを実行して、この接続を作成します。

bq mk --connection --location=$BQ_REGION --project_id=$PROJECT_ID \
--connection_type=CLOUD_RESOURCE $CONNECTION_ID

次に、新しい BigQuery 接続の詳細を表示します。

bq show --connection $PROJECT_ID.$BQ_REGION.$CONNECTION_ID

次に示すように、BigQuery 接続サービス アカウントの名前を変数に保存します。

CONNECTION_SA="<YOUR-SERVICE-ACCOUNT-ID>@gcp-sa-bigquery-condel.iam.gserviceaccount.com"

Cloud Storage バケットにアクセスするためのアクセス権をサービス アカウントに付与します。

gsutil iam ch serviceAccount:$CONNECTION_SA:objectAdmin gs://$BUCKET_NAME

7. BigQuery オブジェクト テーブルを作成する

BigQuery オブジェクト テーブルは、Cloud Storage 内にある非構造化データ オブジェクトの読み取り専用テーブルです。

オブジェクト テーブルによって、Cloud Storage 内の非構造化データを分析できます。リモート関数で分析を行い、これらのオペレーションの結果を BigQuery の残りの構造化データと結合できます。

まず、データセットを作成します。

bq --location=$BQ_REGION mk \
    --dataset \
    $DATASET_ID

次のコマンドは、Cloud Storage の画像バケットに基づいてオブジェクト テーブルを作成します。生成されたテーブルには、そのバケット内のすべての画像の URI が含まれます。

bq mk --table \
--external_table_definition=gs://$BUCKET_NAME/*@$BQ_REGION.$CONNECTION_ID \
--object_metadata=SIMPLE \
$PROJECT_ID:$DATASET_ID.$TABLE_NAME

8. BigQuery リモート関数を作成する

最後のステップは、BigQuery リモート関数を構成することです。

まず、BigQuery 接続サービス アカウントに Cloud Run 関数を呼び出す権限を付与します。Cloud Run 関数サービスの未認証の呼び出しを許可することはおすすめしません。

gcloud run services add-iam-policy-binding $FUNCTION_NAME \
 --member=serviceAccount:$CONNECTION_SA \
 --role="roles/run.invoker" \
 --region $FUNCTION_REGION

次に、SQL クエリを変数に保存します。

SQL_CREATE_FUNCTION="CREATE FUNCTION \`$PROJECT_ID.$DATASET_ID\`.vqa(uri STRING, image_prompt STRING) RETURNS STRING
REMOTE WITH CONNECTION \`$PROJECT_ID.$BQ_REGION.$CONNECTION_ID\`
OPTIONS (
  endpoint = '$ENDPOINT_URL'
)"

クエリを実行します。

bq query --nouse_legacy_sql $SQL_CREATE_FUNCTION

クエリを実行してリモート関数を作成すると、Created <your-project-id>.remote_function_codelab.vqa が表示されます。

9. SQL クエリで BigQuery リモート関数を呼び出す

これで、リモート関数を作成する開発手順が完了しました。これで、SQL クエリ内から Cloud Run 関数を呼び出せるようになりました。

まず、質問と SQL クエリを変数に保存します。この Codelab では、Visual Question & Answering のドキュメントのを使用します。このクエリでは、ストレージ バケットに追加された最新のイメージが使用されます。

export SQL_QUERY="DECLARE question STRING DEFAULT 'What objects are in the image?';
SELECT uri, image_prompt ,\`$DATASET_ID\`.vqa(uri, image_prompt) as result
FROM ( 
  SELECT 
  *, 
  dense_rank() over (order by updated) as rnk ,
  question as image_prompt
  FROM \`$PROJECT_ID.$DATASET_ID.images\`) as innertable
  WHERE rnk  = 1;
"

次に、SQL クエリを実行して、Vertex AI Visual Question Answering(VQA)サービスからのレスポンスを表示します。

bq query --nouse_legacy_sql $SQL_QUERY

結果は次のようになります。

+---------------------------------+--------------------------------+----------+
|               uri               |    image_prompt                |  result  |
+---------------------------------+--------------------------------+----------+
| gs://<YOUR_BUCKET>/image.jpg    | What objects are in the image? |  marbles |
+---------------------------------+--------------------------------+----------+

10. トラブルシューティング

BigQuery テーブルの作成時にエラー BigQuery error in mk operation: Source URI must be a Google Cloud Storage location: gs://$BUCKET_NAME が発生した場合は、コマンドの $BUCKET_NAME の後に /* パスが含まれていることを確認してください。

SQL クエリの実行時に Access Denied: BigQuery BigQuery: Received response code 403 from endpoint <your-function-endpoint> というエラーが発生した場合は、Cloud Functions 呼び出し元ロールの権限が BigQuery 接続サービス アカウントに伝播されるまで 1 ~ 2 分ほど待ってから、再試行してください。

11. 完了

これでこの Codelab は完了です。

BigQuery Remote FunctionsVisual Question Answering(VQA)のドキュメントを確認することをおすすめします。

学習した内容

  • Cloud Run 関数で認証を設定し、認証が正しく構成されていることを確認する方法
  • gcloud ID のトークンを指定して、ローカル開発環境から認証済み関数を呼び出す
  • サービス アカウントを作成し、関数を呼び出す適切なロールを付与する方法
  • 関数の呼び出しに適切なロールを持つローカル開発環境のサービスに権限を委任する方法

12. クリーンアップ

誤って課金されないようにするには(たとえば、この Cloud Run 関数が 無料 tier の Cloud Run 関数の呼び出しの月間割り当てを超える回数誤って呼び出された場合など)、Cloud Functions の関数を削除するか、手順 2 で作成したプロジェクトを削除します。

Cloud Run 関数を削除するには、Cloud Run Cloud コンソール(https://console.cloud.google.com/functions/)に移動し、imagen-vqa 関数(または別の名前を使用した場合は $FUNCTION_NAME)を削除します。

プロジェクト全体を削除する場合は、https://console.cloud.google.com/cloud-resource-manager に移動し、手順 2 で作成したプロジェクトを選択して [削除] を選択します。プロジェクトを削除する場合は、Cloud SDK でプロジェクトを変更する必要があります。使用可能なすべてのプロジェクトのリストを表示するには、gcloud projects list を実行します。