Environment
- OS: Ubuntu 14.04
- Python version: 3.7
- PyTorch version: 1.4.0
- IDE: PyCharm
- GPU: 3 張 RTX 2080 Ti
文章目錄
- 0. 寫在前面
- 1. 設定 GPU
-
- 1.1 選擇 GPU
- 1.2 檢視可用 GPU 的資訊
- 2. 将資料和模型在 GPU 和 CPU 之間移動
-
- 1.2 對于 torch.Tensor
- 2.2 對于 torch.nn.Module
- 3. 多 GPU 資料并行
0. 寫在前面
深度學習的神經網絡模型往往參數巨多,一個能跑的 GPU 是基本配置。這裡記一下 PyTorch 中關于 GPU 的一些函數和訓練代碼。
CPU 和 GPU 的差別可以簡明地參考知乎 CPU 和 GPU 的差別是什麼?。
1. 設定 GPU
1.1 選擇 GPU
以三塊 RTX 2080 Ti 卡為例,它們的實體索引分别為 0、1、2
os.system('nvidia-smi -q -d Memory | grep -A4 GPU | grep Free > temp.txt')
gpu_memories = [int(x.split()[2]) for x in open('temp.txt', 'r').readlines()]
os.system('rm temp.txt')
print('GPU of free memories:', gpu_memories)
# GPU of free memories: [11009, 11009, 11009]
接着,設定索引為 1 和 2 的兩塊卡對目前 python 腳本程式可見
這樣,實體 GPU 中索引為 1 和 2 的 GPU 為對應索引為 0 和 1 邏輯 GPU。
1.2 檢視可用 GPU 的資訊
torch.cuda
子產品中提供了 PyTorch 關于使用 GPU 的函數。
-
傳回是否有可用的 GPUtorch.cuda.is_available()
import torch
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
print(device) # cuda
-
傳回 GPU 裝置的名稱torch.cuda.get_device_name()
import torch
print(torch.cuda.get_device_name()) # GeForce RTX 2080 Ti
-
傳回目前程式可見的 GPU 數目torch.cuda.device_count()
import torch
print(torch.cuda.device_count()) # 2
2. 将資料和模型在 GPU 和 CPU 之間移動
使用
Tensor.is_cuda()
方法能夠檢視資料是否在 GPU 上
import torch
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
tensor_in_cpu = torch.randn((128, 3, 299, 299))
print(tensor_in_cpu.is_cuda)
tensor_in_gpu = tensor_in_cpu.to(device) # 移動資料到 cuda 這塊 GPU
print(tensor_in_gpu.is_cuda)
1.2 對于 torch.Tensor
Tensor.to()
方法,轉換資料類型或所在裝置(CPU / GPU)
- 傳入裝置,如
,則将張量移動到索引為 0 的邏輯 GPU 上torch.device(cuda:0)
- 傳入資料類型,如
,則将張量的類型轉換為該類型torch.float32
注意,該操作非 in-place,需要建立一個變量來引用得到的結果。
import torch
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
tensor_in_cpu = torch.randn((128, 3, 299, 299))
tensor_in_gpu = tensor_in_cpu.to(device) # 移動資料到 cuda:0 這塊 GPU
使用
Tensor.is_cuda()
方法能夠檢視資料是否在 GPU 上
print(tensor_in_cpu.is_cuda) # False
print(tensor_in_gpu.is_cuda) # True
2.2 對于 torch.nn.Module
Module.to()
方法,轉換模型參數的資料類型或模型所在的裝置(CPU / GPU)。
- 傳入裝置,如
,則将模型移動到索引為 0 的邏輯 GPU 上torch.device(cuda:0)
- 傳入資料類型,如
,則将模型中參數的類型轉換為該類型torch.float32
注意,該操作 in-place。
import torchvision
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model = torchvision.models.resnet50()
model.to(device)
3. 多 GPU 資料并行
更多詳細的參考知乎 Pytorch的nn.DataParallel。
torch.nn.DataParallel
類,将模型中的資料分發到不同 GPU 上,讓模型水準上的資料并行,實作多 GPU 訓練。
import torch
from torch.nn import DataParallel, Module, Sequential, Linear, ReLU
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
class FooNet(Module):
def __init__(self, in_features, num_classes):
super(FooNet, self).__init__()
self.fc = Sequential(
Linear(in_features, 100),
ReLU(),
Linear(100, num_classes)
)
def forward(self, x):
print("Batch size of data in 'forward' method:", x.size(0))
return self.fc(x)
# ========== test ==========
batch_size = 128
in_features = 100
num_classes = 100
# create data
inputs = torch.randn(batch_size, in_features)
labels = torch.randn(batch_size, num_classes)
inputs, labels = inputs.to(device), labels.to(device)
# modeling
foonet = FooNet(in_features, num_classes)
foonet = DataParallel(
module=foonet, # 需要包裝分發的模型
device_ids=None, # 可分發的 GPU,預設 None,為分發到所有可見可用的 GPU
output_device=None, # 結果輸出的裝置,預設 None,為主 GPU,即索引為 0 的邏輯 GPU
dim=0 # 應該是按次元 0 将資料分發到不同 GPU 上(文檔并沒有解釋)
)
foonet.to(device)
# forward
outputs = foonet(inputs)
print('Size of output:', outputs.size())
運作結果為列印出
Batch size of data in 'forward' method: 64
Batch size of data in 'forward' method: 64
Size of output: torch.Size([128, 100])
可見,輸入資料一個 batch 為 128,被平均配置設定到兩張卡中,每張卡的模型中資料 batch 為 64。
注意,
DataParallel
包裝後的模型是
torch.nn.parallel.data_parallel.DataParallel
類的執行個體對象,需要用
.module
屬性得到具體的模型
print(type(foonet))
# <class 'torch.nn.parallel.data_parallel.DataParallel'>
print(type(foonet.module))
# <class '__main__.FooNet'>