0
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

PyTorch分散学習の利用方法について、生成AIに聞いて理解してみた

Posted at

分散学習について、全く知らない初心者です。
PyTorch分散学習の利用方法について、生成AIに聞いて理解してみました。

結論

ChatGPTの回答内容で、とてもよく分かりました。良好。
ChatGPT等の生成AIは、こうした「PyTorch分散学習のコード」等のコーディングのことになると、非常に分かりやすく的確に教えてくれます

(一方で、論文の根幹となる概念や数式・アルゴリズムのことになると、根幹の部分についてはあまり教えてくれない・一般的で分りにくい・役に立たないことが多くあります)

分かったこと:

  1. PyTorchでGPU並列学習を行うには?
     → torch.nn.DataParallel(簡易)または torch.nn.parallel.DistributedDataParallel(DDP)(推奨)を使用。後者は複数GPU・複数ノード対応。
  2. DDPで異なるPCのGPUも利用可能?
     → 可能。IPネットワークで接続された複数ノードにまたがる並列処理ができる。
  3. 「ノード」とは?
     → 通常、**物理的な1台のPC(GPUを持つ)**を指す。
  4. torchrun の各引数の意味と注意点は?
     - --nproc_per_node:1台あたりのGPU数(プロセス数)
     - --nnodes:全体のノード数
     - --node_rank:このノードの識別番号(0始まり)
     - --master_addr:通信起点のノードのIP
     - --master_port:通信に使うポート(全ノード共通)
     - train_ddp.py:DDPに対応した学習スクリプト
  5. これらの引数はワーカーノードで自動設定できないのか?
     → torchrun は自動で取得しない。初期化時に全ノードが設定情報を知っている必要があるため、手動指定が必須。自動化にはスクリプトやクラスタマネージャが必要。

生成AIに聞く: PyTorch分散学習の利用方法について

ChatGPTに質問して理解してみます。
まず始めに、ざっくりとサンプルコードから聞いてみます。

ざっくりとサンプルコードから聞く

▼質問(人間)
pytorchで、GPUで並列に学習するためのサンプルコードは?

▼生成AIの回答(ChatGPT-4o)
PyTorchでGPUを使って並列に学習するには、いくつか方法がありますが、代表的なものとして以下の2つがあります。

  • torch.nn.DataParallel(やや古い方法)
  • torch.nn.parallel.DistributedDataParallel(推奨されている方法)

ここでは簡単に両方のサンプルコードを紹介します。

① DataParallelを使ったシンプルな並列学習の例

import torch
import torch.nn as nn
import torch.optim as optim

# 簡単なモデル
class SimpleModel(nn.Module):
    def __init__(self):
        super(SimpleModel, self).__init__()
        self.net = nn.Sequential(
            nn.Linear(100, 50),
            nn.ReLU(),
            nn.Linear(50, 10)
        )

    def forward(self, x):
        return self.net(x)

# モデル定義とDataParallel化
model = SimpleModel()
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model = nn.DataParallel(model)  # これで複数GPUに対応
model.to(device)

# ダミーデータと損失関数
input = torch.randn(64, 100).to(device)
target = torch.randint(0, 10, (64,)).to(device)
criterion = nn.CrossEntropyLoss()

# オプティマイザ
optimizer = optim.Adam(model.parameters(), lr=0.001)

# 学習ステップ
output = model(input)
loss = criterion(output, target)
loss.backward()
optimizer.step()

特徴

  • 1行で並列化でき、コードが簡潔です。
  • ただし通信がメインGPUで行われるため効率はやや劣ります。

② DistributedDataParallel(DDP)を使った推奨される方法(単一ノード・複数GPU)

import os
import torch
import torch.nn as nn
import torch.optim as optim
import torch.multiprocessing as mp
import torch.distributed as dist
from torch.nn.parallel import DistributedDataParallel as DDP

def setup(rank, world_size):
    dist.init_process_group("nccl", rank=rank, world_size=world_size)

def cleanup():
    dist.destroy_process_group()

class SimpleModel(nn.Module):
    def __init__(self):
        super(SimpleModel, self).__init__()
        self.net = nn.Sequential(
            nn.Linear(100, 50),
            nn.ReLU(),
            nn.Linear(50, 10)
        )

    def forward(self, x):
        return self.net(x)

def train(rank, world_size):
    setup(rank, world_size)
    torch.cuda.set_device(rank)

    model = SimpleModel().to(rank)
    ddp_model = DDP(model, device_ids=[rank])

    criterion = nn.CrossEntropyLoss()
    optimizer = optim.Adam(ddp_model.parameters(), lr=0.001)

    input = torch.randn(64, 100).to(rank)
    target = torch.randint(0, 10, (64,)).to(rank)

    output = ddp_model(input)
    loss = criterion(output, target)
    loss.backward()
    optimizer.step()

    cleanup()

if __name__ == "__main__":
    world_size = torch.cuda.device_count()
    mp.spawn(train, args=(world_size,), nprocs=world_size, join=True)

特徴

  • 通信効率が良く、学習速度・スケーラビリティに優れています。
  • 実運用ではこちらが推奨されており、特に大規模なモデルでは必須です。

どちらを選ぶべきか

比較軸 DataParallel DistributedDataParallel
実装の簡単さ ◎(非常に簡単) △(少し複雑)
通信効率 △(遅い) ◎(非常に効率的)
推奨度 ✕(非推奨) ◎(公式でも推奨されている方法)

目的やスケールに応じて選ぶと良いです。小規模な学習や試験コードでは DataParallel が手軽ですが、本番運用や大規模モデルでは DistributedDataParallel の使用が望ましいです。必要であれば、DDPをさらに発展させたデータローダや同期戦略も紹介可能です。

とても的確な回答、大変良好。

torch.nn.parallel.DistributedDataParallelを利用する方法、「python -m torch.distributed.launch」で起動するには?

▼質問(人間)
torch.nn.parallel.DistributedDataParallelを利用する方法で「python -m torch.distributed.launch」で起動するには?

▼生成AIの回答(ChatGPT-4o)
torch.nn.parallel.DistributedDataParallel(DDP)を使用して、python -m torch.distributed.launch でスクリプトを起動する方法について、以下に具体的な手順とサンプルコードを示します。

PyTorch 1.9以降では torch.distributed.launch は非推奨となっており、代わりに torchrun(= torch.distributed.run)の使用が推奨されていますが、ここではご希望に沿って launch モジュールでの使用方法を説明します。

① スクリプトの作成(train_ddp.py)

以下のような DDP 対応のトレーニングスクリプトを準備します。

# train_ddp.py

import os
import torch
import torch.nn as nn
import torch.optim as optim
import torch.distributed as dist
from torch.nn.parallel import DistributedDataParallel as DDP

def main():
    # 環境変数からrankとworld sizeを取得
    local_rank = int(os.environ["LOCAL_RANK"])
    rank = int(os.environ["RANK"])
    world_size = int(os.environ["WORLD_SIZE"])

    dist.init_process_group(backend="nccl")

    torch.cuda.set_device(local_rank)

    # モデルとデバイス設定
    model = nn.Sequential(
        nn.Linear(100, 50),
        nn.ReLU(),
        nn.Linear(50, 10)
    ).to(local_rank)

    ddp_model = DDP(model, device_ids=[local_rank])

    criterion = nn.CrossEntropyLoss()
    optimizer = optim.SGD(ddp_model.parameters(), lr=0.01)

    # ダミーデータ
    inputs = torch.randn(64, 100).to(local_rank)
    targets = torch.randint(0, 10, (64,)).to(local_rank)

    outputs = ddp_model(inputs)
    loss = criterion(outputs, targets)

    loss.backward()
    optimizer.step()

    print(f"Rank {rank}: loss = {loss.item()}")

    dist.destroy_process_group()

if __name__ == "__main__":
    main()

② スクリプトの起動方法

以下のように torch.distributed.launch モジュールを使って実行します。

python -m torch.distributed.launch \
  --nproc_per_node=2 \
  --use_env \
  train_ddp.py

引数の意味

  • --nproc_per_node=2: 使用するGPUの数(例では2枚)。
  • --use_env: 環境変数(RANK, LOCAL_RANK, WORLD_SIZE)でプロセスの設定を行う。これがあると、スクリプト側で明示的な引数パースが不要になります。
  • train_ddp.py: 実行するスクリプト。

③ 注意点

  • torch.distributed.launch非推奨 で、PyTorch 1.9以降では torchrun に置き換えることが推奨されています。
  • GPU数に応じて --nproc_per_node を変更してください。
  • 実際のデータローダと連携する場合、DistributedSampler を使う必要があります。

参考:torchrunでの起動方法(今後はこちらを推奨)

torchrun --nproc_per_node=2 train_ddp.py

この場合もコードは同様に LOCAL_RANKRANK を環境変数から取得する形にしておけば問題ありません。

必要であれば、DistributedSampler を使ったDataLoaderの構成や、マルチノード設定の例も追記できます。

torchrunで起動する場合、pyコードは変更が必要ですか?

▼質問(人間)
torchrunで起動する場合、pyコードは変更が必要ですか?

▼生成AIの回答(ChatGPT-4o)
いいえ、torchrun で起動する場合、PyTorchのトレーニングスクリプトに特別な変更は必要ありません。すでに torch.distributed.launch を使うことを想定して書かれているスクリプトであれば、そのまま torchrun で動作します。

ただし、以下の2点が 正しく実装されていること が前提です:

① torch.distributed.init_process_groupが適切に呼び出されている
torchrunlaunch モジュールと同様に、環境変数(例:RANK, LOCAL_RANK, WORLD_SIZE)を自動的に設定します。したがって、スクリプト内ではそれらを 環境変数から取得する形 で書かれていれば、変更は不要です。

local_rank = int(os.environ["LOCAL_RANK"])
rank = int(os.environ["RANK"])
world_size = int(os.environ["WORLD_SIZE"])

dist.init_process_group(backend="nccl")

② torch.cuda.set_device(local_rank)で各プロセスにGPUを割り当てている

torch.cuda.set_device(local_rank)

これにより、各プロセスが自分専用のGPUを使って学習するようになります。

** torchrunでの実行例**

たとえばGPUが2枚あるマシンで以下のように実行します:

torchrun --nproc_per_node=2 train_ddp.py

torch.distributed.launch の代わりとしてこれだけで十分です。

launchとの違い・利点

項目 launch torchrun (推奨)
モジュール torch.distributed.launch torch.distributed.run
Pythonからのインポート できない(-m専用) インポート可能(柔軟性あり)
エラーハンドリング 弱い 強化されている
環境変数の受け渡し --use_env が必要 デフォルトで渡される
推奨度 非推奨 ✅公式推奨

まとめ

  • torchrun に切り替えてもスクリプトの変更は原則不要です。
  • 環境変数ベースの実装になっていれば、launch 用スクリプトはそのまま torchrun でも動作します。
  • PyTorch公式では torchrun への移行を強く推奨しています。

必要であれば、argparse を使って local_rank を引数から受け取る方法(旧式)と環境変数から取得する方法(推奨)との違いも説明できます。

DistributedDataParallelを利用すると、異なるPCのGPUも利用できる?

▼質問(人間)
このDistributedDataParallelを利用すると、異なるPCのGPUも利用できる?

▼生成AIの回答(ChatGPT-4o)
はい、torch.nn.parallel.DistributedDataParallel(DDP) を利用することで、異なるPC(ノード)間に分散されたGPUを使った学習が可能です。これを「マルチノード・マルチGPU」学習と呼びます。

ただし、単一ノード内のGPU並列と比べて、設定がやや複雑になるため、以下のような構成要素と準備が必要です。

異なるPC間でDDPを動かすために必要なもの

① 各PCが同一ネットワーク内にあること

  • 各PC(ノード)同士がTCPで相互通信できる必要があります(SSHのようなものではなく、実際のデータ通信)。
  • 通信ポート(例えば 29500 番など)を開放しておく必要があります。

② 各ノードでPyTorchとCUDAの環境が一致していること

  • Python、PyTorch、CUDAのバージョンはできる限り揃えるべきです。
  • モデル・データローダのコードは全ノードで同じである必要があります。

③ DDPの初期化設定(主に4つの要素)
PyTorchのDDPはプロセス間通信に NCCL や GLOO という通信バックエンドを使いますが、設定には以下の4つの情報が必要です。

項目 内容
MASTER_ADDR マスターノードのIPアドレス "192.168.0.1"
MASTER_PORT 通信に使うポート番号 "29500"
WORLD_SIZE 全体のプロセス数(=全ノードのGPU合計数) 4(2ノード×2GPU)
RANK 各プロセスのグローバルな番号 0, 1, 2, 3

起動方法の例(2台のPCで、各2GPUを使う)

PC1(マスター)で実行:

torchrun \
  --nproc_per_node=2 \
  --nnodes=2 \
  --node_rank=0 \
  --master_addr=192.168.0.1 \
  --master_port=29500 \
  train_ddp.py

PC2(ワーカー)で実行:

torchrun \
  --nproc_per_node=2 \
  --nnodes=2 \
  --node_rank=1 \
  --master_addr=192.168.0.1 \
  --master_port=29500 \
  train_ddp.py
  • --nnodes=2: 全体で2ノード
  • --node_rank: 各PCに一意に割り振られるノード番号(マスターが0)

スクリプト側に必要な変更点(単一ノードと同じ)

スクリプト内では、torchrun によって自動で設定される環境変数をそのまま使えば大丈夫です:

import os
import torch.distributed as dist

def setup():
    dist.init_process_group(backend="nccl")
    local_rank = int(os.environ["LOCAL_RANK"])
    torch.cuda.set_device(local_rank)

注意点

  • モデルとデータはすべてのノードに配置しておく必要があります。
  • 学習データは通常、DistributedSampler を使ってノードごとに分割して読み込みます。
  • 時間がずれると通信エラーが出る場合があるため、各PCの時刻同期(NTP)も推奨されます。
  • NCCL の通信は InfiniBandNVLink があるとより高速ですが、TCPでも動作します。

補足:AWSやGCPでの活用

クラウド環境でも同様の設定で分散学習が可能です。AWS EC2 などではセキュリティグループでポートを開放する必要があります。さらに、torchrun の代わりに slurm などのジョブスケジューラと連携させることもできます。

まとめ

  • DDPは異なるPCのGPUを連携させた分散学習に対応しています。
  • torchrun を使えば、比較的簡単にマルチノード学習を構成できます。
  • ネットワーク、ポート、環境変数などの正確な設定が重要です。

ご希望があれば、具体的な起動スクリプトやファイル配置、データ分割のサンプルなども提供できます。

MASTER_ADDRには、「マスター」ノードのPCのIPアドレスを指定することになる?

▼質問(人間)
MASTER_ADDRには、「マスター」ノードのPCのIPアドレスを指定することになる?

▼生成AIの回答(ChatGPT-4o)
はい、そのとおりです。MASTER_ADDR には、マスター(ランク0のノード)として機能するPCのIPアドレス を指定します。

分散学習では、複数のノード(PC)で相互に通信しながら学習を進めるため、各プロセスが「どこに接続すれば良いか」を知る必要があります。その接続先として指定されるのが MASTER_ADDR です。

例:2台のPCで学習する場合

  • PC1:IPアドレスが 192.168.0.1
  • PC2:IPアドレスが 192.168.0.2

このとき、PC1 をマスターにするなら:

  • PC1(ノードランク0):
    torchrun --nproc_per_node=2 --nnodes=2 --node_rank=0 \
      --master_addr=192.168.0.1 --master_port=29500 train_ddp.py
    
  • PC2(ノードランク1):
    torchrun --nproc_per_node=2 --nnodes=2 --node_rank=1 \
      --master_addr=192.168.0.1 --master_port=29500 train_ddp.py
    

全プロセスが 192.168.0.129500 ポートを通じて通信を開始します。

補足:マスターの役割とは?

  • 通信の初期化(process groupの構築)を担います。
  • 学習の進行中は、AllReduce などで並列通信をしますが、「中央サーバ」として常に管理しているわけではありません。
  • 通常はどのノードをマスターにしても動作上の差はありませんが、起動順序・信頼性の面から、安定して稼働できるノードを選ぶのが望ましいです。

必要であれば、MASTER_PORT の選び方や、マスター選出の自動化方法などもお伝えできます。

異なるPC(GPUを持つPC)毎の単位を、ノードと言う?

▼質問(人間)
異なるPC(GPUを持つPC)毎の単位を、ノードと言う?

▼生成AIの回答(ChatGPT-4o)
はい、そのとおりです。分散学習において、GPUを持つPC一台ごとの単位を「ノード(node)」と呼びます

ノード(node)とは?

  • 1つの物理的なマシン(サーバーやPC)を指します。
  • 通常、1ノードに対して1つ以上のGPUが搭載されており、そのGPUを使って計算を行います。
  • 異なるノード同士はネットワーク(TCP/IPやInfiniBand)で接続され、相互に通信しながら学習を進めます。

用語の整理:

用語 説明
ノード(node) 分散構成における1台のマシン(GPU付きPC)
プロセス(process) 各ノード上で起動されるトレーニングの実行単位。通常はGPUごとに1つのプロセスを割り当てます。
ワーカ(worker) ノードまたはプロセスをまとめて「ワーカ」と呼ぶこともあります(文脈依存)
ランク(rank) 分散全体でのプロセス番号。全プロセスに一意の番号が振られます(例:0, 1, 2…)
ローカルランク(local_rank) 各ノード内でのGPU番号(例:0, 1 など)

例:2台のPCでそれぞれ2枚のGPUを使う構成

  • ノード数(nnodes): 2
  • 各ノードのGPU数: 2
  • 総プロセス数(world_size): 4
  • 各プロセスは、それぞれ1枚のGPUと対応
[ノード0] PC-A(192.168.0.1)
 ├─ GPU0 → rank 0, local_rank 0
 └─ GPU1 → rank 1, local_rank 1

[ノード1] PC-B(192.168.0.2)
 ├─ GPU0 → rank 2, local_rank 0
 └─ GPU1 → rank 3, local_rank 1

このように、「ノード」は物理マシン単位の概念であり、DDPではその上に複数のプロセス(各GPUに1つずつ)を立てて並列計算を実行します。

ご希望があれば、ノード間通信の仕組みや、クラスタ全体の構成方法についてもさらに詳しく解説できます。

「--node_rank=1」と「--node_rank=2」は優先度の違いになる?それとも、単に識別ID?

▼質問(人間)
「--node_rank=0」はマスターノードとしての意味がある一方で、「--node_rank=1」と「--node_rank=2」には意味の違いがある?
「--node_rank=1」と「--node_rank=2」は優先度の違いになる?それとも、単に識別ID?

▼生成AIの回答(ChatGPT-4o)
--node_rank=1」と「--node_rank=2」の違いは、優先度の違いではなく、単なる識別IDです。したがって、マスターノード以外の node_rank に意味の違いはありません

node_rankの役割とは?

  • 分散学習では、全体の中で「自分はノード全体の中で何番目か」を知る必要があります。
  • これを表すのが --node_rank です。
  • つまり、node_rankノード全体に対する一意なインデックス(識別番号) にすぎません。

node_rank=0がマスターになる理由

  • MASTER_ADDR で指定されるノードが通常「マスター」ノードとなります。
  • torchrun では node_rank=0 のノードがマスターとして初期化される想定で動きます。
  • それ以外のノード(node_rank=1, 2, ...)はマスターに接続して、トレーニングに参加する「ワーカノード」として振る舞います。

具体例(3ノード構成)

ノード IPアドレス node_rank 役割
A 192.168.0.1 0 マスターノード
B 192.168.0.2 1 ワーカノード
C 192.168.0.3 2 ワーカノード

このとき、ノードBとCにおいて node_rank=1node_rank=2 の違いは、全体の通信順位(rankの割り当て)に影響を与えるだけであり、優先順位や役割の違いを意味するものではありません

補足:rankの計算

PyTorchの分散プロセスでは、ノードの node_rank と各ノードの local_rank(GPU番号)から、グローバルな rank が決定されます

rank = node_rank * nproc_per_node + local_rank

たとえば、各ノードにGPUが2枚ある構成で、node_rank=1 のノードでは:

  • local_rank=0 → rank=2
  • local_rank=1 → rank=3

まとめ

  • --node_rank は単なる識別IDです。
  • --node_rank=1--node_rank=2 の間に優先度や役割の違いはありません。
  • 実際には、通信順位の決定とプロセス識別のために用いられるのみです。

ご希望があれば、rankの管理や各ノードにおけるログ出力の分離方法などもご説明できます。

マスター(--node_rank=0)ノードと、1個のワーカーノード(--node_rank=1)を同じPCに指定することは可能?

▼質問(人間)
マスター(--node_rank=0)ノードと、1個のワーカーノード(--node_rank=1)を同じPCに指定することは可能?

▼生成AIの回答(ChatGPT-4o)
はい、マスターノード(--node_rank=0)とワーカーノード(--node_rank=1)を同じPCに指定することは原理的には可能ですが、PyTorchの torchrun を使う場合は通常、1台のPCには1つの node_rank しか割り当てられません

したがって、原則的には1台のPC = 1ノード(1つの node_rank)が基本構成です。

理由:

  • node_rank は「物理マシン(ノード)」ごとに一意でなければならず、1台のPCで複数の node_rank を持つことは想定されていません
  • torchrun は、各ノードでローカルに複数のプロセス(GPU数に応じて)を立ち上げ、それらを分散プロセスグループとして統合します。
  • もし1台のPCで --node_rank=0--node_rank=1 を両方起動しようとすると、通信設定の重複(例:ポートの競合)やリソース管理の混乱が生じる可能性が高いです

ただし、擬似的な検証として同一PCで複数ノードをシミュレートしたい場合には、以下のような方法があります:

方法1:Dockerコンテナを複数立てて、それぞれを別ノードとして扱う

  • ホストPC上に2つのDockerコンテナを起動し、それぞれ異なる --node_rank を割り当てて通信させる。
  • この場合、各コンテナが「独立したノード」として機能するため、分散のテストが可能。

方法2:ローカルホスト上に別プロセスとして手動で起動する(高度な方法)

  • Pythonスクリプト側で通信ポートや init_method にファイルベースのもの(例:file:///tmp/sharedfile)を使い、手動で rank を制御する。
  • 実験的な構成で、正式な torchrun を使った本番環境では推奨されません。

まとめ

  • 通常の torchrun においては、1台のPCには1つの node_rank を割り当てるのが原則です。
  • 同じPCで node_rank=0node_rank=1 を使い分ける構成は推奨されておらず、通信設定や資源競合の問題が起こりやすくなります。
  • どうしても同一PCで複数ノードを模擬したい場合は、Dockerなどの仮想化環境を用いて、物理的に分離されたノードとして扱う工夫が必要です。

もし開発・検証用にローカルPCだけで分散学習を試したいという目的がある場合、それに応じた方法をご提案できます。ご希望があれば教えてください。

各引数について、設定方法(設定例)・設定時の注意点・他項目との違いを教えて下さい。

▼質問(人間)
「torchrun
--nproc_per_node=2
--nnodes=2
--node_rank=1
--master_addr=192.168.0.1
--master_port=29500
train_ddp.py」にある各引数について、設定方法(設定例)・設定時の注意点・他項目との違いを教えて下さい。

▼生成AIの回答(ChatGPT-4o)
以下に、torchrun による分散学習の起動コマンドに含まれる各引数について、設定方法(設定例)・設定時の注意点・他項目との違いをそれぞれ丁寧に整理して解説します。

① --nproc_per_node=2

  • 意味:1ノード(PC)あたりに起動するプロセス数(通常はGPU数と等しい)
  • 設定例
    • GPUが2枚搭載されたマシン → --nproc_per_node=2
    • GPUが4枚 → --nproc_per_node=4
  • 注意点
    • 指定した値と実際のGPU数が合っていないと、CUDAデバイスの初期化でエラーになります。
    • GPU数以上のプロセスを指定すると CUDA_VISIBLE_DEVICES の設定ミスにもつながります。
  • 関連項目との違い
    • nproc_per_nodeローカルプロセス数
    • nnodes × nproc_per_node が**全体の world size(全プロセス数)**になります。

② --nnodes=2

  • 意味:分散学習に参加するノード(PC)全体の数
  • 設定例
    • 2台のPCを使って分散学習 → --nnodes=2
    • 単一マシンで分散を模擬する場合 → --nnodes=1
  • 注意点
    • 各ノードでこの値を同じにしなければなりません(全体の構成を正しく揃える必要があります)。
  • 関連項目との違い
    • nnodes は「物理マシンの数」、nproc_per_node は「1台あたりのプロセス数」。

③ --node_rank=1

  • 意味:このノードが全体の中で何番目かを示す番号(0から始まる)
  • 設定例
    • マスターノード → --node_rank=0
    • 2台目のノード → --node_rank=1
  • 注意点
    • ノード間で重複してはいけません。各ノードに一意な番号を振る必要があります。
    • 通常は、IPアドレスなどの一覧を使って起動スクリプトを自動化することもあります。
  • 関連項目との違い
    • rank は「全体のプロセス番号(プロセス単位)」ですが、node_rank は「ノード単位の番号」です。

④ --master_addr=192.168.0.1

  • 意味:マスターとして動作するノードのIPアドレス
  • 設定例
    • マスターノードのローカルIPが 192.168.0.1 の場合、この値を全ノード共通で指定
  • 注意点
    • 他ノードは、このIPアドレスに接続して初期化通信を行います
    • Dockerやクラウド環境では、ポートフォワーディングやIP制限などに注意が必要です。
  • 関連項目との違い
    • master_addr は通信の出発点を決めるだけであり、マスターノードが常に主導権を持つわけではありません。

⑤ --master_port=29500

  • 意味:マスターが他ノードと通信を確立するために使用するポート番号
  • 設定例
    • よく使われるのは 295001235512345 など(重複しにくい番号)
  • 注意点
    • ポートが他プロセスと競合している場合、エラーになります(特に複数回実験をする場合)。
    • Firewall や Docker ネットワークでは このポートが開放されていること が必要です。
  • 関連項目との違い
    • master_port の指定は必須ではないですが、省略すると torchrun が自動割当しないため、明示的に指定するのが安定です。

⑥ train_ddp.py

  • 意味:実行されるPythonスクリプト。内部で DistributedDataParallel を使用している必要があります。
  • 設定例
    • スクリプト内で次のように分散の初期化を行っている必要があります:
      dist.init_process_group(backend="nccl")
      
  • 注意点
    • スクリプトの中で環境変数(RANK, WORLD_SIZE, LOCAL_RANK)を取得することで、各プロセスが自分の役割を認識します。
    • torchrun を使うと、これらの環境変数は自動で設定されます(手動設定は不要)。

まとめ:起動イメージ(2ノード × 2GPU)

ノードIP node_rank 実行コマンド
192.168.0.1 0 torchrun --nproc_per_node=2 --nnodes=2 --node_rank=0 --master_addr=192.168.0.1 --master_port=29500 train_ddp.py
192.168.0.2 1 torchrun --nproc_per_node=2 --nnodes=2 --node_rank=1 --master_addr=192.168.0.1 --master_port=29500 train_ddp.py

この構成では、合計4プロセスが立ち上がり、それぞれ1枚のGPUを担当して並列に学習を進めます。

さらに詳細(たとえば、ログ出力の制御、失敗時のリカバリ、torchrunの内部挙動など)も知りたい場合は、お知らせください。

マスターノードと通信して、ワーカーノードでは「--nproc_per_node=2 --nnodes=2 --node_rank=1」を自動で解決できるのでは?

▼質問(人間)
マスターノードと通信して、ワーカーノードでは「--nproc_per_node=2 --nnodes=2 --node_rank=1」を自動で解決できるのでは?

▼生成AIの回答(ChatGPT-4o)
ご指摘のように、「ワーカーノードがマスターノードに接続し、--nproc_per_node--nnodes--node_rank自動で解決してくれたら便利」というのは自然な発想です。ただし、PyTorch の torchrun 現状(v2.2 など)では、これらの値を自動で解決する機能は提供されていません

なぜ自動解決できないのか?

分散トレーニングにおける各ノードは、起動時点で以下の情報を知っていなければならないからです:

  1. 自分が全体の中でどのノードか--node_rank
  2. 全体で何ノードあるか--nnodes
  3. 1ノードあたり何プロセスか--nproc_per_node

これらは 分散プロセスの初期化(torch.distributed.init_process_group)時に必要な情報であり、遅延して後から受け取ることはできません。

ワーカーノードが接続後にマスターから教えてもらう方式にしない理由

  • それを行うためには、PyTorchが**「コーディネーションサービス」や「ジョブ管理デーモン」**のようなプロセスを内部で持つ必要があります。
  • これは、たとえば RayKubernetes, Horovod with MPI, DeepSpeed launcher など、より高度な分散フレームワークでは実現されています。
  • しかし、torchrun軽量かつローカルスクリプトベースの分散実行ツールであり、そうしたデーモンを持たない設計方針になっています。

じゃあ、どうすれば便利にできるのか?

以下のような工夫で利便性を高めることが可能です。

① 起動スクリプトで各ノード用のコマンドを生成する(推奨)

たとえば、bashpython スクリプトで以下のような起動制御を行う:

#!/bin/bash

NODE_RANK=$1  # 0 or 1 or 2...
MASTER_ADDR=192.168.0.1

torchrun \
  --nproc_per_node=2 \
  --nnodes=2 \
  --node_rank=${NODE_RANK} \
  --master_addr=${MASTER_ADDR} \
  --master_port=29500 \
  train_ddp.py

これを各ノードで呼び出すだけにすることで、人為的ミスを減らせます。

② クラスタ管理ツールと連携する

  • Kubernetes + TorchElastic(Elastic Launch)
  • SLURM スケジューラ
  • Amazon SageMaker や GCP AI Platform

これらの環境では、ノードランク・ワールドサイズなどが自動で環境変数として渡されるため、ユーザーは torchrun ですら使わずに torch.distributed.launch 相当の初期化が可能になります。

まとめ

  • 現在の torchrun では、--node_rank, --nnodes, --nproc_per_node の自動解決はされません。
  • これは設計上、各ノードが独立に起動され、事前に全体構成を知っている前提だからです。
  • 自動解決をしたい場合は、上位のジョブスケジューラやクラスタマネージャーと連携することが現実的なアプローチです。

もし、ローカルマルチGPU・マルチノード環境で自動化を検討されている場合、簡易スクリプトのテンプレートやツール連携方法もご提案できます。必要であればお知らせください。

なるほど、各ノードが遅延してバラバラに開始されても、上手く動作するように、「--nproc_per_node=2 --nnodes=2 --node_rank=1」が必要ということ。

生成AIに聞いた全履歴

0
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?