精品一区二区三区影院在线午夜_天天躁日日躁狠狠躁AV麻豆_国产午夜福利短视频_中文字幕乱偷无码AV先锋蜜桃_久久精品一二区东京热_国产成人亚洲日韩欧美久久久,国产成人精品久久一区二区三区

Pytorch調研筆記

Pytorch訓練調研

首先我們簡單說明一下,這么多深度學習框架中,為什么選擇PyTorrch呢?

因為PyTorch是當前難得的簡潔優雅且高效快速的框架。在筆者眼里,PyTorch達到目前深度學習框架的最高水平。當前開源的框架中,沒有哪一個框架能夠在靈活性、易用性、速度這三個方面有兩個能同時超過PyTorch。下面是許多研究人員選擇PyTorch的原因。

1、簡潔:PyTorch的設計追求最少的封裝,盡量避免重復造輪子。不像TensorFlow中充斥著session、graph、operation、name_scope、variable、tensor、layer等全新的概念,PyTorch的設計遵循tensor→variable(autograd)→nn.Module 三個由低到高的抽象層次,分別代表高維數組(張量)、自動求導(變量)和神經網絡(層/模塊),而且這三個抽象之間聯系緊密,可以同時進行修改和操作。

簡潔的設計帶來的另外一個好處就是代碼易于理解。PyTorch的源碼只有TensorFlow的十分之一左右,更少的抽象、更直觀的設計使得PyTorch的源碼十分易于閱讀。

2、速度:PyTorch的靈活性不以速度為代價,在許多評測中,PyTorch的速度表現勝過TensorFlow和Keras等框架 。框架的運行速度和程序員的編碼水平有極大關系,但同樣的算法,使用PyTorch實現的那個更有可能快過用其他框架實現的。

3、易用:PyTorch是所有的框架中面向對象設計的最優雅的一個。PyTorch的面向對象的接口設計來源于Torch,而Torch的接口設計以靈活易用而著稱,Keras作者最初就是受Torch的啟發才開發了Keras。PyTorch繼承了Torch的衣缽,尤其是API的設計和模塊的接口都與Torch高度一致。PyTorch的設計最符合人們的思維,它讓用戶盡可能地專注于實現自己的想法,即所思即所得,不需要考慮太多關于框架本身的束縛。

4、活躍的社區:PyTorch提供了完整的文檔,循序漸進的指南,作者親自維護的論壇 供用戶交流和求教問題。Facebook 人工智能研究院對PyTorch提供了強力支持,作為當今排名前三的深度學習研究機構,FAIR的支持足以確保PyTorch獲得持續的開發更新,不至于像許多由個人開發的框架那樣曇花一現。

1、 訓練需要參數及具體含義

Pytorch的基礎部分主要分為以下三個方面:

1.1、Tensor張量

Tensorflow中數據的核心單元就是Tensor。張量包含了一個數據集合,這個數據集合就是原始值變形而來的,它可以是一個任何維度的數據。tensor的rank就是其維度。

首先,我們需要學會使用PyTorch中的Tensor。Tensor在PyTorch中負責存儲基本數據,PyTorch針對Tensor也提供了相對豐富的函數和方法,所以PyTorch中的Tensor與NumPy的數組具有極高的相似性。Tensor是一種高層次架構,也不要明白什么是深度學習,什么是后向傳播,如何對模型進行優化,什么是計算圖等技術細節。更重要的是,在PyTorch中定義的Tensor數據類型可以在GPU上進行運算,而且只需要對變量做一些簡單的類型轉換就能輕易實現。

1.2、變量自動求導

AUTOGRAD自動微分

PyTorch中所有神經網絡的核心是autograd包裹,讓我們簡單介紹一下。這個autograd軟件包為張量上的所有操作提供自動區分。它是一個按運行定義的框架,這意味著您的后端由您的代碼運行方式來定義,并且每個迭代都可能是不同的。

自動微分包:torch.autograd

torch.autograd提供實現任意標量值函數自動微分的類和函數。它需要對現有代碼進行最少的更改-您只需聲明Tensors,則應使用requires_grad=True關鍵詞。

計算給定張量w,r,t的梯度之和:

torch.autograd.backward(tensors, grad_tensors=None, retain_graph=None, create_graph=False, grad_variables=None)

計算并返回輸出w.r.t的梯度之和:

torch.autograd.grad(outputs, inputs, grad_outputs=None, retain_graph=None, create_graph=False, only_inputs=True, allow_unused=False)

局部禁用梯度計算

上下文管理器,禁用梯度計算:CLASS torch.autograd.no_grad

啟用梯度計算的上下文管理器:CLASS torch.autograd.enable_grad

上下文管理器,它將梯度計算設置為ON或OFF:

CLASS torch.autograd.set_grad_enabled(mode)

張量自梯度函數:CLASS torch.Tensor

Function功能:CLASS torch.autograd.Function

數值梯度檢驗:

torch.autograd.gradcheck(func, inputs, eps=1e-06, atol=1e-05, rtol=0.001, raise_exception=True, check_sparse_nnz=False)

等等自動微分函數,詳情間官網:https://pytorch.org/docs/stable/autograd.html

tensor對象通過一系列的運算可以組成動態圖,對于每個tensor對象,有下面幾個變量控制求導的屬性。

在0.3.0版本中,自動求導還需要借助于Variable類來完成,在0.4.0版本中,Variable已經被廢除了,tensor自身即可完成這一過程。

import torch

x = torch.randn((4,4), requires_grad=True)

y = 2*x

z = y.sum()

print z.requires_grad # True

z.backward()

print x.grad

'''tensor([[ 2., 2., 2., 2.],

[ 2., 2., 2., 2.],

[ 2., 2., 2., 2.],

[ 2., 2., 2., 2.]])

'''

1.3、神經網絡層與損失函數優化等高層封裝。網絡層的封裝存在于torch.nn模塊,損失函數由torch.nn.functional模塊提供,優化函數由torch.optim模塊提供

PyTorch與Caffe不同,Caffe中使用Slover創建網絡和測試網絡,采用Prototxt文件來存儲網絡結構和訓練參數,并將網絡參數存儲在Caffemodel文件中。在PyTorch中,主要使用torch.nn這個包,這邊通過官方手冊的一個簡單的例子在說明:

import torch.nn as nn

import torch.nn.functional as F

class Model(nn.Module):

def __init__(self):

super(Model, self).__init__()

self.conv1 = nn.Conv2d(1, 20, 5)

self.conv2 = nn.Conv2d(20, 20, 5)

def forward(self, x):

x = F.relu(self.conv1(x))

return F.relu(self.conv2(x))

這個例子定義了一個只有兩層的網絡Model。其中兩個函數:

- 初始化函數 __init__(self)定義了具體網絡有什么層,這里實際上沒有決定網絡的結構,也就是說將上面的例子中的self.conv1和self.conv2定義的前后順序調換是完全沒有影響的。

- forward函數定義了網絡的前向傳播的順序。

pytorch中具體支持的不同的層請參考官方手冊

下面表格中列出了比較重要的神經網絡層組件。對應的在nn.functional模塊中,提供這些層對應的函數實現。通常對于可訓練參數的層使用module,而對于不需要訓練參數的層如softmax這些,可以使用functional中的函數。

損失函數與優化方法

torch.nn模塊中提供了許多損失函數類,這里列出幾種相對常見的。

優化方法

由torch.optim模塊提供支持

在神經網絡的性能調優中,常見的作法是對不對層的網絡設置不同的學習率。

class model(nn.Module):

def __init__():

super(model,self).__init__()

self.base = Sequencial()

# code for base sub module

self.classifier = Sequencial()

# code for classifier sub module

optim.SGD([

{'params': model.base.parameters()},

{'params': model.classifier.parameters(), 'lr': 1e-3}

], lr=1e-2, momentum=0.9)

訓練需要參數:

在Caffe中,使用solver創建訓練網絡和測試網絡,并采用prototxt文件來存儲網絡結構。PyTorch和Caffe有所不同,PyTorch中主要使用torch.nn模塊來創建網絡模型。

PyTorch訓練時,將一個不可訓練的類型Tensor轉換成可以訓練的類型parameter并將這個parameter綁定到這個module里面(net.parameter()中就有這個綁定的parameter,所以在參數優化的時候可以進行優化的),所以經過類型轉換這個self.v變成了模型的一部分,成為了模型中根據訓練可以改動的參數了。使用這個函數的目的也是想讓某些變量在學習的過程中不斷的修改其值以達到最優化。

2、 訓練需要數據格式

2.1、DataSet與DataLoader

torch.util.data模塊提供了DataSet類用于描述一個數據集。定義自己的數據集需要繼承自DataSet類,且實現__getitem__()與__len__()方法。__getitem__方法返回指定索引處的tensor與其對應的label。

為了支持數據的批量及隨機化操作,可以使用data模塊下的DataLoader類型來返回一個加載器:

DataLoader(dataset,batch_size=1, shuffle=False, sampler=None, batch_sampler=None, num_workers=0)

2.2、torchvision簡介

torchvision是配合pytorch的獨立計算機視覺數據集的工具庫,下面介紹其中常用的數據集類型。

torchvision.datasets.ImageFolder(dir, transform, label_map,loader)

提供了從一個目錄初始化出來一個圖片數據集的便捷方法。

要求目錄下的圖片分類存放,每一類的圖片存儲在以類名為目錄名的目錄下,方法會將每個類名映射到唯一的數字上,如果你對數字有要求,可以用label_map來定義目錄名到數字的映射。

torchvision.datasets.DatasetFolder(dir,transform, label_map, loader, extensions)

提供了從一個目錄初始化一般數據集的便捷方法。目錄下的數據分類存放,每類數據存儲在class_xxx命名的目錄下。

此外torchvision.datasets下實現了常用的數據集,如CIFAR-10/100, ImageNet, COCO, MNIST, LSUN等。

除了數據集,torchvision的model模塊提供了常見的模型實現,如Alex-Net, VGG,Inception, Resnet等。

2.3、torchvision提供的圖像變換工具

torchvision的transforms模塊提供了對PIL.Image對象和Tensor對象的常見操作。如果需要連續應用多個變換,可以使用Compose對象組裝多個變換。

PyTorch訓練需要數據格式:

在Caffe中,生成lmda格式的文件創建數據集。PyTorch和Caffe有所不同,PyTorch中主要使用Dataset和DataLoaderd預處理和加載數據集。

Dataset是PyTorch中圖像數據集中最為重要的一個類,也是PyTorch中所有數據集加載類中應該繼承的父類。其中父類中的兩個私有成員函數必須被重載,否則會出發錯誤提示:

def getitem(self, index):

def len(self):

其中_len_應該返回數據集的大小,而_getitem_應該編寫支持數據集索引的函數,例如通過dataset[i]可以得到數據集中的第i+1個數據。

現在我們開始定義一個自己的數據集類:

# 假設下面這個類是讀取船只的數據類

class ShipDataset(Dataset):

# root:圖像存放的地址根路徑

# agument:是否需要圖像增強

def _init_ (self, root, augment=None):

# 這個list存放所有圖像的地址

self.image_files = np.array([x.path for x in os.scandir(root) if

x.name.endswith(“.jpg”) or x.name.endswith(“.png”)])

self.augment = augment # 是否需要圖像增強

def _getitem_ (self, index):

# 讀取圖像數據并返回

# 這里的open_image是讀取圖像函數,可用PIL,opencv等庫讀取

return open_image(self.image_files[index])

def _len_ (self):

# 返回圖像的數量

return len(self.image_files)

如果我們需要在讀取數據的同時對圖像進行增強的話,可以在__getitem__(self, index)函數中設置圖像增強的代碼,例如

def _getitem_(self, index):

if self.augment:

image = open_image(self.image_files[index])

image = self.augment(image) # 這里對圖像進行了增強

return image

else:

# 如果不進行增強,直接讀取圖像數據并返回

# 這里的open_image是讀取圖像函數,可用PIL等庫讀取

return open_image(self.image_files[index])

當然,圖像增強的方法可以使用Pytorch內置的圖像增強方式,也可以使用自定義或者其他的圖像增強庫。這個很靈活,當然要記住一點,在Pytorch中得到的圖像必須是tensor,也就是說我們還需要再修改一下__getitem__(self, index):

def _getitem_ (self, index):

if self.augment:

image = open_image(self.image_files[index])

image = self.augment(image) # 這里對圖像進行了增強

return to_tensor(image) # 將讀取到的圖像變成tensor再傳出

else:

# 如果不進行增強,直接讀取圖像數據并返回

# 這里的open_image是讀取圖像函數,可用PIL等庫讀取

return to_tensor(open_image(self.image_files[index]))

這樣,一個基本的數據類就設計好了

DataLoader

之前所說的Dataset類是讀入數據集數據并且對讀入的數據進行了索引。但是光有這個功能是不夠用的,在實際的加載數據集的過程中,我們的數據量往往都很大,對此我們還需要一下幾個功能:

l 可以分批次讀取:batch-size

l 可以對數據進行隨機讀取,可以對數據進行洗牌操作(shuffling),打亂數據集內數據分布的順序

l 可以并行加載數據(利用多核處理器加快載入數據的效率)

這時候就需要Dataloader類了,Dataloader這個類并不需要我們自己設計代碼,我們只需要利用DataLoader類讀取我們設計好的ShipDataset即可:

# 利用之前創建好的ShipDataset類去創建數據對象

ship_train_dataset = ShipDataset(data_path, augment=transform)

# 利用dataloader讀取我們的數據對象,并設定batch_size和工作現場

Ship_train_loader = DataLoader(ship_train_dataset, batch_size=16, num_workers=4, shuffle=Fals, **kwargs)

這時候通過ship_train_loader返回的數據就是按照batch-size來返回特定數量的訓練數據的tensor,而且此時利用了多線程,讀取數據的速度相比單線程快很多。

我們這樣讀取:

For image in train_loader:

Image = image.to(device) # 將tensor數據移動到device中

Optimizer.zero_grad()

Output = model(image) # model模型處理(n,c,h,w)格式的數據,n為batch_size

讀取數據的基本模式就是這樣,當然在實際中不可能這么簡單,我們除了圖像數據可能還有json、csv等文件需要我們去讀取配合圖像完成任務。但是原理基本都是一樣的,具體復雜點的例子可以查看官方的例程介紹

3、 參數初始化方式

參數的初始化其實就是對參數賦值。而我們需要學的的參數都是Variable,它其實是對Tensor的封裝,同時提供了data,grad等接口,這就意味著我們可以直接對這些參數進行操作賦值了。這就是PyTorch簡潔高效所在。

PyTorch提供了多種參數初始化函數:

torch.nn.init.constant(tensor, val)

torch.nn.init.normal(tensor, mean=0, std=1)

torch.nn.init.xavier_uniform(tensor, gain=1)

等等。詳細請參考:http://pytorch.org/docs/nn.html#torch-nn-init

注意上面的初始化函數的參數tensor,雖然寫的是tensor,但是也可以是Variable類型的。而神經網絡的參數類型Parameter是Variable類的子類,所以初始化函數可以直接作用于神經網絡參數。

示例:

self.conv1=nn.Conv2d(3,64,kernel_size=7,stride=2,padding=3)

init.xavier_uniform(self.conv1.weight)

init.constant(self.conv1.bias, 0.1)

上面的語句是對網絡的某一層參數進行初始化。如何對整個網絡的參數進行初始化定制呢?

def weights_init(m):

classname=m.__class__.__name__

if classname.find('Conv') != -1:

xavier(m.weight.data)

xavier(m.bias.data)

net = Net()

net.apply(weights_init)

#apply函數會遞歸地搜索網絡內的所有module

并把參數表示的函數應用到所有的module上。

不建議訪問以下劃線為前綴的成員,他們是內部的,如果有改變不會通知用戶。更推薦的一種方法是檢查某個module是否是某種類型:

def weights_init(m):

if isinstance(m, nn.Conv2d):

xavier(m.weight.data)

xavier(m.bias.data)

4、 訓練的完整流程

這是一個簡單的前饋網絡,它接受輸入,一個接一個地通過幾個層輸入,然后最后給出輸出。

神經網絡的典型訓練過程如下:

l 定義具有一些可學習參數(或權重)的神經網絡

l 迭代輸入數據集

l 通過網絡處理輸入

l 計算損失(輸出離正確有多遠)

l 將梯度傳播回網絡參數

l 更新網絡的權重,通常使用簡單的更新規則:

weight=weight-learning_rate*gradient

定義網絡

您只需要定義forward函數,以及backward函數(在其中計算梯度)將自動為您定義autograd。您可以在forward功能模型的可學習參數由net.parameters()

params = list(net.parameters())

print(len(params))

print(params[0].size()) # conv1's .weight

我們還可以嘗試一個隨機的32*32輸入。

input = torch.randn(1, 1, 32, 32)
out = net(input)
print(out)

所有參數和具有隨機梯度的后端的梯度緩沖區為零:

net.zero_grad()
out.backward(torch.randn(1, 10))

計算損失

損失函數接受(輸出,目標)對輸入,并計算一個值,該值估計輸出離目標有多遠。有幾種不同的損失函數在NN包裹下。一個簡單的損失是:nn.MESLoss計算輸入和目標之間的均方誤差。

例如:

output = net(input)
target = torch.randn(10)  # a dummy target, for example
target = target.view(1, -1)  # make it the same shape as output
criterion = nn.MSELoss()
loss = criterion(output, target)
print(loss)

現在,如果你跟著losss在向后的方向上,使用它的.grad_fn屬性,您將看到如下所示的計算圖標:

input -> conv2d -> relu -> maxpool2d -> conv2d -> relu -> maxpool2d
-> view -> linear -> relu -> linear -> relu -> linear
-> MSELoss
-> loss

所以,當我們打電話loss.backword(),整個圖被區分為w,r,t。的損失,以及圖中的所有張量requires_grad=True會有他們的.grad張量隨梯度累計。

Backprop

要反向傳播錯誤,我們索要做的就是loss.backward()。您需要清楚現有的梯度,否則梯度將累計到現有的梯度。現在我們要使用loss.backward(),并看一看前后的偏壓梯度。

net.zero_grad()     # zeroes the gradient buffers of all parameters
print('conv1.bias.grad before backward')
print(net.conv1.bias.grad)
loss.backward()
print('conv1.bias.grad after backward')
print(net.conv1.bias.grad)

至此,我們可以了解如何使用損失函數計算誤差。

更新權重

我們可以使用如下代碼實現這個功能:

learning_rate = 0.01
for f in net.parameters():
    f.data.sub_(f.grad.data * learning_rate)

然而,在使用神經網絡時,您需要使用各種不同的更新規則,如SGD、Nesterov-SGD、ADAM、RMSProp等。為此,我們構建了一個小包:torch.optim實現了所有這些方法。

import torch.optim as optim
# create your optimizer
optimizer = optim.SGD(net.parameters(), lr=0.01)
# in your training loop:
optimizer.zero_grad()   # zero the gradient buffers
output = net(input)
loss = criterion(output, target)
loss.backward()
optimizer.step()    # Does the update

注:

觀察如何手動將漸變緩沖區設置為零。Optimizer.zero_grad()。這是梯度按照backprop部分的。

5、 是否支持分布式訓練

5.1、什么是分布式計算

分布式計算

指的是一種編寫程序的方式,它利用網絡中多個連接的不同組件。通常,大規模計算通過以這種方式布置計算機來實現,這些計算機能夠并行地處理高密度的數值運算。在分布式計算的術語中,這些計算機通常被稱為節點(node),這些節點的集合就是集群。這些節點一般是通過以太網連接的,但是其他的高帶寬網絡也可以利用分布式架構的優勢。

5.2、分布式計算框架

隨著大數據時代的來臨,現有計算方式已不能滿足工作需求,并且CPU近幾年往多核方面發展,單臺電腦性能有限不足以完成復雜計算任務。分布式計算框架能很好的解決此類需要巨大計算量問題,分布式計算框架允許使用商用服務器組成一個計算集群并提供一個并行計算軟件框架,服務器之間的通信、負載均衡、任務計算與處理、任務儲存等復雜的操作都交由系統自動處理,減少了軟件開發人員的負擔。

系統是一個基本C++平臺的分布式計算框架,使用了Digia公司開發工具QT5.10;任務儲存使用了開源數據庫MYSQL。該分布式框架實現了動態鏈接庫的上傳、任務文件上傳、任務文件解析、計算任務的分發、計算任務的計算、計算結果的儲存、計算結果的匯總等功能。在分配任務過程中,本框架優先分配給最先執行完任務的計算節點,高效利用計算資源。當計算節點斷開連接時,本框架會自動將其任務重置,并分發給其他計算節點。使用了任務隊列與任務報錯等容錯手段保證了分布式計算框架的穩定運行。

用戶編寫模板對應函數Read、Handle、Sum,生成動態鏈接庫后上傳至服務器中,隨后將任務文件上傳即可完成整個分布式計算過程。服務器調用Read函數對任務文件進行任務讀取與分解,隨后將分解任務分配至空閑計算節點,計算節點調用Handle函數進行任務計算,計算完畢后回傳任務結果至服務器,當整個計算任務完成時服務器調用Sum函數進行任務的匯總,最后完成分布式任務的計算。

系統結構大致如下:

5.3、PyTorch分布式訓練

如果需要在短時間內完成大量數據的訓練,處理較大的Batch_size時,使用傳統的模型訓練時間就會很慢,但是PyTorch滿足分布式計算的需求。在PyTorch中分布式分為兩種,一種是一機多卡,另一種是多機多卡,即指用多個GPU跑PyTorch,可能是一個機器上的多個GPU,也可能是多個機器上,每個機器上有若干個GPU。

分布式PyTorch,主要是Pytorch在v0.2中發布的一個分布式通信包torch.distributed,我們可以使用import torch.distributed as dist導入使用,分布式Pyrorch允許您在多臺機器之間交換Tensors。使用此軟件包,您可以通過多臺機器和更大的小批量擴展網絡訓練。例如,您將獲得實現精準大型小型SGD:1小時訓練ImageNet的模型。此外,python在默認情況下只使用一個GPU,在多個GPU的情況下就需要使用PyTorch提供的DataPaeallel包。

分布式訓練主要分為以下幾個步驟:

  1. 1.初始化及相關參數解釋

torch.distributed.init_process_group(backend, init_method=None, timeout=datetime.timedelta(0, 1800), world_size=-1, rank=-1, store=None, group_name='')

參數說明:

backend(str): 要使用的后端。根據構建時設置,有效值包括MPI,gloo和NCCL。此字段應作為小寫字符串(例如,“gloo”),也可通過Backend屬性(例如,Backend. GLOO)。如果每臺機器上使用多個線程NCCL后端,每個進程必須對它使用的每個GPU具有獨占訪問權,因為每臺進程之間共享GPU可能導致死鎖。

init_method(str,optional): 指定如何初始化進程組的URL。默認值為“env:/”,如果沒有init_method或store被指定。互斥store。

world_size(int, optional):參與這個工作的進程數目。如store被指定

rank(int,optional): 當前進程的優先級。如store被指定。

Store(store,optional):所有工作人員均可訪問的密鑰/值存儲,用于交換連接/地址信息。互斥init_method。

group_name(str,optional): 用來標記這組進程名的

:PyTorch目前只支持Linux,其中torch.distributed只支持三個后端,GLOO、MPI、NCCL。那么如何選擇用哪個后端呢,默認情況下,GLOO和NCCL后端構建并包含在PyTorch分布式中(NCCL僅在使用CUDA構建時使用)。MPI是一個可選的后端,只有在從源代碼構建PyTorch時才能包含它。(例如,在安裝了MPI的主機上構建PyTorch。)

注:MPL:分布式計算標準。是消息傳遞接口。

經驗法則:

  • 使用NCCL后端進行分布式GPU培訓
  • 使用GLOO侯丹進行分布式CPU訓練

目前支持三種初始化方法:

  • file:// 共享文件系統(要求所有進程可以訪問單個文件系統)有共享文件系統可以選擇
  • tcp:// 這兩種方法都需要可以從所有進程訪問的網絡地址和所需的網絡地址。world_size。第一種方法要求指定屬于秩0進程的地址,不過需要手動設置rank
  • env:// 環境變量(需要您手動分配等級并知道所有進程可訪問節點的地址)這是默認的方法。
  1. 2.分布式數據并行

并行策略主要兩種類型:模型并行和數據并行

數據并行

數據并行指的是,通過位于不同硬件/設備上的同一個網絡的多個副本來處理數據的不同批(batch)。不同于模型并行,每個副本可能是整個網絡,而不僅僅是一部分。這種并行策略隨著數據的增長可以很好地擴展,但是由于整個網絡必須部署再一個設備上,因此可能無法幫助到具有高內存占用的模型。

實際上,在大組織里,為了執行生產質量的深度學習訓練算法,數據并行更加流行也更加常用。

PyTorch提供了一個非常優雅并且易于使用的API,就是torch.distributed。作為用 C 語言寫的底層 MPI 庫的接口。PyTorch 需要從源碼編譯,并且必須與安裝在系統中的 Intel MPI 進行鏈接。

分布式數據并行(DDP)在模塊級實現數據并行。它使用torch.distributed包來同步漸變、參數和緩沖區。并行性在進程內部和跨進程都是可用的。在進程中,ddp將輸入模塊復制到device_ids,相應地沿批處理維度分散輸入,并將輸出收集到output_device,類似于數據并行。在跨進程中,DDP在前送中插入必要的參數同步,在后送中插入漸變同步。只要進程不共享GPU設備,就由用戶將進程映射到可用資源。推薦的(通常是最快的)方法是為每個模塊副本創建一個流程,即在進程中不進行模塊復制。

設置Setup:

包含在PyTorch中的分布式包(即,torch.distributed)使研究人員和實踐者能夠輕松地在進程和機器集群中并行化他們的計算。為此,它利用消息傳遞語義,允許每個進程將數據通信到任何其他進程。而不是多處理(torch.multiprocessing)包,進程可以使用不同的通信后端,而不限于在同一臺機器上執行。

為了開始,我們需要同時運行多個進程的能力。如果您可以訪問計算集群,則應該與本地sysadmin檢查或使用您最喜歡的協調工具。(例如,pdsh,clustershell或者others)為了本教程的目的,我們將使用以下模板使用單個機器和分叉多個進程。

這個腳本生成兩個進程,每個進程將設置分布式環境,初始化進程組(dist.init_process_group),并最終執行給定的run功能。

點對點通信:

從一個進程到另一個進程的數據傳輸稱為點對點通信.這些都是通過send和recv職能或其立即反零件,isend和irecv. 當我們希望對流程的通信進行細粒度控制時,點對點通信非常有用。

集體交流:

與點對點通信不同,集體允許跨所有進程的通信模式。因為我們想要群中所有張量的和,所以我們使用dist.reduce_op.SUM作為精簡操作符。一般說來,任何可交換的數學運算都可以用作算子。

分布式訓練:

簡單地說,我們想要實現一個隨機梯度下降的分布式版本。我們的腳本將允許所有進程在其批數據上計算其模型的梯度,然后平均它們的梯度。為了確保在更改進程數時類似的收斂結果,我們首先必須對數據集進行分區。

假設我們有兩個副本,那么每個進程將有一個train_set60000/2=30000個樣品。我們還將批處理大小除以副本的數量,以維護總體批次大小為128。我們現在可以編寫我們通常的前、后向優化訓練代碼,并添加一個函數調用來平均我們模型的梯度。

至此,仍然需要實現average_gradients(model)函數,它簡單地接受一個模型并平均它在整個世界的梯度

現在我們就成功地實現了分布式同步SGD,可以在大型計算機集群上對任何模型進行訓練。

  1. 3.GPU并行方式

通過對模型進行并行GPU處理(這里一般指單機多卡),可以相對提高處理速度,但是處理方法大致有兩種

將Module放在GPU上運行也十分簡單,只需兩步:

model = model.cuda():將模型的所有參數轉存到GPU

input.cuda():將輸入數據也放置到GPU上

至于如何在多個GPU上并行計算,PyTorch也提供了兩個函數,可實現簡單高效的并行GPU計算。

①nn.parallel.data_parallel(module, inputs, device_ids=None, output_device=None, dim=0, module_kwargs=None)

②class torch.nn.DataParallel(module, device_ids=None, output_device=None, dim=0)

可見二者的參數十分相似,通過device_ids參數可以指定在哪些GPU上進行優化,output_device指定輸出到哪個GPU上。唯一的不同就在于前者直接利用多GPU并行計算得出結果,而后者則返回一個新的module,能夠自動在多GPU上進行并行加速。

DataParallel并行的方式,是將輸入一個batch的數據均分成多份,分別送到對應的GPU進行計算,各個GPU得到的梯度累加。與Module相關的所有數據也都會以淺復制的方式復制多份,在此需要注意,在module中屬性應該是只讀的。

并行處理機制是,首先將模型加載到主 GPU 上,然后再將模型復制到各個指定的從 GPU 中,然后將輸入數據按 batch 維度進行劃分,具體來說就是每個 GPU 分配到的數據 batch 數量是總輸入數據的 batch 除以指定 GPU 個數。每個 GPU 將針對各自的輸入數據獨立進行 forward 計算,最后將各個 GPU 的 loss 進行求和,再用反向傳播更新單個 GPU 上的模型參數,再將更新后的模型參數復制到剩余指定的 GPU 中,這樣就完成了一次迭代計算。所以該接口還要求輸入數據的 batch 數量要不小于所指定的 GPU 數量。

注意:

  • 在一個或多個 GPU 上訓練大批量模型: 梯度累積
  • 充分利用多 GPU 機器:torch.nn.DataParallel
  • 多 GPU 機器上的均衡負載 : PyTorch-Encoding 的 PyTorch 包,包括兩個模塊:DataParallelModel 和 DataParallelCriterion
  • 分布式訓練:在多臺機器上訓練: PyTorch 的 DistributedDataParallel
  • Pytorch 的多 GPU 處理接口是 torch.nn.DataParallel(module, device_ids),其中 module 參數是所要執行的模型,而 device_ids 則是指定并行的 GPU id 列表。

l Pytorch 的多 GPU 處理接口是 torch.nn.DataParallel(module, device_ids),其中 module 參數是所要執行的模型,而 device_ids 則是指定并行的 GPU id 列表。

6、 backward時具體如何更新參數

6.1、loss計算和反向傳播

import torch.nn as nn

criterion = nn.MSELoss().cuda()

output = model(input)

loss = criterion(output, target)

loss.backward()

通過定義損失函數:criterion,然后通過計算網絡真是輸出和真是標簽之間的誤差,得到網絡的損失值:loss;

最后通過loss.backward()完成誤差的反向傳播,通過pytorch的內在機制完成自動求導得到每個參數的梯度。

需要注意,在機器學習或者深度學習中,我們需要通過修改參數使得損失函數最小化或最大化,一般是通過梯度進行網絡模型的參數更新,通過loss的計算和誤差反向傳播,我們得到網絡中,每個參數的梯度值,后面我們再通過優化算法進行網絡參數優化更新。

6.2、網絡參數更新

在更新網絡參數時,我們需要選擇一種調整模型參數更新的策略,即優化算法。

優化算法中,簡單的有一階優化算法:

其中就是通常說的學習率,是函數的梯度;

自己的理解是,對于復雜的優化算法,基本原理也是這樣的,不過計算更加復雜。

在pytorch中,torch.optim是一個實現各種優化算法的包,可以直接通過這個包進行調用。

optimizer = torch.optim.Adam(model.parameters(), lr=0.01)

注意:

1)在前面部分1中,已經通過loss的反向傳播得到了每個參數的梯度,然后再本部分通過定義優化器(優化算法),確定了網絡更新的方式,在上述代碼中,我們將模型的需要更新的參數傳入優化器。

2)注意優化器,即optimizer中,傳入的模型更新的參數,對于網絡中有多個模型的網絡,我們可以選擇需要更新的網絡參數進行輸入即可,上述代碼,只會更新model中的模型參數。對于需要更新多個模型的參數的情況,可以參考以下代碼:

optimizer=torch.optim.Adam([{'params':model.parameters()},{'params': gru.parameters()}], lr=0.01)

3) 在優化前需要先將梯度歸零,即optimizer.zeros()。

6.3、loss計算和參數更新

import torch.nn as nn

import torch

criterion = nn.MSELoss().cuda()

optimizer = torch.optim.Adam(model.parameters(), lr=0.01)

output = model(input)

loss = criterion(output, target)

optimizer.zero_grad() # 將所有參數的梯度都置零

loss.backward() # 誤差反向傳播計算參數梯度

optimizer.step() # 通過梯度做一步參數更新

井陉县| 莎车县| 阿鲁科尔沁旗| 射洪县| 万盛区| 福建省| 容城县| 贡嘎县| 吕梁市| 怀安县| 婺源县| 钟山县| 新竹市| 和顺县| 平和县| 马公市| 阳泉市| 修水县| 嘉义市| 安吉县| 延长县| 九台市| 栾川县| 环江| 武定县| 湘乡市| 郎溪县| 东乡县| 吉林省| 元氏县| 咸宁市| 武宁县| 高陵县| 东港市| 靖宇县| 兴安县| 花莲市| 车致| 顺平县| 锡林浩特市| 扬中市|