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

1. はじめに

概要

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

Cloud Run functions は、デベロッパーがサーバーやランタイム環境を管理することなく、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 functions の関数に送信します。この関数は、画像に関する VQA からの回答を返します。

イラスト

5832020184ccf2b2.png

開発の観点から見ると、この Codelab で完了する手順は次のとおりです。

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

学習内容

  • Python で HTTP Cloud Run functions の関数を作成する方法
  • 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 functions の関数を作成する

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

この Cloud Run functions の関数は、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 functions の関数をデプロイする

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

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

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 リソース接続を作成する

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 functions の関数サービスで認証されていない呼び出しを許可することはおすすめしません。

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 functions の関数を呼び出すことができます。

まず、質問と 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 Invoker ロール権限の付与が BigQuery 接続サービス アカウントに伝播するまで 1 ~ 2 分ほど待ってから再試行してください。

11. 完了

以上で、この Codelab は完了です。

BigQuery リモート関数Visual Question Answering(VQA)のドキュメントを確認することをおすすめします。

学習した内容

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

12. クリーンアップ

意図しない課金を避けるため(たとえば、この Cloud Run functions の関数が、無料枠の月間 Cloud Run functions の関数呼び出し割り当てよりも多く呼び出された場合)、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 を実行すると、使用可能なすべてのプロジェクトのリストが表示されます。