深度學習涉及不少向量或多矩陣運算,如矩陣相乘、矩陣相加、矩陣-向量乘法等。深層模型的算法,如BP,Auto-Encoder,CNN等,均可以寫成矩陣運算的形式,無須寫成循環運算。然而,在單核CPU上執行時,矩陣運算會被展開成循環的形式,本質上仍是串行執行。GPU(Graphic Process Units,圖形處理器)的衆核體繫結構包含幾千個流處理器,可將矩陣運算並行化執行,大幅縮短計算時間。隨着NVIDIA、AMD等公司不斷推動其GPU的大規模並行架構,面向通用計算的GPU已成爲加速可並行應用程序的重要手段。得益於GPU衆核(many-core)體系結構,程序在GPU系統上的運行速度相較於單核CPU每每提高几十倍乃至上千倍。
目前,GPU已經發展到了較爲成熟的階段。利用GPU來訓練深度神經網絡,能夠充分發揮其數以千計計算核心的能力,在使用海量訓練數據的場景下,所耗費的時間大幅縮短,佔用的服務器也更少。若是對適當的深度神經網絡進行合理優化,一塊GPU卡至關於數十甚至上百臺CPU服務器的計算能力,所以GPU已經成爲業界在深度學習模型訓練方面的首選解決方案。
如何使用GPU?如今不少深度學習工具都支持GPU運算,使用時只要簡單配置便可。Pytorch支持GPU,能夠經過to(device)函數來將數據從內存中轉移到GPU顯存,若是有多個GPU還能夠定位到哪一個或哪些GPU。Pytorch通常把GPU做用於張量(Tensor)或模型(包括torch.nn下面的一些網絡模型以及本身建立的模型)等數據結構上。python
25.1 單GPU加速
使用GPU以前,須要確保GPU是可使用,可經過torch.cuda.is_available()的返回值來進行判斷。返回True則具備可以使用的GPU。
經過torch.cuda.device_count()能夠得到可以使用的GPU數量。
如何查看平臺GPU的配置信息?在命令行輸入命令nvidia-smi便可 (適合於Linux或Windows環境)。圖5-13是GPU配置信息樣例,從中能夠看出共有2個GPU。
web
圖5-13 GPU配置信息算法
把數據從內存轉移到GPU,通常針對張量(咱們須要的數據)和模型。
對張量(類型爲FloatTensor或者是LongTensor等),一概直接使用方法.to(device)或.cuda()便可。後端
1
2
3
4
5
6
|
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
#或device = torch.device("cuda:0")
device1 = torch.device("cuda:1")
for batch_idx, (img, label) in enumerate(train_loader):
img=img.to(device)
label=label.to(device)
|
對於模型來講,也是一樣的方式,使用.to(device)或.cuda來將網絡放到GPU顯存。服務器
1
2
3
4
|
#實例化網絡
model = Net()
model.to(device) #使用序號爲0的GPU
#或model.to(device1) #使用序號爲1的GPU
|
25.2 多GPU加速
這裏咱們介紹單主機多GPUs的狀況,單機多GPUs主要採用的DataParallel函數,而不是DistributedParallel,後者通常用於多主機多GPUs,固然也可用於單機多GPU。
使用多卡訓練的方式有不少,固然前提是咱們的設備中存在兩個及以上的GPU。
使用時直接用model傳入torch.nn.DataParallel函數便可,以下代碼:網絡
1
2
|
#對模型
net = torch.nn.DataParallel(model)
|
這時,默認全部存在的顯卡都會被使用。
若是你的電腦有不少顯卡,但只想利用其中一部分,如只使用編號爲0、一、三、4的四個GPU,那麼能夠採用如下方式:數據結構
1
2
3
4
5
6
7
|
#假設有4個GPU,其id設置以下
device_ids =[0,1,2,3]
#對數據
input_data=input_data.to(device=device_ids[0])
#對於模型
net = torch.nn.DataParallel(model)
net.to(device)
|
或者架構
1
2
|
os.environ["CUDA_VISIBLE_DEVICES"] = ','.join(map(str, [0,1,2,3]))
net = torch.nn.DataParallel(model)
|
其中CUDA_VISIBLE_DEVICES 表示當前能夠被Pytorch程序檢測到的GPU。
下面爲單機多GPU的實現代碼。
(1)背景說明
這裏使用波士頓房價數據爲例,共506個樣本,13個特徵。數據劃分紅訓練集和測試集,而後用data.DataLoader轉換爲可批加載的方式。採用nn.DataParallel併發機制,環境有2個GPU。固然,數據量很小,按理不宜用nn.DataParallel,這裏只是爲了說明使用方法。
(2)加載數據併發
1
2
3
4
5
6
|
boston = load_boston()
X,y = (boston.data, boston.target)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=0)
#組合訓練數據及標籤
myset = list(zip(X_train,y_train))
|
(2)把數據轉換爲批處理加載方式
批次大小爲128,打亂數據。dom
1
2
3
4
|
from torch.utils import data
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
dtype = torch.FloatTensor
train_loader = data.DataLoader(myset,batch_size=128,shuffle=True)
|
(3)定義網絡
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
class Net1(nn.Module):
"""
使用sequential構建網絡,Sequential()函數的功能是將網絡的層組合到一塊兒
"""
def __init__(self, in_dim, n_hidden_1, n_hidden_2, out_dim):
super(Net1, self).__init__()
self.layer1 = torch.nn.Sequential(nn.Linear(in_dim, n_hidden_1))
self.layer2 = torch.nn.Sequential(nn.Linear(n_hidden_1, n_hidden_2))
self.layer3 = torch.nn.Sequential(nn.Linear(n_hidden_2, out_dim))
def forward(self, x):
x1 = F.relu(self.layer1(x))
x1 = F.relu(self.layer2(x1))
x2 = self.layer3(x1)
#顯示每一個GPU分配的數據大小
print("\tIn Model: input size", x.size(),"output size", x2.size())
return x2
|
(4)把模型轉換爲多GPU併發處理格式
1
2
3
4
5
6
7
8
|
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
#實例化網絡
model = Net1(13, 16, 32, 1)
if torch.cuda.device_count() > 1:
print("Let's use", torch.cuda.device_count(), "GPUs")
# dim = 0 [64, xxx] -> [32, ...], [32, ...] on 2GPUs
model = nn.DataParallel(model)
model.to(device)
|
運行結果
Let's use 2 GPUs
DataParallel(
(module): Net1(
(layer1): Sequential(
(0): Linear(in_features=13, out_features=16, bias=True)
)
(layer2): Sequential(
(0): Linear(in_features=16, out_features=32, bias=True)
)
(layer3): Sequential(
(0): Linear(in_features=32, out_features=1, bias=True)
)
)
)
(5)選擇優化器及損失函數
1
2
|
optimizer_orig = torch.optim.Adam(model.parameters(), lr=0.01)
loss_func = torch.nn.MSELoss()
|
(6)模型訓練,並可視化損失值。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
from tensorboardX import SummaryWriter
writer = SummaryWriter(log_dir='logs')
for epoch in range(100):
model.train()
for data,label in train_loader:
input = data.type(dtype).to(device)
label = label.type(dtype).to(device)
output = model(input)
loss = loss_func(output, label)
# 反向傳播
optimizer_orig.zero_grad()
loss.backward()
optimizer_orig.step()
print("Outside: input size", input.size() ,"output_size", output.size())
writer.add_scalar('train_loss_paral',loss, epoch)
|
運行的部分結果
In Model: input size torch.Size([64, 13]) output size torch.Size([64, 1])
In Model: input size torch.Size([64, 13]) output size torch.Size([64, 1])
Outside: input size torch.Size([128, 13]) output_size torch.Size([128, 1])
In Model: input size torch.Size([64, 13]) output size torch.Size([64, 1])
In Model: input size torch.Size([64, 13]) output size torch.Size([64, 1])
Outside: input size torch.Size([128, 13]) output_size torch.Size([128, 1])
從運行結果能夠看出,一個批次數據(batch-size=128)拆分紅兩份,每份大小爲64,分別放在不一樣的GPU上。此時用GPU監控也可發現,兩個GPU都同時在使用。
(7)經過web查看損失值的變化狀況
圖5-16 併發運行訓練損失值變化狀況
圖形中出現較大振幅,是因爲採用批次處理,並且數據沒有作任何預處理,對數據進行規範化應該更平滑一些,你們能夠嘗試一下。
單機多GPU也可以使用DistributedParallel,它多用於分佈式訓練,但也能夠用在單機多GPU的訓練,配置比使用nn.DataParallel稍微麻煩一點,可是訓練速度和效果更好一點。具體配置爲:
1
2
3
4
|
#初始化使用nccl後端
torch.distributed.init_process_group(backend="nccl")
#模型並行化
model=torch.nn.parallel.DistributedDataParallel(model)
|
單機運行時使用下面方法啓動
1
|
python -m torch.distributed.launch main.py
|
25.3使用GPU注意事項
使用GPU能夠提高咱們訓練的速度,若是使用不當,可能影響使用效率,具體使用時要注意如下幾點:(1)GPU的數量儘可能爲偶數,奇數的GPU有可能會出現異常中斷的狀況;(2)GPU很快,但數據量較小時,效果可能沒有單GPU好,甚至還不如CPU;(3)若是內存不夠大,使用多GPU訓練的時候可經過設置pin_memory爲False,固然使用精度稍微低一點的數據類型有時也效果。