1. Introduction
Présentation
Dans cet atelier de programmation, vous allez utiliser des jobs Cloud Run pour affiner un modèle Gemma 3, puis diffuser le résultat sur Cloud Run à l'aide de vLLM.
Objectifs de l'atelier
Entraînez un modèle pour qu'il réponde à une expression spécifique avec un résultat spécifique à l'aide de l'ensemble de données KomeijiForce/Text2Emoji, établi dans le cadre de EmojiLM: Modeling the New Emoji Language.
Après l'entraînement, le modèle répond à une phrase préfixée par "Translate to emoji: " (Traduire en emoji : ) par une série d'emoji correspondant à cette phrase.
Points abordés
- Effectuer un réglage précis à l'aide de GPU Cloud Run Jobs
- Mettre en service un modèle à l'aide de Cloud Run avec vLLM
- Utiliser la configuration VPC direct pour un job de GPU afin d'accélérer l'importation et la diffusion du modèle
2. Avant de commencer
Activer les API
Avant de commencer à utiliser cet atelier de programmation, activez les API suivantes en exécutant la commande :
gcloud services enable run.googleapis.com \
compute.googleapis.com \
run.googleapis.com \
cloudbuild.googleapis.com \
secretmanager.googleapis.com \
artifactregistry.googleapis.com
Quota de GPU
Consultez la documentation sur le quota de GPU pour savoir comment demander un quota.
Si vous rencontrez des erreurs de type "Vous n'avez pas de quota pour utiliser des GPU", vérifiez votre quota sur g.co/cloudrun/gpu-quota.
Remarque : Si vous utilisez un nouveau projet, il peut s'écouler quelques minutes entre l'activation de l'API et l'affichage des quotas sur la page des quotas.
Hugging Face
Cet atelier de programmation utilise un modèle hébergé sur Hugging Face. Pour obtenir ce modèle, demandez le jeton d'accès utilisateur Hugging Face avec l'autorisation "Lecture". Vous y ferez référence plus tard sous le nom YOUR_HF_TOKEN.
Pour utiliser le modèle gemma-3-1b-it, vous devez accepter les conditions d'utilisation.
3. Préparation
Configurez les ressources suivantes :
- Compte de service IAM et autorisations IAM associées
- Secret Secret Manager pour stocker votre jeton Hugging Face.
- Un bucket Cloud Storage pour stocker votre modèle affiné.
- Dépôt Artifact Registry pour stocker l'image que vous allez créer pour affiner votre modèle.
- Définissez les variables d'environnement pour cet atelier de programmation. Nous avons prérempli un certain nombre de variables pour vous. Spécifiez l'ID de votre projet, votre région et votre jeton Hugging Face.
export PROJECT_ID=<YOUR_PROJECT_ID> export REGION=<YOUR_REGION> export HF_TOKEN=<YOUR_HF_TOKEN> export NEW_MODEL=gemma-emoji 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 - Créez le compte de service en exécutant la commande suivante :
gcloud iam service-accounts create $SERVICE_ACCOUNT \ --display-name="Service account for fine-tuning codelab" - Utilisez Secret Manager pour stocker le jeton d'accès Hugging Face :
gcloud secrets create $SECRET_ID \ --replication-policy="automatic" printf $HF_TOKEN | gcloud secrets versions add $SECRET_ID --data-file=- - Attribuez le rôle "Accesseur de secrets Secret Manager" à votre compte de service :
gcloud secrets add-iam-policy-binding $SECRET_ID \ --member serviceAccount:$SERVICE_ACCOUNT_ADDRESS \ --role='roles/secretmanager.secretAccessor' - Créez un bucket qui hébergera votre modèle affiné :
gcloud storage buckets create -l $REGION gs://$BUCKET_NAME - Accordez à votre compte de service l'accès au bucket :
gcloud storage buckets add-iam-policy-binding gs://$BUCKET_NAME \ --member=serviceAccount:$SERVICE_ACCOUNT_ADDRESS \ --role=roles/storage.objectAdmin - Créez un dépôt Artifact Registry pour stocker l'image de conteneur :
gcloud artifacts repositories create $AR_REPO \ --repository-format=docker \ --location=$REGION \ --description="codelab for finetuning using CR jobs" \ --project=$PROJECT_ID
4. Créer l'image du job Cloud Run
À l'étape suivante, vous allez créer le code qui effectue les opérations suivantes :
- Importe le modèle Gemma depuis Hugging Face
- Ajuste le modèle avec l'ensemble de données de Hugging Face. La tâche utilise un seul GPU L4 pour l'affinage.
- Importe le modèle affiné nommé
new_modeldans votre bucket Cloud Storage
- Créez un répertoire pour le code de votre job de réglage précis.
mkdir codelab-finetuning-job cd codelab-finetuning-job - Créez un fichier appelé
finetune.py.# Copyright 2025 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 peft import LoraConfig, PeftModel from transformers import ( AutoModelForCausalLM, AutoTokenizer, BitsAndBytesConfig, TrainingArguments, ) 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-3-1b-it") # The instruction dataset to use dataset_name = "KomeijiForce/Text2Emoji" # Fine-tuned model name new_model = os.getenv("NEW_MODEL", "gemma-emoji") ############################ Setup ############################################ # Load the entire model on the GPU 0 device_map = {"": torch.cuda.current_device()} # Limit dataset to a random selection dataset = load_dataset(dataset_name, split="train").shuffle(seed=42).select(range(1000)) # Setup input formats: trains the model to respond to "Translate to emoji:" with emoji output. tokenizer = AutoTokenizer.from_pretrained(model_name) def format_to_chat(example): return { "conversations": [ {"role": "user", "content": f"Translate to emoji: {example['text']}"}, {"role": "assistant", "content": example["emoji"]}, ] } formatted_dataset = dataset.map( format_to_chat, batched=False, # Process row by row remove_columns=dataset.column_names, # Optional: Keep only the new column ) def apply_chat_template(examples): texts = tokenizer.apply_chat_template(examples["conversations"], tokenize=False) return {"text": texts} final_dataset = formatted_dataset.map(apply_chat_template, batched=True) ############################# Config ######################################### # Load tokenizer and model with QLoRA configuration bnb_4bit_compute_dtype = "float16" # Compute dtype for 4-bit base models compute_dtype = getattr(torch, bnb_4bit_compute_dtype) bnb_config = BitsAndBytesConfig( load_in_4bit=True, # Activate 4-bit precision base model loading bnb_4bit_quant_type="nf4", # Quantization type (fp4 or nf4) bnb_4bit_compute_dtype=compute_dtype, bnb_4bit_use_double_quant=False, # Activate nested quantization for 4-bit base models (double quantization) ) # 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 ############################## Train ########################################## # Load LoRA configuration peft_config = LoraConfig( lora_alpha=16, # Alpha parameter for LoRA scaling lora_dropout=0.1, # Dropout probability for LoRA layers, r=8, # LoRA attention dimension bias="none", task_type="CAUSAL_LM", target_modules=["q_proj", "v_proj"], ) # Set training parameters training_arguments = TrainingArguments( output_dir="./results", num_train_epochs=1, per_device_train_batch_size=1, # Batch size per GPU for training gradient_accumulation_steps=2, # Number of update steps to accumulate the gradients for optim="paged_adamw_32bit", save_steps=0, logging_steps=5, learning_rate=2e-4, # Initial learning rate (AdamW optimizer) weight_decay=0.001, # Weight decay to apply to all layers except bias/LayerNorm weights fp16=True, bf16=False, # Enable fp16/bf16 training max_grad_norm=0.3, # Maximum gradient normal (gradient clipping) warmup_ratio=0.03, # Ratio of steps for a linear warmup (from 0 to learning rate) group_by_length=True, # Group sequences into batches with same length # Saves memory and speeds up training considerably lr_scheduler_type="cosine", ) trainer = SFTTrainer( model=model, train_dataset=final_dataset, peft_config=peft_config, dataset_text_field="text", max_seq_length=512, # Maximum sequence length to use tokenizer=tokenizer, args=training_arguments, packing=False, # Pack multiple short examples in the same input sequence to increase efficiency ) trainer.train() trainer.model.save_pretrained(new_model) ################################# Save ######################################## # 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 results 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) - Créez un fichier
requirements.txt: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 - Créez une
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 - Créez le conteneur dans votre dépôt Artifact Registry :
gcloud builds submit \ --tag $REGION-docker.pkg.dev/$PROJECT_ID/$AR_REPO/$IMAGE_NAME \ --region $REGION
5. Déployer et exécuter le job
Dans cette étape, vous allez créer le job avec sortie VPC directe pour accélérer les importations vers Google Cloud Storage.
- Créez le job Cloud Run :
gcloud run jobs create $JOB_NAME \ --region $REGION \ --image $REGION-docker.pkg.dev/$PROJECT_ID/$AR_REPO/$IMAGE_NAME \ --set-env-vars BUCKET_NAME=$BUCKET_NAME \ --set-secrets HF_TOKEN=$SECRET_ID:latest \ --cpu 8.0 \ --memory 32Gi \ --gpu 1 \ --add-volume name=finetuned_model,type=cloud-storage,bucket=$BUCKET_NAME \ --add-volume-mount volume=finetuned_model,mount-path=/finetune/new_model \ --service-account $SERVICE_ACCOUNT_ADDRESS - Exécuter le job :
gcloud run jobs execute $JOB_NAME --region $REGION --async
L'exécution de ce job prendra environ 10 minutes. Vous pouvez vérifier l'état à l'aide du lien fourni dans le résultat de la dernière commande.
6. Utiliser un service Cloud Run pour diffuser votre modèle affiné avec vLLM
Dans cette étape, vous allez déployer un service Cloud Run. Cette configuration utilise le VPC direct pour accéder au bucket Cloud Storage sur un réseau privé afin d'accélérer les téléchargements.
- Déployez votre service Cloud Run :
gcloud run deploy serve-gemma-emoji \ --image us-docker.pkg.dev/vertex-ai/vertex-vision-model-garden-dockers/pytorch-vllm-serve:20250601_0916_RC01 \ --region $REGION \ --port 8000 \ --set-env-vars MODEL_ID=new_model,HF_HUB_OFFLINE=1 \ --cpu 8.0 \ --memory 32Gi \ --gpu 1 \ --add-volume name=finetuned_model,type=cloud-storage,bucket=$BUCKET_NAME \ --add-volume-mount volume=finetuned_model,mount-path=/finetune/new_model \ --service-account $SERVICE_ACCOUNT_ADDRESS \ --max-instances 1 \ --command python3 \ --args="-m,vllm.entrypoints.api_server,--model=/finetune/new_model,--tensor-parallel-size=1" \ --no-gpu-zonal-redundancy \ --labels=dev-tutorial=codelab-tuning \ --no-invoker-iam-check
7. Tester votre modèle affiné
Dans cette étape, vous allez demander à votre modèle de tester le réglage précis à l'aide de curl.
- Obtenez l'URL du service pour votre service Cloud Run :
SERVICE_URL=$(gcloud run services describe serve-gemma-emoji \ --region $REGION --format 'value(status.url)') - Créez votre requête pour votre modèle.
USER_PROMPT="Translate to emoji: I ate a banana for breakfast, later I'm thinking of having soup!" - Appelez votre service à l'aide de curl pour solliciter votre modèle, en filtrant les résultats avec jq :
curl -s -X POST ${SERVICE_URL}/v1/chat/completions \ -H "Content-Type: application/json" \ -H "Authorization: bearer $(gcloud auth print-identity-token)" \ -d @- <<EOF | jq ".choices[0].message.content" { "model": "${NEW_MODEL}", "messages": [{ "role": "user", "content": [ { "type": "text", "text": "${USER_PROMPT}"}] }] } EOF
Un résultat semblable à celui-ci doit s'afficher :
🍌🤔😋🥣
8. Félicitations !
Bravo ! Vous avez terminé cet atelier de programmation.
Nous vous recommandons de consulter la documentation sur les GPU Cloud Run Jobs.
Points abordés
- Effectuer un réglage précis à l'aide de GPU Cloud Run Jobs
- Mettre en service un modèle à l'aide de Cloud Run avec vLLM
- Utiliser la configuration VPC direct pour un job de GPU afin d'accélérer l'importation et la diffusion du modèle
9. Effectuer un nettoyage
Pour éviter des frais involontaires, par exemple si les services Cloud Run sont appelés par inadvertance plus de fois que votre quota mensuel d'appels Cloud Run dans le niveau sans frais, vous pouvez supprimer le service Cloud Run que vous avez créé à l'étape 6.
Pour supprimer le service Cloud Run, accédez à la console Cloud Run à l'adresse https://console.cloud.google.com/run, puis supprimez le service serve-gemma-emoji.
Pour supprimer l'intégralité du projet, accédez à Gérer les ressources, sélectionnez le projet que vous avez créé à l'étape 2, puis choisissez "Supprimer". Si vous supprimez le projet, vous devrez changer de projet dans votre SDK Cloud. Vous pouvez afficher la liste de tous les projets disponibles en exécutant gcloud projects list.