...
- เข้าใจความแตกต่าง และวัตถุประสงค์การใช้งานเบื้องต้นของ frontend-node และ compute-node อ่านเพิ่มเติม
- สามารถรัน batch job บน HPC cluster โดยใช้คำสั่ง sbatch ตามด้วย Slurm script ได้ อ่านเพิ่มเติม
- ทำการติดตั้งและ activate conda environment ใน home หรือ project directory ของท่านบน HPC cluster ได้ อ่านเพิ่มเติม
- มีความเข้าใจในโปรแกรมที่ท่านกำลังใช้งานอยู่เป็นอย่างดี
เกริ่นนำ
โดยทั่วไปแล้วมีเพียงสองเหตุผลที่ทำให้เราต้องการใช้ multiple GPUs ในการเทรน neural networks:
...
Info |
---|
ตรวจสอบความหนาแน่นของคิวได้จากคำสั่ง |
Distributed Data-Parallel (DDP) ใน PyTorch
SPMD หรือที่ย่อมาจาก Single-Program, Multiple Data คือไอเดียที่ว่า “โมเดล” จะถูกทำสำเนาเก็บไว้ที่ GPUs ทุกตัวที่จะใช้ และข้อมูลที่จะใช้กับโมเดลดังกล่าวก็จะถูกแบ่งออกเป็นจำนวนเท่า ๆ กันสำหรับแต่ละ GPUs เมื่อ gradients ถูกคำนวนจากทุก GPUs แล้วจะนำมารวมกันเพื่อหาค่าเฉลี่ย และค่า weights ก็จะถูกอัพเดททั้งชุดผ่าน gradient all-reduced โดยที่กระบวนการนี้จะถูกทำซ้ำด้วย mini-batches ชุดใหม่ที่ถูกส่งเข้า GPUs แต่ละตัวอีกครั้งหนึ่ง
Note |
---|
เราควรใช้ DistributedDataParallel ซึ่งจากการใช้งานทราบกันโดยทั่วไปว่า efficient กว่า DataParallel แต่ถ้าใครอยากลองดูวิธีใช้ DataParallel เรามีให้ลองอ่านที่ multi-GPUs Training with PyTorch -- DataParallel |
Setup หลักเพื่อใช้ DDP PyTorch บน HPC
Setup ส่วนของ PyTorch
dist.init_process_group()
Code Block | ||
---|---|---|
| ||
def setup(rank, world_size): # initialize the process group dist.init_process_group("nccl", rank=rank, world_size=world_size) |
Code block นี้คือจุดสังเกตของการใช้งาน DDP โดยเป็นการสร้าง process group โดยมีฟังก์ชั่น dist.init_process_group() เป็นจุด check point ที่จะ “บล็อก” การทำงานเอาไว้เพื่อรอให้ processes ทั้งหมดที่กระจายกันไปได้ทำงานให้เสร็จกันก่อนที่นี่ (ดูการใช้งานภาพรวมได้ใน Code block ของ SimpleDDP.py ด้านล่าง)
ตัวอย่างของ single-gpu training
Code Block | ||
---|---|---|
| ||
model = Net().to(device) optimizer = optim.Adadelta(model.parameters(), lr=args.lr) |
ตัวอย่างของ multi-GPUs training with DDP
มีโมเดลที่ส่งให้ devices ตามจำนวน local_rank โดยที่โมเดลถูกส่งเข้า DDP อีกทอดหนึ่ง
...
นอกจากนี้เรายังต้องทำให้แน่ใจว่า แต่ละ batch จะถูกส่งไปที่ gpu ทุกตัวใน node
data.distributed.DistributedSampler & data.DataLoader
Code Block | ||
---|---|---|
| ||
train_sampler = torch.utils.data.distributed.DistributedSampler(\ dataset1, num_replicas=world_size, rank=rank) train_loader = torch.utils.data.DataLoader(\ dataset1, batch_size=args.batch_size, \ sampler=train_sampler, \ num_workers=int(os.environ["SLURM_CPUS_PER_TASK"]), \ pin_memory=True) |
Setup ส่วนของ HPC (Slurm configuration)
สำหรับ Slurm script ที่ใช้รันจริงดูได้จาก code block ที่แสดงให้ดูเป็นตัวอย่างที่สามารถทดลองทำซ้ำได้ เช่น SimpleScriptDDP.sh, Script-N-1-worldsize-8.sh เป็นต้น ใน code block ด้านล่างนี้คือ setup หลักที่อยากแสดงให้เห็นก่อน นั่นคือ การเลือก partition ที่จะใช้งาน (-p) การเลือกจำนวนโหนดที่ต้องการใช้งาน (-N) การเลือกจำนวน tasks หรือ process ที่ให้รันในหนึ่งโหนด (--ntasks-per-node) ซึ่งโดยทั่วไปเพื่อให้เกิดประสิทธิภาพการใช้งานสูงสุดเราจะเลือกใช้เต็มจำนวน gpus ที่มี และการเลือกจำนวน cpu ต่อ tasks หรือ process (--cpus-per-task) ซึ่งโดยปกติเราก็จะเลือกใช้เต็มจำนวน cpus ที่มี
...
Info |
---|
ใน TARA เราสามารถเรียกใช้ GPU ได้จากสาม partitions กล่าวคือ gpu, dgx และ dgx-preempt |
...
Examples
Simple DDP
โดยในตัวอย่างนี้ เราจะใช้งาน SimpleDDP.py คู่กับ SimpleScriptDDP.sh ในการส่งคำสั่งเข้า Slurm ผ่าน sbatch
หากเราโฟกัสที่ SimpleDDP.py เราจะพบว่า เป็นการใช้งาน Linear Regression Model ง่าย ๆ แต่เน้นไปที่การทำความเข้าใจกับ parameter เพิ่มเติมอย่าง rank, world_size, gpus_per_node, local_rank เป็นต้น
SimpleDDP.py
Code Block | ||
---|---|---|
| ||
import os import torch import torch.nn as nn import torch.nn.functional as F import torch.distributed as dist from torch.nn.parallel import DistributedDataParallel as DDP from socket import gethostname class Net(nn.Module): def __init__(self): super(Net, self).__init__() self.fc = nn.Linear(42, 3) def forward(self, x): x = self.fc(x) x = F.relu(x) output = F.log_softmax(x, dim=1) return output rank = int(os.environ["SLURM_PROCID"]) world_size = int(os.environ["WORLD_SIZE"]) gpus_per_node = int(os.environ["SLURM_GPUS_ON_NODE"]) assert gpus_per_node == torch.cuda.device_count() print(f"Hello from rank {rank} of {world_size} on {gethostname()} where there are" \ f" {gpus_per_node} allocated GPUs per node.", flush=True) dist.init_process_group("nccl", rank=rank, world_size=world_size) if rank == 0: print(f"Group initialized? {dist.is_initialized()}", flush=True) local_rank = rank - gpus_per_node * (rank // gpus_per_node) torch.cuda.set_device(local_rank) model = Net().to(local_rank) ddp_model = DDP(model, device_ids=[local_rank]) ddp_model.eval() with torch.no_grad(): data = torch.rand(1, 42) data = data.to(local_rank) output = ddp_model(data) print(f"host: {gethostname()}, rank: {rank}, output: {output}") dist.destroy_process_group() |
rank, world_size, gpus_per_node, local_rank
Code Block | ||
---|---|---|
| ||
rank = int(os.environ["SLURM_PROCID"]) world_size = int(os.environ["WORLD_SIZE"]) gpus_per_node = int(os.environ["SLURM_GPUS_ON_NODE"]) ... local_rank = rank - gpus_per_node * (rank // gpus_per_node) torch.cuda.set_device(local_rank) |
...
อย่างไรก็ดี ค่าเหล่านี้จำต้องได้รับการเซ็ตจาก Slurm environment เอง หากมีการเซ็ตไว้โดยแอดมินของ HPC cluster หรือเราสามารถ export เองได้ใน launch script ดังแสดงในตัวอย่างสคริปต์ SimpleScriptDDP.sh
Output SimpleDDP
Code Block | ||||
---|---|---|---|---|
| ||||
WORLD_SIZE=8 MASTER_PORT=15334 MASTER_ADDR=tara-dgx1-002 Hello from rank 6 of 8 on tara-dgx1-002.tara.nstda.or.th where there are 8 allocated GPUs per node. Hello from rank 5 of 8 on tara-dgx1-002.tara.nstda.or.th where there are 8 allocated GPUs per node. Hello from rank 4 of 8 on tara-dgx1-002.tara.nstda.or.th where there are 8 allocated GPUs per node. Hello from rank 7 of 8 on tara-dgx1-002.tara.nstda.or.th where there are 8 allocated GPUs per node. Hello from rank 3 of 8 on tara-dgx1-002.tara.nstda.or.th where there are 8 allocated GPUs per node. Hello from rank 0 of 8 on tara-dgx1-002.tara.nstda.or.th where there are 8 allocated GPUs per node. Hello from rank 1 of 8 on tara-dgx1-002.tara.nstda.or.th where there are 8 allocated GPUs per node. Hello from rank 2 of 8 on tara-dgx1-002.tara.nstda.or.th where there are 8 allocated GPUs per node. Group initialized? True host: tara-dgx1-002.tara.nstda.or.th, rank: 6, output: tensor([[-1.2452, -1.0438, -1.0216]], device='cuda:6') host: tara-dgx1-002.tara.nstda.or.th, rank: 4, output: tensor([[-1.2201, -0.9631, -1.1298]], device='cuda:4') host: tara-dgx1-002.tara.nstda.or.th, rank: 5, output: tensor([[-1.2976, -0.8704, -1.1775]], device='cuda:5') host: tara-dgx1-002.tara.nstda.or.th, rank: 2, output: tensor([[-1.2821, -1.0588, -0.9790]], device='cuda:2') host: tara-dgx1-002.tara.nstda.or.th, rank: 1, output: tensor([[-1.1647, -0.9979, -1.1416]], device='cuda:1') host: tara-dgx1-002.tara.nstda.or.th, rank: 0, output: tensor([[-1.1372, -1.0257, -1.1372]], device='cuda:0') host: tara-dgx1-002.tara.nstda.or.th, rank: 3, output: tensor([[-1.2476, -1.0863, -0.9799]], device='cuda:3') host: tara-dgx1-002.tara.nstda.or.th, rank: 7, output: tensor([[-1.1277, -1.0428, -1.1277]], device='cuda:7') |
SimpleScriptDDP.sh
Code Block | ||
---|---|---|
| ||
#!/bin/bash #SBATCH -A thaisc # account of your project #SBATCH -J SG-torch # create a short name for your job #SBATCH -p dgx-preempt # your choice of partition #SBATCH -N 1 # node count #SBATCH --ntasks-per-node=8 # total number of tasks per node #SBATCH --time=00:05:00 # total run time limit (HH:MM:SS) #SBATCH --mail-type=ALL # send email when job begins, ends #SBATCH --mail-user=me@myorg.org export MASTER_PORT=$(expr 10000 + $(echo -n $SLURM_JOBID | tail -c 4)) echo "MASTER_PORT="$MASTER_PORT export WORLD_SIZE=8 # ควรได้มาจาก $(($SLURM_NNODES * $SLURM_NTASKS_PER_NODE)) echo "WORLD_SIZE="$WORLD_SIZE master_addr=$(scontrol show hostnames "$SLURM_JOB_NODELIST" | head -n 1) export MASTER_ADDR=$master_addr echo "MASTER_ADDR="$MASTER_ADDR module purge module load cuDNN/8.0.5.39-CUDA-11.1.1 source ~/.bashrc myconda conda activate condapy37 srun python simpleDDP.py |
...
Table of Contents | ||||||||
---|---|---|---|---|---|---|---|---|
|
...
MNIST DDP
โดยในตัวอย่างนี้เป็นการแปลง python code แต่เดิมของ MNIST ที่เป็น single gpu ให้เป็น DDP
DDP.py
Code Block | ||||
---|---|---|---|---|
| ||||
import argparse import torch import torch.nn as nn import torch.nn.functional as F import torch.optim as optim from torchvision import datasets, transforms from torch.optim.lr_scheduler import StepLR import os import torch.distributed as dist from torch.nn.parallel import DistributedDataParallel as DDP from socket import gethostname class Net(nn.Module): def __init__(self): super(Net, self).__init__() self.conv1 = nn.Conv2d(1, 32, 3, 1) self.conv2 = nn.Conv2d(32, 64, 3, 1) self.dropout1 = nn.Dropout(0.25) self.dropout2 = nn.Dropout(0.5) self.fc1 = nn.Linear(9216, 128) self.fc2 = nn.Linear(128, 10) def forward(self, x): x = self.conv1(x) x = F.relu(x) x = self.conv2(x) x = F.relu(x) x = F.max_pool2d(x, 2) x = self.dropout1(x) x = torch.flatten(x, 1) x = self.fc1(x) x = F.relu(x) x = self.dropout2(x) x = self.fc2(x) output = F.log_softmax(x, dim=1) return output def train(args, model, device, train_loader, optimizer, epoch): model.train() for batch_idx, (data, target) in enumerate(train_loader): data, target = data.to(device), target.to(device) optimizer.zero_grad() output = model(data) loss = F.nll_loss(output, target) loss.backward() optimizer.step() if batch_idx % args.log_interval == 0: print('Train Epoch: {} [{}/{} ({:.0f}%)]\tLoss: {:.6f}'.format( epoch, batch_idx * len(data), len(train_loader.dataset), 100. * batch_idx / len(train_loader), loss.item())) if args.dry_run: break def test(model, device, test_loader): model.eval() test_loss = 0 correct = 0 with torch.no_grad(): for data, target in test_loader: data, target = data.to(device), target.to(device) output = model(data) test_loss += F.nll_loss(output, target, reduction='sum').item() # sum up batch loss pred = output.argmax(dim=1, keepdim=True) # get the index of the max log-probability correct += pred.eq(target.view_as(pred)).sum().item() test_loss /= len(test_loader.dataset) print('\nTest set: Average loss: {:.4f}, Accuracy: {}/{} ({:.0f}%)\n'.format( test_loss, correct, len(test_loader.dataset), 100. * correct / len(test_loader.dataset))) def setup(rank, world_size): # initialize the process group dist.init_process_group("nccl", rank=rank, world_size=world_size) def main(): # Training settings parser = argparse.ArgumentParser(description='PyTorch MNIST Example') parser.add_argument('--batch-size', type=int, default=64, metavar='N', help='input batch size for training (default: 64)') parser.add_argument('--test-batch-size', type=int, default=1000, metavar='N', help='input batch size for testing (default: 1000)') parser.add_argument('--epochs', type=int, default=14, metavar='N', help='number of epochs to train (default: 14)') parser.add_argument('--lr', type=float, default=1.0, metavar='LR', help='learning rate (default: 1.0)') parser.add_argument('--gamma', type=float, default=0.7, metavar='M', help='Learning rate step gamma (default: 0.7)') parser.add_argument('--no-cuda', action='store_true', default=False, help='disables CUDA training') parser.add_argument('--dry-run', action='store_true', default=False, help='quickly check a single pass') parser.add_argument('--seed', type=int, default=1, metavar='S', help='random seed (default: 1)') parser.add_argument('--log-interval', type=int, default=10, metavar='N', help='how many batches to wait before logging training status') parser.add_argument('--save-model', action='store_true', default=False, help='For Saving the current Model') args = parser.parse_args() use_cuda = not args.no_cuda and torch.cuda.is_available() torch.manual_seed(args.seed) train_kwargs = {'batch_size': args.batch_size} test_kwargs = {'batch_size': args.test_batch_size} if use_cuda: cuda_kwargs = {'num_workers': int(os.environ["SLURM_CPUS_PER_TASK"]), 'pin_memory': True, 'shuffle': True} train_kwargs.update(cuda_kwargs) test_kwargs.update(cuda_kwargs) transform=transforms.Compose([ transforms.ToTensor(), transforms.Normalize((0.1307,), (0.3081,)) ]) dataset1 = datasets.MNIST('data', train=True, download=False, transform=transform) dataset2 = datasets.MNIST('data', train=False, transform=transform) world_size = int(os.environ["WORLD_SIZE"]) rank = int(os.environ["SLURM_PROCID"]) gpus_per_node = int(os.environ["SLURM_GPUS_ON_NODE"]) assert gpus_per_node == torch.cuda.device_count() print(f"Hello from rank {rank} of {world_size} on {gethostname()} where there are" \ f" {gpus_per_node} allocated GPUs per node.", flush=True) setup(rank, world_size) if rank == 0: print(f"Group initialized? {dist.is_initialized()}", flush=True) local_rank = rank - gpus_per_node * (rank // gpus_per_node) torch.cuda.set_device(local_rank) print(f"host: {gethostname()}, rank: {rank}, local_rank: {local_rank}") train_sampler = torch.utils.data.distributed.DistributedSampler(dataset1, num_replicas=world_size, rank=rank) train_loader = torch.utils.data.DataLoader(dataset1, batch_size=args.batch_size, sampler=train_sampler, \ num_workers=int(os.environ["SLURM_CPUS_PER_TASK"]), pin_memory=True) test_loader = torch.utils.data.DataLoader(dataset2, **test_kwargs) model = Net().to(local_rank) ddp_model = DDP(model, device_ids=[local_rank]) optimizer = optim.Adadelta(ddp_model.parameters(), lr=args.lr) scheduler = StepLR(optimizer, step_size=1, gamma=args.gamma) for epoch in range(1, args.epochs + 1): train(args, ddp_model, local_rank, train_loader, optimizer, epoch) if rank == 0: test(ddp_model, local_rank, test_loader) scheduler.step() if args.save_model and rank == 0: torch.save(model.state_dict(), "mnist_cnn.pt") dist.destroy_process_group() if __name__ == '__main__': main() |
--cpus-per-task หรือ SLURM_CPUS_PER_TASK
ซึ่ง Python script DDP.py ดังกล่าวข้างต้นถูกใช้งานร่วมกับ script.sh ดังแสดงด้านล่างนี้ โดยมี 1 บันทัดที่เพิ่มเติมขึ้นมาสำหรับ Data Loader นั่นคือ
...
ซึ่งการกำหนด --cpus-per-task นั้นต้องคำนึงถึงจำนวน cpu ที่มีทั้งหมดของ node หารด้วยจำนวน ntasks-per-node ที่เซ็ตไว้ซึ่งทั้ง partition dgx และ gpu มีจำนวน CPUs เท่ากันที่ 40 CPUs ดังนั้นสำหรับ dgx ค่า cpus-per-task จึงกำหนดได้เป็น 5 และสำหรับ partition gpu ค่า cpus-per-task จึงกำหนดได้เป็น 20
script-N-1-worldsize-8.sh
Code Block | ||
---|---|---|
| ||
#!/bin/bash #SBATCH -A thaisc # account of your project #SBATCH -J SG-torch # create a short name for your job #SBATCH -p dgx-preempt # your choice of partition #SBATCH -N 1 # node count #SBATCH --ntasks-per-node=8 # total number of tasks per node #SBATCH --cpus-per-task=5 # number of cpu per task (5x8=40 cpus) #SBATCH --time=00:05:00 # total run time limit (HH:MM:SS) #SBATCH --mail-type=ALL # send email when job begins, ends #SBATCH --mail-user=me@myorg.org export MASTER_PORT=$(expr 10000 + $(echo -n $SLURM_JOBID | tail -c 4)) echo "MASTER_PORT="$MASTER_PORT export WORLD_SIZE=8 # ควรได้มาจาก $(($SLURM_NNODES * $SLURM_NTASKS_PER_NODE)) echo "WORLD_SIZE="$WORLD_SIZE master_addr=$(scontrol show hostnames "$SLURM_JOB_NODELIST" | head -n 1) export MASTER_ADDR=$master_addr echo "MASTER_ADDR="$MASTER_ADDR module purge module load cuDNN/8.0.5.39-CUDA-11.1.1 source ~/.bashrc myconda conda activate condapy37 srun python DDP.py |
Output DDP กับ 1 Node DGX-1 (8 GPUs)
ผลลัพธ์ของ script-N-1-worldsize-8.sh และ DDP.py แสดงได้ดังต่อไปนี้
Code Block | ||||
---|---|---|---|---|
| ||||
WORLD_SIZE=8 MASTER_PORT=15347 MASTER_ADDR=tara-dgx1-002 Hello from rank 1 of 8 on tara-dgx1-002.tara.nstda.or.th where there are 8 allocated GPUs per node. Hello from rank 3 of 8 on tara-dgx1-002.tara.nstda.or.th where there are 8 allocated GPUs per node. Hello from rank 0 of 8 on tara-dgx1-002.tara.nstda.or.th where there are 8 allocated GPUs per node. Hello from rank 2 of 8 on tara-dgx1-002.tara.nstda.or.th where there are 8 allocated GPUs per node. Hello from rank 5 of 8 on tara-dgx1-002.tara.nstda.or.th where there are 8 allocated GPUs per node. Hello from rank 4 of 8 on tara-dgx1-002.tara.nstda.or.th where there are 8 allocated GPUs per node. Hello from rank 6 of 8 on tara-dgx1-002.tara.nstda.or.th where there are 8 allocated GPUs per node. Hello from rank 7 of 8 on tara-dgx1-002.tara.nstda.or.th where there are 8 allocated GPUs per node. Group initialized? True host: tara-dgx1-002.tara.nstda.or.th, rank: 1, local_rank: 1 host: tara-dgx1-002.tara.nstda.or.th, rank: 3, local_rank: 3 host: tara-dgx1-002.tara.nstda.or.th, rank: 4, local_rank: 4 host: tara-dgx1-002.tara.nstda.or.th, rank: 6, local_rank: 6 host: tara-dgx1-002.tara.nstda.or.th, rank: 0, local_rank: 0 host: tara-dgx1-002.tara.nstda.or.th, rank: 2, local_rank: 2 host: tara-dgx1-002.tara.nstda.or.th, rank: 5, local_rank: 5 host: tara-dgx1-002.tara.nstda.or.th, rank: 7, local_rank: 7 Train Epoch: 1 [0/60000 (0%)] Loss: 2.297117 Train Epoch: 1 [640/60000 (8%)] Loss: 1.329343 Train Epoch: 1 [1280/60000 (17%)] Loss: 0.518520 Train Epoch: 1 [1920/60000 (25%)] Loss: 0.331641 Train Epoch: 1 [2560/60000 (34%)] Loss: 0.256029 Train Epoch: 1 [3200/60000 (42%)] Loss: 0.126544 Train Epoch: 1 [3840/60000 (51%)] Loss: 0.129393 Train Epoch: 1 [4480/60000 (59%)] Loss: 0.135831 Train Epoch: 1 [5120/60000 (68%)] Loss: 0.094554 Train Epoch: 1 [5760/60000 (76%)] Loss: 0.131771 Train Epoch: 1 [6400/60000 (85%)] Loss: 0.078105 Train Epoch: 1 [7040/60000 (93%)] Loss: 0.078772 Train Epoch: 1 [0/60000 (0%)] Loss: 2.315368 Train Epoch: 1 [640/60000 (8%)] Loss: 1.471632 Train Epoch: 1 [1280/60000 (17%)] Loss: 0.394169 Train Epoch: 1 [1920/60000 (25%)] Loss: 0.376319 ... Train Epoch: 14 [5120/60000 (68%)] Loss: 0.003920 Train Epoch: 14 [5760/60000 (76%)] Loss: 0.105166 Train Epoch: 14 [6400/60000 (85%)] Loss: 0.020963 Train Epoch: 14 [7040/60000 (93%)] Loss: 0.071237 Test set: Average loss: 0.0298, Accuracy: 9897/10000 (99%) |
Output DDP กับ 2 Node DGX-1 (16 GPUs)
ผลลัพธ์ของ script-N-2-worldsize-16.sh และ DDP.py แสดงได้ดังต่อไปนี้
Code Block | ||
---|---|---|
| ||
WORLD_SIZE=16 MASTER_PORT=17117 MASTER_ADDR=tara-dgx1-002 Hello from rank 12 of 16 on tara-dgx1-003.tara.nstda.or.th where there are 8 allocated GPUs per node. Hello from rank 13 of 16 on tara-dgx1-003.tara.nstda.or.th where there are 8 allocated GPUs per node. Hello from rank 15 of 16 on tara-dgx1-003.tara.nstda.or.th where there are 8 allocated GPUs per node. Hello from rank 8 of 16 on tara-dgx1-003.tara.nstda.or.th where there are 8 allocated GPUs per node. Hello from rank 9 of 16 on tara-dgx1-003.tara.nstda.or.th where there are 8 allocated GPUs per node. Hello from rank 10 of 16 on tara-dgx1-003.tara.nstda.or.th where there are 8 allocated GPUs per node. Hello from rank 11 of 16 on tara-dgx1-003.tara.nstda.or.th where there are 8 allocated GPUs per node. Hello from rank 14 of 16 on tara-dgx1-003.tara.nstda.or.th where there are 8 allocated GPUs per node. Hello from rank 4 of 16 on tara-dgx1-002.tara.nstda.or.th where there are 8 allocated GPUs per node. Hello from rank 5 of 16 on tara-dgx1-002.tara.nstda.or.th where there are 8 allocated GPUs per node. Hello from rank 6 of 16 on tara-dgx1-002.tara.nstda.or.th where there are 8 allocated GPUs per node. Hello from rank 7 of 16 on tara-dgx1-002.tara.nstda.or.th where there are 8 allocated GPUs per node. Hello from rank 0 of 16 on tara-dgx1-002.tara.nstda.or.th where there are 8 allocated GPUs per node. Hello from rank 1 of 16 on tara-dgx1-002.tara.nstda.or.th where there are 8 allocated GPUs per node. Hello from rank 2 of 16 on tara-dgx1-002.tara.nstda.or.th where there are 8 allocated GPUs per node. Hello from rank 3 of 16 on tara-dgx1-002.tara.nstda.or.th where there are 8 allocated GPUs per node. Group initialized? True host: tara-dgx1-002.tara.nstda.or.th, rank: 4, local_rank: 4 host: tara-dgx1-002.tara.nstda.or.th, rank: 6, local_rank: 6 host: tara-dgx1-002.tara.nstda.or.th, rank: 0, local_rank: 0 host: tara-dgx1-002.tara.nstda.or.th, rank: 2, local_rank: 2 host: tara-dgx1-002.tara.nstda.or.th, rank: 5, local_rank: 5 host: tara-dgx1-002.tara.nstda.or.th, rank: 7, local_rank: 7 host: tara-dgx1-002.tara.nstda.or.th, rank: 1, local_rank: 1 host: tara-dgx1-002.tara.nstda.or.th, rank: 3, local_rank: 3 host: tara-dgx1-003.tara.nstda.or.th, rank: 12, local_rank: 4 host: tara-dgx1-003.tara.nstda.or.th, rank: 13, local_rank: 5 host: tara-dgx1-003.tara.nstda.or.th, rank: 15, local_rank: 7 host: tara-dgx1-003.tara.nstda.or.th, rank: 8, local_rank: 0 host: tara-dgx1-003.tara.nstda.or.th, rank: 9, local_rank: 1 host: tara-dgx1-003.tara.nstda.or.th, rank: 10, local_rank: 2 host: tara-dgx1-003.tara.nstda.or.th, rank: 11, local_rank: 3 host: tara-dgx1-003.tara.nstda.or.th, rank: 14, local_rank: 6 Train Epoch: 1 [0/60000 (0%)] Loss: 2.288294 Train Epoch: 1 [640/60000 (17%)] Loss: 1.147667 Train Epoch: 1 [1280/60000 (34%)] Loss: 0.518015 Train Epoch: 1 [1920/60000 (51%)] Loss: 0.241061 Train Epoch: 1 [2560/60000 (68%)] Loss: 0.159519 Train Epoch: 1 [3200/60000 (85%)] Loss: 0.387764 Train Epoch: 1 [0/60000 (0%)] Loss: 2.306850 Train Epoch: 1 [640/60000 (17%)] Loss: 1.047796 ... |
การติดตั้งอื่น ๆ ที่จำเป็น
อย่าลืม Pre-download ข้อมูลที่ frontend-node ก่อนรัน
Code Block | ||
---|---|---|
| ||
$ conda activate condapy37 (condapy37)$ python Python 3.7.13 (default, Mar 29 2022, 02:18:16) [GCC 7.5.0] :: Anaconda, Inc. on linux Type "help", "copyright", "credits" or "license" for more information. >>> import torch >>> from torchvision import datasets, transforms >>> transform=transforms.Compose([ transforms.ToTensor(), transforms.Normalize((0.1307,), (0.3081,)) ]) >>> dataset1 = datasets.MNIST('data', train=True, download=True, transform=transform) Downloading http://yann.lecun.com/exdb/mnist/train-images-idx3-ubyte.gz Downloading http://yann.lecun.com/exdb/mnist/train-images-idx3-ubyte.gz to data/MNIST/raw/train-images-idx3-ubyte.gz 9913344it [00:01, 5744180.56it/s] Extracting data/MNIST/raw/train-images-idx3-ubyte.gz to data/MNIST/raw Downloading http://yann.lecun.com/exdb/mnist/train-labels-idx1-ubyte.gz Downloading http://yann.lecun.com/exdb/mnist/train-labels-idx1-ubyte.gz to data/MNIST/raw/train-labels-idx1-ubyte.gz 29696it [00:00, 1196864.05it/s] Extracting data/MNIST/raw/train-labels-idx1-ubyte.gz to data/MNIST/raw Downloading http://yann.lecun.com/exdb/mnist/t10k-images-idx3-ubyte.gz Downloading http://yann.lecun.com/exdb/mnist/t10k-images-idx3-ubyte.gz to data/MNIST/raw/t10k-images-idx3-ubyte.gz 1649664it [00:00, 2396549.23it/s] Extracting data/MNIST/raw/t10k-images-idx3-ubyte.gz to data/MNIST/raw Downloading http://yann.lecun.com/exdb/mnist/t10k-labels-idx1-ubyte.gz Downloading http://yann.lecun.com/exdb/mnist/t10k-labels-idx1-ubyte.gz to data/MNIST/raw/t10k-labels-idx1-ubyte.gz 5120it [00:00, 23165950.90it/s] Extracting data/MNIST/raw/t10k-labels-idx1-ubyte.gz to data/MNIST/raw >>> exit() |
ซึ่งตอนนี้ข้อมูล MNIST จาก datasets ที่ torch มีให้ก็ถูก download ลงมาที่ /data/MNIST/raw บนเครื่องที่เรารันเรียบร้อย
Further Reading
Scaling Analysis เพื่อหา the optimal number of GPUs: https://researchcomputing.princeton.edu/support/knowledge-base/scaling-analysis
...
PyTorch Data Loader: https://pytorch.org/docs/stable/data.html
Acknowledgment
Thank you for the very nice resources from Princeton University, as this article is written based on your work.
...