Usa las funciones remotas de BigQuery para hacer preguntas a Vertex AI Visual Question Answering (VQA) en una consulta en SQL

1. Introducción

Descripción general

Las funciones remotas de BigQuery te permiten implementar una función en lenguajes distintos de SQL y JavaScript, o con las bibliotecas y los servicios no permitidos en las funciones definidas por el usuario de BigQuery. Las funciones remotas de BigQuery proporcionan una integración directa con Cloud Run Functions y Cloud Run. Puedes invocar una función remota de BigQuery dentro de una consulta en SQL tomando una o más columnas como entrada y, luego, devolviendo un solo valor como resultado.

Cloud Run Functions es una solución de procesamiento ligera que permite a los desarrolladores crear funciones independientes y de un solo propósito que se pueden activar con HTTPS o responder a CloudEvents sin necesidad de administrar un servidor o un entorno de ejecución. Cloud Run Functions admite Node.js, Python, Go, Java, .NET, Ruby y PHP.

En este codelab, aprenderás a crear una función remota de BigQuery para obtener respuestas a una pregunta sobre imágenes almacenadas en Cloud Storage con la búsqueda de respuestas visuales (VQA) de Vertex AI. Tu consulta en SQL recuperará un URI para una imagen de una tabla en BigQuery. Luego, con una función remota de BigQuery, enviarás el URI de la imagen a una Cloud Run Function que responderá con respuestas de VQA sobre la imagen.

Ilustración

5832020184ccf2b2.png

Desde una perspectiva de desarrollo, estos son los pasos que completarás en este codelab:

  1. Crea el extremo HTTP en Cloud Run Functions
  2. Crea una conexión de tipo CLOUD_RESOURCE
  3. Crea una tabla de objetos de BigQuery para el bucket de Cloud Storage
  4. Crea la función remota
  5. Usa la función remota en una consulta como cualquier otra función definida por el usuario

Qué aprenderás

2. Configuración y requisitos

Requisitos previos

Activar Cloud Shell

  1. En la consola de Cloud, haz clic en Activar Cloud Shelld1264ca30785e435.png.

cb81e7c8e34bc8d.png

Si es la primera vez que inicias Cloud Shell, aparecerá una pantalla intermedia en la que se describirá qué es. Si apareció una pantalla intermedia, haz clic en Continuar.

d95252b003979716.png

El aprovisionamiento y la conexión a Cloud Shell solo tomará unos minutos.

7833d5e1c5d18f54.png

Esta máquina virtual está cargada con todas las herramientas de desarrollo necesarias. Ofrece un directorio principal persistente de 5 GB y se ejecuta en Google Cloud, lo que permite mejorar considerablemente el rendimiento de la red y la autenticación. Gran parte de tu trabajo en este codelab, si no todo, se puede hacer con un navegador.

Una vez que te conectes a Cloud Shell, deberías ver que te autenticaste y que el proyecto se configuró con tu ID del proyecto.

  1. En Cloud Shell, ejecuta el siguiente comando para confirmar que tienes la autenticación:
gcloud auth list

Resultado del comando

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

To set the active account, run:
    $ gcloud config set account `ACCOUNT`
  1. En Cloud Shell, ejecuta el siguiente comando para confirmar que el comando gcloud conoce tu proyecto:
gcloud config list project

Resultado del comando

[core]
project = <PROJECT_ID>

De lo contrario, puedes configurarlo con el siguiente comando:

gcloud config set project <PROJECT_ID>

Resultado del comando

Updated property [core/project].

3. Configura variables de entorno locales

En este código, crearás algunas variables de entorno para mejorar la legibilidad de los comandos gcloud que se usan en este codelab.

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. Crea la función de Cloud Run

Para crear una función remota de BigQuery, primero debes crear un extremo HTTP con Cloud Run Function. El extremo debe poder procesar un lote de filas en una sola solicitud POST HTTP y mostrar los resultados para el lote como una respuesta HTTP.

Esta función de Cloud Run recibirá la URI de almacenamiento de la imagen y la instrucción de la pregunta como entrada de tu consulta de SQL, y devolverá la respuesta de la búsqueda de respuestas visuales (VQA).

En este codelab, se usa un ejemplo del tiempo de ejecución de Python 3.11 con el SDK de Vertex AI para Python.

Crea el código fuente de la función

Primero, crea un directorio y ábrelo con el comando cd.

mkdir imagen-vqa && cd $_

Luego, crea un archivo requirements.txt.

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

A continuación, crea un archivo fuente 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

Implementa la Cloud Run function

Ahora puedes implementar tu función de Cloud Run para el entorno de ejecución de Python 3.11.

Para implementar una función de Cloud Run directamente en Cloud Run, ejecuta el siguiente comando:

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

Si prefieres realizar la implementación como Cloud Functions 2nd gen, usa el siguiente comando:

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

Luego, puedes guardar la URL de la función como una variable de entorno para usarla más adelante.

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

5. Crea el bucket de Cloud Storage

Primero, crea un bucket de Cloud Storage para almacenar tus imágenes.

gcloud storage buckets create gs://$BUCKET_NAME

A continuación, sube una imagen para que la use la VQA. En este codelab, se usa la imagen de ejemplo de la documentación de VQA.

Puedes usar la consola de Cloud para Cloud Storage y subir la imagen directamente a tu bucket. También puedes ejecutar los siguientes comandos para descargar la imagen de ejemplo en tu directorio actual de Cloud Shell.

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

y, luego, súbelo a tu bucket de Cloud Storage.

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

6. Crea una conexión de recurso de Cloud en BigQuery

BigQuery usa una conexión CLOUD_RESOURCE para interactuar con tu Cloud Function. Ejecuta el siguiente comando para crear esta conexión.

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

A continuación, muestra los detalles de la nueva conexión de BigQuery.

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

Guarda el nombre de la cuenta de servicio de conexión de BigQuery en una variable, como se muestra a continuación.

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

Otorga acceso a la cuenta de servicio para que pueda acceder a tu bucket de Cloud Storage.

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

7. Crea una tabla de objetos de BigQuery

Las tablas de objetos de BigQuery son tablas de solo lectura sobre objetos de datos no estructurados que residen en Cloud Storage.

Las tablas de objetos te permiten analizar datos no estructurados en Cloud Storage. Puedes realizar análisis con funciones remotas y, luego, unir los resultados de estas operaciones con el resto de tus datos estructurados en BigQuery.

Primero, crea un conjunto de datos.

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

El siguiente comando crea una tabla de objetos basada en tu bucket de imágenes de Cloud Storage. La tabla resultante contendrá los URIs de todas las imágenes de ese bucket.

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

8. Crea la función remota de BigQuery

El último paso es configurar la función remota de BigQuery.

Primero, otorga permisos a la cuenta de servicio de conexión de BigQuery para invocar la función de Cloud Run. No se recomienda permitir la invocación no autenticada para el servicio de funciones de Cloud Run.

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

A continuación, guarda la consulta en SQL en una variable.

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'
)"

Ahora, ejecuta la consulta.

bq query --nouse_legacy_sql $SQL_CREATE_FUNCTION

Después de ejecutar la consulta para crear la función remota, verás Created <your-project-id>.remote_function_codelab.vqa.

9. Llama a la función remota de BigQuery en una consulta de SQL

Ya completaste los pasos de desarrollo para crear la función remota. Ahora puedes llamar a tu función de Cloud Run desde una consulta en SQL.

Primero, guarda tu pregunta y consulta en SQL en una variable. En este codelab, se usa el ejemplo de la documentación de Visual Question Answering. Esta consulta usa la imagen más reciente que se agregó a tu bucket de almacenamiento.

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;
"

Luego, ejecuta la consulta de SQL para mostrar la respuesta del servicio de búsqueda de respuestas visuales (VQA) de Vertex AI.

bq query --nouse_legacy_sql $SQL_QUERY

Los resultados deberían ser similares al siguiente ejemplo:

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

10. Solución de problemas

Cuando crees la tabla de BigQuery, si recibes un error BigQuery error in mk operation: Source URI must be a Google Cloud Storage location: gs://$BUCKET_NAME, asegúrate de haber incluido la ruta de acceso /* después de $BUCKET_NAME en el comando.

Cuando ejecutes tu consulta SQL, si recibes un error Access Denied: BigQuery BigQuery: Received response code 403 from endpoint <your-function-endpoint>, espera entre 1 y 2 minutos a que el permiso del rol de Cloud Function Invoker se propague a la cuenta de servicio de conexión de BigQuery antes de volver a intentarlo.

11. ¡Felicitaciones!

¡Felicitaciones por completar el codelab!

Te recomendamos que revises la documentación sobre las funciones remotas de BigQuery y la búsqueda de respuestas visuales (VQA).

Temas abordados

  • Cómo configurar la autenticación en una Cloud Run Function y verificar que se haya configurado correctamente
  • Invoca una función autenticada desde un entorno de desarrollo local proporcionando el token de tu identidad de gcloud
  • Cómo crear una cuenta de servicio y otorgarle el rol adecuado para invocar una función
  • Cómo suplantar la identidad de un servicio desde un entorno de desarrollo local que tiene los roles adecuados para invocar una función

12. Limpia

Para evitar cargos involuntarios (por ejemplo, si esta función de Cloud Run se invoca de forma involuntaria más veces que tu asignación mensual de invocaciones de funciones de Cloud Run en el nivel gratuito), puedes borrar la Cloud Function o el proyecto que creaste en el paso 2.

Para borrar la función de Cloud Run, ve a la consola de Cloud Run en https://console.cloud.google.com/functions/ y borra la función imagen-vqa (o $FUNCTION_NAME en caso de que hayas usado otro nombre).

Si decides borrar todo el proyecto, puedes ir a https://console.cloud.google.com/cloud-resource-manager, seleccionar el proyecto que creaste en el paso 2 y elegir Borrar. Si borras el proyecto, deberás cambiar de proyecto en el SDK de Cloud. Para ver la lista de todos los proyectos disponibles, ejecuta gcloud projects list.