diff --git a/notebooks/community/model_garden/model_garden_finetuning_tutorial.ipynb b/notebooks/community/model_garden/model_garden_finetuning_tutorial.ipynb
deleted file mode 100644
index 056cddb21..000000000
--- a/notebooks/community/model_garden/model_garden_finetuning_tutorial.ipynb
+++ /dev/null
@@ -1,2005 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "7d9bbf86da5e"
- },
- "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": "99c1c3fc2ca5"
- },
- "source": [
- "# Vertex AI Model Garden - Finetuning Tutorial\n",
- "\n",
- "
\n",
- " \n",
- " \n",
- "  Run in Colab Enterprise\n",
- " \n",
- " | \n",
- " \n",
- " \n",
- "  View on GitHub\n",
- " \n",
- " | \n",
- "
"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "3de7470326a2"
- },
- "source": [
- "## Overview\n",
- "\n",
- "Finetuning a Large Language Model (LLM) is to take a general-purpose or base LLM model and tailoring it for a specific job. You begin with a pre-trained model (like Gemma or Llama) and further train it on your own data to specialize its abilities and knowledge to your needs.\n",
- "\n",
- "Vertex AI Model Garden simplifies the process of adapting powerful language models to your specific needs. This allows you to have full ownership and control over your customized model, leading to improved quality, reduced expenses, and enhanced data sovereignty.\n",
- "\n",
- "With finetuning, you can:\n",
- "- Adapt the LLM's abilities to particular tasks, such as question answering or classification.\n",
- "- Teach the LLM specialized information, like company-specific data.\n",
- "- Make the LLM smaller and faster, for example, using [PEFT (LoRA)](https://github.com/huggingface/peft) or [knowledge distillation](https://en.wikipedia.org/wiki/Knowledge_distillation).\n",
- "\n",
- "The next section delves into the popular methods available for finetuning large language models. Understanding these different approaches is crucial for effectively adapting a pre-trained model to a specific task or dataset. We will explore the key techniques and discuss their respective strengths and weaknesses.\n",
- "\n",
- "### Full finetuning vs LoRA\n",
- "Both full finetuning and [LoRA (Low-Rank Adaptation)](https://github.com/huggingface/peft) are techniques to adapt a pre-trained LLM to a specific task or dataset, but they differ significantly in their approach. Full Finetuning updates all the parameters of the pre-trained model based on the new data. LoRA freezes the pre-trained model weights and injects trainable rank decomposition matrices into each layer of the [Transformer](https://huggingface.co/learn/nlp-course/en/chapter1/4) architecture. This effectively increases the model's capacity without modifying the original weights.\n",
- "\n",
- "#### Advantages of Full Finetuning:\n",
- "\n",
- "- Quality: Can potentially achieve higher quality, especially with large datasets and complex tasks.\n",
- "- Flexibility: Allows for more extensive modifications to the model's behavior.\n",
- "\n",
- "#### Advantages of LoRA:\n",
- "\n",
- "- Efficiency: Reduces the number of trainable parameters, leading to faster training and lower storage requirements. \n",
- "- Modularity: Allows you to easily switch between different LoRA adapters for different tasks without retraining the entire model.\n",
- "- Preservation of original model: Keeps the original model weights intact, making it easier to revert to the original model or combine with other LoRA adapters.\n",
- "\n",
- "#### When to use Full Finetuning:\n",
- "- You have sufficient computational resources.\n",
- "- You need to achieve the highest possible quality.\n",
- "- You require significant changes to the model's behavior.\n",
- "\n",
- "#### When to use LoRA:\n",
- "\n",
- "- You have limited computational resources.\n",
- "- You need to quickly adapt the model to different tasks.\n",
- "- You want to preserve the original model's capabilities.\n",
- "\n",
- "In this notebook we will show an example of doing LoRA to reduce training and storage costs. To learn more about LoRA, refer to [PEFT (LoRA)](https://github.com/huggingface/peft).\n",
- "\n",
- "### Chat completion\n",
- "This notebook demonstrates the preprocessing, training, and evaluation of a chat completion task. The goal is to make a large language model (LLM) more conversational by finetuning it to generate responses suitable for back-and-forth dialogue. To achieve this, the model needs to understand the flow of conversation and generate responses relevant to the current turn.\n",
- "\n",
- "### CoQA (Conversational Question Answering) task\n",
- "The [CoQA](https://stanfordnlp.github.io/coqa/) dataset, short for Conversational Question Answering, is a valuable resource for researchers developing systems capable of engaging in human-like conversations. With over 8,000 conversations and 127,000+ questions, CoQA provides a large-scale dataset for evaluating and improving conversational question answering systems. This dataset focuses on measuring a machine's ability to understand a text passage and answer a series of interconnected questions that appear in a conversation. CoQA utilizes the F1 metric to evaluate the performance of question answering systems. The F1 metric measures the average word overlap between the predicted answer and the ground truth answer, providing a quantitative measure of accuracy.\n",
- "\n",
- "We will evaluate the quality of the trained model with the CoQA F1 score.\n",
- "\n",
- "### Outline of this tutorial\n",
- "\n",
- "The following outline provides a roadmap of the steps we will take in this notebook. It details the sequential procedures involved in achieving our objective.\n",
- "\n",
- "- Establish a baseline CoQA F1 score with the [Llama-3.1-8B-Instruct](https://huggingface.co/meta-llama/Llama-3.1-8B-Instruct) model to compare to later.\n",
- "- Prepare the [Databricks dolly 15k](https://huggingface.co/datasets/databricks/databricks-dolly-15k) dataset and a training template for chat completion task.\n",
- "- Finetune Llama 3.1 model with a Vertex AI Custom Training Job and evaluate on CoQA with lm-evaluation-harness.\n",
- "- Compare the baseline evaluation results with the finetuned model evaluation results.\n",
- "- Deploy the finetuned Llama 3.1 model on Vertex AI Prediction.\n",
- "- Send prediction requests to your finetuned Llama 3.1 model.\n",
- "\n",
- "### File a bug\n",
- "\n",
- "File a bug on [GitHub](https://github.com/GoogleCloudPlatform/vertex-ai-samples/issues/new) if you encounter any issue with the notebook.\n",
- "\n",
- "### Costs\n",
- "\n",
- "This tutorial uses billable components of Google Cloud:\n",
- "\n",
- "* Vertex AI\n",
- "* Cloud Storage\n",
- "\n",
- "Learn about [Vertex AI pricing](https://cloud.google.com/vertex-ai/pricing), [Cloud Storage pricing](https://cloud.google.com/storage/pricing), and use the [Pricing Calculator](https://cloud.google.com/products/calculator/) to generate a cost estimate based on your projected usage."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "264c07757582"
- },
- "source": [
- "## Before you begin"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "855d6b96f291"
- },
- "outputs": [],
- "source": [
- "# @title Install Python Packages for Finetuning\n",
- "\n",
- "# @markdown Install packages to validate dataset with template.\n",
- "! pip install --upgrade --quiet gcsfs==2024.3.1\n",
- "! pip install --upgrade --quiet accelerate==0.31.0\n",
- "! pip install --upgrade --quiet transformers==4.45.2\n",
- "! pip install --upgrade --quiet datasets==2.19.2\n",
- "\n",
- "# Load local tensorboard.\n",
- "%load_ext tensorboard"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "50273xHFJi5T"
- },
- "outputs": [],
- "source": [
- "# @title Setup Google Cloud project\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. For finetuning, **[click here](https://console.cloud.google.com/iam-admin/quotas?location=us-central1&metric=aiplatform.googleapis.com%2Frestricted_image_training_nvidia_a100_80gb_gpus)** to check if your project already has the required 8 Nvidia A100 80 GB GPUs in the us-central1 region. If yes, then run this notebook in the us-central1 region. If you do not have 8 Nvidia A100 80 GPUs or have more GPU requirements than this, then schedule your job with Nvidia H100 GPUs via Dynamic Workload Scheduler using [these instructions](https://cloud.google.com/vertex-ai/docs/training/schedule-jobs-dws). For Dynamic Workload Scheduler, check the [us-central1](https://console.cloud.google.com/iam-admin/quotas?location=us-central1&metric=aiplatform.googleapis.com%2Fcustom_model_training_preemptible_nvidia_h100_gpus) or [europe-west4](https://console.cloud.google.com/iam-admin/quotas?location=europe-west4&metric=aiplatform.googleapis.com%2Fcustom_model_training_preemptible_nvidia_h100_gpus) quota for Nvidia H100 GPUs. If you do not have enough GPUs, then you can follow [these instructions](https://cloud.google.com/docs/quotas/view-manage#viewing_your_quota_console) to request quota.\n",
- "\n",
- "# @markdown 3. For evaluation and deployment, **[click here](https://console.cloud.google.com/iam-admin/quotas?location=us-central1&metric=aiplatform.googleapis.com%2Fcustom_model_serving_nvidia_l4_gpus)** to check if your project already has the required 1 L4 GPU in the us-central1 region. If yes, then run this notebook in the us-central1 region. If you need more L4 GPUs for your project, then you can follow [these instructions](https://cloud.google.com/docs/quotas/view-manage#viewing_your_quota_console) to request more. Alternatively, 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).\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, us-east5, europe-west4, us-west1, asia-southeast1 |\n",
- "\n",
- "# @markdown 4. **[Optional]** [Create a Cloud Storage bucket](https://cloud.google.com/storage/docs/creating-buckets) for storing experiment outputs. Set the BUCKET_URI for the experiment environment. The specified Cloud Storage bucket (`BUCKET_URI`) should be located in the same region as where the notebook was launched. Note that a multi-region bucket (eg. \"us\") is not considered a match for a single region covered by the multi-region range (eg. \"us-central1\"). If not set, a unique GCS bucket will be created instead.\n",
- "\n",
- "BUCKET_URI = \"\" # @param {type:\"string\"}\n",
- "\n",
- "# @markdown 5. **[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",
- "# Import the necessary packages.\n",
- "! rm -rf vertex-ai-samples && git clone https://github.com/GoogleCloudPlatform/vertex-ai-samples.git\n",
- "! cd vertex-ai-samples && git reset --hard 0727e19520cf7957bceb701c248221bd3dbe4f1f\n",
- "\n",
- "import datetime\n",
- "import importlib\n",
- "import json\n",
- "import os\n",
- "import pprint\n",
- "import uuid\n",
- "from typing import Tuple\n",
- "\n",
- "import numpy as np\n",
- "import pandas as pd\n",
- "from datasets import load_dataset\n",
- "from google.cloud import aiplatform, storage\n",
- "from google.cloud.aiplatform.compat.types import \\\n",
- " custom_job as gca_custom_job_compat\n",
- "from IPython.display import Markdown, display\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",
- "models, endpoints = {}, {}\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",
- "# Cloud Storage bucket for storing the experiment artifacts.\n",
- "# A unique GCS bucket will be created for the purpose of this notebook. If you\n",
- "# prefer using your own GCS bucket, change the value yourself below.\n",
- "now = datetime.datetime.now().strftime(\"%Y%m%d%H%M%S\")\n",
- "BUCKET_NAME = \"/\".join(BUCKET_URI.split(\"/\")[:3])\n",
- "\n",
- "if BUCKET_URI is None or BUCKET_URI.strip() == \"\" or BUCKET_URI == \"gs://\":\n",
- " BUCKET_URI = f\"gs://{PROJECT_ID}-tmp-{now}-{str(uuid.uuid4())[:4]}\"\n",
- " BUCKET_NAME = \"/\".join(BUCKET_URI.split(\"/\")[:3])\n",
- " ! gsutil mb -l {REGION} {BUCKET_URI}\n",
- "else:\n",
- " assert BUCKET_URI.startswith(\"gs://\"), \"BUCKET_URI must start with `gs://`.\"\n",
- " shell_output = ! gsutil ls -Lb {BUCKET_NAME} | grep \"Location constraint:\" | sed \"s/Location constraint://\"\n",
- " bucket_region = shell_output[0].strip().lower()\n",
- " if bucket_region != REGION:\n",
- " raise ValueError(\n",
- " \"Bucket region %s is different from notebook region %s\"\n",
- " % (bucket_region, REGION)\n",
- " )\n",
- "print(f\"Using this GCS Bucket: {BUCKET_URI}\")\n",
- "\n",
- "STAGING_BUCKET = os.path.join(BUCKET_URI, \"temporal\")\n",
- "MODEL_BUCKET = os.path.join(BUCKET_URI, \"llama3_1\")\n",
- "\n",
- "\n",
- "# Initialize Vertex AI API.\n",
- "print(\"Initializing Vertex AI API.\")\n",
- "aiplatform.init(project=PROJECT_ID, location=REGION, staging_bucket=STAGING_BUCKET)\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",
- "\n",
- "# Provision permissions to the SERVICE_ACCOUNT with the GCS bucket\n",
- "! gsutil iam ch serviceAccount:{SERVICE_ACCOUNT}:roles/storage.admin $BUCKET_NAME\n",
- "\n",
- "! gcloud config set project $PROJECT_ID\n",
- "! gcloud projects add-iam-policy-binding --no-user-output-enabled {PROJECT_ID} --member=serviceAccount:{SERVICE_ACCOUNT} --role=\"roles/storage.admin\"\n",
- "! gcloud projects add-iam-policy-binding --no-user-output-enabled {PROJECT_ID} --member=serviceAccount:{SERVICE_ACCOUNT} --role=\"roles/aiplatform.user\""
- ]
- },
- {
- "metadata": {
- "id": "VUSi9jUcvdBC"
- },
- "cell_type": "code",
- "source": [
- "# @title Access Llama 3.1 models\n",
- "\n",
- "# @markdown This notebook utilizes Llama 3.1 models for finetuning and serving via the Hugging Face platform.\n",
- "# @markdown Note that these models are gated. Visit the relevant [model card](https://huggingface.co/meta-llama/Llama-3.1-8B-Instruct)\n",
- "# @markdown on the Hugging Face platform and then follow the steps to accept the\n",
- "# @markdown license agreement and to obtain access to the model weights.\n",
- "\n",
- "LOAD_MODEL_FROM = \"Hugging Face\"\n",
- "base_model_id = \"meta-llama/Llama-3.1-8B-Instruct\" # @param {type: \"string\"}\n",
- "pretrained_model_id = base_model_id\n",
- "\n",
- "# @markdown Additionally, you must provide a Hugging Face User Access Token (with read access) to access the Llama 3.1 models. You can follow the [Hugging Face documentation](https://huggingface.co/docs/hub/en/security-tokens) to create a **read** access token and put it in the `HF_TOKEN` field below.\n",
- "\n",
- "HF_TOKEN = \"\" # @param {type:\"string\", isTemplate:true}\n",
- "\n",
- "if LOAD_MODEL_FROM == \"Hugging Face\":\n",
- " assert (\n",
- " HF_TOKEN\n",
- " ), \"Provide a read access HF_TOKEN to load models from Hugging Face, or select a different model source. You can comment out this assert statement to skip this check.\""
- ],
- "outputs": [],
- "execution_count": null
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "LqC4oqOi7epn"
- },
- "source": [
- "## Evaluation before finetuning"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "kn25gusYEGxG"
- },
- "outputs": [
- {
- "data": {
- "text/markdown": [
- "| alias | exact_match | exact_match_stderr | f1 | f1_stderr |\n",
- "| --- | --- | --- | --- | --- |\n",
- "| coqa | 0.1158 | 0.0137 | 0.1872 | 0.0150 |\n"
- ],
- "text/plain": [
- ""
- ]
- },
- "metadata": {},
- "output_type": "display_data"
- }
- ],
- "source": [
- "# @markdown To establish a performance benchmark baseline, we present the evaluation results of the baseline Llama-3.1-8B-Instruct model on the CoQA task using [lm-evaluation-harness](https://github.com/EleutherAI/lm-evaluation-harness).\n",
- "\n",
- "# @markdown lm-evaluation-harness is a versatile framework for evaluating\n",
- "# @markdown generative language models across numerous tasks. Supporting over 60\n",
- "# @markdown standard academic benchmarks and prototyping multimodal tasks, it's\n",
- "# @markdown compatible with various models, including those from Hugging Face and\n",
- "# @markdown vLLM.\n",
- "\n",
- "# @markdown **[Optional]** You can optionally re-run the baseline evaluation by\n",
- "# @markdown checking the box below. Otherwise, this cell displays the previously\n",
- "# @markdown saved baseline evaluation results.\n",
- "RE_RUN_EVALUATION = False # @param {type:\"boolean\"}\n",
- "\n",
- "\n",
- "def run_lm_evaluation_harness(\n",
- " job_name: str,\n",
- " base_model_id: str = None,\n",
- " machine_type: str = \"g2-standard-12\",\n",
- " accelerator_type: str = \"NVIDIA_L4\",\n",
- " accelerator_count: int = 1,\n",
- " gpu_memory_utilization: float = 0.95,\n",
- " max_model_len: int = 8192,\n",
- " enforce_eager: bool = False,\n",
- " enable_lora: bool = False,\n",
- " max_loras: int = 1,\n",
- " lora_path: str = None,\n",
- " max_num_seqs: int = 64,\n",
- " eval_task: str = \"coqa\",\n",
- ") -> str:\n",
- " \"\"\"Run lm-evaluation-harness to evaluate the model, and returns .\"\"\"\n",
- "\n",
- " if accelerator_type == \"NVIDIA_L4\":\n",
- " dws_kwargs = {}\n",
- " elif accelerator_type == \"NVIDIA_H100_80GB\":\n",
- " dws_kwargs = {\n",
- " \"max_wait_duration\": 1800, # 30 minutes\n",
- " \"scheduling_strategy\": gca_custom_job_compat.Scheduling.Strategy.FLEX_START,\n",
- " }\n",
- " model_args = (\n",
- " f\"pretrained={base_model_id},\"\n",
- " f\"tensor_parallel_size={accelerator_count},\"\n",
- " \"swap_space=16,\"\n",
- " f\"gpu_memory_utilization={gpu_memory_utilization},\"\n",
- " f\"enforce_eager={enforce_eager},\"\n",
- " f\"max_num_seqs={max_num_seqs},max_model_len={max_model_len}\"\n",
- " )\n",
- " if enable_lora:\n",
- " model_args += f\",enable_lora={enable_lora},lora_local_path={lora_path},max_loras={max_loras}\"\n",
- "\n",
- " eval_type = \"vllm\"\n",
- "\n",
- " eval_output_dir = os.path.join(MODEL_BUCKET, job_name)\n",
- " eval_output_dir_gcsfuse = eval_output_dir.replace(\"gs://\", \"/gcs/\")\n",
- "\n",
- " # Prepare evaluation command that runs the evaluation harness.\n",
- " eval_command = [\n",
- " \"lm_eval\",\n",
- " \"--model\",\n",
- " eval_type,\n",
- " \"--tasks\",\n",
- " eval_task,\n",
- " \"--output_path\",\n",
- " f\"{eval_output_dir_gcsfuse}\",\n",
- " \"--model_args\",\n",
- " model_args,\n",
- " ]\n",
- "\n",
- " if \"Instruct\" in base_model_id:\n",
- " eval_command.append(\"--apply_chat_template\")\n",
- "\n",
- " # The evaluation docker image.\n",
- " EVAL_DOCKER_URI = \"us-docker.pkg.dev/vertex-ai/vertex-vision-model-garden-dockers/pytorch-lm-evaluation-harness:20241016_0934_RC00\"\n",
- "\n",
- " container_spec = {\n",
- " \"image_uri\": EVAL_DOCKER_URI,\n",
- " \"command\": eval_command,\n",
- " \"args\": [],\n",
- " }\n",
- " if HF_TOKEN:\n",
- " container_spec[\"env\"] = [\n",
- " {\n",
- " \"name\": \"HF_TOKEN\",\n",
- " \"value\": HF_TOKEN,\n",
- " }\n",
- " ]\n",
- "\n",
- " # Pass evaluation arguments and launch job.\n",
- " worker_pool_specs = [\n",
- " {\n",
- " \"machine_spec\": {\n",
- " \"machine_type\": machine_type,\n",
- " \"accelerator_type\": accelerator_type,\n",
- " \"accelerator_count\": accelerator_count,\n",
- " },\n",
- " \"replica_count\": 1,\n",
- " \"disk_spec\": {\n",
- " \"boot_disk_size_gb\": 500,\n",
- " },\n",
- " \"container_spec\": container_spec,\n",
- " }\n",
- " ]\n",
- "\n",
- " # Add labels for the finetuning job.\n",
- " labels = {\n",
- " \"mg-source\": \"notebook\",\n",
- " \"mg-notebook-name\": \"model_garden_finetuning_tutorial.ipynb\".split(\".\")[0],\n",
- " }\n",
- "\n",
- " labels[\"mg-tune\"] = \"publishers-meta-models-llama3-1\"\n",
- " versioned_model_id = base_model_id.split(\"/\")[1].lower().replace(\".\", \"-\")\n",
- " labels[\"versioned-mg-tune\"] = f\"{labels['mg-tune']}-{versioned_model_id}\"\n",
- "\n",
- " eval_job = aiplatform.CustomJob(\n",
- " display_name=job_name,\n",
- " worker_pool_specs=worker_pool_specs,\n",
- " base_output_dir=eval_output_dir,\n",
- " labels=labels,\n",
- " )\n",
- "\n",
- " eval_job.run(**dws_kwargs)\n",
- "\n",
- " print(\"Evaluation results were saved in:\", eval_output_dir)\n",
- " return eval_output_dir\n",
- "\n",
- "\n",
- "def print_coqa_result(eval_output_dir):\n",
- " # Create a client object for Google Cloud Storage\n",
- " storage_client = storage.Client()\n",
- "\n",
- " # Name of the bucket\n",
- " bucket_name = BUCKET_URI.replace(\"gs://\", \"\", 1)\n",
- "\n",
- " # Get the bucket object\n",
- " bucket = storage_client.get_bucket(bucket_name)\n",
- "\n",
- " prefix = os.path.join(\n",
- " eval_output_dir.replace(BUCKET_URI, \"\", 1).lstrip(\"/\"),\n",
- " pretrained_model_id.replace(\"/\", \"__\"),\n",
- " )\n",
- "\n",
- " # List blobs in the bucket\n",
- " blobs = bucket.list_blobs(prefix=prefix)\n",
- "\n",
- " # Print the names of the blobs\n",
- " for blob in blobs:\n",
- " if blob.name.endswith(\".json\"):\n",
- " data = json.loads(blob.download_as_string())\n",
- " markdown_table = dict_to_markdown_table(data[\"results\"][\"coqa\"])\n",
- " display(Markdown(markdown_table))\n",
- "\n",
- "\n",
- "def dict_to_markdown_table(data):\n",
- " \"\"\"Converts a dictionary to a Markdown table.\"\"\"\n",
- "\n",
- " # Parse the evaluation output keys.\n",
- " keys = data.keys()\n",
- " display_keys = [\n",
- " key.replace(\"em\", \"exact_match\").replace(\",none\", \"\") for key in keys\n",
- " ]\n",
- "\n",
- " # Start with the table header.\n",
- " markdown = \"| \" + \" | \".join(display_keys) + \" |\\n\"\n",
- " markdown += \"| \" + \" | \".join([\"---\"] * len(data)) + \" |\\n\"\n",
- "\n",
- " # Add values.\n",
- " row = []\n",
- " for key in keys:\n",
- " value = data[key]\n",
- " if isinstance(value, float):\n",
- " # Round float values to the 4th decimal place.\n",
- " row.append(f\"{value:.4f}\")\n",
- " else:\n",
- " row.append(str(value))\n",
- "\n",
- " markdown += \"| \" + \" | \".join(row) + \" |\\n\"\n",
- "\n",
- " return markdown\n",
- "\n",
- "\n",
- "# Baseline evaluation parameters.\n",
- "eval_accelerator_type = \"NVIDIA_L4\" # @param [\"NVIDIA_L4\", \"NVIDIA_H100_80GB\"]\n",
- "\n",
- "if eval_accelerator_type == \"NVIDIA_L4\":\n",
- " eval_machine_type = \"g2-standard-96\"\n",
- " eval_accelerator_count = 8\n",
- "elif eval_accelerator_type == \"NVIDIA_H100_80GB\":\n",
- " eval_machine_type = \"a3-highgpu-4g\"\n",
- " eval_accelerator_count = 4\n",
- "else:\n",
- " raise ValueError(f\"Recommended GPU setting not found for: {eval_accelerator_type}.\")\n",
- "\n",
- "common_util.check_quota(\n",
- " project_id=PROJECT_ID,\n",
- " region=REGION,\n",
- " accelerator_type=eval_accelerator_type,\n",
- " accelerator_count=eval_accelerator_count,\n",
- " is_for_training=False,\n",
- ")\n",
- "\n",
- "gpu_memory_utilization = 0.95\n",
- "max_model_len = 8192 # Maximum context length.\n",
- "\n",
- "if RE_RUN_EVALUATION:\n",
- " eval_output_dir = run_lm_evaluation_harness(\n",
- " job_name=common_util.get_job_name_with_datetime(prefix=\"llama3_1-vllm-eval\"),\n",
- " base_model_id=pretrained_model_id,\n",
- " machine_type=eval_machine_type,\n",
- " accelerator_type=eval_accelerator_type,\n",
- " accelerator_count=eval_accelerator_count,\n",
- " gpu_memory_utilization=gpu_memory_utilization,\n",
- " max_model_len=max_model_len,\n",
- " enable_lora=False,\n",
- " )\n",
- "\n",
- " print_coqa_result(eval_output_dir)\n",
- "else:\n",
- " # Show previously saved evaluation results.\n",
- " baseline = {\n",
- " \"alias\": \"coqa\",\n",
- " \"em,none\": 0.11583333333333332,\n",
- " \"em_stderr,none\": 0.013738165693259453,\n",
- " \"f1,none\": 0.1871649907227967,\n",
- " \"f1_stderr,none\": 0.015001300896703354,\n",
- " }\n",
- "\n",
- " # Convert and display\n",
- " markdown_table = dict_to_markdown_table(baseline)\n",
- " display(Markdown(markdown_table))"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "A39xW8wmOUcc"
- },
- "source": [
- "## Prepare training dataset\n"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "S0-r93wDjSdz"
- },
- "source": [
- "In this tutorial, we will use [databricks/databricks-dolly-15k](https://huggingface.co/datasets/databricks/databricks-dolly-15k) dataset to train a Llama 3.1 model to perform a chat completion task.\n",
- "The Databricks Dolly 15k dataset is a collection of 15,000 high-quality\n",
- "human-generated prompt/response pairs.\n",
- "\n",
- "The dataset consists of four columns:\n",
- "\n",
- " - `category`: The category of the prompt, such as brainstorming, classification, closed QA, generation, information extraction, open QA, and summarization.\n",
- "\n",
- " - `context`: The context of the prompt, which can be either a question, a statement, or an image.\n",
- "\n",
- " - `instruction`: The instruction for the model, which specifies the task to be performed.\n",
- "\n",
- " - `response`: The response generated by the model.\n"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "iPl6FyrzIXjM"
- },
- "outputs": [
- {
- "data": {
- "application/vnd.jupyter.widget-view+json": {
- "model_id": "0e5cd04b37734c688822555561d1405c",
- "version_major": 2,
- "version_minor": 0
- },
- "text/plain": [
- "Downloading readme: 0%| | 0.00/8.20k [00:00, ?B/s]"
- ]
- },
- "metadata": {},
- "output_type": "display_data"
- },
- {
- "data": {
- "application/vnd.jupyter.widget-view+json": {
- "model_id": "725d25af9f274696b23158a379836651",
- "version_major": 2,
- "version_minor": 0
- },
- "text/plain": [
- "Downloading data: 0%| | 0.00/13.1M [00:00, ?B/s]"
- ]
- },
- "metadata": {},
- "output_type": "display_data"
- },
- {
- "data": {
- "application/vnd.jupyter.widget-view+json": {
- "model_id": "32083b04dfb44c01b014e93fedc7b0b6",
- "version_major": 2,
- "version_minor": 0
- },
- "text/plain": [
- "Generating train split: 0%| | 0/15011 [00:00, ? examples/s]"
- ]
- },
- "metadata": {},
- "output_type": "display_data"
- },
- {
- "data": {
- "text/markdown": [
- "| instruction | context | response | category |\n",
- "|:---------------------------------------------------------------------------------------------|:----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:-----------------------------------------------------------------------------------------------------------|:---------------|\n",
- "| When did Virgin Australia start operating? | Virgin Australia, the trading name of Virgin Australia Airlines Pty Ltd, is an Australian-based airline. It is the largest airline by fleet size to use the Virgin brand. It commenced services on 31 August 2000 as Virgin Blue, with two aircraft on a single route. It suddenly found itself as a major airline in Australia's domestic market after the collapse of Ansett Australia in September 2001. The airline has since grown to directly serve 32 cities in Australia, from hubs in Brisbane, Melbourne and Sydney. | Virgin Australia commenced services on 31 August 2000 as Virgin Blue, with two aircraft on a single route. | closed_qa |\n",
- "| Which is a species of fish? Tope or Rope | | Tope | classification |\n",
- "| Why can camels survive for long without water? | | Camels use the fat in their humps to keep them filled with energy and hydration for long periods of time. | open_qa |\n",
- "| Alice's parents have three daughters: Amy, Jessy, and what’s the name of the third daughter? | | The name of the third daughter is Alice | open_qa |\n",
- "| When was Tomoaki Komorida born? | Komorida was born in Kumamoto Prefecture on July 10, 1981. After graduating from high school, he joined the J1 League club Avispa Fukuoka in 2000. Although he debuted as a midfielder in 2001, he did not play much and the club was relegated to the J2 League at the end of the 2001 season. In 2002, he moved to the J2 club Oita Trinita. He became a regular player as a defensive midfielder and the club won the championship in 2002 and was promoted in 2003. He played many matches until 2005. In September 2005, he moved to the J2 club Montedio Yamagata. In 2006, he moved to the J2 club Vissel Kobe. Although he became a regular player as a defensive midfielder, his gradually was played less during the summer. In 2007, he moved to the Japan Football League club Rosso Kumamoto (later Roasso Kumamoto) based in his local region. He played as a regular player and the club was promoted to J2 in 2008. Although he did not play as much, he still played in many matches. In 2010, he moved to Indonesia and joined Persela Lamongan. In July 2010, he returned to Japan and joined the J2 club Giravanz Kitakyushu. He played often as a defensive midfielder and center back until 2012 when he retired. | Tomoaki Komorida was born on July 10,1981. | closed_qa |"
- ],
- "text/plain": [
- ""
- ]
- },
- "metadata": {},
- "output_type": "display_data"
- }
- ],
- "source": [
- "# @markdown Let's take a look at some example rows. Run this cell to show the first 5 rows of the dataset.\n",
- "\n",
- "# Load the dataset\n",
- "dataset = load_dataset(\"databricks/databricks-dolly-15k\")\n",
- "\n",
- "# Convert the first 5 rows to a DataFrame\n",
- "df = pd.DataFrame(dataset[\"train\"][:5])\n",
- "\n",
- "# Display the DataFrame as a Markdown table\n",
- "display(Markdown(df.head().to_markdown(index=False, numalign=\"left\", stralign=\"left\")))"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "UFeToj7wODGI"
- },
- "source": [
- "We want to train our model to predict `response` based on the `instruction` and `context`.\n",
- "There are some important considerations when preparing this dataset.\n",
- "1. Since the dataset only contains the train split, we need to split the dataset into train and validation splits to evaluate the status of training.\n",
- "2. Note that only some rows contain `context` and we have to format our dataset accordingly."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "WjADb9VxMJ1a"
- },
- "outputs": [],
- "source": [
- "# @title Separate the dataset into train and validation splits\n",
- "\n",
- "LOCAL_TRAIN_SPLIT = os.path.join(os.getcwd(), \"train.jsonl\")\n",
- "LOCAL_VALIDATION_SPLIT = os.path.join(os.getcwd(), \"validation.jsonl\")\n",
- "\n",
- "# @markdown Set a random seed for reproducibility. You can choose any integer.\n",
- "seed_value = 42 # @param{type:\"integer\"}\n",
- "np.random.seed(seed_value)\n",
- "\n",
- "train_data = dataset[\"train\"]\n",
- "# Shuffle the training data before splitting\n",
- "train_data = train_data.shuffle(seed=seed_value)\n",
- "\n",
- "# Calculate the number of samples for validation\n",
- "# @markdown The ratio of the validation split.\n",
- "validation_split = 0.1 # @param{type:\"number\"}\n",
- "num_validation_samples = int(len(train_data) * validation_split)\n",
- "\n",
- "# Generate random indices for the validation set (now with seed)\n",
- "random_indices = np.random.choice(\n",
- " len(train_data), size=num_validation_samples, replace=False\n",
- ")\n",
- "\n",
- "# Create the validation set\n",
- "validation_data = train_data.select(random_indices)\n",
- "\n",
- "# Create the new train set by excluding the validation samples\n",
- "train_indices = np.array([i for i in range(len(train_data)) if i not in random_indices])\n",
- "new_train_data = train_data.select(train_indices)\n",
- "\n",
- "# Save the new train and validation sets as JSONL files\n",
- "new_train_data.to_json(LOCAL_TRAIN_SPLIT)\n",
- "validation_data.to_json(LOCAL_VALIDATION_SPLIT)\n",
- "\n",
- "print(f\"Train split is saved at {LOCAL_TRAIN_SPLIT}\")\n",
- "print(f\"Validation split is saved at {LOCAL_VALIDATION_SPLIT}\")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "5wvy1FL1RynT"
- },
- "source": [
- "## Vertex Model Garden dataset template\n",
- "\n",
- "Although datasets often have intricate structures, the supported LLM models accept only flat strings. A template facilitates parsing a dataset and preprocessing it to be compatible with the model.\n",
- "\n",
- "Using a template is often the greatest friction point to training an LLM model, so we will explain it in detail in this section.\n",
- "\n",
- "When finetuning a pretrained model, it is advisable to maintain the same format as the original training data. A template helps replicate the format, ensuring consistency and potentially enhancing the finetuning process. Vertex Model Garden training provides templates for streamlined preprocessing of datasets.\n",
- "\n",
- "Vertex Model Garden supports templates for simple instruction-response pair datasets using `str.format()` [method](https://docs.python.org/3/library/stdtypes.html#str.format). See details in this [dataset template documentation](https://github.com/GoogleCloudPlatform/vertex-ai-samples/blob/6fabc23db697b6d8b20113ecf006c454d09b3421/community-content/vertex_model_garden/model_oss/peft/train/vmg/templates/README.md). However, in this tutorial, we will dig deep into a more advanced use-case that supports multi-turn chat messages.\n",
- "\n",
- "In this section we will:\n",
- "- Examine the original `chat_template` that Llama 3.1 model was trained with, so that we can match its format for our training.\n",
- "- Restructure the dataset to a format suitable for conversational AI models.\n",
- "- Create our own `chat_template` that matches the Llama 3.1 model format and incorporate our dataset where the `context` field is optional, as mentioned in the previous section.\n",
- "- Show an example of the string the model gets after applying our `chat_template`.\n",
- "- Create a Vertex Model Garden dataset template that specifies our `chat_template`."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "6Ox7Ou5goS4-"
- },
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "('<|begin_of_text|><|start_header_id|>system<|end_header_id|>\\n'\n",
- " '\\n'\n",
- " 'You are a helpful '\n",
- " 'assistant.<|eot_id|><|start_header_id|>user<|end_header_id|>\\n'\n",
- " '\\n'\n",
- " 'Hello, how are you?<|eot_id|><|start_header_id|>assistant<|end_header_id|>\\n'\n",
- " '\\n'\n",
- " 'I am doing well, thank you.<|eot_id|>')\n"
- ]
- }
- ],
- "source": [
- "# @title What is a chat_template?\n",
- "\n",
- "# @markdown Large language models (LLMs) are frequently used for chatbots. Unlike standard language models that generate text in a single stream, chatbots engage in conversations made up of multiple messages. Each message has a role (e.g., \"user\" or \"assistant\") and text. `chat_template` defines how to transform a list of messages into a single, tokenizable string that the model can understand.\n",
- "\n",
- "# @markdown Since we will finetune the Llama 3.1 model, we first look at the format it was trained with to follow the same format when we train our model.\n",
- "# @markdown We will look at the Llama 3.1 model's `chat_template` that can be found in the\n",
- "# @markdown [tokenizer_config.json](https://huggingface.co/meta-llama/Llama-3.1-8B-Instruct/blob/main/tokenizer_config.json#L2053)\n",
- "# @markdown file in Hugging face model page.\n",
- "# @markdown The `chat_tamplate` is in [Jinja](https://jinja.palletsprojects.com/en/stable/templates/)\n",
- "# @markdown format. If you're\n",
- "# @markdown having trouble understanding it, you can ask a generative AI like [Gemini](https://gemini.google.com)\n",
- "# @markdown to translate the Jinja template for you. For example, you can ask:\n",
- "\n",
- "# @markdown ````\n",
- "# @markdown Translate the following Jinja template to Python, where bos_token is \"<|begin_of_text|>\":\n",
- "# @markdown ```\n",
- "# @markdown {{- bos_token }}\n",
- "# @markdown {#- This block extracts the system message, so we can slot it into the right place. #}\n",
- "# @markdown {%- if messages[0]['role'] == 'system' %}\n",
- "# @markdown {%- set system_message = messages[0]['content']|trim %}\n",
- "# @markdown {%- set messages = messages[1:] %}\n",
- "# @markdown {%- else %}\n",
- "# @markdown {%- set system_message = \"\" %}\n",
- "# @markdown {%- endif %}\n",
- "# @markdown {#- System message #}\n",
- "# @markdown {{- \"<|start_header_id|>system<|end_header_id|>\\n\\n\" }}\n",
- "# @markdown {{- system_message }}\n",
- "# @markdown {{- \"<|eot_id|>\" }}\n",
- "# @markdown {%- for message in messages %}\n",
- "# @markdown {{- '<|start_header_id|>' + message['role'] + '<|end_header_id|>\\n\\n'+ message['content'] | trim + '<|eot_id|>' }}\n",
- "# @markdown {%- endfor %}\n",
- "# @markdown {%- if add_generation_prompt %}\n",
- "# @markdown {{- '<|start_header_id|>assistant<|end_header_id|>\\n\\n' }}\n",
- "# @markdown {%- endif %}\n",
- "# @markdown ```\n",
- "# @markdown ````\n",
- "\n",
- "# @markdown You will get a translated `chat_template` in Python. Note that the\n",
- "# @markdown `bos_token` is specified in the [tokenizer_config.json](https://huggingface.co/meta-llama/Llama-3.1-8B-Instruct/blob/main/tokenizer_config.json#L2052).\n",
- "# @markdown ```\n",
- "# @markdown def render_template(messages, add_generation_prompt=False):\n",
- "# @markdown bos_token = \"<|begin_of_text|>\"\n",
- "# @markdown output = bos_token\n",
- "# @markdown\n",
- "# @markdown system_message = \"\"\n",
- "# @markdown if messages and messages[0]['role'] == 'system':\n",
- "# @markdown system_message = messages[0]['content'].strip()\n",
- "# @markdown messages = messages[1:]\n",
- "# @markdown\n",
- "# @markdown output += \"<|start_header_id|>system<|end_header_id|>\\n\\n\"\n",
- "# @markdown output += system_message\n",
- "# @markdown output += \"<|eot_id|>\"\n",
- "# @markdown\n",
- "# @markdown for message in messages:\n",
- "# @markdown output += f\"<|start_header_id|>{message['role']}<|end_header_id|>\\n\\n{message['content'].strip()}<|eot_id|>\"\n",
- "# @markdown\n",
- "# @markdown if add_generation_prompt:\n",
- "# @markdown output += \"<|start_header_id|>assistant<|end_header_id|>\\n\\n\"\n",
- "# @markdown\n",
- "# @markdown return output\n",
- "# @markdown ```\n",
- "\n",
- "\n",
- "def render_template(messages, add_generation_prompt=False):\n",
- " bos_token = \"<|begin_of_text|>\"\n",
- " output = bos_token\n",
- "\n",
- " system_message = \"\"\n",
- " if messages and messages[0][\"role\"] == \"system\":\n",
- " system_message = messages[0][\"content\"].strip()\n",
- " messages = messages[1:]\n",
- "\n",
- " output += \"<|start_header_id|>system<|end_header_id|>\\n\\n\"\n",
- " output += system_message\n",
- " output += \"<|eot_id|>\"\n",
- "\n",
- " for message in messages:\n",
- " output += (\n",
- " \"<|start_header_id|>\"\n",
- " + message[\"role\"]\n",
- " + \"<|end_header_id|>\\n\\n\"\n",
- " + message[\"content\"].strip()\n",
- " + \"<|eot_id|>\"\n",
- " )\n",
- "\n",
- " if add_generation_prompt:\n",
- " output += \"<|start_header_id|>assistant<|end_header_id|>\\n\\n\"\n",
- "\n",
- " return output\n",
- "\n",
- "\n",
- "# @markdown The `<|begin_of_text|>`, `<|start_header_id|>`, `<|end_header_id|>`, and `<|eot_id|>` tokens serve specific purposes in the context of text generation models.\n",
- "# @markdown These tokens help the model delineate the boundaries of a text generation task. They provide clear markers for the start and end points, enabling the model to function effectively and produce coherent text.\n",
- "\n",
- "# @markdown Run this cell to show an example output of the template given the\n",
- "# @markdown below `messages`.\n",
- "# @markdown ```\n",
- "# @markdown messages = [\n",
- "# @markdown {'role': 'system', 'content': 'You are a helpful assistant.'},\n",
- "# @markdown {'role': 'user', 'content': 'Hello, how are you?'},\n",
- "# @markdown {'role': 'assistant', 'content': 'I am doing well, thank you.'}\n",
- "# @markdown ]\n",
- "# @markdown ```\n",
- "# @markdown Note that each item in the messages represents a single turn or message in the conversation.\n",
- "# @markdown The key `role` indicates who is speaking or providing the content. There are three main roles which the Llama 3.1 model uses:\n",
- "# @markdown - `system`: This role sets the initial context or instructions for the LLM. It's like the stage direction or the background information for the play. It tells the LLM how to behave.\n",
- "# @markdown - `user`: This role represents the human interacting with the LLM. It's what the user says to the model.\n",
- "# @markdown - `assistant`: This role represents the LLM's response. It's what the model says back to the user.\n",
- "\n",
- "\n",
- "# @markdown Look at the format which the original Llama 3.1 model was trained with.\n",
- "\n",
- "messages = [\n",
- " {\"role\": \"system\", \"content\": \"You are a helpful assistant.\"},\n",
- " {\"role\": \"user\", \"content\": \"Hello, how are you?\"},\n",
- " {\"role\": \"assistant\", \"content\": \"I am doing well, thank you.\"},\n",
- "]\n",
- "\n",
- "rendered_text = render_template(messages)\n",
- "pprint.pprint(rendered_text, width=80)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "lEPZ7xm7Z0Z4"
- },
- "outputs": [],
- "source": [
- "# @title Restructure the dataset\n",
- "\n",
- "# @markdown Recall that our dataset has `instruction`, `context`, and `response` columns.\n",
- "# @markdown However, the Llama 3.1 model was trained with only `role` and `content` columns, in a list of messages.\n",
- "# @markdown We will first restructure the `instruction`, `context`, and `response` columns into a list, `messages`, suitable for conversational AI models, where each message has a `role` (`user` or `assistant`), `content`, and `context` keys.\n",
- "\n",
- "# @markdown Click `Show code` below to have a look the `preprocess_jsonl` function.\n",
- "\n",
- "\n",
- "def preprocess_jsonl(input_file, output_file):\n",
- " \"\"\"\n",
- " Preprocesses a JSONL file with 'instruction', 'context', and 'response' keys\n",
- " into a JSONL file with a 'messages' key containing a list of dictionaries\n",
- " with 'role' and 'content'.\n",
- "\n",
- " Args:\n",
- " input_file: Path to the input JSONL file.\n",
- " output_file: Path to the output JSONL file.\n",
- " \"\"\"\n",
- "\n",
- " try:\n",
- " with open(input_file, \"r\", encoding=\"utf-8\") as infile, open(\n",
- " output_file, \"w\", encoding=\"utf-8\"\n",
- " ) as outfile:\n",
- " for line in infile:\n",
- " try:\n",
- " data = json.loads(line.strip())\n",
- "\n",
- " if not all(key in data for key in [\"instruction\", \"response\"]):\n",
- " print(\n",
- " f\"Skipping line due to missing keys: {line.strip()}\"\n",
- " ) # Handle missing keys gracefully\n",
- " continue\n",
- "\n",
- " messages = [\n",
- " {\n",
- " \"role\": \"user\",\n",
- " \"content\": data[\"instruction\"],\n",
- " \"context\": data[\"context\"],\n",
- " },\n",
- " {\"role\": \"assistant\", \"content\": data[\"response\"]},\n",
- " ]\n",
- "\n",
- " new_data = {\"messages\": messages}\n",
- " outfile.write(json.dumps(new_data, ensure_ascii=False) + \"\\n\")\n",
- "\n",
- " except json.JSONDecodeError:\n",
- " print(\n",
- " f\"Skipping invalid JSON line: {line.strip()}\"\n",
- " ) # Handle JSON decode errors\n",
- " continue # Skip to the next line\n",
- "\n",
- " except FileNotFoundError:\n",
- " print(f\"Error: Input file '{input_file}' not found.\")\n",
- " except Exception as e: # Catch other potential errors like UnicodeDecodeError\n",
- " print(f\"An error occurred: {e}\")\n",
- "\n",
- "\n",
- "PREPROCESSED_TRAIN_FILENAME = \"train_preprocessed.jsonl\"\n",
- "PREPROCESSED_VALIDATION_FILENAME = \"validation_preprocessed.jsonl\"\n",
- "\n",
- "LOCAL_TRAIN_SPLIT_PREPROCESSED = os.path.join(os.getcwd(), PREPROCESSED_TRAIN_FILENAME)\n",
- "LOCAL_VALIDATION_SPLIT_PREPROCESSED = os.path.join(\n",
- " os.getcwd(), PREPROCESSED_VALIDATION_FILENAME\n",
- ")\n",
- "\n",
- "preprocess_jsonl(LOCAL_TRAIN_SPLIT, LOCAL_TRAIN_SPLIT_PREPROCESSED)\n",
- "preprocess_jsonl(LOCAL_VALIDATION_SPLIT, LOCAL_VALIDATION_SPLIT_PREPROCESSED)\n",
- "\n",
- "# Upload the dataset to GCS.\n",
- "! gsutil cp $LOCAL_TRAIN_SPLIT_PREPROCESSED $BUCKET_URI/$PREPROCESSED_TRAIN_FILENAME\n",
- "! gsutil cp $LOCAL_VALIDATION_SPLIT_PREPROCESSED $BUCKET_URI/$PREPROCESSED_VALIDATION_FILENAME\n",
- "\n",
- "print(f\"Train and validation splits are uploaded to {BUCKET_URI}/\")"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "JBsk3_TTbVxy"
- },
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "{\"messages\": [{\"role\": \"user\", \"content\": \"When did Virgin Australia start operating?\", \"context\": \"Virgin Australia, the trading name of Virgin Australia Airlines Pty Ltd, is an Australian-based airline. It is the largest airline by fleet size to use the Virgin brand. It commenced services on 31 August 2000 as Virgin Blue, with two aircraft on a single route. It suddenly found itself as a major airline in Australia's domestic market after the collapse of Ansett Australia in September 2001. The airline has since grown to directly serve 32 cities in Australia, from hubs in Brisbane, Melbourne and Sydney.\"}, {\"role\": \"assistant\", \"content\": \"Virgin Australia commenced services on 31 August 2000 as Virgin Blue, with two aircraft on a single route.\"}]}\n",
- "{\"messages\": [{\"role\": \"user\", \"content\": \"Which is a species of fish? Tope or Rope\", \"context\": \"\"}, {\"role\": \"assistant\", \"content\": \"Tope\"}]}\n",
- "{\"messages\": [{\"role\": \"user\", \"content\": \"Why can camels survive for long without water?\", \"context\": \"\"}, {\"role\": \"assistant\", \"content\": \"Camels use the fat in their humps to keep them filled with energy and hydration for long periods of time.\"}]}\n",
- "{\"messages\": [{\"role\": \"user\", \"content\": \"When was Tomoaki Komorida born?\", \"context\": \"Komorida was born in Kumamoto Prefecture on July 10, 1981. After graduating from high school, he joined the J1 League club Avispa Fukuoka in 2000. Although he debuted as a midfielder in 2001, he did not play much and the club was relegated to the J2 League at the end of the 2001 season. In 2002, he moved to the J2 club Oita Trinita. He became a regular player as a defensive midfielder and the club won the championship in 2002 and was promoted in 2003. He played many matches until 2005. In September 2005, he moved to the J2 club Montedio Yamagata. In 2006, he moved to the J2 club Vissel Kobe. Although he became a regular player as a defensive midfielder, his gradually was played less during the summer. In 2007, he moved to the Japan Football League club Rosso Kumamoto (later Roasso Kumamoto) based in his local region. He played as a regular player and the club was promoted to J2 in 2008. Although he did not play as much, he still played in many matches. In 2010, he moved to Indonesia and joined Persela Lamongan. In July 2010, he returned to Japan and joined the J2 club Giravanz Kitakyushu. He played often as a defensive midfielder and center back until 2012 when he retired.\"}, {\"role\": \"assistant\", \"content\": \"Tomoaki Komorida was born on July 10,1981.\"}]}\n",
- "{\"messages\": [{\"role\": \"user\", \"content\": \"If I have more pieces at the time of stalemate, have I won?\", \"context\": \"Stalemate is a situation in chess where the player whose turn it is to move is not in check and has no legal move. Stalemate results in a draw. During the endgame, stalemate is a resource that can enable the player with the inferior position to draw the game rather than lose. In more complex positions, stalemate is much rarer, usually taking the form of a swindle that succeeds only if the superior side is inattentive.[citation needed] Stalemate is also a common theme in endgame studies and other chess problems.\\n\\nThe outcome of a stalemate was standardized as a draw in the 19th century. Before this standardization, its treatment varied widely, including being deemed a win for the stalemating player, a half-win for that player, or a loss for that player; not being permitted; and resulting in the stalemated player missing a turn. Stalemate rules vary in other games of the chess family.\"}, {\"role\": \"assistant\", \"content\": \"No. \\nStalemate is a drawn position. It doesn't matter who has captured more pieces or is in a winning position\"}]}\n",
- "{\"messages\": [{\"role\": \"user\", \"content\": \"Given a reference text about Lollapalooza, where does it take place, who started it and what is it?\", \"context\": \"Lollapalooza /ˌlɒləpəˈluːzə/ (Lolla) is an annual American four-day music festival held in Grant Park in Chicago. It originally started as a touring event in 1991, but several years later, Chicago became its permanent location. Music genres include but are not limited to alternative rock, heavy metal, punk rock, hip hop, and electronic dance music. Lollapalooza has also featured visual arts, nonprofit organizations, and political organizations. The festival, held in Grant Park, hosts an estimated 400,000 people each July and sells out annually. Lollapalooza is one of the largest and most iconic music festivals in the world and one of the longest-running in the United States.\\n\\nLollapalooza was conceived and created in 1991 as a farewell tour by Perry Farrell, singer of the group Jane's Addiction.\"}, {\"role\": \"assistant\", \"content\": \"Lollapalooze is an annual musical festival held in Grant Park in Chicago, Illinois. It was started in 1991 as a farewell tour by Perry Farrell, singe of the group Jane's Addiction. The festival includes an array of musical genres including alternative rock, heavy metal, punk rock, hip hop, and electronic dance music. The festivals welcomes an estimated 400,000 people each year and sells out annually. Some notable headliners include: the Red Hot Chili Peppers, Chance the Rapper, Metallica, and Lady Gage. Lollapalooza is one of the largest and most iconic festivals in the world and a staple of Chicago.\"}]}\n",
- "{\"messages\": [{\"role\": \"user\", \"content\": \"Who gave the UN the land in NY to build their HQ\", \"context\": \"\"}, {\"role\": \"assistant\", \"content\": \"John D Rockerfeller\"}]}\n",
- "{\"messages\": [{\"role\": \"user\", \"content\": \"Who was John Moses Browning?\", \"context\": \"John Moses Browning (January 23, 1855 – November 26, 1926) was an American firearm designer who developed many varieties of military and civilian firearms, cartridges, and gun mechanisms – many of which are still in use around the world. He made his first firearm at age 13 in his father's gun shop and was awarded the first of his 128 firearm patents on October 7, 1879, at the age of 24. He is regarded as one of the most successful firearms designers of the 19th and 20th centuries and pioneered the development of modern repeating, semi-automatic, and automatic firearms.\\n\\nBrowning influenced nearly all categories of firearms design, especially the autoloading of ammunition. He invented, or made significant improvements to, single-shot, lever-action, and pump-action rifles and shotguns. He developed the first reliable and compact autoloading pistols by inventing the telescoping bolt, then integrating the bolt and barrel shroud into what is known as the pistol slide. Browning's telescoping bolt design is now found on nearly every modern semi-automatic pistol, as well as several modern fully automatic weapons. He also developed the first gas-operated firearm, the Colt–Browning Model 1895 machine gun – a system that surpassed mechanical recoil operation to become the standard for most high-power self-loading firearm designs worldwide. He also made significant contributions to automatic cannon development.\\n\\nBrowning's most successful designs include the M1911 pistol, the water-cooled M1917, the air-cooled M1919, and heavy M2 machine guns, the M1918 Browning Automatic Rifle, and the Browning Auto-5 – the first semi-automatic shotgun. Some of these arms are still manufactured, often with only minor changes in detail and cosmetics to those assembled by Browning or his licensees. The Browning-designed M1911 and Hi-Power are some of the most copied firearms in the world.\"}, {\"role\": \"assistant\", \"content\": \"John Moses Browning is one of the most well-known designer of modern firearms. He started building firearms in his father's shop at the age of 13, and was awarded his first patent when he was 24.\\n\\nHe designed the first reliable automatic pistol, and the first gas-operated firearm, as well inventing or improving single-shot, lever-action, and pump-action rifles and shotguns.\\n\\nToday, he is most well-known for the M1911 pistol, the Browning Automatic Rifle, and the Auto-5 shotgun, all of which are in still in current production in either their original design, or with minor changes. His M1911 and Hi-Power pistols designs are some of the most reproduced firearms in the world today.\"}]}\n",
- "{\"messages\": [{\"role\": \"user\", \"content\": \"Who is Thomas Jefferson?\", \"context\": \"Thomas Jefferson (April 13, 1743 – July 4, 1826) was an American statesman, diplomat, lawyer, architect, philosopher, and Founding Father who served as the third president of the United States from 1801 to 1809. Among the Committee of Five charged by the Second Continental Congress with authoring the Declaration of Independence, Jefferson was the Declaration's primary author. Following the American Revolutionary War and prior to becoming the nation's third president in 1801, Jefferson was the first United States secretary of state under George Washington and then the nation's second vice president under John Adams.\"}, {\"role\": \"assistant\", \"content\": \"Thomas Jefferson (April 13, 1743 – July 4, 1826) was an American statesman, diplomat, lawyer, architect, philosopher, and Founding Father who served as the third president of the United States from 1801 to 1809. Among the Committee of Five charged by the Second Continental Congress with authoring the Declaration of Independence, Jefferson was the Declaration's primary author. Following the American Revolutionary War and prior to becoming the nation's third president in 1801, Jefferson was the first United States secretary of state under George Washington and then the nation's second vice president under John Adams.\\n\\nAmong the nation's Founding Fathers, Jefferson is considered unmatched in his intellectual depth and breadth. His passionate writings and advocacy for human rights, including freedom of thought, speech, and religion, were a leading inspiration behind the American Revolution, which ultimately gave rise to the American Revolutionary War, American independence, and the United States Constitution. Jefferson's ideas were globally influential in shaping and inspiring the Age of Enlightenment, which proved transformational in the late 17th and 18th centuries. He was a leading proponent of democracy, republicanism, and individual rights, and produced formative documents and decisions at the state, national, and international levels.\\n\\nDuring the American Revolution, Jefferson represented Virginia in the Second Continental Congress in Philadelphia, which adopted the Declaration of Independence on July 4, 1776. As a Virginia legislator, he drafted a state law for religious freedom. He served as the second Governor of Virginia from 1779 to 1781, during the Revolutionary War. In 1785, Jefferson was appointed the United States Minister to France, and subsequently, the nation's first secretary of state under President George Washington from 1790 to 1793. Jefferson and James Madison organized the Democratic-Republican Party to oppose the Federalist Party during the formation of the First Party System. With Madison, he anonymously wrote the Kentucky and Virginia Resolutions in 1798 and 1799, which sought to strengthen states' rights by nullifying the federal Alien and Sedition Acts.\\n\\nJefferson and Federalist John Adams became friends as well as political rivals, serving in the Continental Congress and drafting the Declaration of Independence together. In the 1796 presidential election between the two, Jefferson came in second, which according to electoral procedure at the time, made him vice president to Adams. Jefferson challenged Adams again in 1800 and won the presidency. After his term in office, Jefferson eventually reconciled with Adams and they shared a correspondence that lasted 14 years. He and Adams both died on the same day, July 4, 1826, which was also the 50th anniversary of Declaration of Independence.\\n\\nAs president, Jefferson pursued the nation's shipping and trade interests against Barbary pirates and aggressive British trade policies. Starting in 1803, he promoted a western expansionist policy with the Louisiana Purchase, which doubled the nation's claimed land area. To make room for settlement, Jefferson began the process of Indian tribal removal from the newly acquired territory. As a result of peace negotiations with France, his administration reduced military forces. He was re-elected in 1804, but his second term was beset with difficulties at home, including the trial of former vice president Aaron Burr. In 1807, American foreign trade was diminished when Jefferson implemented the Embargo Act in response to British threats to U.S. shipping. The same year, Jefferson signed the Act Prohibiting Importation of Slaves.\\n\\nJefferson was a plantation owner, lawyer, and politician, and mastered many disciplines including surveying, mathematics, horticulture, and mechanics. He was also an architect in the Palladian tradition. Jefferson's keen interest in religion and philosophy led to his appointment as president of the American Philosophical Society. He largely shunned organized religion but was influenced by Christianity, Epicureanism, and deism. Jefferson rejected fundamental Christianity, denying Christ's divinity. A philologist, Jefferson knew several languages. He was a prolific letter writer and corresponded with many prominent people, including Edward Carrington, John Taylor of Caroline, and James Madison. In 1785, Jefferson authored Notes on the State of Virginia, considered perhaps the most important American book published before 1800. Jefferson championed the ideals, values, and teachings of the Enlightenment.\\n\\nSince the 1790s, Jefferson was rumored to have had children by his sister-in-law and slave Sally Hemings, leading to what is known as the Jefferson-Hemings controversy. A 1998 DNA test concluded that one of Sally Hemings's children, Eston Hemings, was of the Jefferson male line. According to scholarly consensus, based on documentary and statistical evaluation, as well as oral history, Jefferson probably fathered at least six children with Hemings, including four that survived to adulthood.\\n\\nAfter retiring from public office, Jefferson founded the University of Virginia. Presidential scholars and historians generally praise Jefferson's public achievements, including his advocacy of religious freedom and tolerance in Virginia, his peaceful acquisition of the Louisiana Territory from France without war or controversy, and his ambitious and successful Lewis and Clark Expedition. Some modern historians are critical of Jefferson's personal involvement with slavery. Jefferson is consistently ranked among the top ten presidents of American history.\"}]}\n",
- "{\"messages\": [{\"role\": \"user\", \"content\": \"Who was Kyle Van Zyl playing against when he scored 36 of hisa teams 61 points?\", \"context\": \"Van Zyl joined the Eastern Province Kings Academy, where he played for the Eastern Province U19 side in the 2010 Under-19 Provincial Championship. He was a key player for the Eastern Province U21 side in the 2012 Under-21 Provincial Championship, scoring 71 points in eight appearances. Van Zyl was under the Top SARU Performers, scoring the most tries at 6 in the 2012 Provincial Under 21 in the Rugby Junior Provincials.\\n\\nThis included a record and a remarkable personal haul in their opening match, when he scored 36 of his team's points in a 61–3 victory over Boland U21, consisting of four tries and eight conversions and was awarded Man of the Match.\"}, {\"role\": \"assistant\", \"content\": \"Kyle Van Zyl was playing against Boland U21 when he scored 36 points, leading his team to victory in a 61-3 win.\"}]}\n"
- ]
- }
- ],
- "source": [
- "# @markdown Let's have a look at the top 10 examples in the train split. Note that the messages with `user` role has `context` field as well, and some of them are empty strings.\n",
- "\n",
- "! head -n 10 $LOCAL_TRAIN_SPLIT_PREPROCESSED"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "lJwXTwhVQE0X"
- },
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "('<|begin_of_text|><|start_header_id|>user<|end_header_id|>\\n'\n",
- " '\\n'\n",
- " 'Hello, how are you? Context: This is a test '\n",
- " 'context.<|eot_id|><|start_header_id|>assistant<|end_header_id|>\\n'\n",
- " '\\n'\n",
- " \"I'm doing well, thank \"\n",
- " 'you!<|eot_id|><|start_header_id|>user<|end_header_id|>\\n'\n",
- " '\\n'\n",
- " 'Another question without context.<|eot_id|>')\n"
- ]
- }
- ],
- "source": [
- "# @title Prepare our own chat_template\n",
- "\n",
- "# @markdown Now we will create a `chat_template` for our use-case, following Llama 3.1's `chat_template`.\n",
- "# @markdown We conditionally add context when the message contains the `context` key and it is not empty.\n",
- "\n",
- "# @markdown ```\n",
- "# @markdown chat_template_string = r\"\"\"{{- bos_token }}\n",
- "# @markdown\n",
- "# @markdown {% for message in messages %}\n",
- "# @markdown {{- '<|start_header_id|>' + message.role + '<|end_header_id|>\\n\\n' + message.content | trim }}\n",
- "# @markdown {% if message.context and message.context | length > 0 %}\n",
- "# @markdown {{- ' Context: ' + message.context }}\n",
- "# @markdown {% endif %}\n",
- "# @markdown {{- '<|eot_id|>' }}\n",
- "# @markdown {% endfor %}\n",
- "# @markdown\n",
- "# @markdown {% if add_generation_prompt %}\n",
- "# @markdown {{- '<|start_header_id|>assistant<|end_header_id|>\\n\\n' }}\n",
- "# @markdown {% endif %}\n",
- "# @markdown \"\"\"\n",
- "# @markdown ```\n",
- "\n",
- "# @markdown We use [raw string](https://docs.python.org/3/reference/lexical_analysis.html#string-and-bytes-literals)\n",
- "# @markdown so that we can easily save the `chat_template` as a json string.\n",
- "\n",
- "chat_template_string = r\"\"\"{{- bos_token }}\n",
- "\n",
- "{% for message in messages %}\n",
- " {{- '<|start_header_id|>' + message.role + '<|end_header_id|>\\n\\n' + message.content | trim }}\n",
- " {% if message.context and message.context | length > 0 %}\n",
- " {{- ' Context: ' + message.context }}\n",
- " {% endif %}\n",
- " {{- '<|eot_id|>' }}\n",
- "{% endfor %}\n",
- "\n",
- "{% if add_generation_prompt %}\n",
- " {{- '<|start_header_id|>assistant<|end_header_id|>\\n\\n' }}\n",
- "{% endif %}\n",
- "\"\"\"\n",
- "\n",
- "# @markdown The above `chat_template` can be translated to Python as:\n",
- "# @markdown ```\n",
- "# @markdown def render_template(messages, add_generation_prompt=False, bos_token=\"\"):\n",
- "# @markdown output = bos_token\n",
- "# @markdown\n",
- "# @markdown for message in messages:\n",
- "# @markdown output += f\"<|start_header_id|>{message['role']}<|end_header_id|>\\n\\n{message['content'].strip()}\"\n",
- "# @markdown if 'context' in message and message['context']:\n",
- "# @markdown output += f\" Context: {message['context']}\"\n",
- "# @markdown output += \"<|eot_id|>\"\n",
- "# @markdown\n",
- "# @markdown if add_generation_prompt:\n",
- "# @markdown output += \"<|start_header_id|>assistant<|end_header_id|>\\n\\n\"\n",
- "# @markdown\n",
- "# @markdown return output\n",
- "# @markdown ```\n",
- "\n",
- "# @markdown Run this cell to show an example output of the template given the\n",
- "# @markdown below `messages` and `bos_token=\"<|begin_of_text|>\"`.\n",
- "# @markdown ```\n",
- "# @markdown messages = [\n",
- "# @markdown {\"role\": \"user\", \"content\": \"Hello, how are you?\", \"context\": \"This is a test context.\"},\n",
- "# @markdown {\"role\": \"assistant\", \"content\": \"I'm doing well, thank you!\"},\n",
- "# @markdown {\"role\": \"user\", \"content\": \"Another question without context.\", \"context\": \"\"}\n",
- "# @markdown ]\n",
- "# @markdown ```\n",
- "# @markdown\n",
- "# @markdown This will be the string the model gets under the hood.\n",
- "\n",
- "\n",
- "def render_template(messages, add_generation_prompt=False, bos_token=\"\"):\n",
- " output = bos_token\n",
- "\n",
- " for message in messages:\n",
- " output += f\"<|start_header_id|>{message['role']}<|end_header_id|>\\n\\n{message['content'].strip()}\"\n",
- " if \"context\" in message and message[\"context\"]:\n",
- " output += f\" Context: {message['context']}\"\n",
- " output += \"<|eot_id|>\"\n",
- "\n",
- " if add_generation_prompt:\n",
- " output += \"<|start_header_id|>assistant<|end_header_id|>\\n\\n\"\n",
- "\n",
- " return output\n",
- "\n",
- "\n",
- "messages = [\n",
- " {\n",
- " \"role\": \"user\",\n",
- " \"content\": \"Hello, how are you?\",\n",
- " \"context\": \"This is a test context.\",\n",
- " },\n",
- " {\"role\": \"assistant\", \"content\": \"I'm doing well, thank you!\"},\n",
- " {\"role\": \"user\", \"content\": \"Another question without context.\", \"context\": \"\"},\n",
- "]\n",
- "\n",
- "rendered_text = render_template(messages, bos_token=\"<|begin_of_text|>\")\n",
- "pprint.pprint(rendered_text, width=80)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "6WIFMjMbE5Z8"
- },
- "outputs": [],
- "source": [
- "# @title Prepare a Vertex Model Garden dataset template\n",
- "\n",
- "# @markdown Now we will create a Vertex Model Garden [dataset template](https://github.com/GoogleCloudPlatform/vertex-ai-samples/blob/6fabc23db697b6d8b20113ecf006c454d09b3421/community-content/vertex_model_garden/model_oss/peft/train/vmg/templates/README.md)\n",
- "# @markdown file, `template.json`.\n",
- "# @markdown It is a json file that contains the `chat_template` to format the\n",
- "# @markdown input. There are two important additional fields,\n",
- "# @markdown `instruction_separator` and `response_separator`.\n",
- "# @markdown - `instruction_separator`: A string used to indicate the start of the\n",
- "# @markdown instructions.\n",
- "# @markdown - `response_separator`: A string used to indicate the start of the\n",
- "# @markdown response.\n",
- "\n",
- "# @markdown The `instruction_separator` and `response_separator` fields are\n",
- "# @markdown critical for structuring language model input, particularly in\n",
- "# @markdown chat contexts, and play a key role in masking during training. They\n",
- "# @markdown not only delimit conversation turns but also enable the model to\n",
- "# @markdown focus specifically on learning the response part of the data.\n",
- "\n",
- "# @markdown ```\n",
- "# @markdown template = {\n",
- "# @markdown \"description\": \"Template used by Llama 3.1, accepting databricks dolly dataset.\",\n",
- "# @markdown \"chat_template\": chat_template_string,\n",
- "# @markdown \"instruction_separator\": \"<|start_header_id|>user<|end_header_id|>\\n\\n\",\n",
- "# @markdown \"response_separator\": \"<|start_header_id|>assistant<|end_header_id|>\\n\\n\"\n",
- "# @markdown }\n",
- "# @markdown ```\n",
- "\n",
- "template_data = {\n",
- " \"description\": \"Template used by Llama 3.1, accepting databricks dolly dataset.\",\n",
- " \"chat_template\": chat_template_string,\n",
- " \"instruction_separator\": \"<|start_header_id|>user<|end_header_id|>\\n\\n\",\n",
- " \"response_separator\": \"<|start_header_id|>assistant<|end_header_id|>\\n\\n\",\n",
- "}\n",
- "\n",
- "template_filename = \"template.json\"\n",
- "local_template = os.path.join(os.getcwd(), template_filename)\n",
- "template_path = os.path.join(BUCKET_URI, template_filename)\n",
- "template_string = json.dumps(template_data)\n",
- "with open(local_template, \"w\") as f:\n",
- " f.write(template_string)\n",
- "\n",
- "! gsutil cp $local_template $template_path\n",
- "print(f\"Template is uploaded to {template_path}.\")"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "KwAW99YZHTdy"
- },
- "outputs": [],
- "source": [
- "# @title Set dataset\n",
- "\n",
- "# @markdown The template and dataset prepared above are used by default when the\n",
- "# @markdown fields below are left as blanks. Your dataset settings will be\n",
- "# @markdown printed when the cell runs.\n",
- "\n",
- "# @markdown **[Optional]** You can optionally customize the fields below to\n",
- "# @markdown train with your own dataset. Otherwise, leave the field below as\n",
- "# @markdown blanks.\n",
- "\n",
- "\n",
- "# @markdown Template for formatting language model training data. Must be a\n",
- "# @markdown Google Cloud Storage URI to a JSON file or a filename under\n",
- "# @markdown [templates](https://github.com/GoogleCloudPlatform/vertex-ai-samples/tree/6fabc23db697b6d8b20113ecf006c454d09b3421/community-content/vertex_model_garden/model_oss/peft/train/vmg/templates)\n",
- "# @markdown folder, without `.json` extension.\n",
- "template = \"\" # @param {type:\"string\"}\n",
- "\n",
- "# Hugging Face dataset name or gs:// URI to a custom JSONL dataset.\n",
- "# @markdown The Google Cloud Storage URI to the training dataset or the dataset name in huggingface.\n",
- "train_dataset_name = \"\" # @param {type:\"string\"}\n",
- "# @markdown The train split name for the Hugging Face dataset.\n",
- "train_split_name = \"\" # @param {type:\"string\"}\n",
- "# @markdown The Google Cloud Storage URI to the validation dataset or the dataset name in huggingface.\n",
- "eval_dataset_name = \"\" # @param {type:\"string\"}\n",
- "# @markdown The validation split name for the Hugging Face dataset.\n",
- "eval_split_name = \"\" # @param {type:\"string\"}\n",
- "\n",
- "# @markdown Name of the dataset column containing training text input.\n",
- "instruct_column_in_dataset = \"\" # @param {type:\"string\"}\n",
- "\n",
- "# Set default values.\n",
- "if not template:\n",
- " template = template_path\n",
- "if not train_dataset_name:\n",
- " train_dataset_name = f\"{BUCKET_URI}/{PREPROCESSED_TRAIN_FILENAME}\"\n",
- "if not train_split_name:\n",
- " train_split_name = \"train\"\n",
- "if not eval_dataset_name:\n",
- " eval_dataset_name = f\"{BUCKET_URI}/{PREPROCESSED_VALIDATION_FILENAME}\"\n",
- "if not eval_split_name:\n",
- " eval_split_name = \"train\"\n",
- "if not instruct_column_in_dataset:\n",
- " instruct_column_in_dataset = \"messages\"\n",
- "\n",
- "print(f\"Using template: {template}\")\n",
- "print(f\"Using train dataset: {train_dataset_name}\")\n",
- "print(f\"Using train split: {train_split_name}\")\n",
- "print(f\"Using eval dataset: {eval_dataset_name}\")\n",
- "print(f\"Using eval split: {eval_split_name}\")\n",
- "print(f\"Instruct column: {instruct_column_in_dataset}\")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "cb56d402e84a"
- },
- "source": [
- "## Finetune with HuggingFace PEFT"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "_mNcaofv4zpv"
- },
- "outputs": [],
- "source": [
- "# @title Validate Dataset with Template\n",
- "\n",
- "# @markdown This section validates the train and eval datasets with the template\n",
- "# @markdown before starting the fine tuning job.\n",
- "\n",
- "import transformers\n",
- "\n",
- "dataset_validation_util = importlib.import_module(\n",
- " \"vertex-ai-samples.community-content.vertex_model_garden.model_oss.notebook_util.dataset_validation_util\"\n",
- ")\n",
- "\n",
- "if dataset_validation_util.is_gcs_path(pretrained_model_id):\n",
- " # Download tokenizer.\n",
- " ! mkdir tokenizer\n",
- " ! gsutil cp {pretrained_model_id}/tokenizer.json ./tokenizer\n",
- " ! gsutil cp {pretrained_model_id}/config.json ./tokenizer\n",
- " tokenizer_path = \"./tokenizer\"\n",
- " access_token = \"\"\n",
- "else:\n",
- " tokenizer_path = pretrained_model_id\n",
- " access_token = HF_TOKEN\n",
- "\n",
- "tokenizer = transformers.AutoTokenizer.from_pretrained(\n",
- " tokenizer_path,\n",
- " trust_remote_code=False,\n",
- " use_fast=True,\n",
- " token=access_token,\n",
- ")\n",
- "\n",
- "# Validate the train dataset.\n",
- "dataset_validation_util.validate_dataset_with_template(\n",
- " dataset_name=train_dataset_name,\n",
- " split=train_split_name,\n",
- " input_column=instruct_column_in_dataset,\n",
- " template=template,\n",
- " use_multiprocessing=False,\n",
- " tokenizer=tokenizer,\n",
- ")\n",
- "\n",
- "# Validate the eval dataset.\n",
- "dataset_validation_util.validate_dataset_with_template(\n",
- " dataset_name=eval_dataset_name,\n",
- " split=eval_split_name,\n",
- " input_column=instruct_column_in_dataset,\n",
- " template=template,\n",
- " use_multiprocessing=False,\n",
- " tokenizer=tokenizer,\n",
- ")"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "ivVGS9dHXPOz"
- },
- "outputs": [],
- "source": [
- "# @title Finetune\n",
- "\n",
- "# @markdown This section demonstrates how to finetune the Llama 3.1 model on Vertex AI.\n",
- "# @markdown It uses the Vertex AI SDK to create and run the custom training jobs.\n",
- "\n",
- "# @markdown The training job takes approximately between 10 to 20 mins to set-up. Once done,\n",
- "# @markdown the training job is expected to take around 3 hours with the default configurations.\n",
- "# @markdown To find the training time, throughput, and memory usage of your training job,\n",
- "# @markdown you can go to the training logs and check the log line of the last training epoch.\n",
- "\n",
- "# @markdown **Note**:\n",
- "# @markdown 1. We recommend setting `finetuning_precision_mode` to `float16`.\n",
- "# @markdown 1. If `max_steps>0`, it takes precedence over `epochs`. One can set a small `max_steps`\n",
- "# @markdown value to quickly check the pipeline.\n",
- "\n",
- "# @markdown Acceletor type to use for training.\n",
- "# fmt: off\n",
- "training_accelerator_type = \"NVIDIA_H100_80GB\" # @param [\"NVIDIA_A100_80GB\", \"NVIDIA_H100_80GB\"]\n",
- "# fmt: on\n",
- "\n",
- "# The pre-built training docker image.\n",
- "if training_accelerator_type == \"NVIDIA_A100_80GB\":\n",
- " repo = \"us-docker.pkg.dev/vertex-ai-restricted\"\n",
- " is_restricted_image = True\n",
- " is_dynamic_workload_scheduler = False\n",
- " dws_kwargs = {}\n",
- "else:\n",
- " repo = \"us-docker.pkg.dev/vertex-ai\"\n",
- " is_restricted_image = False\n",
- " is_dynamic_workload_scheduler = True\n",
- " dws_kwargs = {\n",
- " \"max_wait_duration\": 1800, # 30 minutes\n",
- " \"scheduling_strategy\": gca_custom_job_compat.Scheduling.Strategy.FLEX_START,\n",
- " }\n",
- "\n",
- "TRAIN_DOCKER_URI = (\n",
- " f\"{repo}/vertex-vision-model-garden-dockers/pytorch-peft-train:stable_20250213\"\n",
- ")\n",
- "\n",
- "# Worker pool spec.\n",
- "if training_accelerator_type == \"NVIDIA_A100_80GB\":\n",
- " per_node_accelerator_count = 8\n",
- " training_machine_type = \"a2-ultragpu-8g\"\n",
- " boot_disk_size_gb = 500\n",
- "elif training_accelerator_type == \"NVIDIA_H100_80GB\":\n",
- " per_node_accelerator_count = 8\n",
- " training_machine_type = \"a3-highgpu-8g\"\n",
- " boot_disk_size_gb = 2000\n",
- "else:\n",
- " raise ValueError(\n",
- " f\"Recommended machine settings not found for: {training_accelerator_type}. To use another accelerator type, edit this code block to pass in an appropriate `training_machine_type`, `training_accelerator_type`, and `per_node_accelerator_count` by clicking `Show Code` and then modifying the code.\"\n",
- " )\n",
- "\n",
- "# @markdown The number of nodes to use for this worker pool in distributed training.\n",
- "replica_count = 1 # @param{type:\"integer\"}\n",
- "\n",
- "# Set config file.\n",
- "if replica_count == 1:\n",
- " config_file = \"vertex_vision_model_garden_peft/llama_fsdp_8gpu.yaml\"\n",
- "elif replica_count <= 4:\n",
- " config_file = (\n",
- " \"vertex_vision_model_garden_peft/\"\n",
- " f\"llama_hsdp_{replica_count * per_node_accelerator_count}gpu.yaml\"\n",
- " )\n",
- "else:\n",
- " raise ValueError(\n",
- " f\"Recommended config settings not found for replica_count: {replica_count}.\"\n",
- " )\n",
- "\n",
- "# @markdown Batch size for finetuning.\n",
- "per_device_train_batch_size = 2 # @param{type:\"integer\"}\n",
- "# @markdown Number of updates steps to accumulate the gradients for, before performing a backward/update pass.\n",
- "gradient_accumulation_steps = 1 # @param{type:\"integer\"}\n",
- "# @markdown Maximum sequence length.\n",
- "max_seq_length = 4096 # @param{type:\"integer\"}\n",
- "# @markdown Setting a positive `max_steps` here will override `num_epochs`.\n",
- "max_steps = -1 # @param{type:\"integer\"}\n",
- "num_epochs = 2.0 # @param{type:\"number\"}\n",
- "# @markdown Precision mode for finetuning.\n",
- "finetuning_precision_mode = \"float16\" # @param [\"4bit\", \"8bit\", \"float16\"]\n",
- "# @markdown Learning rate.\n",
- "learning_rate = 2e-5 # @param{type:\"number\"}\n",
- "# @markdown The scheduler type to use.\n",
- "lr_scheduler_type = \"cosine\" # @param{type:\"string\"}\n",
- "# @markdown LoRA parameters.\n",
- "lora_rank = 16 # @param{type:\"integer\"}\n",
- "lora_alpha = 32 # @param{type:\"integer\"}\n",
- "lora_dropout = 0.05 # @param{type:\"number\"}\n",
- "# Activates gradient checkpointing for the current model (may be referred to as activation checkpointing or checkpoint activations in other frameworks).\n",
- "gradient_checkpointing = True\n",
- "# Attention implementation to use in the model.\n",
- "attn_implementation = \"flash_attention_2\"\n",
- "# The optimizer for which to schedule the learning rate.\n",
- "optimizer = \"adamw_torch\"\n",
- "# Define the proportion of training to be dedicated to a linear warmup where learning rate gradually increases.\n",
- "warmup_ratio = \"0.01\"\n",
- "# The list or string of integrations to report the results and logs to.\n",
- "report_to = \"tensorboard\"\n",
- "# Number of updates steps before two checkpoint saves.\n",
- "save_steps = 100\n",
- "# Number of update steps between two logs.\n",
- "logging_steps = save_steps\n",
- "\n",
- "# @markdown Evaluation metrics to compute. Supported eval metrics: `loss`,\n",
- "# @markdown `perplexity`, `bleu`, `google_bleu`, `rouge1`, `rouge2`, `rougeL`,\n",
- "# @markdown `rougeLsum`.\n",
- "eval_metric_name = \"loss,perplexity,bleu\" # @param{type:\"string\"}\n",
- "# @markdown Metric to use for best model selection. The training job keeps the\n",
- "# @markdown model with the best evaluation result based on this metric, and it\n",
- "# @markdown is exported at the end of training.\n",
- "metric_for_best_model = \"perplexity\" # @param{type:\"string\"}\n",
- "\n",
- "# @markdown Note that the metrics above are computed on the validation dataset\n",
- "# @markdown to monitor training progress, and can be different from the metrics\n",
- "# @markdown used for the CoQA task.\n",
- "\n",
- "common_util.check_quota(\n",
- " project_id=PROJECT_ID,\n",
- " region=REGION,\n",
- " accelerator_type=training_accelerator_type,\n",
- " accelerator_count=per_node_accelerator_count * replica_count,\n",
- " is_for_training=True,\n",
- " is_restricted_image=is_restricted_image,\n",
- " is_dynamic_workload_scheduler=is_dynamic_workload_scheduler,\n",
- ")\n",
- "\n",
- "job_name = common_util.get_job_name_with_datetime(\"llama3_1-lora-train\")\n",
- "\n",
- "base_output_dir = os.path.join(STAGING_BUCKET, job_name)\n",
- "# Create a GCS folder to store the LORA adapter.\n",
- "lora_output_dir = os.path.join(base_output_dir, \"adapter\")\n",
- "# Create a GCS folder to store the finetuned LORA adapter.\n",
- "final_checkpoint = os.path.join(lora_output_dir, \"node-0\", \"checkpoint-final\")\n",
- "\n",
- "# Add labels for the finetuning job.\n",
- "labels = {\n",
- " \"mg-source\": \"notebook\",\n",
- " \"mg-notebook-name\": \"model_garden_finetuning_tutorial.ipynb\".split(\".\")[0],\n",
- "}\n",
- "\n",
- "labels[\"mg-tune\"] = \"publishers-meta-models-llama3-1\"\n",
- "versioned_model_id = base_model_id.split(\"/\")[1].lower().replace(\".\", \"-\")\n",
- "labels[\"versioned-mg-tune\"] = f\"{labels['mg-tune']}-{versioned_model_id}\"\n",
- "\n",
- "eval_args = [\n",
- " f\"--eval_dataset={eval_dataset_name}\",\n",
- " f\"--eval_column={instruct_column_in_dataset}\",\n",
- " f\"--eval_template={template}\",\n",
- " f\"--eval_split={eval_split_name}\",\n",
- " f\"--eval_steps={save_steps}\",\n",
- " f\"--eval_metric_name={eval_metric_name}\",\n",
- " f\"--metric_for_best_model={metric_for_best_model}\",\n",
- "]\n",
- "\n",
- "train_job_args = [\n",
- " f\"--config_file={config_file}\",\n",
- " \"--task=instruct-lora\",\n",
- " \"--input_masking=True\",\n",
- " f\"--pretrained_model_name_or_path={pretrained_model_id}\",\n",
- " f\"--train_dataset={train_dataset_name}\",\n",
- " f\"--train_split={train_split_name}\",\n",
- " f\"--train_column={instruct_column_in_dataset}\",\n",
- " f\"--output_dir={lora_output_dir}\",\n",
- " f\"--per_device_train_batch_size={per_device_train_batch_size}\",\n",
- " f\"--gradient_accumulation_steps={gradient_accumulation_steps}\",\n",
- " f\"--lora_rank={lora_rank}\",\n",
- " f\"--lora_alpha={lora_alpha}\",\n",
- " f\"--lora_dropout={lora_dropout}\",\n",
- " f\"--max_steps={max_steps}\",\n",
- " f\"--max_seq_length={max_seq_length}\",\n",
- " f\"--learning_rate={learning_rate}\",\n",
- " f\"--lr_scheduler_type={lr_scheduler_type}\",\n",
- " f\"--precision_mode={finetuning_precision_mode}\",\n",
- " f\"--gradient_checkpointing={gradient_checkpointing}\",\n",
- " f\"--num_train_epochs={num_epochs}\",\n",
- " f\"--attn_implementation={attn_implementation}\",\n",
- " f\"--optimizer={optimizer}\",\n",
- " f\"--warmup_ratio={warmup_ratio}\",\n",
- " f\"--report_to={report_to}\",\n",
- " f\"--logging_output_dir={base_output_dir}\",\n",
- " f\"--save_steps={save_steps}\",\n",
- " f\"--logging_steps={logging_steps}\",\n",
- " f\"--train_template={template}\",\n",
- " f\"--huggingface_access_token={HF_TOKEN}\",\n",
- "] + eval_args\n",
- "\n",
- "# Pass training arguments and launch job.\n",
- "train_job = aiplatform.CustomContainerTrainingJob(\n",
- " display_name=job_name,\n",
- " container_uri=TRAIN_DOCKER_URI,\n",
- " labels=labels,\n",
- ")\n",
- "\n",
- "print(\"Running training job with args:\")\n",
- "print(\" \\\\\\n\".join(train_job_args))\n",
- "train_job.run(\n",
- " args=train_job_args,\n",
- " replica_count=replica_count,\n",
- " machine_type=training_machine_type,\n",
- " accelerator_type=training_accelerator_type,\n",
- " accelerator_count=per_node_accelerator_count,\n",
- " boot_disk_size_gb=boot_disk_size_gb,\n",
- " service_account=SERVICE_ACCOUNT,\n",
- " base_output_dir=base_output_dir,\n",
- " sync=False, # Non-blocking call to run.\n",
- " **dws_kwargs,\n",
- ")\n",
- "\n",
- "# Wait until resource has been created.\n",
- "train_job.wait_for_resource_creation()\n",
- "\n",
- "print(\"LoRA adapter will be saved in:\", lora_output_dir)\n",
- "print(\"Final checkpoint will be saved in:\", final_checkpoint)\n",
- "\n",
- "# @markdown Click \"Show Code\" to see more details."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "x93f7805YwJg"
- },
- "outputs": [],
- "source": [
- "# @title Run TensorBoard\n",
- "# @markdown This section shows how to launch TensorBoard in a [Cloud Shell](https://cloud.google.com/shell/docs).\n",
- "# @markdown 1. Click the Cloud Shell icon() on the top right to open the Cloud Shell.\n",
- "# @markdown 2. Copy the `tensorboard` command shown below by running this cell.\n",
- "# @markdown 3. Paste and run the command in the Cloud Shell to launch TensorBoard.\n",
- "# @markdown 4. Once the command runs (You may have to click `Authorize` if prompted), click the link starting with `http://localhost`.\n",
- "\n",
- "# @markdown Note: You may need to wait around 10 minutes after the job starts in order for the TensorBoard logs to be written to the GCS bucket.\n",
- "print(f\"Command to copy: tensorboard --logdir {base_output_dir}/logs\")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "frVznQZJrB1X"
- },
- "source": [
- "The charts below are from an example TensorBoard run. There are some things to note.\n",
- "\n",
- "- Both training and validation loss are decreasing, which is a good sign. It indicates the model is learning and improving its performance on both training and validation data. The consistent decrease in both losses suggests the model is still in a learning phase and could potentially benefit from further training.\n",
- "- The [BLEU](https://en.wikipedia.org/wiki/BLEU) (Bilingual Evaluation Understudy) score is a metric used to evaluate the quality of machine-generated text. It measures the similarity between a machine-generated text and human reference texts. The upward trend in BLEU score aligns with the decreasing loss, reinforcing the conclusion that the model's performance is getting better.\n",
- "- The [perplexity](https://en.wikipedia.org/wiki/Perplexity) score, calculated from cross-entropy, measures the uncertainty of a probabilistic model, particularly in evaluating how well it predicts sequences. The perplexity is decreasing. This is another good sign, as lower perplexity indicates the model is becoming more confident in its predictions.\n",
- "\n",
- "\n"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "bPgd-zmX5RsA"
- },
- "source": [
- "## Evaluate the finetuned model with lm-eval-harness"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "eOjEQqCt9AW7"
- },
- "outputs": [],
- "source": [
- "# Run evaluation after finetuning.\n",
- "\n",
- "# @markdown Now we run evaluation with the newly finetuned model on the same\n",
- "# @markdown CoQA task to compare with the baseline results.\n",
- "\n",
- "if train_job.end_time is None:\n",
- " print(\"Waiting for the training job to finish...\")\n",
- " train_job.wait()\n",
- " print(\"The training job has finished.\")\n",
- "\n",
- "# Baseline evaluation parameters.\n",
- "eval_accelerator_type = \"NVIDIA_L4\" # @param [\"NVIDIA_L4\", \"NVIDIA_H100_80GB\"]\n",
- "\n",
- "if eval_accelerator_type == \"NVIDIA_L4\":\n",
- " eval_machine_type = \"g2-standard-96\"\n",
- " eval_accelerator_count = 8\n",
- "elif eval_accelerator_type == \"NVIDIA_H100_80GB\":\n",
- " eval_machine_type = \"a3-highgpu-4g\"\n",
- " eval_accelerator_count = 4\n",
- "else:\n",
- " raise ValueError(f\"Recommended GPU setting not found for: {eval_accelerator_type}.\")\n",
- "\n",
- "common_util.check_quota(\n",
- " project_id=PROJECT_ID,\n",
- " region=REGION,\n",
- " accelerator_type=eval_accelerator_type,\n",
- " accelerator_count=eval_accelerator_count,\n",
- " is_for_training=False,\n",
- ")\n",
- "\n",
- "gpu_memory_utilization = 0.95\n",
- "max_model_len = 8192 # Maximum context length.\n",
- "lora_output_dir = os.path.join(lora_output_dir, \"node-0\")\n",
- "\n",
- "eval_output_dir = run_lm_evaluation_harness(\n",
- " job_name=common_util.get_job_name_with_datetime(prefix=\"llama3_1-vllm-eval\"),\n",
- " base_model_id=pretrained_model_id,\n",
- " machine_type=eval_machine_type,\n",
- " accelerator_type=eval_accelerator_type,\n",
- " accelerator_count=eval_accelerator_count,\n",
- " gpu_memory_utilization=gpu_memory_utilization,\n",
- " max_model_len=max_model_len,\n",
- " enable_lora=True,\n",
- " lora_path=lora_output_dir.replace(\n",
- " \"gs://\", \"/gcs/\", 1\n",
- " ), # Use GCS Fuse to access GCS.\n",
- ")\n",
- "\n",
- "\n",
- "# @markdown Expected evaluation results:\n",
- "# @markdown > | alias | exact_match | exact_match_stderr | f1 | f1_stderr |\n",
- "# @markdown | --- | --- | --- | --- | --- |\n",
- "# @markdown | coqa | 0.3213 | 0.0197 | 0.4660 | 0.0187 |\n",
- "\n",
- "# @markdown The F1 score improves from the baseline score of 0.1872 to 0.4660.\n",
- "\n",
- "print_coqa_result(eval_output_dir)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "7Ht2xPhj5O2x"
- },
- "source": [
- "## Deploy with vLLM on GPUs"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "qmHW6m8xG_4U"
- },
- "outputs": [],
- "source": [
- "# @title Deploy\n",
- "# @markdown This section deploys the model on an Endpoint. It takes 15 minutes to 1 hour to finish.\n",
- "# @markdown [vLLM](https://docs.vllm.ai/en/latest/index.html) is used to serve the model.\n",
- "\n",
- "# The pre-built serving docker image for vLLM.\n",
- "VLLM_DOCKER_URI = \"us-docker.pkg.dev/vertex-ai/vertex-vision-model-garden-dockers/pytorch-vllm-serve:20250114_0916_RC00_maas\"\n",
- "\n",
- "serve_accelerator_type = \"NVIDIA_L4\" # @param [\"NVIDIA_H100_80GB\", \"NVIDIA_L4\"]\n",
- "\n",
- "# @markdown Find Vertex AI prediction supported accelerators and regions at https://cloud.google.com/vertex-ai/docs/predictions/configure-compute.\n",
- "if serve_accelerator_type == \"NVIDIA_L4\":\n",
- " serve_machine_type = \"g2-standard-96\"\n",
- " serve_accelerator_count = 8\n",
- "elif serve_accelerator_type == \"NVIDIA_H100_80GB\":\n",
- " serve_machine_type = \"a3-highgpu-4g\"\n",
- " serve_accelerator_count = 4\n",
- "else:\n",
- " raise ValueError(\n",
- " f\"Recommended GPU setting not found for: {serve_accelerator_type}.\"\n",
- " )\n",
- "\n",
- "common_util.check_quota(\n",
- " project_id=PROJECT_ID,\n",
- " region=REGION,\n",
- " accelerator_type=serve_accelerator_type,\n",
- " accelerator_count=serve_accelerator_count,\n",
- " is_for_training=False,\n",
- ")\n",
- "\n",
- "# @markdown Set `use_dedicated_endpoint` to False if you don't want to use [dedicated endpoint](https://cloud.google.com/vertex-ai/docs/general/deployment#create-dedicated-endpoint).\n",
- "use_dedicated_endpoint = True # @param {type:\"boolean\"}\n",
- "\n",
- "gpu_memory_utilization = 0.85\n",
- "max_model_len = 8192 # Maximum context length.\n",
- "\n",
- "# Ensure max_model_len does not exceed the limit.\n",
- "if max_model_len > 8192:\n",
- " raise ValueError(\"max_model_len cannot exceed 8192\")\n",
- "\n",
- "\n",
- "def deploy_model_vllm(\n",
- " model_name: str,\n",
- " model_id: str,\n",
- " service_account: str,\n",
- " base_model_id: str = None,\n",
- " machine_type: str = \"g2-standard-8\",\n",
- " accelerator_type: str = \"NVIDIA_L4\",\n",
- " accelerator_count: int = 1,\n",
- " gpu_memory_utilization: float = 0.9,\n",
- " max_model_len: int = 4096,\n",
- " dtype: str = \"auto\",\n",
- " enable_trust_remote_code: bool = False,\n",
- " enforce_eager: bool = False,\n",
- " enable_lora: bool = False,\n",
- " enable_chunked_prefill: bool = False,\n",
- " enable_prefix_cache: bool = False,\n",
- " host_prefix_kv_cache_utilization_target: float = 0.0,\n",
- " max_loras: int = 1,\n",
- " max_cpu_loras: int = 8,\n",
- " use_dedicated_endpoint: bool = False,\n",
- " max_num_seqs: int = 256,\n",
- " model_type: str = None,\n",
- ") -> Tuple[aiplatform.Model, aiplatform.Endpoint]:\n",
- " \"\"\"Deploys trained models with vLLM into Vertex AI.\"\"\"\n",
- " endpoint = aiplatform.Endpoint.create(\n",
- " display_name=f\"{model_name}-endpoint\",\n",
- " dedicated_endpoint_enabled=use_dedicated_endpoint,\n",
- " )\n",
- "\n",
- " if not base_model_id:\n",
- " base_model_id = model_id\n",
- "\n",
- " # See https://docs.vllm.ai/en/latest/models/engine_args.html for a list of possible arguments with descriptions.\n",
- " vllm_args = [\n",
- " \"python\",\n",
- " \"-m\",\n",
- " \"vllm.entrypoints.api_server\",\n",
- " \"--host=0.0.0.0\",\n",
- " \"--port=8080\",\n",
- " f\"--model={model_id}\",\n",
- " f\"--tensor-parallel-size={accelerator_count}\",\n",
- " \"--swap-space=16\",\n",
- " f\"--gpu-memory-utilization={gpu_memory_utilization}\",\n",
- " f\"--max-model-len={max_model_len}\",\n",
- " f\"--dtype={dtype}\",\n",
- " f\"--max-loras={max_loras}\",\n",
- " f\"--max-cpu-loras={max_cpu_loras}\",\n",
- " f\"--max-num-seqs={max_num_seqs}\",\n",
- " \"--disable-log-stats\",\n",
- " ]\n",
- "\n",
- " if enable_trust_remote_code:\n",
- " vllm_args.append(\"--trust-remote-code\")\n",
- "\n",
- " if enforce_eager:\n",
- " vllm_args.append(\"--enforce-eager\")\n",
- "\n",
- " if enable_lora:\n",
- " vllm_args.append(\"--enable-lora\")\n",
- "\n",
- " if enable_chunked_prefill:\n",
- " vllm_args.append(\"--enable-chunked-prefill\")\n",
- "\n",
- " if enable_prefix_cache:\n",
- " vllm_args.append(\"--enable-prefix-caching\")\n",
- "\n",
- " if 0 < host_prefix_kv_cache_utilization_target < 1:\n",
- " vllm_args.append(\n",
- " f\"--host-prefix-kv-cache-utilization-target={host_prefix_kv_cache_utilization_target}\"\n",
- " )\n",
- "\n",
- " if model_type:\n",
- " vllm_args.append(f\"--model-type={model_type}\")\n",
- "\n",
- " env_vars = {\n",
- " \"MODEL_ID\": base_model_id,\n",
- " \"DEPLOY_SOURCE\": \"notebook\",\n",
- " }\n",
- "\n",
- " # HF_TOKEN is not a compulsory field and may not be defined.\n",
- " try:\n",
- " if HF_TOKEN:\n",
- " env_vars[\"HF_TOKEN\"] = HF_TOKEN\n",
- " except NameError:\n",
- " pass\n",
- "\n",
- " model = aiplatform.Model.upload(\n",
- " display_name=model_name,\n",
- " serving_container_image_uri=VLLM_DOCKER_URI,\n",
- " serving_container_args=vllm_args,\n",
- " serving_container_ports=[8080],\n",
- " serving_container_predict_route=\"/generate\",\n",
- " serving_container_health_route=\"/ping\",\n",
- " serving_container_environment_variables=env_vars,\n",
- " serving_container_shared_memory_size_mb=(16 * 1024), # 16 GB\n",
- " serving_container_deployment_timeout=7200,\n",
- " model_garden_source_model_name=\"publishers/meta/models/llama3_1\",\n",
- " )\n",
- " print(\n",
- " f\"Deploying {model_name} on {machine_type} with {accelerator_count} {accelerator_type} GPU(s).\"\n",
- " )\n",
- " model.deploy(\n",
- " endpoint=endpoint,\n",
- " machine_type=machine_type,\n",
- " accelerator_type=accelerator_type,\n",
- " accelerator_count=accelerator_count,\n",
- " deploy_request_timeout=1800,\n",
- " service_account=service_account,\n",
- " system_labels={\n",
- " \"NOTEBOOK_NAME\": \"model_garden_finetuning_tutorial.ipynb\",\n",
- " },\n",
- " )\n",
- " print(\"endpoint_name:\", endpoint.name)\n",
- "\n",
- " return model, endpoint\n",
- "\n",
- "\n",
- "def predict_vllm(\n",
- " prompt: str,\n",
- " max_tokens: int,\n",
- " temperature: float,\n",
- " top_p: float,\n",
- " top_k: int,\n",
- " raw_response: bool,\n",
- " lora_weight: str = \"\",\n",
- " use_dedicated_endpoint: bool = False,\n",
- "):\n",
- " # Parameters for inference.\n",
- " instance = {\n",
- " \"prompt\": prompt,\n",
- " \"max_tokens\": max_tokens,\n",
- " \"temperature\": temperature,\n",
- " \"top_p\": top_p,\n",
- " \"top_k\": top_k,\n",
- " \"raw_response\": raw_response,\n",
- " }\n",
- " if lora_weight:\n",
- " instance[\"dynamic-lora\"] = lora_weight\n",
- " instances = [instance]\n",
- " response = endpoints[\"vllm_gpu\"].predict(\n",
- " instances=instances, use_dedicated_endpoint=use_dedicated_endpoint\n",
- " )\n",
- "\n",
- " for prediction in response.predictions:\n",
- " print(prediction)\n",
- "\n",
- "\n",
- "deploy_pretrained_model_id = pretrained_model_id\n",
- "print(\"Deploying model in:\", deploy_pretrained_model_id)\n",
- "\n",
- "models[\"vllm_gpu\"], endpoints[\"vllm_gpu\"] = deploy_model_vllm(\n",
- " model_name=common_util.get_job_name_with_datetime(prefix=\"llama3_1-vllm-serve\"),\n",
- " model_id=deploy_pretrained_model_id,\n",
- " service_account=SERVICE_ACCOUNT,\n",
- " machine_type=serve_machine_type,\n",
- " accelerator_type=serve_accelerator_type,\n",
- " accelerator_count=serve_accelerator_count,\n",
- " gpu_memory_utilization=gpu_memory_utilization,\n",
- " max_model_len=max_model_len,\n",
- " enable_lora=True,\n",
- " use_dedicated_endpoint=use_dedicated_endpoint,\n",
- " enable_prefix_cache=True,\n",
- " host_prefix_kv_cache_utilization_target=0.7,\n",
- ")\n",
- "\n",
- "# @markdown Click \"Show Code\" to see more details."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "2UYUNn60G_4U"
- },
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Prompt:\n",
- "<|start_header_id|>user<|end_header_id|>\\n\\nWhat was Anya looking for? Context: Anya clutched the worn teddy bear, its button eye dangling precariously. She'd lost it in the park yesterday, and the thought of never seeing Mr. Snuggles again made her tummy ache. She retraced her steps, her eyes scanning the colorful playground equipment and the sprawling green lawn.<|eot_id|><|start_header_id|>assistant<|end_header_id|>\n",
- "Output:\n",
- "Anya was looking for her teddy bear, Mr. Snuggles.\n"
- ]
- }
- ],
- "source": [
- "# @title Predict\n",
- "\n",
- "# @markdown Once deployment succeeds, you can send requests to the endpoint with text prompts. Sampling parameters supported by vLLM can be found [here](https://docs.vllm.ai/en/latest/dev/sampling_params.html).\n",
- "\n",
- "# @markdown The following example demonstrates a reading comprehension question\n",
- "# @markdown answering task. The model is given a question and a short context\n",
- "# @markdown passage, and must give an answer from the provided text.\n",
- "\n",
- "\n",
- "# @markdown - Human: `What was Anya looking for? Context: Anya clutched the worn teddy bear, its button eye dangling precariously. She'd lost it in the park yesterday, and the thought of never seeing Mr. Snuggles again made her tummy ache. She retraced her steps, her eyes scanning the colorful playground equipment and the sprawling green lawn.`\n",
- "\n",
- "# @markdown - Assistant: `Anya was looking for her teddy bear, Mr. Snuggles.`\n",
- "\n",
- "# @markdown Additionally, you can moderate the generated text with Vertex AI. See [Moderate text documentation](https://cloud.google.com/natural-language/docs/moderating-text) for more details.\n",
- "\n",
- "prompt = \"What was Anya looking for? Context: Anya clutched the worn teddy bear, its button eye dangling precariously. She'd lost it in the park yesterday, and the thought of never seeing Mr. Snuggles again made her tummy ache. She retraced her steps, her eyes scanning the colorful playground equipment and the sprawling green lawn.\" # @param {type: \"string\"}\n",
- "prompt_with_headers = (\n",
- " f\"<|start_header_id|>user<|end_header_id|>\\\\n\\\\n{prompt}<|eot_id|>\"\n",
- " \"<|start_header_id|>assistant<|end_header_id|>\\n\\n\"\n",
- ")\n",
- "\n",
- "# @markdown If you encounter the issue like `ServiceUnavailable: 503 Took too long to respond when processing`, you can reduce the maximum number of output tokens, such as set `max_tokens` as 20.\n",
- "max_tokens = 100 # @param {type:\"integer\"}\n",
- "temperature = 1.0 # @param {type:\"number\"}\n",
- "top_p = 1.0 # @param {type:\"number\"}\n",
- "top_k = 1 # @param {type:\"integer\"}\n",
- "raw_response = False # @param {type:\"boolean\"}\n",
- "\n",
- "\n",
- "predict_vllm(\n",
- " prompt=prompt_with_headers,\n",
- " max_tokens=max_tokens,\n",
- " temperature=temperature,\n",
- " top_p=top_p,\n",
- " top_k=top_k,\n",
- " raw_response=raw_response,\n",
- " lora_weight=lora_output_dir,\n",
- " use_dedicated_endpoint=use_dedicated_endpoint,\n",
- ")\n",
- "\n",
- "# @markdown Click \"Show Code\" to see more details."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "af21a3cff1e0"
- },
- "source": [
- "## Clean up resources"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "911406c1561e"
- },
- "outputs": [],
- "source": [
- "# @title Delete the model and endpoint\n",
- "\n",
- "train_job.delete()\n",
- "\n",
- "# @markdown Delete the experiment models and endpoints to recycle the resources\n",
- "# @markdown and avoid unnecessary continuous charges that may incur.\n",
- "\n",
- "# Undeploy model and delete endpoint.\n",
- "for endpoint in endpoints.values():\n",
- " endpoint.delete(force=True)\n",
- "\n",
- "# Delete models.\n",
- "for model in models.values():\n",
- " model.delete()\n",
- "\n",
- "delete_bucket = False # @param {type:\"boolean\"}\n",
- "if delete_bucket:\n",
- " ! gsutil -m rm -r $BUCKET_NAME"
- ]
- }
- ],
- "metadata": {
- "colab": {
- "name": "model_garden_finetuning_tutorial.ipynb",
- "toc_visible": true
- },
- "kernelspec": {
- "display_name": "Python 3",
- "name": "python3"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 0
-}
diff --git a/notebooks/community/model_garden/model_garden_gemma2_finetuning_on_vertex.ipynb b/notebooks/community/model_garden/model_garden_gemma2_finetuning_on_vertex.ipynb
deleted file mode 100644
index c09fe0d41..000000000
--- a/notebooks/community/model_garden/model_garden_gemma2_finetuning_on_vertex.ipynb
+++ /dev/null
@@ -1,1086 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "7d9bbf86da5e"
- },
- "outputs": [],
- "source": [
- "# Copyright 2026 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": "99c1c3fc2ca5"
- },
- "source": [
- "# Vertex AI Model Garden - Gemma 2 Finetuning\n",
- "\n",
- "\n",
- " \n",
- " \n",
- "  Run in Workbench\n",
- " \n",
- " | \n",
- " \n",
- " \n",
- "  Run in Colab Enterprise\n",
- " \n",
- " | \n",
- " \n",
- " \n",
- "  View on GitHub\n",
- " \n",
- " | \n",
- "
"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "3de7470326a2"
- },
- "source": [
- "## Overview\n",
- "\n",
- "This notebook demonstrates finetuning and deploying Gemma 2 models with [Vertex AI Custom Training Job](https://cloud.google.com/vertex-ai/docs/training/create-custom-job). All of the examples in this notebook use parameter efficient finetuning methods [PEFT (LoRA)](https://github.com/huggingface/peft) to reduce training and storage costs. LoRA (Low-Rank Adaptation) is one approach of Parameter Efficient FineTuning (PEFT), where pretrained model weights are frozen and rank decomposition matrices representing the change in model weights are trained during finetuning. Read more about LoRA in the following publication: [Hu, E.J., Shen, Y., Wallis, P., Allen-Zhu, Z., Li, Y., Wang, S., Wang, L. and Chen, W., 2021. Lora: Low-rank adaptation of large language models. *arXiv preprint arXiv:2106.09685*](https://arxiv.org/abs/2106.09685).\n",
- "\n",
- "\n",
- "After tuning, we can deploy models on Vertex with GPU.\n",
- "\n",
- "\n",
- "### Objective\n",
- "\n",
- "- Finetune and deploy Gemma 2 models with Vertex AI Custom Training Jobs.\n",
- "- Evaluate the finetuned model using [lm-evaluation-harness](https://github.com/EleutherAI/lm-evaluation-harness).\n",
- "- Send prediction requests to your finetuned Gemma 2 model.\n",
- "\n",
- "### File a bug\n",
- "\n",
- "File a bug on [GitHub](https://github.com/GoogleCloudPlatform/vertex-ai-samples/issues/new) if you encounter any issue with the notebook.\n",
- "\n",
- "### Costs\n",
- "\n",
- "This tutorial uses billable components of Google Cloud:\n",
- "\n",
- "* Vertex AI\n",
- "* Cloud Storage\n",
- "\n",
- "Learn about [Vertex AI pricing](https://cloud.google.com/vertex-ai/pricing), [Cloud Storage pricing](https://cloud.google.com/storage/pricing), and use the [Pricing Calculator](https://cloud.google.com/products/calculator/) to generate a cost estimate based on your projected usage."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "264c07757582"
- },
- "source": [
- "## Before you begin"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "aS52SK74gDoB"
- },
- "outputs": [],
- "source": [
- "# @title Install Python Packages for Finetuning\n",
- "\n",
- "# @markdown 1. Install packages to validate dataset with template.\n",
- "! pip install --upgrade --quiet gcsfs==2024.3.1\n",
- "! pip install --upgrade --quiet accelerate==0.34.2\n",
- "! pip install --upgrade --quiet transformers==4.47.1\n",
- "! pip install --upgrade --quiet datasets==2.20.0\n",
- "! pip install --upgrade --quiet google-cloud-aiplatform==1.130.0"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "855d6b96f291"
- },
- "outputs": [],
- "source": [
- "# @title Setup Google Cloud project\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. For finetuning, **[click here](https://console.cloud.google.com/iam-admin/quotas?location=us-central1&metric=aiplatform.googleapis.com%2Frestricted_image_training_nvidia_a100_80gb_gpus)** to check if your project already has the required 8 Nvidia A100 80 GB GPUs in the us-central1 region. If yes, then run this notebook in the us-central1 region. If you do not have 8 Nvidia A100 80 GPUs or have more GPU requirements than this, then schedule your job with Nvidia H100 GPUs via Dynamic Workload Scheduler using [these instructions](https://cloud.google.com/vertex-ai/docs/training/schedule-jobs-dws). For Dynamic Workload Scheduler, check the [us-central1](https://console.cloud.google.com/iam-admin/quotas?location=us-central1&metric=aiplatform.googleapis.com%2Fcustom_model_training_preemptible_nvidia_h100_gpus) or [europe-west4](https://console.cloud.google.com/iam-admin/quotas?location=europe-west4&metric=aiplatform.googleapis.com%2Fcustom_model_training_preemptible_nvidia_h100_gpus) quota for Nvidia H100 GPUs. If you do not have enough GPUs, then you can follow [these instructions](https://cloud.google.com/docs/quotas/view-manage#viewing_your_quota_console) to request quota.\n",
- "\n",
- "# @markdown 3. For serving, **[click here](https://console.cloud.google.com/iam-admin/quotas?location=us-central1&metric=aiplatform.googleapis.com%2Fcustom_model_serving_nvidia_l4_gpus)** to check if your project already has the required 1 L4 GPU in the us-central1 region. If yes, then run this notebook in the us-central1 region. If you need more L4 GPUs for your project, then you can follow [these instructions](https://cloud.google.com/docs/quotas/view-manage#viewing_your_quota_console) to request more. Alternatively, 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).\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",
- "# @markdown 4. **[Optional]** [Create a Cloud Storage bucket](https://cloud.google.com/storage/docs/creating-buckets) for storing experiment outputs. Set the BUCKET_URI for the experiment environment. The specified Cloud Storage bucket (`BUCKET_URI`) should be located in the same region as where the notebook was launched. Note that a multi-region bucket (eg. \"us\") is not considered a match for a single region covered by the multi-region range (eg. \"us-central1\"). If not set, a unique GCS bucket will be created instead.\n",
- "\n",
- "BUCKET_URI = \"gs://\" # @param {type:\"string\"}\n",
- "\n",
- "# @markdown 5. **[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",
- "# Import the necessary packages\n",
- "! rm -rf vertex-ai-samples && git clone https://github.com/GoogleCloudPlatform/vertex-ai-samples.git\n",
- "! cd vertex-ai-samples && git reset --hard 7ae13b346a72ee2a2dc8152dd40c6ddd72d6c810\n",
- "\n",
- "import datetime\n",
- "import importlib\n",
- "import os\n",
- "import uuid\n",
- "from typing import Tuple\n",
- "\n",
- "from google.cloud import aiplatform\n",
- "from google.cloud.aiplatform.compat.types import \\\n",
- " custom_job as gca_custom_job_compat\n",
- "\n",
- "if os.environ.get(\"VERTEX_PRODUCT\") != \"COLAB_ENTERPRISE\":\n",
- " ! pip install --upgrade tensorflow\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",
- "models, endpoints = {}, {}\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",
- " if not os.environ.get(\"GOOGLE_CLOUD_REGION\"):\n",
- " raise ValueError(\n",
- " \"REGION must be set. See\"\n",
- " \" https://cloud.google.com/vertex-ai/docs/general/locations for\"\n",
- " \" available cloud locations.\"\n",
- " )\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",
- "# Cloud Storage bucket for storing the experiment artifacts.\n",
- "# A unique GCS bucket will be created for the purpose of this notebook. If you\n",
- "# prefer using your own GCS bucket, change the value yourself below.\n",
- "now = datetime.datetime.now().strftime(\"%Y%m%d%H%M%S\")\n",
- "BUCKET_NAME = \"/\".join(BUCKET_URI.split(\"/\")[:3])\n",
- "\n",
- "if BUCKET_URI is None or BUCKET_URI.strip() == \"\" or BUCKET_URI == \"gs://\":\n",
- " BUCKET_URI = f\"gs://{PROJECT_ID}-tmp-{now}-{str(uuid.uuid4())[:4]}\"\n",
- " BUCKET_NAME = \"/\".join(BUCKET_URI.split(\"/\")[:3])\n",
- " ! gsutil mb -l {REGION} {BUCKET_URI}\n",
- "else:\n",
- " assert BUCKET_URI.startswith(\"gs://\"), \"BUCKET_URI must start with `gs://`.\"\n",
- " shell_output = ! gsutil ls -Lb {BUCKET_NAME} | grep \"Location constraint:\" | sed \"s/Location constraint://\"\n",
- " bucket_region = shell_output[0].strip().lower()\n",
- " if bucket_region != REGION:\n",
- " raise ValueError(\n",
- " \"Bucket region %s is different from notebook region %s\"\n",
- " % (bucket_region, REGION)\n",
- " )\n",
- "print(f\"Using this GCS Bucket: {BUCKET_URI}\")\n",
- "\n",
- "STAGING_BUCKET = os.path.join(BUCKET_URI, \"temporal\")\n",
- "MODEL_BUCKET = os.path.join(BUCKET_URI, \"gemma2\")\n",
- "\n",
- "\n",
- "# Initialize Vertex AI API.\n",
- "print(\"Initializing Vertex AI API.\")\n",
- "aiplatform.init(project=PROJECT_ID, location=REGION, staging_bucket=STAGING_BUCKET)\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",
- "\n",
- "# Provision permissions to the SERVICE_ACCOUNT with the GCS bucket\n",
- "! gsutil iam ch serviceAccount:{SERVICE_ACCOUNT}:roles/storage.admin $BUCKET_NAME\n",
- "\n",
- "! gcloud config set project $PROJECT_ID\n",
- "! gcloud projects add-iam-policy-binding --no-user-output-enabled {PROJECT_ID} --member=serviceAccount:{SERVICE_ACCOUNT} --role=\"roles/storage.admin\"\n",
- "! gcloud projects add-iam-policy-binding --no-user-output-enabled {PROJECT_ID} --member=serviceAccount:{SERVICE_ACCOUNT} --role=\"roles/aiplatform.user\"\n",
- "\n",
- "# @markdown ## Access Gemma 2 Models\n",
- "\n",
- "# @markdown You must provide a Hugging Face User Access Token (with read access) to access the Gemma 2 models. You can follow the [Hugging Face documentation](https://huggingface.co/docs/hub/en/security-tokens) to create a **read** access token and put it in the `HF_TOKEN` field below.\n",
- "\n",
- "HF_TOKEN = \"\" # @param {type:\"string\", isTemplate:true}\n",
- "assert HF_TOKEN, \"Provide a read HF_TOKEN to load models from Hugging Face.\"\n",
- "\n",
- "model_path_prefix = \"google/\""
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "cb56d402e84a"
- },
- "source": [
- "## Finetune with HuggingFace PEFT and Deploy with vLLM on GPUs"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "KwAW99YZHTdy"
- },
- "outputs": [],
- "source": [
- "# @title Set dataset\n",
- "\n",
- "# @markdown Use the Vertex AI SDK to create and run the custom training jobs.\n",
- "\n",
- "# @markdown This notebook uses [timdettmers/openassistant-guanaco](https://huggingface.co/datasets/timdettmers/openassistant-guanaco) dataset as an example.\n",
- "# @markdown You can set `train_dataset` to any existing [Hugging Face dataset](https://huggingface.co/datasets) name, and set `train_column` to the name of the dataset column containing training data. The [timdettmers/openassistant-guanaco](https://huggingface.co/datasets/timdettmers/openassistant-guanaco) has only one column `text`, and therefore we set `train_column` to `text` in this notebook.\n",
- "\n",
- "# @markdown ### (Optional) Prepare a custom JSONL dataset for finetuning\n",
- "\n",
- "# @markdown You can prepare a JSONL file where each line is a valid JSON string as your custom training dataset. For example, here is one line from the [timdettmers/openassistant-guanaco](https://huggingface.co/datasets/timdettmers/openassistant-guanaco) dataset:\n",
- "# @markdown ```\n",
- "# @markdown {\"text\": \"### Human: Hola### Assistant: \\u00a1Hola! \\u00bfEn qu\\u00e9 puedo ayudarte hoy?\"}\n",
- "# @markdown ```\n",
- "\n",
- "# @markdown The JSON object has a key `text`, which should match `train_column`; The value should be one training data point, i.e. a string. After you prepared your JSONL file, you can either upload it to [Hugging Face datasets](https://huggingface.co/datasets) or [Google Cloud Storage](https://cloud.google.com/storage).\n",
- "\n",
- "# @markdown - To upload a JSONL dataset to [Hugging Face datasets](https://huggingface.co/datasets), follow the instructions on [Uploading Datasets](https://huggingface.co/docs/hub/en/datasets-adding). Then, set `dataset_name` to the name of your newly created dataset on Hugging Face.\n",
- "\n",
- "# @markdown - To upload a JSONL dataset to [Google Cloud Storage](https://cloud.google.com/storage), follow the instructions on [Upload objects from a filesystem](https://cloud.google.com/storage/docs/uploading-objects). Then, set `dataset_name` to the `gs://` URI to your JSONL file. For example: `gs://cloud-samples-data/vertex-ai/model-evaluation/peft_train_sample.jsonl`.\n",
- "\n",
- "# @markdown Optionally update the `train_column` field below if your JSON objects use a key other than the default `text`.\n",
- "\n",
- "# @markdown ### (Optional) Format your data with custom JSON template\n",
- "\n",
- "# @markdown Sometimes, your dataset might have multiple text columns and you want to construct the training data with a template. You can prepare a JSON template in the following format:\n",
- "\n",
- "# @markdown ```\n",
- "# @markdown {\n",
- "# @markdown \"description\": \"Template that accepts text-bison format.\",\n",
- "# @markdown \"source\": \"https://cloud.google.com/vertex-ai/generative-ai/docs/models/tune-text-models-supervised#dataset-format\",\n",
- "# @markdown \"prompt_input\": \"\\n\\n<|start_header_id|>user<|end_header_id|>\\n\\n{input_text}<|eot_id|>\\n\\n<|start_header_id|>assistant<|end_header_id|>\\n\\n{output_text}<|eot_id|>\",\n",
- "# @markdown \"instruction_separator\": \"<|start_header_id|>user<|end_header_id|>\\n\\n\",\n",
- "# @markdown \"response_separator\": \"<|start_header_id|>assistant<|end_header_id|>\\n\\n\"\n",
- "# @markdown }\n",
- "# @markdown ```\n",
- "\n",
- "# @markdown As an example, the template above can be used to format the following training data (this line comes from `gs://cloud-samples-data/vertex-ai/model-evaluation/peft_train_sample.jsonl`):\n",
- "\n",
- "# @markdown ```\n",
- "# @markdown {\"input_text\":\"TRANSCRIPT: \\nREASON FOR EVALUATION:,\\n\\n LABEL:\",\"output_text\":\"Chiropractic\"}\n",
- "# @markdown ```\n",
- "\n",
- "# @markdown This example template simply concatenates `input_text` with `output_text` with some special tokens in between.\n",
- "# @markdown\n",
- "# @markdown To try such custom dataset, you can make the following changes:\n",
- "# @markdown 1. Set `template` to `llama3-text-bison`\n",
- "# @markdown 1. Set `train_dataset` to `gs://cloud-samples-data/vertex-ai/model-evaluation/peft_train_sample.jsonl`\n",
- "# @markdown 1. Set `train_split` to `train`\n",
- "# @markdown 1. Set `eval_dataset` to `gs://cloud-samples-data/vertex-ai/model-evaluation/peft_eval_sample.jsonl`\n",
- "# @markdown 1. Set `eval_split` to `train` (**NOT** `test`)\n",
- "# @markdown 1. Set `train_column` as `input_text`.\n",
- "\n",
- "# Template name or gs:// URI to a custom template.\n",
- "template = \"openassistant-guanaco\" # @param {type:\"string\"}\n",
- "\n",
- "# Hugging Face dataset name or gs:// URI to a custom JSONL dataset.\n",
- "train_dataset = \"timdettmers/openassistant-guanaco\" # @param {type:\"string\"}\n",
- "train_split = \"train\" # @param {type:\"string\"}\n",
- "eval_dataset = \"timdettmers/openassistant-guanaco\" # @param {type:\"string\"}\n",
- "eval_split = \"test\" # @param {type:\"string\"}\n",
- "\n",
- "# Name of the dataset column containing training text input.\n",
- "train_column = \"text\" # @param {type:\"string\"}\n",
- "# Maximum sequence length.\n",
- "max_seq_length = 4096 # @param{type:\"integer\"}"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "NoueJWi72OSo"
- },
- "outputs": [],
- "source": [
- "# @title Set model\n",
- "\n",
- "# @markdown Select a model variant of Gemma 2.\n",
- "base_model_id = \"gemma-2-2b-it\" # @param [\"gemma-2-2b\", \"gemma-2-2b-it\", \"gemma-2-9b\", \"gemma-2-9b-it\", \"gemma-2-27b\", \"gemma-2-27b-it\"] {isTemplate: true}\n",
- "pretrained_model_id = os.path.join(model_path_prefix, base_model_id)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "-NlLSiCOvru1"
- },
- "outputs": [],
- "source": [
- "# @title Validate Dataset with Template\n",
- "\n",
- "# @markdown This section validates the train and eval datasets with the template before starting the fine tuning process.\n",
- "\n",
- "dataset_validation_util = importlib.import_module(\n",
- " \"vertex-ai-samples.community-content.vertex_model_garden.model_oss.notebook_util.dataset_validation_util\"\n",
- ")\n",
- "\n",
- "if dataset_validation_util.is_gcs_path(pretrained_model_id):\n",
- " # Download tokenizer.\n",
- " ! mkdir tokenizer\n",
- " ! gsutil cp {pretrained_model_id}/tokenizer.json ./tokenizer\n",
- " ! gsutil cp {pretrained_model_id}/config.json ./tokenizer\n",
- " tokenizer_path = \"./tokenizer\"\n",
- " access_token = \"\"\n",
- "else:\n",
- " tokenizer_path = pretrained_model_id\n",
- " access_token = HF_TOKEN\n",
- "\n",
- "tokenizer = dataset_validation_util.load_tokenizer(tokenizer_path, None, access_token)\n",
- "\n",
- "# Validate the train dataset.\n",
- "dataset_validation_util.validate_dataset_with_template(\n",
- " dataset_name=train_dataset,\n",
- " split=train_split,\n",
- " input_column=train_column,\n",
- " template=template,\n",
- " max_seq_length=max_seq_length,\n",
- " use_multiprocessing=False,\n",
- " tokenizer=tokenizer,\n",
- ")\n",
- "\n",
- "# Validate the eval dataset if it exists.\n",
- "if eval_dataset:\n",
- " dataset_validation_util.validate_dataset_with_template(\n",
- " dataset_name=eval_dataset,\n",
- " split=eval_split,\n",
- " input_column=train_column,\n",
- " template=template,\n",
- " max_seq_length=max_seq_length,\n",
- " use_multiprocessing=False,\n",
- " tokenizer=tokenizer,\n",
- " )"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "ivVGS9dHXPOz"
- },
- "outputs": [],
- "source": [
- "# @title Finetune\n",
- "\n",
- "# @markdown This section demonstrates how to finetune the Gemma 2 model and merge the finetuned LoRA adapter with the base model on Vertex AI. It uses the Vertex AI SDK to create and run the custom training jobs.\n",
- "\n",
- "# @markdown The training job takes approximately between 10 to 20 mins to set-up. Once done, the training job is expected to take around 20 mins with the default configuration. To find the training time, throughput, and memory usage of your training job, you can go to the training logs and check the log line of the last training epoch.\n",
- "\n",
- "# @markdown **Note**:\n",
- "# @markdown 1. We recommend setting `finetuning_precision_mode` to `4bit` because it enables using fewer hardware resources for finetuning.\n",
- "# @markdown 1. If `max_steps > 0`, it takes precedence over `epochs`. One can set a small `max_steps` value to quickly check the pipeline.\n",
- "\n",
- "# @markdown Accelerator type to use for training.\n",
- "training_accelerator_type = \"NVIDIA_A100_80GB\" # @param [\"NVIDIA_A100_80GB\", \"NVIDIA_H100_80GB\"]\n",
- "\n",
- "# The pre-built training docker image.\n",
- "if training_accelerator_type == \"NVIDIA_A100_80GB\":\n",
- " repo = \"us-docker.pkg.dev/vertex-ai-restricted\"\n",
- " is_restricted_image = True\n",
- " is_dynamic_workload_scheduler = False\n",
- " dws_kwargs = {}\n",
- "else:\n",
- " repo = \"us-docker.pkg.dev/vertex-ai\"\n",
- " is_restricted_image = False\n",
- " is_dynamic_workload_scheduler = True\n",
- " dws_kwargs = {\n",
- " \"max_wait_duration\": 1800, # 30 minutes\n",
- " \"scheduling_strategy\": gca_custom_job_compat.Scheduling.Strategy.FLEX_START,\n",
- " }\n",
- "\n",
- "TRAIN_DOCKER_URI = (\n",
- " f\"{repo}/vertex-vision-model-garden-dockers/pytorch-peft-train:stable_20250705\"\n",
- ")\n",
- "\n",
- "# Worker pool spec.\n",
- "if training_accelerator_type == \"NVIDIA_A100_80GB\":\n",
- " per_node_accelerator_count = 8\n",
- " training_machine_type = \"a2-ultragpu-8g\"\n",
- "elif training_accelerator_type == \"NVIDIA_H100_80GB\":\n",
- " per_node_accelerator_count = 8\n",
- " training_machine_type = \"a3-highgpu-8g\"\n",
- "else:\n",
- " raise ValueError(\n",
- " f\"Recommended machine settings not found for: {training_accelerator_type}. To use another accelerator type, edit this code block to pass in an appropriate `training_machine_type`, `training_accelerator_type`, and `per_node_accelerator_count` by clicking `Show Code` and then modifying the code.\"\n",
- " )\n",
- "\n",
- "# @markdown Batch size for finetuning.\n",
- "per_device_train_batch_size = 1 # @param{type:\"integer\"}\n",
- "# @markdown Number of updates steps to accumulate the gradients for, before performing a backward/update pass.\n",
- "gradient_accumulation_steps = 4 # @param{type:\"integer\"}\n",
- "# @markdown Setting a positive `max_steps` here will override `num_train_epochs`.\n",
- "max_steps = -1 # @param{type:\"integer\"}\n",
- "num_train_epochs = 1.0 # @param{type:\"number\"}\n",
- "# @markdown Precision mode for finetuning.\n",
- "finetuning_precision_mode = \"4bit\" # @param [\"4bit\", \"8bit\", \"float16\"]\n",
- "# @markdown Learning rate.\n",
- "learning_rate = 5e-5 # @param{type:\"number\"}\n",
- "# @markdown The scheduler type to use.\n",
- "lr_scheduler_type = \"cosine\" # @param{type:\"string\"}\n",
- "# @markdown LoRA parameters.\n",
- "lora_rank = 16 # @param{type:\"integer\"}\n",
- "lora_alpha = 32 # @param{type:\"integer\"}\n",
- "lora_dropout = 0.05 # @param{type:\"number\"}\n",
- "# Activates gradient checkpointing for the current model (may be referred to as activation checkpointing or checkpoint activations in other frameworks).\n",
- "gradient_checkpointing = True\n",
- "# Attention implementation to use in the model.\n",
- "attn_implementation = \"eager\"\n",
- "# The optimizer for which to schedule the learning rate.\n",
- "optimizer = \"paged_adamw_32bit\"\n",
- "# Define the proportion of training to be dedicated to a linear warmup where learning rate gradually increases.\n",
- "warmup_ratio = \"0.01\"\n",
- "# The list or string of integrations to report the results and logs to.\n",
- "report_to = \"tensorboard\"\n",
- "# Number of updates steps before two checkpoint saves.\n",
- "save_steps = 10\n",
- "# Number of update steps between two logs.\n",
- "logging_steps = save_steps\n",
- "# Train precision of the model.\n",
- "train_precision = \"bfloat16\"\n",
- "\n",
- "# @markdown Evaluation metrics to compute. Supported eval metrics: loss, perplexity, bleu, google_bleu, rouge1, rouge2, rougeL, rougeLsum.\n",
- "eval_metric_name = \"loss,perplexity,bleu\" # @param{type:\"string\"}\n",
- "# @markdown Metric to use for best model selection. This will save the best checkpoint based on the eval metric.\n",
- "metric_for_best_model = \"perplexity\" # @param{type:\"string\"}\n",
- "\n",
- "replica_count = 1\n",
- "\n",
- "common_util.check_quota(\n",
- " project_id=PROJECT_ID,\n",
- " region=REGION,\n",
- " accelerator_type=training_accelerator_type,\n",
- " accelerator_count=per_node_accelerator_count * replica_count,\n",
- " is_for_training=True,\n",
- " is_restricted_image=is_restricted_image,\n",
- " is_dynamic_workload_scheduler=is_dynamic_workload_scheduler,\n",
- ")\n",
- "\n",
- "job_name = common_util.get_job_name_with_datetime(\"gemma2-lora-train\")\n",
- "\n",
- "base_output_dir = os.path.join(STAGING_BUCKET, job_name)\n",
- "# Create a GCS folder to store the LORA adapter.\n",
- "lora_output_dir = os.path.join(base_output_dir, \"adapter\")\n",
- "# Create a GCS folder to store the merged model with the base model and the\n",
- "# finetuned LORA adapter.\n",
- "merged_model_output_dir = os.path.join(base_output_dir, \"merged-model\")\n",
- "\n",
- "# Add labels for the finetuning job.\n",
- "\n",
- "labels = {\n",
- " \"mg-source\": common_util.get_deploy_source(),\n",
- " \"mg-notebook-name\": \"model_garden_gemma2_finetuning_on_vertex.ipynb\".split(\".\")[0],\n",
- "}\n",
- "\n",
- "labels[\"mg-tune\"] = \"publishers-google-models-gemma-2\"\n",
- "versioned_model_id = base_model_id.lower().replace(\".\", \"-\")\n",
- "labels[\"versioned-mg-tune\"] = f\"{labels['mg-tune']}-{versioned_model_id}\"\n",
- "\n",
- "eval_args = [\n",
- " f\"--eval_dataset={eval_dataset}\",\n",
- " f\"--eval_column={train_column}\",\n",
- " f\"--eval_template={template}\",\n",
- " f\"--eval_split={eval_split}\",\n",
- " f\"--eval_steps={save_steps}\",\n",
- " f\"--eval_metric_name={eval_metric_name}\",\n",
- " f\"--metric_for_best_model={metric_for_best_model}\",\n",
- "]\n",
- "\n",
- "train_job_args = [\n",
- " \"--config_file=vertex_vision_model_garden_peft/deepspeed_zero2_8gpu.yaml\",\n",
- " \"--task=instruct-lora\",\n",
- " \"--input_masking=True\",\n",
- " f\"--pretrained_model_name_or_path={pretrained_model_id}\",\n",
- " f\"--train_dataset={train_dataset}\",\n",
- " f\"--train_split={train_split}\",\n",
- " f\"--train_column={train_column}\",\n",
- " f\"--output_dir={lora_output_dir}\",\n",
- " f\"--merge_base_and_lora_output_dir={merged_model_output_dir}\",\n",
- " f\"--per_device_train_batch_size={per_device_train_batch_size}\",\n",
- " f\"--gradient_accumulation_steps={gradient_accumulation_steps}\",\n",
- " f\"--lora_rank={lora_rank}\",\n",
- " f\"--lora_alpha={lora_alpha}\",\n",
- " f\"--lora_dropout={lora_dropout}\",\n",
- " f\"--max_steps={max_steps}\",\n",
- " f\"--max_seq_length={max_seq_length}\",\n",
- " f\"--learning_rate={learning_rate}\",\n",
- " f\"--lr_scheduler_type={lr_scheduler_type}\",\n",
- " f\"--precision_mode={finetuning_precision_mode}\",\n",
- " f\"--train_precision={train_precision}\",\n",
- " f\"--merge_model_precision_mode={train_precision}\",\n",
- " f\"--gradient_checkpointing={gradient_checkpointing}\",\n",
- " f\"--num_train_epochs={num_train_epochs}\",\n",
- " f\"--attn_implementation={attn_implementation}\",\n",
- " f\"--optimizer={optimizer}\",\n",
- " f\"--warmup_ratio={warmup_ratio}\",\n",
- " f\"--report_to={report_to}\",\n",
- " f\"--logging_output_dir={base_output_dir}\",\n",
- " f\"--save_steps={save_steps}\",\n",
- " f\"--logging_steps={logging_steps}\",\n",
- " f\"--train_template={template}\",\n",
- " f\"--huggingface_access_token={HF_TOKEN}\",\n",
- "] + eval_args\n",
- "\n",
- "# Pass training arguments and launch job.\n",
- "train_job = aiplatform.CustomContainerTrainingJob(\n",
- " display_name=job_name,\n",
- " container_uri=TRAIN_DOCKER_URI,\n",
- " labels=labels,\n",
- ")\n",
- "\n",
- "print(\"Running training job with args:\")\n",
- "print(\" \\\\\\n\".join(train_job_args))\n",
- "train_job.run(\n",
- " args=train_job_args,\n",
- " replica_count=replica_count,\n",
- " machine_type=training_machine_type,\n",
- " accelerator_type=training_accelerator_type,\n",
- " accelerator_count=per_node_accelerator_count,\n",
- " boot_disk_size_gb=500,\n",
- " service_account=SERVICE_ACCOUNT,\n",
- " base_output_dir=base_output_dir,\n",
- " sync=False, # Non-blocking call to run.\n",
- " **dws_kwargs,\n",
- ")\n",
- "\n",
- "# Wait until resource has been created.\n",
- "train_job.wait_for_resource_creation()\n",
- "\n",
- "print(\"LoRA adapter will be saved in:\", lora_output_dir)\n",
- "print(\"Trained and merged models will be saved in:\", merged_model_output_dir)\n",
- "\n",
- "# @markdown Click \"Show Code\" to see more details."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "lu-uxrFBmZ0s"
- },
- "outputs": [],
- "source": [
- "# @title Run TensorBoard\n",
- "# @markdown This section shows how to launch TensorBoard in a [Cloud Shell](https://cloud.google.com/shell/docs).\n",
- "# @markdown 1. Click the Cloud Shell icon() on the top right to open the Cloud Shell.\n",
- "# @markdown 2. Copy the `tensorboard` command shown below by running this cell.\n",
- "# @markdown 3. Paste and run the command in the Cloud Shell to launch TensorBoard.\n",
- "# @markdown 4. Once the command runs (You may have to click `Authorize` if prompted), click the link starting with `http://localhost`.\n",
- "\n",
- "# @markdown Note: You may need to wait around 10 minutes after the job starts in order for the TensorBoard logs to be written to the GCS bucket.\n",
- "print(f\"Command to copy: tensorboard --logdir {base_output_dir}/logs\")"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "KdtcMGHgtrVC"
- },
- "outputs": [],
- "source": [
- "# @title Select Evaluation Checkpoint\n",
- "\n",
- "if train_job.end_time is None:\n",
- " print(\"Waiting for the training job to finish...\")\n",
- " train_job.wait()\n",
- " print(\"The training job has finished.\")\n",
- "\n",
- "# @markdown The following checkpoints are available for evaluation:\n",
- "! gcloud storage ls \"{lora_output_dir}/node-0\" | grep \"checkpoint-\""
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "1LBADPr6tTqy"
- },
- "outputs": [],
- "source": [
- "# @title Run Evaluation Job\n",
- "# @markdown This section runs the evaluation using [lm-evaluation-harness](https://github.com/EleutherAI/lm-evaluation-harness) on the finetuned model. The evaluation takes approximately 20 mins to finish.\n",
- "\n",
- "# The pre-built evaluation docker image for LM Evaluation Harness.\n",
- "LM_EVAL_DOCKER_URI = \"us-docker.pkg.dev/vertex-ai/vertex-vision-model-garden-dockers/pytorch-lm-evaluation-harness:20250410_1035_RC00\"\n",
- "\n",
- "# @markdown Set `RUN_EVALUATION` to False to skip the evaluation job.\n",
- "RUN_EVALUATION = True # @param {type:\"boolean\"}\n",
- "\n",
- "eval_accelerator_type = \"NVIDIA_L4\"\n",
- "gpu_memory_utilization = 0.85\n",
- "\n",
- "if \"2b\" in base_model_id:\n",
- " eval_machine_type = \"g2-standard-12\"\n",
- " eval_accelerator_count = 1\n",
- "elif \"9b\" in base_model_id:\n",
- " eval_machine_type = \"g2-standard-48\"\n",
- " eval_accelerator_count = 4\n",
- "elif \"27b\" in base_model_id:\n",
- " eval_machine_type = \"g2-standard-96\"\n",
- " eval_accelerator_count = 8\n",
- " gpu_memory_utilization = 0.8\n",
- "else:\n",
- " raise ValueError(\n",
- " \"Recommended machine settings not found for model: %s\" % base_model_id\n",
- " )\n",
- "\n",
- "# @markdown Set `evaluation_checkpoint_dir` to an intermediate checkpoint from the above training job. If not set, the evaluation job will use the merged model.\n",
- "evaluation_checkpoint_dir = \"\" # @param {type:\"string\"}\n",
- "if evaluation_checkpoint_dir:\n",
- " pretrained = pretrained_model_id\n",
- "else:\n",
- " pretrained = merged_model_output_dir\n",
- "\n",
- "# @markdown Evaluation tasks to run.\n",
- "eval_tasks = \"coqa\" # @param {type:\"string\"}\n",
- "# @markdown Model to use for evaluation.\n",
- "model = \"vllm\" # @param {type:\"string\"}\n",
- "# @markdown Batch size for evaluation.\n",
- "batch_size = \"auto\" # @param {type:\"string\"}\n",
- "apply_chat_template = True if \"-it\" in pretrained_model_id else False\n",
- "max_model_len = 4096 # Maximum context length.\n",
- "\n",
- "model_args = f\"tensor_parallel_size={eval_accelerator_count},max_model_len={max_model_len},gpu_memory_utilization={gpu_memory_utilization},enforce_eager=True\"\n",
- "eval_output_dir = os.path.join(base_output_dir, \"lm_eval\")\n",
- "\n",
- "lm_eval_job_args = [\n",
- " \"--task=lm_eval\",\n",
- " f\"--model={model}\",\n",
- " f\"--eval_tasks={eval_tasks}\",\n",
- " f\"--pretrained_model_name_or_path={pretrained}\",\n",
- " f\"--model_args={model_args}\",\n",
- " f\"--output_dir={eval_output_dir}\",\n",
- " f\"--apply_chat_template={apply_chat_template}\",\n",
- " f\"--batch_size={batch_size}\",\n",
- " f\"--huggingface_access_token={HF_TOKEN}\",\n",
- "]\n",
- "\n",
- "if evaluation_checkpoint_dir:\n",
- " lm_eval_job_args.append(f\"--lora_path={evaluation_checkpoint_dir}\")\n",
- "\n",
- "if RUN_EVALUATION:\n",
- " common_util.check_quota(\n",
- " project_id=PROJECT_ID,\n",
- " region=REGION,\n",
- " accelerator_type=eval_accelerator_type,\n",
- " accelerator_count=eval_accelerator_count,\n",
- " is_for_training=True,\n",
- " )\n",
- " lm_eval_job = aiplatform.CustomContainerTrainingJob(\n",
- " display_name=common_util.get_job_name_with_datetime(\"gemma2-lm-eval\"),\n",
- " container_uri=LM_EVAL_DOCKER_URI,\n",
- " labels=labels,\n",
- " )\n",
- "\n",
- " print(\"Running evaluation job with args:\")\n",
- " print(\" \\\\\\n\".join(lm_eval_job_args))\n",
- " lm_eval_job.run(\n",
- " args=lm_eval_job_args,\n",
- " replica_count=1,\n",
- " machine_type=eval_machine_type,\n",
- " accelerator_type=eval_accelerator_type,\n",
- " accelerator_count=eval_accelerator_count,\n",
- " boot_disk_size_gb=500,\n",
- " service_account=SERVICE_ACCOUNT,\n",
- " base_output_dir=base_output_dir,\n",
- " )\n",
- "\n",
- "# @markdown Click \"Show Code\" to see more details."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "qmHW6m8xG_4U"
- },
- "outputs": [],
- "source": [
- "# @title Deploy\n",
- "# @markdown This section uploads the model to Model Registry and deploys it on the Endpoint. It takes 15 minutes to 1 hour to finish.\n",
- "print(\"Deploying models in:\", merged_model_output_dir)\n",
- "\n",
- "# The pre-built serving docker image for vLLM.\n",
- "VLLM_DOCKER_URI = \"us-docker.pkg.dev/vertex-ai/vertex-vision-model-garden-dockers/pytorch-vllm-serve:20250116_0916_RC00\"\n",
- "\n",
- "# @markdown Set `use_dedicated_endpoint` to False if you don't want to use [dedicated endpoint](https://cloud.google.com/vertex-ai/docs/general/deployment#create-dedicated-endpoint).\n",
- "use_dedicated_endpoint = True # @param {type:\"boolean\"}\n",
- "\n",
- "# Find Vertex AI prediction supported accelerators and regions [here](https://cloud.google.com/vertex-ai/docs/predictions/configure-compute).\n",
- "# @markdown Accelerator type to use for serving.\n",
- "serving_accelerator_type = \"NVIDIA_L4\" # @param [\"NVIDIA_L4\"] {isTemplate: true}\n",
- "\n",
- "if \"2b\" in base_model_id:\n",
- " if serving_accelerator_type == \"NVIDIA_L4\":\n",
- " # Sets 1 L4 (24G) to deploy Gemma 2 2B models.\n",
- " serving_machine_type = \"g2-standard-12\"\n",
- " serving_accelerator_count = 1\n",
- " else:\n",
- " raise ValueError(\n",
- " \"Recommended machine settings not found for accelerator type: %s\"\n",
- " % serving_accelerator_type\n",
- " )\n",
- "elif \"9b\" in base_model_id:\n",
- " if serving_accelerator_type == \"NVIDIA_L4\":\n",
- " # Sets 2 L4 (24G) to deploy Gemma 2 9B models.\n",
- " serving_machine_type = \"g2-standard-24\"\n",
- " serving_accelerator_count = 2\n",
- " else:\n",
- " raise ValueError(\n",
- " \"Recommended machine settings not found for accelerator type: %s\"\n",
- " % serving_accelerator_type\n",
- " )\n",
- "elif \"27b\" in base_model_id:\n",
- " if serving_accelerator_type == \"NVIDIA_L4\":\n",
- " # Sets 4 L4 (24G) to deploy Gemma 2 27B models.\n",
- " serving_machine_type = \"g2-standard-48\"\n",
- " serving_accelerator_count = 4\n",
- " else:\n",
- " raise ValueError(\n",
- " \"Recommended machine settings not found for accelerator type: %s\"\n",
- " % serving_accelerator_type\n",
- " )\n",
- "else:\n",
- " raise ValueError(\n",
- " \"Recommended machine settings not found for model: %s\" % base_model_id\n",
- " )\n",
- "\n",
- "common_util.check_quota(\n",
- " project_id=PROJECT_ID,\n",
- " region=REGION,\n",
- " accelerator_type=serving_accelerator_type,\n",
- " accelerator_count=serving_accelerator_count,\n",
- " is_for_training=False,\n",
- ")\n",
- "\n",
- "gpu_memory_utilization = 0.85\n",
- "max_model_len = 4096 # Maximum context length.\n",
- "\n",
- "\n",
- "def get_deploy_source() -> str:\n",
- " \"\"\"Gets deploy_source string based on running environment.\"\"\"\n",
- " vertex_product = os.environ.get(\"VERTEX_PRODUCT\", \"\")\n",
- " if vertex_product == \"COLAB_ENTERPRISE\":\n",
- " return \"notebook_colab_enterprise\"\n",
- " elif vertex_product == \"WORKBENCH_INSTANCE\":\n",
- " return \"notebook_workbench\"\n",
- " else:\n",
- " # Legacy workbench, legacy colab, or other custom environments.\n",
- " return \"notebook_environment_unspecified\"\n",
- "\n",
- "\n",
- "def deploy_model_vllm(\n",
- " model_name: str,\n",
- " model_id: str,\n",
- " publisher: str,\n",
- " publisher_model_id: str,\n",
- " service_account: str,\n",
- " base_model_id: str = None,\n",
- " machine_type: str = \"g2-standard-8\",\n",
- " accelerator_type: str = \"NVIDIA_L4\",\n",
- " accelerator_count: int = 1,\n",
- " gpu_memory_utilization: float = 0.9,\n",
- " max_model_len: int = 4096,\n",
- " dtype: str = \"auto\",\n",
- " enable_trust_remote_code: bool = False,\n",
- " enforce_eager: bool = False,\n",
- " enable_lora: bool = False,\n",
- " enable_chunked_prefill: bool = False,\n",
- " enable_prefix_cache: bool = False,\n",
- " host_prefix_kv_cache_utilization_target: float = 0.0,\n",
- " max_loras: int = 1,\n",
- " max_cpu_loras: int = 8,\n",
- " use_dedicated_endpoint: bool = False,\n",
- " max_num_seqs: int = 256,\n",
- " model_type: str = None,\n",
- " enable_llama_tool_parser: bool = False,\n",
- " is_spot: bool = False,\n",
- ") -> Tuple[aiplatform.Model, aiplatform.Endpoint]:\n",
- " \"\"\"Deploys trained models with vLLM into Vertex AI.\"\"\"\n",
- " endpoint = aiplatform.Endpoint.create(\n",
- " display_name=f\"{model_name}-endpoint\",\n",
- " dedicated_endpoint_enabled=use_dedicated_endpoint,\n",
- " )\n",
- "\n",
- " if not base_model_id:\n",
- " base_model_id = model_id\n",
- "\n",
- " # See https://docs.vllm.ai/en/latest/models/engine_args.html for a list of possible arguments with descriptions.\n",
- " vllm_args = [\n",
- " \"python\",\n",
- " \"-m\",\n",
- " \"vllm.entrypoints.api_server\",\n",
- " \"--host=0.0.0.0\",\n",
- " \"--port=8080\",\n",
- " f\"--model={model_id}\",\n",
- " f\"--tensor-parallel-size={accelerator_count}\",\n",
- " \"--swap-space=16\",\n",
- " f\"--max-model-len={max_model_len}\",\n",
- " f\"--dtype={dtype}\",\n",
- " f\"--max-loras={max_loras}\",\n",
- " f\"--max-cpu-loras={max_cpu_loras}\",\n",
- " f\"--max-num-seqs={max_num_seqs}\",\n",
- " \"--disable-log-stats\",\n",
- " ]\n",
- "\n",
- " if gpu_memory_utilization:\n",
- " vllm_args.append(f\"--gpu-memory-utilization={gpu_memory_utilization}\")\n",
- "\n",
- " if enable_trust_remote_code:\n",
- " vllm_args.append(\"--trust-remote-code\")\n",
- "\n",
- " if enforce_eager:\n",
- " vllm_args.append(\"--enforce-eager\")\n",
- "\n",
- " if enable_lora:\n",
- " vllm_args.append(\"--enable-lora\")\n",
- "\n",
- " if enable_chunked_prefill:\n",
- " vllm_args.append(\"--enable-chunked-prefill\")\n",
- "\n",
- " if enable_prefix_cache:\n",
- " vllm_args.append(\"--enable-prefix-caching\")\n",
- "\n",
- " if 0 < host_prefix_kv_cache_utilization_target < 1:\n",
- " vllm_args.append(\n",
- " f\"--host-prefix-kv-cache-utilization-target={host_prefix_kv_cache_utilization_target}\"\n",
- " )\n",
- "\n",
- " if model_type:\n",
- " vllm_args.append(f\"--model-type={model_type}\")\n",
- "\n",
- " if enable_llama_tool_parser:\n",
- " vllm_args.append(\"--enable-auto-tool-choice\")\n",
- " vllm_args.append(\"--tool-call-parser=vertex-llama-3\")\n",
- "\n",
- " env_vars = {\n",
- " \"MODEL_ID\": base_model_id,\n",
- " \"DEPLOY_SOURCE\": \"notebook\",\n",
- " }\n",
- "\n",
- " # HF_TOKEN is not a compulsory field and may not be defined.\n",
- " try:\n",
- " if HF_TOKEN:\n",
- " env_vars[\"HF_TOKEN\"] = HF_TOKEN\n",
- " except NameError:\n",
- " pass\n",
- "\n",
- " model = aiplatform.Model.upload(\n",
- " display_name=model_name,\n",
- " serving_container_image_uri=VLLM_DOCKER_URI,\n",
- " serving_container_args=vllm_args,\n",
- " serving_container_ports=[8080],\n",
- " serving_container_predict_route=\"/generate\",\n",
- " serving_container_health_route=\"/ping\",\n",
- " serving_container_environment_variables=env_vars,\n",
- " serving_container_shared_memory_size_mb=(16 * 1024), # 16 GB\n",
- " serving_container_deployment_timeout=7200,\n",
- " model_garden_source_model_name=(\n",
- " f\"publishers/{publisher}/models/{publisher_model_id}\"\n",
- " ),\n",
- " )\n",
- " print(\n",
- " f\"Deploying {model_name} on {machine_type} with {accelerator_count} {accelerator_type} GPU(s).\"\n",
- " )\n",
- " model.deploy(\n",
- " endpoint=endpoint,\n",
- " machine_type=machine_type,\n",
- " accelerator_type=accelerator_type,\n",
- " accelerator_count=accelerator_count,\n",
- " deploy_request_timeout=1800,\n",
- " service_account=service_account,\n",
- " spot=is_spot,\n",
- " system_labels={\n",
- " \"NOTEBOOK_NAME\": \"model_garden_gemma2_finetuning_on_vertex.ipynb\",\n",
- " \"NOTEBOOK_ENVIRONMENT\": get_deploy_source(),\n",
- " },\n",
- " )\n",
- " print(\"endpoint_name:\", endpoint.name)\n",
- "\n",
- " return model, endpoint\n",
- "\n",
- "\n",
- "models[\"vllm_gpu\"], endpoints[\"vllm_gpu\"] = deploy_model_vllm(\n",
- " model_name=common_util.get_job_name_with_datetime(prefix=\"gemma2-vllm-serve\"),\n",
- " model_id=merged_model_output_dir,\n",
- " publisher=\"google\",\n",
- " publisher_model_id=\"gemma2\",\n",
- " service_account=SERVICE_ACCOUNT,\n",
- " machine_type=serving_machine_type,\n",
- " accelerator_type=serving_accelerator_type,\n",
- " accelerator_count=serving_accelerator_count,\n",
- " gpu_memory_utilization=gpu_memory_utilization,\n",
- " max_model_len=max_model_len,\n",
- " use_dedicated_endpoint=use_dedicated_endpoint,\n",
- ")\n",
- "\n",
- "# @markdown Click \"Show code\" to see more details."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "2UYUNn60G_4U"
- },
- "outputs": [],
- "source": [
- "# @title Predict\n",
- "\n",
- "# @markdown Once deployment succeeds, you can send requests to the endpoint with text prompts. Sampling parameters supported by vLLM can be found [here](https://docs.vllm.ai/en/latest/dev/sampling_params.html).\n",
- "\n",
- "# @markdown Example:\n",
- "\n",
- "# @markdown ```\n",
- "# @markdown Human: What is a car?\n",
- "# @markdown Assistant: A car, or a motor car, is a road-connected human-transportation system used to move people or goods from one place to another. The term also encompasses a wide range of vehicles, including motorboats, trains, and aircrafts. Cars typically have four wheels, a cabin for passengers, and an engine or motor. They have been around since the early 19th century and are now one of the most popular forms of transportation, used for daily commuting, shopping, and other purposes.\n",
- "# @markdown ```\n",
- "# @markdown Additionally, you can moderate the generated text with Vertex AI. See [Moderate text documentation](https://cloud.google.com/natural-language/docs/moderating-text) for more details.\n",
- "\n",
- "# Loads an existing endpoint instance using the endpoint name:\n",
- "# - Using `endpoint_name = endpoint.name` allows us to get the\n",
- "# endpoint name of the endpoint `endpoint` created in the cell\n",
- "# above.\n",
- "# - Alternatively, you can set `endpoint_name = \"1234567890123456789\"` to load\n",
- "# an existing endpoint with the ID 1234567890123456789.\n",
- "# You may uncomment the code below to load an existing endpoint.\n",
- "\n",
- "# endpoint_name = \"\" # @param {type:\"string\"}\n",
- "# aip_endpoint_name = (\n",
- "# f\"projects/{PROJECT_ID}/locations/{REGION}/endpoints/{endpoint_name}\"\n",
- "# )\n",
- "# endpoint = aiplatform.Endpoint(aip_endpoint_name)\n",
- "\n",
- "prompt = \"What is a car?\" # @param {type: \"string\"}\n",
- "# @markdown If you encounter an issue like `ServiceUnavailable: 503 Took too long to respond when processing`, you can reduce the maximum number of output tokens, by lowering `max_tokens`.\n",
- "max_tokens = 50 # @param {type:\"integer\"}\n",
- "temperature = 1.0 # @param {type:\"number\"}\n",
- "top_p = 1.0 # @param {type:\"number\"}\n",
- "top_k = 1 # @param {type:\"integer\"}\n",
- "# @markdown Set `raw_response` to `True` to obtain the raw model output. Set `raw_response` to `False` to apply additional formatting in the structure of `\"Prompt:\\n{prompt.strip()}\\nOutput:\\n{output}\"`.\n",
- "raw_response = False # @param {type:\"boolean\"}\n",
- "\n",
- "# Overrides parameters for inferences.\n",
- "instances = [\n",
- " {\n",
- " \"prompt\": prompt,\n",
- " \"max_tokens\": max_tokens,\n",
- " \"temperature\": temperature,\n",
- " \"top_p\": top_p,\n",
- " \"top_k\": top_k,\n",
- " \"raw_response\": raw_response,\n",
- " },\n",
- "]\n",
- "response = endpoints[\"vllm_gpu\"].predict(\n",
- " instances=instances, use_dedicated_endpoint=use_dedicated_endpoint\n",
- ")\n",
- "\n",
- "for prediction in response.predictions:\n",
- " print(prediction)\n",
- "\n",
- "# @markdown Click \"Show Code\" to see more details."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "af21a3cff1e0"
- },
- "source": [
- "## Clean up resources"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "911406c1561e"
- },
- "outputs": [],
- "source": [
- "# Delete the train job.\n",
- "\n",
- "if train_job:\n",
- " train_job.delete()\n",
- "if RUN_EVALUATION and lm_eval_job:\n",
- " lm_eval_job.delete()\n",
- "\n",
- "\n",
- "# @markdown Delete the experiment models and endpoints to recycle the resources\n",
- "# @markdown and avoid unnecessary continuous charges that may incur.\n",
- "\n",
- "# Undeploy model and delete endpoint.\n",
- "for endpoint in endpoints.values():\n",
- " endpoint.delete(force=True)\n",
- "\n",
- "# Delete models.\n",
- "for model in models.values():\n",
- " model.delete()\n",
- "\n",
- "delete_bucket = False # @param {type:\"boolean\"}\n",
- "if delete_bucket:\n",
- " ! gsutil -m rm -r $BUCKET_NAME"
- ]
- }
- ],
- "metadata": {
- "colab": {
- "name": "model_garden_gemma2_finetuning_on_vertex.ipynb",
- "toc_visible": true
- },
- "kernelspec": {
- "display_name": "Python 3",
- "name": "python3"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 0
-}
diff --git a/notebooks/community/model_garden/model_garden_gemma_evaluation.ipynb b/notebooks/community/model_garden/model_garden_gemma_evaluation.ipynb
index 983fa7805..554d07abe 100644
--- a/notebooks/community/model_garden/model_garden_gemma_evaluation.ipynb
+++ b/notebooks/community/model_garden/model_garden_gemma_evaluation.ipynb
@@ -1,406 +1,406 @@
{
- "cells": [
- {
- "cell_type": "code",
- "execution_count": null,
- "language": "python",
- "metadata": {
- "cellView": "form",
- "id": "B8S-yo8qTIcO"
- },
- "outputs": [],
- "source": [
- "# Copyright 2026 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": "MTRywGxLTZfU"
- },
- "source": [
- "# Vertex AI Model Garden - Gemma Evaluation\n",
- "\n",
- "\n",
- " \n",
- " \n",
- "  Run in Colab Enterprise\n",
- " \n",
- " | \n",
- " \n",
- " \n",
- "  View on GitHub\n",
- " \n",
- " | \n",
- "
"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "2CXS0vZfT8_7"
- },
- "source": [
- "## Overview\n",
- "\n",
- "This notebook demonstrates evaluating pre-trained and instruction-tuned Gemma models in Vertex AI.\n",
- "\n",
- "### Objective\n",
- "\n",
- "- Evaluate pre-trained and instruction-tuned Gemma model on any of the benchmark datasets\n",
- "- Clean up the resources\n",
- "\n",
- "| Models |\n",
- "| :- |\n",
- "| [google/gemma-2b](https://huggingface.co/google/gemma-2b)\n",
- "| [google/gemma-2b-it](https://huggingface.co/google/gemma-2b-it)\n",
- "| [google/gemma-7b](https://huggingface.co/google/gemma-7b)\n",
- "| [google/gemma-7b-it](https://huggingface.co/google/gemma-7b-it)\n",
- "| [google/gemma-1.1-2b-it](https://huggingface.co/google/gemma-1.1-2b-it)\n",
- "| [google/gemma-1.1-7b-it](https://huggingface.co/google/gemma-1.1-7b-it)\n",
- "\n",
- "### Costs\n",
- "\n",
- "This tutorial uses billable components of Google Cloud:\n",
- "\n",
- "* Vertex AI\n",
- "* Cloud Storage\n",
- "\n",
- "Learn about [Vertex AI pricing](https://cloud.google.com/vertex-ai/pricing), [Cloud Storage pricing](https://cloud.google.com/storage/pricing), and use the [Pricing Calculator](https://cloud.google.com/products/calculator/) to generate a cost estimate based on your projected usage."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "HCY8PGrFUbT1"
- },
- "source": [
- "## Run the notebook"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "language": "python",
- "metadata": {
- "cellView": "form",
- "id": "81CC3tL1T_TL"
- },
- "outputs": [],
- "source": [
- "# @title Setup Google Cloud project\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]** [Create a Cloud Storage bucket](https://cloud.google.com/storage/docs/creating-buckets) for storing experiment outputs. Set the BUCKET_URI for the experiment environment. The specified Cloud Storage bucket (`BUCKET_URI`) should be located in the same region as where the notebook was launched. Note that a multi-region bucket (eg. \"us\") is not considered a match for a single region covered by the multi-region range (eg. \"us-central1\"). If not set, a unique GCS bucket will be created instead.\n",
- "\n",
- "BUCKET_URI = \"gs://\" # @param {type:\"string\"}\n",
- "\n",
- "# @markdown 3. **[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",
- "# Import the necessary packages\n",
- "\n",
- "! git clone https://github.com/GoogleCloudPlatform/vertex-ai-samples.git\n",
- "\n",
- "import datetime\n",
- "import importlib\n",
- "import os\n",
- "import uuid\n",
- "\n",
- "from google.cloud import aiplatform\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",
- "models, endpoints = {}, {}\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",
- " if not os.environ.get(\"GOOGLE_CLOUD_REGION\"):\n",
- " raise ValueError(\n",
- " \"REGION must be set. See\"\n",
- " \" https://cloud.google.com/vertex-ai/docs/general/locations for\"\n",
- " \" available cloud locations.\"\n",
- " )\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",
- "# Cloud Storage bucket for storing the experiment artifacts.\n",
- "# A unique GCS bucket will be created for the purpose of this notebook. If you\n",
- "# prefer using your own GCS bucket, change the value yourself below.\n",
- "now = datetime.datetime.now().strftime(\"%Y%m%d%H%M%S\")\n",
- "BUCKET_NAME = \"/\".join(BUCKET_URI.split(\"/\")[:3])\n",
- "\n",
- "if BUCKET_URI is None or BUCKET_URI.strip() == \"\" or BUCKET_URI == \"gs://\":\n",
- " BUCKET_URI = f\"gs://{PROJECT_ID}-tmp-{now}-{str(uuid.uuid4())[:4]}\"\n",
- " BUCKET_NAME = \"/\".join(BUCKET_URI.split(\"/\")[:3])\n",
- " ! gsutil mb -l {REGION} {BUCKET_URI}\n",
- "else:\n",
- " assert BUCKET_URI.startswith(\"gs://\"), \"BUCKET_URI must start with `gs://`.\"\n",
- " shell_output = ! gsutil ls -Lb {BUCKET_NAME} | grep \"Location constraint:\" | sed \"s/Location constraint://\"\n",
- " bucket_region = shell_output[0].strip().lower()\n",
- " if bucket_region != REGION:\n",
- " raise ValueError(\n",
- " \"Bucket region %s is different from notebook region %s\"\n",
- " % (bucket_region, REGION)\n",
- " )\n",
- "print(f\"Using this GCS Bucket: {BUCKET_URI}\")\n",
- "\n",
- "STAGING_BUCKET = os.path.join(BUCKET_URI, \"temporal\")\n",
- "MODEL_BUCKET = os.path.join(BUCKET_URI, \"gemma\")\n",
- "\n",
- "\n",
- "# Initialize Vertex AI API.\n",
- "print(\"Initializing Vertex AI API.\")\n",
- "aiplatform.init(project=PROJECT_ID, location=REGION, staging_bucket=STAGING_BUCKET)\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",
- "\n",
- "# Provision permissions to the SERVICE_ACCOUNT with the GCS bucket\n",
- "! gsutil iam ch serviceAccount:{SERVICE_ACCOUNT}:roles/storage.admin $BUCKET_NAME\n",
- "\n",
- "! gcloud config set project $PROJECT_ID\n",
- "! gcloud projects add-iam-policy-binding --no-user-output-enabled {PROJECT_ID} --member=serviceAccount:{SERVICE_ACCOUNT} --role=\"roles/storage.admin\"\n",
- "! gcloud projects add-iam-policy-binding --no-user-output-enabled {PROJECT_ID} --member=serviceAccount:{SERVICE_ACCOUNT} --role=\"roles/aiplatform.user\""
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "language": "python",
- "metadata": {
- "cellView": "form",
- "id": "pNHMbjr0UjrK"
- },
- "outputs": [],
- "source": [
- "# @title Evaluate Gemma models\n",
- "\n",
- "# @markdown This section demonstrates how to evaluate the Gemma models with and without finetuned LoRA adapters using EleutherAI's [Language Model Evaluation Harness (lm-evaluation-harness)](https://github.com/EleutherAI/lm-evaluation-harness) with Vertex CustomJob. Refer the peak GPU memory usage for serving and adjust the machine type, accelerator type and accelerator count accordingly.\n",
- "\n",
- "# @markdown You must provide a Hugging Face User Access Token (with read access) to access the Gemma models. You can follow the [Hugging Face documentation](https://huggingface.co/docs/hub/en/security-tokens) to create a **read** access token and put it in the `HF_TOKEN` field below.\n",
- "HF_TOKEN = \"\" # @param {type:\"string\", isTemplate:true}\n",
- "\n",
- "# @markdown This example uses the dataset [HellaSwag](https://arxiv.org/abs/1905.07830). All supported tasks are listed in [this task table](https://github.com/EleutherAI/lm-evaluation-harness/blob/master/docs/task_table.md).\n",
- "# @markdown Set evaluation dataset.\n",
- "eval_dataset = \"hellaswag\" # @param {type:\"string\"}\n",
- "\n",
- "# Worker pool spec.\n",
- "# Find Vertex AI supported accelerators and regions in:\n",
- "# https://cloud.google.com/vertex-ai/docs/training/configure-compute\n",
- "\n",
- "\n",
- "# Setup evaluation job.\n",
- "# @markdown Set the base model id.\n",
- "base_model_id = \"google/gemma-1.1-2b-it\" # @param[\"google/gemma-2b\", \"google/gemma-2b-it\", \"google/gemma-7b\", \"google/gemma-7b-it\", \"google/gemma-1.1-2b-it\", \"google/gemma-1.1-7b-it\"] {isTemplate:true}\n",
- "job_name = common_util.get_job_name_with_datetime(prefix=\"gemma-eval\")\n",
- "eval_output_dir = os.path.join(MODEL_BUCKET, job_name)\n",
- "eval_output_dir_gcsfuse = eval_output_dir.replace(\"gs://\", \"/gcs/\")\n",
- "\n",
- "# @markdown Set the accelerator type.\n",
- "accelerator_type = \"NVIDIA_L4\" # @param[\"NVIDIA_TESLA_V100\", \"NVIDIA_L4\", \"NVIDIA_TESLA_A100\"]\n",
- "\n",
- "# @markdown To evaluate a PEFT-finetuned model, enter the PEFT output directory to the LoRA adapter below.\n",
- "# @markdown Otherwise, leave it empty.\n",
- "# @markdown See the [finetuning notebook](https://github.com/GoogleCloudPlatform/vertex-ai-samples/blob/main/notebooks/community/model_garden/model_garden_gemma_finetuning_on_vertex.ipynb) for more details.\n",
- "# @markdown Set the PEFT output directory.\n",
- "peft_output_dir = \"\" # @param {type:\"string\"}\n",
- "peft_output_dir_gcsfuse = peft_output_dir.replace(\"gs://\", \"/gcs/\")\n",
- "\n",
- "if accelerator_type == \"NVIDIA_TESLA_A100\":\n",
- " machine_type = \"a2-highgpu-1g\"\n",
- " accelerator_count = 1\n",
- "elif accelerator_type == \"NVIDIA_TESLA_V100\":\n",
- " machine_type = \"n1-standard-8\"\n",
- " accelerator_count = 2\n",
- "elif accelerator_type == \"NVIDIA_L4\":\n",
- " machine_type = \"g2-standard-8\"\n",
- " accelerator_count = 1\n",
- "else:\n",
- " print(f\"Unsupported accelerator type: {accelerator_type}\")\n",
- "\n",
- "replica_count = 1\n",
- "\n",
- "common_util.check_quota(\n",
- " project_id=PROJECT_ID,\n",
- " region=REGION,\n",
- " accelerator_type=accelerator_type,\n",
- " accelerator_count=accelerator_count,\n",
- " is_for_training=True,\n",
- ")\n",
- "\n",
- "# Prepare evaluation command that runs the evaluation harness.\n",
- "# Set `trust_remote_code = True` because evaluating the model requires\n",
- "# executing code from the model repository.\n",
- "# Set `use_accelerate = True` to enable evaluation across multiple GPUs.\n",
- "eval_command = [\n",
- " \"lm_eval\",\n",
- " \"--model\",\n",
- " \"hf\",\n",
- " \"--tasks\",\n",
- " f\"{eval_dataset}\",\n",
- " \"--output_path\",\n",
- " f\"{eval_output_dir_gcsfuse}\",\n",
- "]\n",
- "\n",
- "if peft_output_dir_gcsfuse:\n",
- " eval_command += [\n",
- " \"--model_args\",\n",
- " f\"pretrained={base_model_id},peft={peft_output_dir_gcsfuse},trust_remote_code=True,parallelize=True\",\n",
- " ]\n",
- "else:\n",
- " eval_command += [\n",
- " \"--model_args\",\n",
- " f\"pretrained={base_model_id},trust_remote_code=True,parallelize=True\",\n",
- " ]\n",
- "\n",
- "\n",
- "# The evaluation docker image.\n",
- "EVAL_DOCKER_URI = \"us-docker.pkg.dev/vertex-ai/vertex-vision-model-garden-dockers/pytorch-lm-evaluation-harness:20241016_0934_RC00\"\n",
- "\n",
- "# Pass evaluation arguments and launch job.\n",
- "worker_pool_specs = [\n",
- " {\n",
- " \"machine_spec\": {\n",
- " \"machine_type\": machine_type,\n",
- " \"accelerator_type\": accelerator_type,\n",
- " \"accelerator_count\": accelerator_count,\n",
- " },\n",
- " \"replica_count\": replica_count,\n",
- " \"disk_spec\": {\n",
- " \"boot_disk_size_gb\": 500,\n",
- " },\n",
- " \"container_spec\": {\n",
- " \"image_uri\": EVAL_DOCKER_URI,\n",
- " \"env\": [\n",
- " {\n",
- " \"name\": \"HF_TOKEN\",\n",
- " \"value\": HF_TOKEN,\n",
- " }\n",
- " ],\n",
- " \"command\": eval_command,\n",
- " \"args\": [],\n",
- " },\n",
- " }\n",
- "]\n",
- "\n",
- "eval_job = aiplatform.CustomJob(\n",
- " display_name=job_name,\n",
- " worker_pool_specs=worker_pool_specs,\n",
- " base_output_dir=eval_output_dir,\n",
- ")\n",
- "\n",
- "eval_job.run()\n",
- "\n",
- "print(\"Evaluation results were saved in:\", eval_output_dir)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "CVBxGpwWU3kY"
- },
- "outputs": [],
- "source": [
- "# @title Fetch and print evaluation results\n",
- "import json\n",
- "import re\n",
- "\n",
- "from google.cloud import storage\n",
- "\n",
- "# Fetch evaluation results.\n",
- "storage_client = storage.Client()\n",
- "BUCKET_NAME = BUCKET_URI.split(\"gs://\")[1]\n",
- "bucket = storage_client.get_bucket(BUCKET_NAME)\n",
- "\n",
- "blobs = [b.name for b in bucket.list_blobs()]\n",
- "\n",
- "result_file_path = None\n",
- "for file_path in filter(re.compile(\".*/*.json\").match, blobs):\n",
- " result_file_path = file_path\n",
- " print(f\"Found result file: {file_path}\")\n",
- "\n",
- "if result_file_path is None:\n",
- " raise ValueError(\"No result file found.\")\n",
- "\n",
- "blob = bucket.blob(result_file_path)\n",
- "raw_result = blob.download_as_string()\n",
- "\n",
- "# Print evaluation results.\n",
- "result = json.loads(raw_result)\n",
- "result_formatted = json.dumps(result, indent=2)\n",
- "print(f\"Evaluation result:\\n{result_formatted}\")"
- ]
- },
- {
- "cell_type": "markdown",
- "execution_count": null,
- "metadata": {
- "id": "unjukbcjEBOd"
- },
- "outputs": [],
- "source": [
- "## Clean up resources"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "qWN3cl_VU7pa"
- },
- "outputs": [],
- "source": [
- "# Delete evaluation job.\n",
- "\n",
- "delete_bucket = False # @param {type:\"boolean\"}\n",
- "if delete_bucket:\n",
- " ! gsutil -m rm -r $BUCKET_URI\n",
- " # Uncomment below to delete all artifacts\n",
- " # !gsutil -m rm -r $STAGING_BUCKET $MODEL_BUCKET $EXPERIMENT_BUCKET\n",
- "\n",
- "eval_job.delete()"
- ]
- }
- ],
- "metadata": {
- "colab": {
- "name": "model_garden_gemma_evaluation.ipynb",
- "toc_visible": true
- },
- "kernelspec": {
- "display_name": "Python 3",
- "name": "python3"
- }
+ "cells": [
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "language": "python",
+ "metadata": {
+ "cellView": "form",
+ "id": "B8S-yo8qTIcO"
+ },
+ "outputs": [],
+ "source": [
+ "# Copyright 2026 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."
+ ]
},
- "nbformat": 4,
- "nbformat_minor": 0
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "MTRywGxLTZfU"
+ },
+ "source": [
+ "# Vertex AI Model Garden - Gemma Evaluation\n",
+ "\n",
+ "\n",
+ " \n",
+ " \n",
+ "  Run in Colab Enterprise\n",
+ " \n",
+ " | \n",
+ " \n",
+ " \n",
+ "  View on GitHub\n",
+ " \n",
+ " | \n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "2CXS0vZfT8_7"
+ },
+ "source": [
+ "## Overview\n",
+ "\n",
+ "This notebook demonstrates evaluating pre-trained and instruction-tuned Gemma models in Vertex AI.\n",
+ "\n",
+ "### Objective\n",
+ "\n",
+ "- Evaluate pre-trained and instruction-tuned Gemma model on any of the benchmark datasets\n",
+ "- Clean up the resources\n",
+ "\n",
+ "| Models |\n",
+ "| :- |\n",
+ "| [google/gemma-2b](https://huggingface.co/google/gemma-2b)\n",
+ "| [google/gemma-2b-it](https://huggingface.co/google/gemma-2b-it)\n",
+ "| [google/gemma-7b](https://huggingface.co/google/gemma-7b)\n",
+ "| [google/gemma-7b-it](https://huggingface.co/google/gemma-7b-it)\n",
+ "| [google/gemma-1.1-2b-it](https://huggingface.co/google/gemma-1.1-2b-it)\n",
+ "| [google/gemma-1.1-7b-it](https://huggingface.co/google/gemma-1.1-7b-it)\n",
+ "\n",
+ "### Costs\n",
+ "\n",
+ "This tutorial uses billable components of Google Cloud:\n",
+ "\n",
+ "* Vertex AI\n",
+ "* Cloud Storage\n",
+ "\n",
+ "Learn about [Vertex AI pricing](https://cloud.google.com/vertex-ai/pricing), [Cloud Storage pricing](https://cloud.google.com/storage/pricing), and use the [Pricing Calculator](https://cloud.google.com/products/calculator/) to generate a cost estimate based on your projected usage."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "HCY8PGrFUbT1"
+ },
+ "source": [
+ "## Run the notebook"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "language": "python",
+ "metadata": {
+ "cellView": "form",
+ "id": "81CC3tL1T_TL"
+ },
+ "outputs": [],
+ "source": [
+ "# @title Setup Google Cloud project\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]** [Create a Cloud Storage bucket](https://cloud.google.com/storage/docs/creating-buckets) for storing experiment outputs. Set the BUCKET_URI for the experiment environment. The specified Cloud Storage bucket (`BUCKET_URI`) should be located in the same region as where the notebook was launched. Note that a multi-region bucket (eg. \"us\") is not considered a match for a single region covered by the multi-region range (eg. \"us-central1\"). If not set, a unique GCS bucket will be created instead.\n",
+ "\n",
+ "BUCKET_URI = \"gs://\" # @param {type:\"string\"}\n",
+ "\n",
+ "# @markdown 3. **[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",
+ "# Import the necessary packages\n",
+ "\n",
+ "! git clone https://github.com/GoogleCloudPlatform/vertex-ai-samples.git\n",
+ "\n",
+ "import datetime\n",
+ "import importlib\n",
+ "import os\n",
+ "import uuid\n",
+ "\n",
+ "from google.cloud import aiplatform\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",
+ "models, endpoints = {}, {}\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",
+ " if not os.environ.get(\"GOOGLE_CLOUD_REGION\"):\n",
+ " raise ValueError(\n",
+ " \"REGION must be set. See\"\n",
+ " \" https://cloud.google.com/vertex-ai/docs/general/locations for\"\n",
+ " \" available cloud locations.\"\n",
+ " )\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",
+ "# Cloud Storage bucket for storing the experiment artifacts.\n",
+ "# A unique GCS bucket will be created for the purpose of this notebook. If you\n",
+ "# prefer using your own GCS bucket, change the value yourself below.\n",
+ "now = datetime.datetime.now().strftime(\"%Y%m%d%H%M%S\")\n",
+ "BUCKET_NAME = \"/\".join(BUCKET_URI.split(\"/\")[:3])\n",
+ "\n",
+ "if BUCKET_URI is None or BUCKET_URI.strip() == \"\" or BUCKET_URI == \"gs://\":\n",
+ " BUCKET_URI = f\"gs://{PROJECT_ID}-tmp-{now}-{str(uuid.uuid4())[:4]}\"\n",
+ " BUCKET_NAME = \"/\".join(BUCKET_URI.split(\"/\")[:3])\n",
+ " ! gsutil mb -l {REGION} {BUCKET_URI}\n",
+ "else:\n",
+ " assert BUCKET_URI.startswith(\"gs://\"), \"BUCKET_URI must start with `gs://`.\"\n",
+ " shell_output = ! gsutil ls -Lb {BUCKET_NAME} | grep \"Location constraint:\" | sed \"s/Location constraint://\"\n",
+ " bucket_region = shell_output[0].strip().lower()\n",
+ " if bucket_region != REGION:\n",
+ " raise ValueError(\n",
+ " \"Bucket region %s is different from notebook region %s\"\n",
+ " % (bucket_region, REGION)\n",
+ " )\n",
+ "print(f\"Using this GCS Bucket: {BUCKET_URI}\")\n",
+ "\n",
+ "STAGING_BUCKET = os.path.join(BUCKET_URI, \"temporal\")\n",
+ "MODEL_BUCKET = os.path.join(BUCKET_URI, \"gemma\")\n",
+ "\n",
+ "\n",
+ "# Initialize Vertex AI API.\n",
+ "print(\"Initializing Vertex AI API.\")\n",
+ "aiplatform.init(project=PROJECT_ID, location=REGION, staging_bucket=STAGING_BUCKET)\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",
+ "\n",
+ "# Provision permissions to the SERVICE_ACCOUNT with the GCS bucket\n",
+ "! gsutil iam ch serviceAccount:{SERVICE_ACCOUNT}:roles/storage.admin $BUCKET_NAME\n",
+ "\n",
+ "! gcloud config set project $PROJECT_ID\n",
+ "! gcloud projects add-iam-policy-binding --no-user-output-enabled {PROJECT_ID} --member=serviceAccount:{SERVICE_ACCOUNT} --role=\"roles/storage.admin\"\n",
+ "! gcloud projects add-iam-policy-binding --no-user-output-enabled {PROJECT_ID} --member=serviceAccount:{SERVICE_ACCOUNT} --role=\"roles/aiplatform.user\""
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "language": "python",
+ "metadata": {
+ "cellView": "form",
+ "id": "pNHMbjr0UjrK"
+ },
+ "outputs": [],
+ "source": [
+ "# @title Evaluate Gemma models\n",
+ "\n",
+ "# @markdown This section demonstrates how to evaluate the Gemma models with and without finetuned LoRA adapters using EleutherAI's [Language Model Evaluation Harness (lm-evaluation-harness)](https://github.com/EleutherAI/lm-evaluation-harness) with Vertex CustomJob. Refer the peak GPU memory usage for serving and adjust the machine type, accelerator type and accelerator count accordingly.\n",
+ "\n",
+ "# @markdown You must provide a Hugging Face User Access Token (with read access) to access the Gemma models. You can follow the [Hugging Face documentation](https://huggingface.co/docs/hub/en/security-tokens) to create a **read** access token and put it in the `HF_TOKEN` field below.\n",
+ "HF_TOKEN = \"\" # @param {type:\"string\", isTemplate:true}\n",
+ "\n",
+ "# @markdown This example uses the dataset [HellaSwag](https://arxiv.org/abs/1905.07830). All supported tasks are listed in [this task table](https://github.com/EleutherAI/lm-evaluation-harness/blob/master/docs/task_table.md).\n",
+ "# @markdown Set evaluation dataset.\n",
+ "eval_dataset = \"hellaswag\" # @param {type:\"string\"}\n",
+ "\n",
+ "# Worker pool spec.\n",
+ "# Find Vertex AI supported accelerators and regions in:\n",
+ "# https://cloud.google.com/vertex-ai/docs/training/configure-compute\n",
+ "\n",
+ "\n",
+ "# Setup evaluation job.\n",
+ "# @markdown Set the base model id.\n",
+ "base_model_id = \"google/gemma-1.1-2b-it\" # @param[\"google/gemma-2b\", \"google/gemma-2b-it\", \"google/gemma-7b\", \"google/gemma-7b-it\", \"google/gemma-1.1-2b-it\", \"google/gemma-1.1-7b-it\"] {isTemplate:true}\n",
+ "job_name = common_util.get_job_name_with_datetime(prefix=\"gemma-eval\")\n",
+ "eval_output_dir = os.path.join(MODEL_BUCKET, job_name)\n",
+ "eval_output_dir_gcsfuse = eval_output_dir.replace(\"gs://\", \"/gcs/\")\n",
+ "\n",
+ "# @markdown Set the accelerator type.\n",
+ "accelerator_type = \"NVIDIA_L4\" # @param[\"NVIDIA_TESLA_V100\", \"NVIDIA_L4\", \"NVIDIA_TESLA_A100\"]\n",
+ "\n",
+ "# @markdown To evaluate a PEFT-finetuned model, enter the PEFT output directory to the LoRA adapter below.\n",
+ "# @markdown Otherwise, leave it empty.\n",
+ "# @markdown See the [finetuning notebook](https://github.com/GoogleCloudPlatform/vertex-ai-samples/blob/main/notebooks/community/model_garden/model_garden_gemma_finetuning_on_vertex.ipynb) for more details.\n",
+ "# @markdown Set the PEFT output directory.\n",
+ "peft_output_dir = \"\" # @param {type:\"string\"}\n",
+ "peft_output_dir_gcsfuse = peft_output_dir.replace(\"gs://\", \"/gcs/\")\n",
+ "\n",
+ "if accelerator_type == \"NVIDIA_TESLA_A100\":\n",
+ " machine_type = \"a2-highgpu-1g\"\n",
+ " accelerator_count = 1\n",
+ "elif accelerator_type == \"NVIDIA_TESLA_V100\":\n",
+ " machine_type = \"n1-standard-8\"\n",
+ " accelerator_count = 2\n",
+ "elif accelerator_type == \"NVIDIA_L4\":\n",
+ " machine_type = \"g2-standard-8\"\n",
+ " accelerator_count = 1\n",
+ "else:\n",
+ " print(f\"Unsupported accelerator type: {accelerator_type}\")\n",
+ "\n",
+ "replica_count = 1\n",
+ "\n",
+ "common_util.check_quota(\n",
+ " project_id=PROJECT_ID,\n",
+ " region=REGION,\n",
+ " accelerator_type=accelerator_type,\n",
+ " accelerator_count=accelerator_count,\n",
+ " is_for_training=True,\n",
+ ")\n",
+ "\n",
+ "# Prepare evaluation command that runs the evaluation harness.\n",
+ "# Set `trust_remote_code = True` because evaluating the model requires\n",
+ "# executing code from the model repository.\n",
+ "# Set `use_accelerate = True` to enable evaluation across multiple GPUs.\n",
+ "eval_command = [\n",
+ " \"lm_eval\",\n",
+ " \"--model\",\n",
+ " \"hf\",\n",
+ " \"--tasks\",\n",
+ " f\"{eval_dataset}\",\n",
+ " \"--output_path\",\n",
+ " f\"{eval_output_dir_gcsfuse}\",\n",
+ "]\n",
+ "\n",
+ "if peft_output_dir_gcsfuse:\n",
+ " eval_command += [\n",
+ " \"--model_args\",\n",
+ " f\"pretrained={base_model_id},peft={peft_output_dir_gcsfuse},trust_remote_code=True,parallelize=True\",\n",
+ " ]\n",
+ "else:\n",
+ " eval_command += [\n",
+ " \"--model_args\",\n",
+ " f\"pretrained={base_model_id},trust_remote_code=True,parallelize=True\",\n",
+ " ]\n",
+ "\n",
+ "\n",
+ "# The evaluation docker image.\n",
+ "EVAL_DOCKER_URI = \"us-docker.pkg.dev/vertex-ai/vertex-vision-model-garden-dockers/pytorch-lm-evaluation-harness:20241016_0934_RC00\"\n",
+ "\n",
+ "# Pass evaluation arguments and launch job.\n",
+ "worker_pool_specs = [\n",
+ " {\n",
+ " \"machine_spec\": {\n",
+ " \"machine_type\": machine_type,\n",
+ " \"accelerator_type\": accelerator_type,\n",
+ " \"accelerator_count\": accelerator_count,\n",
+ " },\n",
+ " \"replica_count\": replica_count,\n",
+ " \"disk_spec\": {\n",
+ " \"boot_disk_size_gb\": 500,\n",
+ " },\n",
+ " \"container_spec\": {\n",
+ " \"image_uri\": EVAL_DOCKER_URI,\n",
+ " \"env\": [\n",
+ " {\n",
+ " \"name\": \"HF_TOKEN\",\n",
+ " \"value\": HF_TOKEN,\n",
+ " }\n",
+ " ],\n",
+ " \"command\": eval_command,\n",
+ " \"args\": [],\n",
+ " },\n",
+ " }\n",
+ "]\n",
+ "\n",
+ "eval_job = aiplatform.CustomJob(\n",
+ " display_name=job_name,\n",
+ " worker_pool_specs=worker_pool_specs,\n",
+ " base_output_dir=eval_output_dir,\n",
+ ")\n",
+ "\n",
+ "eval_job.run()\n",
+ "\n",
+ "print(\"Evaluation results were saved in:\", eval_output_dir)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "cellView": "form",
+ "id": "CVBxGpwWU3kY"
+ },
+ "outputs": [],
+ "source": [
+ "# @title Fetch and print evaluation results\n",
+ "import json\n",
+ "import re\n",
+ "\n",
+ "from google.cloud import storage\n",
+ "\n",
+ "# Fetch evaluation results.\n",
+ "storage_client = storage.Client()\n",
+ "BUCKET_NAME = BUCKET_URI.split(\"gs://\")[1]\n",
+ "bucket = storage_client.get_bucket(BUCKET_NAME)\n",
+ "\n",
+ "blobs = [b.name for b in bucket.list_blobs()]\n",
+ "\n",
+ "result_file_path = None\n",
+ "for file_path in filter(re.compile(\".*/*.json\").match, blobs):\n",
+ " result_file_path = file_path\n",
+ " print(f\"Found result file: {file_path}\")\n",
+ "\n",
+ "if result_file_path is None:\n",
+ " raise ValueError(\"No result file found.\")\n",
+ "\n",
+ "blob = bucket.blob(result_file_path)\n",
+ "raw_result = blob.download_as_string()\n",
+ "\n",
+ "# Print evaluation results.\n",
+ "result = json.loads(raw_result)\n",
+ "result_formatted = json.dumps(result, indent=2)\n",
+ "print(f\"Evaluation result:\\n{result_formatted}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "execution_count": null,
+ "metadata": {
+ "id": "unjukbcjEBOd"
+ },
+ "outputs": [],
+ "source": [
+ "## Clean up resources"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "cellView": "form",
+ "id": "qWN3cl_VU7pa"
+ },
+ "outputs": [],
+ "source": [
+ "# Delete evaluation job.\n",
+ "\n",
+ "delete_bucket = False # @param {type:\"boolean\"}\n",
+ "if delete_bucket:\n",
+ " ! gsutil -m rm -r $BUCKET_URI\n",
+ " # Uncomment below to delete all artifacts\n",
+ " # !gsutil -m rm -r $STAGING_BUCKET $MODEL_BUCKET $EXPERIMENT_BUCKET\n",
+ "\n",
+ "eval_job.delete()"
+ ]
+ }
+ ],
+ "metadata": {
+ "colab": {
+ "name": "model_garden_gemma_evaluation.ipynb",
+ "toc_visible": true
+ },
+ "kernelspec": {
+ "display_name": "Python 3",
+ "name": "python3"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
}
diff --git a/notebooks/community/model_garden/model_garden_gemma_finetuning_on_vertex.ipynb b/notebooks/community/model_garden/model_garden_gemma_finetuning_on_vertex.ipynb
deleted file mode 100644
index eae0cc3c5..000000000
--- a/notebooks/community/model_garden/model_garden_gemma_finetuning_on_vertex.ipynb
+++ /dev/null
@@ -1,964 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "7d9bbf86da5e"
- },
- "outputs": [],
- "source": [
- "# Copyright 2026 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": "99c1c3fc2ca5"
- },
- "source": [
- "# Vertex AI Model Garden - Gemma Finetuning\n",
- "\n",
- "\n",
- " \n",
- " \n",
- "  Run in Colab Enterprise\n",
- " \n",
- " | \n",
- " \n",
- " \n",
- "  View on GitHub\n",
- " \n",
- " | \n",
- "
"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "3de7470326a2"
- },
- "source": [
- "## Overview\n",
- "\n",
- "This notebook demonstrates finetuning and deploying Gemma models with [Vertex AI Custom Training Job](https://cloud.google.com/vertex-ai/docs/training/create-custom-job). All of the examples in this notebook use parameter efficient finetuning methods [PEFT (LoRA)](https://github.com/huggingface/peft) to reduce training and storage costs. LoRA (Low-Rank Adaptation) is one approach of Parameter Efficient FineTuning (PEFT), where pretrained model weights are frozen and rank decomposition matrices representing the change in model weights are trained during finetuning. Read more about LoRA in the following publication: [Hu, E.J., Shen, Y., Wallis, P., Allen-Zhu, Z., Li, Y., Wang, S., Wang, L. and Chen, W., 2021. Lora: Low-rank adaptation of large language models. *arXiv preprint arXiv:2106.09685*](https://arxiv.org/abs/2106.09685).\n",
- "\n",
- "\n",
- "After tuning, we can deploy models on Vertex.\n",
- "\n",
- "\n",
- "### Objective\n",
- "\n",
- "- Finetune and deploy Gemma models with Vertex AI Custom Training Jobs.\n",
- "- Send prediction requests to your finetuned Gemma model.\n",
- "\n",
- "\n",
- "### Costs\n",
- "\n",
- "This tutorial uses billable components of Google Cloud:\n",
- "\n",
- "* Vertex AI\n",
- "* Cloud Storage\n",
- "\n",
- "Learn about [Vertex AI pricing](https://cloud.google.com/vertex-ai/pricing), [Cloud Storage pricing](https://cloud.google.com/storage/pricing), and use the [Pricing Calculator](https://cloud.google.com/products/calculator/) to generate a cost estimate based on your projected usage."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "264c07757582"
- },
- "source": [
- "## Before you begin"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "BNAlJh_pGxbL"
- },
- "outputs": [],
- "source": [
- "# @title Install Python Packages for Finetuning\n",
- "\n",
- "# @markdown 1. Install google-cloud-aiplatform package and restart the session if instructed.\n",
- "! pip install --upgrade --quiet google-cloud-aiplatform==1.130.0\n",
- "\n",
- "# @markdown 2. Install packages to validate dataset with template.\n",
- "! pip install --upgrade --quiet accelerate==0.31.0\n",
- "! pip install --upgrade --quiet transformers==4.43.1\n",
- "! pip install --upgrade --quiet datasets==2.19.2\n",
- "\n",
- "# Load local tensorboard.\n",
- "%load_ext tensorboard"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "8CQcnBfWvc-f"
- },
- "outputs": [],
- "source": [
- "# @title Setup Google Cloud project\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. For finetuning, **[click here](https://console.cloud.google.com/iam-admin/quotas?location=us-central1&metric=aiplatform.googleapis.com%2Frestricted_image_training_nvidia_a100_80gb_gpus)** to check if your project already has the required 8 Nvidia A100 80 GB GPUs in the us-central1 region. If yes, then run this notebook in the us-central1 region. If you do not have 8 Nvidia A100 80 GPUs or have more GPU requirements than this, then schedule your job with Nvidia H100 GPUs via Dynamic Workload Scheduler using [these instructions](https://cloud.google.com/vertex-ai/docs/training/schedule-jobs-dws). For Dynamic Workload Scheduler, check the [us-central1](https://console.cloud.google.com/iam-admin/quotas?location=us-central1&metric=aiplatform.googleapis.com%2Fcustom_model_training_preemptible_nvidia_h100_gpus) or [europe-west4](https://console.cloud.google.com/iam-admin/quotas?location=europe-west4&metric=aiplatform.googleapis.com%2Fcustom_model_training_preemptible_nvidia_h100_gpus) quota for Nvidia H100 GPUs. If you do not have enough GPUs, then you can follow [these instructions](https://cloud.google.com/docs/quotas/view-manage#viewing_your_quota_console) to request quota.\n",
- "\n",
- "# @markdown 3. For serving, **[click here](https://console.cloud.google.com/iam-admin/quotas?location=us-central1&metric=aiplatform.googleapis.com%2Fcustom_model_serving_nvidia_l4_gpus)** to check if your project already has the required 1 L4 GPU in the us-central1 region. If yes, then run this notebook in the us-central1 region. If you need more L4 GPUs for your project, then you can follow [these instructions](https://cloud.google.com/docs/quotas/view-manage#viewing_your_quota_console) to request more. Alternatively, 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).\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",
- "# @markdown 4. **[Optional]** [Create a Cloud Storage bucket](https://cloud.google.com/storage/docs/creating-buckets) for storing experiment outputs. Set the BUCKET_URI for the experiment environment. The specified Cloud Storage bucket (`BUCKET_URI`) should be located in the same region as where the notebook was launched. Note that a multi-region bucket (eg. \"us\") is not considered a match for a single region covered by the multi-region range (eg. \"us-central1\"). If not set, a unique GCS bucket will be created instead.\n",
- "\n",
- "BUCKET_URI = \"gs://\" # @param {type:\"string\"}\n",
- "\n",
- "# @markdown 5. **[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",
- "# Import the necessary packages\n",
- "! rm -rf vertex-ai-samples && git clone https://github.com/GoogleCloudPlatform/vertex-ai-samples.git\n",
- "! cd vertex-ai-samples && git reset --hard 7ae13b346a72ee2a2dc8152dd40c6ddd72d6c810\n",
- "\n",
- "import datetime\n",
- "import importlib\n",
- "import os\n",
- "import uuid\n",
- "from typing import Tuple\n",
- "\n",
- "from google.cloud import aiplatform\n",
- "from google.cloud.aiplatform.compat.types import \\\n",
- " custom_job as gca_custom_job_compat\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",
- "models, endpoints = {}, {}\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",
- " if not os.environ.get(\"GOOGLE_CLOUD_REGION\"):\n",
- " raise ValueError(\n",
- " \"REGION must be set. See\"\n",
- " \" https://cloud.google.com/vertex-ai/docs/general/locations for\"\n",
- " \" available cloud locations.\"\n",
- " )\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",
- "# Cloud Storage bucket for storing the experiment artifacts.\n",
- "# A unique GCS bucket will be created for the purpose of this notebook. If you\n",
- "# prefer using your own GCS bucket, change the value yourself below.\n",
- "now = datetime.datetime.now().strftime(\"%Y%m%d%H%M%S\")\n",
- "BUCKET_NAME = \"/\".join(BUCKET_URI.split(\"/\")[:3])\n",
- "\n",
- "if BUCKET_URI is None or BUCKET_URI.strip() == \"\" or BUCKET_URI == \"gs://\":\n",
- " BUCKET_URI = f\"gs://{PROJECT_ID}-tmp-{now}-{str(uuid.uuid4())[:4]}\"\n",
- " BUCKET_NAME = \"/\".join(BUCKET_URI.split(\"/\")[:3])\n",
- " ! gsutil mb -l {REGION} {BUCKET_URI}\n",
- "else:\n",
- " assert BUCKET_URI.startswith(\"gs://\"), \"BUCKET_URI must start with `gs://`.\"\n",
- " shell_output = ! gsutil ls -Lb {BUCKET_NAME} | grep \"Location constraint:\" | sed \"s/Location constraint://\"\n",
- " bucket_region = shell_output[0].strip().lower()\n",
- " if bucket_region != REGION:\n",
- " raise ValueError(\n",
- " \"Bucket region %s is different from notebook region %s\"\n",
- " % (bucket_region, REGION)\n",
- " )\n",
- "print(f\"Using this GCS Bucket: {BUCKET_URI}\")\n",
- "\n",
- "STAGING_BUCKET = os.path.join(BUCKET_URI, \"temporal\")\n",
- "MODEL_BUCKET = os.path.join(BUCKET_URI, \"gemma\")\n",
- "\n",
- "\n",
- "# Initialize Vertex AI API.\n",
- "print(\"Initializing Vertex AI API.\")\n",
- "aiplatform.init(project=PROJECT_ID, location=REGION, staging_bucket=STAGING_BUCKET)\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",
- "\n",
- "# Provision permissions to the SERVICE_ACCOUNT with the GCS bucket\n",
- "! gsutil iam ch serviceAccount:{SERVICE_ACCOUNT}:roles/storage.admin $BUCKET_NAME\n",
- "\n",
- "! gcloud config set project $PROJECT_ID\n",
- "! gcloud projects add-iam-policy-binding --no-user-output-enabled {PROJECT_ID} --member=serviceAccount:{SERVICE_ACCOUNT} --role=\"roles/storage.admin\"\n",
- "! gcloud projects add-iam-policy-binding --no-user-output-enabled {PROJECT_ID} --member=serviceAccount:{SERVICE_ACCOUNT} --role=\"roles/aiplatform.user\"\n",
- "\n",
- "# @markdown ## Access Gemma Models\n",
- "# @markdown For GPU based finetuning and serving, choose between accessing Gemma models on [Hugging Face](https://huggingface.co/)\n",
- "# @markdown or Vertex AI as described below.\n",
- "\n",
- "# @markdown If you already obtained access to Gemma models on [Hugging Face](https://huggingface.co/), you can load models from there.\n",
- "# @markdown Alternatively, you can also load the original Gemma models for finetuning and serving from Vertex AI after accepting the agreement.\n",
- "\n",
- "# @markdown **Select and fill one of the three following sections.**\n",
- "LOAD_MODEL_FROM = \"Hugging Face\" # @param [\"Hugging Face\", \"Google Cloud\"] {isTemplate:true}\n",
- "\n",
- "# @markdown ---\n",
- "\n",
- "# @markdown ### Access Gemma models on Hugging Face for GPU based finetuning and serving\n",
- "# @markdown You must provide a Hugging Face User Access Token (with read access) to access the Gemma models. You can follow the [Hugging Face documentation](https://huggingface.co/docs/hub/en/security-tokens) to create a **read** access token and put it in the `HF_TOKEN` field below.\n",
- "\n",
- "HF_TOKEN = \"\" # @param {type:\"string\", isTemplate:true}\n",
- "if LOAD_MODEL_FROM == \"Hugging Face\":\n",
- " assert (\n",
- " HF_TOKEN\n",
- " ), \"Provide a read HF_TOKEN to load models from Hugging Face, or select a different model source.\"\n",
- "\n",
- "# @markdown *--- Or ---*\n",
- "# @markdown ### Access Gemma models on Vertex AI for GPU based finetuning and serving\n",
- "# @markdown Accept the model agreement to access the models:\n",
- "# @markdown 1. Open the [Gemma model card](https://console.cloud.google.com/vertex-ai/publishers/google/model-garden/335) from [Vertex AI Model Garden](https://cloud.google.com/model-garden).\n",
- "# @markdown 2. Review the agreement on the model card page.\n",
- "# @markdown 3. After accepting the agreement of Gemma, a `https://` link containing Gemma pretrained and finetuned models will be shared.\n",
- "# @markdown 4. Paste the link in the `VERTEX_MODEL_GARDEN_GEMMA` field below.\n",
- "# @markdown **Note:** This will unzip and copy the Gemma model artifacts to your Cloud Storage bucket, which will take around 1 hour.\n",
- "\n",
- "VERTEX_AI_MODEL_GARDEN_GEMMA = \"\" # @param {type:\"string\", isTemplate:true}\n",
- "\n",
- "if LOAD_MODEL_FROM == \"Google Cloud\":\n",
- " assert (\n",
- " VERTEX_AI_MODEL_GARDEN_GEMMA\n",
- " ), \"Accept the agreement of Gemma in Vertex AI Model Garden and get the URL to Gemma model artifacts, or select a different model source.\"\n",
- "\n",
- " # Only use the last part in case a full command is pasted.\n",
- " signed_url = VERTEX_AI_MODEL_GARDEN_GEMMA.split(\" \")[-1].strip('\"')\n",
- "\n",
- " ! mkdir -p ./gemma\n",
- " ! curl -X GET \"{signed_url}\" | tar -xzvf - -C ./gemma/\n",
- " ! gsutil -m cp -R ./gemma/* {MODEL_BUCKET}\n",
- "\n",
- " model_path_prefix = MODEL_BUCKET\n",
- " HF_TOKEN = \"\"\n",
- "else:\n",
- " model_path_prefix = \"google/\"\n",
- "\n",
- "conversion_job = None"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "cb56d402e84a"
- },
- "source": [
- "## Finetune with HuggingFace PEFT and Deploy with vLLM on GPUs"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "KwAW99YZHTdy"
- },
- "outputs": [],
- "source": [
- "# @title Set dataset\n",
- "\n",
- "# @markdown Use the Vertex AI SDK to create and run the custom training jobs.\n",
- "\n",
- "# @markdown This notebook uses [timdettmers/openassistant-guanaco](https://huggingface.co/datasets/timdettmers/openassistant-guanaco) dataset as an example.\n",
- "# @markdown You can set `dataset_name` to any existing [Hugging Face dataset](https://huggingface.co/datasets) name, and set `instruct_column_in_dataset` to the name of the dataset column containing training data. The [timdettmers/openassistant-guanaco](https://huggingface.co/datasets/timdettmers/openassistant-guanaco) has only one column `text`, and therefore we set `instruct_column_in_dataset` to `text` in this notebook.\n",
- "\n",
- "# @markdown ### (Optional) Prepare a custom JSONL dataset for finetuning\n",
- "\n",
- "# @markdown You can prepare a JSONL file where each line is a valid JSON string as your custom training dataset. For example, here is one line from the [timdettmers/openassistant-guanaco](https://huggingface.co/datasets/timdettmers/openassistant-guanaco) dataset:\n",
- "# @markdown ```\n",
- "# @markdown {\"text\": \"### Human: Hola### Assistant: \\u00a1Hola! \\u00bfEn qu\\u00e9 puedo ayudarte hoy?\"}\n",
- "# @markdown ```\n",
- "\n",
- "# @markdown The JSON object has a key `text`, which should match `instruct_column_in_dataset`; The value should be one training data point, i.e. a string. After you prepared your JSONL file, you can either upload it to [Hugging Face datasets](https://huggingface.co/datasets) or [Google Cloud Storage](https://cloud.google.com/storage).\n",
- "\n",
- "# @markdown - To upload a JSONL dataset to [Hugging Face datasets](https://huggingface.co/datasets), follow the instructions on [Uploading Datasets](https://huggingface.co/docs/hub/en/datasets-adding). Then, set `dataset_name` to the name of your newly created dataset on Hugging Face.\n",
- "\n",
- "# @markdown - To upload a JSONL dataset to [Google Cloud Storage](https://cloud.google.com/storage), follow the instructions on [Upload objects from a filesystem](https://cloud.google.com/storage/docs/uploading-objects). Then, set `dataset_name` to the `gs://` URI to your JSONL file. For example: `gs://cloud-samples-data/vertex-ai/model-evaluation/peft_train_sample.jsonl`.\n",
- "\n",
- "# @markdown Optionally update the `instruct_column_in_dataset` field below if your JSON objects use a key other than the default `text`.\n",
- "\n",
- "# @markdown ### (Optional) Format your data with custom JSON template\n",
- "\n",
- "# @markdown Sometimes, your dataset might have multiple text columns and you want to construct the training data with a template. You can prepare a JSON template in the following format:\n",
- "\n",
- "# @markdown ```\n",
- "# @markdown {\n",
- "# @markdown \"description\": \"Template that accepts text-bison format.\",\n",
- "# @markdown \"source\": \"https://cloud.google.com/vertex-ai/generative-ai/docs/models/tune-text-models-supervised#dataset-format\",\n",
- "# @markdown \"prompt_input\": \"\\n\\n<|start_header_id|>user<|end_header_id|>\\n\\n{input_text}<|eot_id|>\\n\\n<|start_header_id|>assistant<|end_header_id|>\\n\\n{output_text}<|eot_id|>\",\n",
- "# @markdown \"instruction_separator\": \"<|start_header_id|>user<|end_header_id|>\\n\\n\",\n",
- "# @markdown \"response_separator\": \"<|start_header_id|>assistant<|end_header_id|>\\n\\n\"\n",
- "# @markdown }\n",
- "# @markdown ```\n",
- "\n",
- "# @markdown As an example, the template above can be used to format the following training data (this line comes from `gs://cloud-samples-data/vertex-ai/model-evaluation/peft_train_sample.jsonl`):\n",
- "\n",
- "# @markdown ```\n",
- "# @markdown {\"input_text\":\"TRANSCRIPT: \\nREASON FOR EVALUATION:,\\n\\n LABEL:\",\"output_text\":\"Chiropractic\"}\n",
- "# @markdown ```\n",
- "\n",
- "# @markdown This example template simply concatenates `input_text` with `output_text` with some special tokens in between.\n",
- "# @markdown\n",
- "# @markdown To try such custom dataset, you can make the following changes:\n",
- "# @markdown 1. Set `template` to `llama3-text-bison`\n",
- "# @markdown 1. Set `train_dataset_name` to `gs://cloud-samples-data/vertex-ai/model-evaluation/peft_train_sample.jsonl`\n",
- "# @markdown 1. Set `train_split_name` to `train`\n",
- "# @markdown 1. Set `eval_dataset_name` to `gs://cloud-samples-data/vertex-ai/model-evaluation/peft_eval_sample.jsonl`\n",
- "# @markdown 1. Set `eval_split_name` to `train` (**NOT** `test`)\n",
- "# @markdown 1. Set `instruct_column_in_dataset` as `input_text`.\n",
- "\n",
- "# Template name or gs:// URI to a custom template.\n",
- "template = \"openassistant-guanaco\" # @param {type:\"string\"}\n",
- "\n",
- "# Hugging Face dataset name or gs:// URI to a custom JSONL dataset.\n",
- "train_dataset_name = \"timdettmers/openassistant-guanaco\" # @param {type:\"string\"}\n",
- "train_split_name = \"train\" # @param {type:\"string\"}\n",
- "eval_dataset_name = \"timdettmers/openassistant-guanaco\" # @param {type:\"string\"}\n",
- "eval_split_name = \"test\" # @param {type:\"string\"}\n",
- "\n",
- "# Name of the dataset column containing training text input.\n",
- "instruct_column_in_dataset = \"text\" # @param {type:\"string\"}"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "SdiyOeyFGxbM"
- },
- "outputs": [],
- "source": [
- "# @title Set model\n",
- "\n",
- "# @markdown Select a model variant of Gemma 2.\n",
- "base_model_id = \"gemma-2b\" # @param[\"gemma-2b\", \"gemma-2b-it\", \"gemma-7b\", \"gemma-7b-it\", \"gemma-1.1-2b-it\", \"gemma-1.1-7b-it\"] {isTemplate:true}\n",
- "pretrained_model_id = os.path.join(model_path_prefix, base_model_id)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "R5PcRc0MGxbM"
- },
- "outputs": [],
- "source": [
- "# @title Validate Dataset with Template\n",
- "\n",
- "# @markdown This section validates the train and eval datasets with the template before starting the fine tuning process.\n",
- "\n",
- "import transformers\n",
- "\n",
- "dataset_validation_util = importlib.import_module(\n",
- " \"vertex-ai-samples.community-content.vertex_model_garden.model_oss.notebook_util.dataset_validation_util\"\n",
- ")\n",
- "\n",
- "if dataset_validation_util.is_gcs_path(pretrained_model_id):\n",
- " # Download tokenizer.\n",
- " ! mkdir tokenizer\n",
- " ! gsutil cp {pretrained_model_id}/tokenizer.json ./tokenizer\n",
- " ! gsutil cp {pretrained_model_id}/config.json ./tokenizer\n",
- " tokenizer_path = \"./tokenizer\"\n",
- " access_token = \"\"\n",
- "else:\n",
- " tokenizer_path = pretrained_model_id\n",
- " access_token = HF_TOKEN\n",
- "\n",
- "tokenizer = transformers.AutoTokenizer.from_pretrained(\n",
- " tokenizer_path,\n",
- " trust_remote_code=False,\n",
- " use_fast=True,\n",
- " token=access_token,\n",
- ")\n",
- "\n",
- "# Validate the train dataset.\n",
- "dataset_validation_util.validate_dataset_with_template(\n",
- " dataset_name=train_dataset_name,\n",
- " split=train_split_name,\n",
- " input_column=instruct_column_in_dataset,\n",
- " template=template,\n",
- " use_multiprocessing=False,\n",
- " tokenizer=tokenizer,\n",
- ")\n",
- "\n",
- "# Validate the eval dataset.\n",
- "dataset_validation_util.validate_dataset_with_template(\n",
- " dataset_name=eval_dataset_name,\n",
- " split=eval_split_name,\n",
- " input_column=instruct_column_in_dataset,\n",
- " template=template,\n",
- " use_multiprocessing=False,\n",
- " tokenizer=tokenizer,\n",
- ")"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "ivVGS9dHXPOz"
- },
- "outputs": [],
- "source": [
- "# @title Finetune\n",
- "# @markdown This section demonstrates how to finetune the Gemma model and merge the finetuned LoRA adapter with the base model on Vertex AI. It uses the Vertex AI SDK to create and run the custom training jobs.\n",
- "\n",
- "# @markdown The training job takes approximately between 10 to 20 mins to set-up. Once done, the training job is expected to take around 20 mins with the default configuration. To find the training time, throughput, and memory usage of your training job, you can go to the training logs and check the log line of the last training epoch.\n",
- "\n",
- "# @markdown **Note**:\n",
- "# @markdown 1. We recommend setting `finetuning_precision_mode` to `4bit` because it enables using fewer hardware resources for finetuning.\n",
- "# @markdown 1. If `max_steps > 0`, it takes precedence over `epochs`. One can set a small `max_steps` value to quickly check the pipeline.\n",
- "\n",
- "# Add labels for the finetuning job.\n",
- "labels = {\n",
- " \"mg-source\": \"notebook\",\n",
- " \"mg-notebook-name\": \"model_garden_gemma_finetuning_on_vertex.ipynb\".split(\".\")[0],\n",
- "}\n",
- "\n",
- "labels[\"mg-tune\"] = \"publishers-google-models-gemma\"\n",
- "versioned_model_id = base_model_id.lower().replace(\".\", \"-\")\n",
- "labels[\"versioned-mg-tune\"] = f\"{labels['mg-tune']}-{versioned_model_id}\"\n",
- "\n",
- "# @markdown Accelerator type to use for training.\n",
- "training_accelerator_type = \"NVIDIA_A100_80GB\" # @param [\"NVIDIA_A100_80GB\", \"NVIDIA_H100_80GB\"]\n",
- "\n",
- "# The pre-built training docker image.\n",
- "if training_accelerator_type == \"NVIDIA_A100_80GB\":\n",
- " repo = \"us-docker.pkg.dev/vertex-ai-restricted\"\n",
- " is_restricted_image = True\n",
- " is_dynamic_workload_scheduler = False\n",
- " dws_kwargs = {}\n",
- "else:\n",
- " repo = \"us-docker.pkg.dev/vertex-ai\"\n",
- " is_restricted_image = False\n",
- " is_dynamic_workload_scheduler = True\n",
- " dws_kwargs = {\n",
- " \"max_wait_duration\": 1800, # 30 minutes\n",
- " \"scheduling_strategy\": gca_custom_job_compat.Scheduling.Strategy.FLEX_START,\n",
- " }\n",
- "\n",
- "TRAIN_DOCKER_URI = (\n",
- " f\"{repo}/vertex-vision-model-garden-dockers/pytorch-peft-train:stable_20240909\"\n",
- ")\n",
- "\n",
- "# Worker pool spec.\n",
- "if training_accelerator_type == \"NVIDIA_A100_80GB\":\n",
- " per_node_accelerator_count = 8\n",
- " training_machine_type = \"a2-ultragpu-8g\"\n",
- "elif training_accelerator_type == \"NVIDIA_H100_80GB\":\n",
- " per_node_accelerator_count = 8\n",
- " training_machine_type = \"a3-highgpu-8g\"\n",
- "else:\n",
- " raise ValueError(\n",
- " f\"Recommended machine settings not found for: {training_accelerator_type}. To use another accelerator type, edit this code block to pass in an appropriate `training_machine_type`, `training_accelerator_type`, and `per_node_accelerator_count` to the deploy_model_vllm function by clicking `Show Code` and then modifying the code.\"\n",
- " )\n",
- "\n",
- "# @markdown Batch size for finetuning.\n",
- "per_device_train_batch_size = 1 # @param{type:\"integer\"}\n",
- "# @markdown Number of updates steps to accumulate the gradients for, before performing a backward/update pass.\n",
- "gradient_accumulation_steps = 4 # @param{type:\"integer\"}\n",
- "# @markdown Maximum sequence length.\n",
- "max_seq_length = 4096 # @param{type:\"integer\"}\n",
- "# @markdown Setting a positive `max_steps` here will override `num_epochs`.\n",
- "max_steps = -1 # @param{type:\"integer\"}\n",
- "num_epochs = 1.0 # @param{type:\"number\"}\n",
- "# @markdown Precision mode for finetuning.\n",
- "finetuning_precision_mode = \"4bit\" # @param [\"4bit\", \"8bit\", \"float16\"]\n",
- "# @markdown Learning rate.\n",
- "learning_rate = 5e-5 # @param{type:\"number\"}\n",
- "# @markdown The scheduler type to use.\n",
- "lr_scheduler_type = \"cosine\" # @param{type:\"string\"}\n",
- "# @markdown LoRA parameters.\n",
- "lora_rank = 16 # @param{type:\"integer\"}\n",
- "lora_alpha = 32 # @param{type:\"integer\"}\n",
- "lora_dropout = 0.05 # @param{type:\"number\"}\n",
- "# Activates gradient checkpointing for the current model (may be referred to as activation checkpointing or checkpoint activations in other frameworks).\n",
- "enable_gradient_checkpointing = True\n",
- "# Attention implementation to use in the model.\n",
- "attn_implementation = \"eager\"\n",
- "# The optimizer for which to schedule the learning rate.\n",
- "optimizer = \"paged_adamw_32bit\"\n",
- "# Define the proportion of training to be dedicated to a linear warmup where learning rate gradually increases.\n",
- "warmup_ratio = \"0.01\"\n",
- "# The list or string of integrations to report the results and logs to.\n",
- "report_to = \"tensorboard\"\n",
- "# Number of updates steps before two checkpoint saves.\n",
- "save_steps = 10\n",
- "# Number of update steps between two logs.\n",
- "logging_steps = save_steps\n",
- "# Train precision of the model.\n",
- "train_precision = \"bfloat16\"\n",
- "\n",
- "replica_count = 1\n",
- "\n",
- "common_util.check_quota(\n",
- " project_id=PROJECT_ID,\n",
- " region=REGION,\n",
- " accelerator_type=training_accelerator_type,\n",
- " accelerator_count=per_node_accelerator_count * replica_count,\n",
- " is_for_training=True,\n",
- " is_restricted_image=is_restricted_image,\n",
- " is_dynamic_workload_scheduler=is_dynamic_workload_scheduler,\n",
- ")\n",
- "\n",
- "job_name = common_util.get_job_name_with_datetime(\"gemma-lora-train\")\n",
- "\n",
- "base_output_dir = os.path.join(STAGING_BUCKET, job_name)\n",
- "# Create a GCS folder to store the LORA adapter.\n",
- "lora_output_dir = os.path.join(base_output_dir, \"adapter\")\n",
- "# Create a GCS folder to store the merged model with the base model and the\n",
- "# finetuned LORA adapter.\n",
- "merged_model_output_dir = os.path.join(base_output_dir, \"merged-model\")\n",
- "\n",
- "eval_args = [\n",
- " f\"--eval_dataset_path={eval_dataset_name}\",\n",
- " f\"--eval_column={instruct_column_in_dataset}\",\n",
- " f\"--eval_template={template}\",\n",
- " f\"--eval_split={eval_split_name}\",\n",
- " f\"--eval_steps={save_steps}\",\n",
- " \"--eval_tasks=builtin_eval\",\n",
- " \"--eval_metric_name=loss\",\n",
- "]\n",
- "\n",
- "train_job_args = [\n",
- " \"--config_file=vertex_vision_model_garden_peft/deepspeed_zero2_8gpu.yaml\",\n",
- " \"--task=instruct-lora\",\n",
- " \"--completion_only=True\",\n",
- " f\"--pretrained_model_id={pretrained_model_id}\",\n",
- " f\"--dataset_name={train_dataset_name}\",\n",
- " f\"--train_split_name={train_split_name}\",\n",
- " f\"--instruct_column_in_dataset={instruct_column_in_dataset}\",\n",
- " f\"--output_dir={lora_output_dir}\",\n",
- " f\"--merge_base_and_lora_output_dir={merged_model_output_dir}\",\n",
- " f\"--per_device_train_batch_size={per_device_train_batch_size}\",\n",
- " f\"--gradient_accumulation_steps={gradient_accumulation_steps}\",\n",
- " f\"--lora_rank={lora_rank}\",\n",
- " f\"--lora_alpha={lora_alpha}\",\n",
- " f\"--lora_dropout={lora_dropout}\",\n",
- " f\"--max_steps={max_steps}\",\n",
- " f\"--max_seq_length={max_seq_length}\",\n",
- " f\"--learning_rate={learning_rate}\",\n",
- " f\"--lr_scheduler_type={lr_scheduler_type}\",\n",
- " f\"--precision_mode={finetuning_precision_mode}\",\n",
- " f\"--train_precision={train_precision}\",\n",
- " f\"--enable_gradient_checkpointing={enable_gradient_checkpointing}\",\n",
- " f\"--num_epochs={num_epochs}\",\n",
- " f\"--attn_implementation={attn_implementation}\",\n",
- " f\"--optimizer={optimizer}\",\n",
- " f\"--warmup_ratio={warmup_ratio}\",\n",
- " f\"--report_to={report_to}\",\n",
- " f\"--logging_output_dir={base_output_dir}\",\n",
- " f\"--save_steps={save_steps}\",\n",
- " f\"--logging_steps={logging_steps}\",\n",
- " f\"--template={template}\",\n",
- " f\"--huggingface_access_token={HF_TOKEN}\",\n",
- "] + eval_args\n",
- "\n",
- "# Pass training arguments and launch job.\n",
- "train_job = aiplatform.CustomContainerTrainingJob(\n",
- " display_name=job_name,\n",
- " container_uri=TRAIN_DOCKER_URI,\n",
- " labels=labels,\n",
- ")\n",
- "\n",
- "print(\"Running training job with args:\")\n",
- "print(\" \\\\\\n\".join(train_job_args))\n",
- "train_job.run(\n",
- " args=train_job_args,\n",
- " replica_count=replica_count,\n",
- " machine_type=training_machine_type,\n",
- " accelerator_type=training_accelerator_type,\n",
- " accelerator_count=per_node_accelerator_count,\n",
- " boot_disk_size_gb=500,\n",
- " service_account=SERVICE_ACCOUNT,\n",
- " base_output_dir=base_output_dir,\n",
- " sync=False, # Non-blocking call to run.\n",
- " **dws_kwargs,\n",
- ")\n",
- "\n",
- "# Wait until resource has been created.\n",
- "train_job.wait_for_resource_creation()\n",
- "\n",
- "print(\"LoRA adapter will be saved in:\", lora_output_dir)\n",
- "print(\"Trained and merged models will be saved in:\", merged_model_output_dir)\n",
- "\n",
- "# @markdown Click \"Show Code\" to see more details."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "FvJV3FwFGxbM"
- },
- "outputs": [],
- "source": [
- "# @title Run TensorBoard\n",
- "# @markdown This section shows how to launch TensorBoard in a [Cloud Shell](https://cloud.google.com/shell/docs).\n",
- "# @markdown 1. Click the Cloud Shell icon() on the top right to open the Cloud Shell.\n",
- "# @markdown 2. Copy the `tensorboard` command shown below by running this cell.\n",
- "# @markdown 3. Paste and run the command in the Cloud Shell to launch TensorBoard.\n",
- "# @markdown 4. Once the command runs (You may have to click `Authorize` if prompted), click the link starting with `http://localhost`.\n",
- "\n",
- "# @markdown Note: You may need to wait around 10 minutes after the job starts in order for the TensorBoard logs to be written to the GCS bucket.\n",
- "print(f\"Command to copy: tensorboard --logdir {base_output_dir}/logs\")"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "qmHW6m8xG_4U"
- },
- "outputs": [],
- "source": [
- "# @title Deploy\n",
- "\n",
- "# @markdown This section uploads the model to Model Registry and deploys it on the Endpoint. It takes 15 minutes to 1 hour to finish.\n",
- "\n",
- "# The pre-built serving docker image for vLLM.\n",
- "VLLM_DOCKER_URI = \"us-docker.pkg.dev/vertex-ai/vertex-vision-model-garden-dockers/pytorch-vllm-serve:20240815_1634_RC00\"\n",
- "\n",
- "# @markdown Set `use_dedicated_endpoint` to False if you don't want to use [dedicated endpoint](https://cloud.google.com/vertex-ai/docs/general/deployment#create-dedicated-endpoint).\n",
- "use_dedicated_endpoint = True # @param {type:\"boolean\"}\n",
- "\n",
- "\n",
- "def get_deploy_source() -> str:\n",
- " \"\"\"Gets deploy_source string based on running environment.\"\"\"\n",
- " vertex_product = os.environ.get(\"VERTEX_PRODUCT\", \"\")\n",
- " if vertex_product == \"COLAB_ENTERPRISE\":\n",
- " return \"notebook_colab_enterprise\"\n",
- " elif vertex_product == \"WORKBENCH_INSTANCE\":\n",
- " return \"notebook_workbench\"\n",
- " else:\n",
- " # Legacy workbench, legacy colab, or other custom environments.\n",
- " return \"notebook_environment_unspecified\"\n",
- "\n",
- "\n",
- "def deploy_model_vllm(\n",
- " model_name: str,\n",
- " model_id: str,\n",
- " publisher: str,\n",
- " publisher_model_id: str,\n",
- " service_account: str,\n",
- " base_model_id: str = None,\n",
- " machine_type: str = \"g2-standard-8\",\n",
- " accelerator_type: str = \"NVIDIA_L4\",\n",
- " accelerator_count: int = 1,\n",
- " gpu_memory_utilization: float = 0.9,\n",
- " max_model_len: int = 4096,\n",
- " dtype: str = \"auto\",\n",
- " enable_trust_remote_code: bool = False,\n",
- " enforce_eager: bool = False,\n",
- " enable_lora: bool = False,\n",
- " enable_chunked_prefill: bool = False,\n",
- " enable_prefix_cache: bool = False,\n",
- " host_prefix_kv_cache_utilization_target: float = 0.0,\n",
- " max_loras: int = 1,\n",
- " max_cpu_loras: int = 8,\n",
- " use_dedicated_endpoint: bool = False,\n",
- " max_num_seqs: int = 256,\n",
- " model_type: str = None,\n",
- " enable_llama_tool_parser: bool = False,\n",
- " is_spot: bool = False,\n",
- ") -> Tuple[aiplatform.Model, aiplatform.Endpoint]:\n",
- " \"\"\"Deploys trained models with vLLM into Vertex AI.\"\"\"\n",
- " endpoint = aiplatform.Endpoint.create(\n",
- " display_name=f\"{model_name}-endpoint\",\n",
- " dedicated_endpoint_enabled=use_dedicated_endpoint,\n",
- " )\n",
- "\n",
- " if not base_model_id:\n",
- " base_model_id = model_id\n",
- "\n",
- " # See https://docs.vllm.ai/en/latest/models/engine_args.html for a list of possible arguments with descriptions.\n",
- " vllm_args = [\n",
- " \"python\",\n",
- " \"-m\",\n",
- " \"vllm.entrypoints.api_server\",\n",
- " \"--host=0.0.0.0\",\n",
- " \"--port=8080\",\n",
- " f\"--model={model_id}\",\n",
- " f\"--tensor-parallel-size={accelerator_count}\",\n",
- " \"--swap-space=16\",\n",
- " f\"--max-model-len={max_model_len}\",\n",
- " f\"--dtype={dtype}\",\n",
- " f\"--max-loras={max_loras}\",\n",
- " f\"--max-cpu-loras={max_cpu_loras}\",\n",
- " f\"--max-num-seqs={max_num_seqs}\",\n",
- " \"--disable-log-stats\",\n",
- " ]\n",
- "\n",
- " if gpu_memory_utilization:\n",
- " vllm_args.append(f\"--gpu-memory-utilization={gpu_memory_utilization}\")\n",
- "\n",
- " if enable_trust_remote_code:\n",
- " vllm_args.append(\"--trust-remote-code\")\n",
- "\n",
- " if enforce_eager:\n",
- " vllm_args.append(\"--enforce-eager\")\n",
- "\n",
- " if enable_lora:\n",
- " vllm_args.append(\"--enable-lora\")\n",
- "\n",
- " if enable_chunked_prefill:\n",
- " vllm_args.append(\"--enable-chunked-prefill\")\n",
- "\n",
- " if enable_prefix_cache:\n",
- " vllm_args.append(\"--enable-prefix-caching\")\n",
- "\n",
- " if 0 < host_prefix_kv_cache_utilization_target < 1:\n",
- " vllm_args.append(\n",
- " f\"--host-prefix-kv-cache-utilization-target={host_prefix_kv_cache_utilization_target}\"\n",
- " )\n",
- "\n",
- " if model_type:\n",
- " vllm_args.append(f\"--model-type={model_type}\")\n",
- "\n",
- " if enable_llama_tool_parser:\n",
- " vllm_args.append(\"--enable-auto-tool-choice\")\n",
- " vllm_args.append(\"--tool-call-parser=vertex-llama-3\")\n",
- "\n",
- " env_vars = {\n",
- " \"MODEL_ID\": base_model_id,\n",
- " \"DEPLOY_SOURCE\": \"notebook\",\n",
- " }\n",
- "\n",
- " # HF_TOKEN is not a compulsory field and may not be defined.\n",
- " try:\n",
- " if HF_TOKEN:\n",
- " env_vars[\"HF_TOKEN\"] = HF_TOKEN\n",
- " except NameError:\n",
- " pass\n",
- "\n",
- " model = aiplatform.Model.upload(\n",
- " display_name=model_name,\n",
- " serving_container_image_uri=VLLM_DOCKER_URI,\n",
- " serving_container_args=vllm_args,\n",
- " serving_container_ports=[8080],\n",
- " serving_container_predict_route=\"/generate\",\n",
- " serving_container_health_route=\"/ping\",\n",
- " serving_container_environment_variables=env_vars,\n",
- " serving_container_shared_memory_size_mb=(16 * 1024), # 16 GB\n",
- " serving_container_deployment_timeout=7200,\n",
- " model_garden_source_model_name=(\n",
- " f\"publishers/{publisher}/models/{publisher_model_id}\"\n",
- " ),\n",
- " )\n",
- " print(\n",
- " f\"Deploying {model_name} on {machine_type} with {accelerator_count} {accelerator_type} GPU(s).\"\n",
- " )\n",
- " model.deploy(\n",
- " endpoint=endpoint,\n",
- " machine_type=machine_type,\n",
- " accelerator_type=accelerator_type,\n",
- " accelerator_count=accelerator_count,\n",
- " deploy_request_timeout=1800,\n",
- " service_account=service_account,\n",
- " spot=is_spot,\n",
- " system_labels={\n",
- " \"NOTEBOOK_NAME\": \"model_garden_gemma_finetuning_on_vertex.ipynb\",\n",
- " \"NOTEBOOK_ENVIRONMENT\": get_deploy_source(),\n",
- " },\n",
- " )\n",
- " print(\"endpoint_name:\", endpoint.name)\n",
- "\n",
- " return model, endpoint\n",
- "\n",
- "\n",
- "if train_job.end_time is None:\n",
- " print(\"Waiting for the training job to finish...\")\n",
- " train_job.wait()\n",
- " print(\"The training job has finished.\")\n",
- "\n",
- "print(\"Deploying models in: \", merged_model_output_dir)\n",
- "\n",
- "# Find Vertex AI prediction supported accelerators and regions in [here](https://cloud.google.com/vertex-ai/docs/predictions/configure-compute).\n",
- "# Sets 1 L4 (24G) to deploy Gemma models.\n",
- "serve_machine_type = \"g2-standard-12\"\n",
- "serve_accelerator_type = \"NVIDIA_L4\"\n",
- "serve_accelerator_count = 1\n",
- "\n",
- "common_util.check_quota(\n",
- " project_id=PROJECT_ID,\n",
- " region=REGION,\n",
- " accelerator_type=serve_accelerator_type,\n",
- " accelerator_count=serve_accelerator_count,\n",
- " is_for_training=False,\n",
- ")\n",
- "\n",
- "# Note that a larger max_model_len will require more GPU memory.\n",
- "max_model_len = 2048\n",
- "\n",
- "models[\"vllm_gpu\"], endpoints[\"vllm_gpu\"] = deploy_model_vllm(\n",
- " model_name=common_util.get_job_name_with_datetime(prefix=\"gemma-vllm-serve\"),\n",
- " base_model_id=f\"google/{base_model_id}\",\n",
- " publisher=\"google\",\n",
- " publisher_model_id=\"gemma\",\n",
- " model_id=merged_model_output_dir,\n",
- " service_account=SERVICE_ACCOUNT,\n",
- " machine_type=serve_machine_type,\n",
- " accelerator_type=serve_accelerator_type,\n",
- " accelerator_count=serve_accelerator_count,\n",
- " max_model_len=max_model_len,\n",
- " use_dedicated_endpoint=use_dedicated_endpoint,\n",
- ")\n",
- "print(\"endpoint_name:\", endpoints[\"vllm_gpu\"].name)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "2UYUNn60G_4U"
- },
- "outputs": [],
- "source": [
- "# @title Predict\n",
- "\n",
- "# @markdown Once deployment succeeds, you can send requests to the endpoint with text prompts.\n",
- "\n",
- "# @markdown Here we use an example from the [timdettmers/openassistant-guanaco](https://huggingface.co/datasets/timdettmers/openassistant-guanaco) to show the finetuning outcome:\n",
- "\n",
- "# @markdown ```\n",
- "# @markdown ### Human: How would the Future of AI in 10 Years look?### Assistant: Predicting the future is always a challenging task, but here are some possible ways that AI could evolve over the next 10 years: Continued advancements in deep learning: Deep learning has been one of the main drivers of recent AI breakthroughs, and we can expect continued advancements in this area. This may include improvements to existing algorithms, as well as the development of new architectures that are better suited to specific types of data and tasks. Increased use of AI in healthcare: AI has the potential to revolutionize healthcare, by improving the accuracy of diagnoses, developing new treatments, and personalizing patient care. We can expect to see continued investment in this area, with more healthcare providers and researchers using AI to improve patient outcomes. Greater automation in the workplace: Automation is already transforming many industries, and AI is likely to play an increasingly important role in this process. We can expect to see more jobs being automated, as well as the development of new types of jobs that require a combination of human and machine skills. More natural and intuitive interactions with technology: As AI becomes more advanced, we can expect to see more natural and intuitive ways of interacting with technology. This may include voice and gesture recognition, as well as more sophisticated chatbots and virtual assistants. Increased focus on ethical considerations: As AI becomes more powerful, there will be a growing need to consider its ethical implications. This may include issues such as bias in AI algorithms, the impact of automation on employment, and the use of AI in surveillance and policing. Overall, the future of AI in 10 years is likely to be shaped by a combination of technological advancements, societal changes, and ethical considerations. While there are many exciting possibilities for AI in the future, it will be important to carefully consider its potential impact on society and to work towards ensuring that its benefits are shared fairly and equitably.\n",
- "# @markdown ```\n",
- "\n",
- "# @markdown Additionally, you can moderate the generated text with Vertex AI. See [Moderate text documentation](https://cloud.google.com/natural-language/docs/moderating-text) for more details.\n",
- "\n",
- "# Loads an existing endpoint instance using the endpoint name:\n",
- "# - Using `endpoint_name = endpoint.name` allows us to get the\n",
- "# endpoint name of the endpoint `endpoint` created in the cell\n",
- "# above.\n",
- "# - Alternatively, you can set `endpoint_name = \"1234567890123456789\"` to load\n",
- "# an existing endpoint with the ID 1234567890123456789.\n",
- "# You may uncomment the code below to load an existing endpoint.\n",
- "\n",
- "# endpoint_name = \"\" # @param {type:\"string\"}\n",
- "# aip_endpoint_name = (\n",
- "# f\"projects/{PROJECT_ID}/locations/{REGION}/endpoints/{endpoint_name}\"\n",
- "# )\n",
- "# endpoint = aiplatform.Endpoint(aip_endpoint_name)\n",
- "\n",
- "prompt = \"How would the Future of AI in 10 Years look?\" # @param {type: \"string\"}\n",
- "max_tokens = 128 # @param {type:\"integer\"}\n",
- "temperature = 1.0 # @param {type:\"number\"}\n",
- "top_p = 0.9 # @param {type:\"number\"}\n",
- "top_k = 1 # @param {type:\"integer\"}\n",
- "\n",
- "# Overrides max_tokens and top_k parameters during inferences.\n",
- "# If you encounter the issue like `ServiceUnavailable: 503 Took too long to respond when processing`,\n",
- "# you can reduce the max length, such as set max_tokens as 20.\n",
- "instances = [\n",
- " {\n",
- " \"prompt\": f\"### Human: {prompt}### Assistant: \",\n",
- " \"max_tokens\": max_tokens,\n",
- " \"temperature\": temperature,\n",
- " \"top_p\": top_p,\n",
- " \"top_k\": top_k,\n",
- " },\n",
- "]\n",
- "response = endpoints[\"vllm_gpu\"].predict(\n",
- " instances=instances, use_dedicated_endpoint=use_dedicated_endpoint\n",
- ")\n",
- "\n",
- "for prediction in response.predictions:\n",
- " print(prediction)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "af21a3cff1e0"
- },
- "source": [
- "## Clean up resources"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "911406c1561e"
- },
- "outputs": [],
- "source": [
- "# Delete the train job.\n",
- "train_job.delete()\n",
- "\n",
- "# Delete the conversion job.\n",
- "if conversion_job:\n",
- " conversion_job.delete()\n",
- "\n",
- "# @markdown Delete the experiment models and endpoints to recycle the resources\n",
- "# @markdown and avoid unnecessary continuous charges that may incur.\n",
- "\n",
- "# Undeploy model and delete endpoint.\n",
- "for endpoint in endpoints.values():\n",
- " endpoint.delete(force=True)\n",
- "\n",
- "# Delete models.\n",
- "for model in models.values():\n",
- " model.delete()\n",
- "\n",
- "delete_bucket = False # @param {type:\"boolean\"}\n",
- "if delete_bucket:\n",
- " ! gsutil -m rm -r $BUCKET_NAME"
- ]
- }
- ],
- "metadata": {
- "colab": {
- "name": "model_garden_gemma_finetuning_on_vertex.ipynb",
- "toc_visible": true
- },
- "kernelspec": {
- "display_name": "Python 3",
- "name": "python3"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 0
-}
diff --git a/notebooks/community/model_garden/model_garden_llama3_1_finetuning_with_workbench.ipynb b/notebooks/community/model_garden/model_garden_llama3_1_finetuning_with_workbench.ipynb
deleted file mode 100644
index d8a96167c..000000000
--- a/notebooks/community/model_garden/model_garden_llama3_1_finetuning_with_workbench.ipynb
+++ /dev/null
@@ -1,1614 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "5YEniAr2q1fG"
- },
- "outputs": [],
- "source": [
- "# Copyright 2026 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": "99c1c3fc2ca5"
- },
- "source": [
- "# Vertex AI Model Garden - Llama 3.1 Finetuning with customized container\n",
- "\n",
- "\n",
- " \n",
- " \n",
- "  Run in Workbench\n",
- " \n",
- " | \n",
- " \n",
- " \n",
- "  Run in Colab Enterprise\n",
- " \n",
- " | \n",
- " \n",
- " \n",
- "  View on GitHub\n",
- " \n",
- " | \n",
- "
"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "3de7470326a2"
- },
- "source": [
- "## Overview\n",
- "\n",
- "This notebook demonstrates -\n",
- "- Making changes to existing finetuning code.\n",
- "- Finetuning Llama 3.1 models with this modified code.\n",
- "- Deploying Llama 3.1 models.\n",
- "\n",
- "All of the examples in this notebook use parameter efficient finetuning methods [PEFT (LoRA)](https://github.com/huggingface/peft) to reduce training and storage costs. LoRA (Low-Rank Adaptation) is one approach of Parameter Efficient FineTuning (PEFT), where pretrained model weights are frozen and rank decomposition matrices representing the change in model weights are trained during finetuning. Read more about LoRA in the following publication: [Hu, E.J., Shen, Y., Wallis, P., Allen-Zhu, Z., Li, Y., Wang, S., Wang, L. and Chen, W., 2021. Lora: Low-rank adaptation of large language models. *arXiv preprint arXiv:2106.09685*](https://arxiv.org/abs/2106.09685).\n",
- "\n",
- "After finetuning, we can deploy models on Vertex with GPU.\n",
- "\n",
- "**It is advised to use the Workbench for this notebook.**\n",
- "\n",
- "\n",
- "### Objective\n",
- "\n",
- "- Customize docker container code and rebuild the container.\n",
- "- Finetune Llama 3.1 models in local environment using docker run.\n",
- "- Finetune Llama 3.1 models with Vertex AI Custom Training Jobs.\n",
- "- Run local predictions for finetuned Llama 3.1 models.\n",
- "- Deploy finetuned Llama 3.1 models on Vertex AI Prediction and send prediction requests.\n",
- "\n",
- "### File a bug\n",
- "\n",
- "File a bug on [GitHub](https://github.com/GoogleCloudPlatform/vertex-ai-samples/issues/new) if you encounter any issue with the notebook.\n",
- "\n",
- "### Costs\n",
- "\n",
- "This tutorial uses billable components of Google Cloud:\n",
- "\n",
- "* Vertex AI\n",
- "* Cloud Storage\n",
- "\n",
- "Learn about [Vertex AI pricing](https://cloud.google.com/vertex-ai/pricing), [Cloud Storage pricing](https://cloud.google.com/storage/pricing), and use the [Pricing Calculator](https://cloud.google.com/products/calculator/) to generate a cost estimate based on your projected usage."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "264c07757582"
- },
- "source": [
- "## Before you begin"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "nb7RPFuS3mvD"
- },
- "source": [
- "### Create workbench local environment."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "qkKLZRF73mvD"
- },
- "source": [
- "For finetuning using workbench local environment, **[click here](https://cloud.google.com/vertex-ai/docs/workbench/instances/create#console)** to create a workbench instance. We need 4 Nvidia A100 GPUs or 8 Nvidia A100 80 GB GPUs or 8 Nvidia H100 80 GB GPUs for training the 8b model. **Note that for 70b and 405b models 8 Nvidia A100 80 GB GPUs or 8 Nvidia H100 80 GB GPUs are required.**\n",
- "Follow [this](https://cloud.google.com/vertex-ai/docs/workbench/instances/create-euc-instance#create-instance) to link service account with workbench instance.\n",
- "\n",
- "Check following links to see if there is enough quota available to create workbench instance: [A100 GPU Quota](https://console.cloud.google.com/iam-admin/quotas?location=us-central1&metric=compute.googleapis.com%2Fnvidia_a100_gpus), [A100 80GB GPU Quota](https://console.cloud.google.com/iam-admin/quotas?location=us-central1&metric=compute.googleapis.com%2Fnvidia_a100_80gb_gpus), [H100 80GB GPU Quota](https://console.cloud.google.com/iam-admin/quotas?location=us-central1&metric=compute.googleapis.com%2Fgpus_per_gpu_family)\n",
- "\n",
- "Refer to [vertex AI Workbench instances locations](https://cloud.google.com/vertex-ai/docs/general/locations#instances) for the workbench instance availability."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "saDLCxT6rI0c"
- },
- "source": [
- "### Install Python Packages for Finetuning"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "855d6b96f291"
- },
- "outputs": [],
- "source": [
- "! pip install --quiet google-cloud-aiplatform\n",
- "! pip install --quiet gcsfs==2024.3.1\n",
- "! pip install --quiet accelerate==0.31.0\n",
- "! pip install --quiet transformers==4.43.1\n",
- "! pip install --quiet datasets==2.19.2\n",
- "! pip install --quiet tensorflow==2.18.0\n",
- "! pip install --upgrade --quiet google-cloud-aiplatform==1.130.0"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "O0IoZB_DKga5"
- },
- "source": [
- "### Import the necessary packages"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "wb-TeIY-Kga5"
- },
- "outputs": [],
- "source": [
- "! rm -rf vertex-ai-samples && git clone https://github.com/GoogleCloudPlatform/vertex-ai-samples.git\n",
- "! cd vertex-ai-samples && git reset --hard dd333b8fdd7dd22e8902a963fb8269885eac49ee\n",
- "\n",
- "import datetime\n",
- "import importlib\n",
- "import os\n",
- "import uuid\n",
- "from typing import Tuple\n",
- "\n",
- "from google.cloud import aiplatform\n",
- "from google.cloud.aiplatform.compat.types import \\\n",
- " custom_job as gca_custom_job_compat\n",
- "\n",
- "common_util = importlib.import_module(\n",
- " \"vertex-ai-samples.notebooks.community.model_garden.docker_source_codes.notebook_util.common_util\"\n",
- ")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "a9mBvuBurI0c"
- },
- "source": [
- "### Setup Google Cloud project"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "8kwN4_T3rI0c"
- },
- "source": [
- "1. [Make sure that billing is enabled for your project](https://cloud.google.com/billing/docs/how-to/modify-project).\n",
- "\n",
- "2. For finetuning using Vertex AI training, schedule your job with cs via Dynamic Workload Scheduler using [these instructions](https://cloud.google.com/vertex-ai/docs/training/schedule-jobs-dws). For Dynamic Workload Scheduler, check Nvidia Tesla A100 quota in [us-central1](https://console.cloud.google.com/iam-admin/quotas?location=us-central1&metric=aiplatform.googleapis.com%2Fcustom_model_training_preemptible_nvidia_a100_gpus) or [europe-west4](https://console.cloud.google.com/iam-admin/quotas?location=europe-west4&metric=aiplatform.googleapis.com%2Fcustom_model_training_preemptible_nvidia_a100_gpus. Check the [us-central1](https://console.cloud.google.com/iam-admin/quotas?location=us-central1&metric=aiplatform.googleapis.com%2Fcustom_model_training_preemptible_nvidia_a100_80gb_gpus) or [europe-west4](https://console.cloud.google.com/iam-admin/quotas?location=europe-west4&metric=aiplatform.googleapis.com%2Fcustom_model_training_preemptible_nvidia_a100_80gb_gpus) quota for Nvidia A100 80GB GPUs. Check the [us-central1](https://console.cloud.google.com/iam-admin/quotas?location=us-central1&metric=aiplatform.googleapis.com%2Fcustom_model_training_preemptible_nvidia_h100_gpus) or [europe-west4](https://console.cloud.google.com/iam-admin/quotas?location=europe-west4&metric=aiplatform.googleapis.com%2Fcustom_model_training_preemptible_nvidia_h100_gpus) quota for Nvidia H100 GPUs. If you do not have enough GPUs, then you can follow [these instructions](https://cloud.google.com/docs/quotas/view-manage#viewing_your_quota_console) to request quota. **Note: 8 Nvidia Tesla A100 GPUs can only be used to run 8b and 70b parameter models. For 405b parameter model, 8 Nvidia A100 80 GB or 8 Nvidia H100 80 GB GPUs are required**.\n",
- "\n",
- "3. For serving, **[click here](https://console.cloud.google.com/iam-admin/quotas?location=us-central1&metric=aiplatform.googleapis.com%2Fcustom_model_serving_nvidia_l4_gpus)** to check if your project already has the required 1 L4 GPU in the us-central1 region. If yes, then run this notebook in the us-central1 region. If you need more L4 GPUs for your project, then you can follow [these instructions](https://cloud.google.com/docs/quotas/view-manage#viewing_your_quota_console) to request more. Alternatively, 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).\n",
- "\n",
- "> | Machine Type | Accelerator Type | Recommended Regions |\n",
- "| ----------- | ----------- | ----------- |\n",
- "| a2-ultragpu-1g | 1 NVIDIA_A100_80GB | us-central1, us-east4, europe-west4, asia-southeast1, us-east4 |\n",
- "| a3-highgpu-2g | 2 NVIDIA_H100_80GB | us-west1, asia-southeast1, europe-west4 |\n",
- "| a3-highgpu-4g | 4 NVIDIA_H100_80GB | us-west1, asia-southeast1, europe-west4 |\n",
- "| a3-highgpu-8g | 8 NVIDIA_H100_80GB | us-central1, us-east5, europe-west4, us-west1, asia-southeast1 |"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "BcIzOjH9Kga5"
- },
- "source": [
- "Set region."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "0jkF1pVFKga5"
- },
- "outputs": [],
- "source": [
- "REGION = \"\" # @param {type:\\\"string\\\"}\"\n",
- "assert REGION, \"Region must be specified.\""
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "vmpFgm3hrI0c"
- },
- "source": [
- "**[Optional]** [Create a Cloud Storage bucket](https://cloud.google.com/storage/docs/creating-buckets) for storing experiment outputs. Set the BUCKET_URI for the experiment environment. The specified Cloud Storage bucket (`BUCKET_URI`) should be located in the same region as where the notebook was launched. Note that a multi-region bucket (eg. \"us\") is not considered a match for a single region covered by the multi-region range (eg. \"us-central1\"). If not set, a unique GCS bucket will be created instead."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "ocGkSSYxrI0c"
- },
- "outputs": [],
- "source": [
- "BUCKET_URI = \"\" # @param {type:\\\"string\\\"}\"\n",
- "if BUCKET_URI and not BUCKET_URI.startswith(\"gs://\"):\n",
- " BUCKET_URI = \"gs://\" + BUCKET_URI"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "05_d3h9fKga5"
- },
- "outputs": [],
- "source": [
- "train_job = None\n",
- "models, endpoints = {}, {}\n",
- "\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",
- " if not os.environ.get(\"GOOGLE_CLOUD_REGION\"):\n",
- " raise ValueError(\n",
- " \"REGION must be set. See\"\n",
- " \" https://cloud.google.com/vertex-ai/docs/general/locations for\"\n",
- " \" available cloud locations.\"\n",
- " )\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",
- "# Cloud Storage bucket for storing the experiment artifacts.\n",
- "# A unique GCS bucket will be created for the purpose of this notebook. If you\n",
- "# prefer using your own GCS bucket, change the value yourself below.\n",
- "now = datetime.datetime.now().strftime(\"%Y%m%d%H%M%S\")\n",
- "BUCKET_NAME = \"/\".join(BUCKET_URI.split(\"/\")[:3])\n",
- "\n",
- "if BUCKET_URI is None or BUCKET_URI.strip() == \"\" or BUCKET_URI == \"gs://\":\n",
- " BUCKET_URI = f\"gs://{PROJECT_ID}-tmp-{now}-{str(uuid.uuid4())[:4]}\"\n",
- " BUCKET_NAME = \"/\".join(BUCKET_URI.split(\"/\")[:3])\n",
- " ! gsutil mb -l {REGION} {BUCKET_URI}\n",
- "else:\n",
- " assert BUCKET_URI.startswith(\"gs://\"), \"BUCKET_URI must start with `gs://`.\"\n",
- " shell_output = ! gsutil ls -Lb {BUCKET_NAME} | grep \"Location constraint:\" | sed \"s/Location constraint://\"\n",
- " bucket_region = shell_output[0].strip().lower()\n",
- " if bucket_region != REGION:\n",
- " raise ValueError(\n",
- " \"Bucket region %s is different from notebook region %s\"\n",
- " % (bucket_region, REGION)\n",
- " )\n",
- "print(f\"Using this GCS Bucket: {BUCKET_URI}\")\n",
- "\n",
- "STAGING_BUCKET = os.path.join(BUCKET_URI, \"temporal\")\n",
- "MODEL_BUCKET = os.path.join(BUCKET_URI, \"llama3_1\")\n",
- "\n",
- "\n",
- "# Initialize Vertex AI API.\n",
- "print(\"Initializing Vertex AI API.\")\n",
- "aiplatform.init(project=PROJECT_ID, location=REGION, staging_bucket=STAGING_BUCKET)\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",
- "\n",
- "# Provision permissions to the SERVICE_ACCOUNT with the GCS bucket\n",
- "! gsutil iam ch serviceAccount:{SERVICE_ACCOUNT}:roles/storage.admin $BUCKET_NAME\n",
- "\n",
- "! gcloud config set project $PROJECT_ID\n",
- "! gcloud projects add-iam-policy-binding --no-user-output-enabled {PROJECT_ID} --member=serviceAccount:{SERVICE_ACCOUNT} --role=\"roles/storage.admin\"\n",
- "! gcloud projects add-iam-policy-binding --no-user-output-enabled {PROJECT_ID} --member=serviceAccount:{SERVICE_ACCOUNT} --role=\"roles/aiplatform.user\""
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "sYI6Njr8rI0c"
- },
- "source": [
- "### Access Llama 3.1 models"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "uMPlolGkrI0c"
- },
- "source": [
- "For GPU based finetuning and serving, choose between accessing Llama 3.1 models on [Hugging Face](https://huggingface.co/) or Vertex AI as described below.\n",
- "\n",
- "If you already obtained access to Llama 3.1 models on [Hugging Face](https://huggingface.co/), you can load models from there.\n",
- "Alternatively, you can also load the original Llama 3.1 models for finetuning and serving from Vertex AI after accepting the agreement.\n",
- "\n",
- "It is recommended to use \"Google Cloud\" for 405B model since it can be downloaded faster."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "-Ngr8K5koIYE"
- },
- "outputs": [],
- "source": [
- "# Modify the following parameter based on the model source.\n",
- "LOAD_MODEL_FROM = \"Google Cloud\" # @param [\"Google Cloud\", \"Hugging Face\"]\n",
- "HF_TOKEN = \"\"\n",
- "MODEL_BUCKET = \"\""
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "SPmUsIgErI0c"
- },
- "source": [
- "#### Access Model from Google Cloud"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "pBUh3K5qrI0c"
- },
- "source": [
- "The original models from Meta are converted into the Hugging Face format for serving in Vertex AI.\n",
- "Accept the model agreement to access the models:\n",
- "1. Open the [Llama 3.1 model card](https://console.cloud.google.com/vertex-ai/publishers/meta/model-garden/llama3_1) from [Vertex AI Model Garden](https://cloud.google.com/model-garden).\n",
- "2. Review and accept the agreement in the pop-up window on the model card page. If you have previously accepted the model agreement, there will not be a pop-up window on the model card page and this step is not needed.\n",
- "3. After accepting the agreement of Llama 3.1, a `gs://` URI containing Llama 3.1 pretrained and finetuned models will be shared.\n",
- "4. Paste the URI in the `MODEL_BUCKET` field below."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "PmFyCuADrI0c"
- },
- "outputs": [],
- "source": [
- "MODEL_BUCKET = \"\" # @param {type:\"string\"}\n",
- "if LOAD_MODEL_FROM == \"Google Cloud\":\n",
- " assert (\n",
- " MODEL_BUCKET\n",
- " ), \"Click the agreement of Llama3.1 in Vertex AI Model Garden, and get the GCS path of the model artifacts.\""
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "LnoAAGyfrI0c"
- },
- "source": [
- "#### Access Llama 3.1 models on Hugging Face"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "L_q9h-SArI0c"
- },
- "source": [
- "You must provide a Hugging Face User Access Token (with read access) to access the Llama 3.1 models. You can follow the [Hugging Face documentation](https://huggingface.co/docs/hub/en/security-tokens) to create a **read** access token and put it in the `HF_TOKEN` field below."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "9m0OhQ9brI0c"
- },
- "outputs": [],
- "source": [
- "HF_TOKEN = \"\" # @param {type:\"string\"}\n",
- "if LOAD_MODEL_FROM == \"Hugging Face\":\n",
- " assert (\n",
- " HF_TOKEN\n",
- " ), \"Provide a read HF_TOKEN to load models from Hugging Face, or select a different model source.\""
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "yMBmQSxlkU5n"
- },
- "source": [
- "## Finetune with HuggingFace PEFT"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "CESFVl_ZrI0c"
- },
- "source": [
- "### Set Dataset"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "o6tTFEI9rI0c"
- },
- "source": [
- "Use the Vertex AI SDK to create and run the custom training jobs.\n",
- "\n",
- "This notebook uses [timdettmers/openassistant-guanaco](https://huggingface.co/datasets/timdettmers/openassistant-guanaco) dataset as an example.\n",
- "You can set `dataset_name` to any existing [Hugging Face dataset](https://huggingface.co/datasets) name, and set `instruct_column_in_dataset` to the name of the dataset column containing training data. The [timdettmers/openassistant-guanaco](https://huggingface.co/datasets/timdettmers/openassistant-guanaco) has only one column `text`, and therefore we set `instruct_column_in_dataset` to `text` in this notebook.\n"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "WISejLC2rI0c"
- },
- "source": [
- "#### (Optional) Prepare a custom JSONL dataset for finetuning\n",
- "You can prepare a JSONL file where each line is a valid JSON string as your custom training dataset. For example, here is one line from the [timdettmers/openassistant-guanaco](https://huggingface.co/datasets/timdettmers/openassistant-guanaco) dataset:\n",
- "```\n",
- "{\"text\": \"### Human: Hola### Assistant: \\u00a1Hola! \\u00bfEn qu\\u00e9 puedo ayudarte hoy?\"}\n",
- "```\n",
- "\n",
- "The JSON object has a key `text`, which should match `instruct_column_in_dataset`; The value should be one training data point, i.e. a string. After you prepared your JSONL file, you can either upload it to [Hugging Face datasets](https://huggingface.co/datasets) or [Google Cloud Storage](https://cloud.google.com/storage).\n",
- "- To upload a JSONL dataset to [Hugging Face datasets](https://huggingface.co/datasets), follow the instructions on [Uploading Datasets](https://huggingface.co/docs/hub/en/datasets-adding). Then, set `dataset_name` to the name of your newly created dataset on Hugging Face.\n",
- "- To upload a JSONL dataset to [Google Cloud Storage](https://cloud.google.com/storage), follow the instructions on [Upload objects from a filesystem](https://cloud.google.com/storage/docs/uploading-objects). Then, set `dataset_name` to the `gs://` URI to your JSONL file. For example: `gs://cloud-samples-data/vertex-ai/model-evaluation/peft_train_sample.jsonl`.\n",
- "\n",
- "Optionally update the `instruct_column_in_dataset` field below if your JSON objects use a key other than the default `text`."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "XnyVrCu2rI0c"
- },
- "source": [
- "#### (Optional) Format your data with custom JSON template\n",
- "\n",
- "Sometimes, your dataset might have multiple text columns and you want to construct the training data with a template. You can prepare a JSON template in the following format:\n",
- "```\n",
- "{\n",
- " \"description\": \"Template used by Llama 3.1, accepting text-bison format.\",\n",
- " \"source\": \"https://cloud.google.com/vertex-ai/generative-ai/docs/models/tune-text-models-supervised#dataset-format\",\n",
- " \"prompt_input\": \"<|start_header_id|>user<|end_header_id|>\\n\\n{input_text}<|eot_id|><|start_header_id|>assistant<|end_header_id|>\\n\\n{output_text}<|eot_id|>\",\n",
- " \"instruction_separator\": \"<|start_header_id|>user<|end_header_id|>\\n\\n\",\n",
- " \"response_separator\": \"<|start_header_id|>assistant<|end_header_id|>\\n\\n\"\n",
- "}\n",
- "```\n",
- "\n",
- "As an example, the template above can be used to format the following training data (this line comes from `gs://cloud-samples-data/vertex-ai/model-evaluation/peft_train_sample.jsonl`):\n",
- "\n",
- "```\n",
- "{\"input_text\":\"TRANSCRIPT: \\nREASON FOR EVALUATION:,\\n\\n LABEL:\",\"output_text\":\"Chiropractic\"}\n",
- "```\n",
- "\n",
- "This example template simply concatenates `input_text` with `output_text` with some special tokens in between.\n",
- "\n",
- "To try such custom dataset, you can make the following changes:\n",
- "1. Set `template` to `llama3-text-bison`\n",
- "2. Set `train_dataset_name` to `gs://cloud-samples-data/vertex-ai/model-evaluation/peft_train_sample.jsonl`\n",
- "3. Set `train_split_name` to `train`\n",
- "4. Set `eval_dataset_name` to `gs://cloud-samples-data/vertex-ai/model-evaluation/peft_eval_sample.jsonl`\n",
- "5. Set `eval_split_name` to `train` (**NOT** `test`)\n",
- "6. Set `instruct_column_in_dataset` as `input_text`."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "Wz78SovrrI0c"
- },
- "outputs": [],
- "source": [
- "# Template name or gs:// URI to a custom template.\n",
- "template = \"openassistant-guanaco\" # @param {type:\"string\"}\n",
- "\n",
- "# Hugging Face dataset name or gs:// URI to a custom JSONL dataset.\n",
- "train_dataset_name = \"timdettmers/openassistant-guanaco\" # @param {type:\"string\"}\n",
- "train_split_name = \"train\" # @param {type:\"string\"}\n",
- "eval_dataset_name = \"timdettmers/openassistant-guanaco\" # @param {type:\"string\"}\n",
- "eval_split_name = \"test\" # @param {type:\"string\"}\n",
- "\n",
- "# Name of the dataset column containing training text input.\n",
- "instruct_column_in_dataset = \"text\" # @param {type:\"string\"}"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "lNg65esqrI0c"
- },
- "source": [
- "### Set model\n",
- "\n",
- "Select a model variant of Llama 3.1."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "nmYE2jJJkU5n"
- },
- "outputs": [],
- "source": [
- "# valid base model ids\n",
- "supported_base_model_ids = [\n",
- " \"meta-llama/Meta-Llama-3.1-8B\",\n",
- " \"meta-llama/Meta-Llama-3.1-8B-Instruct\",\n",
- " \"meta-llama/Meta-Llama-3.1-70B\",\n",
- " \"meta-llama/Meta-Llama-3.1-70B-Instruct\",\n",
- " \"meta-llama/Meta-Llama-3.1-405B\",\n",
- " \"meta-llama/Meta-Llama-3.1-405B-Instruct\",\n",
- "]\n",
- "\n",
- "base_model_id = \"meta-llama/Meta-Llama-3.1-8B-Instruct\"\n",
- "assert base_model_id in supported_base_model_ids, \"Provide a valid base model id.\"\n",
- "\n",
- "if LOAD_MODEL_FROM == \"Google Cloud\":\n",
- " pretrained_model_id = os.path.join(MODEL_BUCKET, base_model_id.split(\"/\")[-1])\n",
- "else:\n",
- " pretrained_model_id = base_model_id"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "H4_WvOHFW8hB"
- },
- "source": [
- "### Modify Finetuning docker\n",
- "Here we will demonstrate how we can make changes to [existing finetuning code from GitHub](https://github.com/GoogleCloudPlatform/vertex-ai-samples/tree/dd333b8fdd7dd22e8902a963fb8269885eac49ee/community-content/vertex_model_garden/model_oss/peft). One can follow a similar process to customize finetuning code."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "RuAb4wnYkU5n"
- },
- "source": [
- "#### Modify Trainer stats"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "DoKNzRIirI0c"
- },
- "source": [
- "The original code for `callbacks.py` is [here](https://github.com/GoogleCloudPlatform/vertex-ai-samples/blob/f641e5d2213f27acb203af02e02ff66b2ef8b9ba/community-content/vertex_model_garden/model_oss/peft/train/vmg/callbacks.py). The `callbacks.py` file contains callbacks in TrainerStatsCallback which will be executed at the end of training step. Currently this contains information about gpu usage, gpu memory usage and training throughput stats.\n",
- "Here we will modify `callbacks.py` to add the TFLOPS stats to trainer stats. TFLOPS is a unit of measurement for a GPU's performance that indicates how many floating-point operations a processor can perform per second."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "jJt33DP5kU5n"
- },
- "outputs": [],
- "source": [
- "%%writefile vertex-ai-samples/community-content/vertex_model_garden/model_oss/peft/train/vmg/callbacks.py\n",
- "\"\"\"Different trainer callbacks for PEFT Trainer.\"\"\"\n",
- "\n",
- "import time\n",
- "\n",
- "from absl import logging\n",
- "import accelerate\n",
- "from transformers import TrainingArguments\n",
- "from transformers.trainer_callback import TrainerCallback\n",
- "from transformers.trainer_callback import TrainerControl\n",
- "from transformers.trainer_callback import TrainerState\n",
- "\n",
- "from vertex_vision_model_garden_peft.train.vmg import utils\n",
- "\n",
- "\n",
- "class TrainerStatsCallback(TrainerCallback):\n",
- " \"\"\"Trainer callback to report trainer stats.\"\"\"\n",
- "\n",
- " def __init__(self, max_seq_length, filename=None):\n",
- " self._max_seq_length = max_seq_length\n",
- " self._filename = filename\n",
- "\n",
- " self._partial_state = accelerate.PartialState()\n",
- " self._start_time = float('nan')\n",
- " self._prev_time = float('nan')\n",
- " self._peak_mem = 0.0\n",
- " self._avg_throughput = 0.0\n",
- " self._avg_tflops_per_sec = 0.0\n",
- "\n",
- " def on_step_end(\n",
- " self,\n",
- " args: TrainingArguments,\n",
- " state: TrainerState,\n",
- " control: TrainerControl,\n",
- " **kwargs,\n",
- " ):\n",
- " if self._partial_state.is_main_process:\n",
- " if state.global_step == 1:\n",
- " self._prev_time = time.time()\n",
- " delta_t = float('nan')\n",
- " self._prev_tflops = state.total_flos / 1e12\n",
- " tflops_per_sec = 0.0\n",
- " else:\n",
- " cur_time = time.time()\n",
- " cur_tflops = state.total_flos / 1e12\n",
- " tflops_per_sec = (cur_tflops - self._prev_tflops) / (\n",
- " cur_time - self._prev_time\n",
- " )\n",
- " self._prev_tflops = cur_tflops\n",
- " self._avg_tflops_per_sec += (\n",
- " tflops_per_sec - self._avg_tflops_per_sec\n",
- " ) / (state.global_step - 1)\n",
- " delta_t = cur_time - self._prev_time\n",
- " self._prev_time = cur_time\n",
- " self._avg_throughput += (delta_t - self._avg_throughput) / (\n",
- " state.global_step - 1\n",
- " )\n",
- "\n",
- " gpu_stats = utils.gpu_stats()\n",
- " self._peak_mem = max(gpu_stats.total_mem, self._peak_mem)\n",
- " logging.info(\n",
- " 'on_step_end: %s, throughput: %.2f s/it, flops: %.2f tflops/s',\n",
- " utils.gpu_stats_str(gpu_stats),\n",
- " delta_t,\n",
- " tflops_per_sec\n",
- " )\n",
- "\n",
- " def on_train_begin(\n",
- " self,\n",
- " args: TrainingArguments,\n",
- " state: TrainerState,\n",
- " control: TrainerControl,\n",
- " **kwargs,\n",
- " ):\n",
- " if self._partial_state.is_main_process:\n",
- " self._start_time = time.time()\n",
- " logging.info('on_train_begin: %s', utils.gpu_stats_str())\n",
- "\n",
- " def on_train_end(\n",
- " self,\n",
- " args: TrainingArguments,\n",
- " state: TrainerState,\n",
- " control: TrainerControl,\n",
- " **kwargs,\n",
- " ):\n",
- " if self._partial_state.is_main_process:\n",
- " train_time = time.time() - self._start_time\n",
- " logging.info(\n",
- " 'training time %.2f s, throughput: %.2f s/it, peak_mem: %.2f GB',\n",
- " train_time,\n",
- " self._avg_throughput,\n",
- " self._peak_mem,\n",
- " )\n",
- " if self._filename:\n",
- " with open(self._filename, 'a') as out_f:\n",
- " out_f.write(\n",
- " f'{self._max_seq_length/1024.0:.1f}k | {self._peak_mem:.2f} |'\n",
- " f' {self._avg_throughput:.2f} | {self._avg_tflops_per_sec:.2f}\\n'\n",
- " )"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "-MqTX9zxrI0c"
- },
- "source": [
- "#### Build docker using gcloud build\n",
- "Here we will add `cloudbuild.yaml` file to build and push docker container using gcloud builds.\n",
- "**Note: gcloud docker build takes at least 15 mins to finish.**"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "lqHkYzxjnoDY"
- },
- "outputs": [],
- "source": [
- "%%writefile vertex-ai-samples/community-content/vertex_model_garden/cloudbuild.yaml\n",
- "steps:\n",
- "- name: 'gcr.io/cloud-builders/docker'\n",
- " script: |\n",
- " docker build -t $_LOCATION-docker.pkg.dev/$_PROJECT_ID/$_REPO_NAME/$_DOCKER_IMAGE_NAME:$_TAG_NAME -f model_oss/peft/train/vmg/dockerfile/train.Dockerfile .\n",
- " automapSubstitutions: true\n",
- "images:\n",
- "- '$_LOCATION-docker.pkg.dev/$_PROJECT_ID/$_REPO_NAME/$_DOCKER_IMAGE_NAME:$_TAG_NAME'"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "_sV8LVeekU5o"
- },
- "outputs": [],
- "source": [
- "REPOSITORY = \"vmg-llama-repo\"\n",
- "\n",
- "TAG_NAME = \"tflops\"\n",
- "\n",
- "DOCKER_IMAGE_NAME = \"peft\"\n",
- "\n",
- "# 1. Create a repository.\n",
- "\n",
- "! gcloud artifacts repositories create {REPOSITORY} --repository-format=docker --location={REGION} --description=\"Docker repository\" --quiet\n",
- "\n",
- "! gcloud artifacts repositories list\n",
- "\n",
- "# 2. Configure authentication to your private repo.\n",
- "\n",
- "! gcloud auth configure-docker {REGION}-docker.pkg.dev --quiet\n",
- "\n",
- "# 3. Build the docker image.\n",
- "\n",
- "! cd vertex-ai-samples/community-content/vertex_model_garden && gcloud builds submit --region=us-central1 \\\n",
- "--substitutions=_LOCATION={REGION},_PROJECT_ID={PROJECT_ID},_REPO_NAME={REPOSITORY},_TAG_NAME={TAG_NAME},_DOCKER_IMAGE_NAME={DOCKER_IMAGE_NAME} --config cloudbuild.yaml"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "dEdCSZnMrI0c"
- },
- "source": [
- "### Set Finetuning Parameters"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "7RNxoBuLrI0c"
- },
- "source": [
- "**Note**:\n",
- "1. We recommend setting `finetuning_precision_mode` to `4bit` because it enables using fewer hardware resources for finetuning.\n",
- "2. If `max_steps > 0`, it takes precedence over `epochs`. One can set a small `max_steps` value to quickly check the pipeline."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "RCy_TGgAznR8"
- },
- "outputs": [],
- "source": [
- "TRAIN_DOCKER_URI = (\n",
- " f\"{REGION}-docker.pkg.dev/{PROJECT_ID}/{REPOSITORY}/{DOCKER_IMAGE_NAME}:{TAG_NAME}\"\n",
- ")\n",
- "\n",
- "# Batch size for finetuning.\n",
- "per_device_train_batch_size = 1 # @param{type:\"integer\"}\n",
- "# Number of updates steps to accumulate the gradients for, before performing a backward/update pass.\n",
- "gradient_accumulation_steps = 4 # @param{type:\"integer\"}\n",
- "# Maximum sequence length.\n",
- "max_seq_length = 4096 # @param{type:\"integer\"}\n",
- "# Setting a positive `max_steps` here will override `num_epochs`.\n",
- "max_steps = -1 # @param{type:\"integer\"}\n",
- "num_epochs = 1.0 # @param{type:\"number\"}\n",
- "# Learning rate.\n",
- "learning_rate = 5e-5 # @param{type:\"number\"}\n",
- "# The scheduler type to use.\n",
- "lr_scheduler_type = \"cosine\" # @param{type:\"string\"}\n",
- "# LoRA parameters.\n",
- "lora_rank = 16 # @param{type:\"integer\"}\n",
- "lora_alpha = 32 # @param{type:\"integer\"}\n",
- "lora_dropout = 0.05 # @param{type:\"number\"}\n",
- "# gradient checkpointing for the current model (may be referred to as activation checkpointing or checkpoint activations in other frameworks).\n",
- "enable_gradient_checkpointing = True\n",
- "# Attention implementation to use in the model.\n",
- "attn_implementation = \"flash_attention_2\"\n",
- "# The optimizer for which to schedule the learning rate.\n",
- "optimizer = \"adamw_torch\"\n",
- "# Define the proportion of training to be dedicated to a linear warmup where learning rate gradually increases.\n",
- "warmup_ratio = \"0.01\"\n",
- "# The list or string of integrations to report the results and logs to.\n",
- "report_to = \"tensorboard\"\n",
- "# Number of updates steps before two checkpoint saves.\n",
- "save_steps = 10\n",
- "# Number of update steps between two logs.\n",
- "logging_steps = save_steps\n",
- "# Precision to use for training.\n",
- "train_precision = \"float16\"\n",
- "\n",
- "\n",
- "base_output_dir = os.path.join(STAGING_BUCKET, \"modified_peft\")\n",
- "# Create a GCS folder to store the LORA adapter.\n",
- "lora_output_dir = os.path.join(base_output_dir, \"adapter\")\n",
- "# Create a GCS folder to store the merged model with the base model and the\n",
- "# finetuned LORA adapter.\n",
- "merged_model_output_dir = os.path.join(base_output_dir, \"merged-model\")\n",
- "# Create a GCS folder to store the finetuned LORA adapter.\n",
- "final_checkpoint = os.path.join(lora_output_dir, \"checkpoint-final\")\n",
- "\n",
- "eval_args = [\n",
- " f\"--eval_dataset_path={eval_dataset_name}\",\n",
- " f\"--eval_column={instruct_column_in_dataset}\",\n",
- " f\"--eval_template={template}\",\n",
- " f\"--eval_split={eval_split_name}\",\n",
- " f\"--eval_steps={save_steps}\",\n",
- " \"--eval_tasks=builtin_eval\",\n",
- " \"--eval_metric_name=loss\",\n",
- "]\n",
- "\n",
- "training_args = [\n",
- " \"--task=instruct-lora\",\n",
- " \"--completion_only=True\",\n",
- " f\"--pretrained_model_id={pretrained_model_id}\",\n",
- " f\"--dataset_name={train_dataset_name}\",\n",
- " f\"--train_split_name={train_split_name}\",\n",
- " f\"--instruct_column_in_dataset={instruct_column_in_dataset}\",\n",
- " f\"--output_dir={lora_output_dir}\",\n",
- " f\"--merge_base_and_lora_output_dir={merged_model_output_dir}\",\n",
- " f\"--per_device_train_batch_size={per_device_train_batch_size}\",\n",
- " f\"--gradient_accumulation_steps={gradient_accumulation_steps}\",\n",
- " f\"--lora_rank={lora_rank}\",\n",
- " f\"--lora_alpha={lora_alpha}\",\n",
- " f\"--lora_dropout={lora_dropout}\",\n",
- " f\"--max_steps={max_steps}\",\n",
- " f\"--max_seq_length={max_seq_length}\",\n",
- " f\"--learning_rate={learning_rate}\",\n",
- " f\"--lr_scheduler_type={lr_scheduler_type}\",\n",
- " f\"--train_precision={train_precision}\",\n",
- " f\"--enable_gradient_checkpointing={enable_gradient_checkpointing}\",\n",
- " f\"--num_epochs={num_epochs}\",\n",
- " f\"--attn_implementation={attn_implementation}\",\n",
- " f\"--optimizer={optimizer}\",\n",
- " f\"--warmup_ratio={warmup_ratio}\",\n",
- " f\"--report_to={report_to}\",\n",
- " f\"--logging_output_dir={base_output_dir}\",\n",
- " f\"--save_steps={save_steps}\",\n",
- " f\"--logging_steps={logging_steps}\",\n",
- " f\"--template={template}\",\n",
- " f\"--huggingface_access_token={HF_TOKEN}\",\n",
- "] + eval_args"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "65VOcDB01lf3"
- },
- "source": [
- "### Local Finetuning"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "2wB0AIwGW8hB"
- },
- "source": [
- "This section demonstrates how to finetune a Llama 3.1 model with the modified peft docker using local run. The cell below will output docker command which need to be run in the local terminal.\n",
- "We need Local environment with 8 Nvidia A100 80 GB GPUs or 8 Nvidia H100 80 GB GPUs to run the command successfully."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "MkuMj8Sv1lf3"
- },
- "outputs": [],
- "source": [
- "import torch\n",
- "\n",
- "num_of_gpus = torch.cuda.device_count()\n",
- "\n",
- "if num_of_gpus == 4:\n",
- " local_training_args = training_args + [\n",
- " \"--config_file=vertex_vision_model_garden_peft/deepspeed_zero2_4gpu.yaml\"\n",
- " ]\n",
- "elif num_of_gpus == 8:\n",
- " local_training_args = training_args + [\n",
- " \"--config_file=vertex_vision_model_garden_peft/llama_fsdp_8gpu.yaml\"\n",
- " ]\n",
- "else:\n",
- " raise ValueError(f\"Unsupported number of GPUs for local training: {num_of_gpus}.\")\n",
- "\n",
- "args = \" \".join(local_training_args)\n",
- "print(\"Run peft training with the following command:\\n\")\n",
- "\n",
- "print(\n",
- " f\"docker run --gpus=all --net=host --rm --shm-size=128gb {TRAIN_DOCKER_URI} {args}\\n\"\n",
- ")\n",
- "\n",
- "print(\"after running the command, check the following files for the training results:\")\n",
- "print(\"LoRA adapter will be saved in:\", lora_output_dir)\n",
- "print(\"Trained and merged models will be saved in:\", merged_model_output_dir)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "k4JBAO7njiNF"
- },
- "source": [
- "#### Verify added trainer stats\n",
- "Once training starts, you should be able to see logs with above changes printed like below:\n",
- "\"on_step_end: GPU memory: 26.50(occupied=15.17, unused=7.83, smi_diff=3.50) GB. Utilization: 48.00%, throughput: 12.08 s/it, flops: 463.39 tflops/s\""
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "0doRxXku1lf3"
- },
- "source": [
- "### Vertex AI finetuning"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "EhPzKwrJW8hB"
- },
- "source": [
- "This section demonstrates how to finetune a Llama 3.1 model with the modified peft docker using Vertex AI run. **This section is expected to take at least 30 mins to finish.**"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "bdvRYbYd1lf3"
- },
- "source": [
- "#### Set machine configuration"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "1yjdPmbH1lf3"
- },
- "outputs": [],
- "source": [
- "# Set accelerator type for training job. Accelerator type must be one of the following: NVIDIA_A100_80GB, NVIDIA_H100_80GB\n",
- "accelerator_type = \"NVIDIA_A100_80GB\" # @param [\"NVIDIA_TESLA_A100\", \"NVIDIA_A100_80GB\", \"NVIDIA_H100_80GB\"]\n",
- "# Set number of replicas to use for training.\n",
- "replica_count = 1\n",
- "\n",
- "# Worker pool spec.\n",
- "if accelerator_type == \"NVIDIA_TESLA_A100\":\n",
- " per_node_accelerator_count = 8\n",
- " machine_type = \"a2-highgpu-8g\"\n",
- " boot_disk_size_gb = 500\n",
- " dws_kwargs = {\n",
- " \"max_wait_duration\": 1800, # 30 minutes\n",
- " \"scheduling_strategy\": gca_custom_job_compat.Scheduling.Strategy.FLEX_START,\n",
- " }\n",
- "elif accelerator_type == \"NVIDIA_A100_80GB\":\n",
- " per_node_accelerator_count = 8\n",
- " machine_type = \"a2-ultragpu-8g\"\n",
- " boot_disk_size_gb = 500\n",
- " dws_kwargs = {\n",
- " \"max_wait_duration\": 1800, # 30 minutes\n",
- " \"scheduling_strategy\": gca_custom_job_compat.Scheduling.Strategy.FLEX_START,\n",
- " }\n",
- "elif accelerator_type == \"NVIDIA_H100_80GB\":\n",
- " per_node_accelerator_count = 8\n",
- " machine_type = \"a3-highgpu-8g\"\n",
- " boot_disk_size_gb = 2000\n",
- " dws_kwargs = {\n",
- " \"max_wait_duration\": 1800, # 30 minutes\n",
- " \"scheduling_strategy\": gca_custom_job_compat.Scheduling.Strategy.FLEX_START,\n",
- " }\n",
- "else:\n",
- " raise ValueError(\n",
- " f\"Recommended machine settings not found for: {accelerator_type}. To use another accelerator type, edit this code block to pass in an appropriate `machine_type`, `accelerator_type`, and `per_node_accelerator_count` to the deploy_model_vllm function by clicking `Show Code` and then modifying the code.\"\n",
- " )\n",
- "\n",
- "if replica_count == 1:\n",
- " config_file = \"vertex_vision_model_garden_peft/llama_fsdp_8gpu.yaml\"\n",
- "elif replica_count <= 4:\n",
- " config_file = (\n",
- " \"vertex_vision_model_garden_peft/\"\n",
- " f\"llama_hsdp_{replica_count * per_node_accelerator_count}gpu.yaml\"\n",
- " )\n",
- "else:\n",
- " raise ValueError(\n",
- " f\"Recommended config settings not found for replica_count: {replica_count}.\"\n",
- " )\n",
- "\n",
- "common_util.check_quota(\n",
- " project_id=PROJECT_ID,\n",
- " region=REGION,\n",
- " accelerator_type=accelerator_type,\n",
- " accelerator_count=per_node_accelerator_count,\n",
- " is_for_training=True,\n",
- ")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "-trU7da81lf3"
- },
- "source": [
- "#### Run training job"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "SYeI6kbE1lf3"
- },
- "outputs": [],
- "source": [
- "vertex_training_args = training_args + [f\"--config_file={config_file}\"]\n",
- "job_name = common_util.get_job_name_with_datetime(\"llama3_1-lora-train\")\n",
- "\n",
- "# Add labels for the finetuning job.\n",
- "labels = {\n",
- " \"mg-source\": \"notebook\",\n",
- " \"mg-notebook-name\": \"model_garden_llama3_1_finetuning_with_workbench.ipynb\".split(\n",
- " \".\"\n",
- " )[0],\n",
- "}\n",
- "labels[\"mg-tune\"] = \"publishers-meta-models-llama3-1\"\n",
- "versioned_model_id = base_model_id.split(\"/\")[1].lower().replace(\".\", \"-\")\n",
- "labels[\"versioned-mg-tune\"] = f\"{labels['mg-tune']}-{versioned_model_id}\"\n",
- "\n",
- "# Pass training arguments and launch job.\n",
- "train_job = aiplatform.CustomContainerTrainingJob(\n",
- " display_name=job_name,\n",
- " container_uri=TRAIN_DOCKER_URI,\n",
- " labels=labels,\n",
- ")\n",
- "\n",
- "print(\"Running training job with args:\")\n",
- "print(\" \\\\\\n\".join(training_args))\n",
- "train_job.run(\n",
- " args=vertex_training_args,\n",
- " replica_count=replica_count,\n",
- " machine_type=machine_type,\n",
- " accelerator_type=accelerator_type,\n",
- " accelerator_count=per_node_accelerator_count,\n",
- " boot_disk_size_gb=boot_disk_size_gb,\n",
- " service_account=SERVICE_ACCOUNT,\n",
- " base_output_dir=base_output_dir,\n",
- " **dws_kwargs,\n",
- ")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "_sEi-9jXB-Zj"
- },
- "source": [
- "#### Verify added trainer stats\n",
- "Once training starts, you can go to above traning job link and open the logs. In the logs you should be able to see above changes printed like below:\n",
- "\"on_step_end: GPU memory: 26.50(occupied=15.17, unused=7.83, smi_diff=3.50) GB. Utilization: 48.00%, throughput: 12.08 s/it, flops: 463.39 tflops/s\""
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "I6yqKZQirI0c"
- },
- "source": [
- "## Deploy with vLLM on GPUs"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "0q_7FsC3uvsr"
- },
- "outputs": [],
- "source": [
- "# Wait until training job is finished.\n",
- "if train_job.end_time is None:\n",
- " print(\"Waiting for the training job to finish...\")\n",
- " train_job.wait()\n",
- " print(\"The training job has finished.\")\n",
- "\n",
- "VLLM_DOCKER_URI = \"us-docker.pkg.dev/vertex-ai/vertex-vision-model-garden-dockers/pytorch-vllm-serve:20240819_0916_RC00\"\n",
- "\n",
- "# Set vllm prediction arguments\n",
- "prompt = \"What is a car?\"\n",
- "# If you encounter the issue like `ServiceUnavailable: 503 Took too long to respond when processing`, you can reduce the maximum number of output tokens, such as set `max_tokens` as 20.\n",
- "max_tokens = 50\n",
- "temperature = 1.0\n",
- "top_p = 1.0\n",
- "top_k = 1\n",
- "raw_response = False"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "xH4yCSECuvsr"
- },
- "source": [
- "### Run Local Predictions\n",
- "This section outputs docker run command. This command can be run inside local terminal.\n",
- "Local environment is required to be at least L4 GPUs for 8x7B models and A100/H100 GPUs for 8x22B models to run the command successfully."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "dPLATBlOuvsr"
- },
- "outputs": [],
- "source": [
- "# Set Docker Arguments\n",
- "GPU_DEVICES = 0\n",
- "SOURCE_DIR = \"/home/jupyter\"\n",
- "CODE_DIR = \"/home/jupyter\"\n",
- "\n",
- "# Set vllm arguments\n",
- "model_id = pretrained_model_id\n",
- "accelerator_count = 1\n",
- "gpu_memory_utilization = 0.95\n",
- "max_model_len = 8192\n",
- "dtype = \"auto\"\n",
- "max_loras = 1\n",
- "max_cpu_loras = 8\n",
- "max_num_seqs = 256\n",
- "enable_trust_remote_code = False\n",
- "enforce_eager = False\n",
- "enable_lora = True\n",
- "model_type = None\n",
- "\n",
- "vllm_args = [\n",
- " \"python\",\n",
- " \"-m\",\n",
- " \"vllm.entrypoints.api_server\",\n",
- " \"--host=0.0.0.0\",\n",
- " \"--port=8080\",\n",
- " f\"--model={model_id}\",\n",
- " f\"--tensor-parallel-size={accelerator_count}\",\n",
- " \"--swap-space=16\",\n",
- " f\"--gpu-memory-utilization={gpu_memory_utilization}\",\n",
- " f\"--max-model-len={max_model_len}\",\n",
- " f\"--dtype={dtype}\",\n",
- " f\"--max-loras={max_loras}\",\n",
- " f\"--max-cpu-loras={max_cpu_loras}\",\n",
- " f\"--max-num-seqs={max_num_seqs}\",\n",
- " \"--disable-log-stats\",\n",
- "]\n",
- "\n",
- "if enable_trust_remote_code:\n",
- " vllm_args.append(\"--trust-remote-code\")\n",
- "\n",
- "if enforce_eager:\n",
- " vllm_args.append(\"--enforce-eager\")\n",
- "\n",
- "if enable_lora:\n",
- " vllm_args.append(\"--enable-lora\")\n",
- "\n",
- "if model_type:\n",
- " vllm_args.append(f\"--model-type={model_type}\")\n",
- "\n",
- "docker_cmd_part = f\"docker run -t --rm --gpus=all --net=host --shm-size 32gb --volume {SOURCE_DIR}:{CODE_DIR} -p 7080:7080 -e NVIDIA_VISIBLE_DEVICES={GPU_DEVICES}\"\n",
- "if HF_TOKEN:\n",
- " docker_cmd_part += f\" -e HF_TOKEN={HF_TOKEN}\"\n",
- "docker_args = \" \".join(vllm_args)\n",
- "cmd = f\"{docker_cmd_part} {VLLM_DOCKER_URI} {docker_args}\"\n",
- "\n",
- "print(f\"run below command in local terminal to start the container:\\n {cmd}\")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "Yx3qlCv69SSg"
- },
- "source": [
- "Once above command has been run successfully, you will see the server running on 7080.\n",
- "You can use below cell to run predictions on the vllm server."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "MV4UgUmN9SSg"
- },
- "outputs": [],
- "source": [
- "import json\n",
- "\n",
- "vllm_request_data = {\n",
- " \"prompt\": prompt,\n",
- " \"max_tokens\": max_tokens,\n",
- " \"temperature\": temperature,\n",
- " \"top_p\": top_p,\n",
- " \"top_k\": top_k,\n",
- " \"raw_response\": raw_response,\n",
- "}\n",
- "\n",
- "curl_data = json.dumps(vllm_request_data)\n",
- "cmd = f\"curl --header 'Content-Type: application/json' --request POST --data '{curl_data}' http://localhost:7080/generate\"\n",
- "\n",
- "print(f\"run below command in local terminal to send request to the container:\\n {cmd}\")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "yiTNQ3tLrI0c"
- },
- "source": [
- "### Deploy with Vertex AI\n",
- "This section uploads the model to Model Registry and deploys it on the Endpoint. It takes 15 minutes to 1 hour to finish depending on the size of model."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "kMQH9asKznR8"
- },
- "outputs": [],
- "source": [
- "if train_job.end_time is None:\n",
- " print(\"Waiting for the training job to finish...\")\n",
- " train_job.wait()\n",
- " print(\"The training job has finished.\")\n",
- "\n",
- "# @markdown Set `use_dedicated_endpoint` to False if you don't want to use [dedicated endpoint](https://cloud.google.com/vertex-ai/docs/general/deployment#create-dedicated-endpoint).\n",
- "use_dedicated_endpoint = True # @param {type:\"boolean\"}\n",
- "\n",
- "# Find Vertex AI prediction supported accelerators and regions [here](https://cloud.google.com/vertex-ai/docs/predictions/configure-compute).\n",
- "if \"8b\" in base_model_id.lower():\n",
- " machine_type = \"g2-standard-12\"\n",
- " accelerator_type = \"NVIDIA_L4\"\n",
- " per_node_accelerator_count = 1\n",
- "elif \"70b\" in base_model_id.lower():\n",
- " machine_type = \"g2-standard-96\"\n",
- " accelerator_type = \"NVIDIA_L4\"\n",
- " per_node_accelerator_count = 8\n",
- "elif \"405b\" in base_model_id.lower():\n",
- " machine_type = \"a3-highgpu-8g\"\n",
- " accelerator_type = \"NVIDIA_H100_80GB\"\n",
- " per_node_accelerator_count = 8\n",
- "else:\n",
- " raise ValueError(f\"Unsupported model ID or GCS path: {base_model_id}.\")\n",
- "\n",
- "\n",
- "def get_deploy_source() -> str:\n",
- " \"\"\"Gets deploy_source string based on running environment.\"\"\"\n",
- " vertex_product = os.environ.get(\"VERTEX_PRODUCT\", \"\")\n",
- " if vertex_product == \"COLAB_ENTERPRISE\":\n",
- " return \"notebook_colab_enterprise\"\n",
- " elif vertex_product == \"WORKBENCH_INSTANCE\":\n",
- " return \"notebook_workbench\"\n",
- " else:\n",
- " # Legacy workbench, legacy colab, or other custom environments.\n",
- " return \"notebook_environment_unspecified\"\n",
- "\n",
- "\n",
- "def deploy_model_vllm(\n",
- " model_name: str,\n",
- " model_id: str,\n",
- " publisher: str,\n",
- " publisher_model_id: str,\n",
- " service_account: str,\n",
- " base_model_id: str = None,\n",
- " machine_type: str = \"g2-standard-8\",\n",
- " accelerator_type: str = \"NVIDIA_L4\",\n",
- " accelerator_count: int = 1,\n",
- " gpu_memory_utilization: float = 0.9,\n",
- " max_model_len: int = 4096,\n",
- " dtype: str = \"auto\",\n",
- " enable_trust_remote_code: bool = False,\n",
- " enforce_eager: bool = False,\n",
- " enable_lora: bool = False,\n",
- " enable_chunked_prefill: bool = False,\n",
- " enable_prefix_cache: bool = False,\n",
- " host_prefix_kv_cache_utilization_target: float = 0.0,\n",
- " max_loras: int = 1,\n",
- " max_cpu_loras: int = 8,\n",
- " use_dedicated_endpoint: bool = False,\n",
- " max_num_seqs: int = 256,\n",
- " model_type: str = None,\n",
- " enable_llama_tool_parser: bool = False,\n",
- " is_spot: bool = False,\n",
- ") -> Tuple[aiplatform.Model, aiplatform.Endpoint]:\n",
- " \"\"\"Deploys trained models with vLLM into Vertex AI.\"\"\"\n",
- " endpoint = aiplatform.Endpoint.create(\n",
- " display_name=f\"{model_name}-endpoint\",\n",
- " dedicated_endpoint_enabled=use_dedicated_endpoint,\n",
- " )\n",
- "\n",
- " if not base_model_id:\n",
- " base_model_id = model_id\n",
- "\n",
- " # See https://docs.vllm.ai/en/latest/models/engine_args.html for a list of possible arguments with descriptions.\n",
- " vllm_args = [\n",
- " \"python\",\n",
- " \"-m\",\n",
- " \"vllm.entrypoints.api_server\",\n",
- " \"--host=0.0.0.0\",\n",
- " \"--port=8080\",\n",
- " f\"--model={model_id}\",\n",
- " f\"--tensor-parallel-size={accelerator_count}\",\n",
- " \"--swap-space=16\",\n",
- " f\"--max-model-len={max_model_len}\",\n",
- " f\"--dtype={dtype}\",\n",
- " f\"--max-loras={max_loras}\",\n",
- " f\"--max-cpu-loras={max_cpu_loras}\",\n",
- " f\"--max-num-seqs={max_num_seqs}\",\n",
- " \"--disable-log-stats\",\n",
- " ]\n",
- "\n",
- " if gpu_memory_utilization:\n",
- " vllm_args.append(f\"--gpu-memory-utilization={gpu_memory_utilization}\")\n",
- "\n",
- " if enable_trust_remote_code:\n",
- " vllm_args.append(\"--trust-remote-code\")\n",
- "\n",
- " if enforce_eager:\n",
- " vllm_args.append(\"--enforce-eager\")\n",
- "\n",
- " if enable_lora:\n",
- " vllm_args.append(\"--enable-lora\")\n",
- "\n",
- " if enable_chunked_prefill:\n",
- " vllm_args.append(\"--enable-chunked-prefill\")\n",
- "\n",
- " if enable_prefix_cache:\n",
- " vllm_args.append(\"--enable-prefix-caching\")\n",
- "\n",
- " if 0 < host_prefix_kv_cache_utilization_target < 1:\n",
- " vllm_args.append(\n",
- " f\"--host-prefix-kv-cache-utilization-target={host_prefix_kv_cache_utilization_target}\"\n",
- " )\n",
- "\n",
- " if model_type:\n",
- " vllm_args.append(f\"--model-type={model_type}\")\n",
- "\n",
- " if enable_llama_tool_parser:\n",
- " vllm_args.append(\"--enable-auto-tool-choice\")\n",
- " vllm_args.append(\"--tool-call-parser=vertex-llama-3\")\n",
- "\n",
- " env_vars = {\n",
- " \"MODEL_ID\": base_model_id,\n",
- " \"DEPLOY_SOURCE\": \"notebook\",\n",
- " }\n",
- "\n",
- " # HF_TOKEN is not a compulsory field and may not be defined.\n",
- " try:\n",
- " if HF_TOKEN:\n",
- " env_vars[\"HF_TOKEN\"] = HF_TOKEN\n",
- " except NameError:\n",
- " pass\n",
- "\n",
- " model = aiplatform.Model.upload(\n",
- " display_name=model_name,\n",
- " serving_container_image_uri=VLLM_DOCKER_URI,\n",
- " serving_container_args=vllm_args,\n",
- " serving_container_ports=[8080],\n",
- " serving_container_predict_route=\"/generate\",\n",
- " serving_container_health_route=\"/ping\",\n",
- " serving_container_environment_variables=env_vars,\n",
- " serving_container_shared_memory_size_mb=(16 * 1024), # 16 GB\n",
- " serving_container_deployment_timeout=7200,\n",
- " model_garden_source_model_name=(\n",
- " f\"publishers/{publisher}/models/{publisher_model_id}\"\n",
- " ),\n",
- " )\n",
- " print(\n",
- " f\"Deploying {model_name} on {machine_type} with {accelerator_count} {accelerator_type} GPU(s).\"\n",
- " )\n",
- " model.deploy(\n",
- " endpoint=endpoint,\n",
- " machine_type=machine_type,\n",
- " accelerator_type=accelerator_type,\n",
- " accelerator_count=accelerator_count,\n",
- " deploy_request_timeout=1800,\n",
- " service_account=service_account,\n",
- " spot=is_spot,\n",
- " system_labels={\n",
- " \"NOTEBOOK_NAME\": \"model_garden_llama3_1_finetuning_with_workbench.ipynb\",\n",
- " \"NOTEBOOK_ENVIRONMENT\": get_deploy_source(),\n",
- " },\n",
- " )\n",
- " print(\"endpoint_name:\", endpoint.name)\n",
- "\n",
- " return model, endpoint\n",
- "\n",
- "\n",
- "# Use FP8 base model for 405B since original model does not fit.\n",
- "deploy_pretrained_model_id = pretrained_model_id\n",
- "if \"Meta-Llama-3.1-405B\" in deploy_pretrained_model_id:\n",
- " deploy_pretrained_model_id += \"-FP8\"\n",
- "print(\"Deploying model in:\", deploy_pretrained_model_id)\n",
- "\n",
- "common_util.check_quota(\n",
- " project_id=PROJECT_ID,\n",
- " region=REGION,\n",
- " accelerator_type=accelerator_type,\n",
- " accelerator_count=per_node_accelerator_count,\n",
- " is_for_training=False,\n",
- ")\n",
- "\n",
- "models[\"vllm_gpu\"], endpoints[\"vllm_gpu\"] = deploy_model_vllm(\n",
- " model_name=common_util.get_job_name_with_datetime(prefix=\"llama3_1-vllm-serve\"),\n",
- " model_id=deploy_pretrained_model_id,\n",
- " publisher=\"meta\",\n",
- " publisher_model_id=\"llama3_1\",\n",
- " service_account=SERVICE_ACCOUNT,\n",
- " machine_type=machine_type,\n",
- " accelerator_type=accelerator_type,\n",
- " accelerator_count=per_node_accelerator_count,\n",
- " gpu_memory_utilization=gpu_memory_utilization,\n",
- " max_model_len=max_model_len,\n",
- " enable_lora=True,\n",
- " use_dedicated_endpoint=use_dedicated_endpoint,\n",
- ")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "wo4qzRjXG6C4"
- },
- "source": [
- "Once deployment succeeds, you can send requests to the endpoint with text prompts. Sampling parameters supported by vLLM can be found [here](https://docs.vllm.ai/en/latest/dev/sampling_params.html).\n",
- "Additionally, you can moderate the generated text with Vertex AI. See [Moderate text documentation](https://cloud.google.com/natural-language/docs/moderating-text) for more details."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "fLKpMietG6C4"
- },
- "outputs": [],
- "source": [
- "def predict_vllm(\n",
- " prompt: str,\n",
- " max_tokens: int,\n",
- " temperature: float,\n",
- " top_p: float,\n",
- " top_k: int,\n",
- " raw_response: bool,\n",
- " lora_weight: str = \"\",\n",
- "):\n",
- " # Parameters for inference.\n",
- " instance = {\n",
- " \"prompt\": prompt,\n",
- " \"max_tokens\": max_tokens,\n",
- " \"temperature\": temperature,\n",
- " \"top_p\": top_p,\n",
- " \"top_k\": top_k,\n",
- " \"raw_response\": raw_response,\n",
- " }\n",
- " if lora_weight:\n",
- " instance[\"dynamic-lora\"] = lora_weight\n",
- " instances = [instance]\n",
- " response = endpoints[\"vllm_gpu\"].predict(\n",
- " instances=instances, use_dedicated_endpoint=use_dedicated_endpoint\n",
- " )\n",
- "\n",
- " for prediction in response.predictions:\n",
- " print(prediction)\n",
- "\n",
- "\n",
- "predict_vllm(\n",
- " prompt=prompt,\n",
- " max_tokens=max_tokens,\n",
- " temperature=temperature,\n",
- " top_p=top_p,\n",
- " top_k=top_k,\n",
- " raw_response=raw_response,\n",
- ")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "dlr6yT3IW8hB"
- },
- "source": [
- "## Clean up resources"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "KjdIvxyDW8hB"
- },
- "outputs": [],
- "source": [
- "if train_job:\n",
- " train_job.delete()\n",
- "\n",
- "# @markdown Delete the experiment models and endpoints to recycle the resources\n",
- "# @markdown and avoid unnecessary continuous charges that may incur.\n",
- "\n",
- "# Undeploy model and delete endpoint.\n",
- "for endpoint in endpoints.values():\n",
- " endpoint.delete(force=True)\n",
- "\n",
- "# Delete models.\n",
- "for model in models.values():\n",
- " model.delete()\n",
- "\n",
- "delete_bucket = False # @param {type:\"boolean\"}\n",
- "if delete_bucket:\n",
- " ! gsutil -m rm -r $BUCKET_NAME"
- ]
- }
- ],
- "metadata": {
- "colab": {
- "name": "model_garden_llama3_1_finetuning_with_workbench.ipynb",
- "toc_visible": true
- },
- "kernelspec": {
- "display_name": "Python 3",
- "name": "python3"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 0
-}
diff --git a/notebooks/community/model_garden/model_garden_pytorch_falcon_evaluation.ipynb b/notebooks/community/model_garden/model_garden_pytorch_falcon_evaluation.ipynb
index d72f2911f..bcb60b94e 100644
--- a/notebooks/community/model_garden/model_garden_pytorch_falcon_evaluation.ipynb
+++ b/notebooks/community/model_garden/model_garden_pytorch_falcon_evaluation.ipynb
@@ -1,374 +1,373 @@
{
- "cells": [
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "id": "7d9bbf86da5e"
- },
- "outputs": [],
- "source": [
- "# Copyright 2023 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": "99c1c3fc2ca5"
- },
- "source": [
- "# Vertex AI Model Garden - Falcon Evaluation\n",
- "\n",
- "\n",
- " \n",
- " \n",
- "  Run in Colab Enterprise\n",
- " \n",
- " | \n",
- " \n",
- " \n",
- "  View on GitHub\n",
- " \n",
- " | \n",
- "
"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "3de7470326a2"
- },
- "source": [
- "## Overview\n",
- "\n",
- "This notebook demonstrates evaluating a pre-trained or a PEFT-finetuned Falcon Instruct models in Vertex AI.\n",
- "\n",
- "### Objective\n",
- "\n",
- "- Evaluate a pre-trained or a PEFT-finetuned Falcon model on any of the benchmark datasets\n",
- "- Clean up the resources\n",
- "\n",
- "| Models |\n",
- "| :- |\n",
- "| [tiiuae/falcon-7b-instruct](https://huggingface.co/tiiuae/falcon-7b-instruct)\n",
- "| [tiiuae/falcon-40b-instruct](https://huggingface.co/tiiuae/falcon-40b-instruct)\n",
- "\n",
- "### Costs\n",
- "\n",
- "This tutorial uses billable components of Google Cloud:\n",
- "\n",
- "* Vertex AI\n",
- "* Cloud Storage\n",
- "\n",
- "Learn about [Vertex AI pricing](https://cloud.google.com/vertex-ai/pricing) and [Cloud Storage pricing](https://cloud.google.com/storage/pricing), and use the [Pricing Calculator](https://cloud.google.com/products/calculator/) to generate a cost estimate based on your projected usage."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "HsAZ1ozfRQt7"
- },
- "source": [
- "## Run the notebook"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "855d6b96f291"
- },
- "outputs": [],
- "source": [
- "# @title Setup Google Cloud project\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] [Create a Cloud Storage bucket](https://cloud.google.com/storage/docs/creating-buckets) for storing experiment outputs. Set the BUCKET_URI for the experiment environment. The specified Cloud Storage bucket (`BUCKET_URI`) should be located in the same region as where the notebook was launched. Note that a multi-region bucket (eg. \"us\") is not considered a match for a single region covered by the multi-region range (eg. \"us-central1\"). If not set, a unique GCS bucket will be created instead.\n",
- "\n",
- "# Import the necessary packages\n",
- "import os\n",
- "from datetime import datetime\n",
- "\n",
- "from google.cloud import aiplatform\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",
- "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",
- "# Cloud Storage bucket for storing the experiment artifacts.\n",
- "# A unique GCS bucket will be created for the purpose of this notebook. If you\n",
- "# prefer using your own GCS bucket, please change the value yourself below.\n",
- "now = datetime.now().strftime(\"%Y%m%d%H%M%S\")\n",
- "BUCKET_URI = \"gs://\" # @param {type:\"string\"}\n",
- "BUCKET_NAME = \"/\".join(BUCKET_URI.split(\"/\")[:3])\n",
- "assert BUCKET_URI.startswith(\"gs://\"), \"BUCKET_URI must start with `gs://`.\"\n",
- "if BUCKET_URI is None or BUCKET_URI.strip() == \"\" or BUCKET_URI == \"gs://\":\n",
- " # Create a unique GCS bucket for this notebook, if not specified by the user\n",
- " BUCKET_URI = f\"gs://{PROJECT_ID}-tmp-{now}\"\n",
- " ! gsutil mb -l {REGION} {BUCKET_URI}\n",
- "else:\n",
- " shell_output = ! gsutil ls -Lb {BUCKET_NAME} | grep \"Location constraint:\" | sed \"s/Location constraint://\"\n",
- " bucket_region = shell_output[0].strip().lower()\n",
- " if bucket_region != REGION:\n",
- " raise ValueError(\n",
- " \"Bucket region %s is different from notebook region %s\"\n",
- " % (bucket_region, REGION)\n",
- " )\n",
- "\n",
- "print(f\"Using this GCS Bucket: {BUCKET_URI}\")\n",
- "\n",
- "STAGING_BUCKET = os.path.join(BUCKET_URI, \"temporal\")\n",
- "EXPERIMENT_BUCKET = os.path.join(BUCKET_URI, \"peft\")\n",
- "MODEL_BUCKET = os.path.join(EXPERIMENT_BUCKET, \"model\")\n",
- "\n",
- "# Initialize Vertex AI API.\n",
- "print(\"Initializing Vertex AI API.\")\n",
- "aiplatform.init(project=PROJECT_ID, location=REGION, staging_bucket=STAGING_BUCKET)\n",
- "\n",
- "# Gets the default BUCKET_URI and SERVICE_ACCOUNT if they were not specified by the user.\n",
- "SERVICE_ACCOUNT = None\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",
- "# Provision permissions to the SERVICE_ACCOUNT with the GCS bucket\n",
- "BUCKET_NAME = \"/\".join(BUCKET_URI.split(\"/\")[:3])\n",
- "! gsutil iam ch serviceAccount:{SERVICE_ACCOUNT}:roles/storage.admin $BUCKET_NAME\n",
- "\n",
- "! gcloud config set project $PROJECT_ID\n",
- "\n",
- "\n",
- "aiplatform.init(project=PROJECT_ID, location=REGION, staging_bucket=STAGING_BUCKET)\n",
- "\n",
- "# The evaluation docker image.\n",
- "EVAL_DOCKER_URI = \"us-docker.pkg.dev/vertex-ai/vertex-vision-model-garden-dockers/pytorch-lm-evaluation-harness:20231011_0934_RC00\"\n",
- "\n",
- "# Define common functions\n",
- "\n",
- "\n",
- "def get_job_name_with_datetime(prefix: str) -> str:\n",
- " \"\"\"Gets the job name with date time when triggering training or deployment\n",
- " jobs in Vertex AI.\n",
- " \"\"\"\n",
- " return prefix + datetime.now().strftime(\"_%Y%m%d_%H%M%S\")"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "g0t0RBixIw0P"
- },
- "outputs": [],
- "source": [
- "# @title Evaluate PEFT-finetuned Falcon Instruct models\n",
- "\n",
- "# @markdown This section demonstrates how to evaluate the Falcon Instruct models fintuned with PEFT LoRA using EleutherAI's [Language Model Evaluation Harness (lm-evaluation-harness)](https://github.com/EleutherAI/lm-evaluation-harness) with Vertex CustomJob. Please reference the peak GPU memory usage for serving and adjust the machine type, accelerator type and accelerator count accordingly.\n",
- "\n",
- "# @markdown This example uses the dataset [TruthfulQA](https://arxiv.org/abs/2109.07958). All supported tasks are listed in [this task table](https://github.com/EleutherAI/lm-evaluation-harness/blob/master/docs/task_table.md).\n",
- "# @markdown Set evaluation dataset.\n",
- "eval_dataset = \"truthfulqa_mc\" # @param {type:\"string\"}\n",
- "\n",
- "# Worker pool spec.\n",
- "# Find Vertex AI supported accelerators and regions in:\n",
- "# https://cloud.google.com/vertex-ai/docs/training/configure-compute\n",
- "\n",
- "\n",
- "# Setup evaluation job.\n",
- "# @markdown Set the base model id.\n",
- "base_model_id = \"tiiuae/falcon-7b-instruct\" # @param [\"tiiuae/falcon-7b-instruct\", \"tiiuae/falcon-40b-instruct\"]\n",
- "job_name = get_job_name_with_datetime(prefix=\"falcon-instruct-peft-eval\")\n",
- "eval_output_dir = os.path.join(MODEL_BUCKET, job_name)\n",
- "eval_output_dir_gcsfuse = eval_output_dir.replace(\"gs://\", \"/gcs/\")\n",
- "\n",
- "# @markdown Sets V100 (16G) to evaluate `tiiuae/falcon-7b-instruct` or `tiiuae/falcon-40b-instruct`.\n",
- "# @markdown If A100 is not available, you may evaluate tiiuae/falcon-40b-instruct with\n",
- "# @markdown multiple V100s. Please keep in mind that the efficiency of evaluating with\n",
- "# @markdown multiple V100s is inferior to that of evaluating with A100s.\n",
- "\n",
- "# @markdown Set the accelerator type.\n",
- "accelerator_type = \"NVIDIA_TESLA_V100\" # @param[\"NVIDIA_TESLA_V100\", \"NVIDIA_L4\", \"NVIDIA_TESLA_A100\", \"NVIDIA_TESLA_A100_80G\"]\n",
- "\n",
- "\n",
- "# @markdown To evaluate a PEFT-finetuned model, enter the PEFT output directory below.\n",
- "# @markdown Otherwise, leave it empty.\n",
- "# @markdown See the finetuning notebook for more details: https://github.com/GoogleCloudPlatform/vertex-ai-samples/blob/main/notebooks/community/model_garden/model_garden_pytorch_llama2_peft_finetuning.ipynb\n",
- "peft_output_dir = \"\" # @param {type:\"string\"}\n",
- "peft_output_dir_gcsfuse = peft_output_dir.replace(\"gs://\", \"/gcs/\")\n",
- "\n",
- "if \"7b\" in base_model_id:\n",
- " # For models containing '7b', set configurations based on the accelerator type provided.\n",
- " if accelerator_type == \"NVIDIA_TESLA_A100\":\n",
- " machine_type = \"a2-highgpu-1g\"\n",
- " accelerator_count = 1\n",
- " elif accelerator_type == \"NVIDIA_TESLA_V100\":\n",
- " machine_type = \"n1-standard-8\"\n",
- " accelerator_count = 1\n",
- " elif accelerator_type == \"NVIDIA_L4\":\n",
- " machine_type = \"g2-standard-8\"\n",
- " accelerator_count = 1\n",
- " else:\n",
- " print(f\"Unsupported accelerator type: {accelerator_type}\")\n",
- "elif \"40b\" in base_model_id:\n",
- " # For models containing '40b', set configurations based on the accelerator type provided.\n",
- " if accelerator_type == \"NVIDIA_TESLA_A100_80GB\":\n",
- " machine_type = \"a2-ultragpu-1g\"\n",
- " accelerator_count = 2\n",
- " elif accelerator_type == \"NVIDIA_L4\":\n",
- " machine_type = \"g2-standard-48\"\n",
- " accelerator_count = 4\n",
- " elif (\n",
- " accelerator_type == \"NVIDIA_TESLA_V100\"\n",
- " ): # Assuming V100 can be used as a fallback for 40b models\n",
- " machine_type = \"n1-standard-8\"\n",
- " accelerator_count = 8\n",
- " else:\n",
- " print(f\"Unsupported accelerator type: {accelerator_type}\")\n",
- "else:\n",
- " print(\"The base_model_id does not specify a recognized model version.\")\n",
- "\n",
- "replica_count = 1\n",
- "\n",
- "\n",
- "# Prepare evaluation command that runs the evaluation harness.\n",
- "# Set `trust_remote_code = True` because evaluating the model requires\n",
- "# executing code from the model repository.\n",
- "# Set `use_accelerate = True` to enable evaluation across multiple GPUs.\n",
- "eval_command = [\n",
- " \"python\",\n",
- " \"main.py\",\n",
- " \"--model\",\n",
- " \"hf-causal-experimental\",\n",
- " \"--tasks\",\n",
- " f\"{eval_dataset}\",\n",
- " \"--output_path\",\n",
- " f\"{eval_output_dir_gcsfuse}\",\n",
- "]\n",
- "\n",
- "if peft_output_dir_gcsfuse:\n",
- " eval_command += [\n",
- " \"--model_args\",\n",
- " f\"pretrained={base_model_id},peft={peft_output_dir_gcsfuse},trust_remote_code=True,use_accelerate=True,device_map_option=auto\",\n",
- " ]\n",
- "else:\n",
- " eval_command += [\n",
- " \"--model_args\",\n",
- " f\"pretrained={base_model_id},trust_remote_code=True,use_accelerate=True,device_map_option=auto\",\n",
- " ]\n",
- "\n",
- "\n",
- "# Pass evaluation arguments and launch job.\n",
- "worker_pool_specs = [\n",
- " {\n",
- " \"machine_spec\": {\n",
- " \"machine_type\": machine_type,\n",
- " \"accelerator_type\": accelerator_type,\n",
- " \"accelerator_count\": accelerator_count,\n",
- " },\n",
- " \"replica_count\": replica_count,\n",
- " \"disk_spec\": {\n",
- " \"boot_disk_size_gb\": 500,\n",
- " },\n",
- " \"container_spec\": {\n",
- " \"image_uri\": EVAL_DOCKER_URI,\n",
- " \"command\": eval_command,\n",
- " \"args\": [],\n",
- " },\n",
- " }\n",
- "]\n",
- "\n",
- "# Submit evaluation custom job.\n",
- "eval_job = aiplatform.CustomJob(\n",
- " display_name=job_name,\n",
- " worker_pool_specs=worker_pool_specs,\n",
- " base_output_dir=eval_output_dir,\n",
- ")\n",
- "\n",
- "eval_job.run()\n",
- "\n",
- "print(\"Evaluation results were saved in:\", eval_output_dir)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "1f15ed6d375a"
- },
- "outputs": [],
- "source": [
- "# @title Fetch and print evaluation results\n",
- "import json\n",
- "\n",
- "from google.cloud import storage\n",
- "\n",
- "# Fetch evaluation results.\n",
- "storage_client = storage.Client()\n",
- "BUCKET_NAME = BUCKET_URI.split(\"gs://\")[1]\n",
- "bucket = storage_client.get_bucket(BUCKET_NAME)\n",
- "RESULT_FILE_PATH = eval_output_dir[len(BUCKET_URI) + 1 :]\n",
- "blob = bucket.blob(RESULT_FILE_PATH)\n",
- "raw_result = blob.download_as_string()\n",
- "\n",
- "# Print evaluation results.\n",
- "result = json.loads(raw_result)\n",
- "result_formatted = json.dumps(result, indent=2)\n",
- "print(f\"Evaluation result:\\n{result_formatted}\")"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "911406c1561e"
- },
- "outputs": [],
- "source": [
- "# @title Clean up resources\n",
- "# Delete evaluation job.\n",
- "\n",
- "delete_bucket = False # @param {type:\"boolean\"}\n",
- "if delete_bucket:\n",
- " ! gsutil -m rm -r $BUCKET_URI\n",
- "\n",
- "eval_job.delete()"
- ]
- }
- ],
- "metadata": {
- "colab": {
- "name": "model_garden_pytorch_falcon_evaluation.ipynb",
- "toc_visible": true
- },
- "kernelspec": {
- "display_name": "Python 3",
- "name": "python3"
- }
+ "cells": [
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "7d9bbf86da5e"
+ },
+ "outputs": [],
+ "source": [
+ "# Copyright 2023 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."
+ ]
},
- "nbformat": 4,
- "nbformat_minor": 0
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "99c1c3fc2ca5"
+ },
+ "source": [
+ "# Vertex AI Model Garden - Falcon Evaluation\n",
+ "\n",
+ "\n",
+ " \n",
+ " \n",
+ "  Run in Colab Enterprise\n",
+ " \n",
+ " | \n",
+ " \n",
+ " \n",
+ "  View on GitHub\n",
+ " \n",
+ " | \n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "3de7470326a2"
+ },
+ "source": [
+ "## Overview\n",
+ "\n",
+ "This notebook demonstrates evaluating a pre-trained or a PEFT-finetuned Falcon Instruct models in Vertex AI.\n",
+ "\n",
+ "### Objective\n",
+ "\n",
+ "- Evaluate a pre-trained or a PEFT-finetuned Falcon model on any of the benchmark datasets\n",
+ "- Clean up the resources\n",
+ "\n",
+ "| Models |\n",
+ "| :- |\n",
+ "| [tiiuae/falcon-7b-instruct](https://huggingface.co/tiiuae/falcon-7b-instruct)\n",
+ "| [tiiuae/falcon-40b-instruct](https://huggingface.co/tiiuae/falcon-40b-instruct)\n",
+ "\n",
+ "### Costs\n",
+ "\n",
+ "This tutorial uses billable components of Google Cloud:\n",
+ "\n",
+ "* Vertex AI\n",
+ "* Cloud Storage\n",
+ "\n",
+ "Learn about [Vertex AI pricing](https://cloud.google.com/vertex-ai/pricing) and [Cloud Storage pricing](https://cloud.google.com/storage/pricing), and use the [Pricing Calculator](https://cloud.google.com/products/calculator/) to generate a cost estimate based on your projected usage."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "HsAZ1ozfRQt7"
+ },
+ "source": [
+ "## Run the notebook"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "cellView": "form",
+ "id": "855d6b96f291"
+ },
+ "outputs": [],
+ "source": [
+ "# @title Setup Google Cloud project\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] [Create a Cloud Storage bucket](https://cloud.google.com/storage/docs/creating-buckets) for storing experiment outputs. Set the BUCKET_URI for the experiment environment. The specified Cloud Storage bucket (`BUCKET_URI`) should be located in the same region as where the notebook was launched. Note that a multi-region bucket (eg. \"us\") is not considered a match for a single region covered by the multi-region range (eg. \"us-central1\"). If not set, a unique GCS bucket will be created instead.\n",
+ "\n",
+ "# Import the necessary packages\n",
+ "import os\n",
+ "from datetime import datetime\n",
+ "\n",
+ "from google.cloud import aiplatform\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",
+ "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",
+ "# Cloud Storage bucket for storing the experiment artifacts.\n",
+ "# A unique GCS bucket will be created for the purpose of this notebook. If you\n",
+ "# prefer using your own GCS bucket, please change the value yourself below.\n",
+ "now = datetime.now().strftime(\"%Y%m%d%H%M%S\")\n",
+ "BUCKET_URI = \"gs://\" # @param {type:\"string\"}\n",
+ "BUCKET_NAME = \"/\".join(BUCKET_URI.split(\"/\")[:3])\n",
+ "assert BUCKET_URI.startswith(\"gs://\"), \"BUCKET_URI must start with `gs://`.\"\n",
+ "if BUCKET_URI is None or BUCKET_URI.strip() == \"\" or BUCKET_URI == \"gs://\":\n",
+ " # Create a unique GCS bucket for this notebook, if not specified by the user\n",
+ " BUCKET_URI = f\"gs://{PROJECT_ID}-tmp-{now}\"\n",
+ " ! gsutil mb -l {REGION} {BUCKET_URI}\n",
+ "else:\n",
+ " shell_output = ! gsutil ls -Lb {BUCKET_NAME} | grep \"Location constraint:\" | sed \"s/Location constraint://\"\n",
+ " bucket_region = shell_output[0].strip().lower()\n",
+ " if bucket_region != REGION:\n",
+ " raise ValueError(\n",
+ " \"Bucket region %s is different from notebook region %s\"\n",
+ " % (bucket_region, REGION)\n",
+ " )\n",
+ "\n",
+ "print(f\"Using this GCS Bucket: {BUCKET_URI}\")\n",
+ "\n",
+ "STAGING_BUCKET = os.path.join(BUCKET_URI, \"temporal\")\n",
+ "EXPERIMENT_BUCKET = os.path.join(BUCKET_URI, \"peft\")\n",
+ "MODEL_BUCKET = os.path.join(EXPERIMENT_BUCKET, \"model\")\n",
+ "\n",
+ "# Initialize Vertex AI API.\n",
+ "print(\"Initializing Vertex AI API.\")\n",
+ "aiplatform.init(project=PROJECT_ID, location=REGION, staging_bucket=STAGING_BUCKET)\n",
+ "\n",
+ "# Gets the default BUCKET_URI and SERVICE_ACCOUNT if they were not specified by the user.\n",
+ "SERVICE_ACCOUNT = None\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",
+ "# Provision permissions to the SERVICE_ACCOUNT with the GCS bucket\n",
+ "BUCKET_NAME = \"/\".join(BUCKET_URI.split(\"/\")[:3])\n",
+ "! gsutil iam ch serviceAccount:{SERVICE_ACCOUNT}:roles/storage.admin $BUCKET_NAME\n",
+ "\n",
+ "! gcloud config set project $PROJECT_ID\n",
+ "\n",
+ "\n",
+ "aiplatform.init(project=PROJECT_ID, location=REGION, staging_bucket=STAGING_BUCKET)\n",
+ "\n",
+ "# The evaluation docker image.\n",
+ "EVAL_DOCKER_URI = \"us-docker.pkg.dev/vertex-ai/vertex-vision-model-garden-dockers/pytorch-lm-evaluation-harness:20231011_0934_RC00\"\n",
+ "\n",
+ "# Define common functions\n",
+ "\n",
+ "\n",
+ "def get_job_name_with_datetime(prefix: str) -> str:\n",
+ " \"\"\"Gets the job name with date time when triggering training or deployment\n",
+ " jobs in Vertex AI.\n",
+ " \"\"\"\n",
+ " return prefix + datetime.now().strftime(\"_%Y%m%d_%H%M%S\")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "cellView": "form",
+ "id": "g0t0RBixIw0P"
+ },
+ "outputs": [],
+ "source": [
+ "# @title Evaluate PEFT-finetuned Falcon Instruct models\n",
+ "\n",
+ "# @markdown This section demonstrates how to evaluate the Falcon Instruct models fintuned with PEFT LoRA using EleutherAI's [Language Model Evaluation Harness (lm-evaluation-harness)](https://github.com/EleutherAI/lm-evaluation-harness) with Vertex CustomJob. Please reference the peak GPU memory usage for serving and adjust the machine type, accelerator type and accelerator count accordingly.\n",
+ "\n",
+ "# @markdown This example uses the dataset [TruthfulQA](https://arxiv.org/abs/2109.07958). All supported tasks are listed in [this task table](https://github.com/EleutherAI/lm-evaluation-harness/blob/master/docs/task_table.md).\n",
+ "# @markdown Set evaluation dataset.\n",
+ "eval_dataset = \"truthfulqa_mc\" # @param {type:\"string\"}\n",
+ "\n",
+ "# Worker pool spec.\n",
+ "# Find Vertex AI supported accelerators and regions in:\n",
+ "# https://cloud.google.com/vertex-ai/docs/training/configure-compute\n",
+ "\n",
+ "\n",
+ "# Setup evaluation job.\n",
+ "# @markdown Set the base model id.\n",
+ "base_model_id = \"tiiuae/falcon-7b-instruct\" # @param [\"tiiuae/falcon-7b-instruct\", \"tiiuae/falcon-40b-instruct\"]\n",
+ "job_name = get_job_name_with_datetime(prefix=\"falcon-instruct-peft-eval\")\n",
+ "eval_output_dir = os.path.join(MODEL_BUCKET, job_name)\n",
+ "eval_output_dir_gcsfuse = eval_output_dir.replace(\"gs://\", \"/gcs/\")\n",
+ "\n",
+ "# @markdown Sets V100 (16G) to evaluate `tiiuae/falcon-7b-instruct` or `tiiuae/falcon-40b-instruct`.\n",
+ "# @markdown If A100 is not available, you may evaluate tiiuae/falcon-40b-instruct with\n",
+ "# @markdown multiple V100s. Please keep in mind that the efficiency of evaluating with\n",
+ "# @markdown multiple V100s is inferior to that of evaluating with A100s.\n",
+ "\n",
+ "# @markdown Set the accelerator type.\n",
+ "accelerator_type = \"NVIDIA_TESLA_V100\" # @param[\"NVIDIA_TESLA_V100\", \"NVIDIA_L4\", \"NVIDIA_TESLA_A100\", \"NVIDIA_TESLA_A100_80G\"]\n",
+ "\n",
+ "\n",
+ "# @markdown To evaluate a PEFT-finetuned model, enter the PEFT output directory below.\n",
+ "# @markdown Otherwise, leave it empty.\n",
+ "peft_output_dir = \"\" # @param {type:\"string\"}\n",
+ "peft_output_dir_gcsfuse = peft_output_dir.replace(\"gs://\", \"/gcs/\")\n",
+ "\n",
+ "if \"7b\" in base_model_id:\n",
+ " # For models containing '7b', set configurations based on the accelerator type provided.\n",
+ " if accelerator_type == \"NVIDIA_TESLA_A100\":\n",
+ " machine_type = \"a2-highgpu-1g\"\n",
+ " accelerator_count = 1\n",
+ " elif accelerator_type == \"NVIDIA_TESLA_V100\":\n",
+ " machine_type = \"n1-standard-8\"\n",
+ " accelerator_count = 1\n",
+ " elif accelerator_type == \"NVIDIA_L4\":\n",
+ " machine_type = \"g2-standard-8\"\n",
+ " accelerator_count = 1\n",
+ " else:\n",
+ " print(f\"Unsupported accelerator type: {accelerator_type}\")\n",
+ "elif \"40b\" in base_model_id:\n",
+ " # For models containing '40b', set configurations based on the accelerator type provided.\n",
+ " if accelerator_type == \"NVIDIA_TESLA_A100_80GB\":\n",
+ " machine_type = \"a2-ultragpu-1g\"\n",
+ " accelerator_count = 2\n",
+ " elif accelerator_type == \"NVIDIA_L4\":\n",
+ " machine_type = \"g2-standard-48\"\n",
+ " accelerator_count = 4\n",
+ " elif (\n",
+ " accelerator_type == \"NVIDIA_TESLA_V100\"\n",
+ " ): # Assuming V100 can be used as a fallback for 40b models\n",
+ " machine_type = \"n1-standard-8\"\n",
+ " accelerator_count = 8\n",
+ " else:\n",
+ " print(f\"Unsupported accelerator type: {accelerator_type}\")\n",
+ "else:\n",
+ " print(\"The base_model_id does not specify a recognized model version.\")\n",
+ "\n",
+ "replica_count = 1\n",
+ "\n",
+ "\n",
+ "# Prepare evaluation command that runs the evaluation harness.\n",
+ "# Set `trust_remote_code = True` because evaluating the model requires\n",
+ "# executing code from the model repository.\n",
+ "# Set `use_accelerate = True` to enable evaluation across multiple GPUs.\n",
+ "eval_command = [\n",
+ " \"python\",\n",
+ " \"main.py\",\n",
+ " \"--model\",\n",
+ " \"hf-causal-experimental\",\n",
+ " \"--tasks\",\n",
+ " f\"{eval_dataset}\",\n",
+ " \"--output_path\",\n",
+ " f\"{eval_output_dir_gcsfuse}\",\n",
+ "]\n",
+ "\n",
+ "if peft_output_dir_gcsfuse:\n",
+ " eval_command += [\n",
+ " \"--model_args\",\n",
+ " f\"pretrained={base_model_id},peft={peft_output_dir_gcsfuse},trust_remote_code=True,use_accelerate=True,device_map_option=auto\",\n",
+ " ]\n",
+ "else:\n",
+ " eval_command += [\n",
+ " \"--model_args\",\n",
+ " f\"pretrained={base_model_id},trust_remote_code=True,use_accelerate=True,device_map_option=auto\",\n",
+ " ]\n",
+ "\n",
+ "\n",
+ "# Pass evaluation arguments and launch job.\n",
+ "worker_pool_specs = [\n",
+ " {\n",
+ " \"machine_spec\": {\n",
+ " \"machine_type\": machine_type,\n",
+ " \"accelerator_type\": accelerator_type,\n",
+ " \"accelerator_count\": accelerator_count,\n",
+ " },\n",
+ " \"replica_count\": replica_count,\n",
+ " \"disk_spec\": {\n",
+ " \"boot_disk_size_gb\": 500,\n",
+ " },\n",
+ " \"container_spec\": {\n",
+ " \"image_uri\": EVAL_DOCKER_URI,\n",
+ " \"command\": eval_command,\n",
+ " \"args\": [],\n",
+ " },\n",
+ " }\n",
+ "]\n",
+ "\n",
+ "# Submit evaluation custom job.\n",
+ "eval_job = aiplatform.CustomJob(\n",
+ " display_name=job_name,\n",
+ " worker_pool_specs=worker_pool_specs,\n",
+ " base_output_dir=eval_output_dir,\n",
+ ")\n",
+ "\n",
+ "eval_job.run()\n",
+ "\n",
+ "print(\"Evaluation results were saved in:\", eval_output_dir)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "cellView": "form",
+ "id": "1f15ed6d375a"
+ },
+ "outputs": [],
+ "source": [
+ "# @title Fetch and print evaluation results\n",
+ "import json\n",
+ "\n",
+ "from google.cloud import storage\n",
+ "\n",
+ "# Fetch evaluation results.\n",
+ "storage_client = storage.Client()\n",
+ "BUCKET_NAME = BUCKET_URI.split(\"gs://\")[1]\n",
+ "bucket = storage_client.get_bucket(BUCKET_NAME)\n",
+ "RESULT_FILE_PATH = eval_output_dir[len(BUCKET_URI) + 1 :]\n",
+ "blob = bucket.blob(RESULT_FILE_PATH)\n",
+ "raw_result = blob.download_as_string()\n",
+ "\n",
+ "# Print evaluation results.\n",
+ "result = json.loads(raw_result)\n",
+ "result_formatted = json.dumps(result, indent=2)\n",
+ "print(f\"Evaluation result:\\n{result_formatted}\")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "cellView": "form",
+ "id": "911406c1561e"
+ },
+ "outputs": [],
+ "source": [
+ "# @title Clean up resources\n",
+ "# Delete evaluation job.\n",
+ "\n",
+ "delete_bucket = False # @param {type:\"boolean\"}\n",
+ "if delete_bucket:\n",
+ " ! gsutil -m rm -r $BUCKET_URI\n",
+ "\n",
+ "eval_job.delete()"
+ ]
+ }
+ ],
+ "metadata": {
+ "colab": {
+ "name": "model_garden_pytorch_falcon_evaluation.ipynb",
+ "toc_visible": true
+ },
+ "kernelspec": {
+ "display_name": "Python 3",
+ "name": "python3"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
}
diff --git a/notebooks/community/model_garden/model_garden_pytorch_falcon_instruct_finetuning.ipynb b/notebooks/community/model_garden/model_garden_pytorch_falcon_instruct_finetuning.ipynb
deleted file mode 100644
index 43bbf40ba..000000000
--- a/notebooks/community/model_garden/model_garden_pytorch_falcon_instruct_finetuning.ipynb
+++ /dev/null
@@ -1,605 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "7d9bbf86da5e"
- },
- "outputs": [],
- "source": [
- "# Copyright 2024 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": "99c1c3fc2ca5"
- },
- "source": [
- "# Vertex AI Model Garden - Falcon Instruct (PEFT Finetuning)\n",
- "\n",
- "\n",
- " \n",
- " \n",
- "  Run in Colab Enterprise\n",
- " \n",
- " | \n",
- " \n",
- " \n",
- "  View on GitHub\n",
- " \n",
- " | \n",
- "
"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "3de7470326a2"
- },
- "source": [
- "## Overview\n",
- "\n",
- "This notebook demonstrates finetuning and deploying Falcon Instruct models with performance efficient finetuning libraries ([PEFT](https://github.com/huggingface/peft)) Falcon Instruct models in Vertex AI.\n",
- "\n",
- "### Objective\n",
- "\n",
- "- Finetune and deploy Falcon Instruct models with PEFT\n",
- "- Cleanup the resources used\n",
- "\n",
- "| Models | LoRA |\n",
- "| :- | :- |\n",
- "| [tiiuae/falcon-7b-instruct](https://huggingface.co/tiiuae/falcon-7b-instruct) | Y |\n",
- "| [tiiuae/falcon-40b-instruct](https://huggingface.co/tiiuae/falcon-40b-instruct) | Y |\n",
- "\n",
- "### Costs\n",
- "\n",
- "This tutorial uses billable components of Google Cloud:\n",
- "\n",
- "* Vertex AI\n",
- "* Cloud Storage\n",
- "\n",
- "Learn about [Vertex AI pricing](https://cloud.google.com/vertex-ai/pricing) and [Cloud Storage pricing](https://cloud.google.com/storage/pricing), and use the [Pricing Calculator](https://cloud.google.com/products/calculator/) to generate a cost estimate based on your projected usage."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "264c07757582"
- },
- "source": [
- "## Run the notebook"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "855d6b96f291"
- },
- "outputs": [],
- "source": [
- "# @title Setup Google Cloud project\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] [Create a Cloud Storage bucket](https://cloud.google.com/storage/docs/creating-buckets) for storing experiment outputs. Set the BUCKET_URI for the experiment environment. The specified Cloud Storage bucket (`BUCKET_URI`) should be located in the same region as where the notebook was launched. Note that a multi-region bucket (eg. \"us\") is not considered a match for a single region covered by the multi-region range (eg. \"us-central1\"). If not set, a unique GCS bucket will be created instead.\n",
- "\n",
- "# Import the necessary packages\n",
- "import os\n",
- "from datetime import datetime\n",
- "from typing import Tuple\n",
- "\n",
- "from google.cloud import aiplatform\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",
- "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",
- "# Cloud Storage bucket for storing the experiment artifacts.\n",
- "# A unique GCS bucket will be created for the purpose of this notebook. If you\n",
- "# prefer using your own GCS bucket, please change the value yourself below.\n",
- "now = datetime.now().strftime(\"%Y%m%d%H%M%S\")\n",
- "BUCKET_URI = \"gs://\" # @param {type:\"string\"}\n",
- "if BUCKET_URI is None or BUCKET_URI.strip() == \"\" or BUCKET_URI == \"gs://\":\n",
- " # Create a unique GCS bucket for this notebook if not specified\n",
- " BUCKET_URI = f\"gs://{PROJECT_ID}-tmp-{now}\"\n",
- " ! gsutil mb -l {REGION} {BUCKET_URI}\n",
- "else:\n",
- " assert BUCKET_URI.startswith(\"gs://\"), \"BUCKET_URI must start with `gs://`.\"\n",
- " BUCKET_NAME = \"/\".join(BUCKET_URI.split(\"/\")[:3])\n",
- " shell_output = ! gsutil ls -Lb {BUCKET_NAME} | grep \"Location constraint:\" | sed \"s/Location constraint://\"\n",
- " bucket_region = shell_output[0].strip().lower()\n",
- " if bucket_region != REGION:\n",
- " raise ValueError(\n",
- " \"Bucket region %s is different from notebook region %s\"\n",
- " % (bucket_region, REGION)\n",
- " )\n",
- "print(f\"Using this GCS Bucket: {BUCKET_URI}\")\n",
- "\n",
- "STAGING_BUCKET = os.path.join(BUCKET_URI, \"temporal\")\n",
- "EXPERIMENT_BUCKET = os.path.join(BUCKET_URI, \"peft\")\n",
- "DATA_BUCKET = os.path.join(EXPERIMENT_BUCKET, \"data\")\n",
- "MODEL_BUCKET = os.path.join(EXPERIMENT_BUCKET, \"model\")\n",
- "\n",
- "# Initialize Vertex AI API.\n",
- "print(\"Initializing Vertex AI API.\")\n",
- "aiplatform.init(project=PROJECT_ID, location=REGION, staging_bucket=STAGING_BUCKET)\n",
- "\n",
- "# Gets the default BUCKET_URI and SERVICE_ACCOUNT if they were not specified by the user.\n",
- "SERVICE_ACCOUNT = None\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",
- "# Create a unique GCS bucket for this notebook, if not specified by the user.\n",
- "if BUCKET_URI is None or BUCKET_URI.strip() == \"\" or BUCKET_URI == \"gs://\":\n",
- " BUCKET_URI = f\"gs://{PROJECT_ID}-tmp-{now}\"\n",
- " ! gsutil mb -l {REGION} {BUCKET_URI}\n",
- "else:\n",
- " shell_output = ! gsutil ls -Lb {BUCKET_URI} | grep \"Location constraint:\" | sed \"s/Location constraint://\"\n",
- " bucket_region = shell_output[0].strip().lower()\n",
- " if bucket_region != REGION:\n",
- " raise ValueError(\n",
- " \"Bucket region %s is different from notebook region %s\"\n",
- " % (bucket_region, REGION)\n",
- " )\n",
- "\n",
- "print(f\"Using this GCS Bucket: {BUCKET_URI}\")\n",
- "\n",
- "# Provision permissions to the SERVICE_ACCOUNT with the GCS bucket\n",
- "BUCKET_NAME = \"/\".join(BUCKET_URI.split(\"/\")[:3])\n",
- "! gsutil iam ch serviceAccount:{SERVICE_ACCOUNT}:roles/storage.admin $BUCKET_NAME\n",
- "\n",
- "! gcloud config set project $PROJECT_ID\n",
- "\n",
- "# The pre-built training and serving docker images.\n",
- "TRAIN_DOCKER_URI = \"us-docker.pkg.dev/vertex-ai/vertex-vision-model-garden-dockers/pytorch-peft-train:20231222_0936_RC00\"\n",
- "PREDICTION_DOCKER_URI = \"us-docker.pkg.dev/vertex-ai/vertex-vision-model-garden-dockers/pytorch-peft-serve:20231129_0948_RC00\"\n",
- "VLLM_DOCKER_URI = \"us-docker.pkg.dev/vertex-ai/vertex-vision-model-garden-dockers/pytorch-vllm-serve:20240410_0916_RC00\"\n",
- "\n",
- "aiplatform.init(project=PROJECT_ID, location=REGION, staging_bucket=STAGING_BUCKET)\n",
- "\n",
- "\n",
- "def create_name_with_datetime(prefix: str) -> str:\n",
- " \"\"\"Creates a name with date time when triggering training or deployment\n",
- " jobs in Vertex AI.\n",
- " \"\"\"\n",
- " return prefix + datetime.now().strftime(\"_%Y%m%d_%H%M%S\")\n",
- "\n",
- "\n",
- "def deploy_model(\n",
- " model_name: str,\n",
- " base_model_id: str,\n",
- " finetuned_lora_model_path: str,\n",
- " service_account: str,\n",
- " task: str,\n",
- " machine_type: str = \"n1-standard-8\",\n",
- " accelerator_type: str = \"NVIDIA_TESLA_V100\",\n",
- " accelerator_count: int = 1,\n",
- ") -> Tuple[aiplatform.Model, aiplatform.Endpoint]:\n",
- " \"\"\"Deploys trained models into Vertex AI.\"\"\"\n",
- " endpoint = aiplatform.Endpoint.create(display_name=f\"{model_name}-endpoint\")\n",
- " serving_env = {\n",
- " \"BASE_MODEL_ID\": base_model_id,\n",
- " \"TASK\": task,\n",
- " \"DEPLOY_SOURCE\": \"notebook\",\n",
- " }\n",
- " if finetuned_lora_model_path:\n",
- " serving_env[\"FINETUNED_LORA_MODEL_PATH\"] = finetuned_lora_model_path\n",
- " model = aiplatform.Model.upload(\n",
- " display_name=model_name,\n",
- " serving_container_image_uri=PREDICTION_DOCKER_URI,\n",
- " serving_container_ports=[7080],\n",
- " serving_container_predict_route=\"/predictions/peft_serving\",\n",
- " serving_container_health_route=\"/ping\",\n",
- " serving_container_environment_variables=serving_env,\n",
- " model_garden_source_model_name=\"publishers/tiiuae/models/falcon-instruct-7b-peft\"\n",
- " )\n",
- " model.deploy(\n",
- " endpoint=endpoint,\n",
- " machine_type=machine_type,\n",
- " accelerator_type=accelerator_type,\n",
- " accelerator_count=accelerator_count,\n",
- " deploy_request_timeout=1800,\n",
- " service_account=service_account,\n",
- " system_labels={\n",
- " \"NOTEBOOK_NAME\": \"model_garden_pytorch_falcon_instruct_finetuning.ipynb\"\n",
- " },\n",
- " )\n",
- " return model, endpoint\n",
- "\n",
- "\n",
- "def deploy_model_vllm(\n",
- " model_name: str,\n",
- " model_id: str,\n",
- " service_account: str,\n",
- " machine_type: str = \"n1-standard-8\",\n",
- " accelerator_type: str = \"NVIDIA_TESLA_V100\",\n",
- " accelerator_count: int = 1,\n",
- " quantization_method: str = \"\",\n",
- ") -> Tuple[aiplatform.Model, aiplatform.Endpoint]:\n",
- " \"\"\"Deploys trained models with vLLM into Vertex AI.\"\"\"\n",
- " endpoint = aiplatform.Endpoint.create(display_name=f\"{model_name}-endpoint\")\n",
- "\n",
- " vllm_args = [\n",
- " \"--host=0.0.0.0\",\n",
- " \"--port=7080\",\n",
- " f\"--model={model_id}\",\n",
- " f\"--tensor-parallel-size={accelerator_count}\",\n",
- " \"--swap-space=16\",\n",
- " \"--gpu-memory-utilization=0.9\",\n",
- " \"--disable-log-stats\",\n",
- " \"--dtype=float16\",\n",
- " \"--trust-remote-code\",\n",
- " ]\n",
- " if quantization_method:\n",
- " vllm_args.append(f\"--quantization={quantization_method}\")\n",
- "\n",
- " model = aiplatform.Model.upload(\n",
- " display_name=model_name,\n",
- " serving_container_image_uri=VLLM_DOCKER_URI,\n",
- " serving_container_command=[\"python\", \"-m\", \"vllm.entrypoints.api_server\"],\n",
- " serving_container_args=vllm_args,\n",
- " serving_container_ports=[7080],\n",
- " serving_container_predict_route=\"/generate\",\n",
- " serving_container_health_route=\"/ping\",\n",
- " model_garden_source_model_name=\"publishers/tiiuae/models/falcon-instruct-7b-peft\"\n",
- " )\n",
- "\n",
- " model.deploy(\n",
- " endpoint=endpoint,\n",
- " machine_type=machine_type,\n",
- " accelerator_type=accelerator_type,\n",
- " accelerator_count=accelerator_count,\n",
- " deploy_request_timeout=1800,\n",
- " service_account=service_account,\n",
- " )\n",
- " return model, endpoint"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "65467b361315"
- },
- "outputs": [],
- "source": [
- "# @title Finetune and deploy Falcon Instruct models with PEFT\n",
- "\n",
- "# @markdown This section demonstrates how to finetune and deploy Falcon Instruct models with PEFT LoRA.\n",
- "\n",
- "# @markdown The peak GPU memory usages are ~11G and ~34G for finetuning LoRA models for [tiiuae/falcon-7b-instruct](https://huggingface.co/tiiuae/falcon-7b-instruct), and [tiiuae/falcon-40b-instruct](https://huggingface.co/tiiuae/falcon-40b-instruct) separately with default training parameters and the example dataset. Falcon-7b-instruct can be finetuned on 1 P100/V100 and falcon-40b-instruct can be finetuned on 1 A100 (40G).\n",
- "\n",
- "# @markdown This example uses the dataset [timdettmers/openassistant-guanaco](https://huggingface.co/datasets/timdettmers/openassistant-guanaco). You can either use a [dataset from huggingface](https://huggingface.co/datasets) or a custom JSONL dataset in [Vertex text model dataset format](https://cloud.google.com/vertex-ai/docs/generative-ai/models/tune-text-models-supervised#dataset-format) stored in Cloud Storage. The `template` parameter is optional.\n",
- "\n",
- "# @markdown To use a custom dataset, you should supply a `gs://` URI to a JSONL file in [Vertex text model dataset format](https://cloud.google.com/vertex-ai/docs/generative-ai/models/tune-text-models-supervised#dataset-format) in the `dataset_name` below.\n",
- "\n",
- "# @markdown For example, here is one data point from the sample dataset `gs://cloud-samples-data/vertex-ai/model-evaluation/peft_train_sample.jsonl`:\n",
- "\n",
- "# @markdown ```json\n",
- "# @markdown {\"input_text\":\"TRANSCRIPT: \\nREASON FOR EVALUATION:,\\n\\n LABEL:\",\"output_text\":\"Chiropractic\"}\n",
- "# @markdown ```\n",
- "\n",
- "# @markdown To use this sample dataset that contains `input_text` and `output_text` fields, set `dataset_name` to `gs://cloud-samples-data/vertex-ai/model-evaluation/peft_train_sample.jsonl` and `template` to `vertex_sample`. For advanced usage with custom datatset fields, see [the template example](https://github.com/tloen/alpaca-lora/blob/main/templates/alpaca.json) and supply your own JSON template as `gs://` URIs.\n",
- "\n",
- "# @markdown Set the base model id.\n",
- "base_model_id = \"tiiuae/falcon-7b-instruct\" # @param [\"tiiuae/falcon-7b-instruct\", \"tiiuae/falcon-40b-instruct\"]\n",
- "\n",
- "# @markdown Set the accelerator type.\n",
- "accelerator_type = \"NVIDIA_TESLA_V100\" # @param[\"NVIDIA_TESLA_V100\", \"NVIDIA_L4\", \"NVIDIA_TESLA_A100\", \"NVIDIA_TESLA_A100_80G\"]\n",
- "\n",
- "# Huggingface dataset name or gs:// URI to a custom JSONL dataset.\n",
- "dataset_name = \"timdettmers/openassistant-guanaco\" # @param {type:\"string\"}\n",
- "# Optional. Template name or gs:// URI to a custom template.\n",
- "template = \"\" # @param {type:\"string\"}\n",
- "\n",
- "# @markdown Set the number of steps in the finetuning job.\n",
- "max_steps = 10 # @param {type:\"integer\"}\n",
- "\n",
- "# Worker pool spec.\n",
- "# Find Vertex AI supported accelerators and regions in:\n",
- "# https://cloud.google.com/vertex-ai/docs/training/configure-compute\n",
- "\n",
- "if \"7b\" in base_model_id:\n",
- " # Uses V100 (16G) to finetune falcon-7b-instruct.\n",
- " if accelerator_type == \"NVIDIA_TESLA_V100\":\n",
- " machine_type = \"n1-standard-8\"\n",
- " accelerator_count = 2\n",
- " # Uses L4 (24G) to finetune falcon-7b-instruct.\n",
- " elif accelerator_type == \"NVIDIA_L4\":\n",
- " machine_type = \"g2-standard-24\"\n",
- " accelerator_count = 2\n",
- " else:\n",
- " raise ValueError(\n",
- " f\"Recommended GPU setting not found for: {accelerator_type} and {base_model_name}.\"\n",
- " )\n",
- "elif \"40b\" in base_model_id:\n",
- " if accelerator_type == \"NVIDIA_TESLA_A100\":\n",
- " machine_type = \"a2-highgpu-1g\"\n",
- " accelerator_count = 1\n",
- " elif accelerator_type == \"NVIDIA_TESLA_V100\":\n",
- " machine_type = \"n1-standard-16\"\n",
- " accelerator_count = 4\n",
- " elif accelerator_type == \"NVIDIA_L4\":\n",
- " machine_type = \"g2-standard-24\"\n",
- " accelerator_count = 2\n",
- " else:\n",
- " raise ValueError(\n",
- " f\"Recommended GPU setting not found for: {accelerator_type} and {base_model_name}.\"\n",
- " )\n",
- "\n",
- "replica_count = 1\n",
- "\n",
- "# Add labels for the finetuning job.\n",
- "labels = {\n",
- " \"mg-source\": \"notebook\",\n",
- " \"mg-notebook-name\": (\n",
- " \"model_garden_pytorch_falcon_instruct_finetuning.ipynb\".split(\".\")[0]\n",
- " ),\n",
- "}\n",
- "\n",
- "labels[\"mg-tune\"] = \"publishers/tiiuae/models/falcon\"\n",
- "versioned_model_id = base_model_id.split(\"/\")[1].replace(\"_\", \"-\")\n",
- "labels[\"versioned-mg-tune\"] = f\"{labels['mg-tune']}-{versioned_model_id}\"\n",
- "\n",
- "# Setup training job.\n",
- "job_name = create_name_with_datetime(\"falcon-finetune-train\")\n",
- "train_job = aiplatform.CustomContainerTrainingJob(\n",
- " display_name=job_name,\n",
- " container_uri=TRAIN_DOCKER_URI,\n",
- " labels=labels,\n",
- ")\n",
- "\n",
- "# Create a GCS folder to store the LORA adapter.\n",
- "finetune_dir = create_name_with_datetime(\"falcon-finetune\")\n",
- "finetune_output_dir = os.path.join(MODEL_BUCKET, finetune_dir)\n",
- "finetune_output_dir_gcsfuse = finetune_output_dir.replace(\"gs://\", \"/gcs/\")\n",
- "\n",
- "# Create a GCS folder to store the merged model with the base model and the\n",
- "# finetuned LORA adapter.\n",
- "merged_model_dir = create_name_with_datetime(\"falcon-merged-model\")\n",
- "merged_model_output_dir = os.path.join(MODEL_BUCKET, merged_model_dir)\n",
- "merged_model_output_dir_gcsfuse = merged_model_output_dir.replace(\"gs://\", \"/gcs/\")\n",
- "\n",
- "# Pass training arguments and launch job.\n",
- "train_job.run(\n",
- " args=[\n",
- " \"--task=instruct-lora\",\n",
- " f\"--pretrained_model_id={base_model_id}\",\n",
- " f\"--dataset_name={dataset_name}\",\n",
- " f\"--output_dir={finetune_output_dir_gcsfuse}\",\n",
- " f\"--merge_base_and_lora_output_dir={merged_model_output_dir_gcsfuse}\",\n",
- " \"--lora_rank=16\",\n",
- " \"--lora_alpha=32\",\n",
- " \"--lora_dropout=0.05\",\n",
- " \"--warmup_steps=10\",\n",
- " f\"--max_steps={max_steps}\",\n",
- " \"--learning_rate=2e-4\",\n",
- " f\"--template={template}\",\n",
- " \"--per_device_train_batch_size=1\",\n",
- " ],\n",
- " replica_count=replica_count,\n",
- " machine_type=machine_type,\n",
- " accelerator_type=accelerator_type,\n",
- " accelerator_count=accelerator_count,\n",
- " boot_disk_size_gb=500,\n",
- ")\n",
- "\n",
- "print(\"The finetuned model can be found at: \", finetune_output_dir)\n",
- "print(\n",
- " \"The finetuned model merged with the base model can be found at: \",\n",
- " merged_model_output_dir,\n",
- ")"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "bf55e38815dc"
- },
- "outputs": [],
- "source": [
- "# @title Deploy to endpoint\n",
- "\n",
- "# @markdown This section uploads the model to Model Registry and deploys it on the Endpoint.\n",
- "\n",
- "# @markdown The model deployment step will take 15 minutes to 40 minutes to complete.\n",
- "\n",
- "# @markdown The peak GPU memory usages for [tiiuae/falcon-7b-instruct](https://huggingface.co/tiiuae/falcon-7b-instruct), and [tiiuae/falcon-40b-instruct](https://huggingface.co/tiiuae/falcon-40b-instruct) with LoRA weights are ~15.5G and ~84G separately with the default settings. Please adjust the machine type, accelerator type and accelerator count accordingly. We use V100 in deployments as an example. Note that V100 serving generally offers better throughput and latency performance than L4 serving, while L4 serving is generally more cost efficient than V100 serving. The serving efficiency of V100 and L4 GPUs is inferior to that of A100 GPUs, but V100 and L4 GPUs are nevertheless good serving solutions if you do not have A100 quota.\n",
- "\n",
- "# Find Vertex AI supported accelerators and regions in:\n",
- "# https://cloud.google.com/vertex-ai/docs/predictions/configure-compute\n",
- "\n",
- "\n",
- "# @markdown Set the base model id.\n",
- "base_model_id = \"tiiuae/falcon-7b-instruct\" # @param [\"tiiuae/falcon-7b-instruct\", \"tiiuae/falcon-40b-instruct\"]\n",
- "\n",
- "# @markdown Set the accelerator type.\n",
- "accelerator_type = \"NVIDIA_TESLA_V100\" # @param[\"NVIDIA_TESLA_V100\", \"NVIDIA_L4\", \"NVIDIA_TESLA_A100\", \"NVIDIA_TESLA_A100_80G\"]\n",
- "\n",
- "\n",
- "if \"7b\" in base_model_id:\n",
- " if accelerator_type == \"NVIDIA_TESLA_A100\":\n",
- " machine_type = \"a2-highgpu-1g\"\n",
- " accelerator_count = 1\n",
- " if accelerator_type == \"NVIDIA_TESLA_V100\":\n",
- " machine_type = \"n1-standard-8\"\n",
- " accelerator_count = 1\n",
- " elif accelerator_type == \"NVIDIA_L4\":\n",
- " machine_type = \"g2-standard-8\"\n",
- " accelerator_count = 1\n",
- " else:\n",
- " raise ValueError(\n",
- " f\"Recommended GPU setting not found for: {accelerator_type} and {base_model_id}.\"\n",
- " )\n",
- "elif \"40b\" in base_model_id:\n",
- " if accelerator_type == \"NVIDIA_TESLA_A100_80GB\":\n",
- " machine_type = \"a2-ultragpu-1g\"\n",
- " accelerator_count = 2\n",
- " elif accelerator_type == \"NVIDIA_TESLA_A100\":\n",
- " machine_type = \"a2-highgpu-1g\"\n",
- " accelerator_count = 4\n",
- " elif accelerator_type == \"NVIDIA_TESLA_V100\":\n",
- " machine_type = \"n1-standard-8\"\n",
- " accelerator_count = 8\n",
- " elif accelerator_type == \"NVIDIA_L4\":\n",
- " machine_type = \"g2-standard-48\"\n",
- " accelerator_count = 4\n",
- " else:\n",
- " raise ValueError(\n",
- " f\"Recommended GPU setting not found for: {accelerator_type} and {base_model_id}.\"\n",
- " )\n",
- "\n",
- "if base_model_id == \"tiiuae/falcon-7b-instruct\":\n",
- " model, endpoint = deploy_model(\n",
- " model_name=create_name_with_datetime(prefix=\"falcon-instruct-serve\"),\n",
- " base_model_id=base_model_id,\n",
- " finetuned_lora_model_path=os.path.join(\n",
- " finetune_output_dir, f\"checkpoint-{max_steps}\"\n",
- " ), # This will avoid override finetuning models.\n",
- " service_account=SERVICE_ACCOUNT,\n",
- " task=\"instruct-lora\",\n",
- " machine_type=machine_type,\n",
- " accelerator_type=accelerator_type,\n",
- " accelerator_count=accelerator_count,\n",
- " )\n",
- "else:\n",
- " model, endpoint = deploy_model_vllm(\n",
- " model_name=create_name_with_datetime(prefix=\"falcon-instruct-vllm\"),\n",
- " model_id=merged_model_output_dir,\n",
- " service_account=SERVICE_ACCOUNT,\n",
- " machine_type=machine_type,\n",
- " accelerator_type=accelerator_type,\n",
- " accelerator_count=accelerator_count,\n",
- " )\n",
- "\n",
- "print(\"endpoint_name:\", endpoint.name)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "id": "4ab04da3ec9a"
- },
- "outputs": [],
- "source": [
- "# @markdown NOTE: After the deployment succeeds, the base model weights will be downloaded on the fly from the original location and LoRA model weights will be downloaded from the GCS bucket used in training above. Thus, an additional 10-30 minutes of waiting time is needed **after** the above model deployment step succeeds and before you can run the next step below. Otherwise you might see a `ServiceUnavailable: 503 502:Bad Gateway` error when you send requests to the endpoint.\n",
- "\n",
- "# @markdown Once deployment succeeds, you can send requests to the endpoint with text prompts.\n",
- "\n",
- "# @markdown Example:\n",
- "\n",
- "# @markdown ```\n",
- "# @markdown Human: What is a car?\n",
- "# @markdown Assistant: A car, or a motor car, is a road-connected human-transportation system used to move people or goods from one place to another. The term also encompasses a wide range of vehicles, including motorboats, trains, and aircrafts. Cars typically have four wheels, a cabin for passengers, and an engine or motor. They have been around since the early 19th century and are now one of the most popular forms of transportation, used for daily commuting, shopping, and other purposes.\n",
- "# @markdown ```\n",
- "\n",
- "# @markdown Additionally, you can moderate the generated text with Vertex AI. See [Moderate text documentation](https://cloud.google.com/natural-language/docs/moderating-text) for more details.\n",
- "\n",
- "# Loads an existing endpoint instance using the endpoint name:\n",
- "# - Using `endpoint_name = endpoint.name` allows us to get the\n",
- "# endpoint name of the endpoint `endpoint` created in the cell\n",
- "# above.\n",
- "# - Alternatively, you can set `endpoint_name = \"1234567890123456789\"` to load\n",
- "# an existing endpoint with the ID 1234567890123456789.\n",
- "# You may uncomment the code below to load an existing endpoint.\n",
- "\n",
- "# endpoint_name = endpoint.name\n",
- "# # endpoint_name = \"\" # @param {type:\"string\"}\n",
- "# aip_endpoint_name = (\n",
- "# f\"projects/{PROJECT_ID}/locations/{REGION}/endpoints/{endpoint_name}\"\n",
- "# )\n",
- "# endpoint = aiplatform.Endpoint(aip_endpoint_name)\n",
- "\n",
- "prompt = \"What is a car?\" # @param {type:\"string\"}\n",
- "max_tokens = 50 # @param {type:\"integer\"}\n",
- "temperature = 1.0 # @param {type:\"number\"}\n",
- "top_p = 1.0 # @param {type:\"number\"}\n",
- "top_k = 10 # @param {type:\"number\"}\n",
- "\n",
- "\n",
- "instances = [\n",
- " {\n",
- " \"prompt\": prompt,\n",
- " \"max_tokens\": max_tokens,\n",
- " \"temperature\": temperature,\n",
- " \"top_p\": top_p,\n",
- " \"top_k\": top_k,\n",
- " },\n",
- "]\n",
- "response = endpoint.predict(instances=instances)\n",
- "\n",
- "for prediction in response.predictions:\n",
- " print(prediction)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "911406c1561e"
- },
- "outputs": [],
- "source": [
- "# @markdown Delete the experiment models and endpoints to recycle the resources\n",
- "# @markdown and avoid unnecessary continouous charges that may incur.\n",
- "\n",
- "# @title Clean up resources\n",
- "\n",
- "delete_bucket = False # @param {type:\"boolean\"}\n",
- "if delete_bucket:\n",
- " ! gsutil -m rm -r $BUCKET_URI\n",
- "\n",
- "# Delete custom train, quantization, and evaluation jobs.\n",
- "train_job.delete()\n",
- "\n",
- "# Undeploy models and delete endpoints.\n",
- "endpoint.delete(force=True)\n",
- "\n",
- "# Delete models.\n",
- "model.delete()"
- ]
- }
- ],
- "metadata": {
- "colab": {
- "name": "model_garden_pytorch_falcon_instruct_finetuning.ipynb",
- "toc_visible": true
- },
- "kernelspec": {
- "display_name": "Python 3",
- "name": "python3"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 0
-}
diff --git a/notebooks/community/model_garden/model_garden_pytorch_gemma_peft_finetuning_hf.ipynb b/notebooks/community/model_garden/model_garden_pytorch_gemma_peft_finetuning_hf.ipynb
deleted file mode 100644
index 82858b807..000000000
--- a/notebooks/community/model_garden/model_garden_pytorch_gemma_peft_finetuning_hf.ipynb
+++ /dev/null
@@ -1,966 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "7d9bbf86da5e"
- },
- "outputs": [],
- "source": [
- "# Copyright 2026 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": "99c1c3fc2ca5"
- },
- "source": [
- "# Vertex AI Model Garden - Gemma Finetuning (PEFT + vLLM)\n",
- "\n",
- "\n",
- " \n",
- " \n",
- "  Run in Colab Enterprise\n",
- " \n",
- " | \n",
- " \n",
- " \n",
- "  View on GitHub\n",
- " \n",
- " | \n",
- "
"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "3de7470326a2"
- },
- "source": [
- "## Overview\n",
- "\n",
- "This notebook demonstrates finetuning and deploying Gemma models with [Vertex AI Custom Training Job](https://cloud.google.com/vertex-ai/docs/training/create-custom-job). Using Vertex AI Pipelines is the quickest way to start finetuning Gemma models, while using a Vertex AI Custom Training Job allows for a higher level of customization and control over the finetuning job. All of the examples in this notebook use parameter efficient finetuning methods [PEFT](https://github.com/huggingface/peft) to reduce training and storage costs.\n",
- "\n",
- "This notebook deploys the model with the [vLLM](https://github.com/vllm-project/vllm) docker and uses [Text moderation APIs](https://cloud.google.com/natural-language/docs/moderating-text) to analyze predictions against a list of safety attributes.\n",
- "\n",
- "\n",
- "### Objective\n",
- "\n",
- "- Finetune and deploy Gemma models with a Vertex AI Custom Training Job.\n",
- "- Send prediction requests to your finetuned Gemma model.\n",
- "\n",
- "\n",
- "### Costs\n",
- "\n",
- "This tutorial uses billable components of Google Cloud:\n",
- "\n",
- "* Vertex AI\n",
- "* Cloud Storage\n",
- "* Cloud NL APIs\n",
- "\n",
- "Learn about [Vertex AI pricing](https://cloud.google.com/vertex-ai/pricing), [Cloud Storage pricing](https://cloud.google.com/storage/pricing), [Cloud NL API pricing](https://cloud.google.com/natural-language/pricing) and use the [Pricing Calculator](https://cloud.google.com/products/calculator/) to generate a cost estimate based on your projected usage."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "264c07757582"
- },
- "source": [
- "## Before you begin"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "wQf_xXTXzjaS"
- },
- "outputs": [],
- "source": [
- "# @title Install Python Packages for Finetuning\n",
- "\n",
- "# @markdown 1. Install google-cloud-aiplatform package and restart the session if instructed.\n",
- "! pip install --upgrade --quiet google-cloud-aiplatform==1.130.0\n",
- "\n",
- "# @markdown 2. Install packages to validate dataset with template.\n",
- "! pip install --upgrade --quiet accelerate==0.31.0\n",
- "! pip install --upgrade --quiet transformers==4.43.1\n",
- "! pip install --upgrade --quiet datasets==2.19.2\n",
- "\n",
- "# Load local tensorboard.\n",
- "%load_ext tensorboard"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "B9p8QmmcD_OP"
- },
- "outputs": [],
- "source": [
- "# @title Setup Google Cloud project\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. For finetuning, **[click here](https://console.cloud.google.com/iam-admin/quotas?location=us-central1&metric=aiplatform.googleapis.com%2Frestricted_image_training_nvidia_a100_80gb_gpus)** to check if your project already has the required 8 Nvidia A100 80 GB GPUs in the us-central1 region. If yes, then run this notebook in the us-central1 region. If you do not have 8 Nvidia A100 80 GPUs or have more GPU requirements than this, then schedule your job with Nvidia H100 GPUs via Dynamic Workload Scheduler using [these instructions](https://cloud.google.com/vertex-ai/docs/training/schedule-jobs-dws). For Dynamic Workload Scheduler, check the [us-central1](https://console.cloud.google.com/iam-admin/quotas?location=us-central1&metric=aiplatform.googleapis.com%2Fcustom_model_training_preemptible_nvidia_h100_gpus) or [europe-west4](https://console.cloud.google.com/iam-admin/quotas?location=europe-west4&metric=aiplatform.googleapis.com%2Fcustom_model_training_preemptible_nvidia_h100_gpus) quota for Nvidia H100 GPUs. If you do not have enough GPUs, then you can follow [these instructions](https://cloud.google.com/docs/quotas/view-manage#viewing_your_quota_console) to request quota.\n",
- "\n",
- "# @markdown 3. For serving, **[click here](https://console.cloud.google.com/iam-admin/quotas?location=us-central1&metric=aiplatform.googleapis.com%2Fcustom_model_serving_nvidia_l4_gpus)** to check if your project already has the required 1 L4 GPU in the us-central1 region. If yes, then run this notebook in the us-central1 region. If you need more L4 GPUs for your project, then you can follow [these instructions](https://cloud.google.com/docs/quotas/view-manage#viewing_your_quota_console) to request more. Alternatively, 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).\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",
- "# @markdown 4. **[Optional]** [Create a Cloud Storage bucket](https://cloud.google.com/storage/docs/creating-buckets) for storing experiment outputs. Set the BUCKET_URI for the experiment environment. The specified Cloud Storage bucket (`BUCKET_URI`) should be located in the same region as where the notebook was launched. Note that a multi-region bucket (eg. \"us\") is not considered a match for a single region covered by the multi-region range (eg. \"us-central1\"). If not set, a unique GCS bucket will be created instead.\n",
- "\n",
- "BUCKET_URI = \"gs://\" # @param {type:\"string\"}\n",
- "\n",
- "# @markdown 5. **[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",
- "# Import the necessary packages\n",
- "! rm -rf vertex-ai-samples && git clone https://github.com/GoogleCloudPlatform/vertex-ai-samples.git\n",
- "! cd vertex-ai-samples && git reset --hard 0727e19520cf7957bceb701c248221bd3dbe4f1f\n",
- "\n",
- "import datetime\n",
- "import importlib\n",
- "import os\n",
- "import uuid\n",
- "from typing import Tuple\n",
- "\n",
- "from google.cloud import aiplatform, language\n",
- "from google.cloud.aiplatform.compat.types import \\\n",
- " custom_job as gca_custom_job_compat\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",
- "models, endpoints = {}, {}\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",
- " if not os.environ.get(\"GOOGLE_CLOUD_REGION\"):\n",
- " raise ValueError(\n",
- " \"REGION must be set. See\"\n",
- " \" https://cloud.google.com/vertex-ai/docs/general/locations for\"\n",
- " \" available cloud locations.\"\n",
- " )\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",
- "# Cloud Storage bucket for storing the experiment artifacts.\n",
- "# A unique GCS bucket will be created for the purpose of this notebook. If you\n",
- "# prefer using your own GCS bucket, change the value yourself below.\n",
- "now = datetime.datetime.now().strftime(\"%Y%m%d%H%M%S\")\n",
- "BUCKET_NAME = \"/\".join(BUCKET_URI.split(\"/\")[:3])\n",
- "\n",
- "if BUCKET_URI is None or BUCKET_URI.strip() == \"\" or BUCKET_URI == \"gs://\":\n",
- " BUCKET_URI = f\"gs://{PROJECT_ID}-tmp-{now}-{str(uuid.uuid4())[:4]}\"\n",
- " BUCKET_NAME = \"/\".join(BUCKET_URI.split(\"/\")[:3])\n",
- " ! gsutil mb -l {REGION} {BUCKET_URI}\n",
- "else:\n",
- " assert BUCKET_URI.startswith(\"gs://\"), \"BUCKET_URI must start with `gs://`.\"\n",
- " shell_output = ! gsutil ls -Lb {BUCKET_NAME} | grep \"Location constraint:\" | sed \"s/Location constraint://\"\n",
- " bucket_region = shell_output[0].strip().lower()\n",
- " if bucket_region != REGION:\n",
- " raise ValueError(\n",
- " \"Bucket region %s is different from notebook region %s\"\n",
- " % (bucket_region, REGION)\n",
- " )\n",
- "print(f\"Using this GCS Bucket: {BUCKET_URI}\")\n",
- "\n",
- "STAGING_BUCKET = os.path.join(BUCKET_URI, \"temporal\")\n",
- "MODEL_BUCKET = os.path.join(BUCKET_URI, \"gemma\")\n",
- "\n",
- "\n",
- "# Initialize Vertex AI API.\n",
- "print(\"Initializing Vertex AI API.\")\n",
- "aiplatform.init(project=PROJECT_ID, location=REGION, staging_bucket=STAGING_BUCKET)\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",
- "\n",
- "# Provision permissions to the SERVICE_ACCOUNT with the GCS bucket\n",
- "! gsutil iam ch serviceAccount:{SERVICE_ACCOUNT}:roles/storage.admin $BUCKET_NAME\n",
- "\n",
- "! gcloud config set project $PROJECT_ID\n",
- "! gcloud projects add-iam-policy-binding --no-user-output-enabled {PROJECT_ID} --member=serviceAccount:{SERVICE_ACCOUNT} --role=\"roles/storage.admin\"\n",
- "! gcloud projects add-iam-policy-binding --no-user-output-enabled {PROJECT_ID} --member=serviceAccount:{SERVICE_ACCOUNT} --role=\"roles/aiplatform.user\"\n",
- "\n",
- "# @markdown ## Access Gemma Models\n",
- "\n",
- "# @markdown Provide a Hugging Face User Access Token (read) to access the Gemma models. You can follow the [Hugging Face documentation](https://huggingface.co/docs/hub/en/security-tokens) to create a **read** access token and put it in the `HF_TOKEN` field below.\n",
- "HF_TOKEN = \"\" # @param {type:\"string\", isTemplate:true}\n",
- "assert HF_TOKEN, \"Provide a read HF_TOKEN to load models from Hugging Face.\"\n",
- "\n",
- "\n",
- "def moderate_text(text: str) -> language.ModerateTextResponse:\n",
- " \"\"\"Calls Vertex AI APIs to analyze text moderations.\"\"\"\n",
- " client = language.LanguageServiceClient()\n",
- " document = language.Document(\n",
- " content=text,\n",
- " type_=language.Document.Type.PLAIN_TEXT,\n",
- " )\n",
- " return client.moderate_text(document=document)\n",
- "\n",
- "\n",
- "def show_text_moderation(text: str, response: language.ModerateTextResponse) -> None:\n",
- " \"\"\"Shows text moderation results.\"\"\"\n",
- " import pandas as pd\n",
- "\n",
- " def confidence(category: language.ClassificationCategory) -> float:\n",
- " return category.confidence\n",
- "\n",
- " columns = [\"category\", \"confidence\"]\n",
- " categories = sorted(response.moderation_categories, key=confidence, reverse=True)\n",
- " data = ((category.name, category.confidence) for category in categories)\n",
- " df = pd.DataFrame(columns=columns, data=data)\n",
- "\n",
- " print(f\"Text analyzed:\\n{text}\")\n",
- " print(df.to_markdown(index=False, tablefmt=\"presto\", floatfmt=\".0%\"))"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "Pq4iF00YG_4T"
- },
- "source": [
- "## Finetune with Vertex AI Custom Training Jobs\n",
- "\n",
- "This section demonstrates how to finetune and deploy Gemma models with PEFT LoRA on Vertex AI Custom Training Jobs. LoRA (Low-Rank Adaptation) is one approach of PEFT (Parameter Efficient FineTuning), where pretrained model weights are frozen and rank decomposition matrices representing the change in model weights are trained during finetuning. Read more about LoRA in the following publication: [Hu, E.J., Shen, Y., Wallis, P., Allen-Zhu, Z., Li, Y., Wang, S., Wang, L. and Chen, W., 2021. Lora: Low-rank adaptation of large language models. *arXiv preprint arXiv:2106.09685*](https://arxiv.org/abs/2106.09685)."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "FD1TvpYZzjaS"
- },
- "outputs": [],
- "source": [
- "# @title Set dataset\n",
- "\n",
- "# @markdown Use the Vertex AI SDK to create and run the custom training jobs.\n",
- "\n",
- "# @markdown This notebook uses [timdettmers/openassistant-guanaco](https://huggingface.co/datasets/timdettmers/openassistant-guanaco) dataset as an example.\n",
- "# @markdown You can set `dataset_name` to any existing [Hugging Face dataset](https://huggingface.co/datasets) name, and set `instruct_column_in_dataset` to the name of the dataset column containing training data. The [timdettmers/openassistant-guanaco](https://huggingface.co/datasets/timdettmers/openassistant-guanaco) has only one column `text`, and therefore we set `instruct_column_in_dataset` to `text` in this notebook.\n",
- "\n",
- "# @markdown ### (Optional) Prepare a custom JSONL dataset for finetuning\n",
- "\n",
- "# @markdown You can prepare a JSONL file where each line is a valid JSON string as your custom training dataset. For example, here is one line from the [timdettmers/openassistant-guanaco](https://huggingface.co/datasets/timdettmers/openassistant-guanaco) dataset:\n",
- "# @markdown ```\n",
- "# @markdown {\"text\": \"### Human: Hola### Assistant: \\u00a1Hola! \\u00bfEn qu\\u00e9 puedo ayudarte hoy?\"}\n",
- "# @markdown ```\n",
- "\n",
- "# @markdown The JSON object has a key `text`, which should match `instruct_column_in_dataset`; The value should be one training data point, i.e. a string. After you prepared your JSONL file, you can either upload it to [Hugging Face datasets](https://huggingface.co/datasets) or [Google Cloud Storage](https://cloud.google.com/storage).\n",
- "\n",
- "# @markdown - To upload a JSONL dataset to [Hugging Face datasets](https://huggingface.co/datasets), follow the instructions on [Uploading Datasets](https://huggingface.co/docs/hub/en/datasets-adding). Then, set `dataset_name` to the name of your newly created dataset on Hugging Face.\n",
- "\n",
- "# @markdown - To upload a JSONL dataset to [Google Cloud Storage](https://cloud.google.com/storage), follow the instructions on [Upload objects from a filesystem](https://cloud.google.com/storage/docs/uploading-objects). Then, set `dataset_name` to the `gs://` URI to your JSONL file. For example: `gs://cloud-samples-data/vertex-ai/model-evaluation/peft_train_sample.jsonl`.\n",
- "\n",
- "# @markdown Optionally update the `instruct_column_in_dataset` field below if your JSON objects use a key other than the default `text`.\n",
- "\n",
- "# @markdown ### (Optional) Format your data with custom JSON template\n",
- "\n",
- "# @markdown Sometimes, your dataset might have multiple text columns and you want to construct the training data with a template. You can prepare a JSON template in the following format:\n",
- "\n",
- "# @markdown ```\n",
- "# @markdown {\n",
- "# @markdown \"description\": \"Template that accepts text-bison format.\",\n",
- "# @markdown \"source\": \"https://cloud.google.com/vertex-ai/generative-ai/docs/models/tune-text-models-supervised#dataset-format\",\n",
- "# @markdown \"prompt_input\": \"\\n\\n<|start_header_id|>user<|end_header_id|>\\n\\n{input_text}<|eot_id|>\\n\\n<|start_header_id|>assistant<|end_header_id|>\\n\\n{output_text}<|eot_id|>\",\n",
- "# @markdown \"instruction_separator\": \"<|start_header_id|>user<|end_header_id|>\\n\\n\",\n",
- "# @markdown \"response_separator\": \"<|start_header_id|>assistant<|end_header_id|>\\n\\n\"\n",
- "# @markdown }\n",
- "# @markdown ```\n",
- "\n",
- "# @markdown As an example, the template above can be used to format the following training data (this line comes from `gs://cloud-samples-data/vertex-ai/model-evaluation/peft_train_sample.jsonl`):\n",
- "\n",
- "# @markdown ```\n",
- "# @markdown {\"input_text\":\"TRANSCRIPT: \\nREASON FOR EVALUATION:,\\n\\n LABEL:\",\"output_text\":\"Chiropractic\"}\n",
- "# @markdown ```\n",
- "\n",
- "# @markdown This example template simply concatenates `input_text` with `output_text` with some special tokens in between.\n",
- "# @markdown\n",
- "# @markdown To try such custom dataset, you can make the following changes:\n",
- "# @markdown 1. Set `template` to `llama3-text-bison`\n",
- "# @markdown 1. Set `train_dataset_name` to `gs://cloud-samples-data/vertex-ai/model-evaluation/peft_train_sample.jsonl`\n",
- "# @markdown 1. Set `train_split_name` to `train`\n",
- "# @markdown 1. Set `eval_dataset_name` to `gs://cloud-samples-data/vertex-ai/model-evaluation/peft_eval_sample.jsonl`\n",
- "# @markdown 1. Set `eval_split_name` to `train` (**NOT** `test`)\n",
- "# @markdown 1. Set `instruct_column_in_dataset` as `input_text`.\n",
- "\n",
- "# Template name or gs:// URI to a custom template.\n",
- "template = \"openassistant-guanaco\" # @param {type:\"string\"}\n",
- "\n",
- "# Hugging Face dataset name or gs:// URI to a custom JSONL dataset.\n",
- "train_dataset_name = \"timdettmers/openassistant-guanaco\" # @param {type:\"string\"}\n",
- "train_split_name = \"train\" # @param {type:\"string\"}\n",
- "eval_dataset_name = \"timdettmers/openassistant-guanaco\" # @param {type:\"string\"}\n",
- "eval_split_name = \"test\" # @param {type:\"string\"}\n",
- "\n",
- "# Name of the dataset column containing training text input.\n",
- "instruct_column_in_dataset = \"text\" # @param {type:\"string\"}"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "wBxu3rEyzjaT"
- },
- "outputs": [],
- "source": [
- "# @title Set model\n",
- "\n",
- "# @markdown Select a model variant of Gemma.\n",
- "base_model_id = \"gemma-1.1-2b-it\" # @param[\"gemma-2b\", \"gemma-2b-it\", \"gemma-7b\", \"gemma-7b-it\", \"gemma-1.1-2b-it\", \"gemma-1.1-7b-it\"] {isTemplate:true}\n",
- "pretrained_model_id = os.path.join(\"google/\", base_model_id)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "jIEDYAnPzjaT"
- },
- "outputs": [],
- "source": [
- "# @title Validate Dataset with Template\n",
- "\n",
- "# @markdown This section validates the train and eval datasets with the template before starting the fine tuning process.\n",
- "\n",
- "import transformers\n",
- "\n",
- "dataset_validation_util = importlib.import_module(\n",
- " \"vertex-ai-samples.community-content.vertex_model_garden.model_oss.notebook_util.dataset_validation_util\"\n",
- ")\n",
- "\n",
- "if dataset_validation_util.is_gcs_path(pretrained_model_id):\n",
- " # Download tokenizer.\n",
- " ! mkdir tokenizer\n",
- " ! gsutil cp {pretrained_model_id}/tokenizer.json ./tokenizer\n",
- " ! gsutil cp {pretrained_model_id}/config.json ./tokenizer\n",
- " tokenizer_path = \"./tokenizer\"\n",
- " access_token = \"\"\n",
- "else:\n",
- " tokenizer_path = pretrained_model_id\n",
- " access_token = HF_TOKEN\n",
- "\n",
- "tokenizer = transformers.AutoTokenizer.from_pretrained(\n",
- " tokenizer_path,\n",
- " trust_remote_code=False,\n",
- " use_fast=True,\n",
- " token=access_token,\n",
- ")\n",
- "\n",
- "# Validate the train dataset.\n",
- "dataset_validation_util.validate_dataset_with_template(\n",
- " dataset_name=train_dataset_name,\n",
- " split=train_split_name,\n",
- " input_column=instruct_column_in_dataset,\n",
- " template=template,\n",
- " use_multiprocessing=False,\n",
- " tokenizer=tokenizer,\n",
- ")\n",
- "\n",
- "# Validate the eval dataset.\n",
- "dataset_validation_util.validate_dataset_with_template(\n",
- " dataset_name=eval_dataset_name,\n",
- " split=eval_split_name,\n",
- " input_column=instruct_column_in_dataset,\n",
- " template=template,\n",
- " use_multiprocessing=False,\n",
- " tokenizer=tokenizer,\n",
- ")"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "rU7ekq-0zjaT"
- },
- "outputs": [],
- "source": [
- "# @title Finetune\n",
- "# @markdown This section demonstrates how to finetune the Gemma model and merge the finetuned LoRA adapter with the base model on Vertex AI. It uses the Vertex AI SDK to create and run the custom training jobs.\n",
- "\n",
- "# @markdown The training job takes approximately between 10 to 20 mins to set-up. Once done, the training job is expected to take around 20 mins with the default configuration. To find the training time, throughput, and memory usage of your training job, you can go to the training logs and check the log line of the last training epoch.\n",
- "\n",
- "# @markdown **Note**:\n",
- "# @markdown 1. We recommend setting `finetuning_precision_mode` to `4bit` because it enables using fewer hardware resources for finetuning.\n",
- "# @markdown 1. If `max_steps > 0`, it takes precedence over `epochs`. One can set a small `max_steps` value to quickly check the pipeline.\n",
- "\n",
- "# @markdown Accelerator type to use for training.\n",
- "training_accelerator_type = \"NVIDIA_A100_80GB\" # @param [\"NVIDIA_A100_80GB\", \"NVIDIA_H100_80GB\"]\n",
- "\n",
- "# The pre-built training docker image.\n",
- "if training_accelerator_type == \"NVIDIA_A100_80GB\":\n",
- " repo = \"us-docker.pkg.dev/vertex-ai-restricted\"\n",
- " is_restricted_image = True\n",
- " is_dynamic_workload_scheduler = False\n",
- " dws_kwargs = {}\n",
- "else:\n",
- " repo = \"us-docker.pkg.dev/vertex-ai\"\n",
- " is_restricted_image = False\n",
- " is_dynamic_workload_scheduler = True\n",
- " dws_kwargs = {\n",
- " \"max_wait_duration\": 1800, # 30 minutes\n",
- " \"scheduling_strategy\": gca_custom_job_compat.Scheduling.Strategy.FLEX_START,\n",
- " }\n",
- "\n",
- "TRAIN_DOCKER_URI = (\n",
- " f\"{repo}/vertex-vision-model-garden-dockers/pytorch-peft-train:stable_20240909\"\n",
- ")\n",
- "\n",
- "# Worker pool spec.\n",
- "if training_accelerator_type == \"NVIDIA_A100_80GB\":\n",
- " per_node_accelerator_count = 8\n",
- " training_machine_type = \"a2-ultragpu-8g\"\n",
- "elif training_accelerator_type == \"NVIDIA_H100_80GB\":\n",
- " per_node_accelerator_count = 8\n",
- " training_machine_type = \"a3-highgpu-8g\"\n",
- "else:\n",
- " raise ValueError(\n",
- " f\"Recommended machine settings not found for: {training_accelerator_type}. To use another accelerator type, edit this code block to pass in an appropriate `training_machine_type`, `training_accelerator_type`, and `per_node_accelerator_count` to the deploy_model_vllm function by clicking `Show Code` and then modifying the code.\"\n",
- " )\n",
- "\n",
- "# @markdown Batch size for finetuning.\n",
- "per_device_train_batch_size = 1 # @param{type:\"integer\"}\n",
- "# @markdown Number of updates steps to accumulate the gradients for, before performing a backward/update pass.\n",
- "gradient_accumulation_steps = 4 # @param{type:\"integer\"}\n",
- "# @markdown Maximum sequence length.\n",
- "max_seq_length = 4096 # @param{type:\"integer\"}\n",
- "# @markdown Setting a positive `max_steps` here will override `num_epochs`.\n",
- "max_steps = -1 # @param{type:\"integer\"}\n",
- "num_epochs = 1.0 # @param{type:\"number\"}\n",
- "# @markdown Precision mode for finetuning.\n",
- "finetuning_precision_mode = \"4bit\" # @param [\"4bit\", \"8bit\", \"float16\"]\n",
- "# @markdown Learning rate.\n",
- "learning_rate = 5e-5 # @param{type:\"number\"}\n",
- "# @markdown The scheduler type to use.\n",
- "lr_scheduler_type = \"cosine\" # @param{type:\"string\"}\n",
- "# @markdown LoRA parameters.\n",
- "lora_rank = 16 # @param{type:\"integer\"}\n",
- "lora_alpha = 32 # @param{type:\"integer\"}\n",
- "lora_dropout = 0.05 # @param{type:\"number\"}\n",
- "# Activates gradient checkpointing for the current model (may be referred to as activation checkpointing or checkpoint activations in other frameworks).\n",
- "enable_gradient_checkpointing = True\n",
- "# Attention implementation to use in the model.\n",
- "attn_implementation = \"eager\"\n",
- "# The optimizer for which to schedule the learning rate.\n",
- "optimizer = \"paged_adamw_32bit\"\n",
- "# Define the proportion of training to be dedicated to a linear warmup where learning rate gradually increases.\n",
- "warmup_ratio = \"0.01\"\n",
- "# The list or string of integrations to report the results and logs to.\n",
- "report_to = \"tensorboard\"\n",
- "# Number of updates steps before two checkpoint saves.\n",
- "save_steps = 10\n",
- "# Number of update steps between two logs.\n",
- "logging_steps = save_steps\n",
- "# Train precision of the model.\n",
- "train_precision = \"bfloat16\"\n",
- "\n",
- "replica_count = 1\n",
- "\n",
- "common_util.check_quota(\n",
- " project_id=PROJECT_ID,\n",
- " region=REGION,\n",
- " accelerator_type=training_accelerator_type,\n",
- " accelerator_count=per_node_accelerator_count * replica_count,\n",
- " is_for_training=True,\n",
- " is_restricted_image=is_restricted_image,\n",
- " is_dynamic_workload_scheduler=is_dynamic_workload_scheduler,\n",
- ")\n",
- "\n",
- "job_name = common_util.get_job_name_with_datetime(\"gemma-lora-train\")\n",
- "\n",
- "base_output_dir = os.path.join(STAGING_BUCKET, job_name)\n",
- "# Create a GCS folder to store the LORA adapter.\n",
- "lora_output_dir = os.path.join(base_output_dir, \"adapter\")\n",
- "# Create a GCS folder to store the merged model with the base model and the\n",
- "# finetuned LORA adapter.\n",
- "merged_model_output_dir = os.path.join(base_output_dir, \"merged-model\")\n",
- "\n",
- "# Add labels for the finetuning job.\n",
- "labels = {\n",
- " \"mg-source\": \"notebook\",\n",
- " \"mg-notebook-name\": \"model_garden_pytorch_gemma_peft_finetuning_hf.ipynb\".split(\n",
- " \".\"\n",
- " )[0],\n",
- "}\n",
- "\n",
- "labels[\"mg-tune\"] = \"publishers-google-models-gemma\"\n",
- "versioned_model_id = base_model_id.lower().replace(\".\", \"-\")\n",
- "labels[\"versioned-mg-tune\"] = f\"{labels['mg-tune']}-{versioned_model_id}\"\n",
- "\n",
- "eval_args = [\n",
- " f\"--eval_dataset_path={eval_dataset_name}\",\n",
- " f\"--eval_column={instruct_column_in_dataset}\",\n",
- " f\"--eval_template={template}\",\n",
- " f\"--eval_split={eval_split_name}\",\n",
- " f\"--eval_steps={save_steps}\",\n",
- " \"--eval_tasks=builtin_eval\",\n",
- " \"--eval_metric_name=loss\",\n",
- "]\n",
- "\n",
- "train_job_args = [\n",
- " \"--config_file=vertex_vision_model_garden_peft/deepspeed_zero2_8gpu.yaml\",\n",
- " \"--task=instruct-lora\",\n",
- " \"--completion_only=True\",\n",
- " f\"--pretrained_model_id={pretrained_model_id}\",\n",
- " f\"--dataset_name={train_dataset_name}\",\n",
- " f\"--train_split_name={train_split_name}\",\n",
- " f\"--instruct_column_in_dataset={instruct_column_in_dataset}\",\n",
- " f\"--output_dir={lora_output_dir}\",\n",
- " f\"--merge_base_and_lora_output_dir={merged_model_output_dir}\",\n",
- " f\"--per_device_train_batch_size={per_device_train_batch_size}\",\n",
- " f\"--gradient_accumulation_steps={gradient_accumulation_steps}\",\n",
- " f\"--lora_rank={lora_rank}\",\n",
- " f\"--lora_alpha={lora_alpha}\",\n",
- " f\"--lora_dropout={lora_dropout}\",\n",
- " f\"--max_steps={max_steps}\",\n",
- " f\"--max_seq_length={max_seq_length}\",\n",
- " f\"--learning_rate={learning_rate}\",\n",
- " f\"--lr_scheduler_type={lr_scheduler_type}\",\n",
- " f\"--precision_mode={finetuning_precision_mode}\",\n",
- " f\"--train_precision={train_precision}\",\n",
- " f\"--enable_gradient_checkpointing={enable_gradient_checkpointing}\",\n",
- " f\"--num_epochs={num_epochs}\",\n",
- " f\"--attn_implementation={attn_implementation}\",\n",
- " f\"--optimizer={optimizer}\",\n",
- " f\"--warmup_ratio={warmup_ratio}\",\n",
- " f\"--report_to={report_to}\",\n",
- " f\"--logging_output_dir={base_output_dir}\",\n",
- " f\"--save_steps={save_steps}\",\n",
- " f\"--logging_steps={logging_steps}\",\n",
- " f\"--template={template}\",\n",
- " f\"--huggingface_access_token={HF_TOKEN}\",\n",
- "] + eval_args\n",
- "\n",
- "# Pass training arguments and launch job.\n",
- "train_job = aiplatform.CustomContainerTrainingJob(\n",
- " display_name=job_name,\n",
- " container_uri=TRAIN_DOCKER_URI,\n",
- " labels=labels,\n",
- ")\n",
- "\n",
- "print(\"Running training job with args:\")\n",
- "print(\" \\\\\\n\".join(train_job_args))\n",
- "train_job.run(\n",
- " args=train_job_args,\n",
- " replica_count=replica_count,\n",
- " machine_type=training_machine_type,\n",
- " accelerator_type=training_accelerator_type,\n",
- " accelerator_count=per_node_accelerator_count,\n",
- " boot_disk_size_gb=500,\n",
- " service_account=SERVICE_ACCOUNT,\n",
- " base_output_dir=base_output_dir,\n",
- " sync=False, # Non-blocking call to run.\n",
- " **dws_kwargs,\n",
- ")\n",
- "\n",
- "# Wait until resource has been created.\n",
- "train_job.wait_for_resource_creation()\n",
- "\n",
- "print(\"LoRA adapter will be saved in:\", lora_output_dir)\n",
- "print(\"Trained and merged models will be saved in:\", merged_model_output_dir)\n",
- "\n",
- "# @markdown Click \"Show Code\" to see more details."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "NdBE5YabzjaT"
- },
- "outputs": [],
- "source": [
- "# @title Run TensorBoard\n",
- "# @markdown This section shows how to launch TensorBoard in a [Cloud Shell](https://cloud.google.com/shell/docs).\n",
- "# @markdown 1. Click the Cloud Shell icon() on the top right to open the Cloud Shell.\n",
- "# @markdown 2. Copy the `tensorboard` command shown below by running this cell.\n",
- "# @markdown 3. Paste and run the command in the Cloud Shell to launch TensorBoard.\n",
- "# @markdown 4. Once the command runs (You may have to click `Authorize` if prompted), click the link starting with `http://localhost`.\n",
- "\n",
- "# @markdown Note: You may need to wait around 10 minutes after the job starts in order for the TensorBoard logs to be written to the GCS bucket.\n",
- "print(f\"Command to copy: tensorboard --logdir {base_output_dir}/logs\")"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "qmHW6m8xG_4U"
- },
- "outputs": [],
- "source": [
- "# @title Deploy with vLLM\n",
- "# @markdown This section uploads the model to Model Registry and deploys it on the Endpoint. It takes 15 minutes to 1 hour to finish.\n",
- "\n",
- "if train_job.end_time is None:\n",
- " print(\"Waiting for the training job to finish...\")\n",
- " train_job.wait()\n",
- " print(\"The training job has finished.\")\n",
- "\n",
- "print(\"Deploying models in:\", merged_model_output_dir)\n",
- "\n",
- "# The pre-built serving docker image for vLLM.\n",
- "VLLM_DOCKER_URI = \"us-docker.pkg.dev/vertex-ai/vertex-vision-model-garden-dockers/pytorch-vllm-serve:20241010_0916_RC00\"\n",
- "\n",
- "# Find Vertex AI prediction supported accelerators and regions in [here](https://cloud.google.com/vertex-ai/docs/predictions/configure-compute).\n",
- "# Sets 1 L4 (24G) to deploy Gemma models.\n",
- "serve_machine_type = \"g2-standard-12\"\n",
- "serve_accelerator_type = \"NVIDIA_L4\"\n",
- "serve_accelerator_count = 1\n",
- "\n",
- "# @markdown Set `use_dedicated_endpoint` to False if you don't want to use [dedicated endpoint](https://cloud.google.com/vertex-ai/docs/general/deployment#create-dedicated-endpoint).\n",
- "use_dedicated_endpoint = True # @param {type:\"boolean\"}\n",
- "\n",
- "common_util.check_quota(\n",
- " project_id=PROJECT_ID,\n",
- " region=REGION,\n",
- " accelerator_type=serve_accelerator_type,\n",
- " accelerator_count=serve_accelerator_count,\n",
- " is_for_training=False,\n",
- ")\n",
- "\n",
- "# Note that a larger max_model_len will require more GPU memory.\n",
- "max_model_len = 2048\n",
- "\n",
- "\n",
- "def get_deploy_source() -> str:\n",
- " \"\"\"Gets deploy_source string based on running environment.\"\"\"\n",
- " vertex_product = os.environ.get(\"VERTEX_PRODUCT\", \"\")\n",
- " if vertex_product == \"COLAB_ENTERPRISE\":\n",
- " return \"notebook_colab_enterprise\"\n",
- " elif vertex_product == \"WORKBENCH_INSTANCE\":\n",
- " return \"notebook_workbench\"\n",
- " else:\n",
- " # Legacy workbench, legacy colab, or other custom environments.\n",
- " return \"notebook_environment_unspecified\"\n",
- "\n",
- "\n",
- "def deploy_model_vllm(\n",
- " model_name: str,\n",
- " model_id: str,\n",
- " publisher: str,\n",
- " publisher_model_id: str,\n",
- " service_account: str,\n",
- " base_model_id: str = None,\n",
- " machine_type: str = \"g2-standard-8\",\n",
- " accelerator_type: str = \"NVIDIA_L4\",\n",
- " accelerator_count: int = 1,\n",
- " gpu_memory_utilization: float = 0.9,\n",
- " max_model_len: int = 4096,\n",
- " dtype: str = \"auto\",\n",
- " enable_trust_remote_code: bool = False,\n",
- " enforce_eager: bool = False,\n",
- " enable_lora: bool = False,\n",
- " enable_chunked_prefill: bool = False,\n",
- " enable_prefix_cache: bool = False,\n",
- " host_prefix_kv_cache_utilization_target: float = 0.0,\n",
- " max_loras: int = 1,\n",
- " max_cpu_loras: int = 8,\n",
- " use_dedicated_endpoint: bool = False,\n",
- " max_num_seqs: int = 256,\n",
- " model_type: str = None,\n",
- " enable_llama_tool_parser: bool = False,\n",
- " is_spot: bool = False,\n",
- ") -> Tuple[aiplatform.Model, aiplatform.Endpoint]:\n",
- " \"\"\"Deploys trained models with vLLM into Vertex AI.\"\"\"\n",
- " endpoint = aiplatform.Endpoint.create(\n",
- " display_name=f\"{model_name}-endpoint\",\n",
- " dedicated_endpoint_enabled=use_dedicated_endpoint,\n",
- " )\n",
- "\n",
- " if not base_model_id:\n",
- " base_model_id = model_id\n",
- "\n",
- " # See https://docs.vllm.ai/en/latest/models/engine_args.html for a list of possible arguments with descriptions.\n",
- " vllm_args = [\n",
- " \"python\",\n",
- " \"-m\",\n",
- " \"vllm.entrypoints.api_server\",\n",
- " \"--host=0.0.0.0\",\n",
- " \"--port=8080\",\n",
- " f\"--model={model_id}\",\n",
- " f\"--tensor-parallel-size={accelerator_count}\",\n",
- " \"--swap-space=16\",\n",
- " f\"--max-model-len={max_model_len}\",\n",
- " f\"--dtype={dtype}\",\n",
- " f\"--max-loras={max_loras}\",\n",
- " f\"--max-cpu-loras={max_cpu_loras}\",\n",
- " f\"--max-num-seqs={max_num_seqs}\",\n",
- " \"--disable-log-stats\",\n",
- " ]\n",
- "\n",
- " if gpu_memory_utilization:\n",
- " vllm_args.append(f\"--gpu-memory-utilization={gpu_memory_utilization}\")\n",
- "\n",
- " if enable_trust_remote_code:\n",
- " vllm_args.append(\"--trust-remote-code\")\n",
- "\n",
- " if enforce_eager:\n",
- " vllm_args.append(\"--enforce-eager\")\n",
- "\n",
- " if enable_lora:\n",
- " vllm_args.append(\"--enable-lora\")\n",
- "\n",
- " if enable_chunked_prefill:\n",
- " vllm_args.append(\"--enable-chunked-prefill\")\n",
- "\n",
- " if enable_prefix_cache:\n",
- " vllm_args.append(\"--enable-prefix-caching\")\n",
- "\n",
- " if 0 < host_prefix_kv_cache_utilization_target < 1:\n",
- " vllm_args.append(\n",
- " f\"--host-prefix-kv-cache-utilization-target={host_prefix_kv_cache_utilization_target}\"\n",
- " )\n",
- "\n",
- " if model_type:\n",
- " vllm_args.append(f\"--model-type={model_type}\")\n",
- "\n",
- " if enable_llama_tool_parser:\n",
- " vllm_args.append(\"--enable-auto-tool-choice\")\n",
- " vllm_args.append(\"--tool-call-parser=vertex-llama-3\")\n",
- "\n",
- " env_vars = {\n",
- " \"MODEL_ID\": base_model_id,\n",
- " \"DEPLOY_SOURCE\": \"notebook\",\n",
- " }\n",
- "\n",
- " # HF_TOKEN is not a compulsory field and may not be defined.\n",
- " try:\n",
- " if HF_TOKEN:\n",
- " env_vars[\"HF_TOKEN\"] = HF_TOKEN\n",
- " except NameError:\n",
- " pass\n",
- "\n",
- " model = aiplatform.Model.upload(\n",
- " display_name=model_name,\n",
- " serving_container_image_uri=VLLM_DOCKER_URI,\n",
- " serving_container_args=vllm_args,\n",
- " serving_container_ports=[8080],\n",
- " serving_container_predict_route=\"/generate\",\n",
- " serving_container_health_route=\"/ping\",\n",
- " serving_container_environment_variables=env_vars,\n",
- " serving_container_shared_memory_size_mb=(16 * 1024), # 16 GB\n",
- " serving_container_deployment_timeout=7200,\n",
- " model_garden_source_model_name=(\n",
- " f\"publishers/{publisher}/models/{publisher_model_id}\"\n",
- " ),\n",
- " )\n",
- " print(\n",
- " f\"Deploying {model_name} on {machine_type} with {accelerator_count} {accelerator_type} GPU(s).\"\n",
- " )\n",
- " model.deploy(\n",
- " endpoint=endpoint,\n",
- " machine_type=machine_type,\n",
- " accelerator_type=accelerator_type,\n",
- " accelerator_count=accelerator_count,\n",
- " deploy_request_timeout=1800,\n",
- " service_account=service_account,\n",
- " spot=is_spot,\n",
- " system_labels={\n",
- " \"NOTEBOOK_NAME\": \"model_garden_pytorch_gemma_peft_finetuning_hf.ipynb\",\n",
- " \"NOTEBOOK_ENVIRONMENT\": get_deploy_source(),\n",
- " },\n",
- " )\n",
- " print(\"endpoint_name:\", endpoint.name)\n",
- "\n",
- " return model, endpoint\n",
- "\n",
- "\n",
- "models[\"vllm_gpu\"], endpoints[\"vllm_gpu\"] = deploy_model_vllm(\n",
- " model_name=common_util.get_job_name_with_datetime(prefix=\"gemma-vllm-serve\"),\n",
- " base_model_id=f\"google/{base_model_id}\",\n",
- " publisher=\"google\",\n",
- " publisher_model_id=\"gemma\",\n",
- " model_id=merged_model_output_dir,\n",
- " service_account=SERVICE_ACCOUNT,\n",
- " machine_type=serve_machine_type,\n",
- " accelerator_type=serve_accelerator_type,\n",
- " accelerator_count=serve_accelerator_count,\n",
- " max_model_len=max_model_len,\n",
- " use_dedicated_endpoint=use_dedicated_endpoint,\n",
- ")\n",
- "\n",
- "# @markdown Click \"Show Code\" to see more details."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "2UYUNn60G_4U"
- },
- "outputs": [],
- "source": [
- "# @title Predict\n",
- "# @markdown Once deployment succeeds, you can send requests to the endpoint with text prompts. Sampling parameters supported by vLLM can be found [here](https://docs.vllm.ai/en/latest/dev/sampling_params.html).\n",
- "\n",
- "# @markdown Example:\n",
- "\n",
- "# @markdown ```\n",
- "# @markdown Human: What is a car?\n",
- "# @markdown Assistant: A car, or a motor car, is a road-connected human-transportation system used to move people or goods from one place to another. The term also encompasses a wide range of vehicles, including motorboats, trains, and aircrafts. Cars typically have four wheels, a cabin for passengers, and an engine or motor. They have been around since the early 19th century and are now one of the most popular forms of transportation, used for daily commuting, shopping, and other purposes.\n",
- "# @markdown ```\n",
- "# @markdown Additionally, you can moderate the generated text with Vertex AI. See [Moderate text documentation](https://cloud.google.com/natural-language/docs/moderating-text) for more details.\n",
- "\n",
- "# Loads an existing endpoint instance using the endpoint name:\n",
- "# - Using `endpoint_name = endpoint.name` allows us to get the\n",
- "# endpoint name of the endpoint `endpoint` created in the cell\n",
- "# above.\n",
- "# - Alternatively, you can set `endpoint_name = \"1234567890123456789\"` to load\n",
- "# an existing endpoint with the ID 1234567890123456789.\n",
- "# You may uncomment the code below to load an existing endpoint.\n",
- "\n",
- "# endpoint_name = \"\" # @param {type:\"string\"}\n",
- "# aip_endpoint_name = (\n",
- "# f\"projects/{PROJECT_ID}/locations/{REGION}/endpoints/{endpoint_name}\"\n",
- "# )\n",
- "# endpoint = aiplatform.Endpoint(aip_endpoint_name)\n",
- "\n",
- "prompt = \"What is a car?\" # @param {type: \"string\"}\n",
- "# @markdown If you encounter an issue like `ServiceUnavailable: 503 Took too long to respond when processing`, you can reduce the maximum number of output tokens, by lowering `max_tokens`.\n",
- "max_tokens = 50 # @param {type:\"integer\"}\n",
- "temperature = 1.0 # @param {type:\"number\"}\n",
- "top_p = 1.0 # @param {type:\"number\"}\n",
- "top_k = 1 # @param {type:\"integer\"}\n",
- "# @markdown Set `raw_response` to `True` to obtain the raw model output. Set `raw_response` to `False` to apply additional formatting in the structure of `\"Prompt:\\n{prompt.strip()}\\nOutput:\\n{output}\"`.\n",
- "raw_response = False # @param {type:\"boolean\"}\n",
- "\n",
- "# Overrides parameters for inferences.\n",
- "instances = [\n",
- " {\n",
- " \"prompt\": prompt,\n",
- " \"max_tokens\": max_tokens,\n",
- " \"temperature\": temperature,\n",
- " \"top_p\": top_p,\n",
- " \"top_k\": top_k,\n",
- " \"raw_response\": raw_response,\n",
- " },\n",
- "]\n",
- "response = endpoints[\"vllm_gpu\"].predict(\n",
- " instances=instances, use_dedicated_endpoint=use_dedicated_endpoint\n",
- ")\n",
- "\n",
- "for prediction in response.predictions:\n",
- " print(prediction)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "2T_cXYJhG_4U"
- },
- "outputs": [],
- "source": [
- "# @markdown Text moderation analyzes a document against a list of safety attributes, which include \"harmful categories\" and topics that may be considered sensitive.\n",
- "\n",
- "for generated_text in response.predictions:\n",
- " # Send a request to the API.\n",
- " response = moderate_text(generated_text)\n",
- " # Show the results.\n",
- " show_text_moderation(generated_text, response)\n",
- "\n",
- "# @markdown Click \"Show Code\" to see more details."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "af21a3cff1e0"
- },
- "source": [
- "## Clean up resources"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "911406c1561e"
- },
- "outputs": [],
- "source": [
- "# Delete the train job.\n",
- "train_job.delete()\n",
- "\n",
- "# @markdown Delete the experiment models and endpoints to recycle the resources\n",
- "# @markdown and avoid unnecessary continuous charges that may incur.\n",
- "\n",
- "# Undeploy model and delete endpoint.\n",
- "for endpoint in endpoints.values():\n",
- " endpoint.delete(force=True)\n",
- "\n",
- "# Delete models.\n",
- "for model in models.values():\n",
- " model.delete()\n",
- "\n",
- "delete_bucket = False # @param {type:\"boolean\"}\n",
- "if delete_bucket:\n",
- " ! gsutil -m rm -r $BUCKET_NAME"
- ]
- }
- ],
- "metadata": {
- "colab": {
- "name": "model_garden_pytorch_gemma_peft_finetuning_hf.ipynb",
- "toc_visible": true
- },
- "kernelspec": {
- "display_name": "Python 3",
- "name": "python3"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 0
-}
diff --git a/notebooks/community/model_garden/model_garden_pytorch_llama2_evaluation.ipynb b/notebooks/community/model_garden/model_garden_pytorch_llama2_evaluation.ipynb
index 11ca04c30..05de5970f 100644
--- a/notebooks/community/model_garden/model_garden_pytorch_llama2_evaluation.ipynb
+++ b/notebooks/community/model_garden/model_garden_pytorch_llama2_evaluation.ipynb
@@ -1,831 +1,831 @@
{
- "cells": [
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "id": "7d9bbf86da5e"
- },
- "outputs": [],
- "source": [
- "# Copyright 2024 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": "99c1c3fc2ca5"
- },
- "source": [
- "# Vertex AI Model Garden - LLaMA 2 (Evaluation)\n",
- "\n",
- "\n",
- " \n",
- " \n",
- "  Run in Colab Enterprise\n",
- " \n",
- " | \n",
- " \n",
- " \n",
- "  View on GitHub\n",
- " \n",
- " | \n",
- "
"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "3de7470326a2"
- },
- "source": [
- "## Overview\n",
- "\n",
- "This notebook demonstrates downloading prebuilt [LLaMA 2 models](https://huggingface.co/meta-llama), evaluating LLaMA 2 models with popular benchmark datasets through Vertex CustomJobs using [EleutherAI's evaluation harness](https://github.com/EleutherAI/lm-evaluation-harness) and running\n",
- "[automatic side-by-side evaluation](https://cloud.google.com/vertex-ai/docs/generative-ai/models/side-by-side-eval).\n",
- "\n",
- "### Objective\n",
- "\n",
- "- Download prebuilt LLaMA 2 models\n",
- "- Evaluate the LLaMA 2 models on any of the benchmark datasets\n",
- "- Run bulk inference job\n",
- "- Run automatic side by side (autoSxS) evaluation job\n",
- "\n",
- "### Costs\n",
- "\n",
- "This tutorial uses billable components of Google Cloud:\n",
- "\n",
- "* Vertex AI\n",
- "* Cloud Storage\n",
- "\n",
- "Learn about [Vertex AI pricing](https://cloud.google.com/vertex-ai/pricing) and [Cloud Storage pricing](https://cloud.google.com/storage/pricing), and use the [Pricing Calculator](https://cloud.google.com/products/calculator/) to generate a cost estimate based on your projected usage."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "264c07757582"
- },
- "source": [
- "## Run the notebook"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "855d6b96f291"
- },
- "outputs": [],
- "source": [
- "# @title Setup Google Cloud project\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] [Create a Cloud Storage bucket](https://cloud.google.com/storage/docs/creating-buckets) for storing\n",
- "# @markdown experiment outputs. Set the BUCKET_URI for the experiment environment. The specified Cloud Storage bucket (`BUCKET_URI`)\n",
- "# @markdown should be located in the same region as where the notebook was launched. Note that a multi-region bucket (eg. \"us\") is\n",
- "# @markdown not considered a match for a single region covered by the multi-region range (eg. \"us-central1\").\n",
- "# @markdown If not set, a unique GCS bucket will be created instead.\n",
- "\n",
- "# Import the necessary packages\n",
- "! pip3 install --upgrade --quiet google-cloud-aiplatform google-cloud-pipeline-components\n",
- "\n",
- "import importlib\n",
- "import json\n",
- "import os\n",
- "import uuid\n",
- "from datetime import datetime\n",
- "from typing import Dict\n",
- "\n",
- "import pandas as pd\n",
- "from google.cloud import aiplatform, storage\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",
- "# Get the default cloud project id.\n",
- "PROJECT_ID = os.environ[\"GOOGLE_CLOUD_PROJECT\"]\n",
- "\n",
- "# Get the default region for launching jobs.\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",
- "# Cloud Storage bucket for storing the experiment artifacts.\n",
- "# A unique GCS bucket will be created for the purpose of this notebook. If you\n",
- "# prefer using your own GCS bucket, change the value yourself below.\n",
- "now = datetime.now().strftime(\"%Y%m%d%H%M%S\")\n",
- "BUCKET_URI = \"gs://\" # @param {type:\"string\"}\n",
- "\n",
- "if BUCKET_URI is None or BUCKET_URI.strip() == \"\" or BUCKET_URI == \"gs://\":\n",
- " BUCKET_URI = f\"gs://{PROJECT_ID}-tmp-{now}-{str(uuid.uuid4())[:4]}\"\n",
- " ! gsutil mb -l {REGION} {BUCKET_URI}\n",
- "else:\n",
- " assert BUCKET_URI.startswith(\"gs://\"), \"BUCKET_URI must start with `gs://`.\"\n",
- " BUCKET_NAME = \"/\".join(BUCKET_URI.split(\"/\")[:3])\n",
- " shell_output = ! gsutil ls -Lb {BUCKET_NAME} | grep \"Location constraint:\" | sed \"s/Location constraint://\"\n",
- " bucket_region = shell_output[0].strip().lower()\n",
- " if bucket_region != REGION:\n",
- " raise ValueError(\n",
- " \"Bucket region %s is different from notebook region %s\"\n",
- " % (bucket_region, REGION)\n",
- " )\n",
- "print(f\"Using this GCS Bucket: {BUCKET_URI}\")\n",
- "\n",
- "STAGING_BUCKET = os.path.join(BUCKET_URI, \"temporal\")\n",
- "EXPERIMENT_BUCKET = os.path.join(BUCKET_URI, \"llama2\")\n",
- "BASE_MODEL_BUCKET = os.path.join(EXPERIMENT_BUCKET, \"base_model\")\n",
- "MODEL_BUCKET = os.path.join(EXPERIMENT_BUCKET, \"model\")\n",
- "OUTPUT_BUCKET_A = os.path.join(EXPERIMENT_BUCKET, \"output_a\")\n",
- "OUTPUT_BUCKET_B = os.path.join(EXPERIMENT_BUCKET, \"output_b\")\n",
- "\n",
- "# Initialize Vertex AI API.\n",
- "print(\"Initializing Vertex AI API.\")\n",
- "aiplatform.init(project=PROJECT_ID, location=REGION, staging_bucket=STAGING_BUCKET)\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",
- "\n",
- "# Provision permissions to the SERVICE_ACCOUNT with the GCS bucket\n",
- "BUCKET_NAME = \"/\".join(BUCKET_URI.split(\"/\")[:3])\n",
- "! gsutil iam ch serviceAccount:{SERVICE_ACCOUNT}:roles/storage.admin $BUCKET_NAME\n",
- "\n",
- "! gcloud config set project $PROJECT_ID\n",
- "\n",
- "# The evaluation and the bulk inference docker images.\n",
- "EVAL_DOCKER_URI = \"us-docker.pkg.dev/vertex-ai/vertex-vision-model-garden-dockers/pytorch-lm-evaluation-harness:20231011_0934_RC00\"\n",
- "BULK_INFERRER_DOCKER_URI = \"us-docker.pkg.dev/vertex-ai/vertex-vision-model-garden-dockers/pytorch-bulk-inferrer:20240708_1042_RC00\"\n",
- "\n",
- "\n",
- "def get_job_name_with_datetime(prefix: str) -> str:\n",
- " \"\"\"Gets the job name with date time when triggering jobs in Vertex AI.\"\"\"\n",
- " return prefix + datetime.now().strftime(\"-%Y%m%d%H%M%S\")\n",
- "\n",
- "\n",
- "def preprocess(\n",
- " output_prediction_a: Dict[str, str], output_prediction_b: Dict[str, str]\n",
- ") -> Dict[str, str]:\n",
- " \"\"\"Preprocesses the output predictions of model a and model b.\n",
- "\n",
- " It takes the output predictions of bulk inference job of the model a and\n",
- " model b and merges into one jsonl file.\n",
- "\n",
- " Args:\n",
- " output_prediction_a:\n",
- " Output json file which contains the predictions of the model a.\n",
- " output_prediction_b:\n",
- " Output json file which contains the predictions of the model b.\n",
- "\n",
- " Returns:\n",
- " Merged jsonl file.\n",
- " \"\"\"\n",
- " # Get the outputs of prediction of to the dataframe.\n",
- " df1 = pd.read_json(output_prediction_a, lines=True)\n",
- " df2 = pd.read_json(output_prediction_b, lines=True)\n",
- "\n",
- " # Rename the columns and merge the dataframes based on the input column.\n",
- " df1 = df1.rename(columns={index_column: \"inputs\", \"prediction\": \"pred_a\"})\n",
- " df2 = df2.rename(columns={index_column: \"inputs\", \"prediction\": \"pred_b\"})\n",
- "\n",
- " df1[\"inputs\"] = df1[\"inputs\"].apply(lambda d: d[\"inputs_pretokenized\"])\n",
- " df2[\"inputs\"] = df2[\"inputs\"].apply(lambda d: d[\"inputs_pretokenized\"])\n",
- "\n",
- " result = pd.merge(df1, df2, on=index_column)\n",
- "\n",
- " # Convert the dataframe to result.jsonl file.\n",
- " return result.to_json(\"result.jsonl\", orient=\"records\", lines=True)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "vNUYdFAeNnq2"
- },
- "outputs": [],
- "source": [
- "# @title Access pretrained LLaMA 2 models\n",
- "\n",
- "# @markdown The original models from Meta are converted into the HuggingFace format for serving in Vertex AI.\n",
- "\n",
- "# @markdown Accept the model agreement to access the models:\n",
- "# @markdown 1. Open the [LLaMA 2 model card](https://console.cloud.google.com/vertex-ai/publishers/meta/model-garden/llama2).\n",
- "# @markdown 2. Review and accept the agreement in the pop-up window on the model card page. If you have previously accepted the model agreement, there will not be a pop-up window on the model card page and this step is not needed.\n",
- "# @markdown 3. A Cloud Storage bucket (starting with ‘gs://’) containing LLaMA 2 pretrained and finetuned models will be shared under the “Documentation” section and its “Get started” subsection.\n",
- "\n",
- "# This path will be shared once click the agreement in Code LLaMA model card\n",
- "# as described in the `Access pretrained Code LLaMA models` section.\n",
- "VERTEX_AI_MODEL_GARDEN_LLAMA2 = \"\" # @param {type:\"string\"}\n",
- "assert (\n",
- " VERTEX_AI_MODEL_GARDEN_LLAMA2\n",
- "), \"Click the agreement of LLaMA2 in Vertex AI Model Garden, and get the GCS path of LLaMA2 model artifacts.\"\n",
- "print(\n",
- " \"Copy LLaMA2 model artifacts from\",\n",
- " VERTEX_AI_MODEL_GARDEN_LLAMA2,\n",
- " \"to \",\n",
- " BASE_MODEL_BUCKET,\n",
- ")\n",
- "! gsutil -m cp -R $VERTEX_AI_MODEL_GARDEN_LLAMA2/* $BASE_MODEL_BUCKET"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "V5AQpnzQS3j6"
- },
- "outputs": [],
- "source": [
- "# @title Evaluate LLaMA 2 models\n",
- "# @markdown This section demonstrates evaluation of LLaMA 2 models using EleutherAI's [Language Model Evaluation Harness (lm-evaluation-harness)](https://github.com/EleutherAI/lm-evaluation-harness) with Vertex Custom Job.\n",
- "\n",
- "# @markdown This example uses the dataset [TruthfulQA](https://arxiv.org/abs/2109.07958).\n",
- "# @markdown All the supported tasks are listed in [this task table](https://github.com/EleutherAI/lm-evaluation-harness/blob/master/docs/task_table.md).\n",
- "\n",
- "# set the base model id\n",
- "base_model_name = \"llama2-7b-chat-hf\" # @param [\"llama2-7b-hf\", \"llama2-7b-chat-hf\", \"llama2-13b-hf\", \"llama2-13b-chat-hf\", \"llama2-70b-hf\", \"llama2-70b-chat-hf\"]\n",
- "base_model_id = os.path.join(BASE_MODEL_BUCKET, base_model_name)\n",
- "\n",
- "# Set the machine_type, accelerator_type, accelerator_count and benchmark dataset.\n",
- "eval_dataset = \"truthfulqa_mc\" # @param [\"truthfulqa_mc\", \"boolq\", \"gsm8k\", \"hellaswag\", \"natural_questions\", \"openai_humaneval\", \"openbookqa\", \"quac\", \"trivia_qa\", \"winograde\"]\n",
- "\n",
- "# Worker pool spec.\n",
- "# Find Vertex AI supported accelerators and regions in:\n",
- "# https://cloud.google.com/vertex-ai/docs/training/configure-compute\n",
- "\n",
- "if base_model_name == \"llama2-7b-hf\":\n",
- " # Sets 1 (24G) to evaluate LLaMA2 7B models.\n",
- " machine_type = \"g2-standard-16\"\n",
- " accelerator_type = \"NVIDIA_L4\"\n",
- " accelerator_count = 1\n",
- "elif base_model_name == \"llama2-7b-chat-hf\":\n",
- " # Sets 1 L4 (24G) to evaluate LLaMA2 7B models.\n",
- " machine_type = \"g2-standard-16\"\n",
- " accelerator_type = \"NVIDIA_L4\"\n",
- " accelerator_count = 1\n",
- "elif base_model_name == \"llama2-13b-hf\":\n",
- " # Sets 2 L4 (24G) to evaluate LLaMA2 13B models.\n",
- " machine_type = \"g2-standard-24\"\n",
- " accelerator_type = \"NVIDIA_L4\"\n",
- " accelerator_count = 2\n",
- "elif base_model_name == \"llama2-13b-chat-hf\":\n",
- " # Sets 2 L4 (24G) to evaluate LLaMA2 13B models.\n",
- " machine_type = \"g2-standard-24\"\n",
- " accelerator_type = \"NVIDIA_L4\"\n",
- " accelerator_count = 2\n",
- "elif base_model_name == \"llama2-70b-hf\":\n",
- " # Sets 8 L4 (24G) to evaluate LLaMA2 70B models.\n",
- " machine_type = \"g2-standard-96\"\n",
- " accelerator_type = \"NVIDIA_L4\"\n",
- " accelerator_count = 8\n",
- "elif base_model_name == \"llama2-70b-chat-hf\":\n",
- " # Sets 8 L4 (24G) to evaluate LLaMA2 70B models.\n",
- " machine_type = \"g2-standard-96\"\n",
- " accelerator_type = \"NVIDIA_L4\"\n",
- " accelerator_count = 8\n",
- "\n",
- "common_util.check_quota(\n",
- " project_id=PROJECT_ID,\n",
- " region=REGION,\n",
- " accelerator_type=accelerator_type,\n",
- " accelerator_count=accelerator_count,\n",
- " is_for_training=True,\n",
- ")\n",
- "\n",
- "replica_count = 1\n",
- "\n",
- "job_name = get_job_name_with_datetime(prefix=\"llama2-eval\")\n",
- "eval_output_dir = os.path.join(MODEL_BUCKET, job_name)\n",
- "eval_output_dir_gcsfuse = eval_output_dir.replace(\"gs://\", \"/gcs/\")\n",
- "base_model_id_gcsfuse = base_model_id.replace(\"gs://\", \"/gcs/\")\n",
- "\n",
- "# @markdown To evaluate a PEFT-finetuned model, enter the PEFT output directory below.\n",
- "# @markdown Otherwise, leave it empty.\n",
- "\n",
- "# @markdown See the [finetuning notebook](https://github.com/GoogleCloudPlatform/vertex-ai-samples/blob/main/notebooks/community/model_garden/model_garden_pytorch_llama2_peft_finetuning.ipynb) for more details:\n",
- "\n",
- "peft_output_dir = \"\" # @param {type:\"string\"}\n",
- "peft_output_dir_gcsfuse = peft_output_dir.replace(\"gs://\", \"/gcs/\")\n",
- "\n",
- "# Prepare evaluation command that runs the evaluation harness.\n",
- "# Set `trust_remote_code = True` because evaluating the model requires\n",
- "# executing code from the model repository.\n",
- "# Set `use_accelerate = True` to enable evaluation across multiple GPUs.\n",
- "eval_command = [\n",
- " \"python\",\n",
- " \"main.py\",\n",
- " \"--model\",\n",
- " \"hf-causal-experimental\",\n",
- " \"--tasks\",\n",
- " f\"{eval_dataset}\",\n",
- " \"--output_path\",\n",
- " f\"{eval_output_dir_gcsfuse}\",\n",
- "]\n",
- "\n",
- "if peft_output_dir_gcsfuse:\n",
- " eval_command += [\n",
- " \"--model_args\",\n",
- " f\"pretrained={base_model_id_gcsfuse},peft={peft_output_dir_gcsfuse},trust_remote_code=True,use_accelerate=True,device_map_option=auto\",\n",
- " ]\n",
- "else:\n",
- " eval_command += [\n",
- " \"--model_args\",\n",
- " f\"pretrained={base_model_id_gcsfuse},trust_remote_code=True,use_accelerate=True,device_map_option=auto\",\n",
- " ]\n",
- "\n",
- "# Run the evaluation job.\n",
- "worker_pool_specs = [\n",
- " {\n",
- " \"machine_spec\": {\n",
- " \"machine_type\": machine_type,\n",
- " \"accelerator_type\": accelerator_type,\n",
- " \"accelerator_count\": accelerator_count,\n",
- " },\n",
- " \"replica_count\": replica_count,\n",
- " \"disk_spec\": {\n",
- " \"boot_disk_size_gb\": 500,\n",
- " },\n",
- " \"container_spec\": {\n",
- " \"image_uri\": EVAL_DOCKER_URI,\n",
- " \"command\": eval_command,\n",
- " \"args\": [],\n",
- " },\n",
- " }\n",
- "]\n",
- "\n",
- "\n",
- "# Add labels for the finetuning job.\n",
- "labels = {\n",
- " \"mg-source\": \"notebook\",\n",
- " \"mg-notebook-name\": \"model_garden_pytorch_llama2_evaluation.ipynb\".split(\".\")[0],\n",
- "}\n",
- "\n",
- "labels[\"mg-tune\"] = \"publishers-meta-models-llama2\"\n",
- "versioned_model_id = base_model_name.lower()\n",
- "labels[\"versioned-mg-tune\"] = f\"{labels['mg-tune']}-{versioned_model_id}\"\n",
- "\n",
- "eval_job = aiplatform.CustomJob(\n",
- " display_name=job_name,\n",
- " worker_pool_specs=worker_pool_specs,\n",
- " base_output_dir=eval_output_dir,\n",
- " labels=labels,\n",
- ")\n",
- "\n",
- "eval_job.run()\n",
- "\n",
- "print(\"Evaluation results were saved in:\", eval_output_dir)\n",
- "\n",
- "# Fetch evaluation results.\n",
- "storage_client = storage.Client()\n",
- "BUCKET_NAME = BUCKET_URI.replace(\"gs://\", \"\")\n",
- "bucket = storage_client.get_bucket(BUCKET_NAME)\n",
- "RESULT_FILE_PATH = eval_output_dir[len(BUCKET_URI) + 1 :]\n",
- "blob = bucket.blob(RESULT_FILE_PATH)\n",
- "raw_result = blob.download_as_string()\n",
- "\n",
- "# Print evaluation results.\n",
- "result = json.loads(raw_result)\n",
- "result_formatted = json.dumps(result, indent=2)\n",
- "print(f\"Evaluation result:\\n{result_formatted}\")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "R3Vm13jGd4aU"
- },
- "source": [
- "### Bulk Inference"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "yyW4BJDr9t-O"
- },
- "outputs": [],
- "source": [
- "# @title [Optional] Generate `input_dataset` for the bulk inference job\n",
- "# @markdown Note: For experimentation, we request that users provide only a few prompts.\n",
- "\n",
- "# @markdown For demonstration, a publicly available [TensorFlow dataset](https://www.tensorflow.org/datasets/catalog/reddit) is used. This dataset contains preprocessed posts from the Reddit dataset.\n",
- "TEST_DATASET = \"gs://vertex-ai/generative-ai/rlhf/text_small/reddit_tfds/val/shard-00000-of-00001.jsonl\" # @param {type:\"string\"}\n",
- "NUM_EXAMPLES = 50 # @param {type:\"integer\"}\n",
- "\n",
- "# Load dataset and modify.\n",
- "df = pd.read_json(TEST_DATASET, lines=True)\n",
- "examples = df.head(NUM_EXAMPLES)\n",
- "\n",
- "# Upload new dataset to GCS.\n",
- "examples.to_json(\"data.json\", orient=\"records\", lines=True)\n",
- "! gsutil cp data.json $BUCKET_URI/temp/data.jsonl\n",
- "DATASET = f\"{BUCKET_URI}/temp/data.jsonl\"\n",
- "\n",
- "print(f\"{NUM_EXAMPLES} examples written to {DATASET}.\")"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "X-yl8yOptBvH"
- },
- "outputs": [],
- "source": [
- "# @title Set up bulk inference job\n",
- "\n",
- "# @markdown Run the bulk inference custom job to generate offline predictions.\n",
- "# @markdown You can perform bulk inference using a base model or LoRA finetuned model.\n",
- "\n",
- "# Setup bulk inference job.\n",
- "model_a = \"llama2-7b-hf\" # @param [\"llama2-7b-hf\", \"llama2-7b-chat-hf\", \"llama2-13b-hf\", \"llama2-13b-chat-hf\", \"llama2-70b-hf\", \"llama2-70b-chat-hf\"]\n",
- "model_b = \"llama2-13b-hf\" # @param [\"llama2-7b-hf\", \"llama2-7b-chat-hf\", \"llama2-13b-hf\", \"llama2-13b-chat-hf\", \"llama2-70b-hf\", \"llama2-70b-chat-hf\"]\n",
- "\n",
- "# @markdown **Required Parameters**\n",
- "\n",
- "# @markdown `input_dataset` : Path to JSONL file containing the input dataset.\n",
- "\n",
- "# @markdown `index_column` : The column which distinguishes unique evaluation examples.\n",
- "\n",
- "# @markdown `input_text` : Indexing key for inputs.\n",
- "\n",
- "# @markdown `output_prediction_a`: Path to JSONL file which will contain output predictions of model a.\n",
- "\n",
- "# @markdown `output_prediction_b`: Path to JSONL file which will contain output predictions of model b.\n",
- "\n",
- "input_dataset = f\"{BUCKET_URI}/temp/data.jsonl\" # @param {type:\"string\"}\n",
- "index_column = \"inputs\" # @param {type:\"string\"}\n",
- "input_text = \"input_text\" # @param {type:\"string\"}\n",
- "\n",
- "output_prediction_a = \"\" # @param {type:\"string\"}\n",
- "output_prediction_b = \"\" # @param {type:\"string\"}\n",
- "\n",
- "\n",
- "# @markdown **Optional Parameters** : Provide the path to the LoRA finetuned models.\n",
- "\n",
- "# @markdown `lora_path_a` : Path to finetuned LoRA adapter of model a.\n",
- "\n",
- "# @markdown `lora_path_b` : Path to finetuned LoRA adapter of model b.\n",
- "\n",
- "lora_path_a = \"\" # @param {type:\"string\"}\n",
- "lora_path_b = \"\" # @param {type:\"string\"}\n",
- "\n",
- "\n",
- "if model_a == model_b:\n",
- " raise ValueError(\"Select different models to run AutoSxS evaluation.\")\n",
- "\n",
- "if model_a in [\"llama2-7b-hf\", \"llama2-7b-chat-hf\"]:\n",
- " machine_type_a = \"g2-standard-16\"\n",
- " accelerator_type_a = \"NVIDIA_L4\"\n",
- " accelerator_count_a = 1\n",
- "elif model_a in [\"llama2-13b-hf\", \"llama2-13b-chat-hf\"]:\n",
- " machine_type_a = \"g2-standard-24\"\n",
- " accelerator_type_a = \"NVIDIA_L4\"\n",
- " accelerator_count_a = 2\n",
- "elif model_a in [\"llama2-70b-hf\", \"llama2-70b-chat-hf\"]:\n",
- " machine_type_a = \"g2-standard-96\"\n",
- " accelerator_type_a = \"NVIDIA_L4\"\n",
- " accelerator_count_a = 8\n",
- "\n",
- "common_util.check_quota(\n",
- " project_id=PROJECT_ID,\n",
- " region=REGION,\n",
- " accelerator_type=accelerator_type_a,\n",
- " accelerator_count=accelerator_count_a,\n",
- " is_for_training=True,\n",
- ")\n",
- "\n",
- "if model_b in [\"llama2-7b-hf\", \"llama2-7b-chat-hf\"]:\n",
- " machine_type_b = \"g2-standard-16\"\n",
- " accelerator_type_b = \"NVIDIA_L4\"\n",
- " accelerator_count_b = 1\n",
- "elif model_b in [\"llama2-13b-hf\", \"llama2-13b-chat-hf\"]:\n",
- " machine_type_b = \"g2-standard-24\"\n",
- " accelerator_type_b = \"NVIDIA_L4\"\n",
- " accelerator_count_b = 2\n",
- "elif model_b in [\"llama2-70b-hf\", \"llama2-70b-chat-hf\"]:\n",
- " machine_type_b = \"g2-standard-96\"\n",
- " accelerator_type_b = \"NVIDIA_L4\"\n",
- " accelerator_count_b = 8\n",
- "\n",
- "common_util.check_quota(\n",
- " project_id=PROJECT_ID,\n",
- " region=REGION,\n",
- " accelerator_type=accelerator_type_b,\n",
- " accelerator_count=accelerator_count_b,\n",
- " is_for_training=True,\n",
- ")\n",
- "\n",
- "replica_count = 1\n",
- "\n",
- "bulk_infer_job_name = get_job_name_with_datetime(prefix=\"bulk-infer\")\n",
- "eval_output_dir = os.path.join(MODEL_BUCKET, bulk_infer_job_name)\n",
- "\n",
- "# Maximum encoder/prefix length. Inputs will be padded or truncated to match this length.\n",
- "input_seq_length = 50\n",
- "\n",
- "# Maximum number of decoder steps. Outputs will be at most this length.\n",
- "targets_seq_length = 50\n",
- "\n",
- "model_id_a = os.path.join(BASE_MODEL_BUCKET, model_a)\n",
- "model_id_b = os.path.join(BASE_MODEL_BUCKET, model_b)\n",
- "\n",
- "worker_pool_specs_base_model_a = [\n",
- " {\n",
- " \"machine_spec\": {\n",
- " \"machine_type\": machine_type_a,\n",
- " \"accelerator_type\": accelerator_type_a,\n",
- " \"accelerator_count\": accelerator_count_a,\n",
- " },\n",
- " \"replica_count\": replica_count,\n",
- " \"disk_spec\": {\n",
- " \"boot_disk_type\": \"pd-ssd\",\n",
- " \"boot_disk_size_gb\": 500,\n",
- " },\n",
- " \"container_spec\": {\n",
- " \"image_uri\": BULK_INFERRER_DOCKER_URI,\n",
- " \"args\": [\n",
- " f\"--large_model_reference={model_id_a}\",\n",
- " f\"--input_model={lora_path_a}\",\n",
- " f\"--input_dataset={input_dataset}\",\n",
- " \"--dataset_split=empty\",\n",
- " f\"--output_prediction={output_prediction_a}\",\n",
- " f\"--output_prediction_gcs_path={OUTPUT_BUCKET_A}\",\n",
- " f\"--inputs_sequence_length={input_seq_length}\",\n",
- " f\"--targets_sequence_length={targets_seq_length}\",\n",
- " f\"--inputs_key={input_text}\",\n",
- " ],\n",
- " },\n",
- " }\n",
- "]\n",
- "\n",
- "worker_pool_specs_base_model_b = [\n",
- " {\n",
- " \"machine_spec\": {\n",
- " \"machine_type\": machine_type_b,\n",
- " \"accelerator_type\": accelerator_type_b,\n",
- " \"accelerator_count\": accelerator_count_b,\n",
- " },\n",
- " \"replica_count\": replica_count,\n",
- " \"disk_spec\": {\n",
- " \"boot_disk_size_gb\": 500,\n",
- " },\n",
- " \"container_spec\": {\n",
- " \"image_uri\": BULK_INFERRER_DOCKER_URI,\n",
- " \"args\": [\n",
- " f\"--large_model_reference={model_id_b}\",\n",
- " f\"--input_model={lora_path_b}\",\n",
- " f\"--input_dataset={input_dataset}\",\n",
- " \"--dataset_split=empty\",\n",
- " f\"--output_prediction={output_prediction_b}\",\n",
- " f\"--output_prediction_gcs_path={OUTPUT_BUCKET_B}\",\n",
- " f\"--inputs_sequence_length={input_seq_length}\",\n",
- " f\"--targets_sequence_length={targets_seq_length}\",\n",
- " f\"--inputs_key={input_text}\",\n",
- " ],\n",
- " },\n",
- " }\n",
- "]"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "x1sPbM1-eEx7"
- },
- "outputs": [],
- "source": [
- "# @title Run bulk inference job for Model A\n",
- "\n",
- "bulk_inferrer_a = aiplatform.CustomJob(\n",
- " display_name=get_job_name_with_datetime(prefix=\"bulk-infer-a\"),\n",
- " worker_pool_specs=worker_pool_specs_base_model_a,\n",
- " base_output_dir=os.path.join(OUTPUT_BUCKET_A, bulk_infer_job_name),\n",
- ")\n",
- "\n",
- "bulk_inferrer_a.run()"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "_WzQmq2yeHSg"
- },
- "outputs": [],
- "source": [
- "# @title Run bulk inference job for Model B\n",
- "\n",
- "bulk_inferrer_b = aiplatform.CustomJob(\n",
- " display_name=get_job_name_with_datetime(prefix=\"bulk-infer-b\"),\n",
- " worker_pool_specs=worker_pool_specs_base_model_b,\n",
- " base_output_dir=os.path.join(OUTPUT_BUCKET_B, bulk_infer_job_name),\n",
- ")\n",
- "\n",
- "bulk_inferrer_b.run()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "7pX-sEhReKdB"
- },
- "source": [
- "### AutoSxS Job"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "hE_8SlrUGrSP"
- },
- "outputs": [],
- "source": [
- "# @title Compile AutoSxS pipeline\n",
- "\n",
- "from google_cloud_pipeline_components.preview import model_evaluation\n",
- "from kfp import compiler\n",
- "\n",
- "template_uri = \"pipeline.yaml\"\n",
- "compiler.Compiler().compile(\n",
- " pipeline_func=model_evaluation.autosxs_pipeline,\n",
- " package_path=template_uri,\n",
- ")"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "VRBrm4d3eLtV"
- },
- "outputs": [],
- "source": [
- "# @title Run AutoSxS pipeline job\n",
- "\n",
- "# @markdown Automatic side-by-side (AutoSxS) is a model-assisted evaluation tool that compares two large language models (LLMs) side by side.\n",
- "# @markdown In order to run AutoSxS, we need to define a `autosxs_pipeline` job with the following parameters. More details of the AutoSxS pipeline configuration can be found [here](https://google-cloud-pipeline-components.readthedocs.io/en/google-cloud-pipeline-components-2.9.0/api/preview/model_evaluation.html#preview.model_evaluation.autosxs_pipeline).\n",
- "\n",
- "\n",
- "# Preprocess the output json files and copy to the GCS bucket\n",
- "preprocess(output_prediction_a, output_prediction_b)\n",
- "PREDS = f\"{BUCKET_URI}/temp/preds/\"\n",
- "! gsutil cp result.jsonl $PREDS\n",
- "\n",
- "# @title AutoSxS Job\n",
- "autosxs_job_name = get_job_name_with_datetime(prefix=\"autosxs\")\n",
- "\n",
- "# @markdown AutoSxS supports evaluating models for summarization and question-answering tasks.\n",
- "\"\"\"\n",
- "Evaluation task in the form {task}@{version}. Task can be one of\n",
- "[summarization, question_answer].\n",
- "version is an integer with three digits or 'latest'.\n",
- "Ex: summarization@001 or question_answer@latest\n",
- "\"\"\"\n",
- "# fmt: off\n",
- "task_name = \"summarization@001\" # @param [\"question_answer@latest\", \"summarization@001\"]\n",
- "# fmt: on\n",
- "\n",
- "\n",
- "parameters = {\n",
- " \"evaluation_dataset\": f\"{BUCKET_URI}/temp/preds/result.jsonl\",\n",
- " \"id_columns\": [\"inputs\"],\n",
- " \"autorater_prompt_parameters\": {\n",
- " \"inference_context\": {\"column\": index_column},\n",
- " \"inference_instruction\": {\"template\": \"{{ default_instruction }}\"},\n",
- " },\n",
- " \"response_column_a\": \"pred_a\",\n",
- " \"response_column_b\": \"pred_b\",\n",
- " \"task\": task_name,\n",
- "}\n",
- "\n",
- "autosxs_job = aiplatform.PipelineJob(\n",
- " job_id=autosxs_job_name,\n",
- " display_name=autosxs_job_name,\n",
- " pipeline_root=os.path.join(BUCKET_URI, autosxs_job_name),\n",
- " template_path=template_uri,\n",
- " parameter_values=parameters,\n",
- " enable_caching=False,\n",
- ")\n",
- "autosxs_job.run()"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "gAXCY0hueRBF"
- },
- "outputs": [],
- "source": [
- "# @title Get autorater judgements\n",
- "# @markdown Autorater is a language model which compares the quality of two model responses based on a pre-defined criteria.\n",
- "# @markdown More details on the autorater can be found [here](https://cloud.google.com/vertex-ai/generative-ai/docs/models/side-by-side-eval#autorater)\n",
- "\n",
- "for details in autosxs_job.task_details:\n",
- " if details.task_name == \"online-evaluation-pairwise\":\n",
- " break\n",
- "\n",
- "# Judgments\n",
- "judgments_uri = details.outputs[\"judgments\"].artifacts[0].uri\n",
- "judgments_df = pd.read_json(judgments_uri, lines=True)\n",
- "judgments_df.head()"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "5NYV1FKjIeD6"
- },
- "outputs": [],
- "source": [
- "# @title Get win-rate\n",
- "# @markdown Win rate is the percentage of the time the autorater has decided that a particular model had a better response.\n",
- "\n",
- "for details in autosxs_job.task_details:\n",
- " if details.task_name == \"model-evaluation-text-generation-pairwise\":\n",
- " break\n",
- "pd.DataFrame([details.outputs[\"autosxs_metrics\"].artifacts[0].metadata])"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "uJuMq31DeWwO"
- },
- "source": [
- "### Clean up resources"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "911406c1561e"
- },
- "outputs": [],
- "source": [
- "# @title Clean up\n",
- "# @markdown Delete the jobs to recycle the resources and avoid unnecessary continouous charges that may incur.\n",
- "\n",
- "eval_job.delete()\n",
- "bulk_inferrer_a.delete()\n",
- "bulk_inferrer_b.delete()\n",
- "autosxs_job.delete()\n",
- "\n",
- "# Delete Cloud Storage objects that were created\n",
- "delete_bucket = False # @param {type: \"boolean\"}\n",
- "if delete_bucket:\n",
- " ! gsutil -m rm -r $EXPERIMENT_BUCKET"
- ]
- }
- ],
- "metadata": {
- "colab": {
- "name": "model_garden_pytorch_llama2_evaluation.ipynb",
- "toc_visible": true
- },
- "kernelspec": {
- "display_name": "Python 3",
- "name": "python3"
- }
+ "cells": [
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "7d9bbf86da5e"
+ },
+ "outputs": [],
+ "source": [
+ "# Copyright 2024 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."
+ ]
},
- "nbformat": 4,
- "nbformat_minor": 0
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "99c1c3fc2ca5"
+ },
+ "source": [
+ "# Vertex AI Model Garden - LLaMA 2 (Evaluation)\n",
+ "\n",
+ "\n",
+ " \n",
+ " \n",
+ "  Run in Colab Enterprise\n",
+ " \n",
+ " | \n",
+ " \n",
+ " \n",
+ "  View on GitHub\n",
+ " \n",
+ " | \n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "3de7470326a2"
+ },
+ "source": [
+ "## Overview\n",
+ "\n",
+ "This notebook demonstrates downloading prebuilt [LLaMA 2 models](https://huggingface.co/meta-llama), evaluating LLaMA 2 models with popular benchmark datasets through Vertex CustomJobs using [EleutherAI's evaluation harness](https://github.com/EleutherAI/lm-evaluation-harness) and running\n",
+ "[automatic side-by-side evaluation](https://cloud.google.com/vertex-ai/docs/generative-ai/models/side-by-side-eval).\n",
+ "\n",
+ "### Objective\n",
+ "\n",
+ "- Download prebuilt LLaMA 2 models\n",
+ "- Evaluate the LLaMA 2 models on any of the benchmark datasets\n",
+ "- Run bulk inference job\n",
+ "- Run automatic side by side (autoSxS) evaluation job\n",
+ "\n",
+ "### Costs\n",
+ "\n",
+ "This tutorial uses billable components of Google Cloud:\n",
+ "\n",
+ "* Vertex AI\n",
+ "* Cloud Storage\n",
+ "\n",
+ "Learn about [Vertex AI pricing](https://cloud.google.com/vertex-ai/pricing) and [Cloud Storage pricing](https://cloud.google.com/storage/pricing), and use the [Pricing Calculator](https://cloud.google.com/products/calculator/) to generate a cost estimate based on your projected usage."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "264c07757582"
+ },
+ "source": [
+ "## Run the notebook"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "cellView": "form",
+ "id": "855d6b96f291"
+ },
+ "outputs": [],
+ "source": [
+ "# @title Setup Google Cloud project\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] [Create a Cloud Storage bucket](https://cloud.google.com/storage/docs/creating-buckets) for storing\n",
+ "# @markdown experiment outputs. Set the BUCKET_URI for the experiment environment. The specified Cloud Storage bucket (`BUCKET_URI`)\n",
+ "# @markdown should be located in the same region as where the notebook was launched. Note that a multi-region bucket (eg. \"us\") is\n",
+ "# @markdown not considered a match for a single region covered by the multi-region range (eg. \"us-central1\").\n",
+ "# @markdown If not set, a unique GCS bucket will be created instead.\n",
+ "\n",
+ "# Import the necessary packages\n",
+ "! pip3 install --upgrade --quiet google-cloud-aiplatform google-cloud-pipeline-components\n",
+ "\n",
+ "import importlib\n",
+ "import json\n",
+ "import os\n",
+ "import uuid\n",
+ "from datetime import datetime\n",
+ "from typing import Dict\n",
+ "\n",
+ "import pandas as pd\n",
+ "from google.cloud import aiplatform, storage\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",
+ "# Get the default cloud project id.\n",
+ "PROJECT_ID = os.environ[\"GOOGLE_CLOUD_PROJECT\"]\n",
+ "\n",
+ "# Get the default region for launching jobs.\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",
+ "# Cloud Storage bucket for storing the experiment artifacts.\n",
+ "# A unique GCS bucket will be created for the purpose of this notebook. If you\n",
+ "# prefer using your own GCS bucket, change the value yourself below.\n",
+ "now = datetime.now().strftime(\"%Y%m%d%H%M%S\")\n",
+ "BUCKET_URI = \"gs://\" # @param {type:\"string\"}\n",
+ "\n",
+ "if BUCKET_URI is None or BUCKET_URI.strip() == \"\" or BUCKET_URI == \"gs://\":\n",
+ " BUCKET_URI = f\"gs://{PROJECT_ID}-tmp-{now}-{str(uuid.uuid4())[:4]}\"\n",
+ " ! gsutil mb -l {REGION} {BUCKET_URI}\n",
+ "else:\n",
+ " assert BUCKET_URI.startswith(\"gs://\"), \"BUCKET_URI must start with `gs://`.\"\n",
+ " BUCKET_NAME = \"/\".join(BUCKET_URI.split(\"/\")[:3])\n",
+ " shell_output = ! gsutil ls -Lb {BUCKET_NAME} | grep \"Location constraint:\" | sed \"s/Location constraint://\"\n",
+ " bucket_region = shell_output[0].strip().lower()\n",
+ " if bucket_region != REGION:\n",
+ " raise ValueError(\n",
+ " \"Bucket region %s is different from notebook region %s\"\n",
+ " % (bucket_region, REGION)\n",
+ " )\n",
+ "print(f\"Using this GCS Bucket: {BUCKET_URI}\")\n",
+ "\n",
+ "STAGING_BUCKET = os.path.join(BUCKET_URI, \"temporal\")\n",
+ "EXPERIMENT_BUCKET = os.path.join(BUCKET_URI, \"llama2\")\n",
+ "BASE_MODEL_BUCKET = os.path.join(EXPERIMENT_BUCKET, \"base_model\")\n",
+ "MODEL_BUCKET = os.path.join(EXPERIMENT_BUCKET, \"model\")\n",
+ "OUTPUT_BUCKET_A = os.path.join(EXPERIMENT_BUCKET, \"output_a\")\n",
+ "OUTPUT_BUCKET_B = os.path.join(EXPERIMENT_BUCKET, \"output_b\")\n",
+ "\n",
+ "# Initialize Vertex AI API.\n",
+ "print(\"Initializing Vertex AI API.\")\n",
+ "aiplatform.init(project=PROJECT_ID, location=REGION, staging_bucket=STAGING_BUCKET)\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",
+ "\n",
+ "# Provision permissions to the SERVICE_ACCOUNT with the GCS bucket\n",
+ "BUCKET_NAME = \"/\".join(BUCKET_URI.split(\"/\")[:3])\n",
+ "! gsutil iam ch serviceAccount:{SERVICE_ACCOUNT}:roles/storage.admin $BUCKET_NAME\n",
+ "\n",
+ "! gcloud config set project $PROJECT_ID\n",
+ "\n",
+ "# The evaluation and the bulk inference docker images.\n",
+ "EVAL_DOCKER_URI = \"us-docker.pkg.dev/vertex-ai/vertex-vision-model-garden-dockers/pytorch-lm-evaluation-harness:20231011_0934_RC00\"\n",
+ "BULK_INFERRER_DOCKER_URI = \"us-docker.pkg.dev/vertex-ai/vertex-vision-model-garden-dockers/pytorch-bulk-inferrer:20240708_1042_RC00\"\n",
+ "\n",
+ "\n",
+ "def get_job_name_with_datetime(prefix: str) -> str:\n",
+ " \"\"\"Gets the job name with date time when triggering jobs in Vertex AI.\"\"\"\n",
+ " return prefix + datetime.now().strftime(\"-%Y%m%d%H%M%S\")\n",
+ "\n",
+ "\n",
+ "def preprocess(\n",
+ " output_prediction_a: Dict[str, str], output_prediction_b: Dict[str, str]\n",
+ ") -> Dict[str, str]:\n",
+ " \"\"\"Preprocesses the output predictions of model a and model b.\n",
+ "\n",
+ " It takes the output predictions of bulk inference job of the model a and\n",
+ " model b and merges into one jsonl file.\n",
+ "\n",
+ " Args:\n",
+ " output_prediction_a:\n",
+ " Output json file which contains the predictions of the model a.\n",
+ " output_prediction_b:\n",
+ " Output json file which contains the predictions of the model b.\n",
+ "\n",
+ " Returns:\n",
+ " Merged jsonl file.\n",
+ " \"\"\"\n",
+ " # Get the outputs of prediction of to the dataframe.\n",
+ " df1 = pd.read_json(output_prediction_a, lines=True)\n",
+ " df2 = pd.read_json(output_prediction_b, lines=True)\n",
+ "\n",
+ " # Rename the columns and merge the dataframes based on the input column.\n",
+ " df1 = df1.rename(columns={index_column: \"inputs\", \"prediction\": \"pred_a\"})\n",
+ " df2 = df2.rename(columns={index_column: \"inputs\", \"prediction\": \"pred_b\"})\n",
+ "\n",
+ " df1[\"inputs\"] = df1[\"inputs\"].apply(lambda d: d[\"inputs_pretokenized\"])\n",
+ " df2[\"inputs\"] = df2[\"inputs\"].apply(lambda d: d[\"inputs_pretokenized\"])\n",
+ "\n",
+ " result = pd.merge(df1, df2, on=index_column)\n",
+ "\n",
+ " # Convert the dataframe to result.jsonl file.\n",
+ " return result.to_json(\"result.jsonl\", orient=\"records\", lines=True)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "cellView": "form",
+ "id": "vNUYdFAeNnq2"
+ },
+ "outputs": [],
+ "source": [
+ "# @title Access pretrained LLaMA 2 models\n",
+ "\n",
+ "# @markdown The original models from Meta are converted into the HuggingFace format for serving in Vertex AI.\n",
+ "\n",
+ "# @markdown Accept the model agreement to access the models:\n",
+ "# @markdown 1. Open the [LLaMA 2 model card](https://console.cloud.google.com/vertex-ai/publishers/meta/model-garden/llama2).\n",
+ "# @markdown 2. Review and accept the agreement in the pop-up window on the model card page. If you have previously accepted the model agreement, there will not be a pop-up window on the model card page and this step is not needed.\n",
+ "# @markdown 3. A Cloud Storage bucket (starting with \u2018gs://\u2019) containing LLaMA 2 pretrained and finetuned models will be shared under the \u201cDocumentation\u201d section and its \u201cGet started\u201d subsection.\n",
+ "\n",
+ "# This path will be shared once click the agreement in Code LLaMA model card\n",
+ "# as described in the `Access pretrained Code LLaMA models` section.\n",
+ "VERTEX_AI_MODEL_GARDEN_LLAMA2 = \"\" # @param {type:\"string\"}\n",
+ "assert (\n",
+ " VERTEX_AI_MODEL_GARDEN_LLAMA2\n",
+ "), \"Click the agreement of LLaMA2 in Vertex AI Model Garden, and get the GCS path of LLaMA2 model artifacts.\"\n",
+ "print(\n",
+ " \"Copy LLaMA2 model artifacts from\",\n",
+ " VERTEX_AI_MODEL_GARDEN_LLAMA2,\n",
+ " \"to \",\n",
+ " BASE_MODEL_BUCKET,\n",
+ ")\n",
+ "! gsutil -m cp -R $VERTEX_AI_MODEL_GARDEN_LLAMA2/* $BASE_MODEL_BUCKET"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "cellView": "form",
+ "id": "V5AQpnzQS3j6"
+ },
+ "outputs": [],
+ "source": [
+ "# @title Evaluate LLaMA 2 models\n",
+ "# @markdown This section demonstrates evaluation of LLaMA 2 models using EleutherAI's [Language Model Evaluation Harness (lm-evaluation-harness)](https://github.com/EleutherAI/lm-evaluation-harness) with Vertex Custom Job.\n",
+ "\n",
+ "# @markdown This example uses the dataset [TruthfulQA](https://arxiv.org/abs/2109.07958).\n",
+ "# @markdown All the supported tasks are listed in [this task table](https://github.com/EleutherAI/lm-evaluation-harness/blob/master/docs/task_table.md).\n",
+ "\n",
+ "# set the base model id\n",
+ "base_model_name = \"llama2-7b-chat-hf\" # @param [\"llama2-7b-hf\", \"llama2-7b-chat-hf\", \"llama2-13b-hf\", \"llama2-13b-chat-hf\", \"llama2-70b-hf\", \"llama2-70b-chat-hf\"]\n",
+ "base_model_id = os.path.join(BASE_MODEL_BUCKET, base_model_name)\n",
+ "\n",
+ "# Set the machine_type, accelerator_type, accelerator_count and benchmark dataset.\n",
+ "eval_dataset = \"truthfulqa_mc\" # @param [\"truthfulqa_mc\", \"boolq\", \"gsm8k\", \"hellaswag\", \"natural_questions\", \"openai_humaneval\", \"openbookqa\", \"quac\", \"trivia_qa\", \"winograde\"]\n",
+ "\n",
+ "# Worker pool spec.\n",
+ "# Find Vertex AI supported accelerators and regions in:\n",
+ "# https://cloud.google.com/vertex-ai/docs/training/configure-compute\n",
+ "\n",
+ "if base_model_name == \"llama2-7b-hf\":\n",
+ " # Sets 1 (24G) to evaluate LLaMA2 7B models.\n",
+ " machine_type = \"g2-standard-16\"\n",
+ " accelerator_type = \"NVIDIA_L4\"\n",
+ " accelerator_count = 1\n",
+ "elif base_model_name == \"llama2-7b-chat-hf\":\n",
+ " # Sets 1 L4 (24G) to evaluate LLaMA2 7B models.\n",
+ " machine_type = \"g2-standard-16\"\n",
+ " accelerator_type = \"NVIDIA_L4\"\n",
+ " accelerator_count = 1\n",
+ "elif base_model_name == \"llama2-13b-hf\":\n",
+ " # Sets 2 L4 (24G) to evaluate LLaMA2 13B models.\n",
+ " machine_type = \"g2-standard-24\"\n",
+ " accelerator_type = \"NVIDIA_L4\"\n",
+ " accelerator_count = 2\n",
+ "elif base_model_name == \"llama2-13b-chat-hf\":\n",
+ " # Sets 2 L4 (24G) to evaluate LLaMA2 13B models.\n",
+ " machine_type = \"g2-standard-24\"\n",
+ " accelerator_type = \"NVIDIA_L4\"\n",
+ " accelerator_count = 2\n",
+ "elif base_model_name == \"llama2-70b-hf\":\n",
+ " # Sets 8 L4 (24G) to evaluate LLaMA2 70B models.\n",
+ " machine_type = \"g2-standard-96\"\n",
+ " accelerator_type = \"NVIDIA_L4\"\n",
+ " accelerator_count = 8\n",
+ "elif base_model_name == \"llama2-70b-chat-hf\":\n",
+ " # Sets 8 L4 (24G) to evaluate LLaMA2 70B models.\n",
+ " machine_type = \"g2-standard-96\"\n",
+ " accelerator_type = \"NVIDIA_L4\"\n",
+ " accelerator_count = 8\n",
+ "\n",
+ "common_util.check_quota(\n",
+ " project_id=PROJECT_ID,\n",
+ " region=REGION,\n",
+ " accelerator_type=accelerator_type,\n",
+ " accelerator_count=accelerator_count,\n",
+ " is_for_training=True,\n",
+ ")\n",
+ "\n",
+ "replica_count = 1\n",
+ "\n",
+ "job_name = get_job_name_with_datetime(prefix=\"llama2-eval\")\n",
+ "eval_output_dir = os.path.join(MODEL_BUCKET, job_name)\n",
+ "eval_output_dir_gcsfuse = eval_output_dir.replace(\"gs://\", \"/gcs/\")\n",
+ "base_model_id_gcsfuse = base_model_id.replace(\"gs://\", \"/gcs/\")\n",
+ "\n",
+ "# @markdown To evaluate a PEFT-finetuned model, enter the PEFT output directory below.\n",
+ "# @markdown Otherwise, leave it empty.\n",
+ "\n",
+ "# @markdown See the [finetuning notebook](https://github.com/GoogleCloudPlatform/vertex-ai-samples/blob/main/notebooks/community/model_garden/model_garden_pytorch_llama2_peft_finetuning.ipynb) for more details:\n",
+ "\n",
+ "peft_output_dir = \"\" # @param {type:\"string\"}\n",
+ "peft_output_dir_gcsfuse = peft_output_dir.replace(\"gs://\", \"/gcs/\")\n",
+ "\n",
+ "# Prepare evaluation command that runs the evaluation harness.\n",
+ "# Set `trust_remote_code = True` because evaluating the model requires\n",
+ "# executing code from the model repository.\n",
+ "# Set `use_accelerate = True` to enable evaluation across multiple GPUs.\n",
+ "eval_command = [\n",
+ " \"python\",\n",
+ " \"main.py\",\n",
+ " \"--model\",\n",
+ " \"hf-causal-experimental\",\n",
+ " \"--tasks\",\n",
+ " f\"{eval_dataset}\",\n",
+ " \"--output_path\",\n",
+ " f\"{eval_output_dir_gcsfuse}\",\n",
+ "]\n",
+ "\n",
+ "if peft_output_dir_gcsfuse:\n",
+ " eval_command += [\n",
+ " \"--model_args\",\n",
+ " f\"pretrained={base_model_id_gcsfuse},peft={peft_output_dir_gcsfuse},trust_remote_code=True,use_accelerate=True,device_map_option=auto\",\n",
+ " ]\n",
+ "else:\n",
+ " eval_command += [\n",
+ " \"--model_args\",\n",
+ " f\"pretrained={base_model_id_gcsfuse},trust_remote_code=True,use_accelerate=True,device_map_option=auto\",\n",
+ " ]\n",
+ "\n",
+ "# Run the evaluation job.\n",
+ "worker_pool_specs = [\n",
+ " {\n",
+ " \"machine_spec\": {\n",
+ " \"machine_type\": machine_type,\n",
+ " \"accelerator_type\": accelerator_type,\n",
+ " \"accelerator_count\": accelerator_count,\n",
+ " },\n",
+ " \"replica_count\": replica_count,\n",
+ " \"disk_spec\": {\n",
+ " \"boot_disk_size_gb\": 500,\n",
+ " },\n",
+ " \"container_spec\": {\n",
+ " \"image_uri\": EVAL_DOCKER_URI,\n",
+ " \"command\": eval_command,\n",
+ " \"args\": [],\n",
+ " },\n",
+ " }\n",
+ "]\n",
+ "\n",
+ "\n",
+ "# Add labels for the finetuning job.\n",
+ "labels = {\n",
+ " \"mg-source\": \"notebook\",\n",
+ " \"mg-notebook-name\": \"model_garden_pytorch_llama2_evaluation.ipynb\".split(\".\")[0],\n",
+ "}\n",
+ "\n",
+ "labels[\"mg-tune\"] = \"publishers-meta-models-llama2\"\n",
+ "versioned_model_id = base_model_name.lower()\n",
+ "labels[\"versioned-mg-tune\"] = f\"{labels['mg-tune']}-{versioned_model_id}\"\n",
+ "\n",
+ "eval_job = aiplatform.CustomJob(\n",
+ " display_name=job_name,\n",
+ " worker_pool_specs=worker_pool_specs,\n",
+ " base_output_dir=eval_output_dir,\n",
+ " labels=labels,\n",
+ ")\n",
+ "\n",
+ "eval_job.run()\n",
+ "\n",
+ "print(\"Evaluation results were saved in:\", eval_output_dir)\n",
+ "\n",
+ "# Fetch evaluation results.\n",
+ "storage_client = storage.Client()\n",
+ "BUCKET_NAME = BUCKET_URI.replace(\"gs://\", \"\")\n",
+ "bucket = storage_client.get_bucket(BUCKET_NAME)\n",
+ "RESULT_FILE_PATH = eval_output_dir[len(BUCKET_URI) + 1 :]\n",
+ "blob = bucket.blob(RESULT_FILE_PATH)\n",
+ "raw_result = blob.download_as_string()\n",
+ "\n",
+ "# Print evaluation results.\n",
+ "result = json.loads(raw_result)\n",
+ "result_formatted = json.dumps(result, indent=2)\n",
+ "print(f\"Evaluation result:\\n{result_formatted}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "R3Vm13jGd4aU"
+ },
+ "source": [
+ "### Bulk Inference"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "cellView": "form",
+ "id": "yyW4BJDr9t-O"
+ },
+ "outputs": [],
+ "source": [
+ "# @title [Optional] Generate `input_dataset` for the bulk inference job\n",
+ "# @markdown Note: For experimentation, we request that users provide only a few prompts.\n",
+ "\n",
+ "# @markdown For demonstration, a publicly available [TensorFlow dataset](https://www.tensorflow.org/datasets/catalog/reddit) is used. This dataset contains preprocessed posts from the Reddit dataset.\n",
+ "TEST_DATASET = \"gs://vertex-ai/generative-ai/rlhf/text_small/reddit_tfds/val/shard-00000-of-00001.jsonl\" # @param {type:\"string\"}\n",
+ "NUM_EXAMPLES = 50 # @param {type:\"integer\"}\n",
+ "\n",
+ "# Load dataset and modify.\n",
+ "df = pd.read_json(TEST_DATASET, lines=True)\n",
+ "examples = df.head(NUM_EXAMPLES)\n",
+ "\n",
+ "# Upload new dataset to GCS.\n",
+ "examples.to_json(\"data.json\", orient=\"records\", lines=True)\n",
+ "! gsutil cp data.json $BUCKET_URI/temp/data.jsonl\n",
+ "DATASET = f\"{BUCKET_URI}/temp/data.jsonl\"\n",
+ "\n",
+ "print(f\"{NUM_EXAMPLES} examples written to {DATASET}.\")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "cellView": "form",
+ "id": "X-yl8yOptBvH"
+ },
+ "outputs": [],
+ "source": [
+ "# @title Set up bulk inference job\n",
+ "\n",
+ "# @markdown Run the bulk inference custom job to generate offline predictions.\n",
+ "# @markdown You can perform bulk inference using a base model or LoRA finetuned model.\n",
+ "\n",
+ "# Setup bulk inference job.\n",
+ "model_a = \"llama2-7b-hf\" # @param [\"llama2-7b-hf\", \"llama2-7b-chat-hf\", \"llama2-13b-hf\", \"llama2-13b-chat-hf\", \"llama2-70b-hf\", \"llama2-70b-chat-hf\"]\n",
+ "model_b = \"llama2-13b-hf\" # @param [\"llama2-7b-hf\", \"llama2-7b-chat-hf\", \"llama2-13b-hf\", \"llama2-13b-chat-hf\", \"llama2-70b-hf\", \"llama2-70b-chat-hf\"]\n",
+ "\n",
+ "# @markdown **Required Parameters**\n",
+ "\n",
+ "# @markdown `input_dataset` : Path to JSONL file containing the input dataset.\n",
+ "\n",
+ "# @markdown `index_column` : The column which distinguishes unique evaluation examples.\n",
+ "\n",
+ "# @markdown `input_text` : Indexing key for inputs.\n",
+ "\n",
+ "# @markdown `output_prediction_a`: Path to JSONL file which will contain output predictions of model a.\n",
+ "\n",
+ "# @markdown `output_prediction_b`: Path to JSONL file which will contain output predictions of model b.\n",
+ "\n",
+ "input_dataset = f\"{BUCKET_URI}/temp/data.jsonl\" # @param {type:\"string\"}\n",
+ "index_column = \"inputs\" # @param {type:\"string\"}\n",
+ "input_text = \"input_text\" # @param {type:\"string\"}\n",
+ "\n",
+ "output_prediction_a = \"\" # @param {type:\"string\"}\n",
+ "output_prediction_b = \"\" # @param {type:\"string\"}\n",
+ "\n",
+ "\n",
+ "# @markdown **Optional Parameters** : Provide the path to the LoRA finetuned models.\n",
+ "\n",
+ "# @markdown `lora_path_a` : Path to finetuned LoRA adapter of model a.\n",
+ "\n",
+ "# @markdown `lora_path_b` : Path to finetuned LoRA adapter of model b.\n",
+ "\n",
+ "lora_path_a = \"\" # @param {type:\"string\"}\n",
+ "lora_path_b = \"\" # @param {type:\"string\"}\n",
+ "\n",
+ "\n",
+ "if model_a == model_b:\n",
+ " raise ValueError(\"Select different models to run AutoSxS evaluation.\")\n",
+ "\n",
+ "if model_a in [\"llama2-7b-hf\", \"llama2-7b-chat-hf\"]:\n",
+ " machine_type_a = \"g2-standard-16\"\n",
+ " accelerator_type_a = \"NVIDIA_L4\"\n",
+ " accelerator_count_a = 1\n",
+ "elif model_a in [\"llama2-13b-hf\", \"llama2-13b-chat-hf\"]:\n",
+ " machine_type_a = \"g2-standard-24\"\n",
+ " accelerator_type_a = \"NVIDIA_L4\"\n",
+ " accelerator_count_a = 2\n",
+ "elif model_a in [\"llama2-70b-hf\", \"llama2-70b-chat-hf\"]:\n",
+ " machine_type_a = \"g2-standard-96\"\n",
+ " accelerator_type_a = \"NVIDIA_L4\"\n",
+ " accelerator_count_a = 8\n",
+ "\n",
+ "common_util.check_quota(\n",
+ " project_id=PROJECT_ID,\n",
+ " region=REGION,\n",
+ " accelerator_type=accelerator_type_a,\n",
+ " accelerator_count=accelerator_count_a,\n",
+ " is_for_training=True,\n",
+ ")\n",
+ "\n",
+ "if model_b in [\"llama2-7b-hf\", \"llama2-7b-chat-hf\"]:\n",
+ " machine_type_b = \"g2-standard-16\"\n",
+ " accelerator_type_b = \"NVIDIA_L4\"\n",
+ " accelerator_count_b = 1\n",
+ "elif model_b in [\"llama2-13b-hf\", \"llama2-13b-chat-hf\"]:\n",
+ " machine_type_b = \"g2-standard-24\"\n",
+ " accelerator_type_b = \"NVIDIA_L4\"\n",
+ " accelerator_count_b = 2\n",
+ "elif model_b in [\"llama2-70b-hf\", \"llama2-70b-chat-hf\"]:\n",
+ " machine_type_b = \"g2-standard-96\"\n",
+ " accelerator_type_b = \"NVIDIA_L4\"\n",
+ " accelerator_count_b = 8\n",
+ "\n",
+ "common_util.check_quota(\n",
+ " project_id=PROJECT_ID,\n",
+ " region=REGION,\n",
+ " accelerator_type=accelerator_type_b,\n",
+ " accelerator_count=accelerator_count_b,\n",
+ " is_for_training=True,\n",
+ ")\n",
+ "\n",
+ "replica_count = 1\n",
+ "\n",
+ "bulk_infer_job_name = get_job_name_with_datetime(prefix=\"bulk-infer\")\n",
+ "eval_output_dir = os.path.join(MODEL_BUCKET, bulk_infer_job_name)\n",
+ "\n",
+ "# Maximum encoder/prefix length. Inputs will be padded or truncated to match this length.\n",
+ "input_seq_length = 50\n",
+ "\n",
+ "# Maximum number of decoder steps. Outputs will be at most this length.\n",
+ "targets_seq_length = 50\n",
+ "\n",
+ "model_id_a = os.path.join(BASE_MODEL_BUCKET, model_a)\n",
+ "model_id_b = os.path.join(BASE_MODEL_BUCKET, model_b)\n",
+ "\n",
+ "worker_pool_specs_base_model_a = [\n",
+ " {\n",
+ " \"machine_spec\": {\n",
+ " \"machine_type\": machine_type_a,\n",
+ " \"accelerator_type\": accelerator_type_a,\n",
+ " \"accelerator_count\": accelerator_count_a,\n",
+ " },\n",
+ " \"replica_count\": replica_count,\n",
+ " \"disk_spec\": {\n",
+ " \"boot_disk_type\": \"pd-ssd\",\n",
+ " \"boot_disk_size_gb\": 500,\n",
+ " },\n",
+ " \"container_spec\": {\n",
+ " \"image_uri\": BULK_INFERRER_DOCKER_URI,\n",
+ " \"args\": [\n",
+ " f\"--large_model_reference={model_id_a}\",\n",
+ " f\"--input_model={lora_path_a}\",\n",
+ " f\"--input_dataset={input_dataset}\",\n",
+ " \"--dataset_split=empty\",\n",
+ " f\"--output_prediction={output_prediction_a}\",\n",
+ " f\"--output_prediction_gcs_path={OUTPUT_BUCKET_A}\",\n",
+ " f\"--inputs_sequence_length={input_seq_length}\",\n",
+ " f\"--targets_sequence_length={targets_seq_length}\",\n",
+ " f\"--inputs_key={input_text}\",\n",
+ " ],\n",
+ " },\n",
+ " }\n",
+ "]\n",
+ "\n",
+ "worker_pool_specs_base_model_b = [\n",
+ " {\n",
+ " \"machine_spec\": {\n",
+ " \"machine_type\": machine_type_b,\n",
+ " \"accelerator_type\": accelerator_type_b,\n",
+ " \"accelerator_count\": accelerator_count_b,\n",
+ " },\n",
+ " \"replica_count\": replica_count,\n",
+ " \"disk_spec\": {\n",
+ " \"boot_disk_size_gb\": 500,\n",
+ " },\n",
+ " \"container_spec\": {\n",
+ " \"image_uri\": BULK_INFERRER_DOCKER_URI,\n",
+ " \"args\": [\n",
+ " f\"--large_model_reference={model_id_b}\",\n",
+ " f\"--input_model={lora_path_b}\",\n",
+ " f\"--input_dataset={input_dataset}\",\n",
+ " \"--dataset_split=empty\",\n",
+ " f\"--output_prediction={output_prediction_b}\",\n",
+ " f\"--output_prediction_gcs_path={OUTPUT_BUCKET_B}\",\n",
+ " f\"--inputs_sequence_length={input_seq_length}\",\n",
+ " f\"--targets_sequence_length={targets_seq_length}\",\n",
+ " f\"--inputs_key={input_text}\",\n",
+ " ],\n",
+ " },\n",
+ " }\n",
+ "]"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "cellView": "form",
+ "id": "x1sPbM1-eEx7"
+ },
+ "outputs": [],
+ "source": [
+ "# @title Run bulk inference job for Model A\n",
+ "\n",
+ "bulk_inferrer_a = aiplatform.CustomJob(\n",
+ " display_name=get_job_name_with_datetime(prefix=\"bulk-infer-a\"),\n",
+ " worker_pool_specs=worker_pool_specs_base_model_a,\n",
+ " base_output_dir=os.path.join(OUTPUT_BUCKET_A, bulk_infer_job_name),\n",
+ ")\n",
+ "\n",
+ "bulk_inferrer_a.run()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "cellView": "form",
+ "id": "_WzQmq2yeHSg"
+ },
+ "outputs": [],
+ "source": [
+ "# @title Run bulk inference job for Model B\n",
+ "\n",
+ "bulk_inferrer_b = aiplatform.CustomJob(\n",
+ " display_name=get_job_name_with_datetime(prefix=\"bulk-infer-b\"),\n",
+ " worker_pool_specs=worker_pool_specs_base_model_b,\n",
+ " base_output_dir=os.path.join(OUTPUT_BUCKET_B, bulk_infer_job_name),\n",
+ ")\n",
+ "\n",
+ "bulk_inferrer_b.run()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "7pX-sEhReKdB"
+ },
+ "source": [
+ "### AutoSxS Job"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "cellView": "form",
+ "id": "hE_8SlrUGrSP"
+ },
+ "outputs": [],
+ "source": [
+ "# @title Compile AutoSxS pipeline\n",
+ "\n",
+ "from google_cloud_pipeline_components.preview import model_evaluation\n",
+ "from kfp import compiler\n",
+ "\n",
+ "template_uri = \"pipeline.yaml\"\n",
+ "compiler.Compiler().compile(\n",
+ " pipeline_func=model_evaluation.autosxs_pipeline,\n",
+ " package_path=template_uri,\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "cellView": "form",
+ "id": "VRBrm4d3eLtV"
+ },
+ "outputs": [],
+ "source": [
+ "# @title Run AutoSxS pipeline job\n",
+ "\n",
+ "# @markdown Automatic side-by-side (AutoSxS) is a model-assisted evaluation tool that compares two large language models (LLMs) side by side.\n",
+ "# @markdown In order to run AutoSxS, we need to define a `autosxs_pipeline` job with the following parameters. More details of the AutoSxS pipeline configuration can be found [here](https://google-cloud-pipeline-components.readthedocs.io/en/google-cloud-pipeline-components-2.9.0/api/preview/model_evaluation.html#preview.model_evaluation.autosxs_pipeline).\n",
+ "\n",
+ "\n",
+ "# Preprocess the output json files and copy to the GCS bucket\n",
+ "preprocess(output_prediction_a, output_prediction_b)\n",
+ "PREDS = f\"{BUCKET_URI}/temp/preds/\"\n",
+ "! gsutil cp result.jsonl $PREDS\n",
+ "\n",
+ "# @title AutoSxS Job\n",
+ "autosxs_job_name = get_job_name_with_datetime(prefix=\"autosxs\")\n",
+ "\n",
+ "# @markdown AutoSxS supports evaluating models for summarization and question-answering tasks.\n",
+ "\"\"\"\n",
+ "Evaluation task in the form {task}@{version}. Task can be one of\n",
+ "[summarization, question_answer].\n",
+ "version is an integer with three digits or 'latest'.\n",
+ "Ex: summarization@001 or question_answer@latest\n",
+ "\"\"\"\n",
+ "# fmt: off\n",
+ "task_name = \"summarization@001\" # @param [\"question_answer@latest\", \"summarization@001\"]\n",
+ "# fmt: on\n",
+ "\n",
+ "\n",
+ "parameters = {\n",
+ " \"evaluation_dataset\": f\"{BUCKET_URI}/temp/preds/result.jsonl\",\n",
+ " \"id_columns\": [\"inputs\"],\n",
+ " \"autorater_prompt_parameters\": {\n",
+ " \"inference_context\": {\"column\": index_column},\n",
+ " \"inference_instruction\": {\"template\": \"{{ default_instruction }}\"},\n",
+ " },\n",
+ " \"response_column_a\": \"pred_a\",\n",
+ " \"response_column_b\": \"pred_b\",\n",
+ " \"task\": task_name,\n",
+ "}\n",
+ "\n",
+ "autosxs_job = aiplatform.PipelineJob(\n",
+ " job_id=autosxs_job_name,\n",
+ " display_name=autosxs_job_name,\n",
+ " pipeline_root=os.path.join(BUCKET_URI, autosxs_job_name),\n",
+ " template_path=template_uri,\n",
+ " parameter_values=parameters,\n",
+ " enable_caching=False,\n",
+ ")\n",
+ "autosxs_job.run()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "cellView": "form",
+ "id": "gAXCY0hueRBF"
+ },
+ "outputs": [],
+ "source": [
+ "# @title Get autorater judgements\n",
+ "# @markdown Autorater is a language model which compares the quality of two model responses based on a pre-defined criteria.\n",
+ "# @markdown More details on the autorater can be found [here](https://cloud.google.com/vertex-ai/generative-ai/docs/models/side-by-side-eval#autorater)\n",
+ "\n",
+ "for details in autosxs_job.task_details:\n",
+ " if details.task_name == \"online-evaluation-pairwise\":\n",
+ " break\n",
+ "\n",
+ "# Judgments\n",
+ "judgments_uri = details.outputs[\"judgments\"].artifacts[0].uri\n",
+ "judgments_df = pd.read_json(judgments_uri, lines=True)\n",
+ "judgments_df.head()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "cellView": "form",
+ "id": "5NYV1FKjIeD6"
+ },
+ "outputs": [],
+ "source": [
+ "# @title Get win-rate\n",
+ "# @markdown Win rate is the percentage of the time the autorater has decided that a particular model had a better response.\n",
+ "\n",
+ "for details in autosxs_job.task_details:\n",
+ " if details.task_name == \"model-evaluation-text-generation-pairwise\":\n",
+ " break\n",
+ "pd.DataFrame([details.outputs[\"autosxs_metrics\"].artifacts[0].metadata])"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "uJuMq31DeWwO"
+ },
+ "source": [
+ "### Clean up resources"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "cellView": "form",
+ "id": "911406c1561e"
+ },
+ "outputs": [],
+ "source": [
+ "# @title Clean up\n",
+ "# @markdown Delete the jobs to recycle the resources and avoid unnecessary continouous charges that may incur.\n",
+ "\n",
+ "eval_job.delete()\n",
+ "bulk_inferrer_a.delete()\n",
+ "bulk_inferrer_b.delete()\n",
+ "autosxs_job.delete()\n",
+ "\n",
+ "# Delete Cloud Storage objects that were created\n",
+ "delete_bucket = False # @param {type: \"boolean\"}\n",
+ "if delete_bucket:\n",
+ " ! gsutil -m rm -r $EXPERIMENT_BUCKET"
+ ]
+ }
+ ],
+ "metadata": {
+ "colab": {
+ "name": "model_garden_pytorch_llama2_evaluation.ipynb",
+ "toc_visible": true
+ },
+ "kernelspec": {
+ "display_name": "Python 3",
+ "name": "python3"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
}
diff --git a/notebooks/community/model_garden/model_garden_pytorch_llama2_peft_finetuning.ipynb b/notebooks/community/model_garden/model_garden_pytorch_llama2_peft_finetuning.ipynb
deleted file mode 100644
index d3ddf49c7..000000000
--- a/notebooks/community/model_garden/model_garden_pytorch_llama2_peft_finetuning.ipynb
+++ /dev/null
@@ -1,682 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "id": "7d9bbf86da5e"
- },
- "outputs": [],
- "source": [
- "# Copyright 2024 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": "99c1c3fc2ca5"
- },
- "source": [
- "# Vertex AI Model Garden - LLaMA2 (PEFT Finetuning)\n",
- "\n",
- "\n",
- " \n",
- " \n",
- "  Run in Colab Enterprise\n",
- " \n",
- " | \n",
- " \n",
- " \n",
- "  View on GitHub\n",
- " \n",
- " | \n",
- "
"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "3de7470326a2"
- },
- "source": [
- "## Overview\n",
- "\n",
- "This notebook demonstrates downloading [LLaMA2 models](https://huggingface.co/meta-llama), finetuning with parameter efficient finetuning libraries ([PEFT](https://github.com/huggingface/peft)), and deploying the finetuned model on Vertex AI.\n",
- "\n",
- "### Objective\n",
- "\n",
- "- Download prebuilt LLaMA2 models.\n",
- "- Finetune and deploy LLaMA2 models with Vertex AI SDK.\n",
- "\n",
- "### Costs\n",
- "\n",
- "This tutorial uses billable components of Google Cloud:\n",
- "\n",
- "* Vertex AI\n",
- "* Cloud Storage\n",
- "\n",
- "Learn about [Vertex AI pricing](https://cloud.google.com/vertex-ai/pricing), [Cloud Storage pricing](https://cloud.google.com/storage/pricing) and use the [Pricing Calculator](https://cloud.google.com/products/calculator/) to generate a cost estimate based on your projected usage."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "264c07757582"
- },
- "source": [
- "## Run the notebook"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "QJgmw34Xwctp"
- },
- "outputs": [],
- "source": [
- "# @title (Optional) Finetune with Vertex AI Pipeline\n",
- "\n",
- "# @markdown Vertex Model Garden offers a pre-configured pipeline that can be launched from the UI, which will fine-tune, evaluate, upload, and deploy your desired LLaMA2 model.\n",
- "# @markdown This pipeline currently supports [huggingface datasets](https://huggingface.co/datasets) for finetuning.\n",
- "\n",
- "# @markdown To launch a LLaMA 2 finetuning pipeline, open the [LLaMA 2 model card](https://console.cloud.google.com/vertex-ai/publishers/google/model-garden/139) and click the \"FINE-TUNE\" button.\n",
- "# @markdown Then, click \"CREATE RUN\" button near the top of the pipeline details page, and follow the instructions to fill in pipeline parameters.\n",
- "\n",
- "# @markdown Learn about [Vertex AI Pipelines](https://cloud.google.com/vertex-ai/docs/pipelines/introduction)."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "IoPsYDwDdFBf"
- },
- "outputs": [],
- "source": [
- "# @title Setup Google Cloud project\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] [Create a Cloud Storage bucket](https://cloud.google.com/storage/docs/creating-buckets) for storing experiment outputs. Set the BUCKET_URI for the experiment environment. The specified Cloud Storage bucket (`BUCKET_URI`) should be located in the same region as where the notebook was launched. Note that a multi-region bucket (eg. \"us\") is not considered a match for a single region covered by the multi-region range (eg. \"us-central1\"). If not set, a unique GCS bucket will be created instead.\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).\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, us-east5, europe-west4, us-west1, asia-southeast1 |\n",
- "\n",
- "\n",
- "# Upgrade Vertex AI SDK.\n",
- "! pip3 install --upgrade --quiet 'google-cloud-aiplatform>=1.64.0'\n",
- "! git clone https://github.com/GoogleCloudPlatform/vertex-ai-samples.git\n",
- "\n",
- "# Import the necessary packages\n",
- "import importlib\n",
- "import os\n",
- "import uuid\n",
- "from datetime import datetime\n",
- "from typing import Tuple\n",
- "\n",
- "from google.cloud import aiplatform\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",
- "# Get the default cloud project id.\n",
- "PROJECT_ID = os.environ[\"GOOGLE_CLOUD_PROJECT\"]\n",
- "\n",
- "# Get the default region for launching jobs.\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",
- "# Cloud Storage bucket for storing the experiment artifacts.\n",
- "# A unique GCS bucket will be created for the purpose of this notebook. If you\n",
- "# prefer using your own GCS bucket, change the value below.\n",
- "now = datetime.now().strftime(\"%Y%m%d%H%M%S\")\n",
- "BUCKET_URI = \"gs://\" # @param {type:\"string\"}\n",
- "\n",
- "if BUCKET_URI is None or BUCKET_URI.strip() == \"\" or BUCKET_URI == \"gs://\":\n",
- " BUCKET_URI = f\"gs://{PROJECT_ID}-tmp-{now}-{str(uuid.uuid4())[:4]}\"\n",
- " ! gsutil mb -l {REGION} {BUCKET_URI}\n",
- "else:\n",
- " assert BUCKET_URI.startswith(\"gs://\"), \"BUCKET_URI must start with `gs://`.\"\n",
- " BUCKET_NAME = \"/\".join(BUCKET_URI.split(\"/\")[:3])\n",
- " shell_output = ! gsutil ls -Lb {BUCKET_NAME} | grep \"Location constraint:\" | sed \"s/Location constraint://\"\n",
- " bucket_region = shell_output[0].strip().lower()\n",
- " if bucket_region != REGION:\n",
- " raise ValueError(\n",
- " \"Bucket region %s is different from notebook region %s\"\n",
- " % (bucket_region, REGION)\n",
- " )\n",
- "print(f\"Using this GCS Bucket: {BUCKET_URI}\")\n",
- "\n",
- "STAGING_BUCKET = os.path.join(BUCKET_URI, \"temporal\")\n",
- "EXPERIMENT_BUCKET = os.path.join(BUCKET_URI, \"peft\")\n",
- "MODEL_BUCKET = os.path.join(BUCKET_URI, \"llama2\")\n",
- "\n",
- "# Initialize Vertex AI API.\n",
- "print(\"Initializing Vertex AI API.\")\n",
- "aiplatform.init(project=PROJECT_ID, location=REGION, staging_bucket=STAGING_BUCKET)\n",
- "\n",
- "# Gets the default BUCKET_URI and SERVICE_ACCOUNT if they were not specified by the user.\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",
- "# Provision permissions to the SERVICE_ACCOUNT with the GCS bucket\n",
- "BUCKET_NAME = \"/\".join(BUCKET_URI.split(\"/\")[:3])\n",
- "! gsutil iam ch serviceAccount:{SERVICE_ACCOUNT}:roles/storage.admin $BUCKET_NAME\n",
- "\n",
- "! gcloud config set project $PROJECT_ID"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "zI30m3bqDtCj"
- },
- "outputs": [],
- "source": [
- "# @title Access LLaMA2 models on Vertex AI for GPU based serving\n",
- "# @markdown The original models from Meta are converted into the Hugging Face format for serving in Vertex AI.\n",
- "# @markdown Accept the model agreement to access the models:\n",
- "# @markdown 1. Open the [LLaMA2 model card](https://console.cloud.google.com/vertex-ai/publishers/google/model-garden/139) from [Vertex AI Model Garden](https://cloud.google.com/model-garden).\n",
- "# @markdown 2. Review and accept the agreement in the pop-up window on the model card page. If you have previously accepted the model agreement, there will not be a pop-up window on the model card page and this step is not needed.\n",
- "# @markdown 3. A Cloud Storage bucket (starting with `gs://`) containing LLaMA 2 pretrained and finetuned models will be shared under the “Documentation” section and its “Get started” subsection.\n",
- "\n",
- "\n",
- "VERTEX_AI_MODEL_GARDEN_LLAMA2 = \"\" # @param {type:\"string\", isTemplate:true}\n",
- "assert (\n",
- " VERTEX_AI_MODEL_GARDEN_LLAMA2\n",
- "), \"Model artifact path is required. Click the agreement of LLaMA2 in Vertex AI Model Garden, and get the GCS path of LLaMA2 model artifacts.\"\n",
- "print(\n",
- " \"Copying LLaMA2 model artifacts from\",\n",
- " VERTEX_AI_MODEL_GARDEN_LLAMA2,\n",
- " \"to\",\n",
- " MODEL_BUCKET,\n",
- ")\n",
- "\n",
- "! gsutil -m cp -R $VERTEX_AI_MODEL_GARDEN_LLAMA2/* $MODEL_BUCKET\n",
- "\n",
- "# The pre-built serving and training docker images.\n",
- "VLLM_DOCKER_URI = \"us-docker.pkg.dev/vertex-ai/vertex-vision-model-garden-dockers/pytorch-vllm-serve:20240326_0916_RC00\"\n",
- "TRAIN_DOCKER_URI = \"us-docker.pkg.dev/vertex-ai/vertex-vision-model-garden-dockers/pytorch-peft-train:20240321_0936_RC00\"\n",
- "\n",
- "\n",
- "def get_job_name_with_datetime(prefix: str) -> str:\n",
- " \"\"\"Gets the job name with date time when triggering training or deployment\n",
- " jobs in Vertex AI.\n",
- " \"\"\"\n",
- " return prefix + datetime.now().strftime(\"_%Y%m%d_%H%M%S\")\n",
- "\n",
- "\n",
- "def deploy_model_vllm(\n",
- " model_name: str,\n",
- " model_id: str,\n",
- " service_account: str,\n",
- " machine_type: str = \"g2-standard-8\",\n",
- " accelerator_type: str = \"NVIDIA_L4\",\n",
- " accelerator_count: int = 1,\n",
- " max_model_len: int = 4096,\n",
- ") -> Tuple[aiplatform.Model, aiplatform.Endpoint]:\n",
- " \"\"\"Deploys trained models with vLLM into Vertex AI.\"\"\"\n",
- " common_util.check_quota(\n",
- " project_id=PROJECT_ID,\n",
- " region=REGION,\n",
- " accelerator_type=accelerator_type,\n",
- " accelerator_count=accelerator_count,\n",
- " is_for_training=False,\n",
- " )\n",
- "\n",
- " endpoint = aiplatform.Endpoint.create(display_name=f\"{model_name}-endpoint\")\n",
- "\n",
- " vllm_args = [\n",
- " \"--host=0.0.0.0\",\n",
- " \"--port=7080\",\n",
- " f\"--tensor-parallel-size={accelerator_count}\",\n",
- " \"--swap-space=16\",\n",
- " \"--gpu-memory-utilization=0.8\",\n",
- " f\"--max-model-len={max_model_len}\",\n",
- " \"--max-num-batched-tokens=4096\",\n",
- " \"--disable-log-stats\",\n",
- " ]\n",
- "\n",
- " env_vars = {\"MODEL_ID\": model_id, \"DEPLOY_SOURCE\": \"notebook\"}\n",
- " model = aiplatform.Model.upload(\n",
- " display_name=model_name,\n",
- " serving_container_image_uri=VLLM_DOCKER_URI,\n",
- " serving_container_command=[\"python\", \"-m\", \"vllm.entrypoints.api_server\"],\n",
- " serving_container_args=vllm_args,\n",
- " serving_container_ports=[7080],\n",
- " serving_container_predict_route=\"/generate\",\n",
- " serving_container_health_route=\"/ping\",\n",
- " serving_container_environment_variables=env_vars,\n",
- " artifact_uri=model_id,\n",
- " model_garden_source_model_name=\"publishers/meta/models/llama2\"\n",
- " )\n",
- " print(\n",
- " f\"Deploying {model_name} on {machine_type} with {accelerator_count} {accelerator_type} GPU(s).\"\n",
- " )\n",
- " model.deploy(\n",
- " endpoint=endpoint,\n",
- " machine_type=machine_type,\n",
- " accelerator_type=accelerator_type,\n",
- " accelerator_count=accelerator_count,\n",
- " deploy_request_timeout=1800,\n",
- " service_account=service_account,\n",
- " system_labels={\n",
- " \"NOTEBOOK_NAME\": \"model_garden_pytorch_llama2_peft_finetuning.ipynb\"\n",
- " },\n",
- " )\n",
- " print(\"endpoint_name:\", endpoint.name)\n",
- "\n",
- " print(\"To load this existing endpoint from a different session:\")\n",
- " print(\"from google.cloud import aiplatform\")\n",
- " print(\n",
- " f'endpoint = aiplatform.Endpoint(\"projects/{PROJECT_ID}/locations/{REGION}/endpoints/{endpoint.name}\")'\n",
- " )\n",
- " return model, endpoint"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "Ax6GlzzMk-sc"
- },
- "outputs": [],
- "source": [
- "# @title Set training dataset\n",
- "\n",
- "# @markdown This notebook uses [timdettmers/openassistant-guanaco](https://huggingface.co/datasets/timdettmers/openassistant-guanaco) dataset as an example.\n",
- "# @markdown You can set `dataset_name` to any existing [Hugging Face dataset](https://huggingface.co/datasets) name, and set `instruct_column_in_dataset` to the name of the dataset column containing training data. The [timdettmers/openassistant-guanaco](https://huggingface.co/datasets/timdettmers/openassistant-guanaco) has only one column `text`, and therefore we set `instruct_column_in_dataset` to `text` in this notebook.\n",
- "\n",
- "# @markdown #### (Optional) Prepare a custom JSONL dataset for finetuning\n",
- "\n",
- "# @markdown You can prepare a JSONL file where each line is a valid JSON string as your custom training dataset. For example, here is one line from the [timdettmers/openassistant-guanaco](https://huggingface.co/datasets/timdettmers/openassistant-guanaco) dataset:\n",
- "# @markdown ```\n",
- "# @markdown {\"text\": \"### Human: Hola### Assistant: \\u00a1Hola! \\u00bfEn qu\\u00e9 puedo ayudarte hoy?\"}\n",
- "# @markdown ```\n",
- "\n",
- "# @markdown The JSON object has a key `text`, which should match `instruct_column_in_dataset`; The value should be one training data point, i.e. a string. After you prepared your JSONL file, you can either upload it to [Hugging Face datasets](https://huggingface.co/datasets) or [Google Cloud Storage](https://cloud.google.com/storage).\n",
- "\n",
- "# @markdown - To upload a JSONL dataset to [Hugging Face datasets](https://huggingface.co/datasets), follow the instructions on [Uploading Datasets](https://huggingface.co/docs/hub/en/datasets-adding). Then, set `dataset_name` to the name of your newly created dataset on Hugging Face.\n",
- "\n",
- "# @markdown - To upload a JSONL dataset to [Google Cloud Storage](https://cloud.google.com/storage), follow the instructions on [Upload objects from a filesystem](https://cloud.google.com/storage/docs/uploading-objects). Then, set `dataset_name` to the `gs://` URI to your JSONL file. For example: `gs://cloud-samples-data/vertex-ai/model-evaluation/peft_train_sample.jsonl`.\n",
- "\n",
- "# @markdown Optionally update the `instruct_column_in_dataset` field below if your JSON objects use a key other than the default `text`.\n",
- "\n",
- "# @markdown #### (Optional) Format your data with custom JSON template\n",
- "\n",
- "# @markdown Sometimes, your dataset might have multiple text columns and you want to construct the training data with a template. You can prepare a JSON template in the following format:\n",
- "\n",
- "# @markdown ```\n",
- "# @markdown {\n",
- "# @markdown \"description\": \"A short template for vertex sample dataset.\",\n",
- "# @markdown \"prompt_input\": \"{input_text}{output_text}\",\n",
- "# @markdown \"prompt_no_input\": \"{input_text}{output_text}\"\n",
- "# @markdown }\n",
- "# @markdown ```\n",
- "\n",
- "# @markdown As an example, the template above can be used to format the following training data (this line comes from `gs://cloud-samples-data/vertex-ai/model-evaluation/peft_train_sample.jsonl`):\n",
- "\n",
- "# @markdown ```\n",
- "# @markdown {\"input_text\":\"TRANSCRIPT: \\nREASON FOR EVALUATION:,\\n\\n LABEL:\",\"output_text\":\"Chiropractic\"}\n",
- "# @markdown ```\n",
- "\n",
- "# @markdown This example template simply concatenates `input_text` with `output_text`. You can set `template` to `vertex_sample` to try out this built-in template with the dataset `gs://cloud-samples-data/vertex-ai/model-evaluation/peft_train_sample.jsonl`, or build more complicated JSON templates such as [the alpaca example](https://github.com/tloen/alpaca-lora/blob/main/templates/alpaca.json). To use your own JSON template, [upload it to Google Cloud Storage](https://cloud.google.com/storage/docs/uploading-objects) and put the `gs://` URI in the `template` field below. Leave `instruct_column_in_dataset` as `text`.\n",
- "\n",
- "# Hugging Face dataset name or gs:// URI to a custom JSONL dataset.\n",
- "dataset_name = \"timdettmers/openassistant-guanaco\" # @param {type:\"string\"}\n",
- "\n",
- "# Name of the dataset column containing training text input.\n",
- "instruct_column_in_dataset = \"text\" # @param {type:\"string\"}\n",
- "\n",
- "# Optional. Template name or gs:// URI to a custom template.\n",
- "template = \"\" # @param {type:\"string\"}"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "e1289e21a9d3"
- },
- "outputs": [],
- "source": [
- "# @title Finetune with PEFT\n",
- "\n",
- "# @markdown This section demonstrates how to finetune the LLaMA 2 models with PEFT LoRA.\n",
- "\n",
- "# @markdown By default, the model will be finetuned for 500 steps on a batch size of 1 to save GPU resources.\n",
- "# @markdown Finetuning `llama2-7b` models is expected to take around 30 minutes.\n",
- "# @markdown To customize finetuning settings and parameters, click \"Show code\" to see more details.\n",
- "\n",
- "# @markdown Set the base model id.\n",
- "base_model_id = \"llama2-7b-hf\" # @param [\"llama2-7b-hf\", \"llama2-7b-chat-hf\", \"llama2-13b-hf\", \"llama2-13b-chat-hf\", \"llama2-70b-hf\", \"llama2-70b-chat-hf\"]\n",
- "model_id = os.path.join(MODEL_BUCKET, base_model_id)\n",
- "\n",
- "# @markdown Set the accelerator type.\n",
- "accelerator_type = \"NVIDIA_TESLA_V100\" # @param [\"NVIDIA_TESLA_V100\", \"NVIDIA_L4\", \"NVIDIA_TESLA_A100\"]\n",
- "\n",
- "# Worker pool spec.\n",
- "# Find Vertex AI supported accelerators and regions in:\n",
- "# https://cloud.google.com/vertex-ai/docs/training/configure-compute\n",
- "machine_type = None\n",
- "if \"7b\" in model_id:\n",
- " if accelerator_type == \"NVIDIA_TESLA_A100\":\n",
- " machine_type = \"a2-highgpu-1g\"\n",
- " accelerator_count = 1\n",
- " elif accelerator_type == \"NVIDIA_TESLA_V100\":\n",
- " machine_type = \"n1-standard-16\"\n",
- " accelerator_count = 2\n",
- " elif accelerator_type == \"NVIDIA_L4\":\n",
- " machine_type = \"g2-standard-8\"\n",
- " accelerator_count = 1\n",
- "elif \"13b\" in model_id:\n",
- " if accelerator_type == \"NVIDIA_TESLA_A100\":\n",
- " machine_type = \"a2-highgpu-1g\"\n",
- " accelerator_count = 1\n",
- " elif accelerator_type == \"NVIDIA_TESLA_V100\":\n",
- " machine_type = \"n1-standard-32\"\n",
- " accelerator_count = 4\n",
- " elif accelerator_type == \"NVIDIA_L4\":\n",
- " machine_type = \"g2-standard-24\"\n",
- " accelerator_count = 2\n",
- "elif \"70b\" in model_id:\n",
- " if accelerator_type == \"NVIDIA_TESLA_A100\":\n",
- " machine_type = \"a2-highgpu-4g\"\n",
- " accelerator_count = 4\n",
- " elif accelerator_type == \"NVIDIA_L4\":\n",
- " machine_type = \"g2-standard-96\"\n",
- " accelerator_count = 8\n",
- "\n",
- "if machine_type is None:\n",
- " raise ValueError(\n",
- " f\"Recommended machine settings not found for: {accelerator_type}. To use another another accelerator, edit this code block to set an appropriate `machine_type`, `accelerator_type`, and `accelerator_count` in worker_pool_specs.\"\n",
- " )\n",
- "\n",
- "common_util.check_quota(\n",
- " project_id=PROJECT_ID,\n",
- " region=REGION,\n",
- " accelerator_type=accelerator_type,\n",
- " accelerator_count=accelerator_count,\n",
- " is_for_training=True,\n",
- ")\n",
- "\n",
- "job_name = get_job_name_with_datetime(\"llama2-train\")\n",
- "output_dir = os.path.join(EXPERIMENT_BUCKET, job_name)\n",
- "merge_job_name = get_job_name_with_datetime(\"llama2-merge\")\n",
- "merged_model_output_dir = os.path.join(EXPERIMENT_BUCKET, merge_job_name)\n",
- "finetune_precision_mode = \"float16\"\n",
- "\n",
- "replica_count = 1\n",
- "\n",
- "# Runs 500 training steps.\n",
- "max_steps = 500 # @param {type: \"integer\"}\n",
- "per_device_train_batch_size = 1\n",
- "# LoRA parameters.\n",
- "lora_rank = 16 # @param {type: \"integer\"}\n",
- "lora_alpha = 32\n",
- "lora_dropout = 0.05\n",
- "\n",
- "flags = {\n",
- " \"learning_rate\": 2e-4,\n",
- " \"precision_mode\": finetune_precision_mode,\n",
- " \"task\": \"instruct-lora\",\n",
- " \"per_device_train_batch_size\": per_device_train_batch_size,\n",
- " \"dataset_name\": dataset_name,\n",
- " \"instruct_column_in_dataset\": instruct_column_in_dataset,\n",
- " \"template\": template,\n",
- " \"pretrained_model_id\": model_id,\n",
- " \"output_dir\": output_dir,\n",
- " \"merge_base_and_lora_output_dir\": merged_model_output_dir,\n",
- " \"warmup_steps\": 10,\n",
- " \"max_steps\": max_steps,\n",
- " \"lora_rank\": lora_rank,\n",
- " \"lora_alpha\": lora_alpha,\n",
- " \"lora_dropout\": lora_dropout,\n",
- "}\n",
- "\n",
- "\n",
- "# Add labels for the finetuning job.\n",
- "labels = {\n",
- " \"mg-source\": \"notebook\",\n",
- " \"mg-notebook-name\": \"model_garden_pytorch_llama2_peft_finetuning.ipynb\".split(\".\")[\n",
- " 0\n",
- " ],\n",
- "}\n",
- "\n",
- "labels[\"mg-tune\"] = \"publishers-meta-models-llama2\"\n",
- "versioned_model_id = base_model_id\n",
- "labels[\"versioned-mg-tune\"] = f\"{labels['mg-tune']}-{versioned_model_id}\"\n",
- "\n",
- "train_job = aiplatform.CustomJob(\n",
- " display_name=job_name,\n",
- " worker_pool_specs=[\n",
- " {\n",
- " \"machine_spec\": {\n",
- " \"machine_type\": machine_type,\n",
- " \"accelerator_type\": accelerator_type,\n",
- " \"accelerator_count\": accelerator_count,\n",
- " },\n",
- " \"replica_count\": replica_count,\n",
- " \"container_spec\": {\n",
- " \"image_uri\": TRAIN_DOCKER_URI,\n",
- " \"args\": [\"--{}={}\".format(k, v) for k, v in flags.items()],\n",
- " },\n",
- " }\n",
- " ],\n",
- " staging_bucket=STAGING_BUCKET,\n",
- " labels=labels,\n",
- ")\n",
- "train_job.run()\n",
- "\n",
- "print(\"The finetuned models of different trials can be found at: \", output_dir)\n",
- "print(\n",
- " \"The finetuned model merged with the base model can be found at: \",\n",
- " merged_model_output_dir,\n",
- ")"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "h0hGj09CuRFQ"
- },
- "outputs": [],
- "source": [
- "# @title Deploy\n",
- "# @markdown This section uploads the model to Model Registry and deploys it on the Endpoint. It takes 15 minutes to 1 hour to finish.\n",
- "# @markdown Click \"Show code\" to see more details.\n",
- "\n",
- "print(\"Deploying models in: \", merged_model_output_dir)\n",
- "\n",
- "# The max_model_len must not exceed the model's context length.\n",
- "# A larger max_model_len will require more GPU memory.\n",
- "max_model_len = 2048\n",
- "# Worker pool spec.\n",
- "# Find Vertex AI supported accelerators and regions in:\n",
- "# https://cloud.google.com/vertex-ai/docs/training/configure-compute\n",
- "machine_type = None\n",
- "accelerator_type = \"NVIDIA_L4\" # @param [\"NVIDIA_L4\", \"NVIDIA_TESLA_V100\", \"NVIDIA_TESLA_A100\", \"NVIDIA_H100_80GB\"]\n",
- "\n",
- "if \"7b\" in model_id:\n",
- " if accelerator_type == \"NVIDIA_TESLA_A100\":\n",
- " machine_type = \"a2-highgpu-1g\"\n",
- " accelerator_count = 1\n",
- " elif accelerator_type == \"NVIDIA_TESLA_V100\":\n",
- " machine_type = \"n1-standard-8\"\n",
- " accelerator_count = 1\n",
- " elif accelerator_type == \"NVIDIA_L4\":\n",
- " machine_type = \"g2-standard-8\"\n",
- " accelerator_count = 1\n",
- "elif \"13b\" in model_id:\n",
- " if accelerator_type == \"NVIDIA_TESLA_A100\":\n",
- " machine_type = \"a2-highgpu-1g\"\n",
- " accelerator_count = 1\n",
- " elif accelerator_type == \"NVIDIA_TESLA_V100\":\n",
- " machine_type = \"n1-standard-16\"\n",
- " accelerator_count = 2\n",
- " elif accelerator_type == \"NVIDIA_L4\":\n",
- " machine_type = \"g2-standard-24\"\n",
- " accelerator_count = 2\n",
- "elif \"70b\" in model_id:\n",
- " if accelerator_type == \"NVIDIA_TESLA_A100\":\n",
- " machine_type = \"a2-highgpu-4g\"\n",
- " accelerator_count = 4\n",
- " elif accelerator_type == \"NVIDIA_L4\":\n",
- " machine_type = \"g2-standard-96\"\n",
- " accelerator_count = 8\n",
- " elif accelerator_type == \"NVIDIA_H100_80GB\":\n",
- " machine_type = \"a3-highgpu-4g\"\n",
- " accelerator_count = 4\n",
- "\n",
- "if machine_type is None:\n",
- " raise ValueError(\n",
- " f\"Recommended machine settings not found for: {accelerator_type}. To use another another accelerator, edit this code block to pass in an appropriate `machine_type`, `accelerator_type`, and `accelerator_count` to the deploy_model_vllm function.\"\n",
- " )\n",
- "\n",
- "model, endpoint = deploy_model_vllm(\n",
- " model_name=get_job_name_with_datetime(prefix=\"llama-vllm-serve\"),\n",
- " model_id=merged_model_output_dir,\n",
- " service_account=SERVICE_ACCOUNT,\n",
- " machine_type=machine_type,\n",
- " accelerator_type=accelerator_type,\n",
- " accelerator_count=accelerator_count,\n",
- " max_model_len=max_model_len,\n",
- ")\n",
- "print(\"endpoint_name:\", endpoint.name)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "vgU_qYHNuy3w"
- },
- "outputs": [],
- "source": [
- "# @title Predict\n",
- "# @markdown Once deployment succeeds, you can send requests to the endpoint with text prompts.\n",
- "\n",
- "# @markdown Here we use an example from the [timdettmers/openassistant-guanaco](https://huggingface.co/datasets/timdettmers/openassistant-guanaco) to show the finetuning outcome:\n",
- "\n",
- "# @markdown ```\n",
- "# @markdown ### Human: How would the Future of AI in 10 Years look?### Assistant: Predicting the future is always a challenging task, but here are some possible ways that AI could evolve over the next 10 years: Continued advancements in deep learning: Deep learning has been one of the main drivers of recent AI breakthroughs, and we can expect continued advancements in this area. This may include improvements to existing algorithms, as well as the development of new architectures that are better suited to specific types of data and tasks. Increased use of AI in healthcare: AI has the potential to revolutionize healthcare, by improving the accuracy of diagnoses, developing new treatments, and personalizing patient care. We can expect to see continued investment in this area, with more healthcare providers and researchers using AI to improve patient outcomes. Greater automation in the workplace: Automation is already transforming many industries, and AI is likely to play an increasingly important role in this process. We can expect to see more jobs being automated, as well as the development of new types of jobs that require a combination of human and machine skills. More natural and intuitive interactions with technology: As AI becomes more advanced, we can expect to see more natural and intuitive ways of interacting with technology. This may include voice and gesture recognition, as well as more sophisticated chatbots and virtual assistants. Increased focus on ethical considerations: As AI becomes more powerful, there will be a growing need to consider its ethical implications. This may include issues such as bias in AI algorithms, the impact of automation on employment, and the use of AI in surveillance and policing. Overall, the future of AI in 10 years is likely to be shaped by a combination of technological advancements, societal changes, and ethical considerations. While there are many exciting possibilities for AI in the future, it will be important to carefully consider its potential impact on society and to work towards ensuring that its benefits are shared fairly and equitably.\n",
- "# @markdown ```\n",
- "\n",
- "# @markdown Additionally, you can moderate the generated text with Vertex AI. See [Moderate text documentation](https://cloud.google.com/natural-language/docs/moderating-text) for more details.\n",
- "\n",
- "# Loads an existing endpoint instance using the endpoint name:\n",
- "# - Using `endpoint_name = endpoint.name` allows us to get the\n",
- "# endpoint name of the endpoint `endpoint` created in the cell\n",
- "# above.\n",
- "# - Alternatively, you can set `endpoint_name = \"1234567890123456789\"` to load\n",
- "# an existing endpoint with the ID 1234567890123456789.\n",
- "# You may uncomment the code below to load an existing endpoint.\n",
- "\n",
- "# endpoint_name = \"\" # @param {type:\"string\"}\n",
- "# aip_endpoint_name = (\n",
- "# f\"projects/{PROJECT_ID}/locations/{REGION}/endpoints/{endpoint_name}\"\n",
- "# )\n",
- "# endpoint = aiplatform.Endpoint(aip_endpoint_name)\n",
- "\n",
- "prompt = \"How would the Future of AI in 10 Years look?\" # @param {type: \"string\"}\n",
- "max_tokens = 128 # @param {type:\"integer\"}\n",
- "temperature = 1.0 # @param {type:\"number\"}\n",
- "top_p = 0.9 # @param {type:\"number\"}\n",
- "top_k = 1 # @param {type:\"integer\"}\n",
- "\n",
- "# Overrides max_tokens and top_k parameters during inferences.\n",
- "# If you encounter the issue like `ServiceUnavailable: 503 Took too long to respond when processing`,\n",
- "# you can reduce the max length, such as set max_tokens as 20.\n",
- "instances = [\n",
- " {\n",
- " \"prompt\": f\"### Human: {prompt}### Assistant: \",\n",
- " \"max_tokens\": max_tokens,\n",
- " \"temperature\": temperature,\n",
- " \"top_p\": top_p,\n",
- " \"top_k\": top_k,\n",
- " },\n",
- "]\n",
- "response = endpoint.predict(instances=instances)\n",
- "\n",
- "for prediction in response.predictions:\n",
- " print(prediction)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "911406c1561e"
- },
- "outputs": [],
- "source": [
- "# @title Clean up resources\n",
- "\n",
- "# @markdown Delete the experiment models and endpoints to recycle the resources\n",
- "# @markdown and avoid unnecessary continuous charges that may incur.\n",
- "\n",
- "if train_job._gca_resource.name:\n",
- " # Training job is submitted.\n",
- " train_job.delete()\n",
- "\n",
- "# Undeploy model and delete endpoint.\n",
- "endpoint.delete(force=True)\n",
- "\n",
- "# Delete model.\n",
- "model.delete()\n",
- "\n",
- "# Delete Cloud Storage objects that were created.\n",
- "delete_bucket = False # @param {type:\"boolean\"}\n",
- "if delete_bucket:\n",
- " ! gsutil -m rm -r $BUCKET_URI"
- ]
- }
- ],
- "metadata": {
- "colab": {
- "name": "model_garden_pytorch_llama2_peft_finetuning.ipynb",
- "toc_visible": true
- },
- "kernelspec": {
- "display_name": "Python 3",
- "name": "python3"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 0
-}
diff --git a/notebooks/community/model_garden/model_garden_pytorch_llama3_1_finetuning.ipynb b/notebooks/community/model_garden/model_garden_pytorch_llama3_1_finetuning.ipynb
deleted file mode 100644
index fd7c87075..000000000
--- a/notebooks/community/model_garden/model_garden_pytorch_llama3_1_finetuning.ipynb
+++ /dev/null
@@ -1,1160 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "7d9bbf86da5e"
- },
- "outputs": [],
- "source": [
- "# Copyright 2026 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": "99c1c3fc2ca5"
- },
- "source": [
- "# Vertex AI Model Garden - Llama 3.1 Finetuning\n",
- "\n",
- "\n",
- " \n",
- " \n",
- "  Run in Colab Enterprise\n",
- " \n",
- " | \n",
- " \n",
- " \n",
- "  View on GitHub\n",
- " \n",
- " | \n",
- "
"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "3de7470326a2"
- },
- "source": [
- "## Overview\n",
- "\n",
- "This notebook demonstrates finetuning and deploying Llama 3.1 models with Vertex AI. All of the examples in this notebook use parameter efficient finetuning methods [PEFT (LoRA)](https://github.com/huggingface/peft) to reduce training and storage costs. LoRA (Low-Rank Adaptation) is one approach of Parameter Efficient FineTuning (PEFT), where pretrained model weights are frozen and rank decomposition matrices representing the change in model weights are trained during finetuning. Read more about LoRA in the following publication: [Hu, E.J., Shen, Y., Wallis, P., Allen-Zhu, Z., Li, Y., Wang, S., Wang, L. and Chen, W., 2021. Lora: Low-rank adaptation of large language models. *arXiv preprint arXiv:2106.09685*](https://arxiv.org/abs/2106.09685).\n",
- "\n",
- "After finetuning, we can deploy models on Vertex with GPU.\n",
- "\n",
- "\n",
- "### Objective\n",
- "\n",
- "- Finetune Llama 3.1 models with Vertex AI Custom Training Jobs.\n",
- "- Evaluate the finetuned model using [lm-evaluation-harness](https://github.com/EleutherAI/lm-evaluation-harness).\n",
- "- Deploy finetuned Llama 3.1 models on Vertex AI Prediction.\n",
- "- Send prediction requests to your finetuned Llama 3.1 models.\n",
- "\n",
- "### File a bug\n",
- "\n",
- "File a bug on [GitHub](https://github.com/GoogleCloudPlatform/vertex-ai-samples/issues/new) if you encounter any issue with the notebook.\n",
- "\n",
- "### Costs\n",
- "\n",
- "This tutorial uses billable components of Google Cloud:\n",
- "\n",
- "* Vertex AI\n",
- "* Cloud Storage\n",
- "\n",
- "Learn about [Vertex AI pricing](https://cloud.google.com/vertex-ai/pricing), [Cloud Storage pricing](https://cloud.google.com/storage/pricing), and use the [Pricing Calculator](https://cloud.google.com/products/calculator/) to generate a cost estimate based on your projected usage."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "264c07757582"
- },
- "source": [
- "## Before you begin"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "855d6b96f291"
- },
- "outputs": [],
- "source": [
- "# @title Install Python Packages for Finetuning\n",
- "\n",
- "# @markdown 1. Install packages to validate dataset with template.\n",
- "! pip install --upgrade --quiet gcsfs==2024.3.1\n",
- "! pip install --upgrade --quiet accelerate==0.34.2\n",
- "! pip install --upgrade --quiet transformers==4.47.1\n",
- "! pip install --upgrade --quiet datasets==2.20.0\n",
- "! pip install --upgrade --quiet google-cloud-aiplatform==1.130.0"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "50273xHFJi5T"
- },
- "outputs": [],
- "source": [
- "# @title Setup Google Cloud project\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. For finetuning, **[click here](https://console.cloud.google.com/iam-admin/quotas?location=us-central1&metric=aiplatform.googleapis.com%2Frestricted_image_training_nvidia_a100_80gb_gpus)** to check if your project already has the required 8 Nvidia A100 80 GB GPUs in the us-central1 region. If yes, then run this notebook in the us-central1 region. If you do not have 8 Nvidia A100 80 GPUs or have more GPU requirements than this, then schedule your job with Nvidia H100 GPUs via Dynamic Workload Scheduler using [these instructions](https://cloud.google.com/vertex-ai/docs/training/schedule-jobs-dws). For Dynamic Workload Scheduler, check the [us-central1](https://console.cloud.google.com/iam-admin/quotas?location=us-central1&metric=aiplatform.googleapis.com%2Fcustom_model_training_preemptible_nvidia_h100_gpus) or [europe-west4](https://console.cloud.google.com/iam-admin/quotas?location=europe-west4&metric=aiplatform.googleapis.com%2Fcustom_model_training_preemptible_nvidia_h100_gpus) quota for Nvidia H100 GPUs. If you do not have enough GPUs, then you can follow [these instructions](https://cloud.google.com/docs/quotas/view-manage#viewing_your_quota_console) to request quota.\n",
- "\n",
- "# @markdown 3. For serving, **[click here](https://console.cloud.google.com/iam-admin/quotas?location=us-central1&metric=aiplatform.googleapis.com%2Fcustom_model_serving_nvidia_l4_gpus)** to check if your project already has the required 1 L4 GPU in the us-central1 region. If yes, then run this notebook in the us-central1 region. If you need more L4 GPUs for your project, then you can follow [these instructions](https://cloud.google.com/docs/quotas/view-manage#viewing_your_quota_console) to request more. Alternatively, 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).\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",
- "# @markdown 4. **[Optional]** [Create a Cloud Storage bucket](https://cloud.google.com/storage/docs/creating-buckets) for storing experiment outputs. Set the BUCKET_URI for the experiment environment. The specified Cloud Storage bucket (`BUCKET_URI`) should be located in the same region as where the notebook was launched. Note that a multi-region bucket (eg. \"us\") is not considered a match for a single region covered by the multi-region range (eg. \"us-central1\"). If not set, a unique GCS bucket will be created instead.\n",
- "\n",
- "BUCKET_URI = \"gs://\" # @param {type:\"string\"}\n",
- "\n",
- "# @markdown 5. **[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",
- "# Import the necessary packages.\n",
- "! rm -rf vertex-ai-samples && git clone https://github.com/GoogleCloudPlatform/vertex-ai-samples.git\n",
- "! cd vertex-ai-samples && git reset --hard 7ae13b346a72ee2a2dc8152dd40c6ddd72d6c810\n",
- "\n",
- "import datetime\n",
- "import importlib\n",
- "import os\n",
- "import uuid\n",
- "from typing import Tuple\n",
- "\n",
- "from google.cloud import aiplatform\n",
- "from google.cloud.aiplatform.compat.types import \\\n",
- " custom_job as gca_custom_job_compat\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",
- "models, endpoints = {}, {}\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",
- " if not os.environ.get(\"GOOGLE_CLOUD_REGION\"):\n",
- " raise ValueError(\n",
- " \"REGION must be set. See\"\n",
- " \" https://cloud.google.com/vertex-ai/docs/general/locations for\"\n",
- " \" available cloud locations.\"\n",
- " )\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",
- "# Cloud Storage bucket for storing the experiment artifacts.\n",
- "# A unique GCS bucket will be created for the purpose of this notebook. If you\n",
- "# prefer using your own GCS bucket, change the value yourself below.\n",
- "now = datetime.datetime.now().strftime(\"%Y%m%d%H%M%S\")\n",
- "BUCKET_NAME = \"/\".join(BUCKET_URI.split(\"/\")[:3])\n",
- "\n",
- "if BUCKET_URI is None or BUCKET_URI.strip() == \"\" or BUCKET_URI == \"gs://\":\n",
- " BUCKET_URI = f\"gs://{PROJECT_ID}-tmp-{now}-{str(uuid.uuid4())[:4]}\"\n",
- " BUCKET_NAME = \"/\".join(BUCKET_URI.split(\"/\")[:3])\n",
- " ! gsutil mb -l {REGION} {BUCKET_URI}\n",
- "else:\n",
- " assert BUCKET_URI.startswith(\"gs://\"), \"BUCKET_URI must start with `gs://`.\"\n",
- " shell_output = ! gsutil ls -Lb {BUCKET_NAME} | grep \"Location constraint:\" | sed \"s/Location constraint://\"\n",
- " bucket_region = shell_output[0].strip().lower()\n",
- " if bucket_region != REGION:\n",
- " raise ValueError(\n",
- " \"Bucket region %s is different from notebook region %s\"\n",
- " % (bucket_region, REGION)\n",
- " )\n",
- "print(f\"Using this GCS Bucket: {BUCKET_URI}\")\n",
- "\n",
- "STAGING_BUCKET = os.path.join(BUCKET_URI, \"temporal\")\n",
- "MODEL_BUCKET = os.path.join(BUCKET_URI, \"llama3_1\")\n",
- "\n",
- "\n",
- "# Initialize Vertex AI API.\n",
- "print(\"Initializing Vertex AI API.\")\n",
- "aiplatform.init(project=PROJECT_ID, location=REGION, staging_bucket=STAGING_BUCKET)\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",
- "\n",
- "# Provision permissions to the SERVICE_ACCOUNT with the GCS bucket\n",
- "! gsutil iam ch serviceAccount:{SERVICE_ACCOUNT}:roles/storage.admin $BUCKET_NAME\n",
- "\n",
- "! gcloud config set project $PROJECT_ID\n",
- "! gcloud projects add-iam-policy-binding --no-user-output-enabled {PROJECT_ID} --member=serviceAccount:{SERVICE_ACCOUNT} --role=\"roles/storage.admin\"\n",
- "! gcloud projects add-iam-policy-binding --no-user-output-enabled {PROJECT_ID} --member=serviceAccount:{SERVICE_ACCOUNT} --role=\"roles/aiplatform.user\""
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "36c21f10355f"
- },
- "outputs": [],
- "source": [
- "# @title Access Llama 3.1 models\n",
- "\n",
- "# @markdown For GPU based finetuning and serving, choose between accessing Llama 3.1 models on [Hugging Face](https://huggingface.co/)\n",
- "# @markdown or Vertex AI as described below.\n",
- "\n",
- "# @markdown If you already obtained access to Llama 3.1 models on [Hugging Face](https://huggingface.co/), you can load models from there.\n",
- "# @markdown Alternatively, you can also load the original Llama 3.1 models for finetuning and serving from Vertex AI after accepting the agreement.\n",
- "\n",
- "# @markdown **Only select and fill one of the following sections.**\n",
- "# It is recommended to use \"Google Cloud\" for 405B model since it can be downloaded faster.\n",
- "LOAD_MODEL_FROM = \"Google Cloud\" # @param [\"Hugging Face\", \"Google Cloud\"] {isTemplate:true}\n",
- "\n",
- "# @markdown ---\n",
- "\n",
- "# @markdown ### Access Llama 3.1 models on Hugging Face for GPU based finetuning and serving\n",
- "# @markdown You must provide a Hugging Face User Access Token (with read access) to access the Llama 3.1 models. You can follow the [Hugging Face documentation](https://huggingface.co/docs/hub/en/security-tokens) to create a **read** access token and put it in the `HF_TOKEN` field below.\n",
- "\n",
- "HF_TOKEN = \"\" # @param {type:\"string\", isTemplate:true}\n",
- "# @markdown *--- Or ---*\n",
- "# @markdown ### Access Llama 3.1 models on Vertex AI for GPU based serving\n",
- "# @markdown The original models from Meta are converted into the Hugging Face format for serving in Vertex AI.\n",
- "# @markdown Accept the model agreement to access the models:\n",
- "# @markdown 1. Open the [Llama 3.1 model card](https://console.cloud.google.com/vertex-ai/publishers/meta/model-garden/llama3_1) from [Vertex AI Model Garden](https://cloud.google.com/model-garden).\n",
- "# @markdown 2. Review and accept the agreement in the pop-up window on the model card page. If you have previously accepted the model agreement, there will not be a pop-up window on the model card page and this step is not needed.\n",
- "# @markdown 3. After accepting the agreement of Llama 3.1, a `gs://` URI containing Llama 3.1 pretrained and finetuned models will be shared.\n",
- "# @markdown 4. Paste the URI in the `VERTEX_AI_MODEL_GARDEN_LLAMA3_1` field below.\n",
- "\n",
- "VERTEX_AI_MODEL_GARDEN_LLAMA3_1 = \"\" # @param {type:\"string\", isTemplate:true}\n",
- "if LOAD_MODEL_FROM == \"Hugging Face\":\n",
- " assert (\n",
- " HF_TOKEN\n",
- " ), \"Provide a read HF_TOKEN to load models from Hugging Face, or select a different model source.\"\n",
- "else:\n",
- " assert (\n",
- " VERTEX_AI_MODEL_GARDEN_LLAMA3_1\n",
- " ), \"Click the agreement of Llama3.1 in Vertex AI Model Garden, and get the GCS path of the model artifacts.\"\n",
- "\n",
- "MODEL_BUCKET = VERTEX_AI_MODEL_GARDEN_LLAMA3_1"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "cb56d402e84a"
- },
- "source": [
- "## Finetune with HuggingFace PEFT and deploy with vLLM on GPUs"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "KwAW99YZHTdy"
- },
- "outputs": [],
- "source": [
- "# @title Set dataset\n",
- "\n",
- "# @markdown Use the Vertex AI SDK to create and run the custom training jobs.\n",
- "\n",
- "# @markdown This notebook uses [timdettmers/openassistant-guanaco](https://huggingface.co/datasets/timdettmers/openassistant-guanaco) dataset as an example.\n",
- "# @markdown You can set `train_dataset` to any existing [Hugging Face dataset](https://huggingface.co/datasets) name, and set `train_column` to the name of the dataset column containing training data. The [timdettmers/openassistant-guanaco](https://huggingface.co/datasets/timdettmers/openassistant-guanaco) has only one column `text`, and therefore we set `train_column` to `text` in this notebook.\n",
- "\n",
- "# @markdown ### (Optional) Prepare a custom JSONL dataset for finetuning\n",
- "\n",
- "# @markdown You can prepare a JSONL file where each line is a valid JSON string as your custom training dataset. For example, here is one line from the [timdettmers/openassistant-guanaco](https://huggingface.co/datasets/timdettmers/openassistant-guanaco) dataset:\n",
- "# @markdown ```\n",
- "# @markdown {\"text\": \"### Human: Hola### Assistant: \\u00a1Hola! \\u00bfEn qu\\u00e9 puedo ayudarte hoy?\"}\n",
- "# @markdown ```\n",
- "\n",
- "# @markdown The JSON object has a key `text`, which should match `train_column`; The value should be one training data point, i.e. a string. After you prepared your JSONL file, you can either upload it to [Hugging Face datasets](https://huggingface.co/datasets) or [Google Cloud Storage](https://cloud.google.com/storage).\n",
- "\n",
- "# @markdown - To upload a JSONL dataset to [Hugging Face datasets](https://huggingface.co/datasets), follow the instructions on [Uploading Datasets](https://huggingface.co/docs/hub/en/datasets-adding). Then, set `dataset_name` to the name of your newly created dataset on Hugging Face.\n",
- "\n",
- "# @markdown - To upload a JSONL dataset to [Google Cloud Storage](https://cloud.google.com/storage), follow the instructions on [Upload objects from a filesystem](https://cloud.google.com/storage/docs/uploading-objects). Then, set `dataset_name` to the `gs://` URI to your JSONL file. For example: `gs://cloud-samples-data/vertex-ai/model-evaluation/peft_train_sample.jsonl`.\n",
- "\n",
- "# @markdown Optionally update the `train_column` field below if your JSON objects use a key other than the default `text`.\n",
- "\n",
- "# @markdown ### (Optional) Format your data with custom JSON template\n",
- "\n",
- "# @markdown Sometimes, your dataset might have multiple text columns and you want to construct the training data with a template. You can prepare a JSON template in the following format:\n",
- "\n",
- "# @markdown ```\n",
- "# @markdown {\n",
- "# @markdown \"description\": \"Template used by Llama 3.1, accepting text-bison format.\",\n",
- "# @markdown \"source\": \"https://cloud.google.com/vertex-ai/generative-ai/docs/models/tune-text-models-supervised#dataset-format\",\n",
- "# @markdown \"prompt_input\": \"<|start_header_id|>user<|end_header_id|>\\n\\n{input_text}<|eot_id|><|start_header_id|>assistant<|end_header_id|>\\n\\n{output_text}<|eot_id|>\",\n",
- "# @markdown \"instruction_separator\": \"<|start_header_id|>user<|end_header_id|>\\n\\n\",\n",
- "# @markdown \"response_separator\": \"<|start_header_id|>assistant<|end_header_id|>\\n\\n\"\n",
- "# @markdown }\n",
- "# @markdown ```\n",
- "\n",
- "# @markdown As an example, the template above can be used to format the following training data (this line comes from `gs://cloud-samples-data/vertex-ai/model-evaluation/peft_train_sample.jsonl`):\n",
- "\n",
- "# @markdown ```\n",
- "# @markdown {\"input_text\":\"TRANSCRIPT: \\nREASON FOR EVALUATION:,\\n\\n LABEL:\",\"output_text\":\"Chiropractic\"}\n",
- "# @markdown ```\n",
- "\n",
- "# @markdown This example template simply concatenates `input_text` with `output_text` with some special tokens in between.\n",
- "# @markdown\n",
- "# @markdown To try such custom dataset, you can make the following changes:\n",
- "# @markdown 1. Set `template` to `llama3-text-bison`\n",
- "# @markdown 1. Set `train_dataset` to `gs://cloud-samples-data/vertex-ai/model-evaluation/peft_train_sample.jsonl`\n",
- "# @markdown 1. Set `train_split` to `train`\n",
- "# @markdown 1. Set `eval_dataset` to `gs://cloud-samples-data/vertex-ai/model-evaluation/peft_eval_sample.jsonl`\n",
- "# @markdown 1. Set `eval_split` to `train` (**NOT** `test`)\n",
- "# @markdown 1. Set `train_column` as `input_text`.\n",
- "\n",
- "# Template name or gs:// URI to a custom template.\n",
- "template = \"openassistant-guanaco\" # @param {type:\"string\"}\n",
- "\n",
- "# Hugging Face dataset name or gs:// URI to a custom JSONL dataset.\n",
- "train_dataset = \"timdettmers/openassistant-guanaco\" # @param {type:\"string\"}\n",
- "train_split = \"train\" # @param {type:\"string\"}\n",
- "eval_dataset = \"timdettmers/openassistant-guanaco\" # @param {type:\"string\"}\n",
- "eval_split = \"test\" # @param {type:\"string\"}\n",
- "\n",
- "# Name of the dataset column containing training text input.\n",
- "train_column = \"text\" # @param {type:\"string\"}\n",
- "# Maximum sequence length.\n",
- "max_seq_length = 4096 # @param{type:\"integer\"}"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "iu1YAu8315sG"
- },
- "outputs": [],
- "source": [
- "# @title Set model\n",
- "\n",
- "# @markdown Select a model variant of Llama 3.1.\n",
- "base_model_id = \"meta-llama/Meta-Llama-3.1-8B-Instruct\" # @param [\"meta-llama/Meta-Llama-3.1-8B\", \"meta-llama/Meta-Llama-3.1-8B-Instruct\", \"meta-llama/Meta-Llama-3.1-70B\", \"meta-llama/Meta-Llama-3.1-70B-Instruct\", \"meta-llama/Meta-Llama-3.1-405B\", \"meta-llama/Meta-Llama-3.1-405B-Instruct\", \"deepseek-ai/DeepSeek-R1-Distill-Llama-70B\"] {isTemplate:true}\n",
- "if LOAD_MODEL_FROM == \"Google Cloud\":\n",
- " pretrained_model_id = os.path.join(MODEL_BUCKET, base_model_id.split(\"/\")[-1])\n",
- "else:\n",
- " pretrained_model_id = base_model_id"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "_mNcaofv4zpv"
- },
- "outputs": [],
- "source": [
- "# @title Validate Dataset with Template\n",
- "\n",
- "# @markdown This section validates the train and eval datasets with the template before starting the fine tuning process.\n",
- "\n",
- "dataset_validation_util = importlib.import_module(\n",
- " \"vertex-ai-samples.community-content.vertex_model_garden.model_oss.notebook_util.dataset_validation_util\"\n",
- ")\n",
- "\n",
- "if dataset_validation_util.is_gcs_path(pretrained_model_id):\n",
- " # Download tokenizer.\n",
- " ! mkdir tokenizer\n",
- " ! gsutil cp {pretrained_model_id}/tokenizer.json ./tokenizer\n",
- " ! gsutil cp {pretrained_model_id}/config.json ./tokenizer\n",
- " tokenizer_path = \"./tokenizer\"\n",
- " access_token = \"\"\n",
- "else:\n",
- " tokenizer_path = pretrained_model_id\n",
- " access_token = HF_TOKEN\n",
- "\n",
- "tokenizer = dataset_validation_util.load_tokenizer(tokenizer_path, None, access_token)\n",
- "\n",
- "# Validate the train dataset.\n",
- "dataset_validation_util.validate_dataset_with_template(\n",
- " dataset_name=train_dataset,\n",
- " split=train_split,\n",
- " input_column=train_column,\n",
- " template=template,\n",
- " max_seq_length=max_seq_length,\n",
- " use_multiprocessing=False,\n",
- " tokenizer=tokenizer,\n",
- ")\n",
- "\n",
- "# Validate the eval dataset if it exists.\n",
- "if eval_dataset:\n",
- " dataset_validation_util.validate_dataset_with_template(\n",
- " dataset_name=eval_dataset,\n",
- " split=eval_split,\n",
- " input_column=train_column,\n",
- " template=template,\n",
- " max_seq_length=max_seq_length,\n",
- " use_multiprocessing=False,\n",
- " tokenizer=tokenizer,\n",
- " )"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "ivVGS9dHXPOz"
- },
- "outputs": [],
- "source": [
- "# @title Finetune\n",
- "\n",
- "# @markdown This section demonstrates how to finetune the Llama 3.1 model on Vertex AI. It uses the Vertex AI SDK to create and run the custom training jobs.\n",
- "\n",
- "# @markdown The training job takes approximately between 10 to 20 mins to set-up. Once done, the training job is expected to take around 75 mins with the default configurations. To find the training time, throughput, and memory usage of your training job, you can go to the training logs and check the log line of the last training epoch.\n",
- "\n",
- "# @markdown **Note**:\n",
- "# @markdown 1. We recommend setting `finetuning_precision_mode` to `4bit` because it enables using fewer hardware resources for finetuning.\n",
- "# @markdown 1. If `max_steps > 0`, it takes precedence over `epochs`. One can set a small `max_steps` value to quickly check the pipeline.\n",
- "\n",
- "# @markdown Acceletor type to use for training.\n",
- "training_accelerator_type = \"NVIDIA_A100_80GB\" # @param [\"NVIDIA_A100_80GB\", \"NVIDIA_H100_80GB\"]\n",
- "\n",
- "# @markdown Set the Training Region. If not set, it will be set to default region.\n",
- "TRAINING_REGION = \"\" # @param {type: \"string\"}\n",
- "if not TRAINING_REGION:\n",
- " TRAINING_REGION = REGION\n",
- "\n",
- "aiplatform.init(location=TRAINING_REGION)\n",
- "\n",
- "# The pre-built training docker image.\n",
- "if training_accelerator_type == \"NVIDIA_A100_80GB\":\n",
- " repo = \"us-docker.pkg.dev/vertex-ai-restricted\"\n",
- " is_restricted_image = True\n",
- " is_dynamic_workload_scheduler = False\n",
- " dws_kwargs = {}\n",
- " if \"405b\" in base_model_id.lower():\n",
- " raise ValueError(\n",
- " \"405B model is not supported with Nvidia A100 GPUs. Use Nvidia H100 GPUs instead.\"\n",
- " )\n",
- "else:\n",
- " repo = \"us-docker.pkg.dev/vertex-ai\"\n",
- " is_restricted_image = False\n",
- " is_dynamic_workload_scheduler = True\n",
- " dws_kwargs = {\n",
- " \"max_wait_duration\": 5400, # 90 minutes\n",
- " \"scheduling_strategy\": gca_custom_job_compat.Scheduling.Strategy.FLEX_START,\n",
- " }\n",
- "\n",
- "TRAIN_DOCKER_URI = (\n",
- " f\"{repo}/vertex-vision-model-garden-dockers/pytorch-peft-train:stable_20250705\"\n",
- ")\n",
- "\n",
- "# Worker pool spec.\n",
- "boot_disk_size_gb = 500\n",
- "if training_accelerator_type == \"NVIDIA_A100_80GB\":\n",
- " per_node_accelerator_count = 8\n",
- " training_machine_type = \"a2-ultragpu-8g\"\n",
- "elif training_accelerator_type == \"NVIDIA_H100_80GB\":\n",
- " per_node_accelerator_count = 8\n",
- " training_machine_type = \"a3-highgpu-8g\"\n",
- " if \"405b\" in base_model_id.lower():\n",
- " boot_disk_size_gb = 2000\n",
- "else:\n",
- " raise ValueError(\n",
- " f\"Recommended machine settings not found for: {training_accelerator_type}. To use another accelerator type, edit this code block to pass in an appropriate `training_machine_type`, `training_accelerator_type`, and `per_node_accelerator_count` by clicking `Show Code` and then modifying the code.\"\n",
- " )\n",
- "\n",
- "# @markdown The number of nodes to use for this worker pool in distributed training.\n",
- "replica_count = 1 # @param{type:\"integer\"}\n",
- "\n",
- "# Set config file.\n",
- "if replica_count == 1:\n",
- " config_file = \"vertex_vision_model_garden_peft/llama_fsdp_8gpu.yaml\"\n",
- "elif replica_count <= 4:\n",
- " config_file = (\n",
- " \"vertex_vision_model_garden_peft/\"\n",
- " f\"llama_hsdp_{replica_count * per_node_accelerator_count}gpu.yaml\"\n",
- " )\n",
- "else:\n",
- " raise ValueError(\n",
- " f\"Recommended config settings not found for replica_count: {replica_count}.\"\n",
- " )\n",
- "\n",
- "# @markdown Batch size for finetuning.\n",
- "per_device_train_batch_size = 1 # @param{type:\"integer\"}\n",
- "# @markdown Number of updates steps to accumulate the gradients for, before performing a backward/update pass.\n",
- "gradient_accumulation_steps = 4 # @param{type:\"integer\"}\n",
- "# @markdown Setting a positive `max_steps` here will override `num_train_epochs`.\n",
- "max_steps = -1 # @param{type:\"integer\"}\n",
- "num_train_epochs = 1.0 # @param{type:\"number\"}\n",
- "# @markdown Precision mode for finetuning.\n",
- "finetuning_precision_mode = \"4bit\" # @param [\"4bit\", \"8bit\", \"float16\"]\n",
- "# @markdown Learning rate.\n",
- "learning_rate = 5e-5 # @param{type:\"number\"}\n",
- "# @markdown The scheduler type to use.\n",
- "lr_scheduler_type = \"cosine\" # @param{type:\"string\"}\n",
- "# @markdown LoRA parameters.\n",
- "lora_rank = 16 # @param{type:\"integer\"}\n",
- "lora_alpha = 32 # @param{type:\"integer\"}\n",
- "lora_dropout = 0.05 # @param{type:\"number\"}\n",
- "# Activates gradient checkpointing for the current model (may be referred to as activation checkpointing or checkpoint activations in other frameworks).\n",
- "gradient_checkpointing = True\n",
- "# Attention implementation to use in the model.\n",
- "attn_implementation = \"flash_attention_2\"\n",
- "# The optimizer for which to schedule the learning rate.\n",
- "optimizer = \"adamw_torch\"\n",
- "# Define the proportion of training to be dedicated to a linear warmup where learning rate gradually increases.\n",
- "warmup_ratio = \"0.01\"\n",
- "# The list or string of integrations to report the results and logs to.\n",
- "report_to = \"tensorboard\"\n",
- "# Number of updates steps before two checkpoint saves.\n",
- "save_steps = 10\n",
- "# Number of update steps between two logs.\n",
- "logging_steps = save_steps\n",
- "\n",
- "# @markdown Evaluation metrics to compute. Supported eval metrics: loss, perplexity, bleu, google_bleu, rouge1, rouge2, rougeL, rougeLsum.\n",
- "eval_metric_name = \"loss,perplexity,bleu\" # @param{type:\"string\"}\n",
- "# @markdown Metric to use for best model selection. This will save the best checkpoint based on the eval metric.\n",
- "metric_for_best_model = \"perplexity\" # @param{type:\"string\"}\n",
- "\n",
- "common_util.check_quota(\n",
- " project_id=PROJECT_ID,\n",
- " region=TRAINING_REGION,\n",
- " accelerator_type=training_accelerator_type,\n",
- " accelerator_count=per_node_accelerator_count * replica_count,\n",
- " is_for_training=True,\n",
- " is_restricted_image=is_restricted_image,\n",
- " is_dynamic_workload_scheduler=is_dynamic_workload_scheduler,\n",
- ")\n",
- "\n",
- "job_name = common_util.get_job_name_with_datetime(\"llama3_1-lora-train\")\n",
- "\n",
- "base_output_dir = os.path.join(STAGING_BUCKET, job_name)\n",
- "# Create a GCS folder to store the LORA adapter.\n",
- "lora_output_dir = os.path.join(base_output_dir, \"adapter\")\n",
- "# Create a GCS folder to store the finetuned LORA adapter.\n",
- "final_checkpoint = os.path.join(lora_output_dir, \"node-0\", \"checkpoint-final\")\n",
- "\n",
- "# Add labels for the finetuning job.\n",
- "labels = {\n",
- " \"mg-source\": \"notebook\",\n",
- " \"mg-notebook-name\": \"model_garden_pytorch_llama3_1_finetuning.ipynb\".split(\".\")[0],\n",
- "}\n",
- "\n",
- "labels[\"mg-tune\"] = \"publishers-meta-models-llama3-1\"\n",
- "versioned_model_id = base_model_id.split(\"/\")[1].lower().replace(\".\", \"-\")\n",
- "labels[\"versioned-mg-tune\"] = f\"{labels['mg-tune']}-{versioned_model_id}\"\n",
- "\n",
- "eval_args = [\n",
- " f\"--eval_dataset={eval_dataset}\",\n",
- " f\"--eval_column={train_column}\",\n",
- " f\"--eval_template={template}\",\n",
- " f\"--eval_split={eval_split}\",\n",
- " f\"--eval_steps={save_steps}\",\n",
- " f\"--eval_metric_name={eval_metric_name}\",\n",
- " f\"--metric_for_best_model={metric_for_best_model}\",\n",
- "]\n",
- "\n",
- "train_job_args = [\n",
- " f\"--config_file={config_file}\",\n",
- " \"--task=instruct-lora\",\n",
- " \"--input_masking=True\",\n",
- " f\"--pretrained_model_name_or_path={pretrained_model_id}\",\n",
- " f\"--train_dataset={train_dataset}\",\n",
- " f\"--train_split={train_split}\",\n",
- " f\"--train_column={train_column}\",\n",
- " f\"--output_dir={lora_output_dir}\",\n",
- " f\"--per_device_train_batch_size={per_device_train_batch_size}\",\n",
- " f\"--gradient_accumulation_steps={gradient_accumulation_steps}\",\n",
- " f\"--lora_rank={lora_rank}\",\n",
- " f\"--lora_alpha={lora_alpha}\",\n",
- " f\"--lora_dropout={lora_dropout}\",\n",
- " f\"--max_steps={max_steps}\",\n",
- " f\"--max_seq_length={max_seq_length}\",\n",
- " f\"--learning_rate={learning_rate}\",\n",
- " f\"--lr_scheduler_type={lr_scheduler_type}\",\n",
- " f\"--precision_mode={finetuning_precision_mode}\",\n",
- " f\"--gradient_checkpointing={gradient_checkpointing}\",\n",
- " f\"--num_train_epochs={num_train_epochs}\",\n",
- " f\"--attn_implementation={attn_implementation}\",\n",
- " f\"--optimizer={optimizer}\",\n",
- " f\"--warmup_ratio={warmup_ratio}\",\n",
- " f\"--report_to={report_to}\",\n",
- " f\"--logging_output_dir={base_output_dir}\",\n",
- " f\"--save_steps={save_steps}\",\n",
- " f\"--logging_steps={logging_steps}\",\n",
- " f\"--train_template={template}\",\n",
- " f\"--huggingface_access_token={HF_TOKEN}\",\n",
- "] + eval_args\n",
- "\n",
- "# Pass training arguments and launch job.\n",
- "train_job = aiplatform.CustomContainerTrainingJob(\n",
- " display_name=job_name,\n",
- " container_uri=TRAIN_DOCKER_URI,\n",
- " labels=labels,\n",
- ")\n",
- "\n",
- "print(\"Running training job with args:\")\n",
- "print(\" \\\\\\n\".join(train_job_args))\n",
- "train_job.run(\n",
- " args=train_job_args,\n",
- " replica_count=replica_count,\n",
- " machine_type=training_machine_type,\n",
- " accelerator_type=training_accelerator_type,\n",
- " accelerator_count=per_node_accelerator_count,\n",
- " boot_disk_size_gb=boot_disk_size_gb,\n",
- " service_account=SERVICE_ACCOUNT,\n",
- " base_output_dir=base_output_dir,\n",
- " sync=False, # Non-blocking call to run.\n",
- " **dws_kwargs,\n",
- ")\n",
- "\n",
- "# Wait until resource has been created.\n",
- "train_job.wait_for_resource_creation()\n",
- "\n",
- "print(\"LoRA adapter will be saved in:\", lora_output_dir)\n",
- "print(\"Final checkpoint will be saved in:\", final_checkpoint)\n",
- "\n",
- "# @markdown Click \"Show Code\" to see more details."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "x93f7805YwJg"
- },
- "outputs": [],
- "source": [
- "# @title Run TensorBoard\n",
- "# @markdown This section shows how to launch TensorBoard in a [Cloud Shell](https://cloud.google.com/shell/docs).\n",
- "# @markdown 1. Click the Cloud Shell icon() on the top right to open the Cloud Shell.\n",
- "# @markdown 2. Copy the `tensorboard` command shown below by running this cell.\n",
- "# @markdown 3. Paste and run the command in the Cloud Shell to launch TensorBoard.\n",
- "# @markdown 4. Once the command runs (You may have to click `Authorize` if prompted), click the link starting with `http://localhost`.\n",
- "\n",
- "# @markdown Note: You may need to wait around 10 minutes after the job starts in order for the TensorBoard logs to be written to the GCS bucket.\n",
- "print(f\"Command to copy: tensorboard --logdir {base_output_dir}/logs\")"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "1f-gJ_dldAuQ"
- },
- "outputs": [],
- "source": [
- "# @title Select Evaluation Checkpoint\n",
- "\n",
- "if train_job.end_time is None:\n",
- " print(\"Waiting for the training job to finish...\")\n",
- " train_job.wait()\n",
- " print(\"The training job has finished.\")\n",
- "\n",
- "# @markdown The following checkpoints are available for evaluation:\n",
- "! gcloud storage ls \"{lora_output_dir}/node-0\" | grep \"checkpoint-\""
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "7j5p83ps88N8"
- },
- "outputs": [],
- "source": [
- "# @title Run Evaluation Job\n",
- "# @markdown This section runs the evaluation using [lm-evaluation-harness](https://github.com/EleutherAI/lm-evaluation-harness) on the finetuned model. The evaluation takes approximately 20 mins to finish.\n",
- "\n",
- "# The pre-built evaluation docker image for LM Evaluation Harness.\n",
- "LM_EVAL_DOCKER_URI = \"us-docker.pkg.dev/vertex-ai/vertex-vision-model-garden-dockers/pytorch-lm-evaluation-harness:20250410_1035_RC00\"\n",
- "\n",
- "# @markdown Set `RUN_EVALUATION` to False to skip the evaluation job.\n",
- "RUN_EVALUATION = True # @param {type:\"boolean\"}\n",
- "\n",
- "# @markdown Set the Evaluation Region. If not set, it will be set to default region.\n",
- "EVAL_REGION = \"\" # @param {type: \"string\"}\n",
- "if not EVAL_REGION:\n",
- " EVAL_REGION = REGION\n",
- "\n",
- "aiplatform.init(location=EVAL_REGION)\n",
- "\n",
- "if \"8b\" in base_model_id.lower():\n",
- " eval_machine_type = \"g2-standard-24\"\n",
- " eval_accelerator_type = \"NVIDIA_L4\"\n",
- " eval_accelerator_count = 2\n",
- " dws_kwargs = {\n",
- " \"max_wait_duration\": 10800, # 180 minutes\n",
- " \"scheduling_strategy\": gca_custom_job_compat.Scheduling.Strategy.FLEX_START,\n",
- " }\n",
- " is_dynamic_workload_scheduler = True\n",
- "elif \"70b\" in base_model_id.lower():\n",
- " eval_machine_type = \"a2-ultragpu-4g\"\n",
- " eval_accelerator_type = \"NVIDIA_A100_80GB\"\n",
- " eval_accelerator_count = 4\n",
- " dws_kwargs = {\n",
- " \"max_wait_duration\": 5400, # 90 minutes\n",
- " \"scheduling_strategy\": gca_custom_job_compat.Scheduling.Strategy.FLEX_START,\n",
- " }\n",
- " is_dynamic_workload_scheduler = True\n",
- "elif \"405b\" in base_model_id.lower():\n",
- " print(\n",
- " \"405B model is not supported for evaluation. We will skip the evaluation job.\"\n",
- " )\n",
- " RUN_EVALUATION = False\n",
- "else:\n",
- " raise ValueError(f\"Unsupported model ID or GCS path: {base_model_id}.\")\n",
- "\n",
- "# @markdown Set `evaluation_checkpoint_dir` to an intermediate checkpoint from the above training job. If not set, the evaluation job will use the final checkpoint.\n",
- "evaluation_checkpoint_dir = \"\" # @param {type:\"string\"}\n",
- "if not evaluation_checkpoint_dir:\n",
- " evaluation_checkpoint_dir = final_checkpoint\n",
- "\n",
- "# @markdown Evaluation tasks to run.\n",
- "eval_tasks = \"coqa\" # @param {type:\"string\"}\n",
- "# @markdown Model to use for evaluation.\n",
- "model = \"vllm\" # @param {type:\"string\"}\n",
- "# @markdown Batch size for evaluation.\n",
- "batch_size = \"auto\" # @param {type:\"string\"}\n",
- "apply_chat_template = True if \"-Instruct\" in pretrained_model_id else False\n",
- "gpu_memory_utilization = 0.8\n",
- "max_model_len = 8192 # Maximum context length.\n",
- "eval_output_dir = os.path.join(base_output_dir, \"lm_eval\")\n",
- "\n",
- "if RUN_EVALUATION:\n",
- " model_args = f\"tensor_parallel_size={eval_accelerator_count},max_model_len={max_model_len},gpu_memory_utilization={gpu_memory_utilization},enforce_eager=True\"\n",
- " lm_eval_job_args = [\n",
- " \"--task=lm_eval\",\n",
- " f\"--model={model}\",\n",
- " f\"--eval_tasks={eval_tasks}\",\n",
- " f\"--pretrained_model_name_or_path={pretrained_model_id}\",\n",
- " f\"--model_args={model_args}\",\n",
- " f'--lora_path={evaluation_checkpoint_dir.rstrip(\"/\")}',\n",
- " f\"--output_dir={eval_output_dir}\",\n",
- " f\"--apply_chat_template={apply_chat_template}\",\n",
- " f\"--batch_size={batch_size}\",\n",
- " f\"--huggingface_access_token={HF_TOKEN}\",\n",
- " ]\n",
- " common_util.check_quota(\n",
- " project_id=PROJECT_ID,\n",
- " region=EVAL_REGION,\n",
- " accelerator_type=eval_accelerator_type,\n",
- " accelerator_count=eval_accelerator_count,\n",
- " is_for_training=True,\n",
- " is_dynamic_workload_scheduler=is_dynamic_workload_scheduler,\n",
- " )\n",
- " # Pass evaluation arguments and launch job.\n",
- " lm_eval_job = aiplatform.CustomContainerTrainingJob(\n",
- " display_name=common_util.get_job_name_with_datetime(\"llama3_1-lm-eval\"),\n",
- " container_uri=LM_EVAL_DOCKER_URI,\n",
- " labels=labels,\n",
- " )\n",
- "\n",
- " print(\"Running evaluation job with args:\")\n",
- " print(\" \\\\\\n\".join(lm_eval_job_args))\n",
- " lm_eval_job.run(\n",
- " args=lm_eval_job_args,\n",
- " replica_count=1,\n",
- " machine_type=eval_machine_type,\n",
- " accelerator_type=eval_accelerator_type,\n",
- " accelerator_count=eval_accelerator_count,\n",
- " boot_disk_size_gb=boot_disk_size_gb,\n",
- " service_account=SERVICE_ACCOUNT,\n",
- " base_output_dir=base_output_dir,\n",
- " **dws_kwargs,\n",
- " )\n",
- "\n",
- "# @markdown Click \"Show Code\" to see more details."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "qmHW6m8xG_4U"
- },
- "outputs": [],
- "source": [
- "# @title Deploy\n",
- "# @markdown This section uploads the model to Model Registry and deploys it on the Endpoint. It takes 15 minutes to 1 hour to finish.\n",
- "\n",
- "# The pre-built serving docker image for vLLM.\n",
- "VLLM_DOCKER_URI = \"us-docker.pkg.dev/vertex-ai/vertex-vision-model-garden-dockers/pytorch-vllm-serve:20250116_0916_RC00\"\n",
- "\n",
- "# @markdown Choose whether to use a [Spot VM](https://cloud.google.com/compute/docs/instances/spot) for the deployment.\n",
- "is_spot = False # @param {type:\"boolean\"}\n",
- "\n",
- "# @markdown Set the Deployment Region. If not set, it will be set to default region.\n",
- "DEPLOY_REGION = \"\" # @param {type: \"string\"}\n",
- "if not DEPLOY_REGION:\n",
- " DEPLOY_REGION = REGION\n",
- "\n",
- "aiplatform.init(location=DEPLOY_REGION)\n",
- "\n",
- "# Find Vertex AI prediction supported accelerators and regions [here](https://cloud.google.com/vertex-ai/docs/predictions/configure-compute).\n",
- "if \"8b\" in base_model_id.lower():\n",
- " machine_type = \"g2-standard-12\"\n",
- " accelerator_type = \"NVIDIA_L4\"\n",
- " per_node_accelerator_count = 1\n",
- "elif \"70b\" in base_model_id.lower():\n",
- " machine_type = \"a3-highgpu-4g\"\n",
- " accelerator_type = \"NVIDIA_H100_80GB\"\n",
- " per_node_accelerator_count = 4\n",
- "elif \"405b\" in base_model_id.lower():\n",
- " machine_type = \"a3-highgpu-8g\"\n",
- " accelerator_type = \"NVIDIA_H100_80GB\"\n",
- " per_node_accelerator_count = 8\n",
- "else:\n",
- " raise ValueError(f\"Unsupported model ID or GCS path: {base_model_id}.\")\n",
- "\n",
- "common_util.check_quota(\n",
- " project_id=PROJECT_ID,\n",
- " region=DEPLOY_REGION,\n",
- " accelerator_type=accelerator_type,\n",
- " accelerator_count=per_node_accelerator_count,\n",
- " is_for_training=False,\n",
- ")\n",
- "\n",
- "# @markdown Set `use_dedicated_endpoint` to False if you don't want to use [dedicated endpoint](https://cloud.google.com/vertex-ai/docs/general/deployment#create-dedicated-endpoint).\n",
- "use_dedicated_endpoint = True # @param {type:\"boolean\"}\n",
- "\n",
- "gpu_memory_utilization = 0.95\n",
- "max_model_len = 8192 # Maximum context length.\n",
- "\n",
- "# Ensure max_model_len does not exceed the limit\n",
- "if max_model_len > 8192:\n",
- " raise ValueError(\"max_model_len cannot exceed 8192\")\n",
- "\n",
- "\n",
- "def get_deploy_source() -> str:\n",
- " \"\"\"Gets deploy_source string based on running environment.\"\"\"\n",
- " vertex_product = os.environ.get(\"VERTEX_PRODUCT\", \"\")\n",
- " if vertex_product == \"COLAB_ENTERPRISE\":\n",
- " return \"notebook_colab_enterprise\"\n",
- " elif vertex_product == \"WORKBENCH_INSTANCE\":\n",
- " return \"notebook_workbench\"\n",
- " else:\n",
- " # Legacy workbench, legacy colab, or other custom environments.\n",
- " return \"notebook_environment_unspecified\"\n",
- "\n",
- "\n",
- "def deploy_model_vllm(\n",
- " model_name: str,\n",
- " model_id: str,\n",
- " publisher: str,\n",
- " publisher_model_id: str,\n",
- " service_account: str,\n",
- " base_model_id: str = None,\n",
- " machine_type: str = \"g2-standard-8\",\n",
- " accelerator_type: str = \"NVIDIA_L4\",\n",
- " accelerator_count: int = 1,\n",
- " gpu_memory_utilization: float = 0.9,\n",
- " max_model_len: int = 4096,\n",
- " dtype: str = \"auto\",\n",
- " enable_trust_remote_code: bool = False,\n",
- " enforce_eager: bool = False,\n",
- " enable_lora: bool = False,\n",
- " enable_chunked_prefill: bool = False,\n",
- " enable_prefix_cache: bool = False,\n",
- " host_prefix_kv_cache_utilization_target: float = 0.0,\n",
- " max_loras: int = 1,\n",
- " max_cpu_loras: int = 8,\n",
- " use_dedicated_endpoint: bool = False,\n",
- " max_num_seqs: int = 256,\n",
- " model_type: str = None,\n",
- " enable_llama_tool_parser: bool = False,\n",
- " is_spot: bool = False,\n",
- ") -> Tuple[aiplatform.Model, aiplatform.Endpoint]:\n",
- " \"\"\"Deploys trained models with vLLM into Vertex AI.\"\"\"\n",
- " endpoint = aiplatform.Endpoint.create(\n",
- " display_name=f\"{model_name}-endpoint\",\n",
- " dedicated_endpoint_enabled=use_dedicated_endpoint,\n",
- " )\n",
- "\n",
- " if not base_model_id:\n",
- " base_model_id = model_id\n",
- "\n",
- " # See https://docs.vllm.ai/en/latest/models/engine_args.html for a list of possible arguments with descriptions.\n",
- " vllm_args = [\n",
- " \"python\",\n",
- " \"-m\",\n",
- " \"vllm.entrypoints.api_server\",\n",
- " \"--host=0.0.0.0\",\n",
- " \"--port=8080\",\n",
- " f\"--model={model_id}\",\n",
- " f\"--tensor-parallel-size={accelerator_count}\",\n",
- " \"--swap-space=16\",\n",
- " f\"--max-model-len={max_model_len}\",\n",
- " f\"--dtype={dtype}\",\n",
- " f\"--max-loras={max_loras}\",\n",
- " f\"--max-cpu-loras={max_cpu_loras}\",\n",
- " f\"--max-num-seqs={max_num_seqs}\",\n",
- " \"--disable-log-stats\",\n",
- " ]\n",
- "\n",
- " if gpu_memory_utilization:\n",
- " vllm_args.append(f\"--gpu-memory-utilization={gpu_memory_utilization}\")\n",
- "\n",
- " if enable_trust_remote_code:\n",
- " vllm_args.append(\"--trust-remote-code\")\n",
- "\n",
- " if enforce_eager:\n",
- " vllm_args.append(\"--enforce-eager\")\n",
- "\n",
- " if enable_lora:\n",
- " vllm_args.append(\"--enable-lora\")\n",
- "\n",
- " if enable_chunked_prefill:\n",
- " vllm_args.append(\"--enable-chunked-prefill\")\n",
- "\n",
- " if enable_prefix_cache:\n",
- " vllm_args.append(\"--enable-prefix-caching\")\n",
- "\n",
- " if 0 < host_prefix_kv_cache_utilization_target < 1:\n",
- " vllm_args.append(\n",
- " f\"--host-prefix-kv-cache-utilization-target={host_prefix_kv_cache_utilization_target}\"\n",
- " )\n",
- "\n",
- " if model_type:\n",
- " vllm_args.append(f\"--model-type={model_type}\")\n",
- "\n",
- " if enable_llama_tool_parser:\n",
- " vllm_args.append(\"--enable-auto-tool-choice\")\n",
- " vllm_args.append(\"--tool-call-parser=vertex-llama-3\")\n",
- "\n",
- " env_vars = {\n",
- " \"MODEL_ID\": base_model_id,\n",
- " \"DEPLOY_SOURCE\": \"notebook\",\n",
- " }\n",
- "\n",
- " # HF_TOKEN is not a compulsory field and may not be defined.\n",
- " try:\n",
- " if HF_TOKEN:\n",
- " env_vars[\"HF_TOKEN\"] = HF_TOKEN\n",
- " except NameError:\n",
- " pass\n",
- "\n",
- " model = aiplatform.Model.upload(\n",
- " display_name=model_name,\n",
- " serving_container_image_uri=VLLM_DOCKER_URI,\n",
- " serving_container_args=vllm_args,\n",
- " serving_container_ports=[8080],\n",
- " serving_container_predict_route=\"/generate\",\n",
- " serving_container_health_route=\"/ping\",\n",
- " serving_container_environment_variables=env_vars,\n",
- " serving_container_shared_memory_size_mb=(16 * 1024), # 16 GB\n",
- " serving_container_deployment_timeout=7200,\n",
- " model_garden_source_model_name=(\n",
- " f\"publishers/{publisher}/models/{publisher_model_id}\"\n",
- " ),\n",
- " )\n",
- " print(\n",
- " f\"Deploying {model_name} on {machine_type} with {accelerator_count} {accelerator_type} GPU(s).\"\n",
- " )\n",
- " model.deploy(\n",
- " endpoint=endpoint,\n",
- " machine_type=machine_type,\n",
- " accelerator_type=accelerator_type,\n",
- " accelerator_count=accelerator_count,\n",
- " deploy_request_timeout=1800,\n",
- " service_account=service_account,\n",
- " spot=is_spot,\n",
- " system_labels={\n",
- " \"NOTEBOOK_NAME\": \"model_garden_pytorch_llama3_1_finetuning.ipynb\",\n",
- " \"NOTEBOOK_ENVIRONMENT\": get_deploy_source(),\n",
- " },\n",
- " )\n",
- " print(\"endpoint_name:\", endpoint.name)\n",
- "\n",
- " return model, endpoint\n",
- "\n",
- "\n",
- "def predict_vllm(\n",
- " prompt: str,\n",
- " max_tokens: int,\n",
- " temperature: float,\n",
- " top_p: float,\n",
- " top_k: int,\n",
- " raw_response: bool,\n",
- " lora_weight: str = \"\",\n",
- "):\n",
- " # Parameters for inference.\n",
- " instance = {\n",
- " \"prompt\": prompt,\n",
- " \"max_tokens\": max_tokens,\n",
- " \"temperature\": temperature,\n",
- " \"top_p\": top_p,\n",
- " \"top_k\": top_k,\n",
- " \"raw_response\": raw_response,\n",
- " }\n",
- " if lora_weight:\n",
- " instance[\"dynamic-lora\"] = lora_weight\n",
- " instances = [instance]\n",
- " response = endpoints[\"vllm_gpu\"].predict(\n",
- " instances=instances, use_dedicated_endpoint=use_dedicated_endpoint\n",
- " )\n",
- "\n",
- " for prediction in response.predictions:\n",
- " print(prediction)\n",
- "\n",
- "\n",
- "# Use FP8 base model for 405B since original model does not fit.\n",
- "deploy_pretrained_model_id = pretrained_model_id\n",
- "if \"Meta-Llama-3.1-405B\" in deploy_pretrained_model_id:\n",
- " deploy_pretrained_model_id += \"-FP8\"\n",
- "print(\"Deploying model in:\", deploy_pretrained_model_id)\n",
- "\n",
- "models[\"vllm_gpu\"], endpoints[\"vllm_gpu\"] = deploy_model_vllm(\n",
- " model_name=common_util.get_job_name_with_datetime(prefix=\"llama3_1-vllm-serve\"),\n",
- " model_id=deploy_pretrained_model_id,\n",
- " publisher=\"meta\",\n",
- " publisher_model_id=\"llama3_1\",\n",
- " service_account=SERVICE_ACCOUNT,\n",
- " machine_type=machine_type,\n",
- " accelerator_type=accelerator_type,\n",
- " accelerator_count=per_node_accelerator_count,\n",
- " gpu_memory_utilization=gpu_memory_utilization,\n",
- " max_model_len=max_model_len,\n",
- " enable_lora=True,\n",
- " use_dedicated_endpoint=use_dedicated_endpoint,\n",
- " is_spot=is_spot,\n",
- ")\n",
- "\n",
- "# @markdown Click \"Show Code\" to see more details."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "2UYUNn60G_4U"
- },
- "outputs": [],
- "source": [
- "# @title Predict\n",
- "\n",
- "# @markdown Once deployment succeeds, you can send requests to the endpoint with text prompts. Sampling parameters supported by vLLM can be found [here](https://docs.vllm.ai/en/latest/dev/sampling_params.html).\n",
- "\n",
- "# @markdown Example:\n",
- "\n",
- "# @markdown ```\n",
- "# @markdown Human: What is a car?\n",
- "# @markdown Assistant: A car, or a motor car, is a road-connected human-transportation system used to move people or goods from one place to another. The term also encompasses a wide range of vehicles, including motorboats, trains, and aircrafts. Cars typically have four wheels, a cabin for passengers, and an engine or motor. They have been around since the early 19th century and are now one of the most popular forms of transportation, used for daily commuting, shopping, and other purposes.\n",
- "# @markdown ```\n",
- "\n",
- "# @markdown Additionally, you can moderate the generated text with Vertex AI. See [Moderate text documentation](https://cloud.google.com/natural-language/docs/moderating-text) for more details.\n",
- "\n",
- "prompt = \"What is a car?\" # @param {type: \"string\"}\n",
- "# @markdown If you encounter the issue like `ServiceUnavailable: 503 Took too long to respond when processing`, you can reduce the maximum number of output tokens, such as set `max_tokens` as 20.\n",
- "max_tokens = 50 # @param {type:\"integer\"}\n",
- "temperature = 1.0 # @param {type:\"number\"}\n",
- "top_p = 1.0 # @param {type:\"number\"}\n",
- "top_k = 1 # @param {type:\"integer\"}\n",
- "raw_response = False # @param {type:\"boolean\"}\n",
- "\n",
- "predict_vllm(\n",
- " prompt=prompt,\n",
- " max_tokens=max_tokens,\n",
- " temperature=temperature,\n",
- " top_p=top_p,\n",
- " top_k=top_k,\n",
- " raw_response=raw_response,\n",
- " lora_weight=final_checkpoint,\n",
- ")\n",
- "\n",
- "# @markdown Click \"Show Code\" to see more details."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "af21a3cff1e0"
- },
- "source": [
- "## Clean up resources"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "911406c1561e"
- },
- "outputs": [],
- "source": [
- "# @title Delete the model and endpoint\n",
- "\n",
- "if train_job:\n",
- " train_job.delete()\n",
- "if RUN_EVALUATION and lm_eval_job:\n",
- " lm_eval_job.delete()\n",
- "\n",
- "\n",
- "# @markdown Delete the experiment models and endpoints to recycle the resources\n",
- "# @markdown and avoid unnecessary continuous charges that may incur.\n",
- "\n",
- "# Undeploy model and delete endpoint.\n",
- "for endpoint in endpoints.values():\n",
- " endpoint.delete(force=True)\n",
- "\n",
- "# Delete models.\n",
- "for model in models.values():\n",
- " model.delete()\n",
- "\n",
- "delete_bucket = False # @param {type:\"boolean\"}\n",
- "if delete_bucket:\n",
- " ! gsutil -m rm -r $BUCKET_NAME"
- ]
- }
- ],
- "metadata": {
- "colab": {
- "name": "model_garden_pytorch_llama3_1_finetuning.ipynb",
- "toc_visible": true
- },
- "kernelspec": {
- "display_name": "Python 3",
- "name": "python3"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 0
-}
diff --git a/notebooks/community/model_garden/model_garden_pytorch_llama3_3_finetuning.ipynb b/notebooks/community/model_garden/model_garden_pytorch_llama3_3_finetuning.ipynb
deleted file mode 100644
index ba82abef2..000000000
--- a/notebooks/community/model_garden/model_garden_pytorch_llama3_3_finetuning.ipynb
+++ /dev/null
@@ -1,1106 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "7d9bbf86da5e"
- },
- "outputs": [],
- "source": [
- "# Copyright 2026 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": "99c1c3fc2ca5"
- },
- "source": [
- "# Vertex AI Model Garden - Llama 3.3 Finetuning\n",
- "\n",
- "\n",
- " \n",
- " \n",
- "  Run in Colab Enterprise\n",
- " \n",
- " | \n",
- " \n",
- " \n",
- "  View on GitHub\n",
- " \n",
- " | \n",
- "
"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "3de7470326a2"
- },
- "source": [
- "## Overview\n",
- "\n",
- "This notebook demonstrates finetuning and deploying Llama 3.3 models with Vertex AI. All of the examples in this notebook use parameter efficient finetuning methods [PEFT (LoRA)](https://github.com/huggingface/peft) to reduce training and storage costs. LoRA (Low-Rank Adaptation) is one approach of Parameter Efficient FineTuning (PEFT), where pretrained model weights are frozen and rank decomposition matrices representing the change in model weights are trained during finetuning. Read more about LoRA in the following publication: [Hu, E.J., Shen, Y., Wallis, P., Allen-Zhu, Z., Li, Y., Wang, S., Wang, L. and Chen, W., 2021. Lora: Low-rank adaptation of large language models. *arXiv preprint arXiv:2106.09685*](https://arxiv.org/abs/2106.09685).\n",
- "\n",
- "After finetuning, we can deploy models on Vertex with GPU.\n",
- "\n",
- "\n",
- "### Objective\n",
- "\n",
- "- Finetune Llama 3.3 models with Vertex AI Custom Training Jobs.\n",
- "- Evaluate the finetuned model using [lm-evaluation-harness](https://github.com/EleutherAI/lm-evaluation-harness).\n",
- "- Deploy finetuned Llama 3.3 models on Vertex AI Prediction.\n",
- "- Send prediction requests to your finetuned Llama 3.3 models.\n",
- "\n",
- "### File a bug\n",
- "\n",
- "File a bug on [GitHub](https://github.com/GoogleCloudPlatform/vertex-ai-samples/issues/new) if you encounter any issue with the notebook.\n",
- "\n",
- "### Costs\n",
- "\n",
- "This tutorial uses billable components of Google Cloud:\n",
- "\n",
- "* Vertex AI\n",
- "* Cloud Storage\n",
- "\n",
- "Learn about [Vertex AI pricing](https://cloud.google.com/vertex-ai/pricing), [Cloud Storage pricing](https://cloud.google.com/storage/pricing), and use the [Pricing Calculator](https://cloud.google.com/products/calculator/) to generate a cost estimate based on your projected usage."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "264c07757582"
- },
- "source": [
- "## Before you begin"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "855d6b96f291"
- },
- "outputs": [],
- "source": [
- "# @title Install Python Packages for Finetuning\n",
- "\n",
- "# @markdown 1. Install packages to validate dataset with template.\n",
- "! pip install --upgrade --quiet gcsfs==2024.3.1\n",
- "! pip install --upgrade --quiet accelerate==0.34.2\n",
- "! pip install --upgrade --quiet transformers==4.47.1\n",
- "! pip install --upgrade --quiet datasets==2.20.0\n",
- "! pip install --upgrade --quiet google-cloud-aiplatform==1.130.0"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "50273xHFJi5T"
- },
- "outputs": [],
- "source": [
- "# @title Setup Google Cloud project\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. For finetuning, **[click here](https://console.cloud.google.com/iam-admin/quotas?location=us-central1&metric=aiplatform.googleapis.com%2Frestricted_image_training_nvidia_a100_80gb_gpus)** to check if your project already has the required 8 Nvidia A100 80 GB GPUs in the us-central1 region. If yes, then run this notebook in the us-central1 region. If you do not have 8 Nvidia A100 80 GPUs or have more GPU requirements than this, then schedule your job with Nvidia H100 GPUs via Dynamic Workload Scheduler using [these instructions](https://cloud.google.com/vertex-ai/docs/training/schedule-jobs-dws). For Dynamic Workload Scheduler, check the [us-central1](https://console.cloud.google.com/iam-admin/quotas?location=us-central1&metric=aiplatform.googleapis.com%2Fcustom_model_training_preemptible_nvidia_h100_gpus) or [europe-west4](https://console.cloud.google.com/iam-admin/quotas?location=europe-west4&metric=aiplatform.googleapis.com%2Fcustom_model_training_preemptible_nvidia_h100_gpus) quota for Nvidia H100 GPUs. If you do not have enough GPUs, then you can follow [these instructions](https://cloud.google.com/docs/quotas/view-manage#viewing_your_quota_console) to request quota.\n",
- "\n",
- "# @markdown 3. For serving, **[click here](https://console.cloud.google.com/iam-admin/quotas?location=us-central1&metric=aiplatform.googleapis.com%2Fcustom_model_serving_nvidia_l4_gpus)** to check if your project already has the required 1 L4 GPU in the us-central1 region. If yes, then run this notebook in the us-central1 region. If you need more L4 GPUs for your project, then you can follow [these instructions](https://cloud.google.com/docs/quotas/view-manage#viewing_your_quota_console) to request more. Alternatively, 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).\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",
- "# @markdown 4. **[Optional]** [Create a Cloud Storage bucket](https://cloud.google.com/storage/docs/creating-buckets) for storing experiment outputs. Set the BUCKET_URI for the experiment environment. The specified Cloud Storage bucket (`BUCKET_URI`) should be located in the same region as where the notebook was launched. Note that a multi-region bucket (eg. \"us\") is not considered a match for a single region covered by the multi-region range (eg. \"us-central1\"). If not set, a unique GCS bucket will be created instead.\n",
- "\n",
- "BUCKET_URI = \"gs://\" # @param {type:\"string\"}\n",
- "\n",
- "# @markdown 5. **[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",
- "# Import the necessary packages.\n",
- "! rm -rf vertex-ai-samples && git clone https://github.com/GoogleCloudPlatform/vertex-ai-samples.git\n",
- "! cd vertex-ai-samples && git reset --hard 7ae13b346a72ee2a2dc8152dd40c6ddd72d6c810\n",
- "\n",
- "import datetime\n",
- "import importlib\n",
- "import os\n",
- "import uuid\n",
- "from typing import Tuple\n",
- "\n",
- "from google.cloud import aiplatform\n",
- "from google.cloud.aiplatform.compat.types import \\\n",
- " custom_job as gca_custom_job_compat\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",
- "models, endpoints = {}, {}\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",
- " if not os.environ.get(\"GOOGLE_CLOUD_REGION\"):\n",
- " raise ValueError(\n",
- " \"REGION must be set. See\"\n",
- " \" https://cloud.google.com/vertex-ai/docs/general/locations for\"\n",
- " \" available cloud locations.\"\n",
- " )\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",
- "# Cloud Storage bucket for storing the experiment artifacts.\n",
- "# A unique GCS bucket will be created for the purpose of this notebook. If you\n",
- "# prefer using your own GCS bucket, change the value yourself below.\n",
- "now = datetime.datetime.now().strftime(\"%Y%m%d%H%M%S\")\n",
- "BUCKET_NAME = \"/\".join(BUCKET_URI.split(\"/\")[:3])\n",
- "\n",
- "if BUCKET_URI is None or BUCKET_URI.strip() == \"\" or BUCKET_URI == \"gs://\":\n",
- " BUCKET_URI = f\"gs://{PROJECT_ID}-tmp-{now}-{str(uuid.uuid4())[:4]}\"\n",
- " BUCKET_NAME = \"/\".join(BUCKET_URI.split(\"/\")[:3])\n",
- " ! gsutil mb -l {REGION} {BUCKET_URI}\n",
- "else:\n",
- " assert BUCKET_URI.startswith(\"gs://\"), \"BUCKET_URI must start with `gs://`.\"\n",
- " shell_output = ! gsutil ls -Lb {BUCKET_NAME} | grep \"Location constraint:\" | sed \"s/Location constraint://\"\n",
- " bucket_region = shell_output[0].strip().lower()\n",
- " if bucket_region != REGION:\n",
- " raise ValueError(\n",
- " \"Bucket region %s is different from notebook region %s\"\n",
- " % (bucket_region, REGION)\n",
- " )\n",
- "print(f\"Using this GCS Bucket: {BUCKET_URI}\")\n",
- "\n",
- "STAGING_BUCKET = os.path.join(BUCKET_URI, \"temporal\")\n",
- "MODEL_BUCKET = os.path.join(BUCKET_URI, \"llama3_3\")\n",
- "\n",
- "\n",
- "# Initialize Vertex AI API.\n",
- "print(\"Initializing Vertex AI API.\")\n",
- "aiplatform.init(project=PROJECT_ID, location=REGION, staging_bucket=STAGING_BUCKET)\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",
- "\n",
- "# Provision permissions to the SERVICE_ACCOUNT with the GCS bucket\n",
- "! gsutil iam ch serviceAccount:{SERVICE_ACCOUNT}:roles/storage.admin $BUCKET_NAME\n",
- "\n",
- "! gcloud config set project $PROJECT_ID\n",
- "! gcloud projects add-iam-policy-binding --no-user-output-enabled {PROJECT_ID} --member=serviceAccount:{SERVICE_ACCOUNT} --role=\"roles/storage.admin\"\n",
- "! gcloud projects add-iam-policy-binding --no-user-output-enabled {PROJECT_ID} --member=serviceAccount:{SERVICE_ACCOUNT} --role=\"roles/aiplatform.user\""
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "36c21f10355f"
- },
- "outputs": [],
- "source": [
- "# @title Access Llama 3.3 models\n",
- "\n",
- "# @markdown For GPU based finetuning and serving, choose between accessing Llama 3.3 models on [Hugging Face](https://huggingface.co/)\n",
- "# @markdown or Vertex AI as described below.\n",
- "\n",
- "# @markdown If you already obtained access to Llama 3.3 models on [Hugging Face](https://huggingface.co/), you can load models from there.\n",
- "# @markdown Alternatively, you can also load the original Llama 3.3 models for finetuning and serving from Vertex AI after accepting the agreement.\n",
- "\n",
- "# @markdown **Only select and fill one of the following sections.**\n",
- "LOAD_MODEL_FROM = \"Google Cloud\" # @param [\"Hugging Face\", \"Google Cloud\"] {isTemplate:true}\n",
- "\n",
- "# @markdown ---\n",
- "\n",
- "# @markdown ### Access Llama 3.3 models on Hugging Face for GPU based finetuning and serving\n",
- "# @markdown You must provide a Hugging Face User Access Token (with read access) to access the Llama 3.3 models. You can follow the [Hugging Face documentation](https://huggingface.co/docs/hub/en/security-tokens) to create a **read** access token and put it in the `HF_TOKEN` field below.\n",
- "# @markdown If you like to tune deepseek-ai/DeepSeek-R1-Distill-Llama-70B with this notebook, select Hugging Face as the model source.\n",
- "\n",
- "HF_TOKEN = \"\" # @param {type:\"string\", isTemplate:true}\n",
- "# @markdown *--- Or ---*\n",
- "# @markdown ### Access Llama 3.3 models on Vertex AI for GPU based serving\n",
- "# @markdown The original models from Meta are converted into the Hugging Face format for serving in Vertex AI.\n",
- "# @markdown Accept the model agreement to access the models:\n",
- "# @markdown 1. Open the [Llama 3.3 model card](https://console.cloud.google.com/vertex-ai/publishers/meta/model-garden/llama3-3) from [Vertex AI Model Garden](https://cloud.google.com/model-garden).\n",
- "# @markdown 2. Review and accept the agreement in the pop-up window on the model card page. If you have previously accepted the model agreement, there will not be a pop-up window on the model card page and this step is not needed.\n",
- "# @markdown 3. After accepting the agreement of Llama 3.3, a `gs://` URI containing Llama 3.3 pretrained and finetuned models will be shared.\n",
- "# @markdown 4. Paste the URI in the `VERTEX_AI_MODEL_GARDEN_LLAMA3_3` field below.\n",
- "\n",
- "VERTEX_AI_MODEL_GARDEN_LLAMA3_3 = \"\" # @param {type:\"string\", isTemplate:true}\n",
- "if LOAD_MODEL_FROM == \"Hugging Face\":\n",
- " assert (\n",
- " HF_TOKEN\n",
- " ), \"Provide a read HF_TOKEN to load models from Hugging Face, or select a different model source.\"\n",
- "else:\n",
- " assert (\n",
- " VERTEX_AI_MODEL_GARDEN_LLAMA3_3\n",
- " ), \"Click the agreement of Llama3.3 in Vertex AI Model Garden, and get the GCS path of the model artifacts.\"\n",
- "\n",
- "MODEL_BUCKET = VERTEX_AI_MODEL_GARDEN_LLAMA3_3\n",
- "\n",
- "# @markdown ---"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "cb56d402e84a"
- },
- "source": [
- "## Finetune with HuggingFace PEFT and deploy with vLLM on GPUs"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "KwAW99YZHTdy"
- },
- "outputs": [],
- "source": [
- "# @title Set dataset\n",
- "\n",
- "# @markdown Use the Vertex AI SDK to create and run the custom training jobs.\n",
- "\n",
- "# @markdown This notebook uses [timdettmers/openassistant-guanaco](https://huggingface.co/datasets/timdettmers/openassistant-guanaco) dataset as an example.\n",
- "# @markdown You can set `train_dataset` to any existing [Hugging Face dataset](https://huggingface.co/datasets) name, and set `train_column` to the name of the dataset column containing training data. The [timdettmers/openassistant-guanaco](https://huggingface.co/datasets/timdettmers/openassistant-guanaco) has only one column `text`, and therefore we set `train_column` to `text` in this notebook.\n",
- "\n",
- "# @markdown ### (Optional) Prepare a custom JSONL dataset for finetuning\n",
- "\n",
- "# @markdown You can prepare a JSONL file where each line is a valid JSON string as your custom training dataset. For example, here is one line from the [timdettmers/openassistant-guanaco](https://huggingface.co/datasets/timdettmers/openassistant-guanaco) dataset:\n",
- "# @markdown ```\n",
- "# @markdown {\"text\": \"### Human: Hola### Assistant: \\u00a1Hola! \\u00bfEn qu\\u00e9 puedo ayudarte hoy?\"}\n",
- "# @markdown ```\n",
- "\n",
- "# @markdown The JSON object has a key `text`, which should match `train_column`; The value should be one training data point, i.e. a string. After you prepared your JSONL file, you can either upload it to [Hugging Face datasets](https://huggingface.co/datasets) or [Google Cloud Storage](https://cloud.google.com/storage).\n",
- "\n",
- "# @markdown - To upload a JSONL dataset to [Hugging Face datasets](https://huggingface.co/datasets), follow the instructions on [Uploading Datasets](https://huggingface.co/docs/hub/en/datasets-adding). Then, set `dataset_name` to the name of your newly created dataset on Hugging Face.\n",
- "\n",
- "# @markdown - To upload a JSONL dataset to [Google Cloud Storage](https://cloud.google.com/storage), follow the instructions on [Upload objects from a filesystem](https://cloud.google.com/storage/docs/uploading-objects). Then, set `dataset_name` to the `gs://` URI to your JSONL file. For example: `gs://cloud-samples-data/vertex-ai/model-evaluation/peft_train_sample.jsonl`.\n",
- "\n",
- "# @markdown Optionally update the `train_column` field below if your JSON objects use a key other than the default `text`.\n",
- "\n",
- "# @markdown ### (Optional) Format your data with custom JSON template\n",
- "\n",
- "# @markdown Sometimes, your dataset might have multiple text columns and you want to construct the training data with a template. You can prepare a JSON template in the following format:\n",
- "\n",
- "# @markdown ```\n",
- "# @markdown {\n",
- "# @markdown \"description\": \"Template used by Llama 3.3, accepting text-bison format.\",\n",
- "# @markdown \"source\": \"https://cloud.google.com/vertex-ai/generative-ai/docs/models/tune-text-models-supervised#dataset-format\",\n",
- "# @markdown \"prompt_input\": \"<|start_header_id|>user<|end_header_id|>\\n\\n{input_text}<|eot_id|><|start_header_id|>assistant<|end_header_id|>\\n\\n{output_text}<|eot_id|>\",\n",
- "# @markdown \"instruction_separator\": \"<|start_header_id|>user<|end_header_id|>\\n\\n\",\n",
- "# @markdown \"response_separator\": \"<|start_header_id|>assistant<|end_header_id|>\\n\\n\"\n",
- "# @markdown }\n",
- "# @markdown ```\n",
- "\n",
- "# @markdown As an example, the template above can be used to format the following training data (this line comes from `gs://cloud-samples-data/vertex-ai/model-evaluation/peft_train_sample.jsonl`):\n",
- "\n",
- "# @markdown ```\n",
- "# @markdown {\"input_text\":\"TRANSCRIPT: \\nREASON FOR EVALUATION:,\\n\\n LABEL:\",\"output_text\":\"Chiropractic\"}\n",
- "# @markdown ```\n",
- "\n",
- "# @markdown This example template simply concatenates `input_text` with `output_text` with some special tokens in between.\n",
- "# @markdown\n",
- "# @markdown To try such custom dataset, you can make the following changes:\n",
- "# @markdown 1. Set `template` to `llama3-text-bison`\n",
- "# @markdown 1. Set `train_dataset` to `gs://cloud-samples-data/vertex-ai/model-evaluation/peft_train_sample.jsonl`\n",
- "# @markdown 1. Set `train_split` to `train`\n",
- "# @markdown 1. Set `eval_dataset` to `gs://cloud-samples-data/vertex-ai/model-evaluation/peft_eval_sample.jsonl`\n",
- "# @markdown 1. Set `eval_split` to `train` (**NOT** `test`)\n",
- "# @markdown 1. Set `train_column` as `input_text`.\n",
- "\n",
- "# Template name or gs:// URI to a custom template.\n",
- "template = \"openassistant-guanaco\" # @param {type:\"string\"}\n",
- "\n",
- "# Hugging Face dataset name or gs:// URI to a custom JSONL dataset.\n",
- "train_dataset = \"timdettmers/openassistant-guanaco\" # @param {type:\"string\"}\n",
- "train_split = \"train\" # @param {type:\"string\"}\n",
- "eval_dataset = \"timdettmers/openassistant-guanaco\" # @param {type:\"string\"}\n",
- "eval_split = \"test\" # @param {type:\"string\"}\n",
- "\n",
- "# Name of the dataset column containing training text input.\n",
- "train_column = \"text\" # @param {type:\"string\"}\n",
- "# Maximum sequence length.\n",
- "max_seq_length = 4096 # @param{type:\"integer\"}"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "iu1YAu8315sG"
- },
- "outputs": [],
- "source": [
- "# @title Set model\n",
- "\n",
- "# @markdown Select a model variant of Llama 3.3.\n",
- "base_model_id = \"meta-llama/Llama-3.3-70B-Instruct\" # @param [\"meta-llama/Llama-3.3-70B-Instruct\", \"deepseek-ai/DeepSeek-R1-Distill-Llama-70B\"] {isTemplate:true}\n",
- "if LOAD_MODEL_FROM == \"Google Cloud\":\n",
- " pretrained_model_id = os.path.join(MODEL_BUCKET, base_model_id.split(\"/\")[-1])\n",
- "else:\n",
- " pretrained_model_id = base_model_id"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "_mNcaofv4zpv"
- },
- "outputs": [],
- "source": [
- "# @title Validate Dataset with Template\n",
- "\n",
- "# @markdown This section validates the train and eval datasets with the template before starting the fine tuning process.\n",
- "\n",
- "dataset_validation_util = importlib.import_module(\n",
- " \"vertex-ai-samples.community-content.vertex_model_garden.model_oss.notebook_util.dataset_validation_util\"\n",
- ")\n",
- "\n",
- "if dataset_validation_util.is_gcs_path(pretrained_model_id):\n",
- " # Download tokenizer.\n",
- " ! mkdir tokenizer\n",
- " ! gsutil cp {pretrained_model_id}/tokenizer.json ./tokenizer\n",
- " ! gsutil cp {pretrained_model_id}/config.json ./tokenizer\n",
- " tokenizer_path = \"./tokenizer\"\n",
- " access_token = \"\"\n",
- "else:\n",
- " tokenizer_path = pretrained_model_id\n",
- " access_token = HF_TOKEN\n",
- "\n",
- "tokenizer = dataset_validation_util.load_tokenizer(tokenizer_path, None, access_token)\n",
- "\n",
- "# Validate the train dataset.\n",
- "dataset_validation_util.validate_dataset_with_template(\n",
- " dataset_name=train_dataset,\n",
- " split=train_split,\n",
- " input_column=train_column,\n",
- " template=template,\n",
- " max_seq_length=max_seq_length,\n",
- " use_multiprocessing=False,\n",
- " tokenizer=tokenizer,\n",
- ")\n",
- "\n",
- "# Validate the eval dataset if it exists.\n",
- "if eval_dataset:\n",
- " dataset_validation_util.validate_dataset_with_template(\n",
- " dataset_name=eval_dataset,\n",
- " split=eval_split,\n",
- " input_column=train_column,\n",
- " template=template,\n",
- " max_seq_length=max_seq_length,\n",
- " use_multiprocessing=False,\n",
- " tokenizer=tokenizer,\n",
- " )"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "ivVGS9dHXPOz"
- },
- "outputs": [],
- "source": [
- "# @title Finetune\n",
- "\n",
- "# @markdown This section demonstrates how to finetune the Llama 3.3 model on Vertex AI. It uses the Vertex AI SDK to create and run the custom training jobs.\n",
- "\n",
- "# @markdown The training job takes approximately between 10 to 20 mins to set-up. Once done, the training job is expected to take around 75 mins with the default configurations. To find the training time, throughput, and memory usage of your training job, you can go to the training logs and check the log line of the last training epoch.\n",
- "\n",
- "# @markdown **Note**:\n",
- "# @markdown 1. We recommend setting `finetuning_precision_mode` to `4bit` because it enables using fewer hardware resources for finetuning.\n",
- "# @markdown 1. If `max_steps>0`, it takes precedence over `epochs`. One can set a small `max_steps` value to quickly check the pipeline.\n",
- "\n",
- "# @markdown Acceletor type to use for training.\n",
- "training_accelerator_type = \"NVIDIA_A100_80GB\" # @param [\"NVIDIA_A100_80GB\", \"NVIDIA_H100_80GB\"]\n",
- "\n",
- "# The pre-built training docker image.\n",
- "if training_accelerator_type == \"NVIDIA_A100_80GB\":\n",
- " repo = \"us-docker.pkg.dev/vertex-ai-restricted\"\n",
- " is_restricted_image = True\n",
- " is_dynamic_workload_scheduler = False\n",
- " dws_kwargs = {}\n",
- "else:\n",
- " repo = \"us-docker.pkg.dev/vertex-ai\"\n",
- " is_restricted_image = False\n",
- " is_dynamic_workload_scheduler = True\n",
- " dws_kwargs = {\n",
- " \"max_wait_duration\": 1800, # 30 minutes\n",
- " \"scheduling_strategy\": gca_custom_job_compat.Scheduling.Strategy.FLEX_START,\n",
- " }\n",
- "\n",
- "TRAIN_DOCKER_URI = (\n",
- " f\"{repo}/vertex-vision-model-garden-dockers/pytorch-peft-train:stable_20250705\"\n",
- ")\n",
- "\n",
- "# Worker pool spec.\n",
- "boot_disk_size_gb = 500\n",
- "if training_accelerator_type == \"NVIDIA_A100_80GB\":\n",
- " per_node_accelerator_count = 8\n",
- " training_machine_type = \"a2-ultragpu-8g\"\n",
- "elif training_accelerator_type == \"NVIDIA_H100_80GB\":\n",
- " per_node_accelerator_count = 8\n",
- " training_machine_type = \"a3-highgpu-8g\"\n",
- "else:\n",
- " raise ValueError(\n",
- " f\"Recommended machine settings not found for: {training_accelerator_type}. To use another accelerator type, edit this code block to pass in an appropriate `training_machine_type`, `training_accelerator_type`, and `per_node_accelerator_count` by clicking `Show Code` and then modifying the code.\"\n",
- " )\n",
- "\n",
- "# @markdown The number of nodes to use for this worker pool in distributed training.\n",
- "replica_count = 1 # @param{type:\"integer\"}\n",
- "\n",
- "# Set config file.\n",
- "if replica_count == 1:\n",
- " config_file = \"vertex_vision_model_garden_peft/llama_fsdp_8gpu.yaml\"\n",
- "elif replica_count <= 4:\n",
- " config_file = (\n",
- " \"vertex_vision_model_garden_peft/\"\n",
- " f\"llama_hsdp_{replica_count * per_node_accelerator_count}gpu.yaml\"\n",
- " )\n",
- "else:\n",
- " raise ValueError(\n",
- " f\"Recommended config settings not found for replica_count: {replica_count}.\"\n",
- " )\n",
- "\n",
- "# @markdown Batch size for finetuning.\n",
- "per_device_train_batch_size = 1 # @param{type:\"integer\"}\n",
- "# @markdown Number of updates steps to accumulate the gradients for, before performing a backward/update pass.\n",
- "gradient_accumulation_steps = 4 # @param{type:\"integer\"}\n",
- "# @markdown Maximum sequence length.\n",
- "max_seq_length = 4096 # @param{type:\"integer\"}\n",
- "# @markdown Setting a positive `max_steps` here will override `num_train_epochs`.\n",
- "max_steps = -1 # @param{type:\"integer\"}\n",
- "num_train_epochs = 1.0 # @param{type:\"number\"}\n",
- "# @markdown Precision mode for finetuning.\n",
- "finetuning_precision_mode = \"4bit\" # @param [\"4bit\", \"8bit\", \"float16\"]\n",
- "# @markdown Learning rate.\n",
- "learning_rate = 5e-5 # @param{type:\"number\"}\n",
- "# @markdown The scheduler type to use.\n",
- "lr_scheduler_type = \"cosine\" # @param{type:\"string\"}\n",
- "# @markdown LoRA parameters.\n",
- "lora_rank = 16 # @param{type:\"integer\"}\n",
- "lora_alpha = 32 # @param{type:\"integer\"}\n",
- "lora_dropout = 0.05 # @param{type:\"number\"}\n",
- "# Activates gradient checkpointing for the current model (may be referred to as activation checkpointing or checkpoint activations in other frameworks).\n",
- "enable_gradient_checkpointing = True\n",
- "# Attention implementation to use in the model.\n",
- "attn_implementation = \"flash_attention_2\"\n",
- "# The optimizer for which to schedule the learning rate.\n",
- "optimizer = \"adamw_torch\"\n",
- "# Define the proportion of training to be dedicated to a linear warmup where learning rate gradually increases.\n",
- "warmup_ratio = \"0.01\"\n",
- "# The list or string of integrations to report the results and logs to.\n",
- "report_to = \"tensorboard\"\n",
- "# Number of updates steps before two checkpoint saves.\n",
- "save_steps = 10\n",
- "# Number of update steps between two logs.\n",
- "logging_steps = save_steps\n",
- "\n",
- "common_util.check_quota(\n",
- " project_id=PROJECT_ID,\n",
- " region=REGION,\n",
- " accelerator_type=training_accelerator_type,\n",
- " accelerator_count=per_node_accelerator_count * replica_count,\n",
- " is_for_training=True,\n",
- " is_restricted_image=is_restricted_image,\n",
- " is_dynamic_workload_scheduler=is_dynamic_workload_scheduler,\n",
- ")\n",
- "\n",
- "job_name = common_util.get_job_name_with_datetime(\"llama3_3-lora-train\")\n",
- "\n",
- "base_output_dir = os.path.join(STAGING_BUCKET, job_name)\n",
- "# Create a GCS folder to store the LORA adapter.\n",
- "lora_output_dir = os.path.join(base_output_dir, \"adapter\")\n",
- "# Create a GCS folder to store the finetuned LORA adapter.\n",
- "final_checkpoint = os.path.join(lora_output_dir, \"node-0\", \"checkpoint-final\")\n",
- "\n",
- "# Add labels for the finetuning job.\n",
- "labels = {\n",
- " \"mg-source\": \"notebook\",\n",
- " \"mg-notebook-name\": \"model_garden_pytorch_llama3_3_finetuning.ipynb\".split(\".\")[0],\n",
- "}\n",
- "\n",
- "labels[\"mg-tune\"] = \"publishers-meta-models-llama3-3\"\n",
- "versioned_model_id = base_model_id.split(\"/\")[1].lower().replace(\".\", \"-\")\n",
- "labels[\"versioned-mg-tune\"] = f\"{labels['mg-tune']}-{versioned_model_id}\"\n",
- "\n",
- "eval_args = [\n",
- " f\"--eval_dataset={eval_dataset}\",\n",
- " f\"--eval_column={train_column}\",\n",
- " f\"--eval_template={template}\",\n",
- " f\"--eval_split={eval_split}\",\n",
- " f\"--eval_steps={save_steps}\",\n",
- " \"--eval_metric_name=loss\",\n",
- "]\n",
- "\n",
- "train_job_args = [\n",
- " f\"--config_file={config_file}\",\n",
- " \"--task=instruct-lora\",\n",
- " \"--input_masking=True\",\n",
- " f\"--pretrained_model_name_or_path={pretrained_model_id}\",\n",
- " f\"--train_dataset={train_dataset}\",\n",
- " f\"--train_split={train_split}\",\n",
- " f\"--train_column={train_column}\",\n",
- " f\"--output_dir={lora_output_dir}\",\n",
- " f\"--per_device_train_batch_size={per_device_train_batch_size}\",\n",
- " f\"--gradient_accumulation_steps={gradient_accumulation_steps}\",\n",
- " f\"--lora_rank={lora_rank}\",\n",
- " f\"--lora_alpha={lora_alpha}\",\n",
- " f\"--lora_dropout={lora_dropout}\",\n",
- " f\"--max_steps={max_steps}\",\n",
- " f\"--max_seq_length={max_seq_length}\",\n",
- " f\"--learning_rate={learning_rate}\",\n",
- " f\"--lr_scheduler_type={lr_scheduler_type}\",\n",
- " f\"--precision_mode={finetuning_precision_mode}\",\n",
- " f\"--gradient_checkpointing={enable_gradient_checkpointing}\",\n",
- " f\"--num_train_epochs={num_train_epochs}\",\n",
- " f\"--attn_implementation={attn_implementation}\",\n",
- " f\"--optimizer={optimizer}\",\n",
- " f\"--warmup_ratio={warmup_ratio}\",\n",
- " f\"--report_to={report_to}\",\n",
- " f\"--logging_output_dir={base_output_dir}\",\n",
- " f\"--save_steps={save_steps}\",\n",
- " f\"--logging_steps={logging_steps}\",\n",
- " f\"--train_template={template}\",\n",
- " f\"--huggingface_access_token={HF_TOKEN}\",\n",
- "] + eval_args\n",
- "\n",
- "# Pass training arguments and launch job.\n",
- "train_job = aiplatform.CustomContainerTrainingJob(\n",
- " display_name=job_name,\n",
- " container_uri=TRAIN_DOCKER_URI,\n",
- " labels=labels,\n",
- ")\n",
- "\n",
- "print(\"Running training job with args:\")\n",
- "print(\" \\\\\\n\".join(train_job_args))\n",
- "train_job.run(\n",
- " args=train_job_args,\n",
- " replica_count=replica_count,\n",
- " machine_type=training_machine_type,\n",
- " accelerator_type=training_accelerator_type,\n",
- " accelerator_count=per_node_accelerator_count,\n",
- " boot_disk_size_gb=boot_disk_size_gb,\n",
- " service_account=SERVICE_ACCOUNT,\n",
- " base_output_dir=base_output_dir,\n",
- " sync=False, # Non-blocking call to run.\n",
- " **dws_kwargs,\n",
- ")\n",
- "\n",
- "# Wait until resource has been created.\n",
- "train_job.wait_for_resource_creation()\n",
- "\n",
- "print(\"LoRA adapter will be saved in:\", lora_output_dir)\n",
- "print(\"Final checkpoint will be saved in:\", final_checkpoint)\n",
- "\n",
- "# @markdown Click \"Show Code\" to see more details."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "x93f7805YwJg"
- },
- "outputs": [],
- "source": [
- "# @title Run TensorBoard\n",
- "# @markdown This section shows how to launch TensorBoard in a [Cloud Shell](https://cloud.google.com/shell/docs).\n",
- "# @markdown 1. Click the Cloud Shell icon() on the top right to open the Cloud Shell.\n",
- "# @markdown 2. Copy the `tensorboard` command shown below by running this cell.\n",
- "# @markdown 3. Paste and run the command in the Cloud Shell to launch TensorBoard.\n",
- "# @markdown 4. Once the command runs (You may have to click `Authorize` if prompted), click the link starting with `http://localhost`.\n",
- "\n",
- "# @markdown Note: You may need to wait around 10 minutes after the job starts in order for the TensorBoard logs to be written to the GCS bucket.\n",
- "print(f\"Command to copy: tensorboard --logdir {base_output_dir}/logs\")"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "maTaH3s_dWw9"
- },
- "outputs": [],
- "source": [
- "# @title Select Evaluation Checkpoint\n",
- "\n",
- "if train_job.end_time is None:\n",
- " print(\"Waiting for the training job to finish...\")\n",
- " train_job.wait()\n",
- " print(\"The training job has finished.\")\n",
- "\n",
- "# @markdown The following checkpoints are available for evaluation:\n",
- "! gcloud storage ls \"{lora_output_dir}/node-0\" | grep \"checkpoint-\""
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "i2nSJldtrZeA"
- },
- "outputs": [],
- "source": [
- "# @title Run Evaluation Job\n",
- "# @markdown This section runs the evaluation using [lm-evaluation-harness](https://github.com/EleutherAI/lm-evaluation-harness) on the finetuned model. The evaluation takes approximately 20 mins to finish.\n",
- "\n",
- "# The pre-built evaluation docker image for LM Evaluation Harness.\n",
- "LM_EVAL_DOCKER_URI = \"us-docker.pkg.dev/vertex-ai/vertex-vision-model-garden-dockers/pytorch-lm-evaluation-harness:20250410_1035_RC00\"\n",
- "\n",
- "# @markdown Set `RUN_EVALUATION` to False to skip the evaluation job.\n",
- "RUN_EVALUATION = True # @param {type:\"boolean\"}\n",
- "\n",
- "eval_machine_type = \"a3-highgpu-8g\"\n",
- "eval_accelerator_type = \"NVIDIA_H100_80GB\"\n",
- "eval_accelerator_count = 8\n",
- "dws_kwargs = {\n",
- " \"max_wait_duration\": 1800, # 30 minutes\n",
- " \"scheduling_strategy\": gca_custom_job_compat.Scheduling.Strategy.FLEX_START,\n",
- "}\n",
- "\n",
- "# @markdown Set `evaluation_checkpoint_dir` to an intermediate checkpoint from the above training job. If not set, the evaluation job will use the final checkpoint.\n",
- "evaluation_checkpoint_dir = \"\" # @param {type:\"string\"}\n",
- "if not evaluation_checkpoint_dir:\n",
- " evaluation_checkpoint_dir = final_checkpoint\n",
- "\n",
- "# @markdown Evaluation tasks to run.\n",
- "eval_tasks = \"coqa\" # @param {type:\"string\"}\n",
- "# @markdown Model to use for evaluation.\n",
- "model = \"vllm\" # @param {type:\"string\"}\n",
- "# @markdown Batch size for evaluation.\n",
- "batch_size = \"auto\" # @param {type:\"string\"}\n",
- "apply_chat_template = True if \"-Instruct\" in pretrained_model_id else False\n",
- "gpu_memory_utilization = 0.9\n",
- "max_model_len = 8192 # Maximum context length.\n",
- "eval_output_dir = os.path.join(base_output_dir, \"lm_eval\")\n",
- "\n",
- "model_args = f\"tensor_parallel_size={eval_accelerator_count},max_model_len={max_model_len},gpu_memory_utilization={gpu_memory_utilization},enforce_eager=True\"\n",
- "\n",
- "lm_eval_job_args = [\n",
- " \"--task=lm_eval\",\n",
- " f\"--model={model}\",\n",
- " f\"--eval_tasks={eval_tasks}\",\n",
- " f\"--pretrained_model_name_or_path={pretrained_model_id}\",\n",
- " f\"--model_args={model_args}\",\n",
- " f'--lora_path={evaluation_checkpoint_dir.rstrip(\"/\")}',\n",
- " f\"--output_dir={eval_output_dir}\",\n",
- " f\"--apply_chat_template={apply_chat_template}\",\n",
- " f\"--batch_size={batch_size}\",\n",
- " f\"--huggingface_access_token={HF_TOKEN}\",\n",
- "]\n",
- "\n",
- "if RUN_EVALUATION:\n",
- " common_util.check_quota(\n",
- " project_id=PROJECT_ID,\n",
- " region=REGION,\n",
- " accelerator_type=eval_accelerator_type,\n",
- " accelerator_count=eval_accelerator_count,\n",
- " is_for_training=True,\n",
- " is_dynamic_workload_scheduler=True,\n",
- " )\n",
- "\n",
- " # Pass evaluation arguments and launch job.\n",
- " lm_eval_job = aiplatform.CustomContainerTrainingJob(\n",
- " display_name=common_util.get_job_name_with_datetime(\"llama3_3-lm-eval\"),\n",
- " container_uri=LM_EVAL_DOCKER_URI,\n",
- " labels=labels,\n",
- " )\n",
- "\n",
- " print(\"Running evaluation job with args:\")\n",
- " print(\" \\\\\\n\".join(lm_eval_job_args))\n",
- " lm_eval_job.run(\n",
- " args=lm_eval_job_args,\n",
- " replica_count=1,\n",
- " machine_type=eval_machine_type,\n",
- " accelerator_type=eval_accelerator_type,\n",
- " accelerator_count=eval_accelerator_count,\n",
- " boot_disk_size_gb=boot_disk_size_gb,\n",
- " service_account=SERVICE_ACCOUNT,\n",
- " base_output_dir=base_output_dir,\n",
- " **dws_kwargs,\n",
- " )\n",
- "\n",
- "# @markdown Click \"Show Code\" to see more details."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "qmHW6m8xG_4U"
- },
- "outputs": [],
- "source": [
- "# @title Deploy\n",
- "# @markdown This section uploads the model to Model Registry and deploys it on the Endpoint. It takes 15 minutes to 1 hour to finish.\n",
- "\n",
- "# The pre-built serving docker image for vLLM.\n",
- "VLLM_DOCKER_URI = \"us-docker.pkg.dev/vertex-ai/vertex-vision-model-garden-dockers/pytorch-vllm-serve:20250116_0916_RC00\"\n",
- "\n",
- "serve_accelerator_type = \"NVIDIA_H100_80GB\" # @param [\"NVIDIA_H100_80GB\", \"NVIDIA_L4\"]\n",
- "\n",
- "# @markdown Find Vertex AI prediction supported accelerators and regions at https://cloud.google.com/vertex-ai/docs/predictions/configure-compute.\n",
- "if serve_accelerator_type == \"NVIDIA_L4\":\n",
- " serve_machine_type = \"g2-standard-96\"\n",
- " serve_accelerator_count = 8\n",
- "elif serve_accelerator_type == \"NVIDIA_H100_80GB\":\n",
- " serve_machine_type = \"a3-highgpu-4g\"\n",
- " serve_accelerator_count = 4\n",
- "else:\n",
- " raise ValueError(\n",
- " f\"Recommended GPU setting not found for: {serve_accelerator_type}.\"\n",
- " )\n",
- "\n",
- "common_util.check_quota(\n",
- " project_id=PROJECT_ID,\n",
- " region=REGION,\n",
- " accelerator_type=serve_accelerator_type,\n",
- " accelerator_count=serve_accelerator_count,\n",
- " is_for_training=False,\n",
- ")\n",
- "\n",
- "# @markdown Set `use_dedicated_endpoint` to False if you don't want to use [dedicated endpoint](https://cloud.google.com/vertex-ai/docs/general/deployment#create-dedicated-endpoint).\n",
- "use_dedicated_endpoint = True # @param {type:\"boolean\"}\n",
- "\n",
- "gpu_memory_utilization = 0.95\n",
- "max_model_len = 8192 # Maximum context length.\n",
- "\n",
- "# Ensure max_model_len does not exceed the limit.\n",
- "if max_model_len > 8192:\n",
- " raise ValueError(\"max_model_len cannot exceed 8192\")\n",
- "\n",
- "\n",
- "def get_deploy_source() -> str:\n",
- " \"\"\"Gets deploy_source string based on running environment.\"\"\"\n",
- " vertex_product = os.environ.get(\"VERTEX_PRODUCT\", \"\")\n",
- " if vertex_product == \"COLAB_ENTERPRISE\":\n",
- " return \"notebook_colab_enterprise\"\n",
- " elif vertex_product == \"WORKBENCH_INSTANCE\":\n",
- " return \"notebook_workbench\"\n",
- " else:\n",
- " # Legacy workbench, legacy colab, or other custom environments.\n",
- " return \"notebook_environment_unspecified\"\n",
- "\n",
- "\n",
- "def deploy_model_vllm(\n",
- " model_name: str,\n",
- " model_id: str,\n",
- " publisher: str,\n",
- " publisher_model_id: str,\n",
- " service_account: str,\n",
- " base_model_id: str = None,\n",
- " machine_type: str = \"g2-standard-8\",\n",
- " accelerator_type: str = \"NVIDIA_L4\",\n",
- " accelerator_count: int = 1,\n",
- " gpu_memory_utilization: float = 0.9,\n",
- " max_model_len: int = 4096,\n",
- " dtype: str = \"auto\",\n",
- " enable_trust_remote_code: bool = False,\n",
- " enforce_eager: bool = False,\n",
- " enable_lora: bool = False,\n",
- " enable_chunked_prefill: bool = False,\n",
- " enable_prefix_cache: bool = False,\n",
- " host_prefix_kv_cache_utilization_target: float = 0.0,\n",
- " max_loras: int = 1,\n",
- " max_cpu_loras: int = 8,\n",
- " use_dedicated_endpoint: bool = False,\n",
- " max_num_seqs: int = 256,\n",
- " model_type: str = None,\n",
- " enable_llama_tool_parser: bool = False,\n",
- " is_spot: bool = False,\n",
- ") -> Tuple[aiplatform.Model, aiplatform.Endpoint]:\n",
- " \"\"\"Deploys trained models with vLLM into Vertex AI.\"\"\"\n",
- " endpoint = aiplatform.Endpoint.create(\n",
- " display_name=f\"{model_name}-endpoint\",\n",
- " dedicated_endpoint_enabled=use_dedicated_endpoint,\n",
- " )\n",
- "\n",
- " if not base_model_id:\n",
- " base_model_id = model_id\n",
- "\n",
- " # See https://docs.vllm.ai/en/latest/models/engine_args.html for a list of possible arguments with descriptions.\n",
- " vllm_args = [\n",
- " \"python\",\n",
- " \"-m\",\n",
- " \"vllm.entrypoints.api_server\",\n",
- " \"--host=0.0.0.0\",\n",
- " \"--port=8080\",\n",
- " f\"--model={model_id}\",\n",
- " f\"--tensor-parallel-size={accelerator_count}\",\n",
- " \"--swap-space=16\",\n",
- " f\"--max-model-len={max_model_len}\",\n",
- " f\"--dtype={dtype}\",\n",
- " f\"--max-loras={max_loras}\",\n",
- " f\"--max-cpu-loras={max_cpu_loras}\",\n",
- " f\"--max-num-seqs={max_num_seqs}\",\n",
- " \"--disable-log-stats\",\n",
- " ]\n",
- "\n",
- " if gpu_memory_utilization:\n",
- " vllm_args.append(f\"--gpu-memory-utilization={gpu_memory_utilization}\")\n",
- "\n",
- " if enable_trust_remote_code:\n",
- " vllm_args.append(\"--trust-remote-code\")\n",
- "\n",
- " if enforce_eager:\n",
- " vllm_args.append(\"--enforce-eager\")\n",
- "\n",
- " if enable_lora:\n",
- " vllm_args.append(\"--enable-lora\")\n",
- "\n",
- " if enable_chunked_prefill:\n",
- " vllm_args.append(\"--enable-chunked-prefill\")\n",
- "\n",
- " if enable_prefix_cache:\n",
- " vllm_args.append(\"--enable-prefix-caching\")\n",
- "\n",
- " if 0 < host_prefix_kv_cache_utilization_target < 1:\n",
- " vllm_args.append(\n",
- " f\"--host-prefix-kv-cache-utilization-target={host_prefix_kv_cache_utilization_target}\"\n",
- " )\n",
- "\n",
- " if model_type:\n",
- " vllm_args.append(f\"--model-type={model_type}\")\n",
- "\n",
- " if enable_llama_tool_parser:\n",
- " vllm_args.append(\"--enable-auto-tool-choice\")\n",
- " vllm_args.append(\"--tool-call-parser=vertex-llama-3\")\n",
- "\n",
- " env_vars = {\n",
- " \"MODEL_ID\": base_model_id,\n",
- " \"DEPLOY_SOURCE\": \"notebook\",\n",
- " }\n",
- "\n",
- " # HF_TOKEN is not a compulsory field and may not be defined.\n",
- " try:\n",
- " if HF_TOKEN:\n",
- " env_vars[\"HF_TOKEN\"] = HF_TOKEN\n",
- " except NameError:\n",
- " pass\n",
- "\n",
- " model = aiplatform.Model.upload(\n",
- " display_name=model_name,\n",
- " serving_container_image_uri=VLLM_DOCKER_URI,\n",
- " serving_container_args=vllm_args,\n",
- " serving_container_ports=[8080],\n",
- " serving_container_predict_route=\"/generate\",\n",
- " serving_container_health_route=\"/ping\",\n",
- " serving_container_environment_variables=env_vars,\n",
- " serving_container_shared_memory_size_mb=(16 * 1024), # 16 GB\n",
- " serving_container_deployment_timeout=7200,\n",
- " model_garden_source_model_name=(\n",
- " f\"publishers/{publisher}/models/{publisher_model_id}\"\n",
- " ),\n",
- " )\n",
- " print(\n",
- " f\"Deploying {model_name} on {machine_type} with {accelerator_count} {accelerator_type} GPU(s).\"\n",
- " )\n",
- " model.deploy(\n",
- " endpoint=endpoint,\n",
- " machine_type=machine_type,\n",
- " accelerator_type=accelerator_type,\n",
- " accelerator_count=accelerator_count,\n",
- " deploy_request_timeout=1800,\n",
- " service_account=service_account,\n",
- " spot=is_spot,\n",
- " system_labels={\n",
- " \"NOTEBOOK_NAME\": \"model_garden_pytorch_llama3_3_finetuning.ipynb\",\n",
- " \"NOTEBOOK_ENVIRONMENT\": get_deploy_source(),\n",
- " },\n",
- " )\n",
- " print(\"endpoint_name:\", endpoint.name)\n",
- "\n",
- " return model, endpoint\n",
- "\n",
- "\n",
- "def predict_vllm(\n",
- " prompt: str,\n",
- " max_tokens: int,\n",
- " temperature: float,\n",
- " top_p: float,\n",
- " top_k: int,\n",
- " raw_response: bool,\n",
- " lora_weight: str = \"\",\n",
- "):\n",
- " # Parameters for inference.\n",
- " instance = {\n",
- " \"prompt\": prompt,\n",
- " \"max_tokens\": max_tokens,\n",
- " \"temperature\": temperature,\n",
- " \"top_p\": top_p,\n",
- " \"top_k\": top_k,\n",
- " \"raw_response\": raw_response,\n",
- " }\n",
- " if lora_weight:\n",
- " instance[\"dynamic-lora\"] = lora_weight\n",
- " instances = [instance]\n",
- " response = endpoints[\"vllm_gpu\"].predict(\n",
- " instances=instances, use_dedicated_endpoint=use_dedicated_endpoint\n",
- " )\n",
- "\n",
- " for prediction in response.predictions:\n",
- " print(prediction)\n",
- "\n",
- "\n",
- "deploy_pretrained_model_id = pretrained_model_id\n",
- "print(\"Deploying model in:\", deploy_pretrained_model_id)\n",
- "\n",
- "models[\"vllm_gpu\"], endpoints[\"vllm_gpu\"] = deploy_model_vllm(\n",
- " model_name=common_util.get_job_name_with_datetime(prefix=\"llama3_3-vllm-serve\"),\n",
- " model_id=deploy_pretrained_model_id,\n",
- " publisher=\"meta\",\n",
- " publisher_model_id=\"llama3-3\",\n",
- " service_account=SERVICE_ACCOUNT,\n",
- " machine_type=serve_machine_type,\n",
- " accelerator_type=serve_accelerator_type,\n",
- " accelerator_count=serve_accelerator_count,\n",
- " gpu_memory_utilization=gpu_memory_utilization,\n",
- " max_model_len=max_model_len,\n",
- " enable_lora=True,\n",
- " use_dedicated_endpoint=use_dedicated_endpoint,\n",
- ")\n",
- "\n",
- "# @markdown Click \"Show Code\" to see more details."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "2UYUNn60G_4U"
- },
- "outputs": [],
- "source": [
- "# @title Predict\n",
- "\n",
- "# @markdown Once deployment succeeds, you can send requests to the endpoint with text prompts. Sampling parameters supported by vLLM can be found [here](https://docs.vllm.ai/en/latest/dev/sampling_params.html).\n",
- "\n",
- "# @markdown Example:\n",
- "\n",
- "# @markdown ```\n",
- "# @markdown Human: What is a car?\n",
- "# @markdown Assistant: A car, or a motor car, is a road-connected human-transportation system used to move people or goods from one place to another. The term also encompasses a wide range of vehicles, including motorboats, trains, and aircrafts. Cars typically have four wheels, a cabin for passengers, and an engine or motor. They have been around since the early 19th century and are now one of the most popular forms of transportation, used for daily commuting, shopping, and other purposes.\n",
- "# @markdown ```\n",
- "\n",
- "# @markdown Additionally, you can moderate the generated text with Vertex AI. See [Moderate text documentation](https://cloud.google.com/natural-language/docs/moderating-text) for more details.\n",
- "\n",
- "prompt = \"What is a car?\" # @param {type: \"string\"}\n",
- "# @markdown If you encounter the issue like `ServiceUnavailable: 503 Took too long to respond when processing`, you can reduce the maximum number of output tokens, such as set `max_tokens` as 20.\n",
- "max_tokens = 50 # @param {type:\"integer\"}\n",
- "temperature = 1.0 # @param {type:\"number\"}\n",
- "top_p = 1.0 # @param {type:\"number\"}\n",
- "top_k = 1 # @param {type:\"integer\"}\n",
- "raw_response = False # @param {type:\"boolean\"}\n",
- "\n",
- "predict_vllm(\n",
- " prompt=prompt,\n",
- " max_tokens=max_tokens,\n",
- " temperature=temperature,\n",
- " top_p=top_p,\n",
- " top_k=top_k,\n",
- " raw_response=raw_response,\n",
- " lora_weight=final_checkpoint,\n",
- ")\n",
- "\n",
- "# @markdown Click \"Show Code\" to see more details."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "af21a3cff1e0"
- },
- "source": [
- "## Clean up resources"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "911406c1561e"
- },
- "outputs": [],
- "source": [
- "# @title Delete the model and endpoint\n",
- "\n",
- "if train_job:\n",
- " train_job.delete()\n",
- "if RUN_EVALUATION and lm_eval_job:\n",
- " lm_eval_job.delete()\n",
- "\n",
- "# @markdown Delete the experiment models and endpoints to recycle the resources\n",
- "# @markdown and avoid unnecessary continuous charges that may incur.\n",
- "\n",
- "# Undeploy model and delete endpoint.\n",
- "for endpoint in endpoints.values():\n",
- " endpoint.delete(force=True)\n",
- "\n",
- "# Delete models.\n",
- "for model in models.values():\n",
- " model.delete()\n",
- "\n",
- "delete_bucket = False # @param {type:\"boolean\"}\n",
- "if delete_bucket:\n",
- " ! gsutil -m rm -r $BUCKET_NAME"
- ]
- }
- ],
- "metadata": {
- "colab": {
- "name": "model_garden_pytorch_llama3_3_finetuning.ipynb",
- "toc_visible": true
- },
- "kernelspec": {
- "display_name": "Python 3",
- "name": "python3"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 0
-}
diff --git a/notebooks/community/model_garden/model_garden_pytorch_llama3_finetuning.ipynb b/notebooks/community/model_garden/model_garden_pytorch_llama3_finetuning.ipynb
deleted file mode 100644
index 70240fc86..000000000
--- a/notebooks/community/model_garden/model_garden_pytorch_llama3_finetuning.ipynb
+++ /dev/null
@@ -1,857 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "7d9bbf86da5e"
- },
- "outputs": [],
- "source": [
- "# Copyright 2026 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": "99c1c3fc2ca5"
- },
- "source": [
- "# Vertex AI Model Garden - Llama 3 Finetuning\n",
- "\n",
- "\n",
- " \n",
- " \n",
- "  Run in Colab Enterprise\n",
- " \n",
- " | \n",
- " \n",
- " \n",
- "  View on GitHub\n",
- " \n",
- " | \n",
- "
"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "3de7470326a2"
- },
- "source": [
- "## Overview\n",
- "\n",
- "This notebook demonstrates finetuning and deploying Llama 3 models with Vertex AI. All of the examples in this notebook use parameter efficient finetuning methods [PEFT (LoRA)](https://github.com/huggingface/peft) to reduce training and storage costs. LoRA (Low-Rank Adaptation) is one approach of Parameter Efficient FineTuning (PEFT), where pretrained model weights are frozen and rank decomposition matrices representing the change in model weights are trained during finetuning. Read more about LoRA in the following publication: [Hu, E.J., Shen, Y., Wallis, P., Allen-Zhu, Z., Li, Y., Wang, S., Wang, L. and Chen, W., 2021. Lora: Low-rank adaptation of large language models. *arXiv preprint arXiv:2106.09685*](https://arxiv.org/abs/2106.09685).\n",
- "\n",
- "After finetuning, we can deploy models on Vertex with GPU.\n",
- "\n",
- "\n",
- "### Objective\n",
- "\n",
- "- Finetune Llama 3 models with Vertex AI Custom Training Jobs.\n",
- "- Deploy finetuned Llama 3 models on Vertex AI Prediction.\n",
- "- Send prediction requests to your finetuned Llama 3 models.\n",
- "\n",
- "\n",
- "### Costs\n",
- "\n",
- "This tutorial uses billable components of Google Cloud:\n",
- "\n",
- "* Vertex AI\n",
- "* Cloud Storage\n",
- "\n",
- "Learn about [Vertex AI pricing](https://cloud.google.com/vertex-ai/pricing), [Cloud Storage pricing](https://cloud.google.com/storage/pricing), and use the [Pricing Calculator](https://cloud.google.com/products/calculator/) to generate a cost estimate based on your projected usage."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "264c07757582"
- },
- "source": [
- "## Before you begin"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "855d6b96f291"
- },
- "outputs": [],
- "source": [
- "# @title Setup Google Cloud project\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]** [Create a Cloud Storage bucket](https://cloud.google.com/storage/docs/creating-buckets) for storing experiment outputs. Set the BUCKET_URI for the experiment environment. The specified Cloud Storage bucket (`BUCKET_URI`) should be located in the same region as where the notebook was launched. Note that a multi-region bucket (eg. \"us\") is not considered a match for a single region covered by the multi-region range (eg. \"us-central1\"). If not set, a unique GCS bucket will be created instead.\n",
- "\n",
- "BUCKET_URI = \"gs://\" # @param {type:\"string\"}\n",
- "\n",
- "# @markdown 3. **[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 4. 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",
- "# @markdown 5. [Make sure that you have GPU quota for Vertex Training (finetuing) and Vertex Prediction (serving)](https://cloud.google.com/docs/quotas/view-manage). The quota name for Vertex Training is \"Custom model training your-gpu-type per region\" and the quota name for Vertex Prediction is \"Custom model serving your-gpu-type per region\" such as `Custom model training Nvidia L4 GPUs per region` and `Custom model serving Nvidia L4 GPUs per region` for L4 GPUs. [Submit a quota increase request](https://cloud.google.com/docs/quotas/view-manage#requesting_higher_quota) if additional quota is needed. At minimum, running this notebook requires 4 L4s for finetuning and 1 L4 for serving. More GPUs may be needed for larger models and different finetuning configurations. To secure GPUs for larger models, ask your customer engineer to get you allowlisted for a Shared Reservation or a Dynamic Workload Scheduler.\n",
- "\n",
- "# Import the necessary packages\n",
- "# Upgrade Vertex AI SDK.\n",
- "! pip3 install --upgrade --quiet 'google-cloud-aiplatform>=1.64.0'\n",
- "! git clone https://github.com/GoogleCloudPlatform/vertex-ai-samples.git\n",
- "\n",
- "import datetime\n",
- "import importlib\n",
- "import os\n",
- "import uuid\n",
- "from typing import Tuple\n",
- "\n",
- "from google.cloud import aiplatform\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",
- "models, endpoints = {}, {}\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",
- " if not os.environ.get(\"GOOGLE_CLOUD_REGION\"):\n",
- " raise ValueError(\n",
- " \"REGION must be set. See\"\n",
- " \" https://cloud.google.com/vertex-ai/docs/general/locations for\"\n",
- " \" available cloud locations.\"\n",
- " )\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",
- "# Cloud Storage bucket for storing the experiment artifacts.\n",
- "# A unique GCS bucket will be created for the purpose of this notebook. If you\n",
- "# prefer using your own GCS bucket, change the value yourself below.\n",
- "now = datetime.datetime.now().strftime(\"%Y%m%d%H%M%S\")\n",
- "BUCKET_NAME = \"/\".join(BUCKET_URI.split(\"/\")[:3])\n",
- "\n",
- "if BUCKET_URI is None or BUCKET_URI.strip() == \"\" or BUCKET_URI == \"gs://\":\n",
- " BUCKET_URI = f\"gs://{PROJECT_ID}-tmp-{now}-{str(uuid.uuid4())[:4]}\"\n",
- " BUCKET_NAME = \"/\".join(BUCKET_URI.split(\"/\")[:3])\n",
- " ! gsutil mb -l {REGION} {BUCKET_URI}\n",
- "else:\n",
- " assert BUCKET_URI.startswith(\"gs://\"), \"BUCKET_URI must start with `gs://`.\"\n",
- " shell_output = ! gsutil ls -Lb {BUCKET_NAME} | grep \"Location constraint:\" | sed \"s/Location constraint://\"\n",
- " bucket_region = shell_output[0].strip().lower()\n",
- " if bucket_region != REGION:\n",
- " raise ValueError(\n",
- " \"Bucket region %s is different from notebook region %s\"\n",
- " % (bucket_region, REGION)\n",
- " )\n",
- "print(f\"Using this GCS Bucket: {BUCKET_URI}\")\n",
- "\n",
- "STAGING_BUCKET = os.path.join(BUCKET_URI, \"temporal\")\n",
- "MODEL_BUCKET = os.path.join(BUCKET_URI, \"llama3\")\n",
- "\n",
- "\n",
- "# Initialize Vertex AI API.\n",
- "print(\"Initializing Vertex AI API.\")\n",
- "aiplatform.init(project=PROJECT_ID, location=REGION, staging_bucket=STAGING_BUCKET)\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",
- "\n",
- "# Provision permissions to the SERVICE_ACCOUNT with the GCS bucket\n",
- "! gsutil iam ch serviceAccount:{SERVICE_ACCOUNT}:roles/storage.admin $BUCKET_NAME\n",
- "\n",
- "! gcloud config set project $PROJECT_ID\n",
- "! gcloud projects add-iam-policy-binding --no-user-output-enabled {PROJECT_ID} --member=serviceAccount:{SERVICE_ACCOUNT} --role=\"roles/storage.admin\"\n",
- "! gcloud projects add-iam-policy-binding --no-user-output-enabled {PROJECT_ID} --member=serviceAccount:{SERVICE_ACCOUNT} --role=\"roles/aiplatform.user\""
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "36c21f10355f"
- },
- "outputs": [],
- "source": [
- "# @title Access Llama 3 models\n",
- "\n",
- "# @markdown For GPU based finetuning and serving, choose between accessing Llama 3 models on [Hugging Face](https://huggingface.co/)\n",
- "# @markdown or Vertex AI as described below.\n",
- "\n",
- "# @markdown If you already obtained access to Llama 3 models on [Hugging Face](https://huggingface.co/), you can load models from there.\n",
- "# @markdown Alternatively, you can also load the original Llama 3 models for finetuning and serving from Vertex AI after accepting the agreement.\n",
- "\n",
- "# @markdown **Only select and fill one of the following sections.**\n",
- "# fmt: off\n",
- "LOAD_MODEL_FROM = \"Hugging Face\" # @param [\"Hugging Face\", \"Google Cloud\"] {isTemplate:true}\n",
- "# fmt: on\n",
- "\n",
- "# @markdown ---\n",
- "\n",
- "# @markdown ### Access Llama 3 models on Hugging Face for GPU based finetuning and serving\n",
- "# @markdown You must provide a Hugging Face User Access Token (with read access) to access the Llama 3 models. You can follow the [Hugging Face documentation](https://huggingface.co/docs/hub/en/security-tokens) to create a **read** access token and put it in the `HF_TOKEN` field below.\n",
- "\n",
- "HF_TOKEN = \"\" # @param {type:\"string\", isTemplate:true}\n",
- "if LOAD_MODEL_FROM == \"Hugging Face\":\n",
- " assert (\n",
- " HF_TOKEN\n",
- " ), \"Provide a read HF_TOKEN to load models from Hugging Face, or select a different model source.\"\n",
- "\n",
- "# @markdown *--- Or ---*\n",
- "# @markdown ### Access Llama 3 models on Vertex AI for GPU based serving\n",
- "# @markdown The original models from Meta are converted into the Hugging Face format for serving in Vertex AI.\n",
- "# @markdown Accept the model agreement to access the models:\n",
- "# @markdown 1. Open the [Llama 3 model card](https://console.cloud.google.com/vertex-ai/publishers/meta/model-garden/llama3) from [Vertex AI Model Garden](https://cloud.google.com/model-garden).\n",
- "# @markdown 2. Review and accept the agreement in the pop-up window on the model card page. If you have previously accepted the model agreement, there will not be a pop-up window on the model card page and this step is not needed.\n",
- "# @markdown 3. After accepting the agreement of Llama 3, a `gs://` URI containing Llama 3 pretrained and finetuned models will be shared.\n",
- "# @markdown 4. Paste the URI in the `VERTEX_AI_MODEL_GARDEN_LLAMA3` field below.\n",
- "\n",
- "VERTEX_AI_MODEL_GARDEN_LLAMA3 = \"\" # @param {type:\"string\", isTemplate:true}\n",
- "\n",
- "if LOAD_MODEL_FROM == \"Google Cloud\":\n",
- " assert (\n",
- " VERTEX_AI_MODEL_GARDEN_LLAMA3\n",
- " ), \"Click the agreement of Llama 3 in Vertex AI Model Garden, and get the GCS path of Llama 3 model artifacts.\"\n",
- " print(\n",
- " \"Copying Llama 3 model artifacts from\",\n",
- " VERTEX_AI_MODEL_GARDEN_LLAMA3,\n",
- " \"to \",\n",
- " MODEL_BUCKET,\n",
- " )\n",
- " HF_TOKEN = \"\"\n",
- "\n",
- " ! gsutil -m cp -R $VERTEX_AI_MODEL_GARDEN_LLAMA3/* $MODEL_BUCKET\n",
- "\n",
- "# @markdown ---"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "cb56d402e84a"
- },
- "source": [
- "## Finetune with HuggingFace PEFT and deploy with vLLM on GPUs"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "KwAW99YZHTdy"
- },
- "outputs": [],
- "source": [
- "# @title Set dataset\n",
- "\n",
- "# @markdown Use the Vertex AI SDK to create and run the custom training jobs.\n",
- "\n",
- "# @markdown This notebook uses [timdettmers/openassistant-guanaco](https://huggingface.co/datasets/timdettmers/openassistant-guanaco) dataset as an example.\n",
- "# @markdown You can set `dataset_name` to any existing [Hugging Face dataset](https://huggingface.co/datasets) name, and set `instruct_column_in_dataset` to the name of the dataset column containing training data. The [timdettmers/openassistant-guanaco](https://huggingface.co/datasets/timdettmers/openassistant-guanaco) has only one column `text`, and therefore we set `instruct_column_in_dataset` to `text` in this notebook.\n",
- "\n",
- "# @markdown ### (Optional) Prepare a custom JSONL dataset for finetuning\n",
- "\n",
- "# @markdown You can prepare a JSONL file where each line is a valid JSON string as your custom training dataset. For example, here is one line from the [timdettmers/openassistant-guanaco](https://huggingface.co/datasets/timdettmers/openassistant-guanaco) dataset:\n",
- "# @markdown ```\n",
- "# @markdown {\"text\": \"### Human: Hola### Assistant: \\u00a1Hola! \\u00bfEn qu\\u00e9 puedo ayudarte hoy?\"}\n",
- "# @markdown ```\n",
- "\n",
- "# @markdown The JSON object has a key `text`, which should match `instruct_column_in_dataset`; The value should be one training data point, i.e. a string. After you prepared your JSONL file, you can either upload it to [Hugging Face datasets](https://huggingface.co/datasets) or [Google Cloud Storage](https://cloud.google.com/storage).\n",
- "\n",
- "# @markdown - To upload a JSONL dataset to [Hugging Face datasets](https://huggingface.co/datasets), follow the instructions on [Uploading Datasets](https://huggingface.co/docs/hub/en/datasets-adding). Then, set `dataset_name` to the name of your newly created dataset on Hugging Face.\n",
- "\n",
- "# @markdown - To upload a JSONL dataset to [Google Cloud Storage](https://cloud.google.com/storage), follow the instructions on [Upload objects from a filesystem](https://cloud.google.com/storage/docs/uploading-objects). Then, set `dataset_name` to the `gs://` URI to your JSONL file. For example: `gs://cloud-samples-data/vertex-ai/model-evaluation/peft_train_sample.jsonl`.\n",
- "\n",
- "# @markdown Optionally update the `instruct_column_in_dataset` field below if your JSON objects use a key other than the default `text`.\n",
- "\n",
- "# @markdown ### (Optional) Format your data with custom JSON template\n",
- "\n",
- "# @markdown Sometimes, your dataset might have multiple text columns and you want to construct the training data with a template. You can prepare a JSON template in the following format:\n",
- "\n",
- "# @markdown ```\n",
- "# @markdown {\n",
- "# @markdown \"description\": \"Template used by Llama 3, accepting text-bison format.\",\n",
- "# @markdown \"source\": \"https://cloud.google.com/vertex-ai/generative-ai/docs/models/tune-text-models-supervised#dataset-format\",\n",
- "# @markdown \"prompt_input\": \"<|start_header_id|>user<|end_header_id|>\\n\\n{input_text}<|eot_id|><|start_header_id|>assistant<|end_header_id|>\\n\\n{output_text}<|eot_id|>\",\n",
- "# @markdown \"instruction_separator\": \"<|start_header_id|>user<|end_header_id|>\\n\\n\",\n",
- "# @markdown \"response_separator\": \"<|start_header_id|>assistant<|end_header_id|>\\n\\n\"\n",
- "# @markdown }\n",
- "# @markdown ```\n",
- "\n",
- "# @markdown As an example, the template above can be used to format the following training data (this line comes from `gs://cloud-samples-data/vertex-ai/model-evaluation/peft_train_sample.jsonl`):\n",
- "\n",
- "# @markdown ```\n",
- "# @markdown {\"input_text\":\"TRANSCRIPT: \\nREASON FOR EVALUATION:,\\n\\n LABEL:\",\"output_text\":\"Chiropractic\"}\n",
- "# @markdown ```\n",
- "\n",
- "# @markdown This example template simply concatenates `input_text` with `output_text` with some special tokens in between.\n",
- "# @markdown\n",
- "# @markdown To try such custom dataset, you can make the following changes:\n",
- "# @markdown 1. Set `template` to `llama3-text-bison`\n",
- "# @markdown 1. Set `train_dataset_name` to `gs://cloud-samples-data/vertex-ai/model-evaluation/peft_train_sample.jsonl`\n",
- "# @markdown 1. Set `train_split_name` to `train`\n",
- "# @markdown 1. Set `eval_dataset_name` to `gs://cloud-samples-data/vertex-ai/model-evaluation/peft_eval_sample.jsonl`\n",
- "# @markdown 1. Set `eval_split_name` to `train` (**NOT** `test`)\n",
- "# @markdown 1. Set `instruct_column_in_dataset` as `input_text`.\n",
- "\n",
- "# Template name or gs:// URI to a custom template.\n",
- "template = \"openassistant-guanaco\" # @param {type:\"string\"}\n",
- "\n",
- "# Hugging Face dataset name or gs:// URI to a custom JSONL dataset.\n",
- "train_dataset_name = \"timdettmers/openassistant-guanaco\" # @param {type:\"string\"}\n",
- "train_split_name = \"train\" # @param {type:\"string\"}\n",
- "eval_dataset_name = \"timdettmers/openassistant-guanaco\" # @param {type:\"string\"}\n",
- "eval_split_name = \"test\" # @param {type:\"string\"}\n",
- "\n",
- "# Name of the dataset column containing training text input.\n",
- "instruct_column_in_dataset = \"text\" # @param {type:\"string\"}"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "ivVGS9dHXPOz"
- },
- "outputs": [],
- "source": [
- "# @title Finetune\n",
- "# @markdown Use the Vertex AI SDK to create and run the custom training jobs.\n",
- "\n",
- "# @markdown **Note**:\n",
- "# @markdown 1. We recommend setting `finetuning_precision_mode` to `4bit` because it enables using fewer hardware resources for finetuning.\n",
- "# @markdown 1. We recommend using NVIDIA_L4 for 8B models and NVIDIA_A100_80GB for 70B models.\n",
- "# @markdown 1. If `max_steps>0`, it will precedence over `epochs`. One can set a small `max_steps` value to quickly check the pipeline.\n",
- "# @markdown 1. With the default setting, training takes between 1.5 ~ 2 hours.\n",
- "\n",
- "TRAIN_DOCKER_URI = \"us-docker.pkg.dev/vertex-ai/vertex-vision-model-garden-dockers/pytorch-peft-train:stable_20240909\"\n",
- "\n",
- "\n",
- "# The Llama 3 base model.\n",
- "MODEL_ID = \"meta-llama/Meta-Llama-3-8B-Instruct\" # @param [\"meta-llama/Meta-Llama-3-8B\", \"meta-llama/Meta-Llama-3-8B-Instruct\", \"meta-llama/Meta-Llama-3-70B\", \"meta-llama/Meta-Llama-3-70B-Instruct\"] {isTemplate:true}\n",
- "if LOAD_MODEL_FROM == \"Google Cloud\":\n",
- " if MODEL_ID == \"meta-llama/Meta-Llama-3-8B\":\n",
- " base_model_id = \"llama3-8b-hf\"\n",
- " elif MODEL_ID == \"meta-llama/Meta-Llama-3-8B-Instruct\":\n",
- " base_model_id = \"llama3-8b-chat-hf\"\n",
- " elif MODEL_ID == \"meta-llama/Meta-Llama-3-70B\":\n",
- " base_model_id = \"llama3-70b-hf\"\n",
- " elif MODEL_ID == \"meta-llama/Meta-Llama-3-70B-Instruct\":\n",
- " base_model_id = \"llama3-70b-chat-hf\"\n",
- " else:\n",
- " raise ValueError(f\"Undefined model ID: {MODEL_ID}.\")\n",
- " base_model_id = os.path.join(MODEL_BUCKET, base_model_id)\n",
- "else:\n",
- " base_model_id = MODEL_ID\n",
- "\n",
- "# The accelerator to use.\n",
- "accelerator_type = \"NVIDIA_L4\" # @param [\"NVIDIA_L4\", \"NVIDIA_A100_80GB\"]\n",
- "\n",
- "# Batch size for finetuning.\n",
- "per_device_train_batch_size = 1 # @param{type:\"integer\"}\n",
- "gradient_accumulation_steps = 8 # @param{type:\"integer\"}\n",
- "# Maximum sequence length.\n",
- "max_seq_length = 4096 # @param{type:\"integer\"}\n",
- "# Setting a positive `max_steps` here will override `num_epochs`\n",
- "max_steps = -1 # @param{type:\"integer\"}\n",
- "num_epochs = 1.0 # @param{type:\"number\"}\n",
- "# Precision mode for finetuning.\n",
- "finetuning_precision_mode = \"4bit\" # @param [\"4bit\", \"8bit\", \"float16\"]\n",
- "# Learning rate.\n",
- "learning_rate = 5e-5 # @param{type:\"number\"}\n",
- "lr_scheduler_type = \"cosine\" # @param{type:\"string\"}\n",
- "# LoRA parameters.\n",
- "lora_rank = 16 # @param{type:\"integer\"}\n",
- "lora_alpha = 32 # @param{type:\"integer\"}\n",
- "lora_dropout = 0.05 # @param{type:\"number\"}\n",
- "enable_gradient_checkpointing = True\n",
- "attn_implementation = \"flash_attention_2\"\n",
- "optimizer = \"paged_adamw_32bit\"\n",
- "warmup_ratio = \"0.01\"\n",
- "report_to = \"tensorboard\"\n",
- "save_steps = 10\n",
- "logging_steps = save_steps\n",
- "\n",
- "# Worker pool spec.\n",
- "machine_type = None\n",
- "if \"8b\" in MODEL_ID.lower():\n",
- " if accelerator_type == \"NVIDIA_L4\":\n",
- " accelerator_count = 4\n",
- " machine_type = \"g2-standard-48\"\n",
- " else:\n",
- " raise ValueError(\n",
- " f\"Recommended machine settings not found for: {accelerator_type}. To use another accelerator, edit this code block to pass in an appropriate `machine_type`, `accelerator_type`, and `accelerator_count` to the deploy_model_vllm function by clicking `Show Code` and then modifying the code.\"\n",
- " )\n",
- "elif \"70b\" in MODEL_ID.lower():\n",
- " if accelerator_type == \"NVIDIA_A100_80GB\":\n",
- " accelerator_count = 4\n",
- " machine_type = \"a2-ultragpu-4g\"\n",
- " else:\n",
- " raise ValueError(\n",
- " f\"Recommended machine settings not found for: {accelerator_type}. To use another accelerator, edit this code block to pass in an appropriate `machine_type`, `accelerator_type`, and `accelerator_count` to the deploy_model_vllm function by clicking `Show Code` and then modifying the code.\"\n",
- " )\n",
- "else:\n",
- " raise ValueError(f\"Unsupported model ID or GCS path: {MODEL_ID}.\")\n",
- "\n",
- "replica_count = 1\n",
- "\n",
- "common_util.check_quota(\n",
- " project_id=PROJECT_ID,\n",
- " region=REGION,\n",
- " accelerator_type=accelerator_type,\n",
- " accelerator_count=accelerator_count,\n",
- " is_for_training=True,\n",
- ")\n",
- "\n",
- "job_name = common_util.get_job_name_with_datetime(\"llama3-lora-train\")\n",
- "\n",
- "base_output_dir = os.path.join(STAGING_BUCKET, job_name)\n",
- "# Create a GCS folder to store the LORA adapter.\n",
- "lora_output_dir = os.path.join(base_output_dir, \"adapter\")\n",
- "# Create a GCS folder to store the merged model with the base model and the\n",
- "# finetuned LORA adapter.\n",
- "merged_model_output_dir = os.path.join(base_output_dir, \"merged-model\")\n",
- "\n",
- "# Add labels for the finetuning job.\n",
- "labels = {\n",
- " \"mg-source\": \"notebook\",\n",
- " \"mg-notebook-name\": \"model_garden_pytorch_llama3_finetuning.ipynb\".split(\".\")[0],\n",
- "}\n",
- "\n",
- "labels[\"mg-tune\"] = \"publishers-meta-models-llama3\"\n",
- "versioned_model_id = base_model_id.split(\"/\")[1].lower().replace(\".\", \"-\")\n",
- "labels[\"versioned-mg-tune\"] = f\"{labels['mg-tune']}-{versioned_model_id}\"\n",
- "\n",
- "eval_args = [\n",
- " f\"--eval_dataset_path={eval_dataset_name}\",\n",
- " f\"--eval_column={instruct_column_in_dataset}\",\n",
- " f\"--eval_template={template}\",\n",
- " f\"--eval_split={eval_split_name}\",\n",
- " f\"--eval_steps={save_steps}\",\n",
- " \"--eval_tasks=builtin_eval\",\n",
- " \"--eval_metric_name=loss\",\n",
- "]\n",
- "\n",
- "train_job_args = [\n",
- " \"--config_file=vertex_vision_model_garden_peft/deepspeed_zero2_4gpu.yaml\",\n",
- " \"--task=instruct-lora\",\n",
- " \"--completion_only=True\",\n",
- " f\"--pretrained_model_id={base_model_id}\",\n",
- " f\"--dataset_name={train_dataset_name}\",\n",
- " f\"--train_split_name={train_split_name}\",\n",
- " f\"--instruct_column_in_dataset={instruct_column_in_dataset}\",\n",
- " f\"--output_dir={lora_output_dir}\",\n",
- " f\"--merge_base_and_lora_output_dir={merged_model_output_dir}\",\n",
- " f\"--per_device_train_batch_size={per_device_train_batch_size}\",\n",
- " f\"--gradient_accumulation_steps={gradient_accumulation_steps}\",\n",
- " f\"--lora_rank={lora_rank}\",\n",
- " f\"--lora_alpha={lora_alpha}\",\n",
- " f\"--lora_dropout={lora_dropout}\",\n",
- " f\"--max_steps={max_steps}\",\n",
- " f\"--max_seq_length={max_seq_length}\",\n",
- " f\"--learning_rate={learning_rate}\",\n",
- " f\"--lr_scheduler_type={lr_scheduler_type}\",\n",
- " f\"--precision_mode={finetuning_precision_mode}\",\n",
- " f\"--enable_gradient_checkpointing={enable_gradient_checkpointing}\",\n",
- " f\"--num_epochs={num_epochs}\",\n",
- " f\"--attn_implementation={attn_implementation}\",\n",
- " f\"--optimizer={optimizer}\",\n",
- " f\"--warmup_ratio={warmup_ratio}\",\n",
- " f\"--report_to={report_to}\",\n",
- " f\"--logging_output_dir={base_output_dir}\",\n",
- " f\"--save_steps={save_steps}\",\n",
- " f\"--logging_steps={logging_steps}\",\n",
- " f\"--template={template}\",\n",
- " f\"--huggingface_access_token={HF_TOKEN}\",\n",
- "] + eval_args\n",
- "\n",
- "# Create TensorBoard\n",
- "tensorboard = aiplatform.Tensorboard.create(job_name)\n",
- "exp = aiplatform.TensorboardExperiment.create(\n",
- " tensorboard_experiment_id=job_name, tensorboard_name=tensorboard.name\n",
- ")\n",
- "\n",
- "# Pass training arguments and launch job.\n",
- "train_job = aiplatform.CustomContainerTrainingJob(\n",
- " display_name=job_name,\n",
- " container_uri=TRAIN_DOCKER_URI,\n",
- " labels=labels,\n",
- ")\n",
- "\n",
- "train_job.run(\n",
- " args=train_job_args,\n",
- " environment_variables={\"WANDB_DISABLED\": True},\n",
- " replica_count=replica_count,\n",
- " machine_type=machine_type,\n",
- " accelerator_type=accelerator_type,\n",
- " accelerator_count=accelerator_count,\n",
- " boot_disk_size_gb=500,\n",
- " service_account=SERVICE_ACCOUNT,\n",
- " tensorboard=tensorboard.resource_name,\n",
- " base_output_dir=base_output_dir,\n",
- ")\n",
- "\n",
- "print(\"LoRA adapter was saved in: \", lora_output_dir)\n",
- "print(\"Trained and merged models were saved in: \", merged_model_output_dir)\n",
- "\n",
- "# @markdown Click \"Show Code\" to see more details."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "qmHW6m8xG_4U"
- },
- "outputs": [],
- "source": [
- "# @title Deploy\n",
- "# @markdown This section uploads the model to Model Registry and deploys it on the Endpoint. It takes 15 minutes to 1 hour to finish.\n",
- "\n",
- "print(\"Deploying models in: \", merged_model_output_dir)\n",
- "\n",
- "# The pre-built serving docker image for vLLM.\n",
- "VLLM_DOCKER_URI = \"us-docker.pkg.dev/vertex-ai/vertex-vision-model-garden-dockers/pytorch-vllm-serve:20240721_0916_RC00\"\n",
- "\n",
- "accelerator_type = \"NVIDIA_H100_80GB\" # @param [\"NVIDIA_L4\", \"NVIDIA_H100_80GB\"]\n",
- "machine_type = None\n",
- "\n",
- "# @markdown Set `use_dedicated_endpoint` to False if you don't want to use [dedicated endpoint](https://cloud.google.com/vertex-ai/docs/general/deployment#create-dedicated-endpoint).\n",
- "use_dedicated_endpoint = True # @param {type:\"boolean\"}\n",
- "\n",
- "# Find Vertex AI prediction supported accelerators and regions in [here](https://cloud.google.com/vertex-ai/docs/predictions/configure-compute).\n",
- "if \"8b\" in MODEL_ID.lower():\n",
- " if accelerator_type == \"NVIDIA_L4\":\n",
- " machine_type = \"g2-standard-12\"\n",
- " accelerator_count = 1\n",
- " elif accelerator_type == \"NVIDIA_H100_80GB\":\n",
- " machine_type = \"a3-highgpu-2g\"\n",
- " accelerator_count = 2\n",
- "else:\n",
- " if accelerator_type == \"NVIDIA_L4\":\n",
- " machine_type = \"g2-standard-96\"\n",
- " accelerator_count = 8\n",
- " elif accelerator_type == \"NVIDIA_H100_80GB\":\n",
- " machine_type = \"a3-highgpu-4g\"\n",
- " accelerator_count = 4\n",
- "\n",
- "if machine_type is None:\n",
- " raise ValueError(\n",
- " f\"Recommended GPU setting not found for: {accelerator_type} and {MODEL_ID.lower()}.\"\n",
- " )\n",
- "\n",
- "common_util.check_quota(\n",
- " project_id=PROJECT_ID,\n",
- " region=REGION,\n",
- " accelerator_type=accelerator_type,\n",
- " accelerator_count=accelerator_count,\n",
- " is_for_training=False,\n",
- ")\n",
- "\n",
- "gpu_memory_utilization = 0.85\n",
- "max_model_len = 8192 # Maximum context length.\n",
- "\n",
- "# Ensure max_model_len does not exceed the limit\n",
- "if max_model_len > 8192:\n",
- " raise ValueError(\"max_model_len cannot exceed 8192\")\n",
- "\n",
- "\n",
- "def deploy_model_vllm(\n",
- " model_name: str,\n",
- " model_id: str,\n",
- " publisher: str,\n",
- " publisher_model_id: str,\n",
- " service_account: str,\n",
- " base_model_id: str = None,\n",
- " machine_type: str = \"g2-standard-8\",\n",
- " accelerator_type: str = \"NVIDIA_L4\",\n",
- " accelerator_count: int = 1,\n",
- " gpu_memory_utilization: float = 0.9,\n",
- " max_model_len: int = 4096,\n",
- " dtype: str = \"auto\",\n",
- " enable_trust_remote_code: bool = False,\n",
- " enforce_eager: bool = False,\n",
- " enable_lora: bool = False,\n",
- " enable_chunked_prefill: bool = False,\n",
- " enable_prefix_cache: bool = False,\n",
- " host_prefix_kv_cache_utilization_target: float = 0.0,\n",
- " max_loras: int = 1,\n",
- " max_cpu_loras: int = 8,\n",
- " use_dedicated_endpoint: bool = False,\n",
- " max_num_seqs: int = 256,\n",
- " model_type: str = None,\n",
- " enable_llama_tool_parser: bool = False,\n",
- ") -> Tuple[aiplatform.Model, aiplatform.Endpoint]:\n",
- " \"\"\"Deploys trained models with vLLM into Vertex AI.\"\"\"\n",
- " endpoint = aiplatform.Endpoint.create(\n",
- " display_name=f\"{model_name}-endpoint\",\n",
- " dedicated_endpoint_enabled=use_dedicated_endpoint,\n",
- " )\n",
- "\n",
- " if not base_model_id:\n",
- " base_model_id = model_id\n",
- "\n",
- " # See https://docs.vllm.ai/en/latest/models/engine_args.html for a list of possible arguments with descriptions.\n",
- " vllm_args = [\n",
- " \"python\",\n",
- " \"-m\",\n",
- " \"vllm.entrypoints.api_server\",\n",
- " \"--host=0.0.0.0\",\n",
- " \"--port=8080\",\n",
- " f\"--model={model_id}\",\n",
- " f\"--tensor-parallel-size={accelerator_count}\",\n",
- " \"--swap-space=16\",\n",
- " f\"--max-model-len={max_model_len}\",\n",
- " f\"--dtype={dtype}\",\n",
- " f\"--max-loras={max_loras}\",\n",
- " f\"--max-cpu-loras={max_cpu_loras}\",\n",
- " f\"--max-num-seqs={max_num_seqs}\",\n",
- " \"--disable-log-stats\",\n",
- " ]\n",
- "\n",
- " if gpu_memory_utilization:\n",
- " vllm_args.append(f\"--gpu-memory-utilization={gpu_memory_utilization}\")\n",
- "\n",
- " if enable_trust_remote_code:\n",
- " vllm_args.append(\"--trust-remote-code\")\n",
- "\n",
- " if enforce_eager:\n",
- " vllm_args.append(\"--enforce-eager\")\n",
- "\n",
- " if enable_lora:\n",
- " vllm_args.append(\"--enable-lora\")\n",
- "\n",
- " if enable_chunked_prefill:\n",
- " vllm_args.append(\"--enable-chunked-prefill\")\n",
- "\n",
- " if enable_prefix_cache:\n",
- " vllm_args.append(\"--enable-prefix-caching\")\n",
- "\n",
- " if 0 < host_prefix_kv_cache_utilization_target < 1:\n",
- " vllm_args.append(\n",
- " f\"--host-prefix-kv-cache-utilization-target={host_prefix_kv_cache_utilization_target}\"\n",
- " )\n",
- "\n",
- " if model_type:\n",
- " vllm_args.append(f\"--model-type={model_type}\")\n",
- "\n",
- " if enable_llama_tool_parser:\n",
- " vllm_args.append(\"--enable-auto-tool-choice\")\n",
- " vllm_args.append(\"--tool-call-parser=vertex-llama-3\")\n",
- "\n",
- " env_vars = {\n",
- " \"MODEL_ID\": base_model_id,\n",
- " \"DEPLOY_SOURCE\": \"notebook\",\n",
- " }\n",
- "\n",
- " # HF_TOKEN is not a compulsory field and may not be defined.\n",
- " try:\n",
- " if HF_TOKEN:\n",
- " env_vars[\"HF_TOKEN\"] = HF_TOKEN\n",
- " except NameError:\n",
- " pass\n",
- "\n",
- " model = aiplatform.Model.upload(\n",
- " display_name=model_name,\n",
- " serving_container_image_uri=VLLM_DOCKER_URI,\n",
- " serving_container_args=vllm_args,\n",
- " serving_container_ports=[8080],\n",
- " serving_container_predict_route=\"/generate\",\n",
- " serving_container_health_route=\"/ping\",\n",
- " serving_container_environment_variables=env_vars,\n",
- " serving_container_shared_memory_size_mb=(16 * 1024), # 16 GB\n",
- " serving_container_deployment_timeout=7200,\n",
- " model_garden_source_model_name=(\n",
- " f\"publishers/{publisher}/models/{publisher_model_id}\"\n",
- " ),\n",
- " )\n",
- " print(\n",
- " f\"Deploying {model_name} on {machine_type} with {accelerator_count} {accelerator_type} GPU(s).\"\n",
- " )\n",
- " model.deploy(\n",
- " endpoint=endpoint,\n",
- " machine_type=machine_type,\n",
- " accelerator_type=accelerator_type,\n",
- " accelerator_count=accelerator_count,\n",
- " deploy_request_timeout=1800,\n",
- " service_account=service_account,\n",
- " system_labels={\n",
- " \"NOTEBOOK_NAME\": \"model_garden_pytorch_llama3_finetuning.ipynb\",\n",
- " \"NOTEBOOK_ENVIRONMENT\": common_util.get_deploy_source(),\n",
- " },\n",
- " )\n",
- " print(\"endpoint_name:\", endpoint.name)\n",
- "\n",
- " return model, endpoint\n",
- "\n",
- "\n",
- "models[\"vllm_gpu\"], endpoints[\"vllm_gpu\"] = deploy_model_vllm(\n",
- " model_name=common_util.get_job_name_with_datetime(prefix=\"llama3-vllm-serve\"),\n",
- " model_id=merged_model_output_dir,\n",
- " publisher=\"meta\",\n",
- " publisher_model_id=\"llama3\",\n",
- " service_account=SERVICE_ACCOUNT,\n",
- " machine_type=machine_type,\n",
- " accelerator_type=accelerator_type,\n",
- " accelerator_count=accelerator_count,\n",
- " gpu_memory_utilization=gpu_memory_utilization,\n",
- " max_model_len=max_model_len,\n",
- " use_dedicated_endpoint=use_dedicated_endpoint,\n",
- ")\n",
- "\n",
- "# @markdown Click \"Show Code\" to see more details."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "2UYUNn60G_4U"
- },
- "outputs": [],
- "source": [
- "# @title Predict\n",
- "\n",
- "# @markdown Once deployment succeeds, you can send requests to the endpoint with text prompts. Sampling parameters supported by vLLM can be found [here](https://docs.vllm.ai/en/latest/dev/sampling_params.html).\n",
- "\n",
- "# @markdown Example:\n",
- "\n",
- "# @markdown ```\n",
- "# @markdown Human: What is a car?\n",
- "# @markdown Assistant: A car, or a motor car, is a road-connected human-transportation system used to move people or goods from one place to another. The term also encompasses a wide range of vehicles, including motorboats, trains, and aircrafts. Cars typically have four wheels, a cabin for passengers, and an engine or motor. They have been around since the early 19th century and are now one of the most popular forms of transportation, used for daily commuting, shopping, and other purposes.\n",
- "# @markdown ```\n",
- "# @markdown Additionally, you can moderate the generated text with Vertex AI. See [Moderate text documentation](https://cloud.google.com/natural-language/docs/moderating-text) for more details.\n",
- "\n",
- "# Loads an existing endpoint instance using the endpoint name:\n",
- "# - Using `endpoint_name = endpoint.name` allows us to get the\n",
- "# endpoint name of the endpoint `endpoint` created in the cell\n",
- "# above.\n",
- "# - Alternatively, you can set `endpoint_name = \"1234567890123456789\"` to load\n",
- "# an existing endpoint with the ID 1234567890123456789.\n",
- "# You may uncomment the code below to load an existing endpoint.\n",
- "\n",
- "# endpoint_name = \"\" # @param {type:\"string\"}\n",
- "# aip_endpoint_name = (\n",
- "# f\"projects/{PROJECT_ID}/locations/{REGION}/endpoints/{endpoint_name}\"\n",
- "# )\n",
- "# endpoint = aiplatform.Endpoint(aip_endpoint_name)\n",
- "\n",
- "prompt = \"What is a car?\" # @param {type: \"string\"}\n",
- "# @markdown If you encounter an issue like `ServiceUnavailable: 503 Took too long to respond when processing`, you can reduce the maximum number of output tokens, by lowering `max_tokens`.\n",
- "max_tokens = 50 # @param {type:\"integer\"}\n",
- "temperature = 1.0 # @param {type:\"number\"}\n",
- "top_p = 1.0 # @param {type:\"number\"}\n",
- "top_k = 1 # @param {type:\"integer\"}\n",
- "# @markdown Set `raw_response` to `True` to obtain the raw model output. Set `raw_response` to `False` to apply additional formatting in the structure of `\"Prompt:\\n{prompt.strip()}\\nOutput:\\n{output}\"`.\n",
- "raw_response = False # @param {type:\"boolean\"}\n",
- "\n",
- "# Overrides parameters for inferences.\n",
- "instances = [\n",
- " {\n",
- " \"prompt\": prompt,\n",
- " \"max_tokens\": max_tokens,\n",
- " \"temperature\": temperature,\n",
- " \"top_p\": top_p,\n",
- " \"top_k\": top_k,\n",
- " \"raw_response\": raw_response,\n",
- " },\n",
- "]\n",
- "response = endpoints[\"vllm_gpu\"].predict(\n",
- " instances=instances, use_dedicated_endpoint=use_dedicated_endpoint\n",
- ")\n",
- "\n",
- "for prediction in response.predictions:\n",
- " print(prediction)\n",
- "\n",
- "# @markdown Click \"Show Code\" to see more details."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "af21a3cff1e0"
- },
- "source": [
- "## Clean up resources"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "911406c1561e"
- },
- "outputs": [],
- "source": [
- "# @title Delete the model and endpoint\n",
- "\n",
- "train_job.delete()\n",
- "\n",
- "# @markdown Delete the experiment models and endpoints to recycle the resources\n",
- "# @markdown and avoid unnecessary continuous charges that may incur.\n",
- "\n",
- "# Undeploy model and delete endpoint.\n",
- "for endpoint in endpoints.values():\n",
- " endpoint.delete(force=True)\n",
- "\n",
- "# Delete models.\n",
- "for model in models.values():\n",
- " model.delete()\n",
- "\n",
- "delete_bucket = False # @param {type:\"boolean\"}\n",
- "if delete_bucket:\n",
- " ! gsutil -m rm -r $BUCKET_NAME"
- ]
- }
- ],
- "metadata": {
- "colab": {
- "name": "model_garden_pytorch_llama3_finetuning.ipynb",
- "toc_visible": true
- },
- "kernelspec": {
- "display_name": "Python 3",
- "name": "python3"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 0
-}
diff --git a/notebooks/community/model_garden/model_garden_pytorch_mistral_peft_tuning.ipynb b/notebooks/community/model_garden/model_garden_pytorch_mistral_peft_tuning.ipynb
deleted file mode 100644
index e4bbc744d..000000000
--- a/notebooks/community/model_garden/model_garden_pytorch_mistral_peft_tuning.ipynb
+++ /dev/null
@@ -1,913 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "iJc36RtD90jd"
- },
- "outputs": [],
- "source": [
- "# Copyright 2026 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": "b9EezHSo90jf"
- },
- "source": [
- "# Vertex AI Model Garden - Mistral-7B (PEFT)\n",
- "\n",
- "\n",
- " \n",
- " \n",
- "  Run in Colab Enterprise\n",
- " \n",
- " | \n",
- " \n",
- " \n",
- "  View on GitHub\n",
- " \n",
- " | \n",
- "
"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "ybMCVFh0_5R8"
- },
- "source": [
- "## Overview\n",
- "In this notebook you will learn how to fine tune Mistral with QLoRa and\n",
- "deploy to Vertex AI endpoint.\n",
- "\n",
- "### Objective\n",
- "\n",
- "* Finetune and merge Mistral model using PEFT training docker image.\n",
- "* Deploy the finetuned model with vLLM docker image on a Vertex AI Endpoint.\n",
- "* Run inference on the deployed Vertex AI Endpoint.\n",
- "\n",
- "### File a bug\n",
- "\n",
- "File a bug on [GitHub](https://github.com/GoogleCloudPlatform/vertex-ai-samples/issues/new) if you encounter any issue with the notebook.\n",
- "\n",
- "### Costs\n",
- "\n",
- "This tutorial uses billable components of Google Cloud:\n",
- "\n",
- "* Vertex AI\n",
- "* Cloud Storage\n",
- "\n",
- "Learn about [Vertex AI pricing](https://cloud.google.com/vertex-ai/pricing), [Cloud Storage pricing](https://cloud.google.com/storage/pricing), and use the [Pricing Calculator](https://cloud.google.com/products/calculator/) to generate a cost estimate based on your projected usage."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "vzvFJU27a8si"
- },
- "source": [
- "## Run the notebook"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "Q86d4aDSgGCu"
- },
- "outputs": [],
- "source": [
- "# @title Install Python Packages for Finetuning\n",
- "\n",
- "# @markdown 1. Install google-cloud-aiplatform package and restart the session if instructed.\n",
- "! pip install --upgrade --quiet google-cloud-aiplatform==1.130.0\n",
- "\n",
- "# @markdown 2. Install packages to validate dataset with template.\n",
- "! pip install --upgrade --quiet gcsfs==2024.3.1\n",
- "! pip install --upgrade --quiet accelerate==0.31.0\n",
- "! pip install --upgrade --quiet transformers==4.43.1\n",
- "! pip install --upgrade --quiet datasets==2.19.2\n",
- "\n",
- "# Load local tensorboard.\n",
- "%load_ext tensorboard"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "I-OjzhpyMHsu"
- },
- "outputs": [],
- "source": [
- "# @title Setup Google Cloud project\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. For finetuning, **[click here](https://console.cloud.google.com/iam-admin/quotas?location=us-central1&metric=aiplatform.googleapis.com%2Frestricted_image_training_nvidia_a100_80gb_gpus)** to check if your project already has the required 8 Nvidia A100 80 GB GPUs in the us-central1 region. If yes, then run this notebook in the us-central1 region. If you do not have 8 Nvidia A100 80 GPUs or have more GPU requirements than this, then schedule your job with Nvidia H100 GPUs via Dynamic Workload Scheduler using [these instructions](https://cloud.google.com/vertex-ai/docs/training/schedule-jobs-dws). For Dynamic Workload Scheduler, check the [us-central1](https://console.cloud.google.com/iam-admin/quotas?location=us-central1&metric=aiplatform.googleapis.com%2Fcustom_model_training_preemptible_nvidia_h100_gpus) or [europe-west4](https://console.cloud.google.com/iam-admin/quotas?location=europe-west4&metric=aiplatform.googleapis.com%2Fcustom_model_training_preemptible_nvidia_h100_gpus) quota for Nvidia H100 GPUs. If you do not have enough GPUs, then you can follow [these instructions](https://cloud.google.com/docs/quotas/view-manage#viewing_your_quota_console) to request quota.\n",
- "\n",
- "# @markdown 3. For serving, **[click here](https://console.cloud.google.com/iam-admin/quotas?location=us-central1&metric=aiplatform.googleapis.com%2Fcustom_model_serving_nvidia_l4_gpus)** to check if your project already has the required 1 L4 GPU in the us-central1 region. If yes, then run this notebook in the us-central1 region. If you need more L4 GPUs for your project, then you can follow [these instructions](https://cloud.google.com/docs/quotas/view-manage#viewing_your_quota_console) to request more. Alternatively, 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).\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",
- "# @markdown 4. **[Optional]** [Create a Cloud Storage bucket](https://cloud.google.com/storage/docs/creating-buckets) for storing experiment outputs. Set the BUCKET_URI for the experiment environment. The specified Cloud Storage bucket (`BUCKET_URI`) should be located in the same region as where the notebook was launched. Note that a multi-region bucket (eg. \"us\") is not considered a match for a single region covered by the multi-region range (eg. \"us-central1\"). If not set, a unique GCS bucket will be created instead.\n",
- "\n",
- "BUCKET_URI = \"gs://\" # @param {type:\"string\"}\n",
- "\n",
- "# @markdown 5. **[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",
- "# Import the necessary packages\n",
- "! rm -rf vertex-ai-samples && git clone https://github.com/GoogleCloudPlatform/vertex-ai-samples.git\n",
- "! cd vertex-ai-samples && git reset --hard 0727e19520cf7957bceb701c248221bd3dbe4f1f\n",
- "\n",
- "import datetime\n",
- "import importlib\n",
- "import os\n",
- "import uuid\n",
- "from typing import Tuple\n",
- "\n",
- "from google.cloud import aiplatform\n",
- "from google.cloud.aiplatform.compat.types import \\\n",
- " custom_job as gca_custom_job_compat\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",
- "models, endpoints = {}, {}\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",
- " if not os.environ.get(\"GOOGLE_CLOUD_REGION\"):\n",
- " raise ValueError(\n",
- " \"REGION must be set. See\"\n",
- " \" https://cloud.google.com/vertex-ai/docs/general/locations for\"\n",
- " \" available cloud locations.\"\n",
- " )\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",
- "# Cloud Storage bucket for storing the experiment artifacts.\n",
- "# A unique GCS bucket will be created for the purpose of this notebook. If you\n",
- "# prefer using your own GCS bucket, change the value yourself below.\n",
- "now = datetime.datetime.now().strftime(\"%Y%m%d%H%M%S\")\n",
- "BUCKET_NAME = \"/\".join(BUCKET_URI.split(\"/\")[:3])\n",
- "\n",
- "if BUCKET_URI is None or BUCKET_URI.strip() == \"\" or BUCKET_URI == \"gs://\":\n",
- " BUCKET_URI = f\"gs://{PROJECT_ID}-tmp-{now}-{str(uuid.uuid4())[:4]}\"\n",
- " BUCKET_NAME = \"/\".join(BUCKET_URI.split(\"/\")[:3])\n",
- " ! gsutil mb -l {REGION} {BUCKET_URI}\n",
- "else:\n",
- " assert BUCKET_URI.startswith(\"gs://\"), \"BUCKET_URI must start with `gs://`.\"\n",
- " shell_output = ! gsutil ls -Lb {BUCKET_NAME} | grep \"Location constraint:\" | sed \"s/Location constraint://\"\n",
- " bucket_region = shell_output[0].strip().lower()\n",
- " if bucket_region != REGION:\n",
- " raise ValueError(\n",
- " \"Bucket region %s is different from notebook region %s\"\n",
- " % (bucket_region, REGION)\n",
- " )\n",
- "print(f\"Using this GCS Bucket: {BUCKET_URI}\")\n",
- "\n",
- "STAGING_BUCKET = os.path.join(BUCKET_URI, \"temporal\")\n",
- "MODEL_BUCKET = os.path.join(BUCKET_URI, \"mistral\")\n",
- "\n",
- "\n",
- "# Initialize Vertex AI API.\n",
- "print(\"Initializing Vertex AI API.\")\n",
- "aiplatform.init(project=PROJECT_ID, location=REGION, staging_bucket=STAGING_BUCKET)\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",
- "\n",
- "# Provision permissions to the SERVICE_ACCOUNT with the GCS bucket\n",
- "! gsutil iam ch serviceAccount:{SERVICE_ACCOUNT}:roles/storage.admin $BUCKET_NAME\n",
- "\n",
- "! gcloud config set project $PROJECT_ID\n",
- "! gcloud projects add-iam-policy-binding --no-user-output-enabled {PROJECT_ID} --member=serviceAccount:{SERVICE_ACCOUNT} --role=\"roles/storage.admin\"\n",
- "! gcloud projects add-iam-policy-binding --no-user-output-enabled {PROJECT_ID} --member=serviceAccount:{SERVICE_ACCOUNT} --role=\"roles/aiplatform.user\""
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "5K169qf_udor"
- },
- "outputs": [],
- "source": [
- "# @title Set dataset\n",
- "\n",
- "# @markdown Use the Vertex AI SDK to create and run the custom training jobs.\n",
- "\n",
- "# @markdown This notebook uses [timdettmers/openassistant-guanaco](https://huggingface.co/datasets/timdettmers/openassistant-guanaco) dataset as an example.\n",
- "# @markdown You can set `dataset_name` to any existing [Hugging Face dataset](https://huggingface.co/datasets) name, and set `instruct_column_in_dataset` to the name of the dataset column containing training data. The [timdettmers/openassistant-guanaco](https://huggingface.co/datasets/timdettmers/openassistant-guanaco) has only one column `text`, and therefore we set `instruct_column_in_dataset` to `text` in this notebook.\n",
- "\n",
- "# @markdown ### (Optional) Prepare a custom JSONL dataset for finetuning\n",
- "\n",
- "# @markdown You can prepare a JSONL file where each line is a valid JSON string as your custom training dataset. For example, here is one line from the [timdettmers/openassistant-guanaco](https://huggingface.co/datasets/timdettmers/openassistant-guanaco) dataset:\n",
- "# @markdown ```\n",
- "# @markdown {\"text\": \"### Human: Hola### Assistant: \\u00a1Hola! \\u00bfEn qu\\u00e9 puedo ayudarte hoy?\"}\n",
- "# @markdown ```\n",
- "\n",
- "# @markdown The JSON object has a key `text`, which should match `instruct_column_in_dataset`; The value should be one training data point, i.e. a string. After you prepared your JSONL file, you can either upload it to [Hugging Face datasets](https://huggingface.co/datasets) or [Google Cloud Storage](https://cloud.google.com/storage).\n",
- "\n",
- "# @markdown - To upload a JSONL dataset to [Hugging Face datasets](https://huggingface.co/datasets), follow the instructions on [Uploading Datasets](https://huggingface.co/docs/hub/en/datasets-adding). Then, set `dataset_name` to the name of your newly created dataset on Hugging Face.\n",
- "\n",
- "# @markdown - To upload a JSONL dataset to [Google Cloud Storage](https://cloud.google.com/storage), follow the instructions on [Upload objects from a filesystem](https://cloud.google.com/storage/docs/uploading-objects). Then, set `dataset_name` to the `gs://` URI to your JSONL file. For example: `gs://cloud-samples-data/vertex-ai/model-evaluation/peft_train_sample.jsonl`.\n",
- "\n",
- "# @markdown Optionally update the `instruct_column_in_dataset` field below if your JSON objects use a key other than the default `text`.\n",
- "\n",
- "# @markdown ### (Optional) Format your data with custom JSON template\n",
- "\n",
- "# @markdown Sometimes, your dataset might have multiple text columns and you want to construct the training data with a template. You can prepare a JSON template in the following format:\n",
- "\n",
- "# @markdown ```\n",
- "# @markdown {\n",
- "# @markdown \"description\": \"Template that accepts text-bison format.\",\n",
- "# @markdown \"source\": \"https://cloud.google.com/vertex-ai/generative-ai/docs/models/tune-text-models-supervised#dataset-format\",\n",
- "# @markdown \"prompt_input\": \"\\n\\n<|start_header_id|>user<|end_header_id|>\\n\\n{input_text}<|eot_id|>\\n\\n<|start_header_id|>assistant<|end_header_id|>\\n\\n{output_text}<|eot_id|>\",\n",
- "# @markdown \"instruction_separator\": \"<|start_header_id|>user<|end_header_id|>\\n\\n\",\n",
- "# @markdown \"response_separator\": \"<|start_header_id|>assistant<|end_header_id|>\\n\\n\"\n",
- "# @markdown }\n",
- "# @markdown ```\n",
- "\n",
- "\n",
- "# @markdown As an example, the template above can be used to format the following training data (this line comes from `gs://cloud-samples-data/vertex-ai/model-evaluation/peft_train_sample.jsonl`):\n",
- "\n",
- "# @markdown ```\n",
- "# @markdown {\"input_text\":\"TRANSCRIPT: \\nREASON FOR EVALUATION:,\\n\\n LABEL:\",\"output_text\":\"Chiropractic\"}\n",
- "# @markdown ```\n",
- "\n",
- "# @markdown This example template simply concatenates `input_text` with `output_text` with some special tokens in between.\n",
- "# @markdown\n",
- "# @markdown To try such custom dataset, you can make the following changes:\n",
- "# @markdown 1. Set `template` to `llama3-text-bison`\n",
- "# @markdown 1. Set `train_dataset_name` to `gs://cloud-samples-data/vertex-ai/model-evaluation/peft_train_sample.jsonl`\n",
- "# @markdown 1. Set `train_split_name` to `train`\n",
- "# @markdown 1. Set `eval_dataset_name` to `gs://cloud-samples-data/vertex-ai/model-evaluation/peft_eval_sample.jsonl`\n",
- "# @markdown 1. Set `eval_split_name` to `train` (**NOT** `test`)\n",
- "# @markdown 1. Set `instruct_column_in_dataset` as `input_text`.\n",
- "\n",
- "# Template name or gs:// URI to a custom template.\n",
- "template = \"openassistant-guanaco\" # @param {type:\"string\"}\n",
- "\n",
- "# Hugging Face dataset name or gs:// URI to a custom JSONL dataset.\n",
- "train_dataset_name = \"timdettmers/openassistant-guanaco\" # @param {type:\"string\"}\n",
- "train_split_name = \"train\" # @param {type:\"string\"}\n",
- "eval_dataset_name = \"timdettmers/openassistant-guanaco\" # @param {type:\"string\"}\n",
- "eval_split_name = \"test\" # @param {type:\"string\"}\n",
- "\n",
- "# Name of the dataset column containing training text input.\n",
- "instruct_column_in_dataset = \"text\" # @param {type:\"string\"}"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "ncoBBZXq2qxf"
- },
- "outputs": [],
- "source": [
- "# @title Set model\n",
- "\n",
- "# @markdown Select a model variant of Mistral.\n",
- "base_model_id = \"mistralai/Mistral-7B-v0.1\" # @param [\"mistralai/Mistral-7B-v0.1\"] {isTemplate: true}\n",
- "pretrained_model_id = f\"gs://vertex-model-garden-public-us/{base_model_id}\""
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "8MTGQCZTxDbN"
- },
- "outputs": [],
- "source": [
- "# @title Validate Dataset with Template\n",
- "\n",
- "# @markdown This section validates the train and eval datasets with the template before starting the fine tuning process.\n",
- "\n",
- "import transformers\n",
- "\n",
- "dataset_validation_util = importlib.import_module(\n",
- " \"vertex-ai-samples.community-content.vertex_model_garden.model_oss.notebook_util.dataset_validation_util\"\n",
- ")\n",
- "\n",
- "if dataset_validation_util.is_gcs_path(pretrained_model_id):\n",
- " # Download tokenizer.\n",
- " ! mkdir tokenizer\n",
- " ! gsutil cp {pretrained_model_id}/tokenizer.json ./tokenizer\n",
- " ! gsutil cp {pretrained_model_id}/config.json ./tokenizer\n",
- " tokenizer_path = \"./tokenizer\"\n",
- " access_token = \"\"\n",
- "else:\n",
- " tokenizer_path = pretrained_model_id\n",
- " access_token = HF_TOKEN\n",
- "\n",
- "tokenizer = transformers.AutoTokenizer.from_pretrained(\n",
- " tokenizer_path,\n",
- " trust_remote_code=False,\n",
- " use_fast=True,\n",
- " token=access_token,\n",
- ")\n",
- "\n",
- "# Validate the train dataset.\n",
- "dataset_validation_util.validate_dataset_with_template(\n",
- " dataset_name=train_dataset_name,\n",
- " split=train_split_name,\n",
- " input_column=instruct_column_in_dataset,\n",
- " template=template,\n",
- " use_multiprocessing=False,\n",
- " tokenizer=tokenizer,\n",
- ")\n",
- "\n",
- "# Validate the eval dataset.\n",
- "dataset_validation_util.validate_dataset_with_template(\n",
- " dataset_name=eval_dataset_name,\n",
- " split=eval_split_name,\n",
- " input_column=instruct_column_in_dataset,\n",
- " template=template,\n",
- " use_multiprocessing=False,\n",
- " tokenizer=tokenizer,\n",
- ")"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "885Vf4o8hbbo"
- },
- "outputs": [],
- "source": [
- "# @title Finetune\n",
- "\n",
- "# @markdown This section demonstrates how to finetune the Mistral-7B model and merge the finetuned LoRA adapter with the base model on Vertex AI. It uses the Vertex AI SDK to create and run the custom training jobs.\n",
- "\n",
- "# @markdown The training job takes approximately between 10 to 20 mins to set-up. Once done, the training job is expected to take around 20 mins with the default configurations. To find the training time, throughput, and memory usage of your training job, you can go to the training logs and check the log line of the last training epoch.\n",
- "\n",
- "# @markdown **Note**:\n",
- "# @markdown 1. We recommend setting `finetuning_precision_mode` to `4bit` because it enables using fewer hardware resources for finetuning.\n",
- "# @markdown 1. If `max_steps > 0`, it takes precedence over `epochs`. One can set a small `max_steps` value to quickly check the pipeline.\n",
- "\n",
- "# @markdown Acceletor type to use for training.\n",
- "accelerator_type = \"NVIDIA_A100_80GB\" # @param [\"NVIDIA_A100_80GB\", \"NVIDIA_H100_80GB\"]\n",
- "\n",
- "# The pre-built training docker image.\n",
- "if accelerator_type == \"NVIDIA_A100_80GB\":\n",
- " repo = \"us-docker.pkg.dev/vertex-ai-restricted\"\n",
- " is_restricted_image = True\n",
- " is_dynamic_workload_scheduler = False\n",
- " dws_kwargs = {}\n",
- "else:\n",
- " repo = \"us-docker.pkg.dev/vertex-ai\"\n",
- " is_restricted_image = False\n",
- " is_dynamic_workload_scheduler = True\n",
- " dws_kwargs = {\n",
- " \"max_wait_duration\": 1800, # 30 minutes\n",
- " \"scheduling_strategy\": gca_custom_job_compat.Scheduling.Strategy.FLEX_START,\n",
- " }\n",
- "\n",
- "TRAIN_DOCKER_URI = (\n",
- " f\"{repo}/vertex-vision-model-garden-dockers/pytorch-peft-train:stable_20240909\"\n",
- ")\n",
- "\n",
- "# Worker pool spec.\n",
- "if accelerator_type == \"NVIDIA_A100_80GB\":\n",
- " per_node_accelerator_count = 8\n",
- " machine_type = \"a2-ultragpu-8g\"\n",
- "elif accelerator_type == \"NVIDIA_H100_80GB\":\n",
- " per_node_accelerator_count = 8\n",
- " machine_type = \"a3-highgpu-8g\"\n",
- "else:\n",
- " raise ValueError(\n",
- " f\"Recommended machine settings not found for: {accelerator_type}. To use another accelerator type, edit this code block to pass in an appropriate `machine_type`, `accelerator_type`, and `per_node_accelerator_count` to the deploy_model_vllm function by clicking `Show Code` and then modifying the code.\"\n",
- " )\n",
- "\n",
- "# @markdown Batch size for finetuning.\n",
- "per_device_train_batch_size = 1 # @param{type:\"integer\"}\n",
- "# @markdown Number of updates steps to accumulate the gradients for, before performing a backward/update pass.\n",
- "gradient_accumulation_steps = 4 # @param{type:\"integer\"}\n",
- "# @markdown Maximum sequence length.\n",
- "max_seq_length = 4096 # @param{type:\"integer\"}\n",
- "# @markdown Setting a positive `max_steps` here will override `num_epochs`.\n",
- "max_steps = -1 # @param{type:\"integer\"}\n",
- "num_epochs = 1.0 # @param{type:\"number\"}\n",
- "# @markdown Precision mode for finetuning.\n",
- "finetuning_precision_mode = \"4bit\" # @param [\"4bit\", \"8bit\", \"float16\"]\n",
- "# @markdown Learning rate.\n",
- "learning_rate = 5e-5 # @param{type:\"number\"}\n",
- "# @markdown The scheduler type to use.\n",
- "lr_scheduler_type = \"cosine\" # @param{type:\"string\"}\n",
- "# @markdown LoRA parameters.\n",
- "lora_rank = 16 # @param{type:\"integer\"}\n",
- "lora_alpha = 32 # @param{type:\"integer\"}\n",
- "lora_dropout = 0.05 # @param{type:\"number\"}\n",
- "# Activates gradient checkpointing for the current model (may be referred to as activation checkpointing or checkpoint activations in other frameworks).\n",
- "enable_gradient_checkpointing = True\n",
- "# Attention implementation to use in the model.\n",
- "attn_implementation = \"flash_attention_2\"\n",
- "# The optimizer for which to schedule the learning rate.\n",
- "optimizer = \"paged_adamw_32bit\"\n",
- "# Define the proportion of training to be dedicated to a linear warmup where learning rate gradually increases.\n",
- "warmup_ratio = \"0.01\"\n",
- "# The list or string of integrations to report the results and logs to.\n",
- "report_to = \"tensorboard\"\n",
- "# Number of updates steps before two checkpoint saves.\n",
- "save_steps = 10\n",
- "# Number of update steps between two logs.\n",
- "logging_steps = save_steps\n",
- "# Train precision of the model.\n",
- "train_precision = \"float16\"\n",
- "\n",
- "replica_count = 1\n",
- "\n",
- "common_util.check_quota(\n",
- " project_id=PROJECT_ID,\n",
- " region=REGION,\n",
- " accelerator_type=accelerator_type,\n",
- " accelerator_count=per_node_accelerator_count * replica_count,\n",
- " is_for_training=True,\n",
- " is_restricted_image=is_restricted_image,\n",
- " is_dynamic_workload_scheduler=is_dynamic_workload_scheduler,\n",
- ")\n",
- "\n",
- "# Setup training job.\n",
- "job_name = common_util.get_job_name_with_datetime(\"mistral-lora-train\")\n",
- "\n",
- "base_output_dir = os.path.join(STAGING_BUCKET, job_name)\n",
- "# Create a GCS folder to store the LORA adapter.\n",
- "lora_output_dir = os.path.join(base_output_dir, \"adapter\")\n",
- "# Create a GCS folder to store the merged model with the base model and the\n",
- "# finetuned LORA adapter.\n",
- "merged_model_output_dir = os.path.join(base_output_dir, \"merged-model\")\n",
- "\n",
- "# Add labels for the finetuning job.\n",
- "labels = {\n",
- " \"mg-source\": \"notebook\",\n",
- " \"mg-notebook-name\": \"model_garden_pytorch_mistral_peft_tuning.ipynb\".split(\".\")[0],\n",
- "}\n",
- "\n",
- "labels[\"mg-tune\"] = \"publishers-mistralai-models-mistral\"\n",
- "versioned_model_id = base_model_id.split(\"/\")[1].lower().replace(\".\", \"-\")\n",
- "labels[\"versioned-mg-tune\"] = f\"{labels['mg-tune']}-{versioned_model_id}\"\n",
- "\n",
- "eval_args = [\n",
- " f\"--eval_dataset_path={eval_dataset_name}\",\n",
- " f\"--eval_column={instruct_column_in_dataset}\",\n",
- " f\"--eval_template={template}\",\n",
- " f\"--eval_split={eval_split_name}\",\n",
- " f\"--eval_steps={save_steps}\",\n",
- " \"--eval_tasks=builtin_eval\",\n",
- " \"--eval_metric_name=loss\",\n",
- "]\n",
- "\n",
- "train_job_args = [\n",
- " \"--config_file=vertex_vision_model_garden_peft/deepspeed_zero2_8gpu.yaml\",\n",
- " \"--task=instruct-lora\",\n",
- " \"--completion_only=False\",\n",
- " f\"--pretrained_model_id={pretrained_model_id}\",\n",
- " f\"--dataset_name={train_dataset_name}\",\n",
- " f\"--train_split_name={train_split_name}\",\n",
- " f\"--instruct_column_in_dataset={instruct_column_in_dataset}\",\n",
- " f\"--output_dir={lora_output_dir}\",\n",
- " f\"--merge_base_and_lora_output_dir={merged_model_output_dir}\",\n",
- " f\"--per_device_train_batch_size={per_device_train_batch_size}\",\n",
- " f\"--gradient_accumulation_steps={gradient_accumulation_steps}\",\n",
- " f\"--lora_rank={lora_rank}\",\n",
- " f\"--lora_alpha={lora_alpha}\",\n",
- " f\"--lora_dropout={lora_dropout}\",\n",
- " f\"--max_steps={max_steps}\",\n",
- " f\"--max_seq_length={max_seq_length}\",\n",
- " f\"--learning_rate={learning_rate}\",\n",
- " f\"--lr_scheduler_type={lr_scheduler_type}\",\n",
- " f\"--precision_mode={finetuning_precision_mode}\",\n",
- " f\"--train_precision={train_precision}\",\n",
- " f\"--enable_gradient_checkpointing={enable_gradient_checkpointing}\",\n",
- " f\"--num_epochs={num_epochs}\",\n",
- " f\"--attn_implementation={attn_implementation}\",\n",
- " f\"--optimizer={optimizer}\",\n",
- " f\"--warmup_ratio={warmup_ratio}\",\n",
- " f\"--report_to={report_to}\",\n",
- " f\"--logging_output_dir={base_output_dir}\",\n",
- " f\"--save_steps={save_steps}\",\n",
- " f\"--logging_steps={logging_steps}\",\n",
- " f\"--template={template}\",\n",
- "] + eval_args\n",
- "\n",
- "train_job = aiplatform.CustomContainerTrainingJob(\n",
- " display_name=job_name,\n",
- " container_uri=TRAIN_DOCKER_URI,\n",
- " labels=labels,\n",
- ")\n",
- "\n",
- "print(\"Running training job with args:\")\n",
- "print(\" \\\\\\n\".join(train_job_args))\n",
- "# Pass training arguments and launch job.\n",
- "train_job.run(\n",
- " args=train_job_args,\n",
- " replica_count=replica_count,\n",
- " machine_type=machine_type,\n",
- " accelerator_type=accelerator_type,\n",
- " accelerator_count=per_node_accelerator_count,\n",
- " boot_disk_size_gb=500,\n",
- " service_account=SERVICE_ACCOUNT,\n",
- " base_output_dir=base_output_dir,\n",
- " sync=False, # Non-blocking call to run.\n",
- " **dws_kwargs,\n",
- ")\n",
- "\n",
- "# Wait until resource has been created.\n",
- "train_job.wait_for_resource_creation()\n",
- "\n",
- "print(\"LoRA adapter will be saved in:\", lora_output_dir)\n",
- "print(\"Trained and merged models will be saved in:\", merged_model_output_dir)\n",
- "\n",
- "# @markdown Click \"Show Code\" to see more details."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "cLNQwLMGmzlR"
- },
- "outputs": [],
- "source": [
- "# @title Run TensorBoard\n",
- "# @markdown This section shows how to launch TensorBoard in a [Cloud Shell](https://cloud.google.com/shell/docs).\n",
- "# @markdown 1. Click the Cloud Shell icon() on the top right to open the Cloud Shell.\n",
- "# @markdown 2. Copy the `tensorboard` command shown below by running this cell.\n",
- "# @markdown 3. Paste and run the command in the Cloud Shell to launch TensorBoard.\n",
- "# @markdown 4. Once the command runs (You may have to click `Authorize` if prompted), click the link starting with `http://localhost`.\n",
- "\n",
- "# @markdown Note: You may need to wait around 10 minutes after the job starts in order for the TensorBoard logs to be written to the GCS bucket.\n",
- "print(f\"Command to copy: tensorboard --logdir {base_output_dir}/logs\")"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "GyDWPdV1NjMT"
- },
- "outputs": [],
- "source": [
- "# @title Deploy\n",
- "# @markdown This section uploads the model to Model Registry and deploys it on the Endpoint. It takes 15 minutes to 1 hour to finish depending on the size of model.\n",
- "\n",
- "if train_job.end_time is None:\n",
- " print(\"Waiting for the training job to finish...\")\n",
- " train_job.wait()\n",
- " print(\"The training job has finished.\")\n",
- "\n",
- "print(\"Deploying models in:\", merged_model_output_dir)\n",
- "\n",
- "# The pre-built serving docker image for vLLM.\n",
- "VLLM_DOCKER_URI = \"us-docker.pkg.dev/vertex-ai/vertex-vision-model-garden-dockers/pytorch-vllm-serve:20240721_0916_RC00\"\n",
- "\n",
- "# Find Vertex AI prediction supported accelerators and regions [here](https://cloud.google.com/vertex-ai/docs/predictions/configure-compute).\n",
- "# @markdown Accelerator type to use for serving.\n",
- "accelerator_type = \"NVIDIA_L4\" # @param [\"NVIDIA_L4\", \"NVIDIA_TESLA_V100\", \"NVIDIA_TESLA_T4\", \"NVIDIA_TESLA_A100\"]\n",
- "\n",
- "if accelerator_type == \"NVIDIA_L4\":\n",
- " machine_type = \"g2-standard-8\"\n",
- " accelerator_count = 1\n",
- "elif accelerator_type == \"NVIDIA_TESLA_V100\":\n",
- " machine_type = \"n1-standard-16\"\n",
- " accelerator_count = 2\n",
- "elif accelerator_type == \"NVIDIA_TESLA_T4\":\n",
- " machine_type = \"n1-standard-16\"\n",
- " accelerator_count = 2\n",
- "elif accelerator_type == \"NVIDIA_TESLA_A100\":\n",
- " machine_type = \"a2-highgpu-1g\"\n",
- " accelerator_count = 1\n",
- "\n",
- "common_util.check_quota(\n",
- " project_id=PROJECT_ID,\n",
- " region=REGION,\n",
- " accelerator_type=accelerator_type,\n",
- " accelerator_count=accelerator_count,\n",
- " is_for_training=False,\n",
- ")\n",
- "\n",
- "# @markdown Set `use_dedicated_endpoint` to False if you don't want to use [dedicated endpoint](https://cloud.google.com/vertex-ai/docs/general/deployment#create-dedicated-endpoint).\n",
- "use_dedicated_endpoint = True # @param {type:\"boolean\"}\n",
- "\n",
- "gpu_memory_utilization = 0.85\n",
- "max_model_len = 8192 # Maximum context length.\n",
- "\n",
- "# Ensure max_model_len does not exceed the limit\n",
- "if max_model_len > 8192:\n",
- " raise ValueError(\"max_model_len cannot exceed 8192\")\n",
- "\n",
- "\n",
- "def get_deploy_source() -> str:\n",
- " \"\"\"Gets deploy_source string based on running environment.\"\"\"\n",
- " vertex_product = os.environ.get(\"VERTEX_PRODUCT\", \"\")\n",
- " if vertex_product == \"COLAB_ENTERPRISE\":\n",
- " return \"notebook_colab_enterprise\"\n",
- " elif vertex_product == \"WORKBENCH_INSTANCE\":\n",
- " return \"notebook_workbench\"\n",
- " else:\n",
- " # Legacy workbench, legacy colab, or other custom environments.\n",
- " return \"notebook_environment_unspecified\"\n",
- "\n",
- "\n",
- "def deploy_model_vllm(\n",
- " model_name: str,\n",
- " model_id: str,\n",
- " publisher: str,\n",
- " publisher_model_id: str,\n",
- " service_account: str,\n",
- " base_model_id: str = None,\n",
- " machine_type: str = \"g2-standard-8\",\n",
- " accelerator_type: str = \"NVIDIA_L4\",\n",
- " accelerator_count: int = 1,\n",
- " gpu_memory_utilization: float = 0.9,\n",
- " max_model_len: int = 4096,\n",
- " dtype: str = \"auto\",\n",
- " enable_trust_remote_code: bool = False,\n",
- " enforce_eager: bool = False,\n",
- " enable_lora: bool = False,\n",
- " enable_chunked_prefill: bool = False,\n",
- " enable_prefix_cache: bool = False,\n",
- " host_prefix_kv_cache_utilization_target: float = 0.0,\n",
- " max_loras: int = 1,\n",
- " max_cpu_loras: int = 8,\n",
- " use_dedicated_endpoint: bool = False,\n",
- " max_num_seqs: int = 256,\n",
- " model_type: str = None,\n",
- " enable_llama_tool_parser: bool = False,\n",
- " is_spot: bool = False,\n",
- ") -> Tuple[aiplatform.Model, aiplatform.Endpoint]:\n",
- " \"\"\"Deploys trained models with vLLM into Vertex AI.\"\"\"\n",
- " endpoint = aiplatform.Endpoint.create(\n",
- " display_name=f\"{model_name}-endpoint\",\n",
- " dedicated_endpoint_enabled=use_dedicated_endpoint,\n",
- " )\n",
- "\n",
- " if not base_model_id:\n",
- " base_model_id = model_id\n",
- "\n",
- " # See https://docs.vllm.ai/en/latest/models/engine_args.html for a list of possible arguments with descriptions.\n",
- " vllm_args = [\n",
- " \"python\",\n",
- " \"-m\",\n",
- " \"vllm.entrypoints.api_server\",\n",
- " \"--host=0.0.0.0\",\n",
- " \"--port=8080\",\n",
- " f\"--model={model_id}\",\n",
- " f\"--tensor-parallel-size={accelerator_count}\",\n",
- " \"--swap-space=16\",\n",
- " f\"--max-model-len={max_model_len}\",\n",
- " f\"--dtype={dtype}\",\n",
- " f\"--max-loras={max_loras}\",\n",
- " f\"--max-cpu-loras={max_cpu_loras}\",\n",
- " f\"--max-num-seqs={max_num_seqs}\",\n",
- " \"--disable-log-stats\",\n",
- " ]\n",
- "\n",
- " if gpu_memory_utilization:\n",
- " vllm_args.append(f\"--gpu-memory-utilization={gpu_memory_utilization}\")\n",
- "\n",
- " if enable_trust_remote_code:\n",
- " vllm_args.append(\"--trust-remote-code\")\n",
- "\n",
- " if enforce_eager:\n",
- " vllm_args.append(\"--enforce-eager\")\n",
- "\n",
- " if enable_lora:\n",
- " vllm_args.append(\"--enable-lora\")\n",
- "\n",
- " if enable_chunked_prefill:\n",
- " vllm_args.append(\"--enable-chunked-prefill\")\n",
- "\n",
- " if enable_prefix_cache:\n",
- " vllm_args.append(\"--enable-prefix-caching\")\n",
- "\n",
- " if 0 < host_prefix_kv_cache_utilization_target < 1:\n",
- " vllm_args.append(\n",
- " f\"--host-prefix-kv-cache-utilization-target={host_prefix_kv_cache_utilization_target}\"\n",
- " )\n",
- "\n",
- " if model_type:\n",
- " vllm_args.append(f\"--model-type={model_type}\")\n",
- "\n",
- " if enable_llama_tool_parser:\n",
- " vllm_args.append(\"--enable-auto-tool-choice\")\n",
- " vllm_args.append(\"--tool-call-parser=vertex-llama-3\")\n",
- "\n",
- " env_vars = {\n",
- " \"MODEL_ID\": base_model_id,\n",
- " \"DEPLOY_SOURCE\": \"notebook\",\n",
- " }\n",
- "\n",
- " # HF_TOKEN is not a compulsory field and may not be defined.\n",
- " try:\n",
- " if HF_TOKEN:\n",
- " env_vars[\"HF_TOKEN\"] = HF_TOKEN\n",
- " except NameError:\n",
- " pass\n",
- "\n",
- " model = aiplatform.Model.upload(\n",
- " display_name=model_name,\n",
- " serving_container_image_uri=VLLM_DOCKER_URI,\n",
- " serving_container_args=vllm_args,\n",
- " serving_container_ports=[8080],\n",
- " serving_container_predict_route=\"/generate\",\n",
- " serving_container_health_route=\"/ping\",\n",
- " serving_container_environment_variables=env_vars,\n",
- " serving_container_shared_memory_size_mb=(16 * 1024), # 16 GB\n",
- " serving_container_deployment_timeout=7200,\n",
- " model_garden_source_model_name=(\n",
- " f\"publishers/{publisher}/models/{publisher_model_id}\"\n",
- " ),\n",
- " )\n",
- " print(\n",
- " f\"Deploying {model_name} on {machine_type} with {accelerator_count} {accelerator_type} GPU(s).\"\n",
- " )\n",
- " model.deploy(\n",
- " endpoint=endpoint,\n",
- " machine_type=machine_type,\n",
- " accelerator_type=accelerator_type,\n",
- " accelerator_count=accelerator_count,\n",
- " deploy_request_timeout=1800,\n",
- " service_account=service_account,\n",
- " spot=is_spot,\n",
- " system_labels={\n",
- " \"NOTEBOOK_NAME\": \"model_garden_pytorch_mistral_peft_tuning.ipynb\",\n",
- " \"NOTEBOOK_ENVIRONMENT\": get_deploy_source(),\n",
- " },\n",
- " )\n",
- " print(\"endpoint_name:\", endpoint.name)\n",
- "\n",
- " return model, endpoint\n",
- "\n",
- "\n",
- "models[\"vllm_gpu\"], endpoints[\"vllm_gpu\"] = deploy_model_vllm(\n",
- " model_name=common_util.get_job_name_with_datetime(prefix=\"mistral-vllm-serve\"),\n",
- " model_id=merged_model_output_dir,\n",
- " publisher=\"mistral-ai\",\n",
- " publisher_model_id=\"mistral\",\n",
- " service_account=SERVICE_ACCOUNT,\n",
- " machine_type=machine_type,\n",
- " accelerator_type=accelerator_type,\n",
- " accelerator_count=accelerator_count,\n",
- " gpu_memory_utilization=gpu_memory_utilization,\n",
- " max_model_len=max_model_len,\n",
- " use_dedicated_endpoint=use_dedicated_endpoint,\n",
- ")\n",
- "\n",
- "# @markdown Click \"Show code\" to see more details."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "4v2Mnui4tH1X"
- },
- "outputs": [],
- "source": [
- "# @title Predict\n",
- "\n",
- "# @markdown Once deployment succeeds, you can send requests to the endpoint with text prompts. Sampling parameters supported by vLLM can be found [here](https://docs.vllm.ai/en/latest/dev/sampling_params.html).\n",
- "\n",
- "# @markdown Example:\n",
- "\n",
- "# @markdown ```\n",
- "# @markdown Human: What is a car?\n",
- "# @markdown Assistant: A car, or a motor car, is a road-connected human-transportation system used to move people or goods from one place to another. The term also encompasses a wide range of vehicles, including motorboats, trains, and aircrafts. Cars typically have four wheels, a cabin for passengers, and an engine or motor. They have been around since the early 19th century and are now one of the most popular forms of transportation, used for daily commuting, shopping, and other purposes.\n",
- "# @markdown ```\n",
- "# @markdown Additionally, you can moderate the generated text with Vertex AI. See [Moderate text documentation](https://cloud.google.com/natural-language/docs/moderating-text) for more details.\n",
- "\n",
- "# Loads an existing endpoint instance using the endpoint name:\n",
- "# - Using `endpoint_name = endpoint.name` allows us to get the\n",
- "# endpoint name of the endpoint `endpoint` created in the cell\n",
- "# above.\n",
- "# - Alternatively, you can set `endpoint_name = \"1234567890123456789\"` to load\n",
- "# an existing endpoint with the ID 1234567890123456789.\n",
- "# You may uncomment the code below to load an existing endpoint.\n",
- "\n",
- "# endpoint_name = \"\" # @param {type:\"string\"}\n",
- "# aip_endpoint_name = (\n",
- "# f\"projects/{PROJECT_ID}/locations/{REGION}/endpoints/{endpoint_name}\"\n",
- "# )\n",
- "# endpoint = aiplatform.Endpoint(aip_endpoint_name)\n",
- "\n",
- "prompt = \"What is a car?\" # @param {type: \"string\"}\n",
- "# @markdown If you encounter an issue like `ServiceUnavailable: 503 Took too long to respond when processing`, you can reduce the maximum number of output tokens, by lowering `max_tokens`.\n",
- "max_tokens = 50 # @param {type:\"integer\"}\n",
- "temperature = 1.0 # @param {type:\"number\"}\n",
- "top_p = 1.0 # @param {type:\"number\"}\n",
- "top_k = 1 # @param {type:\"integer\"}\n",
- "# @markdown Set `raw_response` to `True` to obtain the raw model output. Set `raw_response` to `False` to apply additional formatting in the structure of `\"Prompt:\\n{prompt.strip()}\\nOutput:\\n{output}\"`.\n",
- "raw_response = False # @param {type:\"boolean\"}\n",
- "\n",
- "# Overrides parameters for inferences.\n",
- "instances = [\n",
- " {\n",
- " \"prompt\": prompt,\n",
- " \"max_tokens\": max_tokens,\n",
- " \"temperature\": temperature,\n",
- " \"top_p\": top_p,\n",
- " \"top_k\": top_k,\n",
- " \"raw_response\": raw_response,\n",
- " },\n",
- "]\n",
- "response = endpoints[\"vllm_gpu\"].predict(\n",
- " instances=instances, use_dedicated_endpoint=use_dedicated_endpoint\n",
- ")\n",
- "\n",
- "for prediction in response.predictions:\n",
- " print(prediction)\n",
- "\n",
- "# @markdown Click \"Show Code\" to see more details."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "x9EMCOUJ6-ji"
- },
- "outputs": [],
- "source": [
- "# @title Delete the model and endpoint\n",
- "\n",
- "# @markdown Delete the experiment models and endpoints to recycle the resources\n",
- "# @markdown and avoid unnecessary continuous charges that may incur.\n",
- "\n",
- "# Undeploy model and delete endpoint.\n",
- "for endpoint in endpoints.values():\n",
- " endpoint.delete(force=True)\n",
- "\n",
- "# Delete models.\n",
- "for model in models.values():\n",
- " model.delete()\n",
- "\n",
- "delete_bucket = False # @param {type:\"boolean\"}\n",
- "if delete_bucket:\n",
- " ! gsutil -m rm -r $BUCKET_NAME"
- ]
- }
- ],
- "metadata": {
- "accelerator": "GPU",
- "colab": {
- "name": "model_garden_pytorch_mistral_peft_tuning.ipynb",
- "toc_visible": true
- },
- "kernelspec": {
- "display_name": "Python 3",
- "name": "python3"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 0
-}
diff --git a/notebooks/community/model_garden/model_garden_pytorch_mixtral_peft_tuning.ipynb b/notebooks/community/model_garden/model_garden_pytorch_mixtral_peft_tuning.ipynb
deleted file mode 100644
index a6b1d6209..000000000
--- a/notebooks/community/model_garden/model_garden_pytorch_mixtral_peft_tuning.ipynb
+++ /dev/null
@@ -1,919 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "iJc36RtD90jd"
- },
- "outputs": [],
- "source": [
- "# Copyright 2026 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": "b9EezHSo90jf"
- },
- "source": [
- "# Vertex AI Model Garden - Mixtral-8x7B (PEFT)\n",
- "\n",
- "\n",
- " \n",
- " \n",
- "  Run in Colab Enterprise\n",
- " \n",
- " | \n",
- " \n",
- " \n",
- "  View on GitHub\n",
- " \n",
- " | \n",
- "
"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "ybMCVFh0_5R8"
- },
- "source": [
- "## Overview\n",
- "In this notebook you will learn how to fine tune Mixtral-8x7B with QLoRa and\n",
- "deploy to Vertex AI endpoint.\n",
- "\n",
- "### Objective\n",
- "\n",
- "* Finetune and merge Mixtral-8x7B model with PEFT training docker image.\n",
- "* Deploy the finetuned model with vLLM docker image on a Vertex AI Endpoint.\n",
- "* Run inference on the deployed Vertex AI Endpoint.\n",
- "\n",
- "### File a bug\n",
- "\n",
- "File a bug on [GitHub](https://github.com/GoogleCloudPlatform/vertex-ai-samples/issues/new) if you encounter any issue with the notebook.\n",
- "\n",
- "### Costs\n",
- "\n",
- "This tutorial uses billable components of Google Cloud:\n",
- "\n",
- "* Vertex AI\n",
- "* Cloud Storage\n",
- "\n",
- "Learn about [Vertex AI pricing](https://cloud.google.com/vertex-ai/pricing), [Cloud Storage pricing](https://cloud.google.com/storage/pricing), and use the [Pricing Calculator](https://cloud.google.com/products/calculator/) to generate a cost estimate based on your projected usage."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "vzvFJU27a8si"
- },
- "source": [
- "## Run the notebook"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "DzESmydvgME9"
- },
- "outputs": [],
- "source": [
- "# @title Install Python Packages for Finetuning\n",
- "\n",
- "# @markdown 1. Install google-cloud-aiplatform package and restart the session if instructed.\n",
- "! pip install --upgrade --quiet google-cloud-aiplatform==1.130.0\n",
- "\n",
- "# @markdown 2. Install packages to validate dataset with template.\n",
- "! pip install --upgrade --quiet gcsfs==2024.3.1\n",
- "! pip install --upgrade --quiet accelerate==0.31.0\n",
- "! pip install --upgrade --quiet transformers==4.43.1\n",
- "! pip install --upgrade --quiet datasets==2.19.2\n",
- "\n",
- "# Load local tensorboard.\n",
- "%load_ext tensorboard"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "I-OjzhpyMHsu"
- },
- "outputs": [],
- "source": [
- "# @title Setup Google Cloud project\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. For finetuning, **[click here](https://console.cloud.google.com/iam-admin/quotas?location=us-central1&metric=aiplatform.googleapis.com%2Frestricted_image_training_nvidia_a100_80gb_gpus)** to check if your project already has the required 8 Nvidia A100 80 GB GPUs in the us-central1 region. If yes, then run this notebook in the us-central1 region. If you do not have 8 Nvidia A100 80 GPUs or have more GPU requirements than this, then schedule your job with Nvidia H100 GPUs via Dynamic Workload Scheduler using [these instructions](https://cloud.google.com/vertex-ai/docs/training/schedule-jobs-dws). For Dynamic Workload Scheduler, check the [us-central1](https://console.cloud.google.com/iam-admin/quotas?location=us-central1&metric=aiplatform.googleapis.com%2Fcustom_model_training_preemptible_nvidia_h100_gpus) or [europe-west4](https://console.cloud.google.com/iam-admin/quotas?location=europe-west4&metric=aiplatform.googleapis.com%2Fcustom_model_training_preemptible_nvidia_h100_gpus) quota for Nvidia H100 GPUs. If you do not have enough GPUs, then you can follow [these instructions](https://cloud.google.com/docs/quotas/view-manage#viewing_your_quota_console) to request quota.\n",
- "\n",
- "# @markdown 3. For serving, **[click here](https://console.cloud.google.com/iam-admin/quotas?location=us-central1&metric=aiplatform.googleapis.com%2Fcustom_model_serving_nvidia_l4_gpus)** to check if your project already has the required 1 L4 GPU in the us-central1 region. If yes, then run this notebook in the us-central1 region. If you need more L4 GPUs for your project, then you can follow [these instructions](https://cloud.google.com/docs/quotas/view-manage#viewing_your_quota_console) to request more. Alternatively, 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).\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",
- "# @markdown 4. **[Optional]** [Create a Cloud Storage bucket](https://cloud.google.com/storage/docs/creating-buckets) for storing experiment outputs. Set the BUCKET_URI for the experiment environment. The specified Cloud Storage bucket (`BUCKET_URI`) should be located in the same region as where the notebook was launched. Note that a multi-region bucket (eg. \"us\") is not considered a match for a single region covered by the multi-region range (eg. \"us-central1\"). If not set, a unique GCS bucket will be created instead.\n",
- "\n",
- "BUCKET_URI = \"gs://\" # @param {type:\"string\"}\n",
- "\n",
- "# @markdown 5. **[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",
- "# Import the necessary packages\n",
- "! rm -rf vertex-ai-samples && git clone https://github.com/GoogleCloudPlatform/vertex-ai-samples.git\n",
- "! cd vertex-ai-samples && git reset --hard 0727e19520cf7957bceb701c248221bd3dbe4f1f\n",
- "\n",
- "import datetime\n",
- "import importlib\n",
- "import os\n",
- "import uuid\n",
- "from typing import Tuple\n",
- "\n",
- "from google.cloud import aiplatform\n",
- "from google.cloud.aiplatform.compat.types import \\\n",
- " custom_job as gca_custom_job_compat\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",
- "models, endpoints = {}, {}\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",
- " if not os.environ.get(\"GOOGLE_CLOUD_REGION\"):\n",
- " raise ValueError(\n",
- " \"REGION must be set. See\"\n",
- " \" https://cloud.google.com/vertex-ai/docs/general/locations for\"\n",
- " \" available cloud locations.\"\n",
- " )\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",
- "# Cloud Storage bucket for storing the experiment artifacts.\n",
- "# A unique GCS bucket will be created for the purpose of this notebook. If you\n",
- "# prefer using your own GCS bucket, change the value yourself below.\n",
- "now = datetime.datetime.now().strftime(\"%Y%m%d%H%M%S\")\n",
- "BUCKET_NAME = \"/\".join(BUCKET_URI.split(\"/\")[:3])\n",
- "\n",
- "if BUCKET_URI is None or BUCKET_URI.strip() == \"\" or BUCKET_URI == \"gs://\":\n",
- " BUCKET_URI = f\"gs://{PROJECT_ID}-tmp-{now}-{str(uuid.uuid4())[:4]}\"\n",
- " BUCKET_NAME = \"/\".join(BUCKET_URI.split(\"/\")[:3])\n",
- " ! gsutil mb -l {REGION} {BUCKET_URI}\n",
- "else:\n",
- " assert BUCKET_URI.startswith(\"gs://\"), \"BUCKET_URI must start with `gs://`.\"\n",
- " shell_output = ! gsutil ls -Lb {BUCKET_NAME} | grep \"Location constraint:\" | sed \"s/Location constraint://\"\n",
- " bucket_region = shell_output[0].strip().lower()\n",
- " if bucket_region != REGION:\n",
- " raise ValueError(\n",
- " \"Bucket region %s is different from notebook region %s\"\n",
- " % (bucket_region, REGION)\n",
- " )\n",
- "print(f\"Using this GCS Bucket: {BUCKET_URI}\")\n",
- "\n",
- "STAGING_BUCKET = os.path.join(BUCKET_URI, \"temporal\")\n",
- "MODEL_BUCKET = os.path.join(BUCKET_URI, \"mixtral\")\n",
- "\n",
- "\n",
- "# Initialize Vertex AI API.\n",
- "print(\"Initializing Vertex AI API.\")\n",
- "aiplatform.init(project=PROJECT_ID, location=REGION, staging_bucket=STAGING_BUCKET)\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",
- "\n",
- "# Provision permissions to the SERVICE_ACCOUNT with the GCS bucket\n",
- "! gsutil iam ch serviceAccount:{SERVICE_ACCOUNT}:roles/storage.admin $BUCKET_NAME\n",
- "\n",
- "! gcloud config set project $PROJECT_ID\n",
- "! gcloud projects add-iam-policy-binding --no-user-output-enabled {PROJECT_ID} --member=serviceAccount:{SERVICE_ACCOUNT} --role=\"roles/storage.admin\"\n",
- "! gcloud projects add-iam-policy-binding --no-user-output-enabled {PROJECT_ID} --member=serviceAccount:{SERVICE_ACCOUNT} --role=\"roles/aiplatform.user\""
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "rwP8nr8jnNdt"
- },
- "outputs": [],
- "source": [
- "# @title Set dataset\n",
- "\n",
- "# @markdown Use the Vertex AI SDK to create and run the custom training jobs.\n",
- "\n",
- "# @markdown This notebook uses [timdettmers/openassistant-guanaco](https://huggingface.co/datasets/timdettmers/openassistant-guanaco) dataset as an example.\n",
- "# @markdown You can set `dataset_name` to any existing [Hugging Face dataset](https://huggingface.co/datasets) name, and set `instruct_column_in_dataset` to the name of the dataset column containing training data. The [timdettmers/openassistant-guanaco](https://huggingface.co/datasets/timdettmers/openassistant-guanaco) has only one column `text`, and therefore we set `instruct_column_in_dataset` to `text` in this notebook.\n",
- "\n",
- "# @markdown ### (Optional) Prepare a custom JSONL dataset for finetuning\n",
- "\n",
- "# @markdown You can prepare a JSONL file where each line is a valid JSON string as your custom training dataset. For example, here is one line from the [timdettmers/openassistant-guanaco](https://huggingface.co/datasets/timdettmers/openassistant-guanaco) dataset:\n",
- "# @markdown ```\n",
- "# @markdown {\"text\": \"### Human: Hola### Assistant: \\u00a1Hola! \\u00bfEn qu\\u00e9 puedo ayudarte hoy?\"}\n",
- "# @markdown ```\n",
- "\n",
- "# @markdown The JSON object has a key `text`, which should match `instruct_column_in_dataset`; The value should be one training data point, i.e. a string. After you prepared your JSONL file, you can either upload it to [Hugging Face datasets](https://huggingface.co/datasets) or [Google Cloud Storage](https://cloud.google.com/storage).\n",
- "\n",
- "# @markdown - To upload a JSONL dataset to [Hugging Face datasets](https://huggingface.co/datasets), follow the instructions on [Uploading Datasets](https://huggingface.co/docs/hub/en/datasets-adding). Then, set `dataset_name` to the name of your newly created dataset on Hugging Face.\n",
- "\n",
- "# @markdown - To upload a JSONL dataset to [Google Cloud Storage](https://cloud.google.com/storage), follow the instructions on [Upload objects from a filesystem](https://cloud.google.com/storage/docs/uploading-objects). Then, set `dataset_name` to the `gs://` URI to your JSONL file. For example: `gs://cloud-samples-data/vertex-ai/model-evaluation/peft_train_sample.jsonl`.\n",
- "\n",
- "# @markdown Optionally update the `instruct_column_in_dataset` field below if your JSON objects use a key other than the default `text`.\n",
- "\n",
- "# @markdown ### (Optional) Format your data with custom JSON template\n",
- "\n",
- "# @markdown Sometimes, your dataset might have multiple text columns and you want to construct the training data with a template. You can prepare a JSON template in the following format:\n",
- "\n",
- "# @markdown ```\n",
- "# @markdown {\n",
- "# @markdown \"description\": \"Template that accepts text-bison format.\",\n",
- "# @markdown \"source\": \"https://cloud.google.com/vertex-ai/generative-ai/docs/models/tune-text-models-supervised#dataset-format\",\n",
- "# @markdown \"prompt_input\": \"\\n\\n<|start_header_id|>user<|end_header_id|>\\n\\n{input_text}<|eot_id|>\\n\\n<|start_header_id|>assistant<|end_header_id|>\\n\\n{output_text}<|eot_id|>\",\n",
- "# @markdown \"instruction_separator\": \"<|start_header_id|>user<|end_header_id|>\\n\\n\",\n",
- "# @markdown \"response_separator\": \"<|start_header_id|>assistant<|end_header_id|>\\n\\n\"\n",
- "# @markdown }\n",
- "# @markdown ```\n",
- "\n",
- "\n",
- "# @markdown As an example, the template above can be used to format the following training data (this line comes from `gs://cloud-samples-data/vertex-ai/model-evaluation/peft_train_sample.jsonl`):\n",
- "\n",
- "# @markdown ```\n",
- "# @markdown {\"input_text\":\"TRANSCRIPT: \\nREASON FOR EVALUATION:,\\n\\n LABEL:\",\"output_text\":\"Chiropractic\"}\n",
- "# @markdown ```\n",
- "\n",
- "# @markdown This example template simply concatenates `input_text` with `output_text` with some special tokens in between.\n",
- "# @markdown\n",
- "# @markdown To try such custom dataset, you can make the following changes:\n",
- "# @markdown 1. Set `template` to `llama3-text-bison`\n",
- "# @markdown 1. Set `train_dataset_name` to `gs://cloud-samples-data/vertex-ai/model-evaluation/peft_train_sample.jsonl`\n",
- "# @markdown 1. Set `train_split_name` to `train`\n",
- "# @markdown 1. Set `eval_dataset_name` to `gs://cloud-samples-data/vertex-ai/model-evaluation/peft_eval_sample.jsonl`\n",
- "# @markdown 1. Set `eval_split_name` to `train` (**NOT** `test`)\n",
- "# @markdown 1. Set `instruct_column_in_dataset` as `input_text`.\n",
- "\n",
- "# Template name or gs:// URI to a custom template.\n",
- "template = \"openassistant-guanaco\" # @param {type:\"string\"}\n",
- "\n",
- "# Hugging Face dataset name or gs:// URI to a custom JSONL dataset.\n",
- "train_dataset_name = \"timdettmers/openassistant-guanaco\" # @param {type:\"string\"}\n",
- "train_split_name = \"train\" # @param {type:\"string\"}\n",
- "eval_dataset_name = \"timdettmers/openassistant-guanaco\" # @param {type:\"string\"}\n",
- "eval_split_name = \"test\" # @param {type:\"string\"}\n",
- "\n",
- "# Name of the dataset column containing training text input.\n",
- "instruct_column_in_dataset = \"text\" # @param {type:\"string\"}"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "lkCVPgWl2vxv"
- },
- "outputs": [],
- "source": [
- "# @title Set model\n",
- "\n",
- "# @markdown Select a model variant of Mixtral.\n",
- "base_model_id = \"mistralai/Mixtral-8x7B-v0.1\" # @param [\"mistralai/Mixtral-8x7B-v0.1\"] {isTemplate: true}\n",
- "pretrained_model_id = f\"gs://vertex-model-garden-public-us/{base_model_id}\""
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "YZPqfZ-FvPXS"
- },
- "outputs": [],
- "source": [
- "# @title Validate Dataset with Template\n",
- "\n",
- "# @markdown This section validates the train and eval datasets with the template before starting the fine tuning process.\n",
- "\n",
- "import transformers\n",
- "\n",
- "dataset_validation_util = importlib.import_module(\n",
- " \"vertex-ai-samples.community-content.vertex_model_garden.model_oss.notebook_util.dataset_validation_util\"\n",
- ")\n",
- "\n",
- "if dataset_validation_util.is_gcs_path(pretrained_model_id):\n",
- " # Download tokenizer.\n",
- " ! mkdir tokenizer\n",
- " ! gsutil cp {pretrained_model_id}/tokenizer.json ./tokenizer\n",
- " ! gsutil cp {pretrained_model_id}/config.json ./tokenizer\n",
- " tokenizer_path = \"./tokenizer\"\n",
- " access_token = \"\"\n",
- "else:\n",
- " tokenizer_path = pretrained_model_id\n",
- " access_token = HF_TOKEN\n",
- "\n",
- "tokenizer = transformers.AutoTokenizer.from_pretrained(\n",
- " tokenizer_path,\n",
- " trust_remote_code=False,\n",
- " use_fast=True,\n",
- " token=access_token,\n",
- ")\n",
- "\n",
- "# Validate the train dataset.\n",
- "dataset_validation_util.validate_dataset_with_template(\n",
- " dataset_name=train_dataset_name,\n",
- " split=train_split_name,\n",
- " input_column=instruct_column_in_dataset,\n",
- " template=template,\n",
- " use_multiprocessing=False,\n",
- " tokenizer=tokenizer,\n",
- ")\n",
- "\n",
- "# Validate the eval dataset.\n",
- "dataset_validation_util.validate_dataset_with_template(\n",
- " dataset_name=eval_dataset_name,\n",
- " split=eval_split_name,\n",
- " input_column=instruct_column_in_dataset,\n",
- " template=template,\n",
- " use_multiprocessing=False,\n",
- " tokenizer=tokenizer,\n",
- ")"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "885Vf4o8hbbo"
- },
- "outputs": [],
- "source": [
- "# @title Finetune\n",
- "\n",
- "# @markdown This section demonstrates how to finetune the Mixtral-8x7B model and merge the finetuned LoRA adapter with the base model on Vertex AI. It uses the Vertex AI SDK to create and run the custom training jobs.\n",
- "\n",
- "# @markdown The training job takes approximately between 10 to 20 mins to set-up. Once done, the training job is expected to take around 90 mins with the default configurations. To find the training time, throughput, and memory usage of your training job, you can go to the training logs and check the log line of the last training epoch.\n",
- "\n",
- "# @markdown **Note**:\n",
- "# @markdown 1. We recommend setting `finetuning_precision_mode` to `4bit` because it enables using fewer hardware resources for finetuning.\n",
- "# @markdown 1. If `max_steps > 0`, it takes precedence over `epochs`. One can set a small `max_steps` value to quickly check the pipeline.\n",
- "\n",
- "# @markdown Acceletor type to use for training.\n",
- "accelerator_type = \"NVIDIA_A100_80GB\" # @param [\"NVIDIA_A100_80GB\", \"NVIDIA_H100_80GB\"]\n",
- "\n",
- "# The pre-built training docker image.\n",
- "if accelerator_type == \"NVIDIA_A100_80GB\":\n",
- " repo = \"us-docker.pkg.dev/vertex-ai-restricted\"\n",
- " is_restricted_image = True\n",
- " is_dynamic_workload_scheduler = False\n",
- " dws_kwargs = {}\n",
- "else:\n",
- " repo = \"us-docker.pkg.dev/vertex-ai\"\n",
- " is_restricted_image = False\n",
- " is_dynamic_workload_scheduler = True\n",
- " dws_kwargs = {\n",
- " \"max_wait_duration\": 1800, # 30 minutes\n",
- " \"scheduling_strategy\": gca_custom_job_compat.Scheduling.Strategy.FLEX_START,\n",
- " }\n",
- "\n",
- "TRAIN_DOCKER_URI = (\n",
- " f\"{repo}/vertex-vision-model-garden-dockers/pytorch-peft-train:stable_20240909\"\n",
- ")\n",
- "\n",
- "# Worker pool spec.\n",
- "if accelerator_type == \"NVIDIA_A100_80GB\":\n",
- " per_node_accelerator_count = 8\n",
- " machine_type = \"a2-ultragpu-8g\"\n",
- "elif accelerator_type == \"NVIDIA_H100_80GB\":\n",
- " per_node_accelerator_count = 8\n",
- " machine_type = \"a3-highgpu-8g\"\n",
- "else:\n",
- " raise ValueError(\n",
- " f\"Recommended machine settings not found for: {accelerator_type}. To use another accelerator type, edit this code block to pass in an appropriate `machine_type`, `accelerator_type`, and `per_node_accelerator_count` to the deploy_model_vllm function by clicking `Show Code` and then modifying the code.\"\n",
- " )\n",
- "\n",
- "# @markdown Batch size for finetuning.\n",
- "per_device_train_batch_size = 1 # @param{type:\"integer\"}\n",
- "# @markdown Number of updates steps to accumulate the gradients for, before performing a backward/update pass.\n",
- "gradient_accumulation_steps = 4 # @param{type:\"integer\"}\n",
- "# @markdown Maximum sequence length.\n",
- "max_seq_length = 4096 # @param{type:\"integer\"}\n",
- "# @markdown Setting a positive `max_steps` here will override `num_epochs`.\n",
- "max_steps = -1 # @param{type:\"integer\"}\n",
- "num_epochs = 1.0 # @param{type:\"number\"}\n",
- "# @markdown Precision mode for finetuning.\n",
- "finetuning_precision_mode = \"4bit\" # @param [\"4bit\"]\n",
- "# @markdown Learning rate.\n",
- "learning_rate = 5e-5 # @param{type:\"number\"}\n",
- "# @markdown The scheduler type to use.\n",
- "lr_scheduler_type = \"cosine\" # @param{type:\"string\"}\n",
- "# @markdown LoRA parameters.\n",
- "lora_rank = 16 # @param{type:\"integer\"}\n",
- "lora_alpha = 32 # @param{type:\"integer\"}\n",
- "lora_dropout = 0.05 # @param{type:\"number\"}\n",
- "# Activates gradient checkpointing for the current model (may be referred to as activation checkpointing or checkpoint activations in other frameworks).\n",
- "enable_gradient_checkpointing = True\n",
- "# Attention implementation to use in the model.\n",
- "attn_implementation = \"flash_attention_2\"\n",
- "# The optimizer for which to schedule the learning rate.\n",
- "optimizer = \"paged_adamw_32bit\"\n",
- "# Define the proportion of training to be dedicated to a linear warmup where learning rate gradually increases.\n",
- "warmup_ratio = \"0.01\"\n",
- "# The list or string of integrations to report the results and logs to.\n",
- "report_to = \"tensorboard\"\n",
- "# Number of updates steps before two checkpoint saves.\n",
- "save_steps = 10\n",
- "# Number of update steps between two logs.\n",
- "logging_steps = save_steps\n",
- "# Train precision of the model.\n",
- "train_precision = \"float16\"\n",
- "\n",
- "replica_count = 1\n",
- "\n",
- "common_util.check_quota(\n",
- " project_id=PROJECT_ID,\n",
- " region=REGION,\n",
- " accelerator_type=accelerator_type,\n",
- " accelerator_count=per_node_accelerator_count * replica_count,\n",
- " is_for_training=True,\n",
- " is_restricted_image=is_restricted_image,\n",
- " is_dynamic_workload_scheduler=is_dynamic_workload_scheduler,\n",
- ")\n",
- "\n",
- "# Setup training job.\n",
- "job_name = common_util.get_job_name_with_datetime(\"mixtral-lora-train\")\n",
- "\n",
- "base_output_dir = os.path.join(STAGING_BUCKET, job_name)\n",
- "# Create a GCS folder to store the LORA adapter.\n",
- "lora_output_dir = os.path.join(base_output_dir, \"adapter\")\n",
- "# Create a GCS folder to store the merged model with the base model and the\n",
- "# finetuned LORA adapter.\n",
- "merged_model_output_dir = os.path.join(base_output_dir, \"merged-model\")\n",
- "\n",
- "# Add labels for the finetuning job.\n",
- "labels = {\n",
- " \"mg-source\": \"notebook\",\n",
- " \"mg-notebook-name\": \"model_garden_pytorch_mixtral_peft_tuning.ipynb\".split(\".\")[0],\n",
- "}\n",
- "\n",
- "labels[\"mg-tune\"] = \"publishers-mistralai-models-mixtral\"\n",
- "versioned_model_id = base_model_id.split(\"/\")[1].lower().replace(\".\", \"-\")\n",
- "labels[\"versioned-mg-tune\"] = f\"{labels['mg-tune']}-{versioned_model_id}\"\n",
- "\n",
- "eval_args = [\n",
- " f\"--eval_dataset_path={eval_dataset_name}\",\n",
- " f\"--eval_column={instruct_column_in_dataset}\",\n",
- " f\"--eval_template={template}\",\n",
- " f\"--eval_split={eval_split_name}\",\n",
- " f\"--eval_steps={save_steps}\",\n",
- " \"--eval_tasks=builtin_eval\",\n",
- " \"--eval_metric_name=loss\",\n",
- "]\n",
- "\n",
- "train_job_args = [\n",
- " \"--config_file=vertex_vision_model_garden_peft/deepspeed_zero2_8gpu.yaml\",\n",
- " \"--task=instruct-lora\",\n",
- " \"--completion_only=False\",\n",
- " f\"--pretrained_model_id={pretrained_model_id}\",\n",
- " f\"--dataset_name={train_dataset_name}\",\n",
- " f\"--train_split_name={train_split_name}\",\n",
- " f\"--instruct_column_in_dataset={instruct_column_in_dataset}\",\n",
- " f\"--output_dir={lora_output_dir}\",\n",
- " f\"--merge_base_and_lora_output_dir={merged_model_output_dir}\",\n",
- " f\"--per_device_train_batch_size={per_device_train_batch_size}\",\n",
- " f\"--gradient_accumulation_steps={gradient_accumulation_steps}\",\n",
- " f\"--lora_rank={lora_rank}\",\n",
- " f\"--lora_alpha={lora_alpha}\",\n",
- " f\"--lora_dropout={lora_dropout}\",\n",
- " f\"--max_steps={max_steps}\",\n",
- " f\"--max_seq_length={max_seq_length}\",\n",
- " f\"--learning_rate={learning_rate}\",\n",
- " f\"--lr_scheduler_type={lr_scheduler_type}\",\n",
- " f\"--precision_mode={finetuning_precision_mode}\",\n",
- " f\"--train_precision={train_precision}\",\n",
- " f\"--enable_gradient_checkpointing={enable_gradient_checkpointing}\",\n",
- " f\"--num_epochs={num_epochs}\",\n",
- " f\"--attn_implementation={attn_implementation}\",\n",
- " f\"--optimizer={optimizer}\",\n",
- " f\"--warmup_ratio={warmup_ratio}\",\n",
- " f\"--report_to={report_to}\",\n",
- " f\"--logging_output_dir={base_output_dir}\",\n",
- " f\"--save_steps={save_steps}\",\n",
- " f\"--logging_steps={logging_steps}\",\n",
- " f\"--template={template}\",\n",
- "] + eval_args\n",
- "\n",
- "train_job = aiplatform.CustomContainerTrainingJob(\n",
- " display_name=job_name,\n",
- " container_uri=TRAIN_DOCKER_URI,\n",
- " labels=labels,\n",
- ")\n",
- "\n",
- "print(\"Running training job with args:\")\n",
- "print(\" \\\\\\n\".join(train_job_args))\n",
- "# Pass training arguments and launch job.\n",
- "train_job.run(\n",
- " args=train_job_args,\n",
- " replica_count=replica_count,\n",
- " machine_type=machine_type,\n",
- " accelerator_type=accelerator_type,\n",
- " accelerator_count=per_node_accelerator_count,\n",
- " boot_disk_size_gb=500,\n",
- " service_account=SERVICE_ACCOUNT,\n",
- " base_output_dir=base_output_dir,\n",
- " sync=False, # Non-blocking call to run.\n",
- " **dws_kwargs,\n",
- ")\n",
- "\n",
- "# Wait until resource has been created.\n",
- "train_job.wait_for_resource_creation()\n",
- "\n",
- "print(\"LoRA adapter will be saved in:\", lora_output_dir)\n",
- "print(\"Trained and merged models will be saved in:\", merged_model_output_dir)\n",
- "\n",
- "# @markdown Click \"Show Code\" to see more details."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "gLzO6p0Gm2BM"
- },
- "outputs": [],
- "source": [
- "# @title Run TensorBoard\n",
- "# @markdown This section shows how to launch TensorBoard in a [Cloud Shell](https://cloud.google.com/shell/docs).\n",
- "# @markdown 1. Click the Cloud Shell icon() on the top right to open the Cloud Shell.\n",
- "# @markdown 2. Copy the `tensorboard` command shown below by running this cell.\n",
- "# @markdown 3. Paste and run the command in the Cloud Shell to launch TensorBoard.\n",
- "# @markdown 4. Once the command runs (You may have to click `Authorize` if prompted), click the link starting with `http://localhost`.\n",
- "\n",
- "# @markdown Note: You may need to wait around 10 minutes after the job starts in order for the TensorBoard logs to be written to the GCS bucket.\n",
- "print(f\"Command to copy: tensorboard --logdir {base_output_dir}/logs\")"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "GyDWPdV1NjMT"
- },
- "outputs": [],
- "source": [
- "# @title Deploy\n",
- "# @markdown This section uploads the model to Model Registry and deploys it on the Endpoint. It takes 15 minutes to 1 hour to finish depending on the size of model.\n",
- "\n",
- "if train_job.end_time is None:\n",
- " print(\"Waiting for the training job to finish...\")\n",
- " train_job.wait()\n",
- " print(\"The training job has finished.\")\n",
- "\n",
- "print(\"Deploying models in:\", merged_model_output_dir)\n",
- "\n",
- "# The pre-built serving docker image for vLLM.\n",
- "VLLM_DOCKER_URI = \"us-docker.pkg.dev/vertex-ai/vertex-vision-model-garden-dockers/pytorch-vllm-serve:20240721_0916_RC00\"\n",
- "\n",
- "dtype = \"auto\"\n",
- "\n",
- "# @markdown L4 GPUs are good serving solutions and are more cost effective than V100s for 8x7B models. The 8x22B models only works with A100/H100 GPUs now.\n",
- "\n",
- "# Find Vertex AI prediction supported accelerators and regions [here](https://cloud.google.com/vertex-ai/docs/predictions/configure-compute).\n",
- "# @markdown Accelerator type to use for serving.\n",
- "accelerator_type = \"NVIDIA_L4\" # @param [\"NVIDIA_L4\", \"NVIDIA_TESLA_V100\", \"NVIDIA_H100_80GB\"]\n",
- "\n",
- "if accelerator_type == \"NVIDIA_L4\":\n",
- " machine_type = \"g2-standard-96\"\n",
- " accelerator_count = 8\n",
- "elif accelerator_type == \"NVIDIA_TESLA_V100\":\n",
- " machine_type = \"n1-highmem-32\"\n",
- " accelerator_count = 8\n",
- " dtype = \"float16\"\n",
- "elif accelerator_type == \"NVIDIA_H100_80GB\":\n",
- " machine_type = \"a3-highgpu-8g\"\n",
- " accelerator_count = 8\n",
- "\n",
- "if \"22B\" in base_model_id and accelerator_type != \"NVIDIA_H100_80GB\":\n",
- " raise ValueError(\"8x22B model version only works with H100/A100 GPUs.\")\n",
- "\n",
- "common_util.check_quota(\n",
- " project_id=PROJECT_ID,\n",
- " region=REGION,\n",
- " accelerator_type=accelerator_type,\n",
- " accelerator_count=accelerator_count,\n",
- " is_for_training=False,\n",
- ")\n",
- "\n",
- "# @markdown Set `use_dedicated_endpoint` to False if you don't want to use [dedicated endpoint](https://cloud.google.com/vertex-ai/docs/general/deployment#create-dedicated-endpoint).\n",
- "use_dedicated_endpoint = True # @param {type:\"boolean\"}\n",
- "\n",
- "gpu_memory_utilization = 0.85\n",
- "max_model_len = 8192 # Maximum context length.\n",
- "\n",
- "# Ensure max_model_len does not exceed the limit\n",
- "if max_model_len > 8192:\n",
- " raise ValueError(\"max_model_len cannot exceed 8192\")\n",
- "\n",
- "\n",
- "def get_deploy_source() -> str:\n",
- " \"\"\"Gets deploy_source string based on running environment.\"\"\"\n",
- " vertex_product = os.environ.get(\"VERTEX_PRODUCT\", \"\")\n",
- " if vertex_product == \"COLAB_ENTERPRISE\":\n",
- " return \"notebook_colab_enterprise\"\n",
- " elif vertex_product == \"WORKBENCH_INSTANCE\":\n",
- " return \"notebook_workbench\"\n",
- " else:\n",
- " # Legacy workbench, legacy colab, or other custom environments.\n",
- " return \"notebook_environment_unspecified\"\n",
- "\n",
- "\n",
- "def deploy_model_vllm(\n",
- " model_name: str,\n",
- " model_id: str,\n",
- " publisher: str,\n",
- " publisher_model_id: str,\n",
- " service_account: str,\n",
- " base_model_id: str = None,\n",
- " machine_type: str = \"g2-standard-8\",\n",
- " accelerator_type: str = \"NVIDIA_L4\",\n",
- " accelerator_count: int = 1,\n",
- " gpu_memory_utilization: float = 0.9,\n",
- " max_model_len: int = 4096,\n",
- " dtype: str = \"auto\",\n",
- " enable_trust_remote_code: bool = False,\n",
- " enforce_eager: bool = False,\n",
- " enable_lora: bool = False,\n",
- " enable_chunked_prefill: bool = False,\n",
- " enable_prefix_cache: bool = False,\n",
- " host_prefix_kv_cache_utilization_target: float = 0.0,\n",
- " max_loras: int = 1,\n",
- " max_cpu_loras: int = 8,\n",
- " use_dedicated_endpoint: bool = False,\n",
- " max_num_seqs: int = 256,\n",
- " model_type: str = None,\n",
- " enable_llama_tool_parser: bool = False,\n",
- " is_spot: bool = False,\n",
- ") -> Tuple[aiplatform.Model, aiplatform.Endpoint]:\n",
- " \"\"\"Deploys trained models with vLLM into Vertex AI.\"\"\"\n",
- " endpoint = aiplatform.Endpoint.create(\n",
- " display_name=f\"{model_name}-endpoint\",\n",
- " dedicated_endpoint_enabled=use_dedicated_endpoint,\n",
- " )\n",
- "\n",
- " if not base_model_id:\n",
- " base_model_id = model_id\n",
- "\n",
- " # See https://docs.vllm.ai/en/latest/models/engine_args.html for a list of possible arguments with descriptions.\n",
- " vllm_args = [\n",
- " \"python\",\n",
- " \"-m\",\n",
- " \"vllm.entrypoints.api_server\",\n",
- " \"--host=0.0.0.0\",\n",
- " \"--port=8080\",\n",
- " f\"--model={model_id}\",\n",
- " f\"--tensor-parallel-size={accelerator_count}\",\n",
- " \"--swap-space=16\",\n",
- " f\"--max-model-len={max_model_len}\",\n",
- " f\"--dtype={dtype}\",\n",
- " f\"--max-loras={max_loras}\",\n",
- " f\"--max-cpu-loras={max_cpu_loras}\",\n",
- " f\"--max-num-seqs={max_num_seqs}\",\n",
- " \"--disable-log-stats\",\n",
- " ]\n",
- "\n",
- " if gpu_memory_utilization:\n",
- " vllm_args.append(f\"--gpu-memory-utilization={gpu_memory_utilization}\")\n",
- "\n",
- " if enable_trust_remote_code:\n",
- " vllm_args.append(\"--trust-remote-code\")\n",
- "\n",
- " if enforce_eager:\n",
- " vllm_args.append(\"--enforce-eager\")\n",
- "\n",
- " if enable_lora:\n",
- " vllm_args.append(\"--enable-lora\")\n",
- "\n",
- " if enable_chunked_prefill:\n",
- " vllm_args.append(\"--enable-chunked-prefill\")\n",
- "\n",
- " if enable_prefix_cache:\n",
- " vllm_args.append(\"--enable-prefix-caching\")\n",
- "\n",
- " if 0 < host_prefix_kv_cache_utilization_target < 1:\n",
- " vllm_args.append(\n",
- " f\"--host-prefix-kv-cache-utilization-target={host_prefix_kv_cache_utilization_target}\"\n",
- " )\n",
- "\n",
- " if model_type:\n",
- " vllm_args.append(f\"--model-type={model_type}\")\n",
- "\n",
- " if enable_llama_tool_parser:\n",
- " vllm_args.append(\"--enable-auto-tool-choice\")\n",
- " vllm_args.append(\"--tool-call-parser=vertex-llama-3\")\n",
- "\n",
- " env_vars = {\n",
- " \"MODEL_ID\": base_model_id,\n",
- " \"DEPLOY_SOURCE\": \"notebook\",\n",
- " }\n",
- "\n",
- " # HF_TOKEN is not a compulsory field and may not be defined.\n",
- " try:\n",
- " if HF_TOKEN:\n",
- " env_vars[\"HF_TOKEN\"] = HF_TOKEN\n",
- " except NameError:\n",
- " pass\n",
- "\n",
- " model = aiplatform.Model.upload(\n",
- " display_name=model_name,\n",
- " serving_container_image_uri=VLLM_DOCKER_URI,\n",
- " serving_container_args=vllm_args,\n",
- " serving_container_ports=[8080],\n",
- " serving_container_predict_route=\"/generate\",\n",
- " serving_container_health_route=\"/ping\",\n",
- " serving_container_environment_variables=env_vars,\n",
- " serving_container_shared_memory_size_mb=(16 * 1024), # 16 GB\n",
- " serving_container_deployment_timeout=7200,\n",
- " model_garden_source_model_name=(\n",
- " f\"publishers/{publisher}/models/{publisher_model_id}\"\n",
- " ),\n",
- " )\n",
- " print(\n",
- " f\"Deploying {model_name} on {machine_type} with {accelerator_count} {accelerator_type} GPU(s).\"\n",
- " )\n",
- " model.deploy(\n",
- " endpoint=endpoint,\n",
- " machine_type=machine_type,\n",
- " accelerator_type=accelerator_type,\n",
- " accelerator_count=accelerator_count,\n",
- " deploy_request_timeout=1800,\n",
- " service_account=service_account,\n",
- " spot=is_spot,\n",
- " system_labels={\n",
- " \"NOTEBOOK_NAME\": \"model_garden_pytorch_mixtral_peft_tuning.ipynb\",\n",
- " \"NOTEBOOK_ENVIRONMENT\": get_deploy_source(),\n",
- " },\n",
- " )\n",
- " print(\"endpoint_name:\", endpoint.name)\n",
- "\n",
- " return model, endpoint\n",
- "\n",
- "\n",
- "models[\"vllm_gpu\"], endpoints[\"vllm_gpu\"] = deploy_model_vllm(\n",
- " model_name=common_util.get_job_name_with_datetime(prefix=\"mixtral-vllm-serve\"),\n",
- " model_id=merged_model_output_dir,\n",
- " publisher=\"mistral-ai\",\n",
- " publisher_model_id=\"mixtral\",\n",
- " service_account=SERVICE_ACCOUNT,\n",
- " machine_type=machine_type,\n",
- " accelerator_type=accelerator_type,\n",
- " accelerator_count=accelerator_count,\n",
- " gpu_memory_utilization=gpu_memory_utilization,\n",
- " max_model_len=max_model_len,\n",
- " dtype=dtype,\n",
- " use_dedicated_endpoint=use_dedicated_endpoint,\n",
- ")\n",
- "\n",
- "# @markdown Click \"Show code\" to see more details."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "4v2Mnui4tH1X"
- },
- "outputs": [],
- "source": [
- "# @title Predict\n",
- "\n",
- "# @markdown Once deployment succeeds, you can send requests to the endpoint with text prompts. Sampling parameters supported by vLLM can be found [here](https://docs.vllm.ai/en/latest/dev/sampling_params.html).\n",
- "\n",
- "# @markdown Example:\n",
- "\n",
- "# @markdown ```\n",
- "# @markdown Human: What is a car?\n",
- "# @markdown Assistant: A car, or a motor car, is a road-connected human-transportation system used to move people or goods from one place to another. The term also encompasses a wide range of vehicles, including motorboats, trains, and aircrafts. Cars typically have four wheels, a cabin for passengers, and an engine or motor. They have been around since the early 19th century and are now one of the most popular forms of transportation, used for daily commuting, shopping, and other purposes.\n",
- "# @markdown ```\n",
- "# @markdown Additionally, you can moderate the generated text with Vertex AI. See [Moderate text documentation](https://cloud.google.com/natural-language/docs/moderating-text) for more details.\n",
- "\n",
- "# Loads an existing endpoint instance using the endpoint name:\n",
- "# - Using `endpoint_name = endpoint.name` allows us to get the\n",
- "# endpoint name of the endpoint `endpoint` created in the cell\n",
- "# above.\n",
- "# - Alternatively, you can set `endpoint_name = \"1234567890123456789\"` to load\n",
- "# an existing endpoint with the ID 1234567890123456789.\n",
- "# You may uncomment the code below to load an existing endpoint.\n",
- "\n",
- "# endpoint_name = \"\" # @param {type:\"string\"}\n",
- "# aip_endpoint_name = (\n",
- "# f\"projects/{PROJECT_ID}/locations/{REGION}/endpoints/{endpoint_name}\"\n",
- "# )\n",
- "# endpoint = aiplatform.Endpoint(aip_endpoint_name)\n",
- "\n",
- "prompt = \"What is a car?\" # @param {type: \"string\"}\n",
- "# @markdown If you encounter an issue like `ServiceUnavailable: 503 Took too long to respond when processing`, you can reduce the maximum number of output tokens, by lowering `max_tokens`.\n",
- "max_tokens = 50 # @param {type:\"integer\"}\n",
- "temperature = 1.0 # @param {type:\"number\"}\n",
- "top_p = 1.0 # @param {type:\"number\"}\n",
- "top_k = 1 # @param {type:\"integer\"}\n",
- "# @markdown Set `raw_response` to `True` to obtain the raw model output. Set `raw_response` to `False` to apply additional formatting in the structure of `\"Prompt:\\n{prompt.strip()}\\nOutput:\\n{output}\"`.\n",
- "raw_response = False # @param {type:\"boolean\"}\n",
- "\n",
- "# Overrides parameters for inferences.\n",
- "instances = [\n",
- " {\n",
- " \"prompt\": prompt,\n",
- " \"max_tokens\": max_tokens,\n",
- " \"temperature\": temperature,\n",
- " \"top_p\": top_p,\n",
- " \"top_k\": top_k,\n",
- " \"raw_response\": raw_response,\n",
- " },\n",
- "]\n",
- "response = endpoints[\"vllm_gpu\"].predict(\n",
- " instances=instances, use_dedicated_endpoint=use_dedicated_endpoint\n",
- ")\n",
- "\n",
- "for prediction in response.predictions:\n",
- " print(prediction)\n",
- "\n",
- "# @markdown Click \"Show Code\" to see more details."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "x9EMCOUJ6-ji"
- },
- "outputs": [],
- "source": [
- "# @title Delete the model and endpoint\n",
- "\n",
- "# @markdown Delete the experiment models and endpoints to recycle the resources\n",
- "# @markdown and avoid unnecessary continuous charges that may incur.\n",
- "\n",
- "# Undeploy model and delete endpoint.\n",
- "for endpoint in endpoints.values():\n",
- " endpoint.delete(force=True)\n",
- "\n",
- "# Delete models.\n",
- "for model in models.values():\n",
- " model.delete()\n",
- "\n",
- "delete_bucket = False # @param {type:\"boolean\"}\n",
- "if delete_bucket:\n",
- " ! gsutil -m rm -r $BUCKET_NAME"
- ]
- }
- ],
- "metadata": {
- "accelerator": "GPU",
- "colab": {
- "name": "model_garden_pytorch_mixtral_peft_tuning.ipynb",
- "toc_visible": true
- },
- "kernelspec": {
- "display_name": "Python 3",
- "name": "python3"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 0
-}
diff --git a/notebooks/community/model_garden/model_garden_pytorch_openllama_peft.ipynb b/notebooks/community/model_garden/model_garden_pytorch_openllama_peft.ipynb
deleted file mode 100644
index 8088ed427..000000000
--- a/notebooks/community/model_garden/model_garden_pytorch_openllama_peft.ipynb
+++ /dev/null
@@ -1,1522 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "id": "7d9bbf86da5e"
- },
- "outputs": [],
- "source": [
- "# Copyright 2023 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": "99c1c3fc2ca5"
- },
- "source": [
- "# Vertex AI Model Garden - OpenLLaMA (PEFT)\n",
- "\n",
- "\n",
- " \n",
- " \n",
- " Run in Colab\n",
- " \n",
- " | \n",
- " \n",
- " \n",
- " \n",
- " View on GitHub\n",
- " \n",
- " | \n",
- " \n",
- " \n",
- " \n",
- "Open in Vertex AI Workbench\n",
- " (A Python-3 GPU notebook is recommended)\n",
- " | \n",
- "
"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "3de7470326a2"
- },
- "source": [
- "## Overview\n",
- "\n",
- "This notebook demonstrates running local inference with prebuilt OpenLLaMA, deploying prebuilt OpenLLaMA, deploying prebuilt OpenLLaMA with [vLLM](https://github.com/vllm-project/vllm), finetuning and deploying OpenLLaMA with performance efficient finetuning libraries ([PEFT](https://github.com/huggingface/peft)), quantizing and deploying OpenLLaMA with AWQ or GPTQ, and evaluating PEFT-finetuned OpenLLaMA in Vertex AI.\n",
- "\n",
- "### Objective\n",
- "\n",
- "- Run local inference with prebuilt OpenLLaMA\n",
- "- Deploy prebuilt OpenLLaMA\n",
- "- Deploy prebuilt OpenLLaMA with [vLLM](https://github.com/vllm-project/vllm) to improve serving throughput\n",
- "- Finetune and deploy OpenLLaMA with PEFT\n",
- "- Quantize and deploy OpenLLaMA models with AWQ or GPTQ\n",
- "- Evaluate finetuned OpenLLaMA with PEFT\n",
- "\n",
- "| Models | LoRA |\n",
- "| :- | :- |\n",
- "| [openlm-research/open_llama_3b](https://huggingface.co/openlm-research/open_llama_3b) | Y |\n",
- "| [openlm-research/open_llama_7b](https://huggingface.co/openlm-research/open_llama_7b) | Y |\n",
- "| [openlm-research/open_llama_13b](https://huggingface.co/openlm-research/open_llama_13b) | Y |\n",
- "\n",
- "### Costs\n",
- "\n",
- "This tutorial uses billable components of Google Cloud:\n",
- "\n",
- "* Vertex AI\n",
- "* Cloud Storage\n",
- "\n",
- "Learn about [Vertex AI pricing](https://cloud.google.com/vertex-ai/pricing) and [Cloud Storage pricing](https://cloud.google.com/storage/pricing), and use the [Pricing Calculator](https://cloud.google.com/products/calculator/) to generate a cost estimate based on your projected usage."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "264c07757582"
- },
- "source": [
- "## Before you begin\n",
- "\n",
- "**NOTE**: Jupyter runs lines prefixed with `!` as shell commands, and it interpolates Python variables prefixed with `$` into these commands.\n",
- "\n",
- "Running local inference with OpenLLaMA requires a GPU."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "ioensNKM8ned"
- },
- "source": [
- "### Colab only\n",
- "Run the following commands for Colab and skip this section if you are using Workbench."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "id": "2707b02ef5df"
- },
- "outputs": [],
- "source": [
- "import sys\n",
- "\n",
- "if \"google.colab\" in sys.modules:\n",
- " ! pip3 install --upgrade google-cloud-aiplatform\n",
- " from google.colab import auth as google_auth\n",
- "\n",
- " google_auth.authenticate_user()\n",
- "\n",
- " # Restart the notebook kernel after installs.\n",
- " import IPython\n",
- "\n",
- " app = IPython.Application.instance()\n",
- " app.kernel.do_shutdown(True)\n",
- "! pip3 install transformers==4.31.0\n",
- "! pip3 install sentencepiece==0.1.99\n",
- "! pip3 install accelerate==0.21.0"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "bb7adab99e41"
- },
- "source": [
- "### Setup Google Cloud project\n",
- "\n",
- "1. [Select or create a Google Cloud project](https://console.cloud.google.com/cloud-resource-manager). When you first create an account, you get a $300 free credit towards your compute/storage costs.\n",
- "\n",
- "1. [Make sure that billing is enabled for your project](https://cloud.google.com/billing/docs/how-to/modify-project).\n",
- "\n",
- "1. [Enable the Vertex AI API and Compute Engine API](https://console.cloud.google.com/flows/enableapi?apiid=aiplatform.googleapis.com,compute_component).\n",
- "\n",
- "1. [Create a Cloud Storage bucket](https://cloud.google.com/storage/docs/creating-buckets) for storing experiment outputs.\n",
- "\n",
- "1. [Create a service account](https://cloud.google.com/iam/docs/service-accounts-create#iam-service-accounts-create-console) with `Vertex AI User` and `Storage Object Admin` roles for deploying fine tuned model to Vertex AI endpoint."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "6c460088b873"
- },
- "source": [
- "Set the following variables for the experiment environment. The specified Cloud Storage bucket (`BUCKET_URI`) should be located in the specified region (`REGION`). Note that a multi-region bucket (eg. \"us\") is not considered a match for a single region covered by the multi-region range (eg. \"us-central1\")."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "id": "855d6b96f291"
- },
- "outputs": [],
- "source": [
- "# Cloud project id.\n",
- "PROJECT_ID = \"\" # @param {type:\"string\"}\n",
- "\n",
- "# The region you want to launch jobs in.\n",
- "REGION = \"\" # @param {type:\"string\"}\n",
- "\n",
- "# The Cloud Storage bucket for storing experiments output.\n",
- "# Start with gs:// prefix, e.g. gs://foo_bucket.\n",
- "BUCKET_URI = \"gs://\" # @param {type:\"string\"}\n",
- "\n",
- "! gcloud config set project $PROJECT_ID\n",
- "\n",
- "import os\n",
- "\n",
- "STAGING_BUCKET = os.path.join(BUCKET_URI, \"temporal\")\n",
- "EXPERIMENT_BUCKET = os.path.join(BUCKET_URI, \"peft\")\n",
- "DATA_BUCKET = os.path.join(EXPERIMENT_BUCKET, \"data\")\n",
- "MODEL_BUCKET = os.path.join(EXPERIMENT_BUCKET, \"model\")\n",
- "\n",
- "# The service account looks like:\n",
- "# '@.iam.gserviceaccount.com'\n",
- "# Please go to https://cloud.google.com/iam/docs/service-accounts-create#iam-service-accounts-create-console\n",
- "# and create service account with `Vertex AI User` and `Storage Object Admin` roles.\n",
- "# The service account for deploying fine tuned model.\n",
- "SERVICE_ACCOUNT = \"\" # @param {type:\"string\"}"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "e828eb320337"
- },
- "source": [
- "### Initialize Vertex AI API"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "id": "12cd25839741"
- },
- "outputs": [],
- "source": [
- "from google.cloud import aiplatform\n",
- "\n",
- "aiplatform.init(project=PROJECT_ID, location=REGION, staging_bucket=STAGING_BUCKET)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "2cc825514deb"
- },
- "source": [
- "### Define constants"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "id": "b42bd4fa2b2d"
- },
- "outputs": [],
- "source": [
- "# The pre-built training and serving docker images.\n",
- "TRAIN_DOCKER_URI = \"us-docker.pkg.dev/vertex-ai/vertex-vision-model-garden-dockers/pytorch-peft-train:20231130_0936_RC00\"\n",
- "PREDICTION_DOCKER_URI = \"us-docker.pkg.dev/vertex-ai/vertex-vision-model-garden-dockers/pytorch-peft-serve:20231130_0948_RC00\"\n",
- "VLLM_DOCKER_URI = \"us-docker.pkg.dev/vertex-ai/vertex-vision-model-garden-dockers/pytorch-vllm-serve:20231127_0916_RC00\"\n",
- "VLLM_GPTQ_DOCKER_URI = \"us-docker.pkg.dev/vertex-ai/vertex-vision-model-garden-dockers/pytorch-vllm-serve:gptq\"\n",
- "EVAL_DOCKER_URI = \"us-docker.pkg.dev/vertex-ai/vertex-vision-model-garden-dockers/pytorch-lm-evaluation-harness:20231011_0934_RC00\""
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "0c250872074f"
- },
- "source": [
- "### Define common functions"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "id": "354da31189dc"
- },
- "outputs": [],
- "source": [
- "from datetime import datetime\n",
- "from typing import Tuple\n",
- "\n",
- "from google.cloud import aiplatform\n",
- "\n",
- "\n",
- "def create_name_with_datetime(prefix: str) -> str:\n",
- " \"\"\"Creates a name with date time when triggering training or deployment\n",
- " jobs in Vertex AI.\n",
- " \"\"\"\n",
- " return prefix + datetime.now().strftime(\"_%Y%m%d_%H%M%S\")\n",
- "\n",
- "\n",
- "def deploy_model(\n",
- " model_name: str,\n",
- " model_id: str,\n",
- " finetuned_lora_model_path: str,\n",
- " service_account: str,\n",
- " task: str,\n",
- " precision_loading_mode: str = \"float16\",\n",
- " machine_type: str = \"n1-standard-8\",\n",
- " accelerator_type: str = \"NVIDIA_TESLA_V100\",\n",
- " accelerator_count: int = 1,\n",
- ") -> Tuple[aiplatform.Model, aiplatform.Endpoint]:\n",
- " \"\"\"Deploys trained models into Vertex AI.\"\"\"\n",
- " endpoint = aiplatform.Endpoint.create(display_name=f\"{model_name}-endpoint\")\n",
- " serving_env = {\n",
- " \"MODEL_ID\": model_id,\n",
- " \"PRECISION_LOADING_MODE\": precision_loading_mode,\n",
- " \"TASK\": task,\n",
- " \"DEPLOY_SOURCE\": \"notebook\",\n",
- " }\n",
- " if finetuned_lora_model_path:\n",
- " serving_env[\"FINETUNED_LORA_MODEL_PATH\"] = finetuned_lora_model_path\n",
- " model = aiplatform.Model.upload(\n",
- " display_name=model_name,\n",
- " serving_container_image_uri=PREDICTION_DOCKER_URI,\n",
- " serving_container_ports=[7080],\n",
- " serving_container_predict_route=\"/predictions/peft_serving\",\n",
- " serving_container_health_route=\"/ping\",\n",
- " serving_container_environment_variables=serving_env,\n",
- " model_garden_source_model_name=\"publishers/openlm-research/models/openllama\"\n",
- " )\n",
- " model.deploy(\n",
- " endpoint=endpoint,\n",
- " machine_type=machine_type,\n",
- " accelerator_type=accelerator_type,\n",
- " accelerator_count=accelerator_count,\n",
- " deploy_request_timeout=1800,\n",
- " service_account=service_account,\n",
- " system_labels={\"NOTEBOOK_NAME\": \"model_garden_pytorch_openllama_peft.ipynb\"},\n",
- " )\n",
- " return model, endpoint\n",
- "\n",
- "\n",
- "def deploy_model_vllm(\n",
- " model_name: str,\n",
- " model_id: str,\n",
- " service_account: str,\n",
- " machine_type: str = \"n1-standard-8\",\n",
- " accelerator_type: str = \"NVIDIA_TESLA_V100\",\n",
- " accelerator_count: int = 1,\n",
- " quantization_method: str = \"\",\n",
- ") -> Tuple[aiplatform.Model, aiplatform.Endpoint]:\n",
- " \"\"\"Deploys trained models with vLLM into Vertex AI.\"\"\"\n",
- " endpoint = aiplatform.Endpoint.create(display_name=f\"{model_name}-endpoint\")\n",
- "\n",
- " vllm_args = [\n",
- " \"--host=0.0.0.0\",\n",
- " \"--port=7080\",\n",
- " f\"--model={model_id}\",\n",
- " f\"--tensor-parallel-size={accelerator_count}\",\n",
- " \"--swap-space=16\",\n",
- " \"--gpu-memory-utilization=0.9\",\n",
- " \"--max-num-batched-tokens=4096\",\n",
- " \"--disable-log-stats\",\n",
- " ]\n",
- " if quantization_method:\n",
- " vllm_args.append(f\"--quantization={quantization_method}\")\n",
- " if quantization_method == \"gptq\":\n",
- " vllm_docker_uri = VLLM_GPTQ_DOCKER_URI\n",
- " else:\n",
- " vllm_docker_uri = VLLM_DOCKER_URI\n",
- "\n",
- " serving_env = {\n",
- " \"MODEL_ID\": \"openlm-research/open_llama\",\n",
- " \"DEPLOY_SOURCE\": \"notebook\",\n",
- " }\n",
- " model = aiplatform.Model.upload(\n",
- " display_name=model_name,\n",
- " serving_container_image_uri=vllm_docker_uri,\n",
- " serving_container_command=[\"python\", \"-m\", \"vllm.entrypoints.api_server\"],\n",
- " serving_container_args=vllm_args,\n",
- " serving_container_ports=[7080],\n",
- " serving_container_predict_route=\"/generate\",\n",
- " serving_container_health_route=\"/ping\",\n",
- " serving_container_environment_variables=serving_env,\n",
- " model_garden_source_model_name=\"publishers/openlm-research/models/openllama\"\n",
- " )\n",
- "\n",
- " model.deploy(\n",
- " endpoint=endpoint,\n",
- " machine_type=machine_type,\n",
- " accelerator_type=accelerator_type,\n",
- " accelerator_count=accelerator_count,\n",
- " deploy_request_timeout=1800,\n",
- " service_account=service_account,\n",
- " )\n",
- " return model, endpoint"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "65eaa62632d1"
- },
- "source": [
- "## Run inferences locally with prebuilt OpenLLaMA"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "id": "339601a9500b"
- },
- "outputs": [],
- "source": [
- "import torch\n",
- "from transformers import LlamaForCausalLM, LlamaTokenizer\n",
- "\n",
- "model_path = \"openlm-research/open_llama_3b\"\n",
- "\n",
- "tokenizer = LlamaTokenizer.from_pretrained(model_path)\n",
- "\n",
- "model = LlamaForCausalLM.from_pretrained(\n",
- " model_path,\n",
- " torch_dtype=torch.float16,\n",
- " device_map=\"auto\",\n",
- ")\n",
- "\n",
- "prompt = \"Q: What is the largest animal?\\nA:\"\n",
- "input_ids = tokenizer(prompt, return_tensors=\"pt\").input_ids\n",
- "input_ids = input_ids.to(\"cuda\")\n",
- "generation_output = model.generate(input_ids=input_ids, max_new_tokens=32)\n",
- "print(tokenizer.decode(generation_output[0]))"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "V7VOhhHGpUrj"
- },
- "source": [
- "## Deploy Prebuilt OpenLLaMA with vLLM\n",
- "\n",
- "This section deploys prebuilt OpenLLaMA models with [vLLM](https://github.com/vllm-project/vllm) on the Endpoint. The model deployment step will take ~15 minutes to complete.\n",
- "\n",
- "vLLM is a highly optimized LLM serving framework that can significantly increase serving throughput. The higher QPS you have, the more performance benefits you get from using vLLM."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "4GTNnnuYqrW_"
- },
- "source": [
- "Set the prebuilt model id."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "id": "kLsRoc4Kqrkx"
- },
- "outputs": [],
- "source": [
- "prebuilt_model_id = \"openlm-research/open_llama_7b\" # @param [\"openlm-research/open_llama_3b\", \"openlm-research/open_llama_7b\", \"openlm-research/open_llama_13b\"]"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "id": "YI0vaDi6p2fi"
- },
- "outputs": [],
- "source": [
- "# Finds Vertex AI prediction supported accelerators and regions in\n",
- "# https://cloud.google.com/vertex-ai/docs/predictions/configure-compute.\n",
- "\n",
- "# Sets V100 to deploy open_llama_3b and open_llama_7b.\n",
- "# V100 serving has better throughput and latency performance than L4 serving.\n",
- "machine_type = \"n1-standard-8\"\n",
- "accelerator_type = \"NVIDIA_TESLA_V100\"\n",
- "accelerator_count = 1\n",
- "\n",
- "# Sets L4 to deploy open_llama_3b and open_llama_7b.\n",
- "# L4 serving is more cost efficient than V100 serving.\n",
- "# machine_type = \"g2-standard-8\"\n",
- "# accelerator_type = \"NVIDIA_L4\"\n",
- "# accelerator_count = 1\n",
- "\n",
- "# Sets 2 V100 to deploy open_llama_13b.\n",
- "# V100 serving has better throughput and latency performance than L4 serving.\n",
- "# machine_type = \"n1-standard-16\"\n",
- "# accelerator_type = \"NVIDIA_TESLA_V100\"\n",
- "# accelerator_count = 2\n",
- "\n",
- "# Sets 2 L4 to deploy open_llama_13b.\n",
- "# L4 serving is more cost efficient than V100 serving.\n",
- "# machine_type = \"g2-standard-24\"\n",
- "# accelerator_type = \"NVIDIA_L4\"\n",
- "# accelerator_count = 2\n",
- "\n",
- "if prebuilt_model_id == \"openlm-research/open_llama_3b\":\n",
- " # vLLM currently does not support OpenLLaMA 3B.\n",
- " precision_loading_mode = \"float16\"\n",
- " model_without_peft, endpoint_without_peft = deploy_model(\n",
- " model_name=get_job_name_with_datetime(prefix=\"openllama-serve\"),\n",
- " model_id=model_id,\n",
- " finetuned_lora_model_path=\"\", # This will avoid override finetuning models.\n",
- " service_account=SERVICE_ACCOUNT,\n",
- " task=\"causal-language-modeling-lora\",\n",
- " precision_loading_mode=precision_loading_mode,\n",
- " machine_type=machine_type,\n",
- " accelerator_type=accelerator_type,\n",
- " accelerator_count=accelerator_count,\n",
- " )\n",
- "else:\n",
- " model_without_peft, endpoint_without_peft = deploy_model_vllm(\n",
- " model_name=create_name_with_datetime(prefix=\"openllama-serve-vllm\"),\n",
- " model_id=prebuilt_model_id,\n",
- " service_account=SERVICE_ACCOUNT,\n",
- " machine_type=machine_type,\n",
- " accelerator_type=accelerator_type,\n",
- " accelerator_count=accelerator_count,\n",
- " )"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "dWYmYWoqqBuZ"
- },
- "source": [
- "NOTE: The prebuilt model weights will be downloaded on the fly from the original location after the deployment succeeds. Thus, an additional 5 minutes of waiting time is needed **after** the above model deployment step succeeds and before you can run the next step below. Otherwise you might see a `ServiceUnavailable: 503 502:Bad Gateway` error when you send requests to the endpoint.\n",
- "\n",
- "Once deployment succeeds, you can send requests to the endpoint with text prompts. If you are interested in additional serving parameters, please refer to the vLLM GitHub [examples/api_client.py](https://github.com/vllm-project/vllm/blob/main/examples/api_client.py) for more details."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "id": "fjO4z3qAp3pK"
- },
- "outputs": [],
- "source": [
- "instance = {\n",
- " \"prompt\": \"Hi, Google. How are you doing?\",\n",
- " \"n\": 1,\n",
- " \"max_tokens\": 32,\n",
- " \"temperature\": 1.0,\n",
- " \"top_p\": 1.0,\n",
- " \"top_k\": 10,\n",
- "}\n",
- "response = endpoint_without_peft.predict(instances=[instance])\n",
- "print(response.predictions[0])"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "e70e3519ff8b"
- },
- "source": [
- "## Finetune and deploy OpenLLaMA with PEFT\n",
- "\n",
- "This section demonstrates how to finetune the OpenLLaMA-7b model, merge the finetuned LoRA adapter with the base model, and serve using vLLM."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "5qCrm_kJH5cz"
- },
- "source": [
- "Set the base model id."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "id": "N3UBLiYrM3sU"
- },
- "outputs": [],
- "source": [
- "model_id = \"openlm-research/open_llama_7b\" # @param [\"openlm-research/open_llama_3b\", \"openlm-research/open_llama_7b\", \"openlm-research/open_llama_13b\"]"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "iWGwJHqI7LMs"
- },
- "source": [
- "### Finetune"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "KKEYoRfiHDVv"
- },
- "source": [
- "Use the Vertex AI SDK to create and run the custom training jobs with Vertex AI Model Garden training images.\n",
- "\n",
- "This example uses the dataset [Abirate/english_quotes](https://huggingface.co/datasets/Abirate/english_quotes). You can either use a [dataset from huggingface](https://huggingface.co/datasets) or a custom JSONL dataset in [Vertex text model dataset format](https://cloud.google.com/vertex-ai/docs/generative-ai/models/tune-text-models-supervised#dataset-format) stored in Cloud Storage. The `template` parameter is optional.\n",
- "\n",
- "In order to make the finetuning efficiently, we enabled quantization for loading pretrained models for finetuning LoRA models. Precision options include `\"4bit\"`, `\"8bit\"`, `\"float16\"` (default) and `\"float32\"`, and the precision can be set via `\"--precision_mode\"`. The peak GPU memory usages are ~7G, ~10G and ~16G for finetuning LoRA models for [openlm-research/open_llama_3b](https://huggingface.co/openlm-research/open_llama_3b), [openlm-research/open_llama_7b](https://huggingface.co/openlm-research/open_llama_7b), and [openlm-research/open_llama_13b](https://huggingface.co/openlm-research/open_llama_13b) separately with default training parameters and the example dataset. `open_llama_3b` and `open_llama_7b` can be finetuned on **1 V100 (16G)** and **1 L4 (24G)**, and `open_llama_13b` can be finetuned on **1 L4 (24G)**.\n",
- "\n",
- "In this section, the finetuned LoRA adapter will be saved to a GCS bucket specified by the variable `lora_adapter_dir` below; and we merge the LoRa adapter with the base model, and save it to a separate GCS bucket specified by `merged_model_output_dir` below.\n"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "0810ef72dd9f"
- },
- "source": [
- "#### [Optional] Finetune with a custom dataset\n",
- "\n",
- "To use a custom dataset, you should supply a `gs://` URI to a JSONL file in [Vertex text model dataset format](https://cloud.google.com/vertex-ai/docs/generative-ai/models/tune-text-models-supervised#dataset-format) in the `dataset_name` below.\n",
- "\n",
- "For example, here is one data point from the sample dataset `gs://cloud-samples-data/vertex-ai/model-evaluation/peft_train_sample.jsonl`:\n",
- "\n",
- "```json\n",
- "{\"input_text\":\"TRANSCRIPT: \\nREASON FOR EVALUATION:,\\n\\n LABEL:\",\"output_text\":\"Chiropractic\"}\n",
- "```\n",
- "\n",
- "To use this sample dataset that contains `input_text` and `output_text` fields, set `dataset_name` to `gs://cloud-samples-data/vertex-ai/model-evaluation/peft_train_sample.jsonl` and `template` to `vertex_sample`. For advanced usage with custom datatset fields, see [the template example](https://github.com/tloen/alpaca-lora/blob/main/templates/alpaca.json) and supply your own JSON template as `gs://` URIs."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "id": "65467b361315"
- },
- "outputs": [],
- "source": [
- "# Huggingface dataset name or gs:// URI to a custom JSONL dataset.\n",
- "dataset_name = \"Abirate/english_quotes\" # @param {type:\"string\"}\n",
- "# Optional. Template name or gs:// URI to a custom template.\n",
- "template = \"\" # @param {type:\"string\"}\n",
- "\n",
- "# Worker pool spec.\n",
- "# Finetunes open_llama_3b and open_llama_7b with 1 V100 (16G).\n",
- "machine_type = \"n1-standard-8\"\n",
- "accelerator_type = \"NVIDIA_TESLA_V100\"\n",
- "accelerator_count = 1\n",
- "\n",
- "# Finetunes open_llama_3b and open_llama_7b with 1 L4 (24G).\n",
- "# machine_type = \"g2-standard-8\"\n",
- "# accelerator_type = \"NVIDIA_L4\"\n",
- "# accelerator_count = 1\n",
- "\n",
- "# Finetunes open_llama_13b with 1 L4 (24G).\n",
- "# machine_type = \"g2-standard-8\"\n",
- "# accelerator_type = \"NVIDIA_L4\"\n",
- "# accelerator_count = 1\n",
- "\n",
- "# Finetunes open_llama_13b with 1 A100 (40G).\n",
- "# machine_type = \"a2-highgpu-1g\"\n",
- "# accelerator_type = \"NVIDIA_TESLA_A100\"\n",
- "# accelerator_count = 1\n",
- "\n",
- "replica_count = 1\n",
- "\n",
- "\n",
- "# Setup training job.\n",
- "job_name = create_name_with_datetime(\"openllama-lora-train\")\n",
- "train_job = aiplatform.CustomContainerTrainingJob(\n",
- " display_name=job_name,\n",
- " container_uri=TRAIN_DOCKER_URI,\n",
- ")\n",
- "\n",
- "# Create a GCS folder to store the LORA adapter.\n",
- "lora_adapter_dir = create_name_with_datetime(\"openllama-lora-adapter\")\n",
- "lora_output_dir = os.path.join(MODEL_BUCKET, lora_adapter_dir)\n",
- "lora_output_dir_gcsfuse = lora_output_dir.replace(\"gs://\", \"/gcs/\")\n",
- "\n",
- "# Create a GCS folder to store the merged model with the base model and the\n",
- "# finetuned LORA adapter.\n",
- "merged_model_dir = create_name_with_datetime(\"openllama-merged-model\")\n",
- "merged_model_output_dir = os.path.join(MODEL_BUCKET, merged_model_dir)\n",
- "merged_model_output_dir_gcsfuse = merged_model_output_dir.replace(\"gs://\", \"/gcs/\")\n",
- "\n",
- "# Pass training arguments and launch job.\n",
- "train_job.run(\n",
- " args=[\n",
- " \"--task=causal-language-modeling-lora\",\n",
- " f\"--pretrained_model_id={model_id}\",\n",
- " f\"--dataset_name={dataset_name}\",\n",
- " f\"--output_dir={lora_output_dir_gcsfuse}\",\n",
- " f\"--merge_base_and_lora_output_dir={merged_model_output_dir_gcsfuse}\",\n",
- " \"--lora_rank=16\",\n",
- " \"--lora_alpha=32\",\n",
- " \"--lora_dropout=0.05\",\n",
- " \"--warmup_steps=10\",\n",
- " \"--max_steps=10\",\n",
- " \"--learning_rate=2e-4\",\n",
- " f\"--template={template}\",\n",
- " ],\n",
- " replica_count=replica_count,\n",
- " machine_type=machine_type,\n",
- " accelerator_type=accelerator_type,\n",
- " accelerator_count=accelerator_count,\n",
- " boot_disk_size_gb=500,\n",
- ")\n",
- "\n",
- "print(\"The finetuned Lora adapter can be found at: \", lora_output_dir)\n",
- "print(\n",
- " \"The finetuned Lora adapter merged with the base model can be found at: \",\n",
- " merged_model_output_dir,\n",
- ")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "53b8a1ad6def"
- },
- "source": [
- "### [Optional] Hyperparameter tuning\n",
- "\n",
- "You can use the Vertex AI SDK to create and run the [hyperparameter tuning job](https://cloud.google.com/vertex-ai/docs/training/hyperparameter-tuning-overview) to obtain a better performance by experimenting with different hyperparameters such as learning rates.\n",
- "\n",
- "Define the following specifications:\n",
- "\n",
- "- `worker_pool_specs`: Dictionary specifying the machine type and Docker image.\n",
- "\n",
- "- `parameter_spec`: Dictionary specifying the parameters to optimize. The dictionary key is the string assigned to the command line argument for each hyperparameter in your training application code, and the dictionary value is the parameter specification. The parameter specification includes the type, min/max values, and scale for the hyperparameter.\n",
- "\n",
- "- `metric_spec`: Dictionary specifying the metric to optimize. The dictionary key is the hyperparameter_metric_tag that you set in your training application code, and the value is the optimization goal.\n",
- "\n",
- "The following 4bit QLoRA experiment results show the effectiveness of hyperparameter tuning evaluated on the ARC Challenge dataset (for reference only):\n",
- "\n",
- "| Model | Training time | Trials | Parallel Trials | GPU | ∆arc challenge | ∆hellaswag | ∆truthfulqa_mc | cost |\n",
- "|---------------|---------------|--------|-----------------|------|----------------|------------|----------------|----------|\n",
- "| Openllama-3b | 2d 10hrs | 8 | 1 | L4x1 | +1.62 | +7.32 | +3.34 | \\$29.0232 |\n",
- "| Openllama-7b | 1d 4hrs | 8 | 2 | L4x1 | +2.82 | +3.55 | +6.68 | \\$47.8016 |\n",
- "| Openllama-13b | 6d 10hrs | 8 | 2 | L4x1 | +1.01 | +3.67 | +6.19 | \\$87.9208 |\n",
- "\n",
- "The following example runs 8 trials on `timdettmers/openassistant-guanaco` with different learning rates, and evaluates the model on `arc_challenge` dataset. You can customize the search space by extending the range of learning rates, adding other parameters such as LoRA rank, etc. Please refer to the [hyperparameter tuning documentation](https://cloud.google.com/vertex-ai/docs/training/hyperparameter-tuning-overview) for more information."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "id": "a81402f70641"
- },
- "outputs": [],
- "source": [
- "# Huggingface dataset name or gs:// URI to a custom JSONL dataset.\n",
- "dataset_name = \"timdettmers/openassistant-guanaco\" # @param {type:\"string\"}\n",
- "# Optional. Template name or gs:// URI to a custom template.\n",
- "template = \"\" # @param {type:\"string\"}\n",
- "\n",
- "hpt_precision_mode = \"4bit\"\n",
- "\n",
- "# Worker pool spec for 4bit finetuning.\n",
- "\n",
- "# Finetunes Openllama 3B / 7B / 13B with 1 L4 (24G).\n",
- "machine_type = \"g2-standard-8\"\n",
- "accelerator_type = \"NVIDIA_L4\"\n",
- "accelerator_count = 1"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "55ed2a2a9d54"
- },
- "source": [
- "### [Optional] Custom evaluation dataset\n",
- "\n",
- "To obtain a model with better performance on some specific tasks, you might want to run hyperparameter tuning with a custom evaluation dataset. The hyperparameter tuning service will pick the model according to the evaluation dataset and the metrics you selected. You can use any of the following tasks as the `eval_task` in the code cell below:\n",
- "\n",
- "1. The name of a [lm-evaluation-harness task](https://github.com/EleutherAI/lm-evaluation-harness/tree/big-refactor/lm_eval/tasks).\n",
- "\n",
- "2. `custom_likelihood`. Then, add a flag `--eval_dataset_path=`. The JSONL file must be in the format in Vertex AI language model's [prepare evaluation dataset](https://cloud.google.com/vertex-ai/docs/generative-ai/models/evaluate-models#classification) page.\n",
- "\n",
- "3. `builtin_eval`. The built-in evaluation loop of the trainer will be used to evaluate the model instead of the [lm-evaluation-harness](https://github.com/EleutherAI/lm-evaluation-harness) library. You can supply any eval dataset in the same format as the training dataset by specifying `--eval_dataset_path`, `--eval_split`, `--eval_template`, and `--eval_column`."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "id": "7864cff27197"
- },
- "outputs": [],
- "source": [
- "from google.cloud.aiplatform import hyperparameter_tuning as hpt\n",
- "\n",
- "eval_task = \"arc_challenge\" # @param {type:\"string\"}\n",
- "eval_metric_name = \"acc_norm\" # @param {type:\"string\"}\n",
- "\n",
- "# Runs 10 training steps as a minimal example. Use 1000 to reproduce the experiment results.\n",
- "max_steps = 10 # @param {type:\"integer\"}\n",
- "# Evaluates the model on 10 examples. Use 10000 to reproduce the experiment results.\n",
- "eval_limit = 10 # @param {type:\"integer\"}\n",
- "\n",
- "flags = {\n",
- " \"learning_rate\": 1e-5,\n",
- " \"precision_mode\": hpt_precision_mode,\n",
- " \"task\": \"instruct-lora\",\n",
- " \"pretrained_model_id\": model_id,\n",
- " \"output_dir\": lora_output_dir_gcsfuse,\n",
- " \"warmup_steps\": 10,\n",
- " \"max_steps\": max_steps,\n",
- " \"lora_rank\": 32,\n",
- " \"lora_alpha\": 64,\n",
- " \"lora_dropout\": 0.05,\n",
- " \"dataset_name\": dataset_name,\n",
- " \"eval_steps\": max_steps + 1, # Only evaluates in the end.\n",
- " \"eval_tasks\": eval_task,\n",
- " \"eval_limit\": eval_limit,\n",
- " \"eval_metric_name\": eval_metric_name,\n",
- " \"merge_base_and_lora_output_dir\": merged_model_output_dir_gcsfuse,\n",
- "}\n",
- "\n",
- "# Add labels for the finetuning job.\n",
- "labels = {\n",
- " \"mg-source\": \"notebook\",\n",
- " \"mg-notebook-name\": \"model_garden_pytorch_openllama_peft.ipynb\".split(\".\")[0],\n",
- "}\n",
- "\n",
- "labels[\"mg-tune\"] = \"publishers-openlm-research-models-openllama\"\n",
- "versioned_model_id = prebuilt_model_id.split(\"/\")[1].replace(\"_\", \"-\")\n",
- "labels[\"versioned-mg-tune\"] = f\"{labels['mg-tune']}-{versioned_model_id}\"\n",
- "\n",
- "worker_pool_specs = [\n",
- " {\n",
- " \"machine_spec\": {\n",
- " \"machine_type\": machine_type,\n",
- " \"accelerator_type\": accelerator_type,\n",
- " \"accelerator_count\": accelerator_count,\n",
- " },\n",
- " \"replica_count\": replica_count,\n",
- " \"container_spec\": {\n",
- " \"image_uri\": TRAIN_DOCKER_URI,\n",
- " \"args\": [\"--{}={}\".format(k, v) for k, v in flags.items()],\n",
- " },\n",
- " }\n",
- "]\n",
- "metric_spec = {\"model_performance\": \"maximize\"}\n",
- "parameter_spec = {\n",
- " \"learning_rate\": hpt.DoubleParameterSpec(min=1e-5, max=1e-4, scale=\"linear\"),\n",
- "}\n",
- "train_job = aiplatform.CustomJob(\n",
- " display_name=job_name,\n",
- " worker_pool_specs=worker_pool_specs,\n",
- " staging_bucket=STAGING_BUCKET,\n",
- " labels=labels,\n",
- ")\n",
- "\n",
- "train_hpt_job = aiplatform.HyperparameterTuningJob(\n",
- " display_name=f\"{job_name}_hpt\",\n",
- " custom_job=train_job,\n",
- " metric_spec=metric_spec,\n",
- " parameter_spec=parameter_spec,\n",
- " max_trial_count=8,\n",
- " parallel_trial_count=2,\n",
- ")\n",
- "\n",
- "train_hpt_job.run()\n",
- "\n",
- "print(\"Trained models were saved in: \", lora_output_dir)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "afad2431f37f"
- },
- "source": [
- "Then, find the best trial from the hyperparameter tuning job."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "id": "138b09ee6313"
- },
- "outputs": [],
- "source": [
- "best_trial_id = max(\n",
- " train_hpt_job.trials, key=lambda trial: trial.final_measurement.metrics[0].value\n",
- ").id\n",
- "lora_output_dir = os.path.join(lora_output_dir, f\"trial_{best_trial_id}\")\n",
- "lora_output_dir_gcsfuse = lora_output_dir.replace(\"gs://\", \"/gcs/\")\n",
- "print(f\"Best trial {best_trial_id} saved model in:\", lora_output_dir)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "jqmCtkGnhDmp"
- },
- "source": [
- "### Deploy with vLLM\n",
- "This section uploads the model to Model Registry and deploys it on the Endpoint. vLLM currently does not support serving finetuned [openlm-research/open_llama_3b](https://huggingface.co/openlm-research/open_llama_3b).\n",
- "\n",
- "The model deployment step will take ~15 minutes to complete.\n",
- "\n",
- "The peak GPU memory usages for [openlm-research/open_llama_3b](https://huggingface.co/openlm-research/open_llama_3b), [openlm-research/open_llama_7b](https://huggingface.co/openlm-research/open_llama_7b), and [openlm-research/open_llama_13b](https://huggingface.co/openlm-research/open_llama_13b) with LoRA weights are ~5.3G, ~8.7G and ~15.2G respectively with the default settings.\n",
- "\n",
- "NOTE: vLLM requires a merged model with the base model and the finetuned LoRA adapter. Based on your business need, if you need the base model and the finetuned LoRA weight to be served separately, please consider using the regular Vertex serving instead.\n"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "id": "bf55e38815dc"
- },
- "outputs": [],
- "source": [
- "# Finds Vertex AI prediction supported accelerators and regions in\n",
- "# https://cloud.google.com/vertex-ai/docs/predictions/configure-compute.\n",
- "\n",
- "# Sets V100 to deploy open_llama_3b and open_llama_7b.\n",
- "# V100 serving has better throughput and latency performance than L4 serving.\n",
- "machine_type = \"n1-standard-8\"\n",
- "accelerator_type = \"NVIDIA_TESLA_V100\"\n",
- "accelerator_count = 1\n",
- "\n",
- "# Sets L4 to deploy open_llama_3b and open_llama_7b.\n",
- "# L4 serving is more cost efficient than V100 serving.\n",
- "# machine_type = \"g2-standard-8\"\n",
- "# accelerator_type = \"NVIDIA_L4\"\n",
- "# accelerator_count = 1\n",
- "\n",
- "# Sets 2 V100 to deploy open_llama_13b.\n",
- "# V100 serving has better throughput and latency performance than L4 serving.\n",
- "# machine_type = \"n1-standard-16\"\n",
- "# accelerator_type = \"NVIDIA_TESLA_V100\"\n",
- "# accelerator_count = 2\n",
- "\n",
- "# Sets 2 L4 to deploy open_llama_13b.\n",
- "# L4 serving is more cost efficient than V100 serving.\n",
- "# machine_type = \"g2-standard-24\"\n",
- "# accelerator_type = \"NVIDIA_L4\"\n",
- "# accelerator_count = 2\n",
- "\n",
- "if prebuilt_model_id == \"openlm-research/open_llama_3b\":\n",
- " # vLLM currently does not support OpenLLaMA 3B.\n",
- " precision_loading_mode = \"float16\"\n",
- " model_with_peft, endpoint_with_peft = deploy_model(\n",
- " model_name=get_job_name_with_datetime(prefix=\"openllama-peft-serve\"),\n",
- " model_id=model_id,\n",
- " finetuned_lora_model_path=lora_output_dir, # This will avoid override finetuning models.\n",
- " service_account=SERVICE_ACCOUNT,\n",
- " task=\"causal-language-modeling-lora\",\n",
- " precision_loading_mode=precision_loading_mode,\n",
- " machine_type=machine_type,\n",
- " accelerator_type=accelerator_type,\n",
- " accelerator_count=accelerator_count,\n",
- " )\n",
- "else:\n",
- " model_with_peft, endpoint_with_peft = deploy_model_vllm(\n",
- " model_name=create_name_with_datetime(prefix=\"openllama-peft-serve-vllm\"),\n",
- " model_id=merged_model_output_dir,\n",
- " service_account=SERVICE_ACCOUNT,\n",
- " machine_type=machine_type,\n",
- " accelerator_type=accelerator_type,\n",
- " accelerator_count=accelerator_count,\n",
- " )\n",
- "\n",
- "print(\"endpoint_name:\", endpoint_with_peft.name)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "80b3fd2ace09"
- },
- "source": [
- "NOTE: After the deployment succeeds, the base model weights will be downloaded on the fly from the original location and LoRA model weights will be downloaded from the GCS bucket used in training above. Thus, an additional 5 minutes of waiting time is needed **after** the above model deployment step succeeds and before you can run the next step below. Otherwise you might see a `ServiceUnavailable: 503 502:Bad Gateway` error when you send requests to the endpoint.\n",
- "\n",
- "Once deployment succeeds, you can send requests to the endpoint with text prompts. Parameters supported by vLLM can be found [here](https://github.com/vllm-project/vllm/blob/2e8e49fce3775e7704d413b2f02da6d7c99525c9/vllm/sampling_params.py#L23-L64)."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "id": "4ab04da3ec9a"
- },
- "outputs": [],
- "source": [
- "instance = {\n",
- " \"prompt\": \"Hi, Google. How are you doing?\",\n",
- " \"n\": 1,\n",
- " \"max_tokens\": 32,\n",
- " \"temperature\": 1.0,\n",
- " \"top_p\": 1.0,\n",
- " \"top_k\": 10,\n",
- "}\n",
- "response = endpoint_with_peft.predict(instances=[instance])\n",
- "print(response.predictions[0])"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "vhDf9dNNn4bP"
- },
- "source": [
- "### [Optional] Merge a previously trained LoRA adapter with the base model\n",
- "\n",
- "This section demonstrates how to merge a previously trained LoRA adapter with a base model, and save the merged model to a GCS bucket. Please be aware that the LoRA adapter should be trained on the same base model."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "id": "YHdru1aRqRFF"
- },
- "outputs": [],
- "source": [
- "merge_job_name = create_name_with_datetime(prefix=\"openllama-peft-merge\")\n",
- "\n",
- "# The base model to be merged upon. It can be a huggingface model id, or a GCS\n",
- "# path where the base model was stored.\n",
- "base_model_dir = \"gs://\" # @param {type:\"string\"}\n",
- "# The previously trained LoRA adapter. It needs to be stored in a GCS path.\n",
- "finetuned_lora_adapter_dir = \"\" # @param {type:\"string\"}\n",
- "\n",
- "# The GCS path to save the merged model\n",
- "merged_model_output_dir = os.path.join(MODEL_BUCKET, merge_job_name)\n",
- "merged_model_output_dir_gcsfuse = merged_model_output_dir.replace(\"gs://\", \"/gcs/\")\n",
- "\n",
- "# Worker pool spec.\n",
- "# Merges open_llama_3b and open_llama_7b with 1 V100 (16G).\n",
- "machine_type = \"n1-standard-8\"\n",
- "accelerator_type = \"NVIDIA_TESLA_V100\"\n",
- "\n",
- "# Merges open_llama_3b and open_llama_7b with 1 L4 (24G).\n",
- "# machine_type = \"g2-standard-8\"\n",
- "# accelerator_type = \"NVIDIA_L4\"\n",
- "\n",
- "# Merges open_llama_13b with 1 L4 (24G).\n",
- "# machine_type = \"g2-standard-8\"\n",
- "# accelerator_type = \"NVIDIA_L4\"\n",
- "\n",
- "# Merges open_llama_13b with 1 A100 (40G).\n",
- "# machine_type = \"a2-highgpu-1g\"\n",
- "# accelerator_type = \"NVIDIA_TESLA_A100\"\n",
- "\n",
- "worker_pool_specs = [\n",
- " {\n",
- " \"machine_spec\": {\n",
- " \"machine_type\": machine_type,\n",
- " \"accelerator_type\": accelerator_type,\n",
- " \"accelerator_count\": 1,\n",
- " },\n",
- " \"replica_count\": 1,\n",
- " \"container_spec\": {\n",
- " \"image_uri\": TRAIN_DOCKER_URI,\n",
- " \"command\": [],\n",
- " \"args\": [\n",
- " \"--task=merge-causal-language-model-lora\",\n",
- " \"--merge_model_precision_mode=float16\",\n",
- " \"--pretrained_model_id=%s\" % base_model_dir,\n",
- " \"--finetuned_lora_model_dir=%s\" % finetuned_lora_adapter_dir,\n",
- " \"--merge_base_and_lora_output_dir=%s\" % merged_model_output_dir_gcsfuse,\n",
- " ],\n",
- " },\n",
- " }\n",
- "]\n",
- "\n",
- "merge_custom_job = aiplatform.CustomJob(\n",
- " display_name=merge_job_name,\n",
- " project=PROJECT_ID,\n",
- " worker_pool_specs=worker_pool_specs,\n",
- " staging_bucket=STAGING_BUCKET,\n",
- ")\n",
- "\n",
- "merge_custom_job.run()\n",
- "\n",
- "print(\"The merged model is stored at: \", merged_model_output_dir)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "wi6UZnldXpdi"
- },
- "source": [
- "## Quantize and deploy OpenLLaMA 2 models\n",
- "\n",
- "This section demonstrates post-training quantization of OpenLLaMA models with Vertex Custom Job. Quantization reduces the memory required by a model while attempting to retain the same performance. Two such algorithms to do so are AWQ and GPTQ. Read more about AWQ in the following publication: [AWQ: Activation-aware Weight Quantization for LLM Compression and Acceleration](https://arxiv.org/abs/2306.00978). Read more about GPTQ in the following publication: [GPTQ: Accurate Post-Training Quantization for Generative Pre-trained Transformers\n",
- "](https://arxiv.org/abs/2210.17323)."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "HTUo8dWSo8t9"
- },
- "source": [
- "### Quantize OpenLLaMA models\n",
- "\n",
- "Quantization reduces the amount of GPU required to serve a model by reducing the bit precision of the weights while minimizing drop in performance. Serving quantized models on VLLM requires models to be quantized to 4 bits. It is recommended to first search if a model has already been quantized and made publicly available: [AWQ](https://huggingface.co/TheBloke?search_models=-awq) and [GPTQ](https://huggingface.co/TheBloke?search_models=-gptq).\n",
- "\n",
- "Quantizing models with AWQ with 1 NVIDIA_L4 GPU will take around\n",
- "20 minutes for OpenLLaMA 3B, 30 minutes for OpenLLaMA 7B, and 1 hour for OpenLLaMA 13B.\n",
- "\n",
- "Quantizing models with GPTQ with 1 NVIDIA_L4 GPU will take around 30 minutes for OpenLLaMA 3B, 45 minutes for OpenLLaMA 7B, and 1.5 hour for OpenLLaMA 13B. Finetuned models can also be quantized, so long as the LoRA weights are merged with the base model."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "id": "_YB31uAEpusN"
- },
- "outputs": [],
- "source": [
- "# Setup quantization job.\n",
- "\n",
- "# Set `finetuned_model_path` to `merged_model_output_dir` from the previous\n",
- "# section above to quantize the finetuned model, if not set the base model will\n",
- "# be quantized.\n",
- "finetuned_model_path = \"\" # @param {type:\"string\"}\n",
- "if finetuned_model_path:\n",
- " prequantized_model_path = finetuned_model_path\n",
- "else:\n",
- " prequantized_model_path = model_id\n",
- "\n",
- "quantization_method = \"awq\" # @param [\"awq\", \"gptq\"]\n",
- "quantization_job_name = get_job_name_with_datetime(\n",
- " f\"openllama-{quantization_method}-quantize\"\n",
- ")\n",
- "\n",
- "quantization_output_dir = os.path.join(MODEL_BUCKET, quantization_job_name)\n",
- "quantization_output_dir_gcsfuse = quantization_output_dir.replace(\"gs://\", \"/gcs/\")\n",
- "\n",
- "# Worker pool spec.\n",
- "\n",
- "# Sets 1 L4 (24G) to quantize OpenLLaMA model.\n",
- "machine_type = \"g2-standard-16\"\n",
- "accelerator_type = \"NVIDIA_L4\"\n",
- "accelerator_count = 1\n",
- "\n",
- "\n",
- "# Quantization parameters.\n",
- "quantization_precision_mode = \"4bit\"\n",
- "if quantization_method == \"awq\":\n",
- " awq_dataset_name = \"pileval\"\n",
- " group_size = 64\n",
- " quantization_args = [\n",
- " \"--task=quantize-model\",\n",
- " f\"--quantization_method={quantization_method}\",\n",
- " f\"--pretrained_model_id={model_id}\",\n",
- " f\"--quantization_precision_mode={quantization_precision_mode}\",\n",
- " f\"--quantization_output_dir={quantization_output_dir_gcsfuse}\",\n",
- " f\"--quantization_dataset_name={awq_dataset_name}\",\n",
- " f\"--group_size={group_size}\",\n",
- " ]\n",
- "else:\n",
- " # The original datasets used in GPTQ paper [\"wikitext2\",\"c4\",\"c4-new\",\"ptb\",\"ptb-new\"].\n",
- " gptq_dataset_name = \"c4\" # @param {type:\"string\"}\n",
- " gptq_precision_mode = \"4bit\"\n",
- " group_size = -1\n",
- " damp_percent = 0.1\n",
- " desc_act = True\n",
- " quantization_args = [\n",
- " \"--task=quantize-model\",\n",
- " f\"--quantization_method={quantization_method}\",\n",
- " f\"--pretrained_model_id={model_id}\",\n",
- " f\"--quantization_precision_mode={quantization_precision_mode}\",\n",
- " f\"--quantization_output_dir={quantization_output_dir_gcsfuse}\",\n",
- " f\"--quantization_dataset_name={gptq_dataset_name}\",\n",
- " f\"--group_size={group_size}\",\n",
- " f\"--damp_percent={damp_percent}\",\n",
- " f\"--desc_act={desc_act}\",\n",
- " ]\n",
- "\n",
- "# Pass quantization arguments and launch job.\n",
- "worker_pool_specs = [\n",
- " {\n",
- " \"machine_spec\": {\n",
- " \"machine_type\": machine_type,\n",
- " \"accelerator_type\": accelerator_type,\n",
- " \"accelerator_count\": accelerator_count,\n",
- " },\n",
- " \"replica_count\": 1,\n",
- " \"disk_spec\": {\n",
- " \"boot_disk_type\": \"pd-ssd\",\n",
- " \"boot_disk_size_gb\": 500,\n",
- " },\n",
- " \"container_spec\": {\n",
- " \"image_uri\": TRAIN_DOCKER_URI,\n",
- " \"env\": [\n",
- " {\n",
- " \"name\": \"PYTORCH_CUDA_ALLOC_CONF\",\n",
- " \"value\": \"max_split_size_mb:32\",\n",
- " },\n",
- " ],\n",
- " \"command\": [],\n",
- " \"args\": quantization_args,\n",
- " },\n",
- " }\n",
- "]\n",
- "\n",
- "print(f\"Quantizing {prequantized_model_path}.\")\n",
- "quantize_job = aiplatform.CustomJob(\n",
- " display_name=quantization_job_name,\n",
- " project=PROJECT_ID,\n",
- " worker_pool_specs=worker_pool_specs,\n",
- " staging_bucket=STAGING_BUCKET,\n",
- ")\n",
- "quantize_job.run()\n",
- "\n",
- "print(\"Quantized models were saved in: \", quantization_output_dir)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "ocm3m2Hzr4ln"
- },
- "source": [
- "### Deploy quantized models with Google Cloud Text Moderation\n",
- "This section uploads the model to Model Registry and deploys it on the Endpoint.\n",
- "\n",
- "The model deployment step will take 15 minutes to 1 hour to complete, depending on the model sizes."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "id": "DdINEB5Ur9FX"
- },
- "outputs": [],
- "source": [
- "# Finds Vertex AI prediction supported accelerators and regions in\n",
- "# https://cloud.google.com/vertex-ai/docs/predictions/configure-compute.\n",
- "\n",
- "# Sets 1 L4 (24G) to deploy OpenLLaMA models.\n",
- "machine_type = \"g2-standard-8\"\n",
- "accelerator_type = \"NVIDIA_L4\"\n",
- "accelerator_count = 1\n",
- "\n",
- "\n",
- "if prebuilt_model_id == \"openlm-research/open_llama_3b\":\n",
- " # vLLM currently does not support OpenLLaMA 3B.\n",
- " precision_loading_mode = \"float16\"\n",
- " model_quantized_vllm, endpoint_quantized_vllm = deploy_model(\n",
- " model_name=get_job_name_with_datetime(prefix=\"openllama-quantized-serve\"),\n",
- " model_id=quantization_output_dir,\n",
- " finetuned_lora_model_path=\"\", # This will avoid override finetuning models.\n",
- " service_account=SERVICE_ACCOUNT,\n",
- " task=\"causal-language-modeling-lora\",\n",
- " precision_loading_mode=precision_loading_mode,\n",
- " machine_type=machine_type,\n",
- " accelerator_type=accelerator_type,\n",
- " accelerator_count=accelerator_count,\n",
- " )\n",
- "else:\n",
- " model_quantized_vllm, endpoint_quantized_vllm = deploy_model_vllm(\n",
- " model_name=create_name_with_datetime(prefix=\"openllama-quantized-serve-vllm\"),\n",
- " model_id=quantization_output_dir,\n",
- " service_account=SERVICE_ACCOUNT,\n",
- " machine_type=machine_type,\n",
- " accelerator_type=accelerator_type,\n",
- " accelerator_count=accelerator_count,\n",
- " )"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "y3Iy3q7CsEGe"
- },
- "source": [
- "NOTE: After the deployment succeeds, the model weights will be downloaded on the fly. Thus additional 10 ~ 40 minutes (depending on the model sizes) of waiting time is needed **after** the above model deployment step succeeds and before you run the next step below. Otherwise you might see a `ServiceUnavailable: 503 502:Bad Gateway` error when you send requests to the endpoint.\n",
- "\n",
- "Once deployment succeeds, you can send requests to the endpoint with text prompts.\n",
- "\n",
- "Example:\n",
- "\n",
- "```\n",
- "Human: What is a car?\n",
- "Assistant: A car, or a motor car, is a road-connected human-transportation system used to move people or goods from one place to another. The term also encompasses a wide range of vehicles, including motorboats, trains, and aircrafts. Cars typically have four wheels, a cabin for passengers, and an engine or motor. They have been around since the early 19th century and are now one of the most popular forms of transportation, used for daily commuting, shopping, and other purposes.\n",
- "```"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "id": "eGP9sCq9sF0V"
- },
- "outputs": [],
- "source": [
- "# Loads an existing endpoint instance using the endpoint name:\n",
- "# - Using `endpoint_name = endpoint_quantized_vllm.name` allows us to get the\n",
- "# endpoint name of the endpoint `endpoint_quantized_vllm` created in the cell\n",
- "# above.\n",
- "# - Alternatively, you can set `endpoint_name = \"1234567890123456789\"` to load\n",
- "# an existing endpoint with the ID 1234567890123456789.\n",
- "# You may uncomment the code below to load an existing endpoint.\n",
- "\n",
- "# endpoint_name = endpoint_quantized_vllm.name\n",
- "# # endpoint_name = \"\" # @param {type:\"string\"}\n",
- "# aip_endpoint_name = (\n",
- "# f\"projects/{PROJECT_ID}/locations/{REGION}/endpoints/{endpoint_name}\"\n",
- "# )\n",
- "# endpoint_quantized_vllm = aiplatform.Endpoint(aip_endpoint_name)\n",
- "\n",
- "\n",
- "# Overides max_length and top_k parameters during inferences.\n",
- "# If you encounter the issue like `ServiceUnavailable: 503 Took too long to respond when processing`,\n",
- "# you can reduce the max length, such as set max_length as 20.\n",
- "instances = [\n",
- " {\n",
- " \"prompt\": \"What is a car?\",\n",
- " \"max_tokens\": 50,\n",
- " \"temperature\": 1.0,\n",
- " \"top_p\": 1.0,\n",
- " \"top_k\": 10,\n",
- " },\n",
- "]\n",
- "response = endpoint_quantized_vllm.predict(instances=instances)\n",
- "\n",
- "for prediction in response.predictions:\n",
- " print(prediction)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "JmuUk3l1DoEo"
- },
- "source": [
- "## Evaluate PEFT-finetuned OpenLLaMA\n",
- "\n",
- "This section demonstrates how to evaluate the OpenLLaMA model fintuned with PEFT LoRA using EleutherAI's [Language Model Evaluation Harness (lm-evaluation-harness)](https://github.com/EleutherAI/lm-evaluation-harness) with Vertex CustomJob.\n",
- "\n",
- "This example uses the dataset [TruthfulQA](https://arxiv.org/abs/2109.07958). All supported tasks are listed in [this task table](https://github.com/EleutherAI/lm-evaluation-harness/blob/master/docs/task_table.md)."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "id": "gM4SXaquDoEo"
- },
- "outputs": [],
- "source": [
- "eval_dataset = \"truthfulqa_mc\" # @param {type:\"string\"}\n",
- "\n",
- "# Worker pool spec.\n",
- "# Sets L4 to evaluate open_llama_3b and open_llama_7b.\n",
- "machine_type = \"n1-standard-8\"\n",
- "accelerator_type = \"NVIDIA_TESLA_V100\"\n",
- "accelerator_count = 1\n",
- "\n",
- "# Sets L4 to evaluate open_llama_3b and open_llama_7b.\n",
- "# machine_type = \"g2-standard-8\"\n",
- "# accelerator_type = \"NVIDIA_L4\"\n",
- "# accelerator_count = 1\n",
- "\n",
- "# Sets 2 V100 to evaluate open_llama_13b.\n",
- "# machine_type = \"n1-standard-8\"\n",
- "# accelerator_type = \"NVIDIA_TESLA_V100\"\n",
- "# accelerator_count = 2\n",
- "\n",
- "# Sets 2 L4 to evaluate open_llama_13b.\n",
- "# machine_type = \"g2-standard-24\"\n",
- "# accelerator_type = \"NVIDIA_L4\"\n",
- "# accelerator_count = 2\n",
- "\n",
- "replica_count = 1\n",
- "\n",
- "# Setup evaluation job.\n",
- "job_name = create_name_with_datetime(prefix=\"openllama-peft-eval\")\n",
- "eval_output_dir = os.path.join(MODEL_BUCKET, job_name)\n",
- "eval_output_dir_gcsfuse = eval_output_dir.replace(\"gs://\", \"/gcs/\")"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "id": "g0t0RBixIw0P"
- },
- "outputs": [],
- "source": [
- "# Prepare evaluation command that runs the evaluation harness.\n",
- "# Set `use_accelerate = True` to enable evaluation across multiple GPUs.\n",
- "eval_command = [\n",
- " \"python\",\n",
- " \"main.py\",\n",
- " \"--model\",\n",
- " \"hf-causal-experimental\",\n",
- " \"--model_args\",\n",
- " f\"pretrained={merged_model_output_dir_gcsfuse},use_accelerate=True,device_map_option=auto\",\n",
- " \"--tasks\",\n",
- " f\"{eval_dataset}\",\n",
- " \"--output_path\",\n",
- " f\"{eval_output_dir_gcsfuse}\",\n",
- "]"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "ItWB0WS__CX-"
- },
- "source": [
- "### Submit evaluation CustomJob"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "id": "BbyIk99bDoEo"
- },
- "outputs": [],
- "source": [
- "# Pass evaluation arguments and launch job.\n",
- "worker_pool_specs = [\n",
- " {\n",
- " \"machine_spec\": {\n",
- " \"machine_type\": machine_type,\n",
- " \"accelerator_type\": accelerator_type,\n",
- " \"accelerator_count\": accelerator_count,\n",
- " },\n",
- " \"replica_count\": replica_count,\n",
- " \"disk_spec\": {\n",
- " \"boot_disk_size_gb\": 500,\n",
- " },\n",
- " \"container_spec\": {\n",
- " \"image_uri\": EVAL_DOCKER_URI,\n",
- " \"command\": eval_command,\n",
- " \"args\": [],\n",
- " },\n",
- " }\n",
- "]\n",
- "\n",
- "eval_job = aiplatform.CustomJob(\n",
- " display_name=job_name,\n",
- " worker_pool_specs=worker_pool_specs,\n",
- " base_output_dir=eval_output_dir,\n",
- ")\n",
- "\n",
- "eval_job.run()\n",
- "\n",
- "print(\"Evaluation results were saved in:\", eval_output_dir)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "kN0lE2iu_NXN"
- },
- "source": [
- "### Fetch and print evaluation results"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "id": "927oRxoADoEp"
- },
- "outputs": [],
- "source": [
- "import json\n",
- "\n",
- "from google.cloud import storage\n",
- "\n",
- "# Fetch evaluation results.\n",
- "storage_client = storage.Client()\n",
- "BUCKET_NAME = BUCKET_URI.split(\"gs://\")[1]\n",
- "bucket = storage_client.get_bucket(BUCKET_NAME)\n",
- "RESULT_FILE_PATH = eval_output_dir[len(BUCKET_URI) + 1 :]\n",
- "blob = bucket.blob(RESULT_FILE_PATH)\n",
- "raw_result = blob.download_as_string()\n",
- "\n",
- "# Print evaluation results.\n",
- "result = json.loads(raw_result)\n",
- "result_formatted = json.dumps(result, indent=2)\n",
- "print(f\"Evaluation result:\\n{result_formatted}\")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "af21a3cff1e0"
- },
- "source": [
- "## Clean up resources"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "id": "911406c1561e"
- },
- "outputs": [],
- "source": [
- "# Delete custom train and evaluation jobs.\n",
- "train_job.delete()\n",
- "eval_job.delete()\n",
- "quantize_job.delete()\n",
- "\n",
- "# Undeploy models and delete endpoints.\n",
- "endpoint_without_peft.delete(force=True)\n",
- "endpoint_with_peft.delete(force=True)\n",
- "endpoint_quantized_vllm.delete(force=True)\n",
- "\n",
- "# Delete models.\n",
- "model_without_peft.delete()\n",
- "model_with_peft.delete()\n",
- "model_quantized_vllm.delete()"
- ]
- }
- ],
- "metadata": {
- "colab": {
- "name": "model_garden_pytorch_openllama_peft.ipynb",
- "toc_visible": true
- },
- "kernelspec": {
- "display_name": "Python 3",
- "name": "python3"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 0
-}
diff --git a/notebooks/community/model_garden/model_garden_pytorch_peft.ipynb b/notebooks/community/model_garden/model_garden_pytorch_peft.ipynb
deleted file mode 100644
index 2d3d974ad..000000000
--- a/notebooks/community/model_garden/model_garden_pytorch_peft.ipynb
+++ /dev/null
@@ -1,1047 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "id": "7d9bbf86da5e"
- },
- "outputs": [],
- "source": [
- "# Copyright 2023 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": "99c1c3fc2ca5"
- },
- "source": [
- "# Vertex AI Model Garden - PEFT [DEPRECATED]\n",
- "\n",
- "\n",
- " \n",
- " \n",
- " Run in Colab\n",
- " \n",
- " | \n",
- " \n",
- " \n",
- " \n",
- " View on GitHub\n",
- " \n",
- " | \n",
- " \n",
- " \n",
- " \n",
- "Open in Vertex AI Workbench\n",
- " (A Python-3 CPU notebook is recommended)\n",
- " | \n",
- "
"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "3de7470326a2"
- },
- "source": [
- "## Overview\n",
- "\n",
- "This notebook demonstrates finetuning models with performance efficient finetuning libaries ([PEFT](https://github.com/huggingface/peft)), and running inferences with containers in Vertex AI. There are various models supported in [PEFT](https://github.com/huggingface/peft). This notebook shows examples with some models, such as OpenLLaMA, Falcon-instruct, BERT, RoBERTa-large, and XLM-RoBERTa-large, etc.\n",
- "\n",
- "### Objective\n",
- "\n",
- "- Finetune causal language models with PEFT and run inferences with Vertex AI, supporting\n",
- "\n",
- "| Models | LoRA |\n",
- "| :- | :- |\n",
- "| [openlm-research/open_llama_3b](https://huggingface.co/openlm-research/open_llama_3b) | Y |\n",
- "| [openlm-research/open_llama_7b](https://huggingface.co/openlm-research/open_llama_7b) | Y |\n",
- "| [openlm-research/open_llama_13b](https://huggingface.co/openlm-research/open_llama_13b) | Y |\n",
- "\n",
- "- Finetune instruct models with PEFT and run inferences with Vertex AI, supporting\n",
- "\n",
- "| Models | LoRA |\n",
- "| :- | :- |\n",
- "| [tiiuae/falcon-7b-instruct](https://huggingface.co/tiiuae/falcon-7b-instruct) | Y |\n",
- "| [tiiuae/falcon-40b-instruct](https://huggingface.co/tiiuae/falcon-40b-instruct) | Y |\n",
- "\n",
- "\n",
- "- Finetune sequence classification models with PEFT and run inferences with Vertex AI, supporting\n",
- "\n",
- "| Models | LoRA |\n",
- "| :- | :- |\n",
- "| [bert-base-uncase](https://huggingface.co/bert-base-uncased) | Y |\n",
- "| [RoBERTa-large](https://huggingface.co/roberta-large) | Y |\n",
- "| [XLM-RoBERTa-large](https://huggingface.co/xlm-roberta-large) | Y |\n",
- "\n",
- "### Costs\n",
- "\n",
- "This tutorial uses billable components of Google Cloud:\n",
- "\n",
- "* Vertex AI\n",
- "* Cloud Storage\n",
- "\n",
- "Learn about [Vertex AI pricing](https://cloud.google.com/vertex-ai/pricing) and [Cloud Storage pricing](https://cloud.google.com/storage/pricing), and use the [Pricing Calculator](https://cloud.google.com/products/calculator/) to generate a cost estimate based on your projected usage."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "264c07757582"
- },
- "source": [
- "## Before you begin\n",
- "\n",
- "**NOTE**: Jupyter runs lines prefixed with `!` as shell commands, and it interpolates Python variables prefixed with `$` into these commands."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "ioensNKM8ned"
- },
- "source": [
- "### Colab only\n",
- "Run the following commands for Colab and skip this section if you are using Workbench."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "id": "2707b02ef5df"
- },
- "outputs": [],
- "source": [
- "import sys\n",
- "\n",
- "if \"google.colab\" in sys.modules:\n",
- " ! pip3 install --upgrade google-cloud-aiplatform\n",
- " from google.colab import auth as google_auth\n",
- "\n",
- " google_auth.authenticate_user()\n",
- " # Install gdown for downloading example training images.\n",
- " ! pip3 install gdown\n",
- "\n",
- " # Restart the notebook kernel after installs.\n",
- " import IPython\n",
- "\n",
- " app = IPython.Application.instance()\n",
- " app.kernel.do_shutdown(True)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "bb7adab99e41"
- },
- "source": [
- "### Setup Google Cloud project\n",
- "\n",
- "1. [Select or create a Google Cloud project](https://console.cloud.google.com/cloud-resource-manager). When you first create an account, you get a $300 free credit towards your compute/storage costs.\n",
- "\n",
- "1. [Make sure that billing is enabled for your project](https://cloud.google.com/billing/docs/how-to/modify-project).\n",
- "\n",
- "1. [Enable the Vertex AI API and Compute Engine API](https://console.cloud.google.com/flows/enableapi?apiid=aiplatform.googleapis.com,compute_component).\n",
- "\n",
- "1. [Create a Cloud Storage bucket](https://cloud.google.com/storage/docs/creating-buckets) for storing experiment outputs.\n",
- "\n",
- "1. [Create a service account](https://cloud.google.com/iam/docs/service-accounts-create#iam-service-accounts-create-console) with `Vertex AI User` and `Storage Object Admin` roles for deploying fine tuned model to Vertex AI endpoint."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "6c460088b873"
- },
- "source": [
- "Set the following variables for the experiment environment. The specified Cloud Storage bucket (`BUCKET_URI`) should be located in the specified region (`REGION`). Note that a multi-region bucket (eg. \"us\") is not considered a match for a single region covered by the multi-region range (eg. \"us-central1\")."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "id": "855d6b96f291"
- },
- "outputs": [],
- "source": [
- "# Cloud project id.\n",
- "PROJECT_ID = \"\" # @param {type:\"string\"}\n",
- "\n",
- "# The region you want to launch jobs in.\n",
- "REGION = \"\" # @param {type:\"string\"}\n",
- "\n",
- "# The Cloud Storage bucket for storing experiments output.\n",
- "BUCKET_URI = \"\" # @param {type:\"string\"}\n",
- "\n",
- "! gcloud config set project $PROJECT_ID\n",
- "\n",
- "import os\n",
- "\n",
- "STAGING_BUCKET = os.path.join(BUCKET_URI, \"temporal\")\n",
- "EXPERIMENT_BUCKET = os.path.join(BUCKET_URI, \"peft\")\n",
- "DATA_BUCKET = os.path.join(EXPERIMENT_BUCKET, \"data\")\n",
- "MODEL_BUCKET = os.path.join(EXPERIMENT_BUCKET, \"model\")\n",
- "\n",
- "# The service account looks like:\n",
- "# '@.iam.gserviceaccount.com'\n",
- "# Please go to https://cloud.google.com/iam/docs/service-accounts-create#iam-service-accounts-create-console\n",
- "# and create service account with `Vertex AI User` and `Storage Object Admin` roles.\n",
- "# The service account for deploying fine tuned model.\n",
- "SERVICE_ACCOUNT = \"\" # @param {type:\"string\"}"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "e828eb320337"
- },
- "source": [
- "### Initialize Vertex AI API"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "id": "12cd25839741"
- },
- "outputs": [],
- "source": [
- "from google.cloud import aiplatform\n",
- "\n",
- "aiplatform.init(project=PROJECT_ID, location=REGION, staging_bucket=STAGING_BUCKET)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "2cc825514deb"
- },
- "source": [
- "### Define constants"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "id": "b42bd4fa2b2d"
- },
- "outputs": [],
- "source": [
- "# The pre-built training and serving docker images.\n",
- "TRAIN_DOCKER_URI = \"us-docker.pkg.dev/vertex-ai/vertex-vision-model-garden-dockers/pytorch-peft-train:20231020_0936_RC00\"\n",
- "PREDICTION_DOCKER_URI = \"us-docker.pkg.dev/vertex-ai/vertex-vision-model-garden-dockers/pytorch-peft-serve:20231108_1540_RC00\""
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "0c250872074f"
- },
- "source": [
- "### Define common functions"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "id": "354da31189dc"
- },
- "outputs": [],
- "source": [
- "import os\n",
- "from datetime import datetime\n",
- "\n",
- "from google.cloud import aiplatform\n",
- "\n",
- "\n",
- "def get_job_name_with_datetime(prefix: str):\n",
- " \"\"\"Gets the job name with date time when triggering training or deployment\n",
- " jobs in Vertex AI.\n",
- " \"\"\"\n",
- " return prefix + datetime.now().strftime(\"_%Y%m%d_%H%M%S\")\n",
- "\n",
- "\n",
- "def deploy_model(\n",
- " model_name,\n",
- " model_id,\n",
- " publisher,\n",
- " publisher_model_id,\n",
- " finetuned_lora_model_path,\n",
- " service_account,\n",
- " task,\n",
- " machine_type=\"n1-standard-8\",\n",
- " accelerator_type=\"NVIDIA_TESLA_V100\",\n",
- "):\n",
- " \"\"\"Deploys trained models into Vertex AI.\"\"\"\n",
- " endpoint = aiplatform.Endpoint.create(display_name=f\"{model_name}-endpoint\")\n",
- " serving_env = {\n",
- " \"MODEL_ID\": model_id,\n",
- " \"FINETUNED_LORA_MODEL_PATH\": finetuned_lora_model_path,\n",
- " \"TASK\": task,\n",
- " \"DEPLOY_SOURCE\": \"notebook\",\n",
- " }\n",
- " model = aiplatform.Model.upload(\n",
- " display_name=model_name,\n",
- " serving_container_image_uri=PREDICTION_DOCKER_URI,\n",
- " serving_container_ports=[7080],\n",
- " serving_container_predict_route=\"/predictions/peft_serving\",\n",
- " serving_container_health_route=\"/ping\",\n",
- " serving_container_environment_variables=serving_env,\n",
- " model_garden_source_model_name=(\n",
- " f\"publishers/{publisher}/models/{publisher_model_id}\",\n",
- " ),\n",
- " )\n",
- " model.deploy(\n",
- " endpoint=endpoint,\n",
- " machine_type=machine_type,\n",
- " accelerator_type=accelerator_type,\n",
- " accelerator_count=1,\n",
- " deploy_request_timeout=1800,\n",
- " service_account=service_account,\n",
- " system_labels={\"NOTEBOOK_NAME\": \"model_garden_pytorch_peft.ipynb\"},\n",
- " )\n",
- " return model, endpoint"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "e70e3519ff8b"
- },
- "source": [
- "## Casual Language Modeling + PEFT\n",
- "\n",
- "This section demonstrates how to finetune OpenLLaMA with PEFT LoRA, including [openlm-research/open_llama_3b](https://huggingface.co/openlm-research/open_llama_3b), [openlm-research/open_llama_7b](https://huggingface.co/openlm-research/open_llama_7b), and [openlm-research/open_llama_13b](https://huggingface.co/openlm-research/open_llama_13b)."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "iWGwJHqI7LMs"
- },
- "source": [
- "### Finetune with LoRA"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "5qCrm_kJH5cz"
- },
- "source": [
- "Set the base model id."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "id": "N3UBLiYrM3sU"
- },
- "outputs": [],
- "source": [
- "model_id = \"openlm-research/open_llama_3b\" # @param [\"openlm-research/open_llama_3b\", \"openlm-research/open_llama_7b\", \"openlm-research/open_llama_13b\"]"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "KKEYoRfiHDVv"
- },
- "source": [
- "Use the Vertex AI SDK to create and run the custom training jobs with Vertex AI Model Garden training images.\n",
- "\n",
- "This example uses the dataset [Abirate/english_quotes](https://huggingface.co/datasets/Abirate/english_quotes). You can either use a [dataset from huggingface](https://huggingface.co/datasets) or a custom JSONL dataset in [Vertex text model dataset format](https://cloud.google.com/vertex-ai/docs/generative-ai/models/tune-text-models-supervised#dataset-format) stored in Cloud Storage.\n",
- "\n",
- "In order to make the finetunig efficiently, we enabled quantization (8bits) when loading pretrained models for finetuning LoRA models. The peak GPU memory usages are ~7G, ~10G and ~16G for finetuning LoRA models for [openlm-research/open_llama_3b](https://huggingface.co/openlm-research/open_llama_3b), [openlm-research/open_llama_7b](https://huggingface.co/openlm-research/open_llama_7b), and [openlm-research/open_llama_13b](https://huggingface.co/openlm-research/open_llama_13b) separately with default training parameters and the example dataset. In theory, open_llama_3b and open_llama_7b can be finetuned on 1 V100, and open_llama_13b can be finetuned on 1 A100 (40G). We choose to use 1 A100 (40G) by default to support all these models in this notebook for simplicity."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "bd995e7aa529"
- },
- "source": [
- "#### [Optional] Finetune with a custom dataset\n",
- "\n",
- "To use a custom dataset, you should supply a `gs://` URI to a JSONL file in [Vertex text model dataset format](https://cloud.google.com/vertex-ai/docs/generative-ai/models/tune-text-models-supervised#dataset-format) in the `dataset_name` below. The `template` parameter is optional.\n",
- "\n",
- "For example, here is one data point from the sample dataset `gs://cloud-samples-data/vertex-ai/model-evaluation/peft_train_sample.jsonl`:\n",
- "\n",
- "```json\n",
- "{\"input_text\":\"TRANSCRIPT: \\nREASON FOR EVALUATION:,\\n\\n LABEL:\",\"output_text\":\"Chiropractic\"}\n",
- "```\n",
- "\n",
- "To use this sample dataset that contains `input_text` and `output_text` fields, set `dataset_name` to `gs://cloud-samples-data/vertex-ai/model-evaluation/peft_train_sample.jsonl` and `template` to `vertex_sample`. For advanced usage with custom datatset fields, see [the template example](https://github.com/tloen/alpaca-lora/blob/main/templates/alpaca.json) and supply your own JSON template as `gs://` URIs."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "id": "65467b361315"
- },
- "outputs": [],
- "source": [
- "# Huggingface dataset name or gs:// URI to a custom JSONL dataset.\n",
- "dataset_name = \"Abirate/english_quotes\" # @param {type:\"string\"}\n",
- "# Optional. Template name or gs:// URI to a custom template.\n",
- "template = \"\" # @param {type:\"string\"}\n",
- "\n",
- "# Worker pool spec.\n",
- "# machine_type = \"n1-standard-8\"\n",
- "# accelerator_type = \"NVIDIA_TESLA_V100\"\n",
- "machine_type = \"a2-highgpu-1g\"\n",
- "accelerator_type = \"NVIDIA_TESLA_A100\"\n",
- "replica_count = 1\n",
- "accelerator_count = 1\n",
- "\n",
- "# Add labels for the finetuning job.\n",
- "labels = {\n",
- " \"mg-source\": \"notebook\",\n",
- " \"mg-notebook-name\": \"model_garden_pytorch_peft.ipynb\".split(\".\")[0],\n",
- "}\n",
- "\n",
- "labels[\"mg-tune\"] = \"publishers-openlm-research-models-openllama\"\n",
- "versioned_model_id = model_id.split(\"/\")[1].replace(\"_\", \"-\")\n",
- "labels[\"versioned-mg-tune\"] = f\"{labels['mg-tune']}-{versioned_model_id}\"\n",
- "\n",
- "# Setup training job.\n",
- "job_name = get_job_name_with_datetime(\"openllama-lora-train\")\n",
- "train_job = aiplatform.CustomContainerTrainingJob(\n",
- " display_name=job_name,\n",
- " container_uri=TRAIN_DOCKER_URI,\n",
- " labels=labels,\n",
- ")\n",
- "output_dir = os.path.join(MODEL_BUCKET, job_name)\n",
- "output_dir_gcsfuse = output_dir.replace(\"gs://\", \"/gcs/\")\n",
- "\n",
- "# Pass training arguments and launch job.\n",
- "train_job.run(\n",
- " args=[\n",
- " \"--task=causal-language-modeling-lora\",\n",
- " f\"--pretrained_model_id={model_id}\",\n",
- " f\"--dataset_name={dataset_name}\",\n",
- " f\"--output_dir={output_dir_gcsfuse}\",\n",
- " \"--lora_rank=16\",\n",
- " \"--lora_alpha=32\",\n",
- " \"--lora_dropout=0.05\",\n",
- " \"--warmup_steps=10\",\n",
- " \"--max_steps=10\",\n",
- " \"--learning_rate=2e-4\",\n",
- " f\"--template={template}\",\n",
- " ],\n",
- " replica_count=replica_count,\n",
- " machine_type=machine_type,\n",
- " accelerator_type=accelerator_type,\n",
- " accelerator_count=accelerator_count,\n",
- " boot_disk_size_gb=500,\n",
- ")\n",
- "\n",
- "print(\"Trained models were saved in: \", output_dir)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "jqmCtkGnhDmp"
- },
- "source": [
- "### Run inferences with serving images\n",
- "This section uploads the model to Model Registry and deploys it on the Endpoint.\n",
- "\n",
- "The model deployment step will take ~15 minutes to complete.\n",
- "\n",
- "The peak GPU memory usages for [openlm-research/open_llama_3b](https://huggingface.co/openlm-research/open_llama_3b), [openlm-research/open_llama_7b](https://huggingface.co/openlm-research/open_llama_7b), and [openlm-research/open_llama_13b](https://huggingface.co/openlm-research/open_llama_13b) with LoRA weights are ~5.3G, ~8.7G and ~15.2G separately with the default settings. We use V100 in deployments for simplicity."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "id": "bf55e38815dc"
- },
- "outputs": [],
- "source": [
- "model, endpoint = deploy_model(\n",
- " model_name=get_job_name_with_datetime(prefix=\"openllama-peft-serve\"),\n",
- " model_id=model_id,\n",
- " publisher=\"openlm-research\",\n",
- " publisher_model_id=\"openllama\",\n",
- " finetuned_lora_model_path=output_dir,\n",
- " service_account=SERVICE_ACCOUNT,\n",
- " task=\"causal-language-modeling-lora\",\n",
- ")\n",
- "print(\"endpoint_name:\", endpoint.name)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "80b3fd2ace09"
- },
- "source": [
- "NOTE: The model weights will be downloaded after the deployment succeeds. Thus additional 5 minutes of waiting time is needed **after** the above model deployment step succeeds and before you run the next step below. Otherwise you might see a `ServiceUnavailable: 503 502:Bad Gateway` error when you send requests to the endpoint.\n",
- "\n",
- "Once deployment succeeds, you can send requests to the endpoint with text prompts."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "id": "4ab04da3ec9a"
- },
- "outputs": [],
- "source": [
- "# # Loads an existing endpoint as below.\n",
- "# endpoint_name = endpoint.name\n",
- "# aip_endpoint_name = (\n",
- "# f\"projects/{PROJECT_ID}/locations/{REGION}/endpoints/{endpoint_name}\"\n",
- "# )\n",
- "# endpoint = aiplatform.Endpoint(aip_endpoint_name)\n",
- "instances = [\n",
- " {\n",
- " \"prompt\": \"Hi Google.\",\n",
- " \"max_tokens\": 50,\n",
- " \"temperature\": 1.0,\n",
- " \"top_p\": 1.0,\n",
- " \"top_k\": 10,\n",
- " },\n",
- "]\n",
- "response = endpoint.predict(instances=instances)\n",
- "\n",
- "for prediction in response.predictions:\n",
- " print(prediction)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "af21a3cff1e0"
- },
- "source": [
- "### Clean up resources"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "id": "911406c1561e"
- },
- "outputs": [],
- "source": [
- "# Delete custom train jobs.\n",
- "train_job.delete()\n",
- "\n",
- "# Undeploy model and delete endpoint.\n",
- "endpoint.delete(force=True)\n",
- "\n",
- "# Delete models.\n",
- "model.delete()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "W2USaHtQbE-l"
- },
- "source": [
- "## Instruct + PEFT\n",
- "\n",
- "This section demonstrates how to finetune Falcon-instruct with PEFT LoRA, including [tiiuae/falcon-7b-instruct](https://huggingface.co/tiiuae/falcon-7b-instruct), and [tiiuae/falcon-40b-instruct](https://huggingface.co/tiiuae/falcon-40b-instruct)."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "PwluMqvYbLu9"
- },
- "source": [
- "### Finetune with LoRA"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "ap1pQd-ckAAb"
- },
- "source": [
- "Set the base model id."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "id": "vVTfG2bpbR90"
- },
- "outputs": [],
- "source": [
- "model_id = \"tiiuae/falcon-7b-instruct\" # @param [\"tiiuae/falcon-7b-instruct\", \"tiiuae/falcon-40b-instruct\"]"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "iJe_us8mkZEV"
- },
- "source": [
- "Use the Vertex AI SDK to create and run the custom training jobs with Vertex AI Model Garden training images.\n",
- "\n",
- "This example uses the dataset [timdettmers/openassistant-guanaco](https://huggingface.co/datasets/timdettmers/openassistant-guanaco). You can either use a [dataset from huggingface](https://huggingface.co/datasets) or a custom JSONL dataset in [Vertex text model dataset format](https://cloud.google.com/vertex-ai/docs/generative-ai/models/tune-text-models-supervised#dataset-format) stored in Cloud Storage. The `template` parameter is optional.\n",
- "\n",
- "The peak GPU memory usages are ~11G and ~34G for finetuning LoRA models for [tiiuae/falcon-7b-instruct](https://huggingface.co/tiiuae/falcon-7b-instruct), and [tiiuae/falcon-40b-instruct](https://huggingface.co/tiiuae/falcon-40b-instruct) separately with default training parameters and the example dataset. In theory, falcon-7b-instruct can be finetuned on 1 P100/V100, and falcon-40b-instruct can be finetuned on 1 A100 (40G). We choose to use 1 A100 (40G) by default to support all these models in this notebook for simplicity."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "4c128f63be2a"
- },
- "source": [
- "#### [Optional] Finetune with a custom dataset\n",
- "\n",
- "To use a custom dataset, you should supply a `gs://` URI to a JSONL file in [Vertex text model dataset format](https://cloud.google.com/vertex-ai/docs/generative-ai/models/tune-text-models-supervised#dataset-format) in the `dataset_name` below.\n",
- "\n",
- "For example, here is one data point from the sample dataset `gs://cloud-samples-data/vertex-ai/model-evaluation/peft_train_sample.jsonl`:\n",
- "\n",
- "```json\n",
- "{\"input_text\":\"TRANSCRIPT: \\nREASON FOR EVALUATION:,\\n\\n LABEL:\",\"output_text\":\"Chiropractic\"}\n",
- "```\n",
- "\n",
- "To use this sample dataset that contains `input_text` and `output_text` fields, set `dataset_name` to `gs://cloud-samples-data/vertex-ai/model-evaluation/peft_train_sample.jsonl` and `template` to `vertex_sample`. For advanced usage with custom datatset fields, see [the template example](https://github.com/tloen/alpaca-lora/blob/main/templates/alpaca.json) and supply your own JSON template as `gs://` URIs."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "id": "-i4M7mWPbV8s"
- },
- "outputs": [],
- "source": [
- "# Huggingface dataset name or gs:// URI to a custom JSONL dataset.\n",
- "dataset_name = \"timdettmers/openassistant-guanaco\" # @param {type:\"string\"}\n",
- "# Optional. Template name or gs:// URI to a custom template.\n",
- "template = \"\" # @param {type:\"string\"}\n",
- "\n",
- "# Worker pool spec.\n",
- "machine_type = \"a2-highgpu-1g\"\n",
- "accelerator_type = \"NVIDIA_TESLA_A100\"\n",
- "replica_count = 1\n",
- "accelerator_count = 1\n",
- "\n",
- "# Setup training job.\n",
- "job_name = get_job_name_with_datetime(\"falcon-instruct-lora-train\")\n",
- "train_job = aiplatform.CustomContainerTrainingJob(\n",
- " display_name=job_name,\n",
- " container_uri=TRAIN_DOCKER_URI,\n",
- ")\n",
- "output_dir = os.path.join(MODEL_BUCKET, job_name)\n",
- "output_dir_gcsfuse = output_dir.replace(\"gs://\", \"/gcs/\")\n",
- "\n",
- "# Pass training arguments and launch job.\n",
- "max_steps = 10\n",
- "train_job.run(\n",
- " args=[\n",
- " \"--task=instruct-lora\",\n",
- " f\"--pretrained_model_id={model_id}\",\n",
- " f\"--dataset_name={dataset_name}\",\n",
- " f\"--output_dir={output_dir_gcsfuse}\",\n",
- " \"--lora_rank=64\",\n",
- " \"--lora_alpha=16\",\n",
- " \"--lora_dropout=0.1\",\n",
- " \"--warmup_ratio=0.03\",\n",
- " f\"--max_steps={max_steps}\",\n",
- " \"--max_seq_length=512\",\n",
- " \"--learning_rate=2e-4\",\n",
- " f\"--template={template}\",\n",
- " ],\n",
- " replica_count=replica_count,\n",
- " machine_type=machine_type,\n",
- " accelerator_type=accelerator_type,\n",
- " accelerator_count=accelerator_count,\n",
- " boot_disk_size_gb=500,\n",
- ")\n",
- "\n",
- "print(\"Trained models were saved in: \", output_dir)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "oCgivsdllZ1x"
- },
- "source": [
- "### Run inferences with serving images"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "_lNIFvielcyV"
- },
- "source": [
- "This section uploads the model to Model Registry and deploys it on the Endpoint.\n",
- "\n",
- "The model deployment step will take ~15 minutes to complete.\n",
- "\n",
- "The peak GPU memory usages for [tiiuae/falcon-7b-instruct](https://huggingface.co/tiiuae/falcon-7b-instruct), and [tiiuae/falcon-40b-instruct](https://huggingface.co/tiiuae/falcon-40b-instruct) with LoRA weights are ~15.5G and ~38.2G separately with the default settings. We use V100 in deployments for simplicity."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "id": "inrBXzoxk53t"
- },
- "outputs": [],
- "source": [
- "# # If deploy finetuned falcon-40b-instruct models, please set\n",
- "# machine_type = \"a2-highgpu-1g\",\n",
- "# accelerator_type = \"NVIDIA_TESLA_A100\"\n",
- "machine_type = \"n1-standard-8\"\n",
- "accelerator_type = \"NVIDIA_TESLA_V100\"\n",
- "\n",
- "model, endpoint = deploy_model(\n",
- " model_name=get_job_name_with_datetime(prefix=\"falcon-peft-serve\"),\n",
- " model_id=model_id,\n",
- " publisher=\"tiiuae\",\n",
- " publisher_model_id=\"falcon-instruct-7b-peft\",\n",
- " finetuned_lora_model_path=os.path.join(output_dir, \"checkpoint-\" + str(max_steps)),\n",
- " service_account=SERVICE_ACCOUNT,\n",
- " task=\"instruct-lora\",\n",
- " machine_type=machine_type,\n",
- " accelerator_type=accelerator_type,\n",
- ")\n",
- "print(\"endpoint_name: \", endpoint.name)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "_dmO3XooliAs"
- },
- "source": [
- "NOTE: The model weights will be downloaded after the deployment succeeds. Thus additional 5 minutes of waiting time is needed after the above model deployment step succeeds and before you run the next step below. Otherwise you might see a ServiceUnavailable: 503 502:Bad Gateway error when you send requests to the endpoint.\n",
- "\n",
- "Once deployment succeeds, you can send requests to the endpoint with text prompts.\n",
- "\n",
- "Example:\n",
- "\n",
- "```\n",
- "Human: What is a car?\n",
- "Assistant: A car, or a motor car, is a road-connected human-transportation system used to move people or goods from one place to another. The term also encompasses a wide range of vehicles, including motorboats, trains, and aircrafts. Cars typically have four wheels, a cabin for passengers, and an engine or motor. They have been around since the early 19th century and are now one of the most popular forms of transportation, used for daily commuting, shopping, and other purposes.\n",
- "```"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "id": "JrKsrffGl14T"
- },
- "outputs": [],
- "source": [
- "# # Loads an existing endpoint as below.\n",
- "# endpoint_name = endpoint.name\n",
- "# aip_endpoint_name = (\n",
- "# f\"projects/{PROJECT_ID}/locations/{REGION}/endpoints/{endpoint_name}\"\n",
- "# )\n",
- "# endpoint = aiplatform.Endpoint(aip_endpoint_name)\n",
- "instances = [\n",
- " {\n",
- " \"prompt\": \"What is a car?\",\n",
- " \"max_tokens\": 50,\n",
- " \"temperature\": 1.0,\n",
- " \"top_p\": 1.0,\n",
- " \"top_k\": 10,\n",
- " },\n",
- "]\n",
- "response = endpoint.predict(instances=instances)\n",
- "\n",
- "for prediction in response.predictions:\n",
- " print(prediction)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "uk-t69nFl9rr"
- },
- "source": [
- "### Clean up resources"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "id": "CcIX2WAJmAI7"
- },
- "outputs": [],
- "source": [
- "# Delete custom train jobs.\n",
- "train_job.delete()\n",
- "\n",
- "# Undeploy model and delete endpoint.\n",
- "endpoint.delete(force=True)\n",
- "\n",
- "# Delete models.\n",
- "model.delete()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "og_s64QVmJDb"
- },
- "source": [
- "## Sequence Classification + PEFT\n",
- "\n",
- "This section demonstrates how to finetune sequence classification models with PEFT LoRA, including [bert-base-uncased](https://huggingface.co/bert-base-uncased), [RoBERTa-large](https://huggingface.co/roberta-large), and [XLM-RoBERTa-large](https://huggingface.co/xlm-roberta-large)."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "QqJcvCb2mn8l"
- },
- "source": [
- "### Finetune with PEFT"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "c0E7dfqloeBB"
- },
- "source": [
- "Set the base model id."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "id": "eru79ot6mtNG"
- },
- "outputs": [],
- "source": [
- "model_id = \"xlm-roberta-large\" # @param [\"bert-base-uncased\", \"roberta-large\", \"xlm-roberta-large\"]"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "y9n-qheaotS9"
- },
- "source": [
- "Use the Vertex AI SDK to create and run the custom training jobs with Vertex AI Model Garden training images.\n",
- "\n",
- "This example uses the dataset [glue](https://huggingface.co/datasets/glue).\n",
- "\n",
- "The peak GPU memory usages are ~8.8G and ~15G for finetuning LoRA models for [RoBERTa-large](https://huggingface.co/roberta-large), and [XLM-RoBERTa-large](https://huggingface.co/xlm-roberta-large) separately with default training parameters and the example dataset. In theory, RoBERTa-large can be finetuned on 1 P100/V100, and XLM-RoBERTa-large can be finetuned on 1 A100 (40G). We choose to use 1 A100 (40G) by default to support all these models in this notebook for simplicity."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "id": "OHeSYEKinfP3"
- },
- "outputs": [],
- "source": [
- "dataset_name = \"glue\" # @param {type:\"string\"}\n",
- "\n",
- "# Worker pool spec.\n",
- "# # Please switch to A100 or other powerful machines if you run out of memory.\n",
- "# machine_type = \"a2-highgpu-1g\"\n",
- "# accelerator_type = \"NVIDIA_TESLA_A100\"\n",
- "machine_type = \"n1-standard-8\"\n",
- "accelerator_type = \"NVIDIA_TESLA_V100\"\n",
- "replica_count = 1\n",
- "accelerator_count = 1\n",
- "\n",
- "# Setup training job.\n",
- "job_name = get_job_name_with_datetime(os.path.basename(model_id) + \"-lora-train\")\n",
- "train_job = aiplatform.CustomContainerTrainingJob(\n",
- " display_name=job_name,\n",
- " container_uri=TRAIN_DOCKER_URI,\n",
- ")\n",
- "output_dir = os.path.join(MODEL_BUCKET, job_name)\n",
- "output_dir_gcsfuse = output_dir.replace(\"gs://\", \"/gcs/\")\n",
- "\n",
- "# Pass training arguments and launch job.\n",
- "train_job.run(\n",
- " args=[\n",
- " \"--task=sequence-classification-lora\",\n",
- " f\"--pretrained_model_id={model_id}\",\n",
- " f\"--dataset_name={dataset_name}\",\n",
- " f\"--output_dir={output_dir_gcsfuse}\",\n",
- " \"--lora_rank=8\",\n",
- " \"--lora_alpha=16\",\n",
- " \"--lora_dropout=0.1\",\n",
- " \"--num_epochs=20\",\n",
- " \"--batch_size=32\",\n",
- " \"--learning_rate=3e-4\",\n",
- " ],\n",
- " replica_count=replica_count,\n",
- " machine_type=machine_type,\n",
- " accelerator_type=accelerator_type,\n",
- " accelerator_count=accelerator_count,\n",
- " boot_disk_size_gb=500,\n",
- ")\n",
- "\n",
- "print(\"Trained models were saved in: \", output_dir)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "kJuHaBCqoacg"
- },
- "source": [
- "### Run inferences with serving images"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "jw1GFXydoft0"
- },
- "source": [
- "This section uploads the model to Model Registry and deploys it on the Endpoint.\n",
- "\n",
- "The model deployment step will take ~15 minutes to complete.\n",
- "\n",
- "The peak GPU memory usages for [RoBERTa-large](https://huggingface.co/roberta-large), and [XLM-RoBERTa-large](https://huggingface.co/xlm-roberta-large) with LoRA weights are ~2G and ~2.4G separately with the default settings. We use V100 in deployments for simplicity."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "id": "A5j0a3AZoj3h"
- },
- "outputs": [],
- "source": [
- "if model_id == \"bert-base-uncased\":\n",
- " publisher = \"google\"\n",
- " publisher_model_id = \"bert-base-uncased\"\n",
- "elif model_id == \"roberta-large\":\n",
- " publisher = \"meta\"\n",
- " publisher_model_id = \"roberta-large\"\n",
- "elif model_id == \"xlm-roberta-large\":\n",
- " publisher = \"meta\"\n",
- " publisher_model_id = \"xlm-roberta-large\"\n",
- "else:\n",
- " raise ValueError(f\"Unsupported model id: {model_id}\")\n",
- "\n",
- "model, endpoint = deploy_model(\n",
- " model_name=get_job_name_with_datetime(prefix=\"sequence-classification-peft-serve\"),\n",
- " model_id=model_id,\n",
- " publisher=publisher,\n",
- " publisher_model_id=publisher_model_id,\n",
- " finetuned_lora_model_path=output_dir,\n",
- " service_account=SERVICE_ACCOUNT,\n",
- " task=\"sequence-classification-lora\",\n",
- ")\n",
- "print(\"endpoint_name:\", endpoint.name)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "6dR3-Hc7oqu-"
- },
- "source": [
- "NOTE: The model weights will be downloaded after the deployment succeeds. Thus additional 5 minutes of waiting time is needed after the above model deployment step succeeds and before you run the next step below. Otherwise you might see a ServiceUnavailable: 503 502:Bad Gateway error when you send requests to the endpoint.\n",
- "\n",
- "Once deployment succeeds, you can send requests to the endpoint with text prompts."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "id": "xAmULPegoyXl"
- },
- "outputs": [],
- "source": [
- "# # Loads an existing endpoint as below.\n",
- "# endpoint_name = endpoint.name\n",
- "# aip_endpoint_name = (\n",
- "# f\"projects/{PROJECT_ID}/locations/{REGION}/endpoints/{endpoint_name}\"\n",
- "# )\n",
- "# endpoint = aiplatform.Endpoint(aip_endpoint_name)\n",
- "instances = [\n",
- " {\"prompt\": \"The cat sat on the mat.\"},\n",
- "]\n",
- "response = endpoint.predict(instances=instances)\n",
- "labels = [int(item) for item in response.predictions]\n",
- "print(labels)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "1Vg_b46Jo6C1"
- },
- "source": [
- "### Clean up resources"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "id": "vNiTDL_VpAlp"
- },
- "outputs": [],
- "source": [
- "# Delete custom train jobs.\n",
- "train_job.delete()\n",
- "\n",
- "# Undeploy model and delete endpoint.\n",
- "endpoint.delete(force=True)\n",
- "\n",
- "# Delete models.\n",
- "model.delete()"
- ]
- }
- ],
- "metadata": {
- "colab": {
- "name": "model_garden_pytorch_peft.ipynb",
- "toc_visible": true
- },
- "kernelspec": {
- "display_name": "Python 3",
- "name": "python3"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 0
-}
diff --git a/notebooks/community/model_garden/model_garden_pytorch_qwen2_5_finetuning.ipynb b/notebooks/community/model_garden/model_garden_pytorch_qwen2_5_finetuning.ipynb
deleted file mode 100644
index 24bf834a8..000000000
--- a/notebooks/community/model_garden/model_garden_pytorch_qwen2_5_finetuning.ipynb
+++ /dev/null
@@ -1,1050 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "7d9bbf86da5e"
- },
- "outputs": [],
- "source": [
- "# Copyright 2026 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": "99c1c3fc2ca5"
- },
- "source": [
- "# Vertex AI Model Garden - Qwen 2.5 Finetuning\n",
- "\n",
- "\n",
- " \n",
- " \n",
- "  Run in Colab Enterprise\n",
- " \n",
- " | \n",
- " \n",
- " \n",
- "  View on GitHub\n",
- " \n",
- " | \n",
- "
"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "3de7470326a2"
- },
- "source": [
- "## Overview\n",
- "\n",
- "This notebook demonstrates finetuning and deploying Qwen 2.5 models with Vertex AI. All of the examples in this notebook use parameter efficient finetuning methods [PEFT (LoRA)](https://github.com/huggingface/peft) to reduce training and storage costs. LoRA (Low-Rank Adaptation) is one approach of Parameter Efficient FineTuning (PEFT), where pretrained model weights are frozen and rank decomposition matrices representing the change in model weights are trained during finetuning. Read more about LoRA in the following publication: [Hu, E.J., Shen, Y., Wallis, P., Allen-Zhu, Z., Li, Y., Wang, S., Wang, L. and Chen, W., 2021. Lora: Low-rank adaptation of large language models. *arXiv preprint arXiv:2106.09685*](https://arxiv.org/abs/2106.09685).\n",
- "\n",
- "After finetuning, we can deploy models on Vertex with GPU.\n",
- "\n",
- "\n",
- "### Objective\n",
- "\n",
- "- Finetune Qwen 2.5 models with Vertex AI Custom Training Jobs.\n",
- "- Deploy finetuned Qwen 2.5 models on Vertex AI Prediction.\n",
- "- Send prediction requests to your finetuned Qwen 2.5 models.\n",
- "\n",
- "### File a bug\n",
- "\n",
- "File a bug on [GitHub](https://github.com/GoogleCloudPlatform/vertex-ai-samples/issues/new) if you encounter any issue with the notebook.\n",
- "\n",
- "### Costs\n",
- "\n",
- "This tutorial uses billable components of Google Cloud:\n",
- "\n",
- "* Vertex AI\n",
- "* Cloud Storage\n",
- "\n",
- "Learn about [Vertex AI pricing](https://cloud.google.com/vertex-ai/pricing), [Cloud Storage pricing](https://cloud.google.com/storage/pricing), and use the [Pricing Calculator](https://cloud.google.com/products/calculator/) to generate a cost estimate based on your projected usage."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "264c07757582"
- },
- "source": [
- "## Before you begin"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "855d6b96f291"
- },
- "outputs": [],
- "source": [
- "# @title Install Python Packages for Finetuning\n",
- "\n",
- "# @markdown 1. Install packages to validate dataset with template.\n",
- "! pip install --upgrade --quiet gcsfs==2024.3.1\n",
- "! pip install --upgrade --quiet accelerate==0.34.2\n",
- "! pip install --upgrade --quiet transformers==4.47.1\n",
- "! pip install --upgrade --quiet datasets==2.20.0"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "50273xHFJi5T"
- },
- "outputs": [],
- "source": [
- "# @title Setup Google Cloud project\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. For finetuning, **[click here](https://console.cloud.google.com/iam-admin/quotas?location=us-central1&metric=aiplatform.googleapis.com%2Frestricted_image_training_nvidia_a100_80gb_gpus)** to check if your project already has the required 8 Nvidia A100 80 GB GPUs in the us-central1 region. If yes, then run this notebook in the us-central1 region. If you do not have 8 Nvidia A100 80 GPUs or have more GPU requirements than this, then schedule your job with Nvidia H100 GPUs via Dynamic Workload Scheduler using [these instructions](https://cloud.google.com/vertex-ai/docs/training/schedule-jobs-dws). For Dynamic Workload Scheduler, check the [us-central1](https://console.cloud.google.com/iam-admin/quotas?location=us-central1&metric=aiplatform.googleapis.com%2Fcustom_model_training_preemptible_nvidia_h100_gpus) or [europe-west4](https://console.cloud.google.com/iam-admin/quotas?location=europe-west4&metric=aiplatform.googleapis.com%2Fcustom_model_training_preemptible_nvidia_h100_gpus) quota for Nvidia H100 GPUs. If you do not have enough GPUs, then you can follow [these instructions](https://cloud.google.com/docs/quotas/view-manage#viewing_your_quota_console) to request quota.\n",
- "\n",
- "# @markdown 3. For serving, **[click here](https://console.cloud.google.com/iam-admin/quotas?location=us-central1&metric=aiplatform.googleapis.com%2Fcustom_model_serving_nvidia_l4_gpus)** to check if your project already has the required 1 L4 GPU in the us-central1 region. If yes, then run this notebook in the us-central1 region. If you need more L4 GPUs for your project, then you can follow [these instructions](https://cloud.google.com/docs/quotas/view-manage#viewing_your_quota_console) to request more. Alternatively, 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).\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",
- "# @markdown 4. **[Optional]** [Create a Cloud Storage bucket](https://cloud.google.com/storage/docs/creating-buckets) for storing experiment outputs. Set the BUCKET_URI for the experiment environment. The specified Cloud Storage bucket (`BUCKET_URI`) should be located in the same region as where the notebook was launched. Note that a multi-region bucket (eg. \"us\") is not considered a match for a single region covered by the multi-region range (eg. \"us-central1\"). If not set, a unique GCS bucket will be created instead.\n",
- "\n",
- "BUCKET_URI = \"gs://\" # @param {type:\"string\"}\n",
- "\n",
- "# @markdown 5. **[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",
- "# Import the necessary packages\n",
- "! rm -rf vertex-ai-samples && git clone https://github.com/GoogleCloudPlatform/vertex-ai-samples.git\n",
- "! cd vertex-ai-samples && git reset --hard 7ae13b346a72ee2a2dc8152dd40c6ddd72d6c810\n",
- "\n",
- "import datetime\n",
- "import importlib\n",
- "import os\n",
- "import uuid\n",
- "from typing import Tuple\n",
- "\n",
- "from google.cloud import aiplatform\n",
- "from google.cloud.aiplatform.compat.types import \\\n",
- " custom_job as gca_custom_job_compat\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",
- "models, endpoints = {}, {}\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",
- " if not os.environ.get(\"GOOGLE_CLOUD_REGION\"):\n",
- " raise ValueError(\n",
- " \"REGION must be set. See\"\n",
- " \" https://cloud.google.com/vertex-ai/docs/general/locations for\"\n",
- " \" available cloud locations.\"\n",
- " )\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",
- "# Cloud Storage bucket for storing the experiment artifacts.\n",
- "# A unique GCS bucket will be created for the purpose of this notebook. If you\n",
- "# prefer using your own GCS bucket, change the value yourself below.\n",
- "now = datetime.datetime.now().strftime(\"%Y%m%d%H%M%S\")\n",
- "BUCKET_NAME = \"/\".join(BUCKET_URI.split(\"/\")[:3])\n",
- "\n",
- "if BUCKET_URI is None or BUCKET_URI.strip() == \"\" or BUCKET_URI == \"gs://\":\n",
- " BUCKET_URI = f\"gs://{PROJECT_ID}-tmp-{now}-{str(uuid.uuid4())[:4]}\"\n",
- " BUCKET_NAME = \"/\".join(BUCKET_URI.split(\"/\")[:3])\n",
- " ! gsutil mb -l {REGION} {BUCKET_URI}\n",
- "else:\n",
- " assert BUCKET_URI.startswith(\"gs://\"), \"BUCKET_URI must start with `gs://`.\"\n",
- " shell_output = ! gsutil ls -Lb {BUCKET_NAME} | grep \"Location constraint:\" | sed \"s/Location constraint://\"\n",
- " bucket_region = shell_output[0].strip().lower()\n",
- " if bucket_region != REGION:\n",
- " raise ValueError(\n",
- " \"Bucket region %s is different from notebook region %s\"\n",
- " % (bucket_region, REGION)\n",
- " )\n",
- "print(f\"Using this GCS Bucket: {BUCKET_URI}\")\n",
- "\n",
- "STAGING_BUCKET = os.path.join(BUCKET_URI, \"temporal\")\n",
- "MODEL_BUCKET = os.path.join(BUCKET_URI, \"qwen2_5\")\n",
- "\n",
- "\n",
- "# Initialize Vertex AI API.\n",
- "print(\"Initializing Vertex AI API.\")\n",
- "aiplatform.init(project=PROJECT_ID, location=REGION, staging_bucket=STAGING_BUCKET)\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",
- "\n",
- "# Provision permissions to the SERVICE_ACCOUNT with the GCS bucket\n",
- "! gsutil iam ch serviceAccount:{SERVICE_ACCOUNT}:roles/storage.admin $BUCKET_NAME\n",
- "\n",
- "! gcloud config set project $PROJECT_ID\n",
- "! gcloud projects add-iam-policy-binding --no-user-output-enabled {PROJECT_ID} --member=serviceAccount:{SERVICE_ACCOUNT} --role=\"roles/storage.admin\"\n",
- "! gcloud projects add-iam-policy-binding --no-user-output-enabled {PROJECT_ID} --member=serviceAccount:{SERVICE_ACCOUNT} --role=\"roles/aiplatform.user\""
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "36c21f10355f"
- },
- "outputs": [],
- "source": [
- "# @title Access Qwen 2.5 models\n",
- "\n",
- "# @markdown ### Access Qwen 2.5 models on Hugging Face for GPU based finetuning and serving\n",
- "# @markdown You must provide a Hugging Face User Access Token (with read access) to access the Qwen 2.5 models. You can follow the [Hugging Face documentation](https://huggingface.co/docs/hub/en/security-tokens) to create a **read** access token and put it in the `HF_TOKEN` field below.\n",
- "\n",
- "HF_TOKEN = \"\" # @param {type:\"string\", isTemplate:true}\n",
- "assert (\n",
- " HF_TOKEN\n",
- "), \"Provide a read HF_TOKEN to load models from Hugging Face, or select a different model source.\"\n",
- "# @markdown ---"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "cb56d402e84a"
- },
- "source": [
- "## Finetune with HuggingFace PEFT and deploy with vLLM on GPUs"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "KwAW99YZHTdy"
- },
- "outputs": [],
- "source": [
- "# @title Set dataset\n",
- "\n",
- "# @markdown Use the Vertex AI SDK to create and run the custom training jobs.\n",
- "\n",
- "# @markdown This notebook uses [timdettmers/openassistant-guanaco](https://huggingface.co/datasets/timdettmers/openassistant-guanaco) dataset as an example.\n",
- "# @markdown You can set `train_dataset` to any existing [Hugging Face dataset](https://huggingface.co/datasets) name, and set `train_column` to the name of the dataset column containing training data. The [timdettmers/openassistant-guanaco](https://huggingface.co/datasets/timdettmers/openassistant-guanaco) has only one column `text`, and therefore we set `train_column` to `text` in this notebook.\n",
- "\n",
- "# @markdown ### (Optional) Prepare a custom JSONL dataset for finetuning\n",
- "\n",
- "# @markdown You can prepare a JSONL file where each line is a valid JSON string as your custom training dataset. For example, here is one line from the [timdettmers/openassistant-guanaco](https://huggingface.co/datasets/timdettmers/openassistant-guanaco) dataset:\n",
- "# @markdown ```\n",
- "# @markdown {\"text\": \"### Human: Hola### Assistant: \\u00a1Hola! \\u00bfEn qu\\u00e9 puedo ayudarte hoy?\"}\n",
- "# @markdown ```\n",
- "\n",
- "# @markdown The JSON object has a key `text`, which should match `train_column`; The value should be one training data point, i.e. a string. After you prepared your JSONL file, you can either upload it to [Hugging Face datasets](https://huggingface.co/datasets) or [Google Cloud Storage](https://cloud.google.com/storage).\n",
- "\n",
- "# @markdown - To upload a JSONL dataset to [Hugging Face datasets](https://huggingface.co/datasets), follow the instructions on [Uploading Datasets](https://huggingface.co/docs/hub/en/datasets-adding). Then, set `dataset_name` to the name of your newly created dataset on Hugging Face.\n",
- "\n",
- "# @markdown - To upload a JSONL dataset to [Google Cloud Storage](https://cloud.google.com/storage), follow the instructions on [Upload objects from a filesystem](https://cloud.google.com/storage/docs/uploading-objects). Then, set `dataset_name` to the `gs://` URI to your JSONL file. For example: `gs://cloud-samples-data/vertex-ai/model-evaluation/peft_train_sample.jsonl`.\n",
- "\n",
- "# @markdown Optionally update the `train_column` field below if your JSON objects use a key other than the default `text`.\n",
- "\n",
- "# @markdown ### (Optional) Format your data with custom JSON template\n",
- "\n",
- "# @markdown Sometimes, your dataset might have multiple text columns and you want to construct the training data with a template. You can prepare a JSON template in the following format:\n",
- "\n",
- "# @markdown ```\n",
- "# @markdown {\n",
- "# @markdown \"description\": \"Template used by Llama 3.1, accepting text-bison format.\",\n",
- "# @markdown \"source\": \"https://cloud.google.com/vertex-ai/generative-ai/docs/models/tune-text-models-supervised#dataset-format\",\n",
- "# @markdown \"prompt_input\": \"<|start_header_id|>user<|end_header_id|>\\n\\n{input_text}<|eot_id|><|start_header_id|>assistant<|end_header_id|>\\n\\n{output_text}<|eot_id|>\",\n",
- "# @markdown \"instruction_separator\": \"<|start_header_id|>user<|end_header_id|>\\n\\n\",\n",
- "# @markdown \"response_separator\": \"<|start_header_id|>assistant<|end_header_id|>\\n\\n\"\n",
- "# @markdown }\n",
- "# @markdown ```\n",
- "\n",
- "# @markdown As an example, the template above can be used to format the following training data (this line comes from `gs://cloud-samples-data/vertex-ai/model-evaluation/peft_train_sample.jsonl`):\n",
- "\n",
- "# @markdown ```\n",
- "# @markdown {\"input_text\":\"TRANSCRIPT: \\nREASON FOR EVALUATION:,\\n\\n LABEL:\",\"output_text\":\"Chiropractic\"}\n",
- "# @markdown ```\n",
- "\n",
- "# @markdown This example template simply concatenates `input_text` with `output_text` with some special tokens in between.\n",
- "# @markdown\n",
- "# @markdown To try such custom dataset, you can make the following changes:\n",
- "# @markdown 1. Set `template` to `llama3-text-bison`\n",
- "# @markdown 1. Set `train_dataset` to `gs://cloud-samples-data/vertex-ai/model-evaluation/peft_train_sample.jsonl`\n",
- "# @markdown 1. Set `train_split` to `train`\n",
- "# @markdown 1. Set `eval_dataset` to `gs://cloud-samples-data/vertex-ai/model-evaluation/peft_eval_sample.jsonl`\n",
- "# @markdown 1. Set `eval_split` to `train` (**NOT** `test`)\n",
- "# @markdown 1. Set `train_column` as `input_text`.\n",
- "\n",
- "# Template name or gs:// URI to a custom template.\n",
- "template = \"openassistant-guanaco\" # @param {type:\"string\"}\n",
- "\n",
- "# Hugging Face dataset name or gs:// URI to a custom JSONL dataset.\n",
- "train_dataset = \"timdettmers/openassistant-guanaco\" # @param {type:\"string\"}\n",
- "train_split = \"train\" # @param {type:\"string\"}\n",
- "eval_dataset = \"timdettmers/openassistant-guanaco\" # @param {type:\"string\"}\n",
- "eval_split = \"test\" # @param {type:\"string\"}\n",
- "\n",
- "# Name of the dataset column containing training text input.\n",
- "train_column = \"text\" # @param {type:\"string\"}\n",
- "# Maximum sequence length.\n",
- "max_seq_length = 4096 # @param{type:\"integer\"}"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "oHvYSPr7JdZq"
- },
- "outputs": [],
- "source": [
- "# @title Set model\n",
- "\n",
- "# @markdown Select a model variant of Qwen 2.5.\n",
- "base_model_id = \"Qwen/Qwen2.5-7B-Instruct\" # @param [\"Qwen/Qwen2.5-7B-Instruct\", \"Qwen/Qwen2.5-14B-Instruct\", \"Qwen/Qwen2.5-32B-Instruct\"] {isTemplate:true}\n",
- "pretrained_model_id = base_model_id"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "X3N8O9_0JdZq"
- },
- "outputs": [],
- "source": [
- "# @title Validate Dataset with Template\n",
- "\n",
- "# @markdown This section validates the train and eval datasets with the template before starting the fine tuning process.\n",
- "\n",
- "dataset_validation_util = importlib.import_module(\n",
- " \"vertex-ai-samples.community-content.vertex_model_garden.model_oss.notebook_util.dataset_validation_util\"\n",
- ")\n",
- "\n",
- "if dataset_validation_util.is_gcs_path(pretrained_model_id):\n",
- " # Download tokenizer.\n",
- " ! mkdir tokenizer\n",
- " ! gsutil cp {pretrained_model_id}/tokenizer.json ./tokenizer\n",
- " ! gsutil cp {pretrained_model_id}/config.json ./tokenizer\n",
- " tokenizer_path = \"./tokenizer\"\n",
- " access_token = \"\"\n",
- "else:\n",
- " tokenizer_path = pretrained_model_id\n",
- " access_token = HF_TOKEN\n",
- "\n",
- "tokenizer = dataset_validation_util.load_tokenizer(tokenizer_path, None, access_token)\n",
- "\n",
- "# Validate the train dataset.\n",
- "dataset_validation_util.validate_dataset_with_template(\n",
- " dataset_name=train_dataset,\n",
- " split=train_split,\n",
- " input_column=train_column,\n",
- " template=template,\n",
- " max_seq_length=max_seq_length,\n",
- " use_multiprocessing=False,\n",
- " tokenizer=tokenizer,\n",
- ")\n",
- "\n",
- "# Validate the eval dataset if it exists.\n",
- "if eval_dataset:\n",
- " dataset_validation_util.validate_dataset_with_template(\n",
- " dataset_name=eval_dataset,\n",
- " split=eval_split,\n",
- " input_column=train_column,\n",
- " template=template,\n",
- " max_seq_length=max_seq_length,\n",
- " use_multiprocessing=False,\n",
- " tokenizer=tokenizer,\n",
- " )"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "VvQLIpjhJdZq"
- },
- "outputs": [],
- "source": [
- "# @title Finetune\n",
- "\n",
- "# @markdown This section demonstrates how to finetune the Qwen 2.5 model on Vertex AI. It uses the Vertex AI SDK to create and run the custom training jobs.\n",
- "\n",
- "# @markdown The training job takes approximately between 10 to 20 mins to set-up. Once done, the training job is expected to take around 2.5 hours with the default configurations. To find the training time, throughput, and memory usage of your training job, you can go to the training logs and check the log line of the last training epoch.\n",
- "\n",
- "# @markdown **Note**:\n",
- "# @markdown 1. We recommend setting `finetuning_precision_mode` to `4bit` because it enables using fewer hardware resources for finetuning.\n",
- "# @markdown 1. If `max_steps > 0`, it takes precedence over `epochs`. One can set a small `max_steps` value to quickly check the pipeline.\n",
- "\n",
- "# @markdown Acceletor type to use for training.\n",
- "# fmt: off\n",
- "training_accelerator_type = \"NVIDIA_A100_80GB\" # @param [\"NVIDIA_A100_80GB\", \"NVIDIA_H100_80GB\"]\n",
- "# fmt: on\n",
- "\n",
- "# The pre-built training docker image.\n",
- "if training_accelerator_type == \"NVIDIA_A100_80GB\":\n",
- " repo = \"us-docker.pkg.dev/vertex-ai-restricted\"\n",
- " is_restricted_image = True\n",
- " is_dynamic_workload_scheduler = False\n",
- " dws_kwargs = {}\n",
- "else:\n",
- " repo = \"us-docker.pkg.dev/vertex-ai\"\n",
- " is_restricted_image = False\n",
- " is_dynamic_workload_scheduler = True\n",
- " dws_kwargs = {\n",
- " \"max_wait_duration\": 1800, # 30 minutes\n",
- " \"scheduling_strategy\": gca_custom_job_compat.Scheduling.Strategy.FLEX_START,\n",
- " }\n",
- "\n",
- "TRAIN_DOCKER_URI = (\n",
- " f\"{repo}/vertex-vision-model-garden-dockers/pytorch-peft-train:stable_20250705\"\n",
- ")\n",
- "\n",
- "# Worker pool spec.\n",
- "boot_disk_size_gb = 500\n",
- "if training_accelerator_type == \"NVIDIA_A100_80GB\":\n",
- " per_node_accelerator_count = 8\n",
- " training_machine_type = \"a2-ultragpu-8g\"\n",
- "elif training_accelerator_type == \"NVIDIA_H100_80GB\":\n",
- " per_node_accelerator_count = 8\n",
- " training_machine_type = \"a3-highgpu-8g\"\n",
- "else:\n",
- " raise ValueError(\n",
- " f\"Recommended machine settings not found for: {training_accelerator_type}. To use another accelerator type, edit this code block to pass in an appropriate `training_machine_type`, `training_accelerator_type`, and `per_node_accelerator_count` by clicking `Show Code` and then modifying the code.\"\n",
- " )\n",
- "\n",
- "# The number of nodes to use for this worker pool in distributed training.\n",
- "replica_count = 1\n",
- "\n",
- "# Set config file.\n",
- "config_file = \"vertex_vision_model_garden_peft/qwen2_fsdp_8gpu.yaml\"\n",
- "\n",
- "# @markdown Batch size for finetuning.\n",
- "per_device_train_batch_size = 1 # @param{type:\"integer\"}\n",
- "# @markdown Number of updates steps to accumulate the gradients for, before performing a backward/update pass.\n",
- "gradient_accumulation_steps = 4 # @param{type:\"integer\"}\n",
- "# @markdown Setting a positive `max_steps` here will override `num_train_epochs`.\n",
- "max_steps = -1 # @param{type:\"integer\"}\n",
- "num_train_epochs = 1.0 # @param{type:\"number\"}\n",
- "# @markdown Precision mode for finetuning.\n",
- "finetuning_precision_mode = \"4bit\" # @param [\"4bit\", \"float16\"]\n",
- "# @markdown Learning rate.\n",
- "learning_rate = 5e-5 # @param{type:\"number\"}\n",
- "# @markdown The scheduler type to use.\n",
- "lr_scheduler_type = \"cosine\" # @param{type:\"string\"}\n",
- "# @markdown LoRA parameters.\n",
- "lora_rank = 16 # @param{type:\"integer\"}\n",
- "lora_alpha = 32 # @param{type:\"integer\"}\n",
- "lora_dropout = 0.05 # @param{type:\"number\"}\n",
- "# Activates gradient checkpointing for the current model (may be referred to as activation checkpointing or checkpoint activations in other frameworks).\n",
- "gradient_checkpointing = True\n",
- "# Attention implementation to use in the model.\n",
- "attn_implementation = \"flash_attention_2\"\n",
- "# The optimizer for which to schedule the learning rate.\n",
- "optimizer = \"adamw_torch\"\n",
- "# Define the proportion of training to be dedicated to a linear warmup where learning rate gradually increases.\n",
- "warmup_ratio = \"0.01\"\n",
- "# The list or string of integrations to report the results and logs to.\n",
- "report_to = \"tensorboard\"\n",
- "# Number of updates steps before two checkpoint saves.\n",
- "save_steps = 10\n",
- "# Number of update steps between two logs.\n",
- "logging_steps = save_steps\n",
- "\n",
- "# @markdown Evaluation metrics to compute. Supported eval metrics: loss, perplexity, bleu, google_bleu, rouge1, rouge2, rougeL, rougeLsum.\n",
- "eval_metric_name = \"loss,perplexity,bleu\" # @param{type:\"string\"}\n",
- "# @markdown Metric to use for best model selection. This will save the best checkpoint based on the eval metric.\n",
- "metric_for_best_model = \"perplexity\" # @param{type:\"string\"}\n",
- "\n",
- "common_util.check_quota(\n",
- " project_id=PROJECT_ID,\n",
- " region=REGION,\n",
- " accelerator_type=training_accelerator_type,\n",
- " accelerator_count=per_node_accelerator_count * replica_count,\n",
- " is_for_training=True,\n",
- " is_restricted_image=is_restricted_image,\n",
- " is_dynamic_workload_scheduler=is_dynamic_workload_scheduler,\n",
- ")\n",
- "\n",
- "job_name = common_util.get_job_name_with_datetime(\"qwen2_5-lora-train\")\n",
- "\n",
- "base_output_dir = os.path.join(STAGING_BUCKET, job_name)\n",
- "# Create a GCS folder to store the LORA adapter.\n",
- "lora_output_dir = os.path.join(base_output_dir, \"adapter\")\n",
- "# Create a GCS folder to store the merged model with the base model and the\n",
- "# finetuned LORA adapter.\n",
- "merged_model_output_dir = os.path.join(base_output_dir, \"merged-model\")\n",
- "\n",
- "# Add labels for the finetuning job.\n",
- "labels = {\n",
- " \"mg-source\": \"notebook\",\n",
- " \"mg-notebook-name\": \"model_garden_pytorch_qwen2_5_finetuning.ipynb\".split(\".\")[0],\n",
- "}\n",
- "\n",
- "labels[\"mg-tune\"] = \"publishers-qwen-models-qwen2-5\"\n",
- "versioned_model_id = base_model_id.split(\"/\")[1].lower().replace(\".\", \"-\")\n",
- "labels[\"versioned-mg-tune\"] = f\"{labels['mg-tune']}-{versioned_model_id}\"\n",
- "\n",
- "eval_args = [\n",
- " f\"--eval_dataset={eval_dataset}\",\n",
- " f\"--eval_column={train_column}\",\n",
- " f\"--eval_template={template}\",\n",
- " f\"--eval_split={eval_split}\",\n",
- " f\"--eval_steps={save_steps}\",\n",
- " f\"--eval_metric_name={eval_metric_name}\",\n",
- " f\"--metric_for_best_model={metric_for_best_model}\",\n",
- "]\n",
- "\n",
- "train_job_args = [\n",
- " f\"--config_file={config_file}\",\n",
- " \"--task=instruct-lora\",\n",
- " \"--input_masking=True\",\n",
- " f\"--pretrained_model_name_or_path={pretrained_model_id}\",\n",
- " f\"--train_dataset={train_dataset}\",\n",
- " f\"--train_split={train_split}\",\n",
- " f\"--train_column={train_column}\",\n",
- " f\"--output_dir={lora_output_dir}\",\n",
- " f\"--per_device_train_batch_size={per_device_train_batch_size}\",\n",
- " f\"--gradient_accumulation_steps={gradient_accumulation_steps}\",\n",
- " f\"--merge_base_and_lora_output_dir={merged_model_output_dir}\",\n",
- " f\"--lora_rank={lora_rank}\",\n",
- " f\"--lora_alpha={lora_alpha}\",\n",
- " f\"--lora_dropout={lora_dropout}\",\n",
- " f\"--max_steps={max_steps}\",\n",
- " f\"--max_seq_length={max_seq_length}\",\n",
- " f\"--learning_rate={learning_rate}\",\n",
- " f\"--lr_scheduler_type={lr_scheduler_type}\",\n",
- " f\"--precision_mode={finetuning_precision_mode}\",\n",
- " f\"--gradient_checkpointing={gradient_checkpointing}\",\n",
- " f\"--num_train_epochs={num_train_epochs}\",\n",
- " f\"--attn_implementation={attn_implementation}\",\n",
- " f\"--optimizer={optimizer}\",\n",
- " f\"--warmup_ratio={warmup_ratio}\",\n",
- " f\"--report_to={report_to}\",\n",
- " f\"--logging_output_dir={base_output_dir}\",\n",
- " f\"--save_steps={save_steps}\",\n",
- " f\"--logging_steps={logging_steps}\",\n",
- " f\"--train_template={template}\",\n",
- " f\"--huggingface_access_token={HF_TOKEN}\",\n",
- "] + eval_args\n",
- "\n",
- "# Pass training arguments and launch job.\n",
- "train_job = aiplatform.CustomContainerTrainingJob(\n",
- " display_name=job_name,\n",
- " container_uri=TRAIN_DOCKER_URI,\n",
- " labels=labels,\n",
- ")\n",
- "\n",
- "print(\"Running training job with args:\")\n",
- "print(\" \\\\\\n\".join(train_job_args))\n",
- "train_job.run(\n",
- " args=train_job_args,\n",
- " replica_count=replica_count,\n",
- " machine_type=training_machine_type,\n",
- " accelerator_type=training_accelerator_type,\n",
- " accelerator_count=per_node_accelerator_count,\n",
- " boot_disk_size_gb=boot_disk_size_gb,\n",
- " service_account=SERVICE_ACCOUNT,\n",
- " base_output_dir=base_output_dir,\n",
- " sync=False, # Non-blocking call to run.\n",
- " **dws_kwargs,\n",
- ")\n",
- "\n",
- "# Wait until resource has been created.\n",
- "train_job.wait_for_resource_creation()\n",
- "\n",
- "print(\"LoRA adapter will be saved in:\", lora_output_dir)\n",
- "print(\"Trained and merged models will be saved in:\", merged_model_output_dir)\n",
- "\n",
- "# @markdown Click \"Show Code\" to see more details."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "6jQZVMzcJdZq"
- },
- "outputs": [],
- "source": [
- "# @title Run TensorBoard\n",
- "# @markdown This section shows how to launch TensorBoard in a [Cloud Shell](https://cloud.google.com/shell/docs).\n",
- "# @markdown 1. Click the Cloud Shell icon() on the top right to open the Cloud Shell.\n",
- "# @markdown 2. Copy the `tensorboard` command shown below by running this cell.\n",
- "# @markdown 3. Paste and run the command in the Cloud Shell to launch TensorBoard.\n",
- "# @markdown 4. Once the command runs (You may have to click `Authorize` if prompted), click the link starting with `http://localhost`.\n",
- "\n",
- "# @markdown Note: You may need to wait around 10 minutes after the job starts in order for the TensorBoard logs to be written to the GCS bucket.\n",
- "print(f\"Command to copy: tensorboard --logdir {base_output_dir}/logs\")"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "Ys3FzqDaJdZq"
- },
- "outputs": [],
- "source": [
- "# @title Select Evaluation Checkpoint\n",
- "\n",
- "if train_job.end_time is None:\n",
- " print(\"Waiting for the training job to finish...\")\n",
- " train_job.wait()\n",
- " print(\"The training job has finished.\")\n",
- "\n",
- "# @markdown The following checkpoints are available for evaluation:\n",
- "! gcloud storage ls \"{lora_output_dir}/node-0\" | grep \"checkpoint-\""
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "dztN2-JdJdZq"
- },
- "outputs": [],
- "source": [
- "# @title Run Evaluation Job\n",
- "# @markdown This section runs the evaluation using [lm-evaluation-harness](https://github.com/EleutherAI/lm-evaluation-harness) on the finetuned model. The evaluation takes approximately 20 mins to finish.\n",
- "\n",
- "# The pre-built evaluation docker image for LM Evaluation Harness.\n",
- "LM_EVAL_DOCKER_URI = \"us-docker.pkg.dev/vertex-ai/vertex-vision-model-garden-dockers/pytorch-lm-evaluation-harness:20250410_1035_RC00\"\n",
- "\n",
- "# @markdown Set `RUN_EVALUATION` to False to skip the evaluation job.\n",
- "RUN_EVALUATION = True # @param {type:\"boolean\"}\n",
- "\n",
- "eval_accelerator_type = \"NVIDIA_L4\"\n",
- "gpu_memory_utilization = 0.85\n",
- "\n",
- "if \"7B\" in base_model_id or \"14B\" in base_model_id:\n",
- " eval_machine_type = \"g2-standard-48\"\n",
- " eval_accelerator_count = 4\n",
- "elif \"32B\" in base_model_id:\n",
- " eval_machine_type = \"g2-standard-96\"\n",
- " eval_accelerator_count = 8\n",
- "else:\n",
- " raise ValueError(\n",
- " \"Recommended machine settings not found for model: %s\" % base_model_id\n",
- " )\n",
- "\n",
- "# @markdown Set `evaluation_checkpoint_dir` to an intermediate checkpoint from the above training job. If not set, the evaluation job will use the merged model.\n",
- "evaluation_checkpoint_dir = \"\" # @param {type:\"string\"}\n",
- "if evaluation_checkpoint_dir:\n",
- " pretrained = pretrained_model_id\n",
- "else:\n",
- " pretrained = merged_model_output_dir\n",
- "\n",
- "# @markdown Evaluation tasks to run.\n",
- "eval_tasks = \"coqa\" # @param {type:\"string\"}\n",
- "# @markdown Model to use for evaluation.\n",
- "model = \"vllm\" # @param {type:\"string\"}\n",
- "# @markdown Batch size for evaluation.\n",
- "batch_size = \"auto\" # @param {type:\"string\"}\n",
- "apply_chat_template = True\n",
- "max_model_len = 4096 # Maximum context length.\n",
- "\n",
- "model_args = f\"tensor_parallel_size={eval_accelerator_count},max_model_len={max_model_len},gpu_memory_utilization={gpu_memory_utilization},enforce_eager=True\"\n",
- "eval_output_dir = os.path.join(base_output_dir, \"lm_eval\")\n",
- "\n",
- "lm_eval_job_args = [\n",
- " \"--task=lm_eval\",\n",
- " f\"--model={model}\",\n",
- " f\"--eval_tasks={eval_tasks}\",\n",
- " f\"--pretrained_model_name_or_path={pretrained}\",\n",
- " f\"--model_args={model_args}\",\n",
- " f\"--output_dir={eval_output_dir}\",\n",
- " f\"--apply_chat_template={apply_chat_template}\",\n",
- " f\"--batch_size={batch_size}\",\n",
- " f\"--huggingface_access_token={HF_TOKEN}\",\n",
- "]\n",
- "\n",
- "if evaluation_checkpoint_dir:\n",
- " lm_eval_job_args.append(f\"--lora_path={evaluation_checkpoint_dir}\")\n",
- "\n",
- "if RUN_EVALUATION:\n",
- " common_util.check_quota(\n",
- " project_id=PROJECT_ID,\n",
- " region=REGION,\n",
- " accelerator_type=eval_accelerator_type,\n",
- " accelerator_count=eval_accelerator_count,\n",
- " is_for_training=True,\n",
- " )\n",
- " lm_eval_job = aiplatform.CustomContainerTrainingJob(\n",
- " display_name=common_util.get_job_name_with_datetime(\"qwen2_5-lm-eval\"),\n",
- " container_uri=LM_EVAL_DOCKER_URI,\n",
- " labels=labels,\n",
- " )\n",
- "\n",
- " print(\"Running evaluation job with args:\")\n",
- " print(\" \\\\\\n\".join(lm_eval_job_args))\n",
- " lm_eval_job.run(\n",
- " args=lm_eval_job_args,\n",
- " replica_count=1,\n",
- " machine_type=eval_machine_type,\n",
- " accelerator_type=eval_accelerator_type,\n",
- " accelerator_count=eval_accelerator_count,\n",
- " boot_disk_size_gb=boot_disk_size_gb,\n",
- " service_account=SERVICE_ACCOUNT,\n",
- " base_output_dir=base_output_dir,\n",
- " )\n",
- "\n",
- "# @markdown Click \"Show Code\" to see more details."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "qWpHMUt3JdZq"
- },
- "outputs": [],
- "source": [
- "# @title Deploy\n",
- "# @markdown This section uploads the model to Model Registry and deploys it on the Endpoint. It takes 15 minutes to 1 hour to finish.\n",
- "\n",
- "print(\"Deploying models in:\", merged_model_output_dir)\n",
- "\n",
- "# The pre-built serving docker image for vLLM.\n",
- "VLLM_DOCKER_URI = \"us-docker.pkg.dev/vertex-ai/vertex-vision-model-garden-dockers/pytorch-vllm-serve:20250116_0916_RC00\"\n",
- "\n",
- "# @markdown Set `use_dedicated_endpoint` to False if you don't want to use [dedicated endpoint](https://cloud.google.com/vertex-ai/docs/general/deployment#create-dedicated-endpoint).\n",
- "use_dedicated_endpoint = True # @param {type:\"boolean\"}\n",
- "\n",
- "# Find Vertex AI prediction supported accelerators and regions [here](https://cloud.google.com/vertex-ai/docs/predictions/configure-compute).\n",
- "accelerator_type = \"NVIDIA_L4\"\n",
- "if \"7b\" in base_model_id.lower():\n",
- " machine_type = \"g2-standard-24\"\n",
- " per_node_accelerator_count = 2\n",
- "elif \"14b\" in base_model_id.lower() or \"32b\" in base_model_id.lower():\n",
- " machine_type = \"g2-standard-48\"\n",
- " per_node_accelerator_count = 4\n",
- "else:\n",
- " raise ValueError(f\"Unsupported model ID: {base_model_id}.\")\n",
- "\n",
- "common_util.check_quota(\n",
- " project_id=PROJECT_ID,\n",
- " region=REGION,\n",
- " accelerator_type=accelerator_type,\n",
- " accelerator_count=per_node_accelerator_count,\n",
- " is_for_training=False,\n",
- ")\n",
- "\n",
- "gpu_memory_utilization = 0.95\n",
- "max_model_len = 8192 # Maximum context length.\n",
- "\n",
- "# Ensure max_model_len does not exceed the limit\n",
- "if max_model_len > 8192:\n",
- " raise ValueError(\"max_model_len cannot exceed 8192\")\n",
- "\n",
- "\n",
- "def deploy_model_vllm(\n",
- " model_name: str,\n",
- " model_id: str,\n",
- " publisher: str,\n",
- " publisher_model_id: str,\n",
- " service_account: str,\n",
- " base_model_id: str = None,\n",
- " machine_type: str = \"g2-standard-8\",\n",
- " accelerator_type: str = \"NVIDIA_L4\",\n",
- " accelerator_count: int = 1,\n",
- " gpu_memory_utilization: float = 0.9,\n",
- " max_model_len: int = 4096,\n",
- " dtype: str = \"auto\",\n",
- " enable_trust_remote_code: bool = False,\n",
- " enforce_eager: bool = False,\n",
- " enable_lora: bool = False,\n",
- " enable_chunked_prefill: bool = False,\n",
- " enable_prefix_cache: bool = False,\n",
- " host_prefix_kv_cache_utilization_target: float = 0.0,\n",
- " max_loras: int = 1,\n",
- " max_cpu_loras: int = 8,\n",
- " use_dedicated_endpoint: bool = False,\n",
- " max_num_seqs: int = 256,\n",
- " model_type: str = None,\n",
- " enable_llama_tool_parser: bool = False,\n",
- ") -> Tuple[aiplatform.Model, aiplatform.Endpoint]:\n",
- " \"\"\"Deploys trained models with vLLM into Vertex AI.\"\"\"\n",
- " endpoint = aiplatform.Endpoint.create(\n",
- " display_name=f\"{model_name}-endpoint\",\n",
- " dedicated_endpoint_enabled=use_dedicated_endpoint,\n",
- " )\n",
- "\n",
- " if not base_model_id:\n",
- " base_model_id = model_id\n",
- "\n",
- " # See https://docs.vllm.ai/en/latest/models/engine_args.html for a list of possible arguments with descriptions.\n",
- " vllm_args = [\n",
- " \"python\",\n",
- " \"-m\",\n",
- " \"vllm.entrypoints.api_server\",\n",
- " \"--host=0.0.0.0\",\n",
- " \"--port=8080\",\n",
- " f\"--model={model_id}\",\n",
- " f\"--tensor-parallel-size={accelerator_count}\",\n",
- " \"--swap-space=16\",\n",
- " f\"--max-model-len={max_model_len}\",\n",
- " f\"--dtype={dtype}\",\n",
- " f\"--max-loras={max_loras}\",\n",
- " f\"--max-cpu-loras={max_cpu_loras}\",\n",
- " f\"--max-num-seqs={max_num_seqs}\",\n",
- " \"--disable-log-stats\",\n",
- " ]\n",
- "\n",
- " if gpu_memory_utilization:\n",
- " vllm_args.append(f\"--gpu-memory-utilization={gpu_memory_utilization}\")\n",
- "\n",
- " if enable_trust_remote_code:\n",
- " vllm_args.append(\"--trust-remote-code\")\n",
- "\n",
- " if enforce_eager:\n",
- " vllm_args.append(\"--enforce-eager\")\n",
- "\n",
- " if enable_lora:\n",
- " vllm_args.append(\"--enable-lora\")\n",
- "\n",
- " if enable_chunked_prefill:\n",
- " vllm_args.append(\"--enable-chunked-prefill\")\n",
- "\n",
- " if enable_prefix_cache:\n",
- " vllm_args.append(\"--enable-prefix-caching\")\n",
- "\n",
- " if 0 < host_prefix_kv_cache_utilization_target < 1:\n",
- " vllm_args.append(\n",
- " f\"--host-prefix-kv-cache-utilization-target={host_prefix_kv_cache_utilization_target}\"\n",
- " )\n",
- "\n",
- " if model_type:\n",
- " vllm_args.append(f\"--model-type={model_type}\")\n",
- "\n",
- " if enable_llama_tool_parser:\n",
- " vllm_args.append(\"--enable-auto-tool-choice\")\n",
- " vllm_args.append(\"--tool-call-parser=vertex-llama-3\")\n",
- "\n",
- " env_vars = {\n",
- " \"MODEL_ID\": base_model_id,\n",
- " \"DEPLOY_SOURCE\": \"notebook\",\n",
- " }\n",
- "\n",
- " # HF_TOKEN is not a compulsory field and may not be defined.\n",
- " try:\n",
- " if HF_TOKEN:\n",
- " env_vars[\"HF_TOKEN\"] = HF_TOKEN\n",
- " except NameError:\n",
- " pass\n",
- "\n",
- " model = aiplatform.Model.upload(\n",
- " display_name=model_name,\n",
- " serving_container_image_uri=VLLM_DOCKER_URI,\n",
- " serving_container_args=vllm_args,\n",
- " serving_container_ports=[8080],\n",
- " serving_container_predict_route=\"/generate\",\n",
- " serving_container_health_route=\"/ping\",\n",
- " serving_container_environment_variables=env_vars,\n",
- " serving_container_shared_memory_size_mb=(16 * 1024), # 16 GB\n",
- " serving_container_deployment_timeout=7200,\n",
- " model_garden_source_model_name=(\n",
- " f\"publishers/{publisher}/models/{publisher_model_id}\"\n",
- " ),\n",
- " )\n",
- " print(\n",
- " f\"Deploying {model_name} on {machine_type} with {accelerator_count} {accelerator_type} GPU(s).\"\n",
- " )\n",
- " model.deploy(\n",
- " endpoint=endpoint,\n",
- " machine_type=machine_type,\n",
- " accelerator_type=accelerator_type,\n",
- " accelerator_count=accelerator_count,\n",
- " deploy_request_timeout=1800,\n",
- " service_account=service_account,\n",
- " system_labels={\n",
- " \"NOTEBOOK_NAME\": \"model_garden_pytorch_qwen2_5_finetuning.ipynb\",\n",
- " \"NOTEBOOK_ENVIRONMENT\": common_util.get_deploy_source(),\n",
- " },\n",
- " )\n",
- " print(\"endpoint_name:\", endpoint.name)\n",
- "\n",
- " return model, endpoint\n",
- "\n",
- "\n",
- "models[\"vllm_gpu\"], endpoints[\"vllm_gpu\"] = deploy_model_vllm(\n",
- " model_name=common_util.get_job_name_with_datetime(prefix=\"qwen2_5-vllm-serve\"),\n",
- " model_id=merged_model_output_dir,\n",
- " publisher=\"qwen\",\n",
- " publisher_model_id=\"qwen2_5\",\n",
- " service_account=SERVICE_ACCOUNT,\n",
- " machine_type=machine_type,\n",
- " accelerator_type=accelerator_type,\n",
- " accelerator_count=per_node_accelerator_count,\n",
- " gpu_memory_utilization=gpu_memory_utilization,\n",
- " max_model_len=max_model_len,\n",
- " enable_lora=True,\n",
- " use_dedicated_endpoint=use_dedicated_endpoint,\n",
- ")\n",
- "\n",
- "# @markdown Click \"Show Code\" to see more details."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "2UYUNn60G_4U"
- },
- "outputs": [],
- "source": [
- "# @title Predict\n",
- "\n",
- "# @markdown Once deployment succeeds, you can send requests to the endpoint with text prompts. Sampling parameters supported by vLLM can be found [here](https://docs.vllm.ai/en/latest/dev/sampling_params.html).\n",
- "\n",
- "# @markdown Example:\n",
- "\n",
- "# @markdown ```\n",
- "# @markdown Human: What is a car?\n",
- "# @markdown Assistant: A car, or a motor car, is a road-connected human-transportation system used to move people or goods from one place to another. The term also encompasses a wide range of vehicles, including motorboats, trains, and aircrafts. Cars typically have four wheels, a cabin for passengers, and an engine or motor. They have been around since the early 19th century and are now one of the most popular forms of transportation, used for daily commuting, shopping, and other purposes.\n",
- "# @markdown ```\n",
- "# @markdown Additionally, you can moderate the generated text with Vertex AI. See [Moderate text documentation](https://cloud.google.com/natural-language/docs/moderating-text) for more details.\n",
- "\n",
- "# Loads an existing endpoint instance using the endpoint name:\n",
- "# - Using `endpoint_name = endpoint.name` allows us to get the\n",
- "# endpoint name of the endpoint `endpoint` created in the cell\n",
- "# above.\n",
- "# - Alternatively, you can set `endpoint_name = \"1234567890123456789\"` to load\n",
- "# an existing endpoint with the ID 1234567890123456789.\n",
- "# You may uncomment the code below to load an existing endpoint.\n",
- "\n",
- "# endpoint_name = \"\" # @param {type:\"string\"}\n",
- "# aip_endpoint_name = (\n",
- "# f\"projects/{PROJECT_ID}/locations/{REGION}/endpoints/{endpoint_name}\"\n",
- "# )\n",
- "# endpoint = aiplatform.Endpoint(aip_endpoint_name)\n",
- "\n",
- "prompt = \"What is a car?\" # @param {type: \"string\"}\n",
- "# @markdown If you encounter an issue like `ServiceUnavailable: 503 Took too long to respond when processing`, you can reduce the maximum number of output tokens, by lowering `max_tokens`.\n",
- "max_tokens = 50 # @param {type:\"integer\"}\n",
- "temperature = 1.0 # @param {type:\"number\"}\n",
- "top_p = 1.0 # @param {type:\"number\"}\n",
- "top_k = 1 # @param {type:\"integer\"}\n",
- "# @markdown Set `raw_response` to `True` to obtain the raw model output. Set `raw_response` to `False` to apply additional formatting in the structure of `\"Prompt:\\n{prompt.strip()}\\nOutput:\\n{output}\"`.\n",
- "raw_response = False # @param {type:\"boolean\"}\n",
- "\n",
- "# Overrides parameters for inferences.\n",
- "instances = [\n",
- " {\n",
- " \"prompt\": prompt,\n",
- " \"max_tokens\": max_tokens,\n",
- " \"temperature\": temperature,\n",
- " \"top_p\": top_p,\n",
- " \"top_k\": top_k,\n",
- " \"raw_response\": raw_response,\n",
- " },\n",
- "]\n",
- "response = endpoints[\"vllm_gpu\"].predict(\n",
- " instances=instances, use_dedicated_endpoint=use_dedicated_endpoint\n",
- ")\n",
- "\n",
- "for prediction in response.predictions:\n",
- " print(prediction)\n",
- "\n",
- "# @markdown Click \"Show Code\" to see more details."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "af21a3cff1e0"
- },
- "source": [
- "## Clean up resources"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "cellView": "form",
- "id": "911406c1561e"
- },
- "outputs": [],
- "source": [
- "# @title Delete the model and endpoint\n",
- "\n",
- "if train_job:\n",
- " train_job.delete()\n",
- "if RUN_EVALUATION and lm_eval_job:\n",
- " lm_eval_job.delete()\n",
- "\n",
- "\n",
- "# @markdown Delete the experiment models and endpoints to recycle the resources\n",
- "# @markdown and avoid unnecessary continuous charges that may incur.\n",
- "\n",
- "# Undeploy model and delete endpoint.\n",
- "for endpoint in endpoints.values():\n",
- " endpoint.delete(force=True)\n",
- "\n",
- "# Delete models.\n",
- "for model in models.values():\n",
- " model.delete()\n",
- "\n",
- "delete_bucket = False # @param {type:\"boolean\"}\n",
- "if delete_bucket:\n",
- " ! gsutil -m rm -r $BUCKET_NAME"
- ]
- }
- ],
- "metadata": {
- "colab": {
- "name": "model_garden_pytorch_qwen2_5_finetuning.ipynb",
- "toc_visible": true
- },
- "kernelspec": {
- "display_name": "Python 3",
- "name": "python3"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 0
-}