《電子技術應用》
您所在的位置:首頁 > 可編程邏輯 > 業界動態 > 教程 | PyTorch經驗指南:技巧與陷阱

教程 | PyTorch經驗指南:技巧與陷阱

2018-07-30

PyTorch 的構建者表明,PyTorch 的哲學是解決當務之急,也就是說即時構建運行計算圖。目前,PyTorch 也已經借助這種即時運行的概念成為最受歡迎的框架之一,開發者能快速構建模型與驗證想法,并通過神經網絡交換格式 ONNX 在多個框架之間快速遷移。本文從基本概念開始介紹了 PyTorch 的使用方法、訓練經驗與技巧,并展示了可能出現的問題與解決方案。


項目地址:https://github.com/Kaixhin/grokking-pytorch


PyTorch 是一種靈活的深度學習框架,它允許通過動態神經網絡(例如利用動態控流——如 if 語句或 while 循環的網絡)進行自動微分。它還支持 GPU 加速、分布式訓練以及各類優化任務,同時還擁有許多更簡潔的特性。以下是作者關于如何利用 PyTorch 的一些說明,里面雖然沒有包含該庫的所有細節或最優方法,但可能會對大家有所幫助。


神經網絡是計算圖的一個子類。計算圖接收輸入數據,數據被路由到對數據執行處理的節點,并可能被這些節點轉換。在深度學習中,神經網絡中的神經元(節點)通常利用參數或可微函數轉換數據,這樣可以優化參數以通過梯度下降將損失最小化。更廣泛地說,函數是隨機的,圖結構可以是動態的。所以說,雖然神經網絡可能非常適合數據流式編程,但 PyTorch 的 API 卻更關注命令式編程——一種編程更常考慮的形式。這令讀取代碼和推斷復雜程序變得簡單,而無需損耗不必要的性能;PyTorch 速度很快,且擁有大量優化,作為終端用戶你毫無后顧之憂。


本文其余部分寫的是關于 grokking PyTorch 的內容,都是基于 MINIST 官網實例,應該要在學習完官網初學者教程后再查看。為便于閱讀,代碼以塊狀形式呈現,并帶有注釋,因此不會像純模塊化代碼一樣被分割成不同的函數或文件。


Pytorch 基礎


PyTorch 使用一種稱之為 imperative / eager 的范式,即每一行代碼都要求構建一個圖,以定義完整計算圖的一個部分。即使完整的計算圖還沒有構建好,我們也可以獨立地執行這些作為組件的小計算圖,這種動態計算圖被稱為「define-by-run」方法。

微信圖片_20180730224506.gif


PyTorch 張量


正如 PyTorch 文檔所說,如果我們熟悉 NumPy 的多維數組,那么 Torch 張量的很多操作我們能輕易地掌握。PyTorch 提供了 CPU 張量和 GPU 張量,并且極大地加速了計算的速度。


從張量的構建與運行就能體會,相比 TensorFLow,在 PyTorch 中聲明張量、初始化張量要簡潔地多。例如,使用 torch.Tensor(5, 3) 語句就能隨機初始化一個 5×3 的二維張量,因為 PyTorch 是一種動態圖,所以它聲明和真實賦值是同時進行的。


在 PyTorch 中,torch.Tensor 是一種多維矩陣,其中每個元素都是單一的數據類型,且該構造函數默認為 torch.FloatTensor。以下是具體的張量類型:

微信圖片_20180730224537.jpg

除了直接定義維度,一般我們還可以從 Python 列表或 NumPy 數組中創建張量。而且根據使用 Python 列表和元組等數據結構的習慣,我們可以使用相似的索引方式進行取值或賦值。PyTorch 同樣支持廣播(Broadcasting)操作,一般它會隱式地把一個數組的異常維度調整到與另一個算子相匹配的維度,以實現維度兼容。


自動微分模塊


TensorFlow、Caffe 和 CNTK 等大多數框架都使用靜態計算圖,開發者必須建立或定義一個神經網絡,并重復使用相同的結構來執行模型訓練。改變網絡的模式就意味著我們必須從頭開始設計并定義相關的模塊。


但 PyTorch 使用的技術為自動微分(automatic differentiation)。在這種機制下,系統會有一個 Recorder 來記錄我們執行的運算,然后再反向計算對應的梯度。這種技術在構建神經網絡的過程中十分強大,因為我們可以通過計算前向傳播過程中參數的微分來節省時間。


從概念上來說,Autograd 會維護一個圖并記錄對變量執行的所有運算。這會產生一個有向無環圖,其中葉結點為輸入向量,根結點為輸出向量。通過從根結點到葉結點追蹤圖的路徑,我們可以輕易地使用鏈式法則自動計算梯度。

微信圖片_20180730224603.jpg


在內部,Autograd 將這個圖表征為 Function 對象的圖,并且可以應用 apply() 計算評估圖的結果。在計算前向傳播中,當 Autograd 在執行請求的計算時,它還會同時構建一個表征梯度計算的圖,且每個 Variable 的 .grad_fn 屬性就是這個圖的輸入單元。在前向傳播完成后,我們可以在后向傳播中根據這個動態圖來計算梯度。


PyTorch 還有很多基礎的模塊,例如控制學習過程的最優化器、搭建深度模型的神經網絡模塊和數據加載與處理等。這一節展示的張量與自動微分模塊是 PyTorch 最為核心的概念之一,讀者可查閱 PyTorch 文檔了解更詳細的內容。


下面作者以 MNIST 為例從數據加載到模型測試具體討論了 PyTorch 的使用、思考技巧與陷阱。


PyTorch 實用指南


導入


import argparse
import torch
from torch import nn, optim
from torch.nn import functional as F
from torch.utils.data import DataLoader
from torchvision import datasets, transforms


除了用于計算機視覺問題的 torchvision 模塊外,這些都是標準化導入。


設置


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('--epochs', type=int, default=10, metavar='N',
 help='number of epochs to train (default: 10)')
parser.add_argument('--lr', type=float, default=0.01, metavar='LR',
 help='learning rate (default: 0.01)')
parser.add_argument('--momentum', type=float, default=0.5, metavar='M',
 help='SGD momentum (default: 0.5)')
parser.add_argument('--no-cuda', action='store_true', default=False,
 help='disables CUDA training')
parser.add_argument('--seed', type=int, default=1, metavar='S',
 help='random seed (default: 1)')
parser.add_argument('--save-interval', type=int, default=10, metavar='N',
 help='how many batches to wait before checkpointing')
parser.add_argument('--resume', action='store_true', default=False,
 help='resume training from checkpoint')
args = parser.parse_args()

use_cuda = torch.cuda.is_available() and not args.no_cuda
device = torch.device('cuda' if use_cuda else 'cpu')
torch.manual_seed(args.seed)
if use_cuda:
 torch.cuda.manual_seed(args.seed)


argparse 是在 Python 中處理命令行參數的一種標準方式。


編寫與設備無關的代碼(可用時受益于 GPU 加速,不可用時會倒退回 CPU)時,選擇并保存適當的 torch.device, 不失為一種好方法,它可用于確定存儲張量的位置。關于與設備無關代碼的更多內容請參閱官網文件。PyTorch 的方法是使用戶能控制設備,這對簡單示例來說有些麻煩,但是可以更容易地找出張量所在的位置——這對于 a)調試很有用,并且 b)可有效地使用手動化設備。


對于可重復實驗,有必要為使用隨機數生成的任何數據設置隨機種子(如果也使用隨機數,則包括隨機或 numpy)。要注意,cuDNN 用的是非確定算法,可以通過語句 torch.backends.cudnn.enabled = False 將其禁用。


數據


train_data = datasets.MNIST('data', train=True, download=True,
 transform=transforms.Compose([
 transforms.ToTensor(),
 transforms.Normalize((0.1307,), (0.3081,))]))
test_data = datasets.MNIST('data', train=False, transform=transforms.Compose([
 transforms.ToTensor(),
 transforms.Normalize((0.1307,), (0.3081,))]))

train_loader = DataLoader(train_data, batch_size=args.batch_size,
 shuffle=True, num_workers=4, pin_memory=True)
test_loader = DataLoader(test_data, batch_size=args.batch_size,
 num_workers=4, pin_memory=True)


torchvision.transforms 對于單張圖像有非常多便利的轉換工具,例如裁剪和歸一化等。


DataLoader 包含非常多的參數,除了 batch_size 和 shuffle,num_workers 和 pin_memory 對于高效加載數據同樣非常重要。例如配置 num_workers > 0 將使用子進程異步加載數據,而不是使用一個主進程塊加載數據。參數 pin_memory 使用固定 RAM 以加速 RAM 到 GPU 的轉換,且在僅使用 CPU 時不會做任何運算。


模型


class Net(nn.Module):
 def __init__(self):
 super(Net, self).__init__()
 self.conv1 = nn.Conv2d(1, 10, kernel_size=5)
 self.conv2 = nn.Conv2d(10, 20, kernel_size=5)
 self.conv2_drop = nn.Dropout2d()
 self.fc1 = nn.Linear(320, 50)
 self.fc2 = nn.Linear(50, 10)

 def forward(self, x):
 x = F.relu(F.max_pool2d(self.conv1(x), 2))
 x = F.relu(F.max_pool2d(self.conv2_drop(self.conv2(x)), 2))
 x = x.view(-1, 320)
 x = F.relu(self.fc1(x))
 x = self.fc2(x)
 return F.log_softmax(x, dim=1)

model = Net().to(device)
optimiser = optim.SGD(model.parameters(), lr=args.lr, momentum=args.momentum)

if args.resume:
 model.load_state_dict(torch.load('model.pth'))
 optimiser.load_state_dict(torch.load('optimiser.pth'))


神經網絡初始化一般包括變量、包含可訓練參數的層級、可能獨立的可訓練參數和不可訓練的緩存器。隨后前向傳播將這些初始化參數與 F 中的函數結合,其中該函數為不包含參數的純函數。有些開發者喜歡使用完全函數化的網絡(如保持所有參數獨立,使用 F.conv2d 而不是 nn.Conv2d),或者完全由 layers 函數構成的網絡(如使用 nn.ReLU 而不是 F.relu)。


在將 device 設置為 GPU 時,.to(device) 是一種將設備參數(和緩存器)發送到 GPU 的便捷方式,且在將 device 設置為 CPU 時不會做任何處理。在將網絡參數傳遞給優化器之前,把它們傳遞給適當的設備非常重要,不然的話優化器不能正確地追蹤參數。


神經網絡(nn.Module)和優化器(optim.Optimizer)都能保存和加載它們的內部狀態,而.load_state_dict(state_dict) 是完成這一操作的推薦方法,我們可以從以前保存的狀態字典中加載兩者的狀態并恢復訓練。此外,保存整個對象可能會出錯。


這里沒討論的一些注意事項即前向傳播可以使用控制流,例如一個成員變量或數據本身能決定 if 語句的執行。此外,在前向傳播的過程中打印張量也是可行的,這令 debug 更加簡單。最后,前向傳播可以使用多個參數。以下使用間斷的代碼塊展示這一點:


def forward(self, x, hx, drop=False):
 hx2 = self.rnn(x, hx)
 print(hx.mean().item(), hx.var().item())
 if hx.max.item() > 10 or self.can_drop and drop:
 return hx
 else:
 return hx2


訓練


model.train()
train_losses = []

for i, (data, target) in enumerate(train_loader):
 data, target = data.to(device), target.to(device)
 optimiser.zero_grad()
 output = model(data)
 loss = F.nll_loss(output, target)
 loss.backward()
 train_losses.append(loss.item())
 optimiser.step()

 if i % 10 == 0:
 print(i, loss.item())
 torch.save(model.state_dict(), 'model.pth')
 torch.save(optimiser.state_dict(), 'optimiser.pth')
 torch.save(train_losses, 'train_losses.pth')


網絡模塊默認設置為訓練模式,這影響了某些模塊的工作方式,最明顯的是 dropout 和批歸一化。最好用.train() 對其進行手動設置,這樣可以把訓練標記向下傳播到所有子模塊。


在使用 loss.backward() 收集一系列新的梯度以及用 optimiser.step() 做反向傳播之前,有必要手動地將由 optimiser.zero_grad() 優化的參數梯度歸零。默認情況下,PyTorch 會累加梯度,在單次迭代中沒有足夠資源來計算所有需要的梯度時,這種做法非常便利。


PyTorch 使用一種基于 tape 的自動化梯度(autograd)系統,它收集按順序在張量上執行的運算,然后反向重放它們來執行反向模式微分。這正是為什么 PyTorch 如此靈活并允許執行任意計算圖的原因。如果沒有張量需要做梯度更新(當你需要為該過程構建一個張量時,你必須設置 requires_grad=True),則不需要保存任何圖。然而,網絡傾向于包含需要梯度更新的參數,因此任何網絡輸出過程中執行的計算都將保存在圖中。因此如果想保存在該過程中得到的數據,你將需要手動禁止梯度更新,或者,更常見的做法是將其保存為一個 Python 數(通過一個 Python 標量上的.item())或者 NumPy 數組。更多關于 autograd 的細節詳見官網文件。


截取計算圖的一種方式是使用.detach(),當通過沿時間的截斷反向傳播訓練 RNN 時,數據流傳遞到一個隱藏狀態可能會應用這個函數。當對損失函數求微分(其中一個成分是另一個網絡的輸出)時,也會很方便。但另一個網絡不應該用「loss - examples」的模式進行優化,包括在 GAN 訓練中從生成器的輸出訓練判別器,或使用價值函數作為基線(例如 A2C)訓練 actor-critic 算法的策略。另一種在 GAN 訓練(從判別器訓練生成器)中能高效阻止梯度計算的方法是在整個網絡參數上建立循環,并設置 param.requires_grad=False,這在微調中也很常用。


除了在控制臺/日志文件里記錄結果以外,檢查模型參數(以及優化器狀態)也是很重要的。你還可以使用 torch.save() 來保存一般的 Python 對象,但其它標準選擇還包括內建的 pickle。


測試


model.eval()
test_loss, correct = 0, 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, size_average=False).item()
 pred = output.argmax(1, keepdim=True)
 correct += pred.eq(target.view_as(pred)).sum().item()

test_loss /= len(test_data)
acc = correct / len(test_data)
print(acc, test_loss)


為了早點響應.train(),應利用.eval() 將網絡明確地設置為評估模式。


正如前文所述,計算圖通常會在使用網絡時生成。通過 with torch.no_grad() 使用 no_grad 上下文管理器,可以防止這種情況發生。


其它


內存有問題?可以查看官網文件獲取幫助。


CUDA 出錯?它們很難調試,而且通常是一個邏輯問題,會在 CPU 上產生更易理解的錯誤信息。如果你計劃使用 GPU,那最好能夠在 CPU 和 GPU 之間輕松切換。更普遍的開發技巧是設置代碼,以便在啟動合適的項目(例如準備一個較小/合成的數據集、運行一個 train + test epoch 等)之前快速運行所有邏輯來檢查它。如果這是一個 CUDA 錯誤,或者你沒法切換到 CPU,設置 CUDA_LAUNCH_BLOCKING=1 將使 CUDA 內核同步啟動,從而提供更詳細的錯誤信息。


torch.multiprocessing,甚至只是一次運行多個 PyTorch 腳本的注意事項。因為 PyTorch 使用多線程 BLAS 庫來加速 CPU 上的線性代數計算,所以它通常需要使用多個內核。如果你想一次運行多個任務,在具有多進程或多個腳本的情況下,通過將環境變量 OMP_NUM_THREADS 設置為 1 或另一個較小的數字來手動減少線程,這樣做減少了 CPU thrashing 的可能性。官網文件還有一些其它注意事項,尤其是關于多進程。


本站內容除特別聲明的原創文章之外,轉載內容只為傳遞更多信息,并不代表本網站贊同其觀點。轉載的所有的文章、圖片、音/視頻文件等資料的版權歸版權所有權人所有。本站采用的非本站原創文章及圖片等內容無法一一聯系確認版權者。如涉及作品內容、版權和其它問題,請及時通過電子郵件或電話通知我們,以便迅速采取適當措施,避免給雙方造成不必要的經濟損失。聯系電話:010-82306118;郵箱:aet@chinaaet.com。
亚洲一区二区欧美_亚洲丝袜一区_99re亚洲国产精品_日韩亚洲一区二区
亚洲人成在线播放| 欧美诱惑福利视频| 香港久久久电影| 一区二区三区偷拍| 亚洲日产国产精品| 亚洲国产精品久久人人爱蜜臀| 国产女主播一区二区三区| 欧美视频导航| 欧美性猛交xxxx乱大交蜜桃| 欧美激情在线观看| 欧美插天视频在线播放| 久久综合国产精品| 蜜臀a∨国产成人精品| 开心色5月久久精品| 美日韩精品免费| 蜜臀99久久精品久久久久久软件| 麻豆成人在线| 蜜桃av噜噜一区| 蜜桃伊人久久| 欧美激情一区二区| 欧美人与性禽动交情品| 欧美激情二区三区| 欧美精品一区二区三区久久久竹菊| 欧美1区免费| 欧美劲爆第一页| 欧美日韩国产免费观看| 欧美日韩免费一区二区三区| 欧美日韩伦理在线| 国产精品欧美日韩一区| 国产欧美韩国高清| 韩国欧美一区| 在线不卡欧美| 日韩视频第一页| 亚洲午夜电影| 午夜在线视频一区二区区别| 欧美制服丝袜第一页| 亚洲国内自拍| 一本色道久久综合精品竹菊 | 欧美怡红院视频| 久久精品女人的天堂av| 亚洲日本理论电影| 亚洲午夜av在线| 香蕉视频成人在线观看| 久久精品亚洲一区二区三区浴池| 久久色中文字幕| 欧美日本韩国一区| 国产精品私拍pans大尺度在线| 国产亚洲亚洲| 亚洲国产日本| 亚洲小视频在线观看| 久久精品99国产精品| 亚洲另类在线视频| 午夜精彩国产免费不卡不顿大片| 久久久久久亚洲精品中文字幕| 欧美a级片网站| 国产精品扒开腿爽爽爽视频| 很黄很黄激情成人| 一区二区三区国产在线观看| 久久www成人_看片免费不卡| 99精品福利视频| 欧美一区二区三区精品电影| 免费在线看成人av| 国产精品久久久久久模特| 激情欧美丁香| 亚洲视频一区在线| 亚洲国产日韩一区二区| 亚洲午夜激情网页| 久久综合给合久久狠狠色 | 亚洲大片av| 亚洲一区二区免费看| 久久久久久网| 欧美先锋影音| 亚洲福利视频网| 亚洲欧美国产不卡| 一本久道久久综合中文字幕| 久久国产黑丝| 欧美日韩一区二区三区在线视频 | 黄色小说综合网站| 亚洲视频狠狠| 日韩系列欧美系列| 久久高清一区| 国产精品成人一区二区| 136国产福利精品导航网址| 亚洲砖区区免费| 一本色道综合亚洲| 免费看黄裸体一级大秀欧美| 国产精品久久久久免费a∨大胸 | 亚洲视频在线二区| 免费国产自线拍一欧美视频| 国产美女一区| 亚洲视屏一区| 一区二区三区导航| 欧美xxx成人| 国内精品**久久毛片app| 9国产精品视频| 亚洲美女视频网| 媚黑女一区二区| 好吊色欧美一区二区三区四区| 亚洲调教视频在线观看| 99视频一区二区| 欧美成人黑人xx视频免费观看| 国产亚洲精品一区二区| 亚洲一级一区| 亚洲男人的天堂在线| 欧美日韩精品欧美日韩精品| 亚洲国产美国国产综合一区二区| 久久精品国产v日韩v亚洲| 欧美在线视频全部完| 国产精品卡一卡二卡三| 日韩一二在线观看| av不卡在线观看| 欧美精品粉嫩高潮一区二区| 亚洲国产精品久久久久秋霞不卡| 亚洲国产精品久久久久秋霞不卡 | 亚洲欧美日韩另类| 午夜精品一区二区三区在线视| 欧美视频专区一二在线观看| av成人免费在线观看| 这里只有精品在线播放| 欧美日韩精品高清| 99这里只有精品| 亚洲一级二级| 国产精品福利网| 亚洲小少妇裸体bbw| 欧美一级大片在线观看| 国产老肥熟一区二区三区| 亚洲欧美国产精品va在线观看| 欧美一区二区视频观看视频| 国产伦精品一区二区三区免费 | 亚洲欧美日韩一区| 国产精品入口66mio| 亚洲欧美精品伊人久久| 欧美一区二区网站| 国内精品久久久久久| 亚洲国产精品一区二区尤物区| 免费亚洲婷婷| 亚洲精品国产精品国自产观看浪潮| a4yy欧美一区二区三区| 欧美调教视频| 亚洲欧美日韩区 | 欧美精品少妇一区二区三区| 亚洲精品乱码久久久久久蜜桃91 | 亚洲精品在线电影| 欧美日韩在线播| 亚洲一区二区三区中文字幕| 欧美一区在线看| 在线观看久久av| 99精品视频免费观看| 欧美性色aⅴ视频一区日韩精品| 亚洲专区在线视频| 玖玖视频精品| 日韩视频在线永久播放| 午夜精品一区二区在线观看| 国模精品一区二区三区色天香| 亚洲精品一区二区在线| 国产精品久久久久久久久久尿 | 亚洲网站视频福利| 久久久另类综合| 亚洲日本aⅴ片在线观看香蕉| 亚洲在线视频网站| 国产在线成人| 99视频一区| 国产一级久久| 一本久久a久久精品亚洲| 国产精品美女黄网| 亚洲国产欧美精品| 欧美日韩一区二区三区在线观看免| 欧美一区二区久久久| 欧美片第1页综合| 欧美亚洲一区二区在线观看| 欧美成人有码| 亚洲欧美一区二区三区久久| 免费观看成人鲁鲁鲁鲁鲁视频| 一区二区高清在线观看| 久久久人成影片一区二区三区观看 | 欧美日韩国产色站一区二区三区| 亚洲一区三区视频在线观看| 美女精品在线| 国产农村妇女精品一二区| 欧美亚洲综合久久| 欧美刺激午夜性久久久久久久| 一区二区三区成人| 久热这里只精品99re8久| 99视频精品| 久久久久网址| 一区二区三区视频观看| 久久久久综合网| 亚洲网站在线播放| 欧美成人四级电影| 亚洲欧美中文日韩v在线观看| 欧美91精品| 欧美亚洲尤物久久| 国产精品成人在线观看| 亚洲电影在线| 国产精品网站在线观看| 99精品视频网| 亚洲大片在线观看| 欧美在线首页| 中国女人久久久| 欧美人与性动交a欧美精品|