Cara menyesuaikan LLM menggunakan Tugas Cloud Run

1. Pengantar

Ringkasan

Dalam codelab ini, Anda akan menggunakan tugas Cloud Run untuk menyempurnakan model Gemma 3, lalu menayangkan hasilnya di Cloud Run menggunakan vLLM.

Yang akan Anda lakukan

Latih model untuk merespons frasa tertentu dengan hasil tertentu menggunakan set data KomeijiForce/Text2Emoji, yang dibuat sebagai bagian dari EmojiLM: Modeling the New Emoji Language.

Setelah pelatihan, model merespons kalimat yang diawali dengan "Terjemahkan ke emoji: ", dengan serangkaian emoji yang sesuai dengan kalimat tersebut.

Yang akan Anda pelajari

  • Cara melakukan penyesuaian menggunakan GPU Tugas Cloud Run
  • Cara menayangkan model menggunakan Cloud Run dengan vLLM
  • Cara menggunakan konfigurasi VPC Langsung untuk Tugas GPU agar model dapat diupload dan ditayangkan lebih cepat

2. Sebelum memulai

Mengaktifkan API

Sebelum Anda dapat mulai menggunakan codelab ini, aktifkan API berikut dengan menjalankan:

gcloud services enable run.googleapis.com \
    compute.googleapis.com \
    run.googleapis.com \
    cloudbuild.googleapis.com \
    secretmanager.googleapis.com \
    artifactregistry.googleapis.com

Kuota GPU

Tinjau dokumentasi Kuota GPU untuk mengonfirmasi cara meminta kuota.

Jika Anda mengalami error "Anda tidak memiliki kuota untuk menggunakan GPU", konfirmasi kuota Anda di g.co/cloudrun/gpu-quota.

Catatan: Jika Anda menggunakan project baru, mungkin perlu waktu beberapa menit antara pengaktifan API dan munculnya kuota di halaman kuota.

Hugging Face

Codelab ini menggunakan model yang dihosting di Hugging Face. Untuk mendapatkan model ini, minta token akses pengguna Hugging Face dengan izin "Baca". Anda akan merujuknya nanti sebagai YOUR_HF_TOKEN.

Untuk menggunakan model gemma-3-1b-it, Anda harus menyetujui persyaratan penggunaan.

3. Penyiapan dan Persyaratan

Siapkan resource berikut:

  • Akun layanan IAM dan izin IAM terkait,
  • Secret Manager secret untuk menyimpan token Hugging Face Anda,
  • Bucket Cloud Storage untuk menyimpan model yang di-fine-tune, dan
  • Repositori Artifact Registry untuk menyimpan image yang akan Anda buat untuk menyempurnakan model.
  1. Tetapkan variabel lingkungan untuk codelab ini. Kami telah mengisi otomatis sejumlah variabel untuk Anda. Tentukan project ID, region, dan token Hugging Face Anda.
    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
    
  2. Buat akun layanan dengan menjalankan perintah ini:
    gcloud iam service-accounts create $SERVICE_ACCOUNT \
      --display-name="Service account for fine-tuning codelab"
    
  3. Gunakan Secret Manager untuk menyimpan token akses Hugging Face:
    gcloud secrets create $SECRET_ID \
          --replication-policy="automatic"
    
    printf $HF_TOKEN | gcloud secrets versions add $SECRET_ID --data-file=-
    
  4. Beri akun layanan Anda peran Secret Manager Secret Accessor:
    gcloud secrets add-iam-policy-binding $SECRET_ID \
      --member serviceAccount:$SERVICE_ACCOUNT_ADDRESS \
      --role='roles/secretmanager.secretAccessor'
    
  5. Buat bucket yang akan menghosting model yang di-fine-tune:
    gcloud storage buckets create -l $REGION gs://$BUCKET_NAME
    
  6. Beri akun layanan Anda akses ke bucket:
    gcloud storage buckets add-iam-policy-binding gs://$BUCKET_NAME \
      --member=serviceAccount:$SERVICE_ACCOUNT_ADDRESS \
      --role=roles/storage.objectAdmin
    
  7. Buat repositori Artifact Registry untuk menyimpan image container:
    gcloud artifacts repositories create $AR_REPO \
        --repository-format=docker \
        --location=$REGION \
        --description="codelab for finetuning using CR jobs" \
        --project=$PROJECT_ID
    

4. Buat image tugas Cloud Run

Pada langkah berikutnya, Anda akan membuat kode yang melakukan hal berikut:

  • Mengimpor model Gemma dari Hugging Face
  • Melakukan penyempurnaan pada model dengan set data dari Hugging Face. Tugas ini menggunakan satu GPU L4 untuk penyesuaian.
  • Mengupload model yang di-fine-tune bernama new_model ke bucket Cloud Storage Anda
  1. Buat direktori untuk kode tugas penyesuaian Anda.
    mkdir codelab-finetuning-job
    cd codelab-finetuning-job
    
  2. Buat file bernama 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)
    
    
  3. Buat file 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
    
  4. Buat 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
    
  5. Bangun container di repositori Artifact Registry Anda:
    gcloud builds submit \
      --tag $REGION-docker.pkg.dev/$PROJECT_ID/$AR_REPO/$IMAGE_NAME \
      --region $REGION
    

5. Men-deploy dan menjalankan tugas

Pada langkah ini, Anda akan membuat tugas dengan traffic keluar VPC langsung untuk upload yang lebih cepat ke Google Cloud Storage.

  1. Buat Tugas 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
    
  2. Jalankan tugas:
    gcloud run jobs execute $JOB_NAME --region $REGION --async
    

Tugas ini akan memerlukan waktu sekitar 10 menit untuk diselesaikan. Anda dapat memeriksa status menggunakan link yang diberikan dalam output perintah terakhir.

6. Menggunakan layanan Cloud Run untuk menayangkan model yang di-fine-tune dengan vLLM

Pada langkah ini, Anda akan men-deploy layanan Cloud Run. Konfigurasi ini menggunakan VPC langsung untuk mengakses bucket Cloud Storage melalui jaringan pribadi agar download lebih cepat.

  • Deploy Layanan Cloud Run Anda:
    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. Menguji model yang di-fine-tune

Pada langkah ini, Anda akan meminta model untuk menguji penyesuaian menggunakan curl.

  1. Dapatkan URL layanan untuk layanan Cloud Run Anda:
    SERVICE_URL=$(gcloud run services describe serve-gemma-emoji \
        --region $REGION --format 'value(status.url)')
    
  2. Buat perintah untuk model Anda.
    USER_PROMPT="Translate to emoji: I ate a banana for breakfast, later I'm thinking of having soup!"
    
  3. Panggil layanan Anda menggunakan curl untuk meminta model Anda, memfilter hasilnya dengan 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
    
    

Anda akan melihat respons seperti berikut:

🍌🤔😋🥣

8. Selamat!

Selamat, Anda telah menyelesaikan codelab.

Sebaiknya tinjau dokumentasi GPU Cloud Run Jobs.

Yang telah kita bahas

  • Cara melakukan penyesuaian menggunakan GPU Tugas Cloud Run
  • Cara menayangkan model menggunakan Cloud Run dengan vLLM
  • Cara menggunakan konfigurasi VPC Langsung untuk Tugas GPU agar model dapat diupload dan ditayangkan lebih cepat

9. Pembersihan

Untuk menghindari tagihan yang tidak disengaja, misalnya, jika layanan Cloud Run dipanggil secara tidak sengaja lebih banyak daripada alokasi pemanggilan Cloud Run bulanan Anda di tingkat gratis, Anda dapat menghapus layanan Cloud Run yang Anda buat di Langkah 6.

Untuk menghapus layanan Cloud Run, buka Konsol Cloud Run di https://console.cloud.google.com/run, lalu hapus layanan serve-gemma-emoji.

Untuk menghapus seluruh project, buka Manage Resources, pilih project yang Anda buat di Langkah 2, lalu pilih Hapus. Jika menghapus project, Anda harus mengubah project di Cloud SDK. Anda dapat melihat daftar semua project yang tersedia dengan menjalankan gcloud projects list.