Menyesuaikan Gemma 4 di cluster GKE A4 multi-host

Tutorial ini menunjukkan cara menyesuaikan model bahasa besar (google/gemma-4-31b-it) Gemma 4 31B pada cluster Autopilot Google Kubernetes Engine (GKE) multi-host dan multi-GPU di Google Cloud. Cluster ini menggunakan dua instance virtual machine (VM) A4 (a4-highgpu-8g) dengan total 16 GPU NVIDIA B200.

Tiga proses utama yang dijelaskan dalam tutorial ini adalah sebagai berikut:

  1. Deploy cluster GKE multi-host dalam mode Autopilot.
  2. Bangun image container kustom dengan dependensi penyesuaian yang diperlukan menggunakan Cloud Build.
  3. Mengorkestrasi workload penyesuaian multi-host terdistribusi di semua 16 GPU dengan menggunakan JobSet Kubernetes dan library Hugging Face Accelerate dengan Fully Sharded Data Parallel v2 (FSDP v2), yang mendorong titik pemeriksaan ke Hugging Face Hub.

Tutorial ini ditujukan untuk engineer, peneliti, administrator, dan operator platform machine learning (ML), serta spesialis data dan AI yang men-deploy cluster GKE di Google Cloud untuk menyesuaikan LLM di beberapa host.

Tujuan

  • Akses model Gemma 4 menggunakan Hugging Face.

  • Siapkan lingkungan Anda.

  • Buat dan deploy cluster GKE A4 multi-host.

  • Lakukan penyesuaian model Gemma 4 31B di 16 GPU menggunakan Kubernetes JobSet dan Hugging Face Accelerate dengan FSDP v2.

  • Pantau tugas Anda.

  • Lihat bobot adaptor yang di-fine-tune di Hugging Face Hub.

  • Jalankan pembersihan.

Biaya

Dalam dokumen ini, Anda akan menggunakan komponen Google Cloudyang dapat ditagih berikut:

Untuk membuat perkiraan biaya berdasarkan proyeksi penggunaan Anda, gunakan kalkulator harga.

Pengguna Google Cloud baru mungkin memenuhi syarat untuk mendapatkan uji coba gratis.

Sebelum memulai

Untuk mendapatkan izin yang Anda perlukan untuk menyelesaikan tutorial ini, minta administrator Anda untuk memberi Anda peran IAM berikut di project Anda:

Untuk mengetahui informasi selengkapnya tentang pemberian peran, lihat Mengelola akses ke project, folder, dan organisasi.

Anda mungkin juga bisa mendapatkan izin yang diperlukan melalui peran khusus atau peran bawaan lainnya.

  1. Aktifkan API yang diperlukan, jika ada yang belum diaktifkan:

    Peran yang diperlukan untuk mengaktifkan API

    Untuk mengaktifkan API, Anda memerlukan izin serviceusage.services.enable. Jika Anda membuat project, kemungkinan Anda sudah memiliki izin ini melalui peran Pemilik (roles/owner). Jika tidak, Anda bisa mendapatkan izin ini melalui peran Admin Penggunaan Layanan (roles/serviceusage.serviceUsageAdmin). Pelajari cara memberikan peran.

    gcloud services enable compute.googleapis.com container.googleapis.com artifactregistry.googleapis.com cloudbuild.googleapis.com logging.googleapis.com cloudresourcemanager.googleapis.com servicenetworking.googleapis.com
  2. Aktifkan akun layanan Compute Engine default untuk projectGoogle Cloud Anda:

    export PROJECT_NUMBER="$(gcloud projects describe "YOUR_PROJECT_ID" --format "value(project_number)")"
    gcloud iam service-accounts enable "${PROJECT_NUMBER}-compute@developer.gserviceaccount.com" \
        --project=YOUR_PROJECT_ID
  3. Berikan peran IAM dengan hak istimewa terendah yang diperlukan akun layanan Compute Engine default untuk membuat image container dan menjalankan workload penyesuaian:

    ROLES=(
      "roles/artifactregistry.writer"
      "roles/cloudbuild.builds.builder"
      "roles/logging.logWriter"
      "roles/monitoring.metricWriter"
      "roles/monitoring.viewer"
      "roles/stackdriver.resourceMetadata.writer"
      "roles/storage.objectViewer"
    )
    for role in "${ROLES[@]}"; do
      gcloud projects add-iam-policy-binding YOUR_PROJECT_ID \
          --member="serviceAccount:${PROJECT_NUMBER}-compute@developer.gserviceaccount.com" \
          --role="${role}" 1>/dev/null
    done
    unset ROLES
  4. Pastikan peran diberikan ke akun layanan default Compute Engine:

    echo "Displaying roles for ${PROJECT_NUMBER}-compute@developer.gserviceaccount.com:"
    gcloud projects get-iam-policy YOUR_PROJECT_ID \
        --flatten="bindings[].members" \
        --filter="bindings.members:serviceAccount:${PROJECT_NUMBER}-compute@developer.gserviceaccount.com" \
        --format="table(bindings.role)"
  5. Buat kredensial autentikasi lokal untuk akun pengguna Anda:

    gcloud auth application-default login
  6. Aktifkan Login OS untuk project Anda:

    gcloud compute project-info add-metadata \
        --metadata=enable-oslogin=TRUE \
        --project=YOUR_PROJECT_ID

Mengakses Gemma 4 menggunakan Hugging Face

Untuk menggunakan Hugging Face guna mengakses Gemma 4, selesaikan langkah-langkah berikut:

  1. Login ke Hugging Face dan setujui perjanjian lisensi Gemma 4.
  2. Buat token akses write Hugging Face.
    Klik Profil Anda > Setelan > Token akses > +Buat token baru.
  3. Salin dan simpan nilai token akses write. Anda menggunakan token ini untuk mendownload model dasar dan mengirimkan checkpoint adapter yang telah disesuaikan ke Hugging Face Hub sebelum GKE memperkecil skala node GPU.

Menyiapkan lingkungan Anda

Untuk menyiapkan lingkungan Anda, tetapkan variabel lingkungan berikut:

export PROJECT_ID="YOUR_PROJECT_ID"
export CLUSTER_NAME="YOUR_CLUSTER_NAME"
export CLUSTER_REGION="YOUR_REGION"
export RESERVATION="YOUR_RESERVATION_NAME"
export HF_TOKEN="YOUR_HF_TOKEN"
export ARTIFACT_REPO_LOCATION="YOUR_ARTIFACT_REGISTRY_LOCATION"
export NUM_NODES="YOUR_NUMBER_OF_NODES"

gcloud config set project "${PROJECT_ID}"
gcloud config set billing/quota_project "${PROJECT_ID}"

Ganti kode berikut:

  • YOUR_PROJECT_ID: ID Google Cloud project tempat Anda ingin membuat cluster GKE.

  • YOUR_CLUSTER_NAME: nama cluster GKE yang akan dibuat.

  • YOUR_REGION: region tempat Anda ingin membuat cluster GKE. Anda hanya dapat membuat cluster di region tempat reservasi Anda berada.

  • YOUR_RESERVATION_NAME: ID untuk kapasitas yang dipesan.

  • YOUR_HF_TOKEN: token akses Hugging Face write yang Anda buat di bagian sebelumnya.

  • YOUR_ARTIFACT_REGISTRY_LOCATION: Google Cloud region (misalnya, us-central1) tempat Anda ingin membuat repositori Artifact Registry. Untuk meminimalkan latensi penarikan gambar, gunakan region yang sama dengan yang Anda tentukan untuk YOUR_REGION.

  • YOUR_NUMBER_OF_NODES: jumlah node VM A4 dalam tugas penyesuaian Anda. Untuk tutorial multi-host ini dengan 16 GPU NVIDIA B200 di dua instance a4-highgpu-8g, tetapkan nilai ini ke 2.

Membuat cluster GKE multi-host dalam mode Autopilot

Buat cluster GKE multi-host dalam mode Autopilot:

gcloud container clusters create-auto "${CLUSTER_NAME}" \
    --project="${PROJECT_ID}" \
    --location="${CLUSTER_REGION}"

Mungkin perlu waktu beberapa menit untuk menyelesaikan pembuatan cluster GKE. Untuk memverifikasi bahwa Google Cloud telah selesai membuat cluster Anda, buka Kubernetes clusters di konsol Google Cloud .

Mengonfigurasi kubectl untuk berkomunikasi dengan cluster GKE Anda

Konfigurasi kubectl untuk berkomunikasi dengan cluster GKE Anda:

gcloud container clusters get-credentials "${CLUSTER_NAME}" \
    --location="${CLUSTER_REGION}"

Buat secret Kubernetes untuk kredensial Hugging Face

Buat secret Kubernetes untuk menyimpan token Hugging Face Anda:

kubectl create secret generic hf-secret \
    --from-literal=hf_api_token="${HF_TOKEN}" \
    --dry-run=client -o yaml | kubectl apply -f -

Menyiapkan workload Anda

Untuk menyiapkan workload, Anda perlu melakukan hal berikut:

  1. Buat skrip beban kerja.

  2. Gunakan Docker dan Cloud Build untuk membuat container penyesuaian.

Membuat skrip beban kerja

Untuk membuat file dan skrip konfigurasi yang digunakan workload penyesuaian, selesaikan langkah-langkah berikut:

  1. Buat direktori untuk skrip beban kerja. Gunakan direktori ini sebagai direktori kerja Anda.

    mkdir llm-finetuning-gemma
    cd llm-finetuning-gemma
  2. Buat file cloudbuild.yaml untuk membangun image container beban kerja Anda dengan Cloud Build dan kirimkan ke Artifact Registry:

    steps:
    - name: 'gcr.io/cloud-builders/docker'
      args:
      - 'build'
      - '-t'
      - '$_ARTIFACT_REPO_LOCATION-docker.pkg.dev/$PROJECT_ID/gemma/finetune-gemma-multihost-gpu:2.0.0'
      - '.'
    images:
    - '$_ARTIFACT_REPO_LOCATION-docker.pkg.dev/$PROJECT_ID/gemma/finetune-gemma-multihost-gpu:2.0.0'
    options:
      logging: CLOUD_LOGGING_ONLY
  3. Buat file Dockerfile untuk menentukan lingkungan dan menginstal dependensi yang diperlukan untuk menyelesaikan tugas penyesuaian:

    FROM nvidia/cuda:12.8.1-cudnn-devel-ubuntu24.04
    RUN apt-get update && \
        apt-get -y install python3 python3-dev gcc python3-pip \
            python3-venv git curl vim && \
        rm -rf /var/lib/apt/lists/*
    RUN python3 -m venv /opt/venv
    ENV PATH="/opt/venv/bin:/usr/local/nvidia/bin:$PATH"
    ENV LD_LIBRARY_PATH="/usr/local/nvidia/lib64:$LD_LIBRARY_PATH"
    RUN pip3 install setuptools wheel packaging ninja
    RUN pip3 install torch torchvision torchaudio \
        --index-url https://download.pytorch.org/whl/cu128
    RUN pip3 install \
        "transformers>=5.5.0" \
        trl==0.29.1 \
        peft==0.18.1 \
        accelerate==1.13.0 \
        bitsandbytes==0.49.2 \
        datasets==4.8.4 \
        evaluate==0.4.5 \
        tensorboard==2.20.0 \
        protobuf==6.31.1 \
        sentencepiece==0.2.0
    WORKDIR /workspace
    COPY finetune.py /workspace/finetune.py
    COPY accel_fsdp_gemma4_config.yaml /workspace/accel_fsdp_gemma4_config.yaml
    CMD ["accelerate", "launch", "--config_file", "/workspace/accel_fsdp_gemma4_config.yaml", "/workspace/finetune.py"]
  4. Buat file accel_fsdp_gemma4_config.yaml. Konfigurasi ini mengarahkan Hugging Face Accelerate untuk membagi Gemma4TextDecoderLayer di 16 GPU pada dua host dengan menggunakan FSDP v2:

    compute_environment: LOCAL_MACHINE
    debug: false
    distributed_type: FSDP
    downcast_bf16: 'no'
    enable_cpu_affinity: false
    fsdp_config:
      fsdp_activation_checkpointing: false
      fsdp_auto_wrap_policy: TRANSFORMER_BASED_WRAP
      fsdp_cpu_ram_efficient_loading: false
      fsdp_offload_params: true
      fsdp_reshard_after_forward: true
      fsdp_state_dict_type: FULL_STATE_DICT
      fsdp_transformer_layer_cls_to_wrap: Gemma4TextDecoderLayer
      fsdp_version: 2
    machine_rank: 0
    main_training_function: main
    mixed_precision: bf16
    num_machines: 2
    num_processes: 16
    rdzv_backend: static
    same_network: true
    tpu_env: []
    tpu_use_cluster: false
    tpu_use_sudo: false
    use_cpu: false
  5. Buat manifes finetune.yaml Kubernetes JobSet:

    apiVersion: resource.k8s.io/v1
    kind: ResourceClaimTemplate
    metadata:
      name: mrdma
    spec:
      spec:
        devices:
          requests:
          - name: mrdma
            exactly:
              deviceClassName: mrdma.google.com
    ---
    apiVersion: jobset.x-k8s.io/v1alpha2
    kind: JobSet
    metadata:
      name: finetune-jobset
      namespace: default
    spec:
      failurePolicy:
        maxRestarts: 2
      replicatedJobs:
      - name: workers
        replicas: 1
        template:
          spec:
            parallelism: ${NUM_NODES}
            completions: ${NUM_NODES}
            backoffLimit: 0
            template:
              metadata:
                annotations:
                  kubectl.kubernetes.io/default-container: finetuner
              spec:
                terminationGracePeriodSeconds: 600
                restartPolicy: OnFailure
                nodeSelector:
                  cloud.google.com/compute-class: "Accelerator"
                  cloud.google.com/gke-accelerator: "nvidia-b200"
                  cloud.google.com/reservation-name: ${RESERVATION}
                  cloud.google.com/reservation-affinity: "specific"
                  cloud.google.com/gke-gpu-driver-version: latest
                containers:
                - name: finetuner
                  image: $IMAGE_URL
                  command: ["bash", "-c"]
                  args:
                  - |
                    NUM_PROCESSES=$(( ${NUM_NODES} * 8 ))
                    accelerate launch \
                      --config_file /workspace/accel_fsdp_gemma4_config.yaml \
                      --num_machines ${NUM_NODES} \
                      --num_processes ${NUM_PROCESSES} \
                      --machine_rank ${JOB_COMPLETION_INDEX} \
                      --main_process_ip finetune-jobset-workers-0-0.finetune-jobset.default.svc.cluster.local \
                      --main_process_port 29500 \
                      /workspace/finetune.py \
                      --base_model google/gemma-4-31b-it \
                      --new_model gemma-31b-text-to-sql \
                      --per_device_train_batch_size 4 \
                      --gradient_accumulation_steps 4 \
                      --num_train_epochs 3 \
                      --learning_rate 1e-5 \
                      --save_strategy steps \
                      --save_steps 15 \
                      --push_to_hub
                  resources:
                    limits:
                      nvidia.com/gpu: "8"
                      memory: "1000Gi"
                      ephemeral-storage: "350Gi"
                    requests:
                      nvidia.com/gpu: "8"
                      memory: "1000Gi"
                      ephemeral-storage: "350Gi"
                  env:
                  - name: HF_TOKEN
                    valueFrom:
                      secretKeyRef:
                        name: hf-secret
                        key: hf_api_token
                  - name: NUM_NODES
                    value: "${NUM_NODES}"
                  volumeMounts:
                  - mountPath: /dev/shm
                    name: dshm
                volumes:
                - name: dshm
                  emptyDir:
                    medium: Memory
                    sizeLimit: 64Gi
  6. Buat skrip supervised fine-tuning finetune.py:

    import argparse
    import torch
    from datasets import load_dataset
    from huggingface_hub import login
    from peft import LoraConfig
    from transformers import AutoConfig, AutoModelForCausalLM, AutoTokenizer
    from trl import SFTConfig, SFTTrainer
    
    
    def get_args():
        def str2bool(v):
            if isinstance(v, bool):
                return v
            return v.lower() in ("yes", "true", "t", "1")
    
        parser = argparse.ArgumentParser()
        parser.add_argument(
            "--base_model",
            "--model_id",
            dest="base_model",
            type=str,
            default="google/gemma-4-31b-it",
            help="Hugging Face model ID",
        )
        parser.add_argument(
            "--hf_token",
            type=str,
            default=None,
            help="Hugging Face token for gated models and Hub uploads",
        )
        parser.add_argument(
            "--trust_remote",
            type=str2bool,
            default=False,
            help="Trust remote code when loading tokenizer",
        )
        parser.add_argument(
            "--use_fast",
            type=str2bool,
            default=True,
            help="Determines if a fast Rust-based tokenizer should be used",
        )
        parser.add_argument(
            "--dataset_name",
            type=str,
            default="philschmid/gretel-synthetic-text-to-sql",
            help="Hugging Face dataset name",
        )
        parser.add_argument(
            "--new_model",
            "--output_dir",
            dest="new_model",
            type=str,
            default="gemma-31b-text-to-sql",
            help="Directory and repository name to save model checkpoints",
        )
    
        # LoRA arguments
        parser.add_argument(
            "--lora_r", type=int, default=16, help="LoRA attention dimension"
        )
        parser.add_argument(
            "--lora_alpha", type=int, default=32, help="LoRA alpha scaling factor"
        )
        parser.add_argument(
            "--lora_dropout",
            type=float,
            default=0.05,
            help="LoRA dropout probability",
        )
        # SFTConfig arguments
        parser.add_argument(
            "--max_length",
            type=int,
            default=1024,
            help="Maximum sequence length",
        )
        parser.add_argument(
            "--num_train_epochs",
            type=int,
            default=3,
            help="Number of training epochs",
        )
        parser.add_argument(
            "--per_device_train_batch_size",
            type=int,
            default=4,
            help="Batch size per device during training",
        )
        parser.add_argument(
            "--gradient_accumulation_steps",
            type=int,
            default=4,
            help="Gradient accumulation steps",
        )
        parser.add_argument(
            "--learning_rate", type=float, default=1e-5, help="Learning rate"
        )
        parser.add_argument(
            "--logging_steps", type=int, default=10, help="Log every X steps"
        )
        parser.add_argument(
            "--save_strategy",
            type=str,
            default="steps",
            help="Checkpoint save strategy",
        )
        parser.add_argument(
            "--save_steps",
            type=int,
            default=15,
            help="Save checkpoint every X steps",
        )
        parser.add_argument(
            "--push_to_hub",
            action="store_true",
            help="Push model back up to Hugging Face Hub",
        )
        parser.add_argument(
            "--hub_private_repo",
            type=str2bool,
            default=True,
            help="Push to a private repository on Hugging Face Hub",
        )
        return parser.parse_args()
    
    
    def main():
        args = get_args()
        # --- 1. Setup and Login ---
        if args.hf_token:
            login(args.hf_token)
        # --- 2. Create and prepare the fine-tuning dataset ---
        dataset = load_dataset(args.dataset_name, split="train")
        dataset = dataset.shuffle().select(range(12500))
        dataset = dataset.train_test_split(test_size=2500 / 12500)
        # --- 3. Configure Model and Tokenizer ---
        if torch.cuda.is_available() and torch.cuda.get_device_capability()[0] >= 8:
            torch_dtype_obj = torch.bfloat16
        else:
            torch_dtype_obj = torch.float16
        tokenizer = AutoTokenizer.from_pretrained(
            args.base_model,
            trust_remote_code=args.trust_remote,
            use_fast=args.use_fast,
        )
        if tokenizer.pad_token is None:
            tokenizer.pad_token = tokenizer.eos_token
    
        # --- 4. Define the Formatting Function ---
        def formatting_func(example):
            system_message = (
                "You are a text to SQL query translator. Users will ask you "
                "questions in English and you will generate a SQL query based "
                "on the provided SCHEMA."
            )
            user_prompt = (
                "Given the <USER_QUERY> and the <SCHEMA>, generate the "
                "corresponding SQL command to retrieve the desired data, "
                "considering the query's syntax, semantics, and schema "
                "constraints.\n\n<SCHEMA>\n{context}\n</SCHEMA>\n\n"
                "<USER_QUERY>\n{question}\n</USER_QUERY>\n"
            )
    
            messages = [
                {"role": "system", "content": system_message},
                {
                    "role": "user",
                    "content": user_prompt.format(
                        question=example["sql_prompt"],
                        context=example["sql_context"],
                    ),
                },
                {"role": "assistant", "content": example["sql"]},
            ]
            return tokenizer.apply_chat_template(messages, tokenize=False)
    
        # --- 5. Load Model and Configure LoRA ---
        config = AutoConfig.from_pretrained(args.base_model)
        config.use_cache = False
        print("Loading base model...")
        model = AutoModelForCausalLM.from_pretrained(
            args.base_model,
            config=config,
            attn_implementation="sdpa",
            torch_dtype=torch_dtype_obj,
        )
    
        peft_config = LoraConfig(
            lora_alpha=args.lora_alpha,
            lora_dropout=args.lora_dropout,
            r=args.lora_r,
            bias="none",
            target_modules=[
                "q_proj",
                "k_proj",
                "v_proj",
                "o_proj",
                "gate_proj",
                "up_proj",
                "down_proj",
            ],
            exclude_modules=r".*(vision_tower|embed_vision|audio_tower|embed_audio).*",
            task_type="CAUSAL_LM",
        )
        # --- 6. Configure Training Arguments ---
        training_args = SFTConfig(
            output_dir=args.new_model,
            max_length=args.max_length,
            num_train_epochs=args.num_train_epochs,
            per_device_train_batch_size=args.per_device_train_batch_size,
            gradient_accumulation_steps=args.gradient_accumulation_steps,
            learning_rate=args.learning_rate,
            logging_steps=args.logging_steps,
            save_strategy=args.save_strategy,
            save_steps=args.save_steps,
            packing=False,
            label_names=["domain"],
            gradient_checkpointing=True,
            gradient_checkpointing_kwargs={"use_reentrant": False},
            optim="adamw_torch",
            fp16=torch_dtype_obj == torch.float16,
            bf16=torch_dtype_obj == torch.bfloat16,
            max_grad_norm=0.3,
            warmup_steps=0.03,
            lr_scheduler_type="constant",
            push_to_hub=args.push_to_hub,
            hub_private_repo=args.hub_private_repo,
            report_to="tensorboard",
        )
        # --- 7. Create Trainer and Start Training ---
        trainer = SFTTrainer(
            model=model,
            args=training_args,
            peft_config=peft_config,
            train_dataset=dataset["train"],
            eval_dataset=dataset["test"],
            processing_class=tokenizer,
            formatting_func=formatting_func,
        )
        print("Starting training...")
        trainer.train()
        print("Training finished.")
        # --- 8. Save the final model ---
        print(f"Saving final model to {args.new_model}")
        if trainer.is_fsdp_enabled:
            trainer.accelerator.state.fsdp_plugin.set_state_dict_type(
                "FULL_STATE_DICT"
            )
        trainer.save_model(args.new_model)
        if torch.distributed.is_initialized():
            torch.distributed.destroy_process_group()
    
    
    if __name__ == "__main__":
        main()

Menggunakan Docker dan Cloud Build untuk membuat container penyesuaian

  1. Buat repositori Docker Artifact Registry:

    gcloud artifacts repositories create gemma \
        --repository-format=docker \
        --location="${ARTIFACT_REPO_LOCATION}" \
        --description="Repository for Gemma fine tuning workload containers" || true
  2. Instal definisi resource kustom (CRD) JobSet yang diperlukan untuk mengatur workload multi-host:

    kubectl apply --server-side \
        -f https://github.com/kubernetes-sigs/jobset/releases/download/v0.12.0/manifests.yaml
  3. Di direktori llm-finetuning-gemma yang Anda buat pada langkah sebelumnya, kirim build container ke Cloud Build:

    gcloud builds submit . \
        --substitutions=_ARTIFACT_REPO_LOCATION="${ARTIFACT_REPO_LOCATION}"
  4. Ekspor URL image container multi-host. Anda akan menggunakannya di langkah selanjutnya dalam tutorial ini, saat men-deploy manifes JobSet:

    IMAGE_REGISTRY="${ARTIFACT_REPO_LOCATION}-docker.pkg.dev/${PROJECT_ID}"
    export IMAGE_URL="${IMAGE_REGISTRY}/gemma/finetune-gemma-multihost-gpu:2.0.0"

Mulai workload penyesuaian Anda

Untuk men-deploy dan memantau workload penyesuaian terdistribusi, selesaikan langkah-langkah berikut:

  1. Ganti variabel lingkungan ke dalam manifes penyesuaian untuk membuat tugas penyesuaian:

    envsubst '${RESERVATION} ${IMAGE_URL} ${NUM_NODES}' < finetune.yaml \
        | kubectl apply -f -

    Karena cluster Anda berjalan dalam mode GKE Autopilot, mungkin perlu waktu beberapa menit untuk menyediakan dua node A4 yang mendukung GPU dan menarik image container.

  2. Pantau pod pekerja hingga kedua pod bertransisi ke status Running:

    watch kubectl get pods
  3. Setelah pod pekerja bertransisi ke Running, streaming log pelatihan:

    kubectl logs -l "job-name=finetune-jobset-workers-0" -f

Memantau workload Anda

Anda dapat memantau pemakaian GPU di seluruh cluster GKE untuk memverifikasi bahwa semua 16 GPU di kedua host A4 secara aktif memproses langkah-langkah pelatihan. Buat dan buka link kemampuan pengamatan di browser Anda:

echo "https://console.cloud.google.com/kubernetes/clusters/details/${CLUSTER_REGION}/${CLUSTER_NAME}/observability?mods=monitoring_api_prod&project=${PROJECT_ID}&pageState=(\"timeRange\":(\"duration\":\"PT1H\"),\"nav\":(\"section\":\"gpu\"),\"groupBy\":(\"groupByType\":\"namespacesTop5\"))"

Saat Anda memantau workload, perhatikan perilaku berikut:

  • Pemanfaatan GPU: Untuk tugas penyesuaian terdistribusi yang berjalan lancar, Anda dapat melihat pemanfaatan GPU di semua 16 GPU NVIDIA B200 meningkat dan stabil di sekitar 95%–100% selama langkah-langkah pelatihan.
  • Durasi tugas: Di dua node a4-highgpu-8g (16 GPU B200), tugas penyesuaian 3-epoch memerlukan waktu sekitar 2 setengah jam untuk diselesaikan.

Melihat bobot adaptor yang di-fine-tune

Setelah pelatihan selesai, lihat bobot dan titik pemeriksaan adaptor LoRA yang di-fine-tune di Hugging Face Hub di https://huggingface.co/YOUR_HF_USERNAME/gemma-31b-text-to-sql.

Pembersihan

Agar tidak menimbulkan biaya tambahan, hapus resource yang dibuat selama tutorial ini.

Menghapus resource

  1. Hapus penyesuaian JobSet:

    kubectl delete jobset finetune-jobset
  2. Hapus cluster GKE Anda:

    gcloud container clusters delete "${CLUSTER_NAME}" \
        --region="${CLUSTER_REGION}"
  3. Hapus repositori Artifact Registry Anda:

    gcloud artifacts repositories delete gemma \
        --location="${ARTIFACT_REPO_LOCATION}" \
        --quiet

Langkah berikutnya