카테고리 없음

torchrun 명령어를 활용한 PytorchJob 분산 데이터 병렬 처리 (with minGPT)

개발허재 2023. 10. 30. 23:25

minGPT 소개

A PyTorch re-implementation of GPT, both training and inference

출처: https://github.com/karpathy/minGPT

 

PytorchJob

PytorchJob은 Kubeflow 프로젝트의 Training-operator 컴포넌트에서 Pytorch 병렬처리를 위한 CRD입니다.

(참고: https://www.kubeflow.org/docs/components/training/pytorch/)

저는 https://github.com/kubeflow/training-operator/tree/master/examples/pytorch 의 예제코드를 가지고 기초적인 실습을 진행했습니다.

하지만, LLM을 위한 Transformer, Huggingface 등의 라이브러리를 사용하면서 복잡성과 디테일이 필요해지면서 PytorchJob이 어떻게 동작하는지 살펴보았습니다.

 

PytorchJob Manifest

apiVersion: "kubeflow.org/v1"
kind: "PyTorchJob"
metadata:
  name: "pytorch-mingpt-nccl"
spec:
  pytorchReplicaSpecs:
    Master:
      replicas: 1
      restartPolicy: OnFailure
      template:
        metadata:
          annotations:
            sidecar.istio.io/inject: "false"
        spec:
          containers:
            - name: pytorch
              image: pytorch_mingpt:latest
              imagePullPolicy: Always
              command: ["torchrun"]
              args: ["--nnodes", $(WORLD_SIZE), "--nproc_per_node", "1", "--rdzv-backend", "c10d", "--rdzv-endpoint", $(MASTER_ADDR):$(MASTER_PORT), "/temp/minGPT-ddp/main.py"]
              resources:
                limits:
                  nvidia.com/gpu: 1
              volumeMounts:
              - name: share-volume
                mountPath: /temp
          volumes:
          - name: share-volume
            nfs:
              path: /my/nas/path
              server: my.nas.com
    Worker:
      replicas: 2
      restartPolicy: OnFailure
      template:
        metadata:
          annotations:
            sidecar.istio.io/inject: "false"
        spec:
          containers:
            - name: pytorch
              image: pytorch_mingpt:latest
              imagePullPolicy: Always
              command: ["torchrun"]
              args: ["--nnodes", $(WORLD_SIZE), "--nproc_per_node", "1", "--rdzv-backend", "c10d", "--rdzv-endpoint", $(MASTER_ADDR):$(MASTER_PORT), "/temp/minGPT-ddp/main.py"]
              resources:
                limits:
                  nvidia.com/gpu: 1
              volumeMounts:
              - name: share-volume
                mountPath: /temp
          volumes:
          - name: share-volume
            nfs:
              path: /my/nas/path
              server: my.nas.com

위와 같이 Master node 1개, Worker node 2개로 replica 설정 후 apply하였습니다.

 

Master node Pod

apiVersion: v1
kind: Pod
metadata:
  ...
spec:
  containers:
    ...
    env:
    - name: PYTHONUNBUFFERED
      value: "0"
    - name: MASTER_PORT
      value: "23456"
    - name: MASTER_ADDR
      value: pytorch-mingpt-nccl-master-0
    - name: WORLD_SIZE
      value: "3"
    - name: RANK
      value: "0"
...

Worker nodes Pod

apiVersion: v1
kind: Pod
metadata:
  ...
spec:
  containers:
    ...
    env:
    - name: PYTHONUNBUFFERED
      value: "0"
    - name: MASTER_PORT
      value: "23456"
    - name: MASTER_ADDR
      value: pytorch-mingpt-nccl-master-0
    - name: WORLD_SIZE
      value: "3"
    - name: RANK
      value: "0"
...
apiVersion: v1
kind: Pod
metadata:
  ...
spec:
  containers:
    ...
    env:
    - name: PYTHONUNBUFFERED
      value: "0"
    - name: MASTER_PORT
      value: "23456"
    - name: MASTER_ADDR
      value: pytorch-mingpt-nccl-master-0
    - name: WORLD_SIZE
      value: "3"
    - name: RANK
      value: "0"
...

각 Master, Worker node Pod의 spec.containers.env 필드 부분만 추출했습니다.

PytorchJob은 각 Pod에 위와 같이 환경변수를 자동으로 주입해주는 것을 확인할 수 있었습니다.

(추가적으로, Worker Pod에서는 Master Pod의 Service Domain으로 alive check 요청을 보내는 initContainer도 띄웁니다.)

따라서, 기존 분산학습돌릴때 직접 정해주던 rank, world_size 등을 직접 선언할 필요가 없어졌습니다.

 

torchrun

"How to install Torchrun?"

torchrun은 pytorch 1.10부터 사용 가능하며 이전버전에서는 python -m torch.distributed.run 명령어로 실행 가능합니다.

출처: https://discuss.pytorch.org/t/how-to-install-torchrun/135138

 

다음으로, 위 PytorchJob Manifest 에서의 Command와 args 부분에 대한 설명입니다.

 

command로는 torchrun이 들어가고,

args 부분에서 선언되는

  • nnodes는 노드의 수를 설정하는 인수이며 env로 선언되는 WORLD_SIZE로부터 변수값으로 할당하기 위해  $(WORLD_SIZE)로 표기합니다.
  • nproc_per_node는 각 노드에서 실행될 프로세스 수를 설정하는 인수입니다. 각 노드에서 GPU 1장을 사용하기 위해 1로 표기합니다.
  • rdzv-backend는 분산 학습을 위한 백엔드를 설정하는 옵션입니다. 분산 학습에서 백엔드는 각 학습 프로세스 간에 상호 통신 및 조정을 담당합니다. 주로 사용되는 백엔드 중 하나는 "c10d"입니다. "c10d"는 PyTorch의 분산 백엔드로, 다수의 GPU 노드 간 효율적인 통신을 지원합니다. GPU 노드간 통신을 위해 c10d로 표기합니다.
  • rdzv-endpoint는 분산 학습을 위한 백엔드와 상호 작용할 때 사용할 엔드포인트(endpoint)를 설정하는 옵션입니다. 이 엔드포인트는 백엔드에서 제공되며, 학습 프로세스 간에 서로 찾아갈 수 있게 해줍니다. 주로 사용되는 형식은 hostname:port이며, 이를 통해 분산 학습 프로세스가 서로 연결됩니다. env로부터 불러온 값을 통해 $(MASTER_ADDR):$(MASTER_PORT)로 표기합니다.
  • 참고로 master_addr은 rdzv-backend가 static 이 아니거나 rdzv-endpoint가 선언되지 않았을때에만 사용됩니다. (master_addr is only used for static rdzv_backend and when rdzv_endpoint is not specified.)
  • 여기서, rdzv(rendezvous)란 분산된 시스템 내의 모든 워커가 동기화되고 통신할 수 있게 하는 것입니다. (출처: https://pytorch.org/docs/stable/elastic/rendezvous.html)

torchrun reference: https://pytorch.org/docs/stable/elastic/run.html

 

학습에 사용되는 minGPT ddp 프로젝트는 https://github.com/pytorch/examples/tree/main/distributed/minGPT-ddp 로부터 불러왔고

os.environ["NCCL_DEBUG"] = "INFO"를 추가하여 디버깅 및 더많은 로깅 정보를 가져오게하여 학습결과를 아래와 같이 출력해본 결과

마스터 노드에서는 아래와 같은 출력이,

root@BD1DEV-L-KUBESPAWNER-MASTER-001:/home/hsd3030# k logs -n kubeflow pytorch-mingpt-nccl-master-0 --all-containers -f
master_addr is only used for static rdzv_backend and when rdzv_endpoint is not specified.
0
[2023-10-29 10:13:19,772][torch.distributed.distributed_c10d][INFO] - Added key: store_based_barrier_key:1 to store for rank: 0
[2023-10-29 10:13:19,773][torch.distributed.distributed_c10d][INFO] - Rank 0: Completed store-based barrier for key:store_based_barrier_key:1 with 3 nodes.
init model
Data has 55769 characters, 59 unique.
number of parameters: 27.32M
Snapshot not found. Training model from scratch
pytorch-mingpt-nccl-master-0:10:10 [0] NCCL INFO Bootstrap : Using eth0:10.244.7.217<0>
pytorch-mingpt-nccl-master-0:10:10 [0] NCCL INFO NET/Plugin : No plugin found (libnccl-net.so), using internal implementation
pytorch-mingpt-nccl-master-0:10:10 [0] NCCL INFO cudaDriverVersion 12000
NCCL version 2.14.3+cuda11.7
pytorch-mingpt-nccl-master-0:10:31 [0] NCCL INFO Failed to open libibverbs.so[.1]
pytorch-mingpt-nccl-master-0:10:31 [0] NCCL INFO NET/Socket : Using [0]eth0:10.244.7.217<0>
pytorch-mingpt-nccl-master-0:10:31 [0] NCCL INFO Using network Socket
pytorch-mingpt-nccl-master-0:10:31 [0] NCCL INFO Channel 00/02 :    0   1   2
pytorch-mingpt-nccl-master-0:10:31 [0] NCCL INFO Channel 01/02 :    0   1   2
pytorch-mingpt-nccl-master-0:10:31 [0] NCCL INFO Trees [0] 2/-1/-1->0->-1 [1] 2/-1/-1->0->1
pytorch-mingpt-nccl-master-0:10:31 [0] NCCL INFO Channel 00/0 : 2[3000] -> 0[3000] [receive] via NET/Socket/0
pytorch-mingpt-nccl-master-0:10:31 [0] NCCL INFO Channel 01/0 : 2[3000] -> 0[3000] [receive] via NET/Socket/0
pytorch-mingpt-nccl-master-0:10:31 [0] NCCL INFO Channel 00/0 : 0[3000] -> 1[3000] [send] via NET/Socket/0
pytorch-mingpt-nccl-master-0:10:31 [0] NCCL INFO Channel 01/0 : 0[3000] -> 1[3000] [send] via NET/Socket/0
pytorch-mingpt-nccl-master-0:10:31 [0] NCCL INFO Connected all rings
pytorch-mingpt-nccl-master-0:10:31 [0] NCCL INFO Channel 01/0 : 1[3000] -> 0[3000] [receive] via NET/Socket/0
pytorch-mingpt-nccl-master-0:10:31 [0] NCCL INFO Channel 00/0 : 0[3000] -> 2[3000] [send] via NET/Socket/0
pytorch-mingpt-nccl-master-0:10:31 [0] NCCL INFO Channel 01/0 : 0[3000] -> 2[3000] [send] via NET/Socket/0
pytorch-mingpt-nccl-master-0:10:31 [0] NCCL INFO Connected all trees
pytorch-mingpt-nccl-master-0:10:31 [0] NCCL INFO threadThresholds 8/8/64 | 24/8/64 | 512 | 512
pytorch-mingpt-nccl-master-0:10:31 [0] NCCL INFO 2 coll channels, 2 p2p channels, 2 p2p channels per peer
pytorch-mingpt-nccl-master-0:10:31 [0] NCCL INFO comm 0x6fdef80 rank 0 nranks 3 cudaDev 0 busId 3000 - Init COMPLETE
train
[GPU0] Epoch 1 | Iter 0 | Train Loss 4.18325
[2023-10-29 10:13:23,283][torch.nn.parallel.distributed][INFO] - Reducer buckets have been rebuilt in this iteration.
[GPU0] Epoch 1 | Iter 0 | Eval Loss 2.32799
pytorch-mingpt-nccl-master-0:10:32 [0] NCCL INFO [Service thread] Connection closed by localRank 0
pytorch-mingpt-nccl-master-0:10:10 [0] NCCL INFO comm 0x6fdef80 rank 0 nranks 3 cudaDev 0 busId 3000 - Abort COMPLETE

 

워커노드에서는 아래와 같은 출력이 나오는 것을 확인할 수 있었습니다.

root@BD1DEV-L-KUBESPAWNER-MASTER-001:/home/hsd3030# k logs -n kubeflow pytorch-mingpt-nccl-worker-0 --all-containers -f
nslookup: can't resolve '(null)': Name does not resolve
nslookup: can't resolve 'pytorch-mingpt-nccl-master-0': Name does not resolve
waiting for master
nslookup: can't resolve '(null)': Name does not resolve
 
Name:      pytorch-mingpt-nccl-master-0
Address 1: 10.244.7.217 10-244-7-217.pytorch-mingpt-nccl-master-0.kubeflow.svc.cluster.local
master_addr is only used for static rdzv_backend and when rdzv_endpoint is not specified.
0
[2023-10-29 10:13:16,704][torch.distributed.distributed_c10d][INFO] - Added key: store_based_barrier_key:1 to store for rank: 1
[2023-10-29 10:13:17,697][torch.distributed.distributed_c10d][INFO] - Rank 1: Completed store-based barrier for key:store_based_barrier_key:1 with 3 nodes.
init model
Data has 55769 characters, 59 unique.
number of parameters: 27.32M
Snapshot not found. Training model from scratch
pytorch-mingpt-nccl-worker-0:9:9 [0] NCCL INFO cudaDriverVersion 11040
pytorch-mingpt-nccl-worker-0:9:9 [0] NCCL INFO Bootstrap : Using eth0:10.244.3.32<0>
pytorch-mingpt-nccl-worker-0:9:9 [0] NCCL INFO NET/Plugin : No plugin found (libnccl-net.so), using internal implementation
pytorch-mingpt-nccl-worker-0:9:28 [0] NCCL INFO Failed to open libibverbs.so[.1]
pytorch-mingpt-nccl-worker-0:9:28 [0] NCCL INFO NET/Socket : Using [0]eth0:10.244.3.32<0>
pytorch-mingpt-nccl-worker-0:9:28 [0] NCCL INFO Using network Socket
pytorch-mingpt-nccl-worker-0:9:28 [0] NCCL INFO Trees [0] -1/-1/-1->1->2 [1] 0/-1/-1->1->-1
pytorch-mingpt-nccl-worker-0:9:28 [0] NCCL INFO Channel 00/0 : 0[3000] -> 1[3000] [receive] via NET/Socket/0
pytorch-mingpt-nccl-worker-0:9:28 [0] NCCL INFO Channel 01/0 : 0[3000] -> 1[3000] [receive] via NET/Socket/0
pytorch-mingpt-nccl-worker-0:9:28 [0] NCCL INFO Channel 00/0 : 1[3000] -> 2[3000] [send] via NET/Socket/0
pytorch-mingpt-nccl-worker-0:9:28 [0] NCCL INFO Channel 01/0 : 1[3000] -> 2[3000] [send] via NET/Socket/0
pytorch-mingpt-nccl-worker-0:9:28 [0] NCCL INFO Connected all rings
pytorch-mingpt-nccl-worker-0:9:28 [0] NCCL INFO Channel 00/0 : 2[3000] -> 1[3000] [receive] via NET/Socket/0
pytorch-mingpt-nccl-worker-0:9:28 [0] NCCL INFO Channel 01/0 : 1[3000] -> 0[3000] [send] via NET/Socket/0
pytorch-mingpt-nccl-worker-0:9:28 [0] NCCL INFO Connected all trees
pytorch-mingpt-nccl-worker-0:9:28 [0] NCCL INFO threadThresholds 8/8/64 | 24/8/64 | 512 | 512
pytorch-mingpt-nccl-worker-0:9:28 [0] NCCL INFO 2 coll channels, 2 p2p channels, 2 p2p channels per peer
pytorch-mingpt-nccl-worker-0:9:28 [0] NCCL INFO comm 0x64942c0 rank 1 nranks 3 cudaDev 0 busId 3000 - Init COMPLETE
train
[GPU1] Epoch 1 | Iter 0 | Train Loss 4.18533
[2023-10-29 10:13:21,202][torch.nn.parallel.distributed][INFO] - Reducer buckets have been rebuilt in this iteration.
[GPU1] Epoch 1 | Iter 0 | Eval Loss 2.31698
pytorch-mingpt-nccl-worker-0:9:29 [0] NCCL INFO [Service thread] Connection closed by localRank 0
pytorch-mingpt-nccl-worker-0:9:9 [0] NCCL INFO comm 0x64942c0 rank 1 nranks 3 cudaDev 0 busId 3000 - Abort COMPLETE

(Epoch은 1로 수정하였습니다.)

 

 

마스터 노드의 로그에서 주요하게 살펴봐야할 점은 아래와 같습니다.

  • "Added key: store_based_barrier_key:1 to store for rank: 0""Rank 0: Completed store-based barrier for key:store_based_barrier_key:1 with 3 nodes." 메시지는 분산 학습 프로세스 간의 동기화 및 조정을 보여줍니다.
  • "Failed to open libibverbs.so[.1]"은 InfiniBand 네트워크 라이브러리 또는 디바이스를 사용하는 프로그램 또는 라이브러리에서 InfiniBand 인터넷 라이브러리 libibverbs를 찾지 못한 경우 발생합니다. InfiniBand 디바이스를 사용하지만, 일단 무시하도록 처리했습니다.
  • "Connected all rings""Connected all trees"는 GPU 노드 간의 통신이 성공적으로 연결되었음을 나타냅니다.

 

결론

이로써, 위 모든 과정을 통해 Kubeflow Training-Operator를 통해 PytorchJob이 어떻게 동작하고 어떤 설정을 해주며 학습 코드와 실행 커맨드는 어떻게 지정해줘야 하는가에 대해 자세히 살펴볼 수 있었고, LLM 학습을 위해 여러 실험환경을 제공할 수 있게 되었습니다.