mirror of
https://github.com/GoogleCloudPlatform/vertex-ai-samples.git
synced 2026-09-26 14:42:04 +00:00
Added notebook sample for batch inference using the remote sensing VMG models
PiperOrigin-RevId: 866061602
This commit is contained in:
committed by
Copybara-Service
parent
531d9cfee0
commit
2990c53292
@@ -0,0 +1,320 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"cellView": "form",
|
||||
"id": "rPH7NVRWhCGT"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Copyright 2025 Google LLC\n",
|
||||
"#\n",
|
||||
"# Licensed under the Apache License, Version 2.0 (the \"License\");\n",
|
||||
"# you may not use this file except in compliance with the License.\n",
|
||||
"# You may obtain a copy of the License at\n",
|
||||
"#\n",
|
||||
"# https://www.apache.org/licenses/LICENSE-2.0\n",
|
||||
"#\n",
|
||||
"# Unless required by applicable law or agreed to in writing, software\n",
|
||||
"# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
|
||||
"# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
|
||||
"# See the License for the specific language governing permissions and\n",
|
||||
"# limitations under the License."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "OOTGI5tjjWqL"
|
||||
},
|
||||
"source": [
|
||||
"# RS Imagery Batch Inference on VertexAI\n",
|
||||
"\n",
|
||||
"This notebook shows how to run a Batch Prediction Job deployed VLMs (image and text) on Vertex AI.\n",
|
||||
"\n",
|
||||
"**Prepare the environment for interacting with Vertex AI:**\n",
|
||||
"\n",
|
||||
"Initialize the Vertex AI SDK using the aiplatform.init() function.\n",
|
||||
"\n",
|
||||
"Configure the SDK to work with your specific Google Cloud project (PROJECT_ID) and region (REGION) that were defined in the previous configuration cell. This step is necessary before using other SDK functions to manage Vertex AI resources."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"cellView": "form",
|
||||
"id": "cH6TEJQ-F_ne"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# @title Setup Notebook\n",
|
||||
"\n",
|
||||
"# @markdown 1. [Make sure that billing is enabled for your project](https://cloud.google.com/billing/docs/how-to/modify-project).\n",
|
||||
"\n",
|
||||
"# @markdown 2. **[Optional]** Set region. If not set, the region will be set automatically according to Colab Enterprise environment.\n",
|
||||
"\n",
|
||||
"REGION = \"\" # @param {type:\"string\"}\n",
|
||||
"\n",
|
||||
"# @markdown 3. If you want to run predictions with A100 80GB or H100 GPUs, we recommend using the regions listed below. **NOTE:** Make sure you have associated quota in selected regions. Click the links to see your current quota for each GPU type: [Nvidia A100 80GB](https://console.cloud.google.com/iam-admin/quotas?metric=aiplatform.googleapis.com%2Fcustom_model_serving_nvidia_a100_80gb_gpus), [Nvidia H100 80GB](https://console.cloud.google.com/iam-admin/quotas?metric=aiplatform.googleapis.com%2Fcustom_model_serving_nvidia_h100_gpus). You can request for quota following the instructions at [\"Request a higher quota\"](https://cloud.google.com/docs/quota/view-manage#requesting_higher_quota).\n",
|
||||
"\n",
|
||||
"# @markdown | Machine Type | Accelerator Type | Recommended Regions |\n",
|
||||
"# @markdown | ----------- | ----------- | ----------- |\n",
|
||||
"# @markdown | a2-ultragpu-1g | 1 NVIDIA_A100_80GB | us-central1, us-east4, europe-west4, asia-southeast1, us-east4 |\n",
|
||||
"# @markdown | a3-highgpu-2g | 2 NVIDIA_H100_80GB | us-west1, asia-southeast1, europe-west4 |\n",
|
||||
"# @markdown | a3-highgpu-4g | 4 NVIDIA_H100_80GB | us-west1, asia-southeast1, europe-west4 |\n",
|
||||
"# @markdown | a3-highgpu-8g | 8 NVIDIA_H100_80GB | us-central1, europe-west4, us-west1, asia-southeast1 |\n",
|
||||
"\n",
|
||||
"import base64\n",
|
||||
"import io\n",
|
||||
"import json\n",
|
||||
"from typing import Any\n",
|
||||
"\n",
|
||||
"from google.cloud import aiplatform, storage\n",
|
||||
"from PIL import Image\n",
|
||||
"\n",
|
||||
"# Import common utils\n",
|
||||
"if os.environ.get(\"VERTEX_PRODUCT\") != \"COLAB_ENTERPRISE\":\n",
|
||||
" ! pip install --upgrade tensorflow\n",
|
||||
"! git clone https://github.com/GoogleCloudPlatform/vertex-ai-samples.git\n",
|
||||
"\n",
|
||||
"common_util = importlib.import_module(\n",
|
||||
" \"vertex-ai-samples.notebooks.community.model_garden.docker_source_codes.notebook_util.common_util\"\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"# Setup GCP & VertexAI\n",
|
||||
"\n",
|
||||
"# Get the default cloud project id.\n",
|
||||
"PROJECT_ID = os.environ[\"GOOGLE_CLOUD_PROJECT\"]\n",
|
||||
"\n",
|
||||
"# Get the default region for launching jobs.\n",
|
||||
"if not REGION:\n",
|
||||
" REGION = os.environ[\"GOOGLE_CLOUD_REGION\"]\n",
|
||||
"\n",
|
||||
"# Enable the Vertex AI API and Compute Engine API, if not already.\n",
|
||||
"print(\"Enabling Vertex AI API and Compute Engine API.\")\n",
|
||||
"! gcloud services enable aiplatform.googleapis.com compute.googleapis.com\n",
|
||||
"\n",
|
||||
"# Initialize Vertex AI API.\n",
|
||||
"print(\"Initializing Vertex AI API.\")\n",
|
||||
"aiplatform.init(project=PROJECT_ID, location=REGION)\n",
|
||||
"\n",
|
||||
"# Gets the default SERVICE_ACCOUNT.\n",
|
||||
"shell_output = ! gcloud projects describe $PROJECT_ID\n",
|
||||
"project_number = shell_output[-1].split(\":\")[1].strip().replace(\"'\", \"\")\n",
|
||||
"SERVICE_ACCOUNT = f\"{project_number}-compute@developer.gserviceaccount.com\"\n",
|
||||
"print(\"Using this default Service Account:\", SERVICE_ACCOUNT)\n",
|
||||
"\n",
|
||||
"! gcloud config set project $PROJECT_ID\n",
|
||||
"import vertexai\n",
|
||||
"\n",
|
||||
"vertexai.init(\n",
|
||||
" project=PROJECT_ID,\n",
|
||||
" location=REGION,\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def to_png_bytes(img: Image.Image) -> bytes:\n",
|
||||
" \"\"\"Encodes the `img` as PNG bytes.\"\"\"\n",
|
||||
" buf = io.BytesIO()\n",
|
||||
" img.save(buf, format=\"PNG\")\n",
|
||||
" return buf.getvalue()\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def to_b64_png(img: Image.Image) -> str:\n",
|
||||
" \"\"\"Converts the `img` to a b64 encoded PNG bytes.\"\"\"\n",
|
||||
" return base64.b64encode(to_png_bytes(img)).decode()\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def write_jsonl_instances(\n",
|
||||
" bucket: storage.Bucket, path: str, instances: list[dict[str, Any]]\n",
|
||||
"):\n",
|
||||
" \"\"\"Writes the list of instances (dicts) as a JSONL serialized file.\n",
|
||||
"\n",
|
||||
" Each dict is an inference instance, matching one of the following structures:\n",
|
||||
"\n",
|
||||
" {'image': <b64 image>} - Image inference only.\n",
|
||||
" {'text': <str>} - Text inference only.\n",
|
||||
" {'image': <b64 image, 'texts': list<str>} - Image & text inference.\n",
|
||||
" \"\"\"\n",
|
||||
" with bucket.blob(path).open(\"wt\") as f:\n",
|
||||
" f.writelines(json.dumps(instance) for instance in instances)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"cellView": "form",
|
||||
"id": "qVHaf1GmKaT6"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# @title Initialize data bucket\n",
|
||||
"# @markdown ### Enter a GCS bucket name and a path within the bucket:\n",
|
||||
"\n",
|
||||
"BUCKET_NAME = \"\" # @param { type : 'string' }\n",
|
||||
"OUTPUT_PATH = \"batch_inference/inputs\" # @param { type : 'string' }\n",
|
||||
"\n",
|
||||
"storage_client = storage.Client()\n",
|
||||
"bucket = storage_client.bucket(BUCKET_NAME)\n",
|
||||
"\n",
|
||||
"# Download sample image\n",
|
||||
"!wget -O harbor.jpg https://mrsg.aegean.gr/images/uploads/it2zi0eidej4ql33llj.jpg\n",
|
||||
"harbor_img = Image.open(\"harbor.jpg\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"cellView": "form",
|
||||
"id": "0nOBY2kjGHob"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# @title Prepare data: Image list file\n",
|
||||
"\n",
|
||||
"input_uris = []\n",
|
||||
"\n",
|
||||
"# The sample image (harbor) is replicated 10 times as an example.\n",
|
||||
"for i in range(10):\n",
|
||||
" img_path = f\"{OUTPUT_PATH}/images/img{i}.png\"\n",
|
||||
" input_uris.append(f\"gs://{BUCKET_NAME}/{img_path}\")\n",
|
||||
" bucket.blob(img_path).upload_from_string(\n",
|
||||
" to_png_bytes(harbor_img), content_type=\"image/png\"\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
"with bucket.blob(f\"{OUTPUT_PATH}/input_uris.txt\").open(\"wt\") as f:\n",
|
||||
" f.writelines([f\"{i}\\n\" for i in input_uris])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"cellView": "form",
|
||||
"id": "ap9IjkphGHbZ"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# @title Prepare JSONL input\n",
|
||||
"\n",
|
||||
"# This cell generates sample inputs (JSONL files) in 3 formats:\n",
|
||||
"# Image, text and image_text, the input files are sharded to optionally reduce\n",
|
||||
"# the size of each file. The file pattern (with wildcards) is used as input for\n",
|
||||
"# the batch pipeline, e.g. \"gs://bucket_path/image*.jsonl\"\n",
|
||||
"\n",
|
||||
"# Write 3 shards of text input instances (10 instances each).\n",
|
||||
"instances = [{\"text\": \"test string\"}] * 10\n",
|
||||
"for i in range(3):\n",
|
||||
" write_jsonl_instances(bucket, f\"{OUTPUT_PATH}/text{i}.jsonl\", instances)\n",
|
||||
"\n",
|
||||
"# Write 10 image input instances into 3 shards.\n",
|
||||
"instances = [{\"image\": to_b64_png(harbor_img)}] * 10\n",
|
||||
"for p in range(3):\n",
|
||||
" write_jsonl_instances(bucket, f\"{OUTPUT_PATH}/image{p}.jsonl\", instances)\n",
|
||||
"\n",
|
||||
"# Write 10 image & texts input instances into a single file.\n",
|
||||
"instances = [\n",
|
||||
" {\"image\": to_b64_png(harbor_img), \"texts\": [\"text1\", \"text2\"]},\n",
|
||||
"] * 10\n",
|
||||
"write_jsonl_instances(bucket, f\"{OUTPUT_PATH}/combined.jsonl\", instances)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"cellView": "form",
|
||||
"id": "a2j33P9hGN9H"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# @title Run batch inference\n",
|
||||
"\n",
|
||||
"JOB_DISPLAY_NAME = \"batch-inference-vlm-test\" # @param { type: 'string' }\n",
|
||||
"# @markdown Enter the project number and model id, the project number is not the\n",
|
||||
"# @markdown same as project id (it can be found in the project settings).\n",
|
||||
"PROJECT_NUMBER = \"<project_number_here>\" # @param { type: 'string' }\n",
|
||||
"MODEL_ID = \"<model_id_here>\" # @param { type: 'string' }\n",
|
||||
"MODEL_RESOURCE_NAME = f\"projects/{PROJECT_NUMBER}/locations/{REGION}/models/{MODEL_ID}\"\n",
|
||||
"\n",
|
||||
"# @markdown Choose the input (instances) format, either a file-list of images or\n",
|
||||
"# @markdown a JSONL file pattern of JSON formatted inputs.\n",
|
||||
"INPUT_SOURCE_FORMAT = \"file-list\" # @param[\"file-list\", \"jsonl\"]\n",
|
||||
"# @markdown Configure batch input source This can use string wildcards such as\n",
|
||||
"# @markdown '*' and '?' to support sharded inputs.\n",
|
||||
"INPUT_SOURCE_PATTERN = \"gs://<bucket>/batch_inference/inputs/inputlist.txt\" # @param { type: 'string' }\n",
|
||||
"# @markdown Configure the output folder path, predictions will be written here.\n",
|
||||
"GCS_OUTPUT_PATH = \"gs://<bucket>/batch_inference/outputs\" # @param { type: 'string' }\n",
|
||||
"# @markdown Configure the batch runtime setup\n",
|
||||
"USE_GPU = True # @param { type: 'boolean' }\n",
|
||||
"BATCH_SIZE = 16 # @param { type: 'number' }\n",
|
||||
"REPLICA_COUNT = 1 # @param { type: 'number' }\n",
|
||||
"MAX_REPLICA_COUNT = 4 # @param { type: 'number' }\n",
|
||||
"\n",
|
||||
"machine_type = \"g2-standard-8\"\n",
|
||||
"if USE_GPU:\n",
|
||||
" accelerator_type = \"NVIDIA_L4\"\n",
|
||||
" accelerator_count = 1\n",
|
||||
"else:\n",
|
||||
" accelerator_type = None\n",
|
||||
" accelerator_count = None\n",
|
||||
"\n",
|
||||
"model = aiplatform.Model(MODEL_RESOURCE_NAME)\n",
|
||||
"\n",
|
||||
"job = model.batch_predict(\n",
|
||||
" job_display_name=JOB_DISPLAY_NAME,\n",
|
||||
" gcs_source=INPUT_SOURCE_PATTERN,\n",
|
||||
" gcs_destination_prefix=GCS_OUTPUT_PATH,\n",
|
||||
" instances_format=INPUT_SOURCE_FORMAT,\n",
|
||||
" machine_type=machine_type,\n",
|
||||
" accelerator_count=accelerator_count,\n",
|
||||
" accelerator_type=accelerator_type,\n",
|
||||
" starting_replica_count=REPLICA_COUNT,\n",
|
||||
" max_replica_count=MAX_REPLICA_COUNT,\n",
|
||||
" labels={\n",
|
||||
" \"task\": \"batch-inference\",\n",
|
||||
" \"vertex-ai-pipelines-run-billing-id\": JOB_DISPLAY_NAME,\n",
|
||||
" },\n",
|
||||
" batch_size=BATCH_SIZE,\n",
|
||||
" sync=False,\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"print(f\"Batch prediction job started: {job}\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"cellView": "form",
|
||||
"id": "KF9Lwsb5GOIS"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Monitor the job status\n",
|
||||
"\n",
|
||||
"print(\n",
|
||||
" f\"Running batch prediction {job.display_name}, resource:\"\n",
|
||||
" f\" {job.resource_name}. State: {job.state}\"\n",
|
||||
")"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"name": "model_garden_remote_sensing_batch_prediction.ipynb",
|
||||
"toc_visible": true
|
||||
},
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"name": "python3"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 0
|
||||
}
|
||||
Reference in New Issue
Block a user