Affiner Gemma 4 sur un cluster GKE A4 multi-hôte

Ce tutoriel explique comment affiner un grand modèle de langage (google/gemma-4-31b-it) Gemma 4 31B sur un cluster Google Kubernetes Engine (GKE) Autopilot multihôte et multi-GPU sur Google Cloud. Ce cluster utilise deux instances de machine virtuelle A4 (a4-highgpu-8g) avec un total de 16 GPU NVIDIA B200.

Les trois principaux processus décrits dans ce tutoriel sont les suivants :

  1. Déployez un cluster GKE multihôte en mode Autopilot.
  2. Créez une image de conteneur personnalisée avec les dépendances de réglage précis requises à l'aide de Cloud Build.
  3. Orchestrez une charge de travail d'affinage distribuée sur plusieurs hôtes sur les 16 GPU en utilisant Kubernetes JobSet et la bibliothèque Hugging Face Accelerate avec Fully Sharded Data Parallel v2 (FSDP v2), en envoyant les points de contrôle vers Hugging Face Hub.

Ce tutoriel s'adresse aux ingénieurs en machine learning (ML), aux chercheurs, aux administrateurs et opérateurs de plate-forme, ainsi qu'aux spécialistes des données et de l'IA qui déploient des clusters GKE sur Google Cloud pour affiner les LLM sur plusieurs hôtes.

Objectifs

  • Accédez au modèle Gemma 4 à l'aide de Hugging Face.

  • Préparez votre environnement.

  • Créez et déployez un cluster GKE A4 multihôte.

  • Ajustez le modèle Gemma 4 31B sur 16 GPU à l'aide de Kubernetes JobSet et de Hugging Face Accelerate avec FSDP v2.

  • surveiller votre job ;

  • Affichez les pondérations de l'adaptateur affiné sur le hub Hugging Face.

  • Effectuer un nettoyage.

Coûts

Dans ce document, vous utilisez les composants facturables suivants de Google Cloud :

Pour obtenir une estimation des coûts en fonction de votre utilisation prévue, utilisez le simulateur de coût.

Les nouveaux utilisateurs de Google Cloud peuvent bénéficier d'un essai sans frais.

Avant de commencer

Pour obtenir les autorisations nécessaires pour suivre ce tutoriel, demandez à votre administrateur de vous accorder les rôles IAM suivants sur votre projet :

Pour en savoir plus sur l'attribution de rôles, consultez la page Gérer l'accès aux projets, aux dossiers et aux organisations.

Vous pouvez également obtenir les autorisations requises avec des rôles personnalisés ou d'autres rôles prédéfinis.

  1. Activez les API requises, le cas échéant :

    Rôles requis pour activer les API

    Pour activer les API, vous devez disposer de l'autorisation serviceusage.services.enable. Si vous avez créé le projet, vous disposez probablement déjà de cette autorisation grâce au rôle Propriétaire (roles/owner). Sinon, vous pouvez obtenir cette autorisation grâce au rôle Administrateur Service Usage (roles/serviceusage.serviceUsageAdmin). Découvrez comment attribuer des rôles.

    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. Activez le compte de service Compute Engine par défaut pour votre projetGoogle Cloud  :

    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. Attribuez les rôles IAM suivant le principe du moindre privilège dont le compte de service Compute Engine par défaut a besoin pour créer l'image de conteneur et exécuter la charge de travail d'affinage :

    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. Vérifiez que les rôles ont été attribués au compte de service Compute Engine par défaut :

    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. Créez des identifiants d'authentification locaux pour votre compte utilisateur :

    gcloud auth application-default login
  6. Activez OS Login pour votre projet :

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

Accéder à Gemma 4 à l'aide de Hugging Face

Pour utiliser Hugging Face afin d'accéder à Gemma 4, procédez comme suit :

  1. Connectez-vous à Hugging Face et acceptez le contrat de licence Gemma 4.
  2. Créez un jeton d'accès write Hugging Face.
    Cliquez sur Votre profil > Paramètres > Jetons d'accès > + Créer un jeton.
  3. Copiez et enregistrez la valeur du jeton d'accès write. Vous utilisez ce jeton pour télécharger le modèle de base et transférer les points de contrôle de l'adaptateur affiné vers Hugging Face Hub avant que GKE ne réduise la capacité des nœuds GPU.

Préparer votre environnement

Pour préparer votre environnement, définissez les variables d'environnement suivantes :

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}"

Remplacez les éléments suivants :

  • YOUR_PROJECT_ID : ID du Google Cloud projet dans lequel vous souhaitez créer le cluster GKE.

  • YOUR_CLUSTER_NAME : nom du cluster GKE à créer.

  • YOUR_REGION : région dans laquelle vous souhaitez créer votre cluster GKE. Vous ne pouvez créer le cluster que dans la région où se trouve votre réservation.

  • YOUR_RESERVATION_NAME : identifiant de votre capacité réservée.

  • YOUR_HF_TOKEN : jeton d'accès write Hugging Face que vous avez créé dans la section précédente.

  • YOUR_ARTIFACT_REGISTRY_LOCATION : région Google Cloud (par exemple, us-central1) dans laquelle vous souhaitez créer votre dépôt Artifact Registry. Pour réduire la latence d'extraction des images, utilisez la même région que celle spécifiée pour YOUR_REGION.

  • YOUR_NUMBER_OF_NODES : nombre de nœuds de VM A4 dans votre job d'affinage. Pour ce tutoriel multihôte avec 16 GPU NVIDIA B200 répartis sur deux instances a4-highgpu-8g, définissez cette valeur sur 2.

Créer un cluster GKE multihôte en mode Autopilot

Créez un cluster GKE multihôte en mode Autopilot :

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

La création du cluster GKE peut prendre plusieurs minutes. Pour vérifier que Google Cloud a terminé de créer votre cluster, accédez à Clusters Kubernetes dans la console Google Cloud .

Configurer kubectl pour communiquer avec votre cluster GKE

Configurez kubectl pour communiquer avec votre cluster GKE :

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

Créer un secret Kubernetes pour les identifiants Hugging Face

Créez un secret Kubernetes pour stocker votre jeton Hugging Face :

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

Préparer votre charge de travail

Pour préparer votre charge de travail, procédez comme suit :

  1. Créez des scripts de charge de travail.

  2. Utilisez Docker et Cloud Build pour créer un conteneur d'affinage.

Créer des scripts de charge de travail

Pour créer les fichiers de configuration et les scripts utilisés par votre charge de travail d'affinage, procédez comme suit :

  1. Créez un répertoire pour les scripts de charge de travail. Utilisez ce répertoire comme répertoire de travail.

    mkdir llm-finetuning-gemma
    cd llm-finetuning-gemma
  2. Créez le fichier cloudbuild.yaml pour créer l'image de conteneur de votre charge de travail avec Cloud Build et la transférer vers 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. Créez un fichier Dockerfile pour définir l'environnement et installer les dépendances requises pour effectuer le job d'affinage :

    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. Créez le fichier accel_fsdp_gemma4_config.yaml. Cette configuration indique à Hugging Face Accelerate de partitionner Gemma4TextDecoderLayer sur 16 GPU sur deux hôtes à l'aide de 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. Créez le fichier manifeste 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. Créez le script d'affinage supervisé 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()

Créer un conteneur d'affinage à l'aide de Docker et Cloud Build

  1. Créez un dépôt 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. Installez les définitions de ressources personnalisées (CRD) JobSet requises pour orchestrer les charges de travail multihôtes :

    kubectl apply --server-side \
        -f https://github.com/kubernetes-sigs/jobset/releases/download/v0.12.0/manifests.yaml
  3. Dans le répertoire llm-finetuning-gemma que vous avez créé lors d'une étape précédente, envoyez la compilation du conteneur à Cloud Build :

    gcloud builds submit . \
        --substitutions=_ARTIFACT_REPO_LOCATION="${ARTIFACT_REPO_LOCATION}"
  4. Exportez l'URL de l'image de conteneur multi-hôte. Vous en aurez besoin lors d'une prochaine étape de ce tutoriel, lorsque vous déploierez le fichier manifeste JobSet :

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

Démarrer votre charge de travail d'affinage

Pour déployer et surveiller votre charge de travail d'affinage distribué, procédez comme suit :

  1. Remplacez les variables d'environnement dans le fichier manifeste d'affinage pour créer le job d'affinage :

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

    Étant donné que votre cluster s'exécute en mode GKE Autopilot, le provisionnement des deux nœuds A4 compatibles avec les GPU et l'extraction de l'image de conteneur peuvent prendre quelques minutes.

  2. Surveillez les pods de nœud de calcul jusqu'à ce que les deux pods passent à l'état Running :

    watch kubectl get pods
  3. Une fois que les pods de nœuds de calcul sont passés à l'état Running, diffusez les journaux d'entraînement :

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

Surveiller votre charge de travail

Vous pouvez surveiller l'utilisation des GPU dans votre cluster GKE pour vérifier que les 16 GPU des deux hôtes A4 traitent activement les étapes d'entraînement. Générez et ouvrez le lien d'observabilité dans votre navigateur :

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\"))"

Lorsque vous surveillez votre charge de travail, attendez-vous au comportement suivant :

  • Utilisation du GPU : pour une tâche de réglage fin distribuée saine, vous pouvez vous attendre à ce que l'utilisation du GPU sur les 16 GPU NVIDIA B200 augmente et se stabilise entre 95 % et 100% pendant les étapes d'entraînement.
  • Durée du job : sur deux nœuds a4-highgpu-8g (16 GPU B200), le job de réglage fin de trois époques prend environ deux heures et demie.

Afficher les pondérations de votre adaptateur affiné

Une fois l'entraînement terminé, consultez les pondérations et les points de contrôle de votre adaptateur LoRA affiné sur Hugging Face Hub à l'adresse https://huggingface.co/YOUR_HF_USERNAME/gemma-31b-text-to-sql.

Effectuer un nettoyage

Pour éviter des frais supplémentaires, supprimez les ressources créées au cours de ce tutoriel.

Supprimer vos ressources

  1. Supprimez l'affinage JobSet :

    kubectl delete jobset finetune-jobset
  2. Supprimez le cluster GKE :

    gcloud container clusters delete "${CLUSTER_NAME}" \
        --region="${CLUSTER_REGION}"
  3. Supprimez votre dépôt Artifact Registry :

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

Étapes suivantes