このチュートリアルでは、 Google Cloudのマルチノード、マルチ GPU Slurm クラスタで大規模言語モデル(LLM)をトレーニングする方法について説明します。このチュートリアルで使用するモデルは、Qwen2 15 億パラメータ モデルに基づいています。Slurm クラスタは、それぞれ 8 個の NVIDIA B200 GPU を搭載した 2 つの a4-highgpu-8g 仮想マシン(VM)を使用します。
このチュートリアルで説明する主なプロセスは次の 2 つです。
- Google Cloud Cluster Toolkit を使用して、本番環境グレードの高性能 Slurm クラスタをデプロイします。このデプロイの一環として、必要なソフトウェアがプリインストールされたカスタム VM イメージを作成します。共有 Filestore インスタンスを設定し、高速 RDMA ネットワーキングを構成します。
- クラスタをデプロイしたら、このチュートリアルに付属するスクリプトのセットを使用して、分散事前トレーニング ジョブを実行します。このジョブは、Hugging Face Accelerate ライブラリを活用します。
このチュートリアルは、Google Cloud に高性能の Slurm クラスタをデプロイして LLM をトレーニングすることに関心のある ML エンジニア、研究者、プラットフォーム管理者、オペレーター、データおよび AI スペシャリストを対象としています。
目標
- Hugging Face を使用して Qwen2 モデルにアクセスします。
- 環境を準備します。
- 本番環境グレードの A4 Slurm クラスタを作成してデプロイします。
- Accelerate ライブラリを使用して Qwen2 モデルをトレーニングします。
- ジョブをモニタリングします。
- クリーンアップする。
費用
このドキュメントでは、課金対象である次の Google Cloudコンポーネントを使用します。
料金計算ツールを使うと、予想使用量に基づいて費用の見積もりを生成できます。
始める前に
- Google Cloud アカウントにログインします。 Google Cloudを初めて使用する場合は、 アカウントを作成して、実際のシナリオでの Google プロダクトのパフォーマンスを評価してください。新規のお客様には、ワークロードの実行、テスト、デプロイができる無料クレジット $300 分を差し上げます。
-
Google Cloud CLI をインストールします。
-
外部 ID プロバイダ(IdP)を使用している場合は、まず連携 ID を使用して gcloud CLI にログインする必要があります。
-
gcloud CLI を初期化するには、次のコマンドを実行します。
gcloud init -
Google Cloud プロジェクトを作成または選択します。
プロジェクトの選択または作成に必要なロール
- プロジェクトを選択する: プロジェクトの選択に特定の IAM ロールは必要ありません。ロールが付与されているプロジェクトであれば、どのプロジェクトでも選択できます。
-
プロジェクトを作成する: プロジェクトを作成するには、
resourcemanager.projects.create権限を含むプロジェクト作成者ロール(roles/resourcemanager.projectCreator)が必要です。ロールを付与する方法を確認する。
-
Google Cloud プロジェクトを作成します。
gcloud projects create PROJECT_ID
PROJECT_IDは、作成する Google Cloud プロジェクトの名前に置き換えます。 -
作成した Google Cloud プロジェクトを選択します。
gcloud config set project PROJECT_ID
PROJECT_IDは、 Google Cloud プロジェクトの名前に置き換えます。
必要な API を有効にします。
API を有効にするために必要なロール
API を有効にするには、
serviceusage.services.enable権限を含む Service Usage 管理者 IAM ロール(roles/serviceusage.serviceUsageAdmin)が必要です。ロールを付与する方法を確認する。gcloud services enable gcloud services enable compute.googleapis.com file.googleapis.com logging.googleapis.com cloudresourcemanager.googleapis.com servicenetworking.googleapis.com
-
Google Cloud CLI をインストールします。
-
外部 ID プロバイダ(IdP)を使用している場合は、まず連携 ID を使用して gcloud CLI にログインする必要があります。
-
gcloud CLI を初期化するには、次のコマンドを実行します。
gcloud init -
Google Cloud プロジェクトを作成または選択します。
プロジェクトの選択または作成に必要なロール
- プロジェクトを選択する: プロジェクトの選択に特定の IAM ロールは必要ありません。ロールが付与されているプロジェクトであれば、どのプロジェクトでも選択できます。
-
プロジェクトを作成する: プロジェクトを作成するには、
resourcemanager.projects.create権限を含むプロジェクト作成者ロール(roles/resourcemanager.projectCreator)が必要です。ロールを付与する方法を確認する。
-
Google Cloud プロジェクトを作成します。
gcloud projects create PROJECT_ID
PROJECT_IDは、作成する Google Cloud プロジェクトの名前に置き換えます。 -
作成した Google Cloud プロジェクトを選択します。
gcloud config set project PROJECT_ID
PROJECT_IDは、 Google Cloud プロジェクトの名前に置き換えます。
必要な API を有効にします。
API を有効にするために必要なロール
API を有効にするには、
serviceusage.services.enable権限を含む Service Usage 管理者 IAM ロール(roles/serviceusage.serviceUsageAdmin)が必要です。ロールを付与する方法を確認する。gcloud services enable gcloud services enable compute.googleapis.com file.googleapis.com logging.googleapis.com cloudresourcemanager.googleapis.com servicenetworking.googleapis.com
-
ユーザー アカウントにロールを付与します。次の IAM ロールごとに次のコマンドを 1 回実行します。
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
次のように置き換えます。
PROJECT_ID: プロジェクト ID。USER_IDENTIFIER: ユーザー アカウントの識別子。例:myemail@example.comROLE: ユーザー アカウントに付与する IAM ロール。
- Google Cloud プロジェクトのデフォルトのサービス アカウントを有効にします。
gcloud iam service-accounts enable PROJECT_NUMBER-compute@developer.gserviceaccount.com \ --project=PROJECT_ID
PROJECT_NUMBER は、使用するプロジェクト番号に置き換えます。プロジェクト番号を確認するには、 既存のプロジェクトを取得するをご覧ください。
- デフォルトのサービス アカウントに編集者ロール(
roles/editor)を付与します。gcloud projects add-iam-policy-binding PROJECT_ID \ --member="serviceAccount:PROJECT_NUMBER-compute@developer.gserviceaccount.com" \ --role=roles/editor
- ユーザー アカウントのローカル認証情報を作成します。
gcloud auth application-default login
- プロジェクトで OS Login を有効にします。
gcloud compute project-info add-metadata --metadata=enable-oslogin=TRUE
- Hugging Face アカウントにログインするか、アカウントを作成します。
Hugging Face を使用して Qwen2 にアクセスする
Hugging Face を使用して Qwen2 にアクセスする手順は次のとおりです。
環境を準備する
環境を準備する手順は次のとおりです。
Cluster Toolkit GitHub リポジトリのクローンを作成します。
git clone https://github.com/GoogleCloudPlatform/cluster-toolkit.gitCloud Storage バケットを作成します。
gcloud storage buckets create gs://BUCKET_NAME \ --project=PROJECT_ID次のように置き換えます。
BUCKET_NAME: バケットの命名要件に沿った Cloud Storage バケットの名前。PROJECT_ID: Cloud Storage バケットを作成するGoogle Cloud プロジェクトの ID。
A4 Slurm クラスタを作成する
A4 Slurm クラスタを作成する手順は次のとおりです。
cluster-toolkitディレクトリに移動します。cd cluster-toolkitCluster Toolkit を初めて使用する場合は、
gclusterバイナリをビルドします。makeexamples/machine-learning/a4-highgpu-8gディレクトリに移動します。cd examples/machine-learning/a4-highgpu-8g/a4high-slurm-deployment.yamlファイルを開き、次のように編集します。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_URL次のように置き換えます。
BUCKET_NAME: 前のセクションで作成した Cloud Storage バケットの名前。PROJECT_ID: Cloud Storage が存在し、Slurm クラスタを作成するGoogle Cloud プロジェクトの ID。REGION: 予約が存在するリージョン。ZONE: 予約が存在するゾーン。RESERVATION_URL: Slurm クラスタの作成に使用する予約の URL。予約が存在するプロジェクトに基づいて、次のいずれかの値を指定します。予約がプロジェクトに存在する場合:
RESERVATION_NAME予約が別のプロジェクトにあり、プロジェクトで予約を使用できる場合:
projects/RESERVATION_PROJECT_ID/reservations/RESERVATION_NAME
クラスタをデプロイします。
./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./gcluster deployコマンドは 2 フェーズのプロセスです。最初のフェーズでは、すべてのソフトウェアがプリインストールされたカスタム イメージがビルドされます。この処理には最大 35 分かかることがあります。
第 2 フェーズでは、そのカスタム イメージを使用してクラスタをデプロイします。このプロセスは、最初のフェーズよりも早く完了します。
第 1 フェーズは成功したが第 2 フェーズが失敗した場合は、第 1 フェーズをスキップして Slurm クラスタのデプロイを再試行できます。
./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
ワークロードを準備する
ワークロードを準備する手順は次のとおりです。
ワークロード スクリプトを作成する
トレーニング ワークロードで使用するスクリプトを作成する手順は次のとおりです。
Python 仮想環境を設定するには、次の内容で
install_environment.shファイルを作成します。#!/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. ---"ファインチューニング ジョブの構成を指定するには、次の内容で
accelerate_config.yamlファイルを作成します。# 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" 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: falseSlurm クラスタで実行するジョブのタスクを指定するには、次の内容で
submit.slurmファイルを作成します。#!/bin/bash #SBATCH --job-name=qwen2-pretrain-smollm-fineweb #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=logs/slurm-%j.out #SBATCH --error=logs/slurm-%j.err set -e echo "--- Slurm Job Started ---" # --- STAGE 1: Setup environment and pre-process data on each node's local SSD --- # This command runs once per node. srun --ntasks=$SLURM_NNODES --ntasks-per-node=1 bash -c ' set -e 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}" PROCESSED_DATA_DIR="/mnt/localssd/processed_data_${SLURM_JOB_ID}" rsync -a --info=progress2 ~/./.venv/ ${LOCAL_VENV}/ mkdir -p ${LOCAL_CACHE} ${PROCESSED_DATA_DIR} echo "Pre-processing data on $(hostname)..." source ${LOCAL_VENV}/bin/activate export HF_HOME=${LOCAL_CACHE} export HF_DATASETS_CACHE=${LOCAL_CACHE} # This runs the new preprocessing script. It ensures only ONE process per node # downloads and processes the data, avoiding rate limiting and redundant work. python preprocess_data.py \ --dataset_name "HuggingFaceFW/fineweb-edu" \ --dataset_config "CC-MAIN-2024-10" \ --tokenizer_id "Qwen/Qwen2-1.5B" \ --max_seq_length 1024 \ --output_path ${PROCESSED_DATA_DIR} 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}" PROCESSED_DATA_DIR="/mnt/localssd/processed_data_${SLURM_JOB_ID}" LOCAL_OUTPUT_DIR="/mnt/localssd/outputs_${SLURM_JOB_ID}" mkdir -p ${LOCAL_OUTPUT_DIR} # This is the main training command. It launches one Python process per GPU. srun --ntasks=$((SLURM_NNODES * 8)) --gpus-per-task=1 bash -c " source ${LOCAL_VENV}/bin/activate # The training script now loads the pre-processed data from the local SSD. python train.py \ --model_config_id "Qwen/Qwen2-1.5B" \ --preprocessed_data_path ${PROCESSED_DATA_DIR} \ --output_dir ${LOCAL_OUTPUT_DIR} \ --per_device_train_batch_size 4 \ --gradient_accumulation_steps 4 \ --max_steps 10000 \ --learning_rate 5e-5 \ --save_strategy steps \ --save_steps 500 " # --- 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}/ ~/qwen2-from-scratch-on-smollm-fineweb/ " echo "--- Slurm Job Finished ---"ファインチューニング ジョブの依存関係を指定するには、次の内容で
requirements.txtファイルを作成します。# 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.0データセットをダウンロードしてトークン化し、トレーニングの準備ができた形式に前処理するには、次の内容の
preprocess_data.pyファイルを作成します。import argparse from datasets import load_dataset from transformers import AutoTokenizer import os from itertools import chain def get_args(): parser = argparse.ArgumentParser(description="Download and preprocess a dataset.") parser.add_argument("--dataset_name", type=str, required=True) parser.add_argument("--dataset_config", type=str, required=True) parser.add_argument("--tokenizer_id", type=str, required=True) parser.add_argument("--max_seq_length", type=int, required=True) parser.add_argument("--output_path", type=str, required=True, help="Path to save the processed dataset.") return parser.parse_args() def main(): args = get_args() if os.path.exists(args.output_path) and os.listdir(args.output_path): print(f"Processed dataset already exists at {args.output_path}. Skipping.") return # 1. Load tokenizer tokenizer = AutoTokenizer.from_pretrained(args.tokenizer_id) # 2. Load raw dataset print(f"Loading raw dataset {args.dataset_name}...") raw_dataset = load_dataset(args.dataset_name, name=args.dataset_config, split="train") # 3. Tokenize def tokenize_function(examples): return tokenizer(examples["text"]) num_proc = os.cpu_count() print(f"Tokenizing dataset using {num_proc} processes...") print("Tokenizing dataset...") tokenized_dataset = raw_dataset.map( tokenize_function, batched=True, remove_columns=raw_dataset.column_names, desc="Running tokenizer on dataset", num_proc=num_proc, ) # 4. Group texts def group_texts(examples): concatenated_examples = {k: list(chain.from_iterable(examples[k])) for k in examples.keys()} total_length = len(concatenated_examples[list(examples.keys())[0]]) if total_length >= args.max_seq_length: total_length = (total_length // args.max_seq_length) * args.max_seq_length result = { k: [t[i : i + args.max_seq_length] for i in range(0, total_length, args.max_seq_length)] for k, t in concatenated_examples.items() } result["labels"] = result["input_ids"].copy() return result print("Grouping texts...") lm_dataset = tokenized_dataset.map( group_texts, batched=True, desc=f"Grouping texts in chunks of {args.max_seq_length}", num_proc=num_proc, ) # 5. Save to disk print(f"Saving processed dataset to {args.output_path}...") lm_dataset.save_to_disk(args.output_path) print("Preprocessing complete.") if __name__ == "__main__": main()ジョブの手順を指定するには、次の内容を含む
train.pyファイルを作成します。import torch import argparse from datasets import load_dataset, load_from_disk import os from transformers import ( AutoConfig, AutoTokenizer, AutoModelForCausalLM, Trainer, TrainingArguments, DataCollatorForLanguageModeling, ) from huggingface_hub import login def get_args(): parser = argparse.ArgumentParser() parser.add_argument("--model_config_id", type=str, default="Qwen/Qwen2-1.5B", help="Hugging Face model config to use for architecture.") # Data arguments - used if preprocessed data is not available parser.add_argument("--dataset_name", type=str, default="HuggingFaceFW/fineweb-edu", help="Hugging Face dataset for pre-training.") parser.add_argument("--dataset_config", type=str, default="CC-MAIN-2024-10", help="Config for the smollm-corpus dataset, e.g., 'fineweb-edu-dedup'.") parser.add_argument("--preprocessed_data_path", type=str, default=None, help="Path to a preprocessed dataset on disk. If provided, skips download and processing.") # General arguments parser.add_argument("--hf_token", type=str, default=None, help="Hugging Face token for private models/tokenizers") parser.add_argument("--output_dir", type=str, default="qwen2-from-scratch-on-olmo", help="Directory to save model checkpoints") # TrainingArguments parser.add_argument("--max_seq_length", type=int, default=1024, help="Maximum sequence length") parser.add_argument("--num_train_epochs", type=int, default=1, help="Number of training epochs") parser.add_argument("--max_steps", type=int, default=-1, help="If set to a positive number, it overrides num_train_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=5e-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=500, 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. Load Tokenizer --- # We load the tokenizer from the specified config ID to ensure compatibility # with the model architecture (e.g., special tokens). tokenizer = AutoTokenizer.from_pretrained(args.model_config_id) # --- 4. Initialize Model from Scratch --- print(f"Initializing a new model from {args.model_config_id} configuration...") config = AutoConfig.from_pretrained(args.model_config_id) model = AutoModelForCausalLM.from_config(config) print(f"Model has {model.num_parameters():,} parameters.") # --- 3. Load or Create and prepare the training dataset --- if args.preprocessed_data_path and os.path.exists(args.preprocessed_data_path): print(f"Loading preprocessed dataset from {args.preprocessed_data_path}...") lm_dataset = load_from_disk(args.preprocessed_data_path) else: print("No preprocessed dataset found, starting from raw data...") raw_dataset = load_dataset(args.dataset_name, name=args.dataset_config, split="train") # Tokenization function def tokenize_function(examples): return tokenizer(examples["text"]) tokenized_dataset = raw_dataset.map( tokenize_function, batched=True, remove_columns=raw_dataset.column_names, desc="Running tokenizer on dataset", ) # Main data processing function that will concatenate all texts from our dataset # and generate chunks of max_seq_length. def group_texts(examples): # Concatenate all texts. concatenated_examples = {k: sum(examples[k], []) for k in examples.keys()} total_length = len(concatenated_examples[list(examples.keys())[0]]) # We drop the small remainder. if total_length >= args.max_seq_length: total_length = (total_length // args.max_seq_length) * args.max_seq_length # Split by chunks of max_len. result = { k: [t[i : i + args.max_seq_length] for i in range(0, total_length, args.max_seq_length)] for k, t in concatenated_examples.items() } result["labels"] = result["input_ids"].copy() return result lm_dataset = tokenized_dataset.map( group_texts, batched=True, desc=f"Grouping texts in chunks of {args.max_seq_length}", ) # --- 5. Configure Training Arguments --- # Check for bfloat16 support use_bf16 = torch.cuda.is_available() and torch.cuda.is_bf16_supported() training_args = TrainingArguments( output_dir=args.output_dir, num_train_epochs=args.num_train_epochs, max_steps=args.max_steps, 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, save_total_limit=2, # Optional: Limit the number of checkpoints bf16=use_bf16, fp16=not use_bf16, optim="adamw_torch", lr_scheduler_type="cosine", warmup_ratio=0.03, report_to="tensorboard", gradient_checkpointing=True, # Required for gradient checkpointing with some parallelization strategies gradient_checkpointing_kwargs={"use_reentrant": False}, ) # --- 6. Create Trainer and Start Training --- # Data collator will take care of creating batches for causal language modeling data_collator = DataCollatorForLanguageModeling(tokenizer=tokenizer, mlm=False) trainer = Trainer( model=model, args=training_args, train_dataset=lm_dataset, # eval_dataset=... # Optional: if you have a validation set tokenizer=tokenizer, data_collator=data_collator, ) print("Starting training from scratch...") trainer.train() print("Training finished.") # --- 7. Save the final model --- print(f"Saving final model to {args.output_dir}") trainer.save_model() if __name__ == "__main__": main()
スクリプトを Slurm クラスタにアップロードする
前のセクションで作成したスクリプトを Slurm クラスタにアップロードする手順は次のとおりです。
ログインノードを特定するには、プロジェクト内のすべての A4 VM を一覧表示します。
gcloud compute instances list --filter="machineType:a4-highgpu-8g"ログインノードの名前は
a4-high-login-001のようになります。スクリプトをログインノードのホーム ディレクトリにアップロードします。
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":~/LOGIN_NODE_NAMEは、ログインノードの名前に置き換えます。
Slurm クラスタに接続する
SSH を使用してログインノードに接続し、Slurm クラスタに接続します。
gcloud compute ssh LOGIN_NODE_NAME \
--project=PROJECT_ID \
--tunnel-through-iap \
--zone=ZONE
フレームワークとツールをインストールする
ログインノードに接続したら、次の手順でフレームワークとツールをインストールします。
Hugging Face アクセス トークンの環境変数を作成します。
export HUGGING_FACE_TOKEN="HUGGING_FACE_TOKEN"必要な依存関係をすべて含む Python 仮想環境を設定します。
chmod +x install_environment.sh ./install_environment.sh
ワークロードの事前トレーニングを開始する
ワークロードのトレーニングを開始するには、次の操作を行います。
Slurm スケジューラにジョブを送信します。
sbatch submit.slurmSlurm クラスタのログインノードで、
homeディレクトリに作成された出力ファイルを確認することで、ジョブの進行状況をモニタリングできます。tail -f logs/slurm-qwen2-pretrain-smollm-fineweb.errジョブが正常に開始されると、
.errファイルにプログレスバーが表示され、ジョブの進行状況に応じて更新されます。
ワークロードをモニタリングする
Slurm クラスタでの GPU の使用状況をモニタリングして、ファインチューニング ジョブが効率的に実行されていることを確認できます。ブラウザで次のリンクを開きます。
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
ワークロードをモニタリングすると、次の情報を確認できます。
GPU 使用率: 正常なファインチューニング ジョブの場合、トレーニング全体を通して、16 個の GPU(クラスタ内の各 VM に 8 個の GPU)の使用率が上昇し、特定のレベルで安定することが予想されます。
ジョブの所要時間: ジョブが完了するまでに約 1 時間かかります。
モデルをダウンロードする
ジョブが正常に実行されると、トレーニング済みモデルがログインノードの ~/qwen2-from-scratch-on-smollm-fineweb/ ディレクトリに保存されます。この永続共有ディレクトリはクラスタ内のすべてのノードにマウントされるため、ジョブの完了後やコンピューティング ノードの割り当て解除後もモデル チェックポイントは使用可能です。
次の例に示すように、gcloud compute scp コマンドを使用して、保存したモデルをログインノードからローカルマシンにダウンロードできます。
# From your local machine
LOGIN_NODE_NAME="your-login-node-name" # e.g., a4high-login-001
PROJECT_ID="your-gcp-project-id"
ZONE="your-cluster-zone" # e.g., us-west4-a
gcloud compute scp --project="$PROJECT_ID" --zone="$ZONE" --tunnel-through-iap \
"${LOGIN_NODE_NAME}":~/qwen2-from-scratch-on-smollm-fineweb/ ./qwen2-trained-model/ --recurse
モデルをダウンロードしたら、次のことができます。
- 推論用のモデルを読み込む: Hugging Face Transformers フレームワークを使用して
qwen2-trained-model/ディレクトリを読み込み、新しくトレーニングした Qwen2 モデルで推論を実行します。 - 追加のファインチューニング: 保存したチェックポイントを、より具体的なデータセットに対する追加のファインチューニングの出発点として使用します。
- モデルを Hugging Face Hub に push する: トレーニング済みモデルを Hugging Face Hub に push して共有します。
クリーンアップ
このチュートリアルで使用したリソースについて、Google Cloud アカウントに課金されないようにするには、リソースを含むプロジェクトを削除するか、プロジェクトを維持して個々のリソースを削除します。
プロジェクトの削除
Google Cloud プロジェクトを削除します。
gcloud projects delete PROJECT_ID
Slurm クラスタを削除する
Slurm クラスタを削除する手順は次のとおりです。
cluster-toolkitディレクトリに移動します。Terraform ファイルと作成されたすべてのリソースを破棄します。
./gcluster destroy a4-high --auto-approve