Ce tutoriel explique comment affiner le grand modèle de langage (LLM) Gemma 3 sur un cluster Slurm multinœud qui utilise deux instances de machine virtuelle (VM) A4. Dans ce tutoriel, vous allez effectuer les opérations suivantes :
Créer une image personnalisée
Configurez un réseau RDMA.
Exécutez un job de réglage distribué. Pour un entraînement efficace sur plusieurs nœuds, vous utilisez la bibliothèque Hugging Face Accelerate avec le parallélisme des données entièrement segmentées (FSDP).
Ce tutoriel s'adresse aux ingénieurs en machine learning (ML), aux administrateurs et opérateurs de plate-forme, ainsi qu'aux spécialistes des données et de l'IA qui souhaitent utiliser les fonctionnalités de planification des jobs Slurm pour gérer les charges de travail de réglage fin.
Objectifs
Accédez à Gemma 3 à l'aide de Hugging Face.
Préparez votre environnement.
Créez un cluster Slurm A4.
Préparer votre charge de travail.
Exécutez un job d'affinage.
surveiller votre job ;
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.
Avant de commencer
- Connectez-vous à votre compte Google Cloud . Si vous débutez sur Google Cloud, créez un compte pour évaluer les performances de nos produits en conditions réelles. Les nouveaux clients bénéficient également de 300 $de crédits sans frais pour exécuter, tester et déployer des charges de travail.
-
Installez la Google Cloud CLI.
-
Si vous utilisez un fournisseur d'identité (IdP) externe, vous devez d'abord vous connecter à la gcloud CLI avec votre identité fédérée.
-
Pour initialiser la gcloud CLI, exécutez la commande suivante :
gcloud init -
Créez ou sélectionnez un projet Google Cloud .
Rôles requis pour sélectionner ou créer un projet
- Sélectionnez un projet : la sélection d'un projet ne nécessite pas de rôle IAM spécifique. Vous pouvez sélectionner n'importe quel projet pour lequel un rôle vous a été attribué.
-
Créer un projet : pour créer un projet, vous devez disposer du rôle Créateur de projet (
roles/resourcemanager.projectCreator), qui contient l'autorisationresourcemanager.projects.create. Découvrez comment attribuer des rôles.
-
Créez un projet Google Cloud :
gcloud projects create PROJECT_ID
Remplacez
PROJECT_IDpar le nom du projet Google Cloud que vous créez. -
Sélectionnez le projet Google Cloud que vous avez créé :
gcloud config set project PROJECT_ID
Remplacez
PROJECT_IDpar le nom de votre projet Google Cloud .
-
Vérifiez que la facturation est activée pour votre projet Google Cloud .
Activez l'API requise :
Rôles requis pour activer les API
Pour activer les API, vous avez besoin du rôle IAM Administrateur Service Usage (
roles/serviceusage.serviceUsageAdmin), qui contient l'autorisationserviceusage.services.enable. Découvrez comment attribuer des rôles.gcloud services enable compute.googleapis.com file.googleapis.com logging.googleapis.com cloudresourcemanager.googleapis.com servicenetworking.googleapis.com
-
Installez la Google Cloud CLI.
-
Si vous utilisez un fournisseur d'identité (IdP) externe, vous devez d'abord vous connecter à la gcloud CLI avec votre identité fédérée.
-
Pour initialiser la gcloud CLI, exécutez la commande suivante :
gcloud init -
Créez ou sélectionnez un projet Google Cloud .
Rôles requis pour sélectionner ou créer un projet
- Sélectionnez un projet : la sélection d'un projet ne nécessite pas de rôle IAM spécifique. Vous pouvez sélectionner n'importe quel projet pour lequel un rôle vous a été attribué.
-
Créer un projet : pour créer un projet, vous devez disposer du rôle Créateur de projet (
roles/resourcemanager.projectCreator), qui contient l'autorisationresourcemanager.projects.create. Découvrez comment attribuer des rôles.
-
Créez un projet Google Cloud :
gcloud projects create PROJECT_ID
Remplacez
PROJECT_IDpar le nom du projet Google Cloud que vous créez. -
Sélectionnez le projet Google Cloud que vous avez créé :
gcloud config set project PROJECT_ID
Remplacez
PROJECT_IDpar le nom de votre projet Google Cloud .
-
Vérifiez que la facturation est activée pour votre projet Google Cloud .
Activez l'API requise :
Rôles requis pour activer les API
Pour activer les API, vous avez besoin du rôle IAM Administrateur Service Usage (
roles/serviceusage.serviceUsageAdmin), qui contient l'autorisationserviceusage.services.enable. Découvrez comment attribuer des rôles.gcloud services enable compute.googleapis.com file.googleapis.com logging.googleapis.com cloudresourcemanager.googleapis.com servicenetworking.googleapis.com
-
Attribuez des rôles à votre compte utilisateur. Exécutez la commande suivante une fois pour chacun des rôles IAM suivants :
roles/compute.admin, roles/iam.serviceAccountUser, roles/file.editor, roles/storage.admin, roles/serviceusage.serviceUsageAdmingcloud projects add-iam-policy-binding PROJECT_ID --member="user:USER_IDENTIFIER" --role=ROLE
Remplacez les éléments suivants :
PROJECT_ID: ID de votre projetUSER_IDENTIFIER: identifiant de votre compte d'utilisateur. Par exemple,myemail@example.com.ROLE: rôle IAM que vous accordez à votre compte utilisateur.
- Activez le compte de service par défaut pour votre projet Google Cloud :
gcloud iam service-accounts enable PROJECT_NUMBER-compute@developer.gserviceaccount.com \ --project=PROJECT_ID
Remplacez PROJECT_NUMBER par votre numéro de projet. Pour consulter le numéro de votre projet, consultez Obtenir un projet existant.
- Attribuez le rôle Éditeur (
roles/editor) au compte de service par défaut :gcloud projects add-iam-policy-binding PROJECT_ID \ --member="serviceAccount:PROJECT_NUMBER-compute@developer.gserviceaccount.com" \ --role=roles/editor
- Créez des identifiants d'authentification locaux pour votre compte utilisateur :
gcloud auth application-default login
- Activez OS Login pour votre projet :
gcloud compute project-info add-metadata --metadata=enable-oslogin=TRUE
- Connectez-vous à votre compte Hugging Face ou créez-en un.
Accéder à Gemma 3 à l'aide de Hugging Face
Pour utiliser Hugging Face afin d'accéder à Gemma 3, procédez comme suit :
Signez le contrat de consentement pour utiliser Gemma 3 12B.
Créez un jeton d'accès
readHugging Face. Cliquez sur Votre profil > Paramètres > Jetons d'accès > + Créer un jeton.Copiez et enregistrez la valeur du jeton
read access. Vous en aurez besoin dans la suite de ce tutoriel.
Préparer votre environnement
Pour préparer votre environnement, procédez comme suit :
Clonez le dépôt GitHub Cluster Toolkit :
git clone https://github.com/GoogleCloudPlatform/cluster-toolkit.gitCréez un bucket Cloud Storage :
gcloud storage buckets create gs://BUCKET_NAME \ --project=PROJECT_IDRemplacez les éléments suivants :
BUCKET_NAME: nom de votre bucket Cloud Storage qui respecte les exigences de dénomination des buckets.PROJECT_ID: ID du projetGoogle Cloud dans lequel vous souhaitez créer votre bucket Cloud Storage.
Créer un cluster Slurm A4
Pour créer un cluster Slurm A4, procédez comme suit :
Accédez au répertoire
cluster-toolkit:cd cluster-toolkitSi vous utilisez Cluster Toolkit pour la première fois, créez le binaire
gcluster:makeAccédez au répertoire
examples/machine-learning/a4-highgpu-8g:cd examples/machine-learning/a4-highgpu-8g/Ouvrez le fichier
a4high-slurm-deployment.yaml, puis modifiez-le comme suit :terraform_backend_defaults: type: gcs configuration: bucket: BUCKET_NAME vars: deployment_name: a4-high project_id: PROJECT_ID region: REGION zone: ZONE a4h_cluster_size: 2 a4h_reservation_name: RESERVATION_URLRemplacez les éléments suivants :
BUCKET_NAME: nom du bucket Cloud Storage que vous avez créé dans la section précédente.PROJECT_ID: ID duGoogle Cloud projet dans lequel existe votre Cloud Storage et dans lequel vous souhaitez créer votre cluster Slurm.REGION: région où se trouve votre réservation.ZONE: zone où se trouve votre réservation.RESERVATION_URL: URL de la réservation que vous souhaitez utiliser pour créer votre cluster Slurm. En fonction du projet dans lequel la réservation existe, spécifiez l'une des valeurs suivantes :La réservation existe dans votre projet :
RESERVATION_NAMELa réservation existe dans un autre projet et votre projet peut l'utiliser :
projects/RESERVATION_PROJECT_ID/reservations/RESERVATION_NAME
Déployez le cluster :
./gcluster deploy -d examples/machine-learning/a4-highgpu-8g/a4high-slurm-deployment.yaml examples/machine-learning/a4-highgpu-8g/a4high-slurm-blueprint.yaml --auto-approveLa commande
./gcluster deployse déroule en deux phases :La première phase consiste à créer une image personnalisée avec tous les logiciels préinstallés. Cette opération peut prendre jusqu'à 35 minutes.
La deuxième phase déploie le cluster à l'aide de cette image personnalisée. Ce processus devrait se terminer plus rapidement que la première phase.
Si la première phase réussit, mais que la deuxième échoue, vous pouvez essayer de déployer à nouveau le cluster Slurm en ignorant la première phase :
./gcluster deploy -d examples/machine-learning/a4-highgpu-8g/a4high-slurm-deployment.yaml examples/machine-learning/a4-highgpu-8g/a4high-slurm-blueprint.yaml --auto-approve --skip "image" -w
Préparer votre charge de travail
Pour préparer votre charge de travail, procédez comme suit :
Créer des scripts de charge de travail
Pour créer les scripts que votre charge de travail d'affinage utilisera, procédez comme suit :
Pour configurer l'environnement virtuel Python, créez le fichier
install_environment.shavec le contenu suivant :#!/bin/bash # This script should be run ONCE on the login node to set up the # shared Python virtual environment. set -e echo "--- Creating Python virtual environment in /home ---" python3 -m venv ~/.venv echo "--- Activating virtual environment ---" source ~/.venv/bin/activate echo "--- Installing build dependencies ---" pip install --upgrade pip wheel packaging echo "--- Installing PyTorch for CUDA 12.8 ---" pip install torch --index-url https://download.pytorch.org/whl/cu128 echo "--- Installing application requirements ---" pip install -r requirements.txt echo "--- Environment setup complete. You can now submit jobs with sbatch. ---"Pour spécifier les configurations de votre tâche d'affinage, créez le fichier
accelerate_config.yamlavec le contenu suivant :# Default configuration for a 2-node, 8-GPU-per-node (16 total GPUs) FSDP training job. compute_environment: "LOCAL_MACHINE" distributed_type: "FSDP" downcast_bf16: "no" fsdp_config: fsdp_auto_wrap_policy: "TRANSFORMER_BASED_WRAP" fsdp_backward_prefetch: "BACKWARD_PRE" fsdp_cpu_ram_efficient_loading: true fsdp_forward_prefetch: false fsdp_offload_params: false fsdp_sharding_strategy: "FULL_SHARD" fsdp_state_dict_type: "FULL_STATE_DICT" fsdp_transformer_layer_cls_to_wrap: "Gemma3DecoderLayer" fsdp_use_orig_params: true machine_rank: 0 main_training_function: "main" mixed_precision: "bf16" num_machines: 2 num_processes: 16 rdzv_backend: "static" same_network: true tpu_env: [] use_cpu: falsePour spécifier les tâches à exécuter sur votre cluster Slurm, créez le fichier
submit.slurmavec le contenu suivant :#!/bin/bash #SBATCH --job-name=gemma3-finetune #SBATCH --nodes=2 #SBATCH --ntasks-per-node=8 # 8 tasks per node #SBATCH --gpus-per-task=1 # 1 GPU per task #SBATCH --partition=a4high #SBATCH --output=slurm-%j.out #SBATCH --error=slurm-%j.err set -e echo "--- Slurm Job Started ---" # --- STAGE 1: Copy Environment to Local SSD on all nodes --- srun --ntasks=$SLURM_NNODES --ntasks-per-node=1 bash -c ' echo "Setting up local environment on $(hostname)..." LOCAL_VENV="/mnt/localssd/venv_job_${SLURM_JOB_ID}" LOCAL_CACHE="/mnt/localssd/hf_cache_job_${SLURM_JOB_ID}" rsync -a --info=progress2 ~/./.venv/ ${LOCAL_VENV}/ mkdir -p ${LOCAL_CACHE} echo "Setup on $(hostname) complete." ' # --- STAGE 2: Run the Training Job using the Local Environment --- echo "--- Starting Training ---" LOCAL_VENV="/mnt/localssd/venv_job_${SLURM_JOB_ID}" LOCAL_CACHE="/mnt/localssd/hf_cache_job_${SLURM_JOB_ID}" LOCAL_OUTPUT_DIR="/mnt/localssd/outputs_${SLURM_JOB_ID}" mkdir -p ${LOCAL_OUTPUT_DIR} # This is the main training command. srun --ntasks=$((SLURM_NNODES * 8)) --gpus-per-task=1 bash -c " source ${LOCAL_VENV}/bin/activate export HF_HOME=${LOCAL_CACHE} export HF_DATASETS_CACHE=${LOCAL_CACHE} # Run the Python script directly. # Accelerate will divide the work python ~/train.py \ --model_id google/gemma-3-12b-pt \ --output_dir ${LOCAL_OUTPUT_DIR} \ --per_device_train_batch_size 1 \ --gradient_accumulation_steps 8 \ --num_train_epochs 3 \ --learning_rate 1e-5 \ --save_strategy steps \ --save_steps 100 " # --- STAGE 3: Copy Final Model from Local SSD to Home Directory --- echo "--- Copying final model from local SSD to /home ---" # This command runs only on the first node of the job allocation # and copies the final model back to the persistent shared directory. srun --nodes=1 --ntasks=1 --ntasks-per-node=1 bash -c " rsync -a --info=progress2 ${LOCAL_OUTPUT_DIR}/ ~/gemma-12b-text-to-sql-finetuned/ " echo "--- Slurm Job Finished ---"Pour spécifier les dépendances de votre job d'affinage, créez le fichier
requirements.txtavec le contenu suivant :# Hugging Face Libraries (Pinned to recent, stable versions for reproducibility) transformers==4.53.3 datasets==4.0.0 accelerate==1.9.0 evaluate==0.4.5 bitsandbytes==0.46.1 trl==0.19.1 peft==0.16.0 # Other dependencies tensorboard==2.20.0 protobuf==6.31.1 sentencepiece==0.2.0Pour spécifier les instructions de votre job, créez le fichier
train.pyavec le contenu suivant :import torch import argparse from datasets import load_dataset from transformers import AutoTokenizer, AutoModelForCausalLM, BitsAndBytesConfig, AutoConfig from peft import LoraConfig, prepare_model_for_kbit_training, get_peft_model from trl import SFTTrainer, SFTConfig from huggingface_hub import login def get_args(): parser = argparse.ArgumentParser() parser.add_argument("--model_id", type=str, default="google/gemma-3-12b-pt", help="Hugging Face model ID") parser.add_argument("--hf_token", type=str, default=None, help="Hugging Face token for private models") parser.add_argument("--dataset_name", type=str, default="philschmid/gretel-synthetic-text-to-sql", help="Hugging Face dataset name") parser.add_argument("--output_dir", type=str, default="gemma-12b-text-to-sql", help="Directory 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=16, 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_seq_length", type=int, default=512, 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=8, help="Batch size per device during training") parser.add_argument("--gradient_accumulation_steps", type=int, default=1, 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=100, help="Save checkpoint every X steps") 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 --- # The SFTTrainer will use the `formatting_func` to apply the chat template. 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 torch_dtype_str = "bfloat16" else: torch_dtype_obj = torch.float16 torch_dtype_str = "float16" tokenizer = AutoTokenizer.from_pretrained(args.model_id) tokenizer.pad_token = tokenizer.eos_token gemma_chat_template = ( "" "" ) tokenizer.chat_template = gemma_chat_template # --- 4. Define the Formatting Function --- # This function will be used by the SFTTrainer to format each sample # from the dataset into the correct chat template format. def formatting_func(example): # The create_conversation logic is now implicitly handled by this. # We need to construct the messages list here. 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": "user", "content": user_prompt.format(question=example["sql_prompt"][0], context=example["sql_context"][0])}, {"role": "assistant", "content": example["sql"][0]} ] return tokenizer.apply_chat_template(messages, tokenize=False) # --- 5. Load Quantized Model and Apply PEFT --- # Define the quantization configuration quantization_config = BitsAndBytesConfig( load_in_4bit=True, bnb_4bit_quant_type='nf4', bnb_4bit_compute_dtype=torch_dtype_obj, bnb_4bit_use_double_quant=True, ) config = AutoConfig.from_pretrained(args.model_id) config.use_cache = False # Load the base model with quantization print("Loading base model...") model = AutoModelForCausalLM.from_pretrained( args.model_id, config=config, quantization_config=quantization_config, attn_implementation="eager", torch_dtype=torch_dtype_obj, ) # Prepare the model for k-bit training model = prepare_model_for_kbit_training(model) # Configure LoRA. 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"], task_type="CAUSAL_LM", ) # Apply the PEFT config to the model print("Applying PEFT configuration...") model = get_peft_model(model, peft_config) model.print_trainable_parameters() # --- 6. Configure Training Arguments --- training_args = SFTConfig( output_dir=args.output_dir, max_seq_length=args.max_seq_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=True, gradient_checkpointing=True, gradient_checkpointing_kwargs={"use_reentrant": False}, optim="adamw_torch", fp16=True if torch_dtype_obj == torch.float16 else False, bf16=True if torch_dtype_obj == torch.bfloat16 else False, max_grad_norm=0.3, warmup_ratio=0.03, lr_scheduler_type="constant", push_to_hub=False, report_to="tensorboard", dataset_kwargs={ "add_special_tokens": False, "append_concat_token": True, } ) # --- 7. Create Trainer and Start Training --- trainer = SFTTrainer( model=model, args=training_args, train_dataset=dataset["train"], eval_dataset=dataset["test"], formatting_func=formatting_func, ) print("Starting training...") trainer.train() print("Training finished.") # --- 8. Save the final model --- print(f"Saving final model to {args.output_dir}") trainer.save_model() if __name__ == "__main__": main()
Importer des scripts dans le cluster Slurm
Pour importer les scripts que vous avez créés dans la section précédente vers le cluster Slurm, procédez comme suit :
Pour identifier votre nœud de connexion, listez toutes les VM A4 de votre projet :
gcloud compute instances list --filter="machineType:a4-highgpu-8g"Le nom du nœud de connexion est semblable à
a4-high-login-001.Importez vos scripts dans le répertoire d'accueil du nœud de connexion :
gcloud compute scp \ --project=PROJECT_ID \ --zone=ZONE \ --tunnel-through-iap \ ./train.py \ ./requirements.txt \ ./submit.slurm \ ./install_environment.sh \ ./accelerate_config.yaml \ "LOGIN_NODE_NAME":~/Remplacez
LOGIN_NODE_NAMEpar le nom du nœud de connexion.
Se connecter au cluster Slurm
Connectez-vous au cluster Slurm en vous connectant au nœud de connexion via SSH :
gcloud compute ssh LOGIN_NODE_NAME \
--project=PROJECT_ID \
--tunnel-through-iap \
--zone=ZONE
Installer des frameworks et des outils
Une fois connecté au nœud de connexion, installez les frameworks et les outils en procédant comme suit :
Créez une variable d'environnement pour votre jeton d'accès Hugging Face :
export HUGGING_FACE_TOKEN="HUGGING_FACE_TOKEN"Configurez un environnement virtuel Python avec toutes les dépendances requises :
chmod +x install_environment.sh ./install_environment.sh
Démarrer votre charge de travail d'affinage
Pour démarrer votre charge de travail d'affinage :
Envoyez le job au planificateur Slurm :
sbatch submit.slurmSur le nœud de connexion de votre cluster Slurm, vous pouvez surveiller la progression du job en vérifiant les fichiers de sortie créés dans votre répertoire
home:tail -f slurm-gemma3-finetune.errSi votre job démarre correctement, le fichier
.erraffiche une barre de progression qui se met à jour à mesure que votre job progresse.
Surveiller votre charge de travail
Vous pouvez surveiller l'utilisation des GPU dans votre cluster Slurm pour vérifier que votre job de réglage fin s'exécute efficacement. Pour ce faire, ouvrez le lien suivant dans votre navigateur :
https://console.cloud.google.com/monitoring/metrics-explorer?project=PROJECT_ID&pageState=%7B%22xyChart%22%3A%7B%22dataSets%22%3A%5B%7B%22timeSeriesFilter%22%3A%7B%22filter%22%3A%22metric.type%3D%5C%22agent.googleapis.com%2Fgpu%2Futilization%5C%22%20resource.type%3D%5C%22gce_instance%5C%22%22%2C%22perSeriesAligner%22%3A%22ALIGN_MEAN%22%7D%2C%22plotType%22%3A%22LINE%22%7D%5D%7D%7D
Lorsque vous surveillez votre charge de travail, vous pouvez voir les éléments suivants :
Utilisation des GPU : pour une tâche d'affinage saine, vous pouvez vous attendre à ce que l'utilisation de vos 16 GPU (huit GPU pour chaque VM du cluster) augmente et se stabilise à un niveau spécifique tout au long de votre entraînement.
Durée du job : l'exécution du job devrait prendre environ une heure.
Effectuer un nettoyage
Pour éviter que les ressources utilisées dans ce tutoriel soient facturées sur votre compte Google Cloud, supprimez le projet contenant les ressources, ou conservez le projet et supprimez chaque ressource individuellement.
Supprimer votre projet
Supprimer un projet Google Cloud :
gcloud projects delete PROJECT_ID
Supprimer votre cluster Slurm
Pour supprimer votre cluster Slurm, procédez comme suit :
Accédez au répertoire
cluster-toolkit.Détruisez le fichier Terraform et toutes les ressources créées :
./gcluster destroy a4-high --auto-approve