Informationen zu diesem Codelab
1. Einführung
Übersicht
In diesem Codelab verwenden Sie Cloud Run-Jobs, um ein Gemma-Modell zu optimieren, und stellen das Ergebnis dann mit vLLM in Cloud Run bereit.
In diesem Codelab verwenden Sie einen Text-zu-SQL-Dataset, damit der LLM auf eine Frage in natürlicher Sprache mit einer SQL-Abfrage antwortet.
Lerninhalte
- Feinabstimmung mit Cloud Run-Jobs mit GPU
- Modelle mit Cloud Run und vLLM bereitstellen
- Direct VPC-Konfiguration für einen GPU-Job verwenden, um das Modell schneller hochzuladen und bereitzustellen
2. Hinweis
APIs aktivieren
Bevor Sie dieses Codelab verwenden können, müssen Sie die folgenden APIs aktivieren. Führen Sie dazu Folgendes aus:
gcloud services enable run.googleapis.com \
compute.googleapis.com \
run.googleapis.com \
cloudbuild.googleapis.com \
secretmanager.googleapis.com \
artifactregistry.googleapis.com
GPU-Kontingent
Kontingenterhöhung für eine unterstützte Region anfordern Das Kontingent beträgt nvidia_l4_gpu_allocation_no_zonal_redundancy
und wird unter „Cloud Run Admin API“ angezeigt.
Hinweis: Wenn Sie ein neues Projekt verwenden, kann es einige Minuten dauern, bis die Kontingente auf dieser Seite angezeigt werden, nachdem Sie die API aktiviert haben.
Hugging Face
In diesem Codelab wird ein Modell verwendet, das auf Hugging Face gehostet wird. Wenn Sie dieses Modell abrufen möchten, fordern Sie das Hugging Face-Nutzerzugriffstoken mit der Berechtigung „Lesen“ an. Diese wird später als YOUR_HF_TOKEN
bezeichnet.
Außerdem müssen Sie den Nutzungsbedingungen zustimmen, um das Modell verwenden zu können: https://huggingface.co/google/gemma-2b
3. Einrichtung und Anforderungen
Richten Sie die folgenden Ressourcen ein:
- IAM-Dienstkonto und zugehörige IAM-Berechtigungen,
- Secret Manager-Secret zum Speichern des Hugging Face-Tokens,
- Cloud Storage-Bucket zum Speichern des optimierten Modells und
- Artifact Registry-Repository zum Speichern des Images, das Sie zum Optimieren Ihres Modells erstellen.
- Legen Sie Umgebungsvariablen für dieses Codelab fest. Wir haben bereits einige Variablen für Sie ausgefüllt. Geben Sie Ihre Projekt-ID, Region und Ihr Hugging Face-Token an.
export PROJECT_ID=<YOUR_PROJECT_ID>
export REGION=<YOUR_REGION>
export HF_TOKEN=<YOUR_HF_TOKEN>
export AR_REPO=codelab-finetuning-jobs
export IMAGE_NAME=finetune-to-gcs
export JOB_NAME=finetuning-to-gcs-job
export BUCKET_NAME=$PROJECT_ID-codelab-finetuning-jobs
export SECRET_ID=HF_TOKEN
export SERVICE_ACCOUNT="finetune-job-sa"
export SERVICE_ACCOUNT_ADDRESS=$SERVICE_ACCOUNT@$PROJECT_ID.iam.gserviceaccount.com - Erstellen Sie das Dienstkonto mit dem folgenden Befehl:
gcloud iam service-accounts create $SERVICE_ACCOUNT \
--display-name="Service account for fine-tuning codelab" - So speichern Sie das Hugging Face-Zugriffstoken mit Secret Manager:
gcloud secrets create $SECRET_ID \
--replication-policy="automatic"
printf $HF_TOKEN | gcloud secrets versions add $SECRET_ID --data-file=- - Weisen Sie Ihrem Dienstkonto die Rolle „Secret Manager Secret Accessor“ zu:
gcloud secrets add-iam-policy-binding $SECRET_ID \
--member serviceAccount:$SERVICE_ACCOUNT_ADDRESS \
--role='roles/secretmanager.secretAccessor' - Erstellen Sie einen Bucket, in dem das optimierte Modell gehostet wird:
gcloud storage buckets create -l $REGION gs://$BUCKET_NAME
- Gewähren Sie Ihrem Dienstkonto Zugriff auf den Bucket:
gcloud storage buckets add-iam-policy-binding gs://$BUCKET_NAME \
--member=serviceAccount:$SERVICE_ACCOUNT_ADDRESS \
--role=roles/storage.objectAdmin - Erstellen Sie ein Artifact Registry-Repository zum Speichern des Container-Images:
gcloud artifacts repositories create $AR_REPO \
--repository-format=docker \
--location=$REGION \
--description="codelab for finetuning using CR jobs" \
--project=$PROJECT_ID
4. Cloud Run-Job-Image erstellen
Im nächsten Schritt erstellen Sie den Code, der Folgendes tut:
- Importiert das Gemma-Modell aus Hugging Face
- Das Modell wird mit dem Dataset von Hugging Face optimiert. Für die Feinabstimmung wird eine einzelne L4-GPU verwendet.
- Lädt das optimierte Modell namens
new_model
in Ihren Cloud Storage-Bucket hoch
- Erstellen Sie ein Verzeichnis für den Jobcode der Feinabstimmung.
mkdir codelab-finetuning-job
cd codelab-finetuning-job - Erstellen Sie eine Datei mit dem Namen
finetune.py
.# Copyright 2024 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import os
import torch
from datasets import load_dataset
from transformers import (
AutoModelForCausalLM,
AutoTokenizer,
BitsAndBytesConfig,
TrainingArguments,
)
from peft import LoraConfig, PeftModel
from trl import SFTTrainer
# Cloud Storage bucket to upload the model
bucket_name = os.getenv("BUCKET_NAME", "YOUR_BUCKET_NAME")
# The model that you want to train from the Hugging Face hub
model_name = os.getenv("MODEL_NAME", "google/gemma-2b")
# The instruction dataset to use
dataset_name = "b-mc2/sql-create-context"
# Fine-tuned model name
new_model = os.getenv("NEW_MODEL", "gemma-2b-sql")
################################################################################
# QLoRA parameters
################################################################################
# LoRA attention dimension
lora_r = int(os.getenv("LORA_R", "4"))
# Alpha parameter for LoRA scaling
lora_alpha = int(os.getenv("LORA_ALPHA", "8"))
# Dropout probability for LoRA layers
lora_dropout = 0.1
################################################################################
# bitsandbytes parameters
################################################################################
# Activate 4-bit precision base model loading
use_4bit = True
# Compute dtype for 4-bit base models
bnb_4bit_compute_dtype = "float16"
# Quantization type (fp4 or nf4)
bnb_4bit_quant_type = "nf4"
# Activate nested quantization for 4-bit base models (double quantization)
use_nested_quant = False
################################################################################
# TrainingArguments parameters
################################################################################
# Output directory where the model predictions and checkpoints will be stored
output_dir = "./results"
# Number of training epochs
num_train_epochs = 1
# Enable fp16/bf16 training (set bf16 to True with an A100)
fp16 = True
bf16 = False
# Batch size per GPU for training
per_device_train_batch_size = int(os.getenv("TRAIN_BATCH_SIZE", "1"))
# Batch size per GPU for evaluation
per_device_eval_batch_size = int(os.getenv("EVAL_BATCH_SIZE", "2"))
# Number of update steps to accumulate the gradients for
gradient_accumulation_steps = int(os.getenv("GRADIENT_ACCUMULATION_STEPS", "1"))
# Enable gradient checkpointing
gradient_checkpointing = True
# Maximum gradient normal (gradient clipping)
max_grad_norm = 0.3
# Initial learning rate (AdamW optimizer)
learning_rate = 2e-4
# Weight decay to apply to all layers except bias/LayerNorm weights
weight_decay = 0.001
# Optimizer to use
optim = "paged_adamw_32bit"
# Learning rate schedule
lr_scheduler_type = "cosine"
# Number of training steps (overrides num_train_epochs)
max_steps = -1
# Ratio of steps for a linear warmup (from 0 to learning rate)
warmup_ratio = 0.03
# Group sequences into batches with same length
# Saves memory and speeds up training considerably
group_by_length = True
# Save checkpoint every X updates steps
save_steps = 0
# Log every X updates steps
logging_steps = int(os.getenv("LOGGING_STEPS", "50"))
################################################################################
# SFT parameters
################################################################################
# Maximum sequence length to use
max_seq_length = int(os.getenv("MAX_SEQ_LENGTH", "512"))
# Pack multiple short examples in the same input sequence to increase efficiency
packing = False
# Load the entire model on the GPU 0
device_map = {'':torch.cuda.current_device()}
# Set limit to a positive number
limit = int(os.getenv("DATASET_LIMIT", "5000"))
dataset = load_dataset(dataset_name, split="train")
if limit != -1:
dataset = dataset.shuffle(seed=42).select(range(limit))
def transform(data):
question = data['question']
context = data['context']
answer = data['answer']
template = "Question: {question}\nContext: {context}\nAnswer: {answer}"
return {'text': template.format(question=question, context=context, answer=answer)}
transformed = dataset.map(transform)
# Load tokenizer and model with QLoRA configuration
compute_dtype = getattr(torch, bnb_4bit_compute_dtype)
bnb_config = BitsAndBytesConfig(
load_in_4bit=use_4bit,
bnb_4bit_quant_type=bnb_4bit_quant_type,
bnb_4bit_compute_dtype=compute_dtype,
bnb_4bit_use_double_quant=use_nested_quant,
)
# Check GPU compatibility with bfloat16
if compute_dtype == torch.float16 and use_4bit:
major, _ = torch.cuda.get_device_capability()
if major >= 8:
print("=" * 80)
print("Your GPU supports bfloat16")
print("=" * 80)
# Load base model
model = AutoModelForCausalLM.from_pretrained(
model_name,
quantization_config=bnb_config,
device_map=device_map,
torch_dtype=torch.float16,
)
model.config.use_cache = False
model.config.pretraining_tp = 1
# Load LLaMA tokenizer
tokenizer = AutoTokenizer.from_pretrained(model_name, trust_remote_code=True)
tokenizer.pad_token = tokenizer.eos_token
tokenizer.padding_side = "right"
# Load LoRA configuration
peft_config = LoraConfig(
lora_alpha=lora_alpha,
lora_dropout=lora_dropout,
r=lora_r,
bias="none",
task_type="CAUSAL_LM",
target_modules=["q_proj", "v_proj"]
)
# Set training parameters
training_arguments = TrainingArguments(
output_dir=output_dir,
num_train_epochs=num_train_epochs,
per_device_train_batch_size=per_device_train_batch_size,
gradient_accumulation_steps=gradient_accumulation_steps,
optim=optim,
save_steps=save_steps,
logging_steps=logging_steps,
learning_rate=learning_rate,
weight_decay=weight_decay,
fp16=fp16,
bf16=bf16,
max_grad_norm=max_grad_norm,
max_steps=max_steps,
warmup_ratio=warmup_ratio,
group_by_length=group_by_length,
lr_scheduler_type=lr_scheduler_type,
)
trainer = SFTTrainer(
model=model,
train_dataset=transformed,
peft_config=peft_config,
dataset_text_field="text",
max_seq_length=max_seq_length,
tokenizer=tokenizer,
args=training_arguments,
packing=packing,
)
trainer.train()
trainer.model.save_pretrained(new_model)
# Reload model in FP16 and merge it with LoRA weights
base_model = AutoModelForCausalLM.from_pretrained(
model_name,
low_cpu_mem_usage=True,
return_dict=True,
torch_dtype=torch.float16,
device_map=device_map,
)
model = PeftModel.from_pretrained(base_model, new_model)
model = model.merge_and_unload()
# push to Cloud Storage
file_path_to_save_the_model = '/finetune/new_model'
model.save_pretrained(file_path_to_save_the_model)
tokenizer.save_pretrained(file_path_to_save_the_model) requirements.txt
-Datei erstellen:accelerate==0.34.2
bitsandbytes==0.45.5
datasets==2.19.1
transformers==4.51.3
peft==0.11.1
trl==0.8.6
torch==2.3.0- Erstellen Sie ein
Dockerfile
:FROM nvidia/cuda:12.6.2-runtime-ubuntu22.04
RUN apt-get update && \
apt-get -y --no-install-recommends install python3-dev gcc python3-pip git && \
rm -rf /var/lib/apt/lists/*
COPY requirements.txt /requirements.txt
RUN pip3 install -r requirements.txt --no-cache-dir
COPY finetune.py /finetune.py
ENV PYTHONUNBUFFERED 1
CMD python3 /finetune.py --device cuda - Erstellen Sie den Container in Ihrem Artifact Registry-Repository:
gcloud builds submit \
--tag $REGION-docker.pkg.dev/$PROJECT_ID/$AR_REPO/$IMAGE_NAME \
--region $REGION
5. Job bereitstellen und ausführen
In diesem Schritt erstellen Sie die YAML-Konfiguration für Ihren Job mit direktem VPC-Ausgang für schnellere Uploads in Google Cloud Storage.
Diese Datei enthält Variablen, die Sie in einem nachfolgenden Schritt aktualisieren.
- Erstellen Sie eine Datei mit dem Namen
finetune-job.yaml.tmpl
:apiVersion: run.googleapis.com/v1
kind: Job
metadata:
name: $JOB_NAME
labels:
cloud.googleapis.com/location: $REGION
annotations:
run.googleapis.com/launch-stage: ALPHA
spec:
template:
metadata:
annotations:
run.googleapis.com/execution-environment: gen2
run.googleapis.com/network-interfaces: '[{"network":"default","subnetwork":"default"}]'
spec:
parallelism: 1
taskCount: 1
template:
spec:
serviceAccountName: $SERVICE_ACCOUNT_ADDRESS
containers:
- name: $IMAGE_NAME
image: $REGION-docker.pkg.dev/$PROJECT_ID/$AR_REPO/$IMAGE_NAME
env:
- name: MODEL_NAME
value: "google/gemma-2b"
- name: NEW_MODEL
value: "gemma-2b-sql-finetuned"
- name: BUCKET_NAME
value: "$BUCKET_NAME"
- name: LORA_R
value: "8"
- name: LORA_ALPHA
value: "16"
- name: GRADIENT_ACCUMULATION_STEPS
value: "2"
- name: DATASET_LIMIT
value: "1000"
- name: LOGGING_STEPS
value: "5"
- name: HF_TOKEN
valueFrom:
secretKeyRef:
key: 'latest'
name: HF_TOKEN
resources:
limits:
cpu: 8000m
nvidia.com/gpu: '1'
memory: 32Gi
volumeMounts:
- mountPath: /finetune/new_model
name: finetuned_model
volumes:
- name: finetuned_model
csi:
driver: gcsfuse.run.googleapis.com
readOnly: false
volumeAttributes:
bucketName: $BUCKET_NAME
maxRetries: 3
timeoutSeconds: '3600'
nodeSelector:
run.googleapis.com/accelerator: nvidia-l4 - Ersetzen Sie die Variablen in der YAML-Datei durch Ihre Umgebungsvariablen. Führen Sie dazu den folgenden Befehl aus:
envsubst < finetune-job.yaml.tmpl > finetune-job.yaml
- Erstellen Sie den Cloud Run-Job:
gcloud alpha run jobs replace finetune-job.yaml
- Job ausführen:
gcloud alpha run jobs execute $JOB_NAME --region $REGION --async
Die Ausführung des Jobs dauert etwa 10 Minuten. Sie können den Status über den Link in der Ausgabe des letzten Befehls prüfen.
6. Cloud Run-Dienst zum Bereitstellen Ihres optimierten Modells mit vLLM verwenden
In diesem Schritt stellen Sie einen Cloud Run-Dienst bereit. Bei dieser Konfiguration wird Direct VPC verwendet, um über ein privates Netzwerk auf den Cloud Storage-Bucket zuzugreifen und so schnellere Downloads zu ermöglichen.
Diese Datei enthält Variablen, die Sie in einem nachfolgenden Schritt aktualisieren.
service.yaml.tmpl
-Datei erstellen:apiVersion: serving.knative.dev/v1
kind: Service
metadata:
name: serve-gemma-sql
labels:
cloud.googleapis.com/location: $REGION
annotations:
run.googleapis.com/launch-stage: BETA
run.googleapis.com/ingress: all
run.googleapis.com/ingress-status: all
spec:
template:
metadata:
labels:
annotations:
autoscaling.knative.dev/maxScale: '1'
run.googleapis.com/cpu-throttling: 'false'
run.googleapis.com/gpu-zonal-redundancy-disabled: 'true'
run.googleapis.com/network-interfaces: '[{"network":"default","subnetwork":"default"}]'
spec:
containers:
- name: serve-finetuned
image: us-docker.pkg.dev/vertex-ai/vertex-vision-model-garden-dockers/pytorch-vllm-serve:20250505_0916_RC00
ports:
- name: http1
containerPort: 8000
resources:
limits:
cpu: 8000m
nvidia.com/gpu: '1'
memory: 32Gi
volumeMounts:
- name: fuse
mountPath: /finetune/new_model
command: ["python3", "-m", "vllm.entrypoints.api_server"]
args:
- --model=/finetune/new_model
- --tensor-parallel-size=1
env:
- name: MODEL_ID
value: 'new_model'
- name: HF_HUB_OFFLINE
value: '1'
volumes:
- name: fuse
csi:
driver: gcsfuse.run.googleapis.com
volumeAttributes:
bucketName: $BUCKET_NAME
nodeSelector:
run.googleapis.com/accelerator: nvidia-l4- Aktualisieren Sie die Datei
service.yaml
mit dem Namen Ihres Buckets.envsubst < service.yaml.tmpl > service.yaml
- Cloud Run-Dienst bereitstellen:
gcloud alpha run services replace service.yaml
7. Optimiertes Modell testen
In diesem Schritt bitten Sie das Modell, die Feinabstimmung zu testen.
- Rufen Sie die Dienst-URL für Ihren Cloud Run-Dienst ab:
SERVICE_URL=$(gcloud run services describe serve-gemma-sql --platform managed --region $REGION --format 'value(status.url)')
- Erstellen Sie einen Prompt für Ihr Modell.
USER_PROMPT="Question: What are the first name and last name of all candidates? Context: CREATE TABLE candidates (candidate_id VARCHAR); CREATE TABLE people (first_name VARCHAR, last_name VARCHAR, person_id VARCHAR)"
- Rufen Sie Ihren Dienst mit CURL auf, um Ihr Modell zu prompten:
curl -X POST $SERVICE_URL/generate \
-H "Content-Type: application/json" \
-H "Authorization: bearer $(gcloud auth print-identity-token)" \
-d @- <<EOF
{
"prompt": "${USER_PROMPT}"
}
EOF
Die Antwort sieht ungefähr so aus:
{"predictions":["Prompt:\nQuestion: What are the first name and last name of all candidates? Context: CREATE TABLE candidates (candidate_id VARCHAR); CREATE TABLE people (first_name VARCHAR, last_name VARCHAR, person_id VARCHAR)\nOutput:\n CREATE TABLE people_to_candidates (candidate_id VARCHAR, person_id VARCHAR) CREATE TABLE people_to_people (person_id VARCHAR, person_id VARCHAR) CREATE TABLE people_to_people_to_candidates (person_id VARCHAR, candidate_id"]}
8. Glückwunsch!
Herzlichen Glückwunsch zum Abschluss des Codelabs.
Lesen Sie die Cloud Run-Dokumentation.
Behandelte Themen
- Feinabstimmung mit Cloud Run-Jobs mit GPU
- Modelle mit Cloud Run und vLLM bereitstellen
- Direct VPC-Konfiguration für einen GPU-Job verwenden, um das Modell schneller hochzuladen und bereitzustellen
9. Bereinigen
Wenn die Cloud Run-Dienste versehentlich häufiger aufgerufen werden als Ihre monatliche Cloud Run-Aufrufzuweisung im kostenlosen Tarif, können Sie den in Schritt 6 erstellten Cloud Run-Dienst löschen, um unbeabsichtigte Kosten zu vermeiden.
Wenn Sie den Cloud Run-Dienst löschen möchten, rufen Sie die Cloud Console unter https://console.cloud.google.com/run auf und löschen Sie den Dienst serve-gemma-sql
.
Wenn Sie das gesamte Projekt löschen möchten, rufen Sie Ressourcen verwalten auf, wählen Sie das Projekt aus, das Sie in Schritt 2 erstellt haben, und klicken Sie auf „Löschen“. Wenn Sie das Projekt löschen, müssen Sie die Projekte in Ihrem Cloud SDK ändern. Sie können eine Liste aller verfügbaren Projekte aufrufen, indem Sie gcloud projects list
ausführen.