1. Introdução
Visão geral
Neste codelab, você vai usar jobs do Cloud Run para ajustar um modelo Gemma 3 e veicular o resultado no Cloud Run usando o vLLM.
Atividades deste laboratório
Treine um modelo para responder a uma frase específica com um resultado específico usando o conjunto de dados KomeijiForce/Text2Emoji, estabelecido como parte do EmojiLM: Modeling the New Emoji Language (em inglês).
Depois do treinamento, o modelo responde a uma frase com o prefixo "Traduza para emoji: " com uma série de emojis correspondentes a essa frase.
O que você vai aprender
- Como fazer ajuste refinado usando a GPU do Cloud Run Jobs
- Como veicular um modelo usando o Cloud Run com vLLM
- Como usar a configuração da VPC direta em um job de GPU para fazer upload e disponibilizar o modelo mais rapidamente
2. Antes de começar
Ativar APIs
Antes de começar a usar este codelab, ative as seguintes APIs executando:
gcloud services enable run.googleapis.com \
compute.googleapis.com \
run.googleapis.com \
cloudbuild.googleapis.com \
secretmanager.googleapis.com \
artifactregistry.googleapis.com
Cota de GPU
Consulte a documentação de cota de GPU para confirmar como solicitar cota.
Se você encontrar erros do tipo "Não há cota para usar GPUs", confirme sua cota em g.co/cloudrun/gpu-quota.
Observação: se você estiver usando um novo projeto, pode levar alguns minutos entre a ativação da API e o aparecimento das cotas na página correspondente.
Hugging face
Este codelab usa um modelo hospedado no Hugging Face. Para receber esse modelo, solicite o token de acesso do usuário do Hugging Face com a permissão "Ler". Você vai se referir a ele mais tarde como YOUR_HF_TOKEN.
Para usar o modelo gemma-3-1b-it, é necessário aceitar os termos de uso.
3. Configuração e requisitos
Configure os seguintes recursos:
- Conta de serviço do IAM e permissões associadas do IAM
- Secret do Secret Manager para armazenar seu token do Hugging Face.
- Bucket do Cloud Storage para armazenar o modelo refinado e
- Repositório do Artifact Registry para armazenar a imagem que você vai criar para ajustar o modelo.
- Defina as variáveis de ambiente para este codelab. Preenchemos algumas variáveis para você. Especifique o ID do projeto, a região e o token do 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 - Execute este comando para criar a conta de serviço:
gcloud iam service-accounts create $SERVICE_ACCOUNT \ --display-name="Service account for fine-tuning codelab" - Use o Secret Manager para armazenar o token de acesso do Hugging Face:
gcloud secrets create $SECRET_ID \ --replication-policy="automatic" printf $HF_TOKEN | gcloud secrets versions add $SECRET_ID --data-file=- - Conceda à sua conta de serviço o papel de Acessador de secrets do Secret Manager:
gcloud secrets add-iam-policy-binding $SECRET_ID \ --member serviceAccount:$SERVICE_ACCOUNT_ADDRESS \ --role='roles/secretmanager.secretAccessor' - Crie um bucket para hospedar o modelo ajustado:
gcloud storage buckets create -l $REGION gs://$BUCKET_NAME - Conceda à sua conta de serviço acesso ao bucket:
gcloud storage buckets add-iam-policy-binding gs://$BUCKET_NAME \ --member=serviceAccount:$SERVICE_ACCOUNT_ADDRESS \ --role=roles/storage.objectAdmin - Crie um repositório do Artifact Registry para armazenar a imagem do contêiner:
gcloud artifacts repositories create $AR_REPO \ --repository-format=docker \ --location=$REGION \ --description="codelab for finetuning using CR jobs" \ --project=$PROJECT_ID
4. Criar a imagem do job do Cloud Run
Na próxima etapa, você vai criar o código que faz o seguinte:
- Importa o modelo Gemma do Hugging Face.
- Realiza o ajuste de detalhes no modelo com o conjunto de dados do Hugging Face. O job usa uma única GPU L4 para ajuste refinado.
- Faz upload do modelo refinado chamado
new_modelpara seu bucket do Cloud Storage.
- Crie um diretório para o código do job de ajuste refinado.
mkdir codelab-finetuning-job cd codelab-finetuning-job - Crie um arquivo chamado
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) - Crie um arquivo
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 - Crie uma
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 - Crie o contêiner no repositório do Artifact Registry:
gcloud builds submit \ --tag $REGION-docker.pkg.dev/$PROJECT_ID/$AR_REPO/$IMAGE_NAME \ --region $REGION
5. Implantar e executar o job
Nesta etapa, você vai criar o job com saída direta de VPC para uploads mais rápidos no Google Cloud Storage.
- Crie o job do 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 - Execute o job:
gcloud run jobs execute $JOB_NAME --region $REGION --async
O job leva cerca de 10 minutos para ser concluído. Verifique o status usando o link fornecido na saída do último comando.
6. Usar um serviço do Cloud Run para veicular seu modelo refinado com vLLM
Nesta etapa, você vai implantar um serviço do Cloud Run. Essa configuração usa a VPC direta para acessar o bucket do Cloud Storage em uma rede particular e fazer downloads mais rápidos.
- Implante o serviço do 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. Testar o modelo ajustado
Nesta etapa, você vai pedir ao modelo para testar o ajuste refinado usando curl.
- Extraia o URL do serviço do Cloud Run:
SERVICE_URL=$(gcloud run services describe serve-gemma-emoji \ --region $REGION --format 'value(status.url)') - Crie o comando para seu modelo.
USER_PROMPT="Translate to emoji: I ate a banana for breakfast, later I'm thinking of having soup!" - Chame seu serviço usando curl para solicitar o modelo, filtrando os resultados com 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
Uma resposta semelhante a esta vai aparecer:
🍌🤔😋🥣
8. Parabéns!
Parabéns por concluir o codelab!
Recomendamos que você consulte a documentação sobre GPUs do Cloud Run Jobs.
O que aprendemos
- Como fazer ajuste refinado usando a GPU do Cloud Run Jobs
- Como veicular um modelo usando o Cloud Run com vLLM
- Como usar a configuração da VPC direta em um job de GPU para fazer upload e disponibilizar o modelo mais rapidamente
9. Limpar
Para evitar cobranças acidentais, por exemplo, se os serviços do Cloud Run forem invocados mais vezes do que sua alocação mensal de invocações do Cloud Run no nível sem custo financeiro, exclua o serviço do Cloud Run criado na etapa 6.
Para excluir o serviço do Cloud Run, acesse o console do Cloud Run em https://console.cloud.google.com/run e exclua o serviço serve-gemma-emoji.
Para excluir todo o projeto, acesse Gerenciar recursos, selecione o projeto criado na etapa 2 e escolha "Excluir". Se você excluir o projeto, vai precisar mudar de projeto no SDK do Cloud. Para conferir a lista de todos os projetos disponíveis, execute gcloud projects list.