使用 BigQuery 远程函数在 SQL 查询中向 Vertex AI 视觉问答 (VQA) 提问

1. 简介

概览

借助 BigQuery 远程函数,您可以使用 SQL 和 JavaScript 以外的语言或 BigQuery 用户定义的函数中不允许的库和服务来实现函数。BigQuery 远程函数可与 Cloud Run 函数Cloud Run 直接集成。您可以在 SQL 查询中调用 BigQuery 远程函数,方法是将一个或多个列作为输入,然后返回单个值作为输出。

Cloud Run 函数是一个轻量级计算解决方案,可供开发者创建单一用途的独立函数,无需管理服务器或运行时环境即可使用 HTTPS 触发或响应 CloudEvents。Cloud Run 函数支持 Node.js、Python、Go、Java、.NET、Ruby 和 PHP。

在本 Codelab 中,您将学习如何创建 BigQuery 远程函数,以便使用 Vertex AI Visual Question Answering (VQA) 功能来解答有关存储在 Cloud Storage 中的图片的问题。您的 SQL 查询将从 BigQuery 中的表中检索图片的 URI。然后,您将使用 BigQuery 远程函数将图片 URI 发送到 Cloud Run 函数,该函数将返回 VQA 对图片的回答。

插图

5832020184ccf2b2

从开发角度来看,您将在此 Codelab 中完成以下步骤:

  1. 在 Cloud Run 函数中创建 HTTP 端点
  2. 创建类型为 CLOUD_RESOURCE 的连接
  3. 为 Cloud Storage 存储分区创建 BigQuery 对象表
  4. 创建远程函数
  5. 与任何其他用户定义的函数一样,在查询中使用该远程函数

学习内容

2. 设置和要求

前提条件

激活 Cloud Shell

  1. 在 Cloud Console 中,点击激活 Cloud Shelld1264ca30785e435.png

cb81e7c8e34bc8d.png

如果这是您首次启动 Cloud Shell,系统会显示一个中间屏幕,介绍 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 使用 Python 版 Vertex AI SDK 为 python311 运行时提供了一个示例。

创建函数的源代码

首先,创建一个目录并通过 cd 命令进入该目录。

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

然后,您可以将函数网址保存为环境变量以供日后使用。

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 函数服务进行未经身份验证的调用。

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>,请尝试等待大约 1-2 分钟,让 Cloud Functions 调用方角色权限授予传播到 BigQuery 连接服务账号,然后再重试。

11. 恭喜!

恭喜您完成此 Codelab!

建议您查看有关 BigQuery 远程函数视觉问答 (VQA) 的文档。

所学内容

  • 如何在 Cloud Run 函数上配置身份验证,以及如何验证身份验证是否已正确配置
  • 通过为您的 gcloud 身份提供令牌,从本地开发环境调用经过身份验证的函数
  • 如何创建服务账号并向其授予调用函数的适当角色
  • 如何从具有调用函数所需适当角色的本地开发环境中冒充服务

12. 清理

为避免意外产生费用(例如,如果此 Cloud Run 函数意外被调用次数超过了免费层级的 Cloud Run 函数调用月度配额),您可以删除该 Cloud Functions 函数,也可以删除您在第 2 步中创建的项目。

如需删除 Cloud Run 函数,请前往 https://console.cloud.google.com/functions/ 访问 Cloud Run Cloud 控制台,然后删除 imagen-vqa 函数(如果您使用了其他名称,则删除 $FUNCTION_NAME)。

如果您选择删除整个项目,可以前往 https://console.cloud.google.com/cloud-resource-manager,选择您在第 2 步中创建的项目,然后选择“删除”。如果您删除该项目,则需要在 Cloud SDK 中更改项目。您可以通过运行 gcloud projects list 来查看所有可用项目的列表。