ในบทความนี้จะกล่าวถึงตัวอย่างการติดตั้ง Python package ที่เกี่ยวข้องกับ PyTorch และการทดสอบรันงานบน gpu node ของ TARA HPC โดยใช้ตัวอย่าง Package modules อย่างง่ายจากการทำ 3D-Deep Learning ด้วย PyTorch
ตัวอย่างโปรแกรม multi-GPUs PyTorch เบื้องต้น
ผู้ใช้งานสามารถ copy ไฟล์โปรแกรมตัวอย่างที่ใช้ในบทความนี้ได้ในระบบ TARA ที่
/tarafs/data/project/common/AI/examples/basic-multigpu-pytorch.py
import PyTorch modules และกำหนด parameters
import torch import torch.nn as nn from torch.utils.data import Dataset, DataLoader # Parameters and DataLoaders input_size = 5 output_size = 2 batch_size = 30 data_size = 100
สร้าง dummy dataset ขึ้นมาด้วยการ random
class RandomDataset(Dataset): def __init__(self, size, length): self.len = length self.data = torch.randn(length, size) def __getitem__(self, index): return self.data[index] def __len__(self): return self.len rand_loader = DataLoader(dataset=RandomDataset(input_size, data_size), batch_size=batch_size, shuffle=True)
สร้าง simple model
ที่แค่รับ input แล้วทำ linear operation แล้วส่งออก output เพื่อแสดงการทำงานของ DataParallel
ซึ่งก็คือส่วนที่ทำให้เกิดการใช้งาน multi-GPUs ได้ด้วยการแบ่งข้อมูลออกไปที่แต่ละ GPUs
โดยในโปรแกรม เราอยากเห็นขนาดของ input/output tensors จึงได้ให้พิมพ์ออกมา
class Model(nn.Module): # Our model def __init__(self, input_size, output_size): super(Model, self).__init__() self.fc = nn.Linear(input_size, output_size) def forward(self, input): output = self.fc(input) print("\tIn Model: input size", input.size(), "output size", output.size()) return output
สร้าง object device
ซึ่งจะเป็นจุดที่เราส่ง tensor/model ให้กับ device ที่เรามีเพื่อการทำงาน
โดยในตัวอย่างด้านล่าง เราจะตรวจสอบว่ามี cuda ในเครื่องหรือไม่ หากไม่มีจะสั่งให้ device ใช้งาน cpu
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
สร้าง model instance และการรันงานแบบ nn.DataParallel
โดยจุดที่เราสนใจเป็นพิเศษในบทความนี้คือ หากในเครื่องที่เราได้รันงานมี GPUs หลายตัว เราจะสามารถ wrap model ของเราด้วย nn.DataParallel
ได้ จากนั้นจึงนำ model ที่ได้ส่งให้กับ device ที่เรากำหนดไว้ก่อนหน้านี้
model = Model(input_size, output_size) if torch.cuda.device_count() > 1: print("Let's use", torch.cuda.device_count(), "GPUs!") # dim = 0 [30, xxx] -> [10, ...], [10, ...], [10, ...] on 3 GPUs model = nn.DataParallel(model) model.to(device)
ในขั้นตอนนี้ถ้าหากเรามี GPU มากกว่าหนึ่งตัว เช่น gpu node ในระบบ TARA ก็จะได้ผลลัพธ์ออกมาเป็น
Let’s use 2 GPUs!
run the model
สั่งให้พิมพ์ขนาดของ input/output tensors ออกมาให้ดูด้วย
for data in rand_loader: input = data.to(device) output = model(input) print("Outside: input size", input.size(), "output_size", output.size())
cpu node
ดังนั้นหาก machine ที่เราใช้ไม่มี GPU เช่น
รันใน compute node (tara-c-001
) บนระบบ TARA จะได้ output ดังแสดงด้านล่าง ซึ่งจะพบว่าไม่มีการแบ่งข้อมูลออกไปเนื่องจากไม่มี GPU และเป็นการรันงานบน cpu
[apiyatum@tara-c-001 segmed]$ python basic-multigpu-pytorch.py torch version : 1.9.0+cu102 cuda available? : False cuda version: 10.2 cuda device count: 0 cuda device id: In Model: input size torch.Size([30, 5]) output size torch.Size([30, 2]) Outside: input size torch.Size([30, 5]) output_size torch.Size([30, 2]) In Model: input size torch.Size([30, 5]) output size torch.Size([30, 2]) Outside: input size torch.Size([30, 5]) output_size torch.Size([30, 2]) In Model: input size torch.Size([30, 5]) output size torch.Size([30, 2]) Outside: input size torch.Size([30, 5]) output_size torch.Size([30, 2]) In Model: input size torch.Size([10, 5]) output size torch.Size([10, 2]) Outside: input size torch.Size([10, 5]) output_size torch.Size([10, 2]) [apiyatum@tara-c-001 segmed]$
gpu node
และหากเครื่องที่ได้ใช้มี GPUs เช่น รันใน gpu node บนระบบ TARA (tara-g-001
) จะได้ output แบบนี้
(venv-3DDL) [apiyatum@tara-g-001 segmed]$ python basic-multigpu-pytorch.py torch version : 1.9.0+cu102 cuda available? : True cuda version: 10.2 cuda device count: 2 cuda device id: , 0, 1 Let's use 2 GPUs! In Model: input size torch.Size([15, 5]) output size torch.Size([15, 2]) In Model: input size torch.Size([15, 5]) output size torch.Size([15, 2]) Outside: input size torch.Size([30, 5]) output_size torch.Size([30, 2]) In Model: input size torch.Size([15, 5]) output size torch.Size([15, 2]) In Model: input size torch.Size([15, 5]) output size torch.Size([15, 2]) Outside: input size torch.Size([30, 5]) output_size torch.Size([30, 2]) In Model: input size torch.Size([15, 5]) output size torch.Size([15, 2]) In Model: input size torch.Size([15, 5]) output size torch.Size([15, 2]) Outside: input size torch.Size([30, 5]) output_size torch.Size([30, 2]) In Model: input size torch.Size([5, 5]) output size torch.Size([5, 2]) In Model: input size torch.Size([5, 5]) output size torch.Size([5, 2]) Outside: input size torch.Size([10, 5]) output_size torch.Size([10, 2]) (venv-3DDL) [apiyatum@tara-g-001 segmed]$
ซึ่งจะเห็นได้ว่ามีการแบ่งข้อมูลไปรันโมเดลที่ GPUs ทั้งสองตัว
ติดตั้ง PyTorch package
สมมุติว่าท่านต้องการเรียกใช้ library ดังแสดงใน header ของโปรแกรม python หนึ่งที่เรียกใช้ torch3D torchvision torchsummary
import numpy as np import torch import torch.nn as nn import torchvision from torchvision import models from torchsummary import summary
และท่านมี requirements-3DDL.txt ที่ได้จากการ pip freeze จากเครื่อง development ของท่าน ดังนี้
$ cat requirements-3DDL.txt cycler==0.10.0 fvcore==0.1.5.post20210812 imageio==2.9.0 iopath==0.1.9 kiwisolver==1.3.1 matplotlib==3.4.3 networkx==2.6.2 numpy==1.21.2 opencv-python==4.5.3.56 Pillow==8.3.1 plotly==5.2.1 portalocker==2.3.0 pyparsing==2.4.7 python-dateutil==2.8.2 PyTorch3d==0.5.0 PyWavelets==1.1.1 PyYAML==5.4.1 scikit-image==0.18.2 scipy==1.7.1 six==1.16.0 tabulate==0.8.9 tenacity==8.0.1 termcolor==1.1.0 tifffile==2021.8.8 torch==1.9.0 torchaudio==0.9.0 torchsummary==1.5.1 torchvision==0.10.0 tqdm==4.62.1 typing-extensions==3.10.0.0 yacs==0.1.8
ท่านสามารถติดตั้ง library ดังกล่าวได้บน project home directory ของท่านบน TARA HPC ใน Virtual Environment (source activate) ของท่าน หรือติดตั้งใน Singularity container เพื่อการใช้งานบน TARA HPC ได้
ติดตั้ง Package module ที่ต้องการใน virtualenv และทดสอบใช้งาน
อย่าลืม load module ซอฟแวร์ที่ต้องการใช้งานก่อนเริ่มทำงานใน TARA
สร้าง Virtual Environment ใน TARA
ในตัวอย่างด้านล่าง ได้ทำการ module load Python แล้วสร้าง virtualenv ไว้ในโฟลเดอร์ venv-3DDL จากนั้น activate virtualenv ที่สร้างขึ้นด้วยคำสั่ง source venv-3DDL/bin/activate
แล้วจึงเริ่มทำการติดตั้ง Package ที่ต้องการ เช่น pip install -r requirement.txt
หรือ การติดตั้ง Package แบบระบุที่อยู่
[apiyatum@tara-frontend-1 segmed]$ module load Python [apiyatum@tara-frontend-1 segmed]$ virtualenv venv-3DDL ... [apiyatum@tara-frontend-1 segmed]$ source venv-3DDL/bin/activate (venv-3DDL) [apiyatum@tara-frontend-1 segmed]$ pip list Package Version ---------- ------- pip 21.1.3 setuptools 57.4.0 wheel 0.36.2 (venv-3DDL) [apiyatum@tara-frontend-1 segmed]$ pip install -r requirements-3DDL.txt ... ERROR: Could not find a version that satisfies the requirement PyTorch3d==0.5.0 (from versions: 0.0.1) ERROR: No matching distribution found for PyTorch3d==0.5.0
ERROR: Could not find a version that satisfies the requirement PyTorch3d==0.5.0 (from versions: 0.0.1)
ERROR: No matching distribution found for PyTorch3d==0.5.0
จาก error ข้างต้นและผลการค้นบนอินเตอร์เน็ต (อ่านเพิ่มเติม) ทำให้ติดตั้ง specific version ของ PyTorch3D ที่เหมาะสมกับเวอร์ชั่นของ Python, cuda, และ PyTorch ที่กำลังใช้งานอยู่บนระบบ TARA ได้ดังนี้
(venv-3DDL) [apiyatum@tara-frontend-1 segmed]$ pip install pytorch3d \ -f https://dl.fbaipublicfiles.com/pytorch3d/packaging/wheels/py39_cu102_pyt190/download.html
ทดสอบใช้งานด้วย sinteract
sinteract -p gpu
หลังจากติดตั้งเสร็จสิ้นจึงทดสอบโปรแกรมที่ต้องการรันต่อ $ python basic-multigpu-pytorch.py
โดยในกรณีนี้เราต้องการใช้งาน gpu จึงจองทรัพยากรแบบ sinteract
เพื่อทำการทดสอบโปรแกรมเบื้องต้น
[apiyatum@tara-frontend-1 segmed]$ sinteract -p gpu No account specified. Please select account to charge from: [1] pre0005 ... [6] thaisc [q] Quit Please type a selection: 6 Running interactive job using thaisc account srun: job 1314560 queued and waiting for resources srun: job 1314560 has been allocated resources [apiyatum@tara-g-001 segmed]$
ซึ่งจะสังเกตเห็นว่าตอนนี้เราสามารถจองทรัพยากรสำเร็จและได้เปลี่ยนจาก frontend-1 node มาอยู่บน tara-g-001 หรือ gpu node เบอร์ 001 แล้วนั่นเอง
output
จากนั้นเรียกใช้งาน virtual environment ที่เราต้องการ แล้วทดสอบโปรแกรมกับ package ต่างๆที่ได้ติดตั้งไว้แล้วใน vitualenv
[apiyatum@tara-g-001 segmed]$ source venv-3DDL/bin/activate (venv-3DDL) [apiyatum@tara-g-001 segmed]$ python basic-multigpu-pytorch.py torch version : 1.9.0+cu102 cuda available? : True cuda version: 10.2 cuda device count: 2 cuda device id: , 0, 1 Let's use 2 GPUs! In Model: input size torch.Size([15, 5]) output size torch.Size([15, 2]) In Model: input size torch.Size([15, 5]) output size torch.Size([15, 2]) Outside: input size torch.Size([30, 5]) output_size torch.Size([30, 2]) In Model: input size torch.Size([15, 5]) output size torch.Size([15, 2]) In Model: input size torch.Size([15, 5]) output size torch.Size([15, 2]) Outside: input size torch.Size([30, 5]) output_size torch.Size([30, 2]) In Model: input size torch.Size([15, 5]) output size torch.Size([15, 2]) In Model: input size torch.Size([15, 5]) output size torch.Size([15, 2]) Outside: input size torch.Size([30, 5]) output_size torch.Size([30, 2]) In Model: input size torch.Size([5, 5]) output size torch.Size([5, 2]) In Model: input size torch.Size([5, 5]) output size torch.Size([5, 2]) Outside: input size torch.Size([10, 5]) output_size torch.Size([10, 2])
ติดตั้ง Package module ที่ต้องการใน Singularity container และทดสอบใช้งาน
อ้างอิงการสร้าง Singularity Container อย่างง่ายใน 5 ขั้นตอน
สร้าง Singularity container
ตัวอย่างด้านล่างนี้ทำใน local machine ที่ติดตั้ง singularity ไว้บน Linux machine โดยทำตามเอกสารอ้างอิง และเพิ่มเติมการทดสอบรันตัวอย่างโปรแกรม Python (basic-multigpu-pytorch.py
) ภายใน container เพื่อยืนยันว่าได้ติดตั้ง package module ที่ถูกต้องพร้อมใช้งานจริง
[apiyatum@localhost ~]$ singularity pull docker://nvcr.io/nvidia/pytorch:21.07-py3 [apiyatum@localhost ~]$ sudo singularity build --sandbox mysandbox/ pytorch_21.07-py3.sif INFO: Starting build... INFO: Creating sandbox directory... INFO: Build complete: mysandbox/ [apiyatum@localhost ~]$ singularity shell --writable mysandbox/ Singularity mysandbox:/> Singularity mysandbox:/> pip install torchsummary Singularity mysandbox:/> pip install pytorch3d -f https://dl.fbaipublicfiles.com/pytorch3d/packaging/wheels/py39_cu102_pyt190/download.html Singularity mysandbox:/> python basic-multigpu-pytorch.py torch version : 1.8.1+cu102 cuda available? : False cuda version: 10.2 cuda device count: 0 cuda device id: In Model: input size torch.Size([30, 5]) output size torch.Size([30, 2]) Outside: input size torch.Size([30, 5]) output_size torch.Size([30, 2]) In Model: input size torch.Size([30, 5]) output size torch.Size([30, 2]) Outside: input size torch.Size([30, 5]) output_size torch.Size([30, 2]) In Model: input size torch.Size([30, 5]) output size torch.Size([30, 2]) Outside: input size torch.Size([30, 5]) output_size torch.Size([30, 2]) In Model: input size torch.Size([10, 5]) output size torch.Size([10, 2]) Outside: input size torch.Size([10, 5]) output_size torch.Size([10, 2]) Singularity mysandbox:/> exit [apiyatum@localhost ~]$ singularity build pytorch_21.07-py3-3DDL.sif mysandbox/ INFO: Starting build... INFO: Creating SIF file... INFO: Build complete: pytorch_21.07-py3-3DDL.sif [apiyatum@localhost ~]$
จะเห็นจาก output ที่ทดสอบภายใน container ว่าไม่มี cuda device เนื่องจากเรากำลังอยู่ใน linux machine ที่ไม่มี GPUs แต่จากผลลัพธ์แสดงให้เห็นว่าสามารถใช้งาน package modules ต่างๆที่จำเป็นได้แล้ว
ทดสอบใช้งานด้วย sbatch
เตรียม submission script
ใน submission script นี้กำหนดให้ใช้งาน partition “dgx-preempt” โดยเราทราบอยู่แล้วว่า dgx node มีจำนวน 8 GPUs ด้วยกัน
#!/bin/bash #SBATCH -p dgx-preempt #SBATCH -N 1 --ntasks-per-node=40 #SBATCH -t 00:10:00 #SBATCH -J 3DDL #SBATCH -A thaisc module purge module load Singularity singularity exec --nv pytorch_21.07-py3-3DDL.sif python basic-multigpu-pytorch.py
อย่าลืมใส่ “--nv” เพราะจะทำให้ใช้งาน GPUs ไม่ได้
output
$ cat slurm-1314656.out torch version : 1.8.1+cu102 cuda available? : True cuda version: 10.2 cuda device count: 8 cuda device id: , 0, 1, 2, 3, 4, 5, 6, 7 Let's use 8 GPUs! In Model: input size torch.Size([4, 5]) output size torch.Size([4, 2]) In Model: input size torch.Size([4, 5]) output size torch.Size([4, 2]) In Model: input size torch.Size([4, 5]) output size torch.Size([4, 2]) In Model: input size torch.Size([4, 5]) output size torch.Size([4, 2]) In Model: input size torch.Size([4, 5]) output size torch.Size([4, 2]) In Model: input size torch.Size([4, 5]) output size torch.Size([4, 2]) In Model: input size torch.Size([4, 5]) output size torch.Size([4, 2]) In Model: input size torch.Size([2, 5]) output size torch.Size([2, 2]) Outside: input size torch.Size([30, 5]) output_size torch.Size([30, 2]) In Model: input size torch.Size([4, 5]) output size torch.Size([4, 2]) In Model: input size torch.Size([4, 5]) output size torch.Size([4, 2]) In Model: input size torch.Size([4, 5]) output size torch.Size([4, 2]) In Model: input size torch.Size([4, 5]) output size torch.Size([4, 2]) In Model: input size torch.Size([4, 5]) output size torch.Size([4, 2]) In Model: input size torch.Size([4, 5]) output size torch.Size([4, 2]) In Model: input size torch.Size([4, 5]) output size torch.Size([4, 2]) In Model: input size torch.Size([2, 5]) output size torch.Size([2, 2]) Outside: input size torch.Size([30, 5]) output_size torch.Size([30, 2]) In Model: input size torch.Size([4, 5]) output size torch.Size([4, 2]) In Model: input size torch.Size([4, 5]) output size torch.Size([4, 2]) In Model: input size torch.Size([4, 5]) output size torch.Size([4, 2]) In Model: input size torch.Size([4, 5]) output size torch.Size([4, 2]) In Model: input size torch.Size([4, 5]) output size torch.Size([4, 2]) In Model: input size torch.Size([4, 5]) output size torch.Size([4, 2]) In Model: input size torch.Size([4, 5]) output size torch.Size([4, 2]) In Model: input size torch.Size([2, 5]) output size torch.Size([2, 2]) Outside: input size torch.Size([30, 5]) output_size torch.Size([30, 2]) In Model: input size torch.Size([2, 5]) output size torch.Size([2, 2]) In Model: input size torch.Size([2, 5]) output size torch.Size([2, 2]) In Model: input size torch.Size([2, 5]) output size torch.Size([2, 2]) In Model: input size torch.Size([2, 5]) output size torch.Size([2, 2]) In Model: input size torch.Size([2, 5]) output size torch.Size([2, 2]) Outside: input size torch.Size([10, 5]) output_size torch.Size([10, 2])
สังเกตจากผลลัพธ์จะเห็นได้ว่า nn.DataParallel
ทำการแบ่งข้อมูลกระจายไปรันใน GPUs ทุกตัวของ dgx node