このチュートリアルでは、Ray Core、Ray Train、PyTorch Fully Sharded Data Parallel(FSDP2)、TorchTPU を使用して、Google Kubernetes Engine(GKE)のマルチホスト Cloud TPU スライスで分散 PyTorch ワークロードを実行し、Gemma 4 モデルをファインチューニングする方法について説明します。
このチュートリアルは、Kubernetes のマルチホスト TPU スライスで分散 PyTorch トレーニング ワークロードをオーケストレートする機械学習(ML)エンジニア、プラットフォーム管理者、オペレーター、データおよび AI スペシャリストを対象としています。 Google Cloud のコンテンツで使用されている一般的なロールとタスクの例の詳細については、一般的な GKE ユーザーロールとタスクをご覧ください。
このチュートリアルを開始する前に、次の内容をよく理解しておいてください。
背景
このセクションでは、このチュートリアルで使用されている重要なテクノロジーについて説明します。
TPU
Tensor Processing Unit(TPU)は、機械学習ワークロード用に Google がカスタム設計したアクセラレータです。このチュートリアルは、TPU Trillium(v6e)と Ironwood(TPU7x)の両方をサポートしています。マルチホスト TPU スライスでは、複数の TPU ホストが高速チップ間相互接続(ICI)ネットワークを介して直接通信し、単一のチップまたは TPU ホストのメモリ容量を超えるモデルのトレーニングに必要な高帯域幅メモリ(HBM)と低レイテンシの集団通信を提供します。
TorchTPU
TorchTPU は、Google と Meta の共同開発による Cloud TPU 向けのオープンソース PyTorch バックエンドです。TorchTPU を使用すると、最小限のコード変更で既存の PyTorch モデルを TPU ハードウェアで実行し、Accelerated Linear Algebra(XLA)コンパイラを介してトレーニングと推論のワークロードを単一チップからマルチスライス TPU Pod にスケールできます。TorchTPU は、TPU ICI ネットワーク上でデバイスごとに 1 つのプロセスを実行するモデルを使用し、eager execution、torch.compile、torch.distributed、DeviceMesh、DTensor、Fully Sharded Data Parallel(FSDP2)などの標準の PyTorch API をサポートしています。
TPU での KubeRay と Ray
KubeRay は、Ray アプリケーションをデプロイして管理するための Kubernetes オペレーターです。Ray on GKE アドオンを有効にすると、マネージド KubeRay オペレーターと KubeRay TPU Webhook がデプロイされます。KubeRay が google.com/tpu リソースをリクエストするワーカー Pod を作成すると、Webhook は、マルチホストの調整に必要な Pod ホスト名、ヘッドレス Service、TPU 環境変数を自動的に構成します。
Ray は、起動時に各ノードの TPU トポロジを検出し、TPU スライス間で分散ワークロードをスケジュールします。分散 TorchTPU ワークロードは、次の 2 つのレベルでオーケストレートできます。
- Ray Core は、テンソル並列計算や強化学習(RL)パイプラインなどのカスタム分散実行ループに、トポロジを考慮したプレースメント グループとアクター スケジューリングを提供します。
- Ray Train は、PyTorch Distributed Data Parallel(DDP)、PyTorch FSDP2、JAX 単一プログラム、複数データ(SPMD)など、マネージド分散データ並列トレーニングとモデル並列トレーニングを提供します。これには、組み込みのチェックポイント機能と自動障害復旧機能が含まれます。
目標
このチュートリアルでは、次の方法を説明します。
- TPU Trillium(v6e)または Ironwood(TPU7x)で Autopilot または Standard の GKE クラスタを作成します。
- TorchTPU ベースイメージの上に Ray と Hugging Face のトレーニング ライブラリをレイヤ化したコンテナ イメージをビルドします。
- RayCluster カスタム リソースをデプロイして、ヘッドノードと TPU ワーカーノードをプロビジョニングします。
- Ray Core を使用して、TPU スライス全体で PyTorch
DeviceMeshとDTensorを使用して分散テンソル並列計算を実行します。 - Ray Train を使用して、マルチホスト TPU スライスで PyTorch FSDP2 を使用して Gemma 4 モデルをファインチューニングする。
費用
のドキュメントでは、課金対象である次の Google Cloudコンポーネントを使用します。
料金計算ツールを使うと、予想使用量に基づいて費用の見積もりを算出できます。
このチュートリアルに記載されているタスクの完了後、作成したリソースを削除すると、それ以上の請求は発生しません。詳細については、クリーンアップをご覧ください。
始める前に
作業を始める前に、次のタスクが完了していることを確認してください。
- Artifact Registry API、Cloud Storage API、Google Kubernetes Engine API を有効にします。 API を有効にする
- このタスクに Google Cloud CLI を使用する場合は、gcloud CLI をインストールして初期化します。gcloud CLI をインストール済みの場合は、
gcloud components updateコマンドを実行して最新のバージョンを取得します。以前のバージョンの gcloud CLI では、このドキュメントのコマンドを実行できない場合があります。
- 選択したゾーンで、プロジェクトに TPU Trillium(v6e)または Ironwood(TPU7x)の容量に十分な割り当てがあることを確認します。詳細については、Cloud TPU の割り当てをご覧ください。
- アカウントに次の IAM ロールがあることを確認します。
- Kubernetes Engine 管理者(
roles/container.admin) - Artifact Registry 管理者(
roles/artifactregistry.admin) - Storage 管理者(
roles/storage.admin)
- Kubernetes Engine 管理者(
環境を準備する
このチュートリアルでは、Cloud Shell を使用して Google Cloudでホストされているリソースを管理します。Cloud Shell には、このチュートリアルに必要なソフトウェア(kubectl、docker、python3、gcloud CLI など)がプリインストールされています。
Cloud Shell を使用して環境を設定するには、次の操作を行います。
Google Cloud コンソールで
(Cloud Shell をアクティブにする)をクリックして、Google Cloud コンソールで Cloud Shell セッションを起動します。このアクションにより、 Google Cloud コンソールの下部ペインでセッションが起動します。
kubernetes-engine-samplesリポジトリのクローンを作成し、このチュートリアルのサンプル ディレクトリに移動します。git clone https://github.com/GoogleCloudPlatform/kubernetes-engine-samples.git cd kubernetes-engine-samples/ai-ml/gke-ray/raytrain/torchtpuRay CLI がインストールされたローカル Python 仮想環境を作成して有効にします。
python3 -m venv ~/ray-env source ~/ray-env/bin/activate pip install "ray[default]==2.58.0"Gemma 4 モデルの重みをダウンロードするには、Hugging Face で Google のライセンス契約に同意します。
- Hugging Face の Gemma 4 モデルページに移動します。
- ログインし、[同意してリポジトリにアクセス] をクリックしてライセンス条項に同意します。
- Hugging Face アカウントの設定で、
Read権限を持つユーザー アクセス トークンを生成します。
Google Cloud プロジェクト、クラスタ、Cloud Storage バケット、Hugging Face トークンの環境変数を設定します。
export PROJECT_ID=PROJECT_ID export CLUSTER_NAME=CLUSTER_NAME export REGION=REGION export ZONE=ZONE export ARTIFACT_REGISTRY=ARTIFACT_REGISTRY export BUCKET_NAME=BUCKET_NAME export HF_TOKEN=HF_TOKEN export IMAGE_URI=${REGION}-docker.pkg.dev/${PROJECT_ID}/${ARTIFACT_REGISTRY}/ray-torchtpu:latest gcloud config set project ${PROJECT_ID}次のように置き換えます。
PROJECT_ID: 実際の Google Cloud プロジェクト ID。CLUSTER_NAME: GKE クラスタの名前(ray-torchtpu-clusterなど)。REGION: Artifact Registry リポジトリ、Cloud Storage バケット、TPU 容量が配置されているリージョン。ZONE: TPU の容量が使用可能なゾーン。TPU バージョンごとに使用可能なリージョンとゾーンについては、TPU のリージョンとゾーンをご覧ください。ARTIFACT_REGISTRY: Artifact Registry Docker リポジトリの名前(例:ray-torchtpu)。BUCKET_NAME: Ray Train チェックポイントを保存する Cloud Storage バケットの名前(PROJECT_ID-ray-torchtpuなど)。HF_TOKEN: Hugging Face アクセス トークン。
GKE クラスタを作成する
マネージド Ray Operator アドオンを有効にして、GKE Autopilot クラスタまたは Standard クラスタで分散 TorchTPU トレーニング ワークロードを実行できます。
GKE クラスタを作成し、
kubectlコマンドライン ツールの認証情報を構成します。Autopilot
Ray Operator アドオンを有効にして、リージョン GKE Autopilot クラスタを作成します。
gcloud container clusters create-auto ${CLUSTER_NAME} \ --project=${PROJECT_ID} \ --region=${REGION} \ --cluster-version=1.36 \ --enable-ray-operator後のステップで RayCluster カスタム リソースをデプロイすると、GKE Autopilot は、Pod リソース リクエストとノードセレクタに一致する CPU ヘッドノードとマルチホスト TPU スライスを自動的にプロビジョニングします。
クラスタと通信するように
kubectlコマンドライン ツールを構成します。gcloud container clusters get-credentials ${CLUSTER_NAME} \ --project=${PROJECT_ID} \ --region=${REGION}
標準
Ray ヘッド Pod、イメージ ストリーミング、GKE Dataplane V2、Workload Identity Federation for GKE を有効にして、CPU ノードプールを含むゾーン GKE Standard クラスタを作成します。
gcloud container clusters create ${CLUSTER_NAME} \ --project=${PROJECT_ID} \ --zone=${ZONE} \ --cluster-version=1.36 \ --machine-type=n2-standard-8 \ --num-nodes=1 \ --addons=RayOperator \ --enable-image-streaming \ --enable-dataplane-v2 \ --workload-pool=${PROJECT_ID}.svc.id.googクラスタと通信するように
kubectlコマンドライン ツールを構成します。gcloud container clusters get-credentials ${CLUSTER_NAME} \ --project=${PROJECT_ID} \ --zone=${ZONE}TPU Trillium(v6e)の 4 つのホストにまたがる
4x4トポロジ、または Ironwood(TPU7x)の 2 つのホストにまたがる2x2x2トポロジを使用して、16 デバイスのマルチホスト TPU スライス ノードプールを作成します。Trillium(v6e)
gcloud container node-pools create tpu-multi-host-pool \ --project=${PROJECT_ID} \ --cluster=${CLUSTER_NAME} \ --zone=${ZONE} \ --machine-type=ct6e-standard-4t \ --tpu-topology=4x4 \ --num-nodes=4Ironwood(TPU7x)
gcloud container node-pools create tpu-multi-host-pool \ --project=${PROJECT_ID} \ --cluster=${CLUSTER_NAME} \ --zone=${ZONE} \ --machine-type=tpu7x-standard-4t \ --tpu-topology=2x2x2 \ --num-nodes=2
マルチホスト TPU ノード間で共有される Ray Train チェックポイントを保存する Cloud Storage バケットを作成し、
defaultKubernetes ServiceAccount リソースにバケットへのアクセス権を付与します。gcloud storage buckets create gs://${BUCKET_NAME} --location=${REGION} PROJECT_NUMBER=$(gcloud projects describe ${PROJECT_ID} --format="value(projectNumber)") gcloud storage buckets add-iam-policy-binding gs://${BUCKET_NAME} \ --member="principal://iam.googleapis.com/projects/${PROJECT_NUMBER}/locations/global/workloadIdentityPools/${PROJECT_ID}.svc.id.goog/subject/ns/default/sa/default" \ --role="roles/storage.objectAdmin"Ray Pod がゲート付きの Gemma 4 の重みをダウンロードできるように、Hugging Face アクセス トークンを使用して Kubernetes Secret を作成します。
kubectl create secret generic hf-secret \ --from-literal=hf_api_token=${HF_TOKEN}
コンテナ イメージをビルドする
TorchTPU と libtpu ライブラリを含むビルド済みの TorchTPU ベースイメージに、Ray と Hugging Face トレーニング ライブラリをインストールするコンテナ イメージをビルドします。
Artifact Registry Docker リポジトリを作成し、Docker 認証を構成します。
gcloud artifacts repositories create ${ARTIFACT_REGISTRY} \ --repository-format=docker \ --location=${REGION} gcloud auth configure-docker ${REGION}-docker.pkg.dev,us-docker.pkg.devサンプル ディレクトリの
Dockerfileを確認します。Ray
TorchTrainerクラスを使用したシングルスライス トレーニングと、Ironwood(TPU7x)でのチップレットごとのスケジューリングには、Ray バージョン 2.58.0 以降が必要です。Gemma 4 のファインチューニングには、transformersライブラリ バージョン 5.5.0 以降が必要です。イメージをビルドして Artifact Registry に push します。
docker build -t ${IMAGE_URI} . docker push ${IMAGE_URI}
RayCluster カスタム リソースをデプロイする
CPU ヘッド Pod、Ray 共有メモリ オブジェクト ストア用のインメモリ /dev/shm ボリューム、マルチホスト TPU ワーカー グループをプロビジョニングする RayCluster カスタム リソースをデプロイします。
サンプル リポジトリで、TPU バージョンの RayCluster マニフェストを確認します。
Trillium(v6e)
Ironwood(TPU7x)
Ironwood(TPU7x)では、各物理チップに 2 つのチップレットが含まれており、4 チップホストあたり 8 つの論理 TPU コアが提供されます。このマニフェストでは、
resourcesセクションでTPUフィールドの値を"8"に設定し、RAY_TPU_RESOURCE_PER_CHIP環境変数を"2"に設定して、ホストごとに 8 つの論理TPUリソースをアドバタイズしてスケジュールします。マニフェストを適用して RayCluster カスタム リソースを作成します。
Trillium(v6e)
envsubst < ray-cluster.tpu-v6e-multihost.yaml | kubectl apply -f -Ironwood(TPU7x)
envsubst < ray-cluster.tpu-v7x-multihost.yaml | kubectl apply -f -RayCluster ヘッド Pod とすべての TPU ワーカー Pod が
Running状態になるまで待ちます。kubectl get pods -l ray.io/cluster=ray-torchtpu-cluster出力は次のようになります。
Trillium(v6e)
NAME READY STATUS RESTARTS AGE ray-torchtpu-cluster-head-pdpk6 1/1 Running 0 2m30s ray-torchtpu-cluster-v6e-16-worker-brwpc 1/1 Running 0 2m30s ray-torchtpu-cluster-v6e-16-worker-dx4w7 1/1 Running 0 2m30s ray-torchtpu-cluster-v6e-16-worker-h4d4w 1/1 Running 0 2m30s ray-torchtpu-cluster-v6e-16-worker-t2g24 1/1 Running 0 2m30sIronwood(TPU7x)
NAME READY STATUS RESTARTS AGE ray-torchtpu-cluster-head-k9x2m 1/1 Running 0 2m30s ray-torchtpu-cluster-tpu7x-16-worker-7cwpq 1/1 Running 0 2m30s ray-torchtpu-cluster-tpu7x-16-worker-m98r9 1/1 Running 0 2m30sいずれかのワーカー Pod の環境変数を調べて、KubeRay TPU Webhook がマルチホスト TPU 調整変数と TorchTPU トポロジ変数を挿入したことを確認します。
kubectl get pods -l ray.io/node-type=worker \ -o=jsonpath='{range .items[0].spec.containers[0].env[*]}{.name}={.value}{"\n"}{end}' \ | grep -E "TPU_WORKER_ID|TPU_WORKER_HOSTNAMES|TPU_NAME|TORCH_TPU_TOPOLOGY|TORCH_TPU_SLICEBUILDER_ADDRESSES"出力は次のようになります。
Trillium(v6e)
TPU_WORKER_HOSTNAMES=v6e-16-0-0.ray-torchtpu-cluster-headless,v6e-16-0-1.ray-torchtpu-cluster-headless,v6e-16-0-2.ray-torchtpu-cluster-headless,v6e-16-0-3.ray-torchtpu-cluster-headless TORCH_TPU_TOPOLOGY=4,4,1 TORCH_TPU_SLICEBUILDER_ADDRESSES=v6e-16-0-0.ray-torchtpu-cluster-headless:8471,v6e-16-0-0.ray-torchtpu-cluster-headless:8472,v6e-16-0-0.ray-torchtpu-cluster-headless:8473,v6e-16-0-0.ray-torchtpu-cluster-headless:8474,v6e-16-0-1.ray-torchtpu-cluster-headless:8471,v6e-16-0-1.ray-torchtpu-cluster-headless:8472,v6e-16-0-1.ray-torchtpu-cluster-headless:8473,v6e-16-0-1.ray-torchtpu-cluster-headless:8474,v6e-16-0-2.ray-torchtpu-cluster-headless:8471,v6e-16-0-2.ray-torchtpu-cluster-headless:8472,v6e-16-0-2.ray-torchtpu-cluster-headless:8473,v6e-16-0-2.ray-torchtpu-cluster-headless:8474,v6e-16-0-3.ray-torchtpu-cluster-headless:8471,v6e-16-0-3.ray-torchtpu-cluster-headless:8472,v6e-16-0-3.ray-torchtpu-cluster-headless:8473,v6e-16-0-3.ray-torchtpu-cluster-headless:8474 TPU_WORKER_ID=3 TPU_NAME=v6e-16-0Ironwood(TPU7x)
TPU_WORKER_HOSTNAMES=tpu7x-16-0-0.ray-torchtpu-cluster-headless,tpu7x-16-0-1.ray-torchtpu-cluster-headless TORCH_TPU_TOPOLOGY=2,2,2,2 TORCH_TPU_SLICEBUILDER_ADDRESSES=tpu7x-16-0-0.ray-torchtpu-cluster-headless:8471,tpu7x-16-0-0.ray-torchtpu-cluster-headless:8472,tpu7x-16-0-0.ray-torchtpu-cluster-headless:8473,tpu7x-16-0-0.ray-torchtpu-cluster-headless:8474,tpu7x-16-0-0.ray-torchtpu-cluster-headless:8475,tpu7x-16-0-0.ray-torchtpu-cluster-headless:8476,tpu7x-16-0-0.ray-torchtpu-cluster-headless:8477,tpu7x-16-0-0.ray-torchtpu-cluster-headless:8478,tpu7x-16-0-1.ray-torchtpu-cluster-headless:8471,tpu7x-16-0-1.ray-torchtpu-cluster-headless:8472,tpu7x-16-0-1.ray-torchtpu-cluster-headless:8473,tpu7x-16-0-1.ray-torchtpu-cluster-headless:8474,tpu7x-16-0-1.ray-torchtpu-cluster-headless:8475,tpu7x-16-0-1.ray-torchtpu-cluster-headless:8476,tpu7x-16-0-1.ray-torchtpu-cluster-headless:8477,tpu7x-16-0-1.ray-torchtpu-cluster-headless:8478 TPU_WORKER_ID=1 TPU_NAME=tpu7x-16-0Ray ヘッドサービスへのポート転送セッションを確立します。
kubectl port-forward svc/ray-torchtpu-cluster-head-svc 8265:8265 >/dev/null 2>&1 &TPU ワーカーノードと自動検出された
ray.io/tpu-*ラベルは、Ray ダッシュボードの [Cluster] タブで確認できます。
Ray Core で分散テンソル並列処理を実行する
Ray Core には、ray.util.tpu モジュールの slice_placement_group 関数が用意されています。この関数は、完全なマルチホスト TPU スライスを予約します。また、PlacementGroupSchedulingStrategy クラスは、スライス全体の論理 TPU デバイスごとに 1 つの Ray アクターをスケジュールします。
サンプル リポジトリの
ray_core_collectives.pyスクリプトを確認します。このスクリプトは、TPU スライス全体で分散テンソル並列処理を 3 つのステップで実行します。
slice_placement_group関数は、16 デバイスの TPU スライスを予約し、各 TPU デバイスに 1 つのTorchTPUSliceWorkerアクターをスケジュールします。- 各アクターは、ホスト インデックスとローカル デバイス ID からグローバル ランクを計算し、
tpu_distプロセス グループを初期化します。 init_device_mesh関数とparallelize_module関数は、16 台の TPU デバイスすべてにフィードフォワード レイヤをシャーディングし、単一デバイスの CPU リファレンスに対して出力を検証し、ICI でall_reduceコレクティブを実行します。
TPU バージョンの Ray Core ジョブを送信します。
Trillium(v6e)
ray job submit \ --address http://localhost:8265 \ --working-dir . \ --runtime-env-json '{"excludes": ["ray-env", ".git", "*.whl"]}' \ -- python3 ray_core_collectives.py --topology=4x4 --accelerator-type=TPU-V6EIronwood(TPU7x)
ray job submit \ --address http://localhost:8265 \ --working-dir . \ --runtime-env-json '{"excludes": ["ray-env", ".git", "*.whl"]}' \ -- python3 ray_core_collectives.py --topology=2x2x2 --accelerator-type=TPU-V7X出力は次のようになります。
Trillium(v6e)
Rank 0 (worker_id=0, local_rank=0, ip=10.72.3.20): all_reduce_sum=[136.0, 136.0, 136.0, 136.0] Rank 1 (worker_id=0, local_rank=1, ip=10.72.3.20): all_reduce_sum=[136.0, 136.0, 136.0, 136.0] Rank 2 (worker_id=0, local_rank=2, ip=10.72.3.20): all_reduce_sum=[136.0, 136.0, 136.0, 136.0] Rank 3 (worker_id=0, local_rank=3, ip=10.72.3.20): all_reduce_sum=[136.0, 136.0, 136.0, 136.0] Rank 4 (worker_id=1, local_rank=0, ip=10.72.14.20): all_reduce_sum=[136.0, 136.0, 136.0, 136.0] Rank 5 (worker_id=1, local_rank=1, ip=10.72.14.20): all_reduce_sum=[136.0, 136.0, 136.0, 136.0] Rank 6 (worker_id=1, local_rank=2, ip=10.72.14.20): all_reduce_sum=[136.0, 136.0, 136.0, 136.0] Rank 7 (worker_id=1, local_rank=3, ip=10.72.14.20): all_reduce_sum=[136.0, 136.0, 136.0, 136.0] Rank 8 (worker_id=2, local_rank=0, ip=10.72.7.20): all_reduce_sum=[136.0, 136.0, 136.0, 136.0] Rank 9 (worker_id=2, local_rank=1, ip=10.72.7.20): all_reduce_sum=[136.0, 136.0, 136.0, 136.0] Rank 10 (worker_id=2, local_rank=2, ip=10.72.7.20): all_reduce_sum=[136.0, 136.0, 136.0, 136.0] Rank 11 (worker_id=2, local_rank=3, ip=10.72.7.20): all_reduce_sum=[136.0, 136.0, 136.0, 136.0] Rank 12 (worker_id=3, local_rank=0, ip=10.72.6.20): all_reduce_sum=[136.0, 136.0, 136.0, 136.0] Rank 13 (worker_id=3, local_rank=1, ip=10.72.6.20): all_reduce_sum=[136.0, 136.0, 136.0, 136.0] Rank 14 (worker_id=3, local_rank=2, ip=10.72.6.20): all_reduce_sum=[136.0, 136.0, 136.0, 136.0] Rank 15 (worker_id=3, local_rank=3, ip=10.72.6.20): all_reduce_sum=[136.0, 136.0, 136.0, 136.0] All 16 TorchTPU ranks verified DTensor TP parity (max_abs_diff=0.002470) and all_reduce sum = 136.0 ------------------------------------------ Job 'raysubmit_HFTkKrjSnfza3ZFD' succeeded ------------------------------------------Ironwood(TPU7x)
Rank 0 (worker_id=0, local_rank=0, ip=172.16.14.6): all_reduce_sum=[136.0, 136.0, 136.0, 136.0] Rank 1 (worker_id=0, local_rank=1, ip=172.16.14.6): all_reduce_sum=[136.0, 136.0, 136.0, 136.0] Rank 2 (worker_id=0, local_rank=2, ip=172.16.14.6): all_reduce_sum=[136.0, 136.0, 136.0, 136.0] Rank 3 (worker_id=0, local_rank=3, ip=172.16.14.6): all_reduce_sum=[136.0, 136.0, 136.0, 136.0] Rank 4 (worker_id=0, local_rank=4, ip=172.16.14.6): all_reduce_sum=[136.0, 136.0, 136.0, 136.0] Rank 5 (worker_id=0, local_rank=5, ip=172.16.14.6): all_reduce_sum=[136.0, 136.0, 136.0, 136.0] Rank 6 (worker_id=0, local_rank=6, ip=172.16.14.6): all_reduce_sum=[136.0, 136.0, 136.0, 136.0] Rank 7 (worker_id=0, local_rank=7, ip=172.16.14.6): all_reduce_sum=[136.0, 136.0, 136.0, 136.0] Rank 8 (worker_id=1, local_rank=0, ip=172.16.15.7): all_reduce_sum=[136.0, 136.0, 136.0, 136.0] Rank 9 (worker_id=1, local_rank=1, ip=172.16.15.7): all_reduce_sum=[136.0, 136.0, 136.0, 136.0] Rank 10 (worker_id=1, local_rank=2, ip=172.16.15.7): all_reduce_sum=[136.0, 136.0, 136.0, 136.0] Rank 11 (worker_id=1, local_rank=3, ip=172.16.15.7): all_reduce_sum=[136.0, 136.0, 136.0, 136.0] Rank 12 (worker_id=1, local_rank=4, ip=172.16.15.7): all_reduce_sum=[136.0, 136.0, 136.0, 136.0] Rank 13 (worker_id=1, local_rank=5, ip=172.16.15.7): all_reduce_sum=[136.0, 136.0, 136.0, 136.0] Rank 14 (worker_id=1, local_rank=6, ip=172.16.15.7): all_reduce_sum=[136.0, 136.0, 136.0, 136.0] Rank 15 (worker_id=1, local_rank=7, ip=172.16.15.7): all_reduce_sum=[136.0, 136.0, 136.0, 136.0] All 16 TorchTPU ranks verified DTensor TP parity (max_abs_diff=0.002470) and all_reduce sum = 136.0 ------------------------------------------ Job 'raysubmit_MrJbAe3hvARBcBN2' succeeded ------------------------------------------
Ray Train と PyTorch FSDP2 を使用して Gemma 4 モデルをファインチューニングする
Ray Train の TorchTrainer クラスは、マルチホスト TPU スライス全体で TPU スライスの予約、ワーカーの配置、torch.distributed の初期化を自動化します。
サンプル リポジトリの
ray_train_llm_finetune.pyスクリプトを確認します。このスクリプトは、databricks/databricks-dolly-15k指示データセットでgoogle/gemma-4-E2B-itモデルをファインチューニングします。このスクリプトは、次のコンポーネントを使用して
TorchTrainerクラスを構成します。ScalingConfigクラスは、トレーニング ジョブの分散ワーカー トポロジを構成します。use_tpu=True、num_workers=16、topology、accelerator_typeの各パラメータを設定すると、16 デバイスの TPU スライスが予約され、TPU コアごとに 1 つのワーカーがスケジュールされます。TorchConfigクラスは、ワーカー全体でtorch.distributedプロセス グループを構成します。backend="tpu_dist"パラメータを設定すると、train_func関数が実行される前に、16 個のワーカーすべてが TPU ICI ネットワーク経由で接続されます。create_dataloader関数は、指示データセットをダウンロードしてトークン化し、PyTorchDataLoaderオブジェクトを使用してトークン シーケンスをバッチ処理します。また、ray.train.torch.prepare_data_loader関数を呼び出して、各ワーカーが割り当てられた TPU デバイスで各グローバル バッチの互いに素なシャードを受け取るようにします。train_func関数は、bfloat16精度でモデルを読み込み、各フィードフォワード ブロックとルートモデルで PyTorch FSDP2fully_shard関数を呼び出して、16 個の TPU コアすべてにパラメータ、勾配、オプティマイザーの状態をシャーディングします。Checkpointクラスとray.train.report関数は、各エポックの終了時にランク 0 で完全なモデル状態辞書を収集し、チェックポイントを Cloud Storage バケットにアップロードします。
TPU バージョンの
TorchTrainerFSDP2 ファインチューニング ジョブを送信します。Trillium(v6e)
ray job submit \ --address http://localhost:8265 \ --working-dir . \ --runtime-env-json '{"excludes": ["ray-env", ".git", "*.whl"]}' \ -- python3 ray_train_llm_finetune.py \ --topology=4x4 \ --accelerator-type=TPU-V6E \ --storage-path=gs://${BUCKET_NAME}/ray_train_torchtpuIronwood(TPU7x)
ray job submit \ --address http://localhost:8265 \ --working-dir . \ --runtime-env-json '{"excludes": ["ray-env", ".git", "*.whl"]}' \ -- python3 ray_train_llm_finetune.py \ --topology=2x2x2 \ --accelerator-type=TPU-V7X \ --storage-path=gs://${BUCKET_NAME}/ray_train_torchtpu出力は次のようになります。
Trillium(v6e)
Starting Ray Train FSDP2 SFT job: model=google/gemma-4-E2B-it, topology=4x4, accelerator_type=TPU-V6E, num_workers=16 (TrainController pid=5407) Attempting to start training worker group of size 16 with the following resources: [{'TPU': 1, 'accelerator_type:TPU-V6E': 0.001}] * 16 (RayTrainWorker pid=19733, ip=10.72.3.20) Setting up process group for: env:// [rank=0, world_size=16] (RayTrainWorker pid=19733, ip=10.72.3.20) Epoch 1/3 | loss=3.1402 | epoch_time=28.17s | tokens/s=18613.5 (RayTrainWorker pid=19733, ip=10.72.3.20) Checkpoint successfully created at: Checkpoint(filesystem=gcs, path=my-project-ray-torchtpu/ray_train_torchtpu/torchtpu_gemma4_fsdp2_sft_1791864427/checkpoint_2026-10-13_04-08-10.762175) (RayTrainWorker pid=19733, ip=10.72.3.20) Epoch 2/3 | loss=2.3318 | epoch_time=26.07s | tokens/s=20112.8 (RayTrainWorker pid=19733, ip=10.72.3.20) Checkpoint successfully created at: Checkpoint(filesystem=gcs, path=my-project-ray-torchtpu/ray_train_torchtpu/torchtpu_gemma4_fsdp2_sft_1791864427/checkpoint_2026-10-13_04-10-02.548088) (RayTrainWorker pid=19733, ip=10.72.3.20) Epoch 3/3 | loss=2.2050 | epoch_time=25.52s | tokens/s=20545.2 (RayTrainWorker pid=19733, ip=10.72.3.20) Checkpoint successfully created at: Checkpoint(filesystem=gcs, path=my-project-ray-torchtpu/ray_train_torchtpu/torchtpu_gemma4_fsdp2_sft_1791864427/checkpoint_2026-10-13_04-11-48.701853) Training completed! Final metrics: {'epoch': 3, 'loss': 2.205, 'epoch_time_s': 25.52, 'tokens_per_s': 20545.2} Saved checkpoint: Checkpoint(filesystem=gcs, path=my-project-ray-torchtpu/ray_train_torchtpu/torchtpu_gemma4_fsdp2_sft_1791864427/checkpoint_2026-10-13_04-11-48.701853) ------------------------------------------ Job 'raysubmit_zbm4pnFZitA1YECA' succeeded ------------------------------------------Ironwood(TPU7x)
Starting Ray Train FSDP2 SFT job: model=google/gemma-4-E2B-it, topology=2x2x2, accelerator_type=TPU-V7X, num_workers=16 (TrainController pid=1162) Attempting to start training worker group of size 16 with the following resources: [{'TPU': 1, 'accelerator_type:TPU-V7X': 0.001}] * 16 (RayTrainWorker pid=257, ip=172.16.26.6) Setting up process group for: env:// [rank=0, world_size=16] (RayTrainWorker pid=257, ip=172.16.26.6) Epoch 1/3 | loss=3.1407 | epoch_time=26.28s | tokens/s=19953.3 (RayTrainWorker pid=257, ip=172.16.26.6) Checkpoint successfully created at: Checkpoint(filesystem=gcs, path=my-project-ray-torchtpu/ray_train_torchtpu/torchtpu_gemma4_fsdp2_sft_1791867004/checkpoint_2026-10-13_04-52-00.026930) (RayTrainWorker pid=257, ip=172.16.26.6) Epoch 2/3 | loss=2.3235 | epoch_time=16.68s | tokens/s=31429.9 (RayTrainWorker pid=257, ip=172.16.26.6) Checkpoint successfully created at: Checkpoint(filesystem=gcs, path=my-project-ray-torchtpu/ray_train_torchtpu/torchtpu_gemma4_fsdp2_sft_1791867004/checkpoint_2026-10-13_04-53-33.533341) (RayTrainWorker pid=257, ip=172.16.26.6) Epoch 3/3 | loss=2.1964 | epoch_time=16.98s | tokens/s=30884.6 (RayTrainWorker pid=257, ip=172.16.26.6) Checkpoint successfully created at: Checkpoint(filesystem=gcs, path=my-project-ray-torchtpu/ray_train_torchtpu/torchtpu_gemma4_fsdp2_sft_1791867004/checkpoint_2026-10-13_04-55-01.508219) Training completed! Final metrics: {'epoch': 3, 'loss': 2.1964, 'epoch_time_s': 16.98, 'tokens_per_s': 30884.6} Saved checkpoint: Checkpoint(filesystem=gcs, path=my-project-ray-torchtpu/ray_train_torchtpu/torchtpu_gemma4_fsdp2_sft_1791867004/checkpoint_2026-10-13_04-55-01.508219) ------------------------------------------ Job 'raysubmit_zSNnCXhHkwKiSmJh' succeeded ------------------------------------------トレーニング出力には次の情報が表示されます。
- エポック 1 には、最初の XLA グラフのコンパイルが含まれます。エポック 2 と 3 では、
/dev/shm/torch_tpu_cacheからキャッシュに保存された実行可能ファイルを再利用し、TPU Trillium(v6e)では 1 秒あたり 20,000 トークン、Ironwood(TPU7x)では 1 秒あたり 31,000 トークンに達します。 - 16 個の FSDP2 ワーカーランクすべてで同期されたグローバル クロスエントロピー損失は、
databricks/databricks-dolly-15k指示データセットの 3 つのファインチューニング エポック全体で、エポック 1 の 3.14 からエポック 3 の 2.20 に減少しています。 - 各エポックの終了時に、ランク 0 は完全なモデル状態辞書を収集し、チェックポイントを Cloud Storage バケットにアップロードします。
CheckpointConfig(num_to_keep=1, checkpoint_score_attribute="loss", checkpoint_score_order="min")オブジェクトで構成されているように、損失が最も低いチェックポイントを保持します。
- エポック 1 には、最初の XLA グラフのコンパイルが含まれます。エポック 2 と 3 では、
ワークロードのパフォーマンスをモニタリングする
Cloud Monitoring で TPU TensorCore 使用率、HBM 使用率、Ray クラスタ指標をモニタリングするには、Ray on GKE クラスタのログと指標を収集して表示すると Ray ダッシュボードで TPU 指標を表示するをご覧ください。
Ray ダッシュボードにアクセスする
組み込みの Ray ダッシュボードで Ray アクター、プレースメント グループ、トレーニング ジョブのステータスを検査する手順は次のとおりです。
Ray ヘッドサービスへのポート転送がまだ実行されていない場合は、バックグラウンドで開始します。
kubectl port-forward svc/ray-torchtpu-cluster-head-svc 8265:8265 >/dev/null 2>&1 &Cloud Shell で、
[ウェブでプレビュー] をクリックし、[ポート 8265 でプレビュー] を選択するか、ローカル ブラウザで
http://localhost:8265を開きます。[Jobs]、[Cluster]、[Metrics] タブをクリックして、ワーカーログ、プレースメント グループの状態、TPU ノードラベル、[TPU Utilization by Node] グラフを確認します。
クリーンアップ
このチュートリアルで使用したリソースについて Google Cloud アカウントに課金されないようにするには、RayCluster カスタム リソース、GKE クラスタ、Cloud Storage バケット、Artifact Registry リポジトリを削除します。
バックグラウンドのポート転送プロセスを停止し、RayCluster カスタム リソースと Kubernetes Secret を削除します。
pkill -f "kubectl port-forward" || true kubectl delete raycluster ray-torchtpu-cluster kubectl delete secret hf-secret --ignore-not-foundGKE クラスタを削除します。
Autopilot
gcloud container clusters delete ${CLUSTER_NAME} \ --region=${REGION}標準
gcloud container clusters delete ${CLUSTER_NAME} \ --zone=${ZONE}Cloud Storage バケットとそのチェックポイントの内容を削除します。
gcloud storage rm --recursive gs://${BUCKET_NAME}Artifact Registry リポジトリを削除します。
gcloud artifacts repositories delete ${ARTIFACT_REGISTRY} \ --location=${REGION}
次のステップ
- Cloud TPU での PyTorch の詳細を確認する。
- TorchTPU のドキュメントを確認する。
- Ray と TorchTPU を使用してマルチホスト TPU で LLM をサービングする方法を確認する。
- DWS と Kueue を使用して TPU で AI トレーニングを最適化する方法を確認する。
- GKE の TPU についてを確認する。
- Ray Train のドキュメントを確認する。