60 分钟极速入门 PyTorch
2017 年初,Facebook 在機器學習和科學計算工具 Torch 的基礎上,針對 Python 語言發布了一個全新的機器學習工具包 PyTorch。 因其在靈活性、易用性、速度方面的優秀表現,經過2年多的發展,目前 PyTorch 已經成為從業者最重要的研發工具之一。
現在為大家奉上出 60 分鐘極速入門 PyTorch 的小教程,助你輕松上手 PyTorch!大家也可直接在實驗樓學習:PyTorch 深度學習基礎課程。
PyTorch 基礎
PyTorch 使用一種稱之為 imperative / eager 的范式,即每一行代碼都要求構建一個圖,以定義完整計算圖的一個部分。即使完整的計算圖還沒有構建好,我們也可以獨立地執行這些作為組件的小計算圖,這種動態計算圖被稱為「define-by-run」方法。
PyTorch 具有兩個比較基礎的庫,所有基礎操作都需要提前引入。下面我們引入基礎庫。
import torch import torchvisionPyTorch 張量
PyTorch 的基本數據單元是張量(Tensor),它實際上是一種 N 維數組。
創建
創建一個未初始化 5X3 的矩陣:
x = torch.empty(5, 3)創建一個隨機初始化都矩陣:
x = torch.rand(5, 3)創建一個 0 填充的矩陣,指定數據類型為 long:
x = torch.zeros(5, 3, dtype=torch.long)創建一個張量并使用現有數據初始化:
x = torch.tensor([5.5, 3]) x根據現有張量創建新張量:
x = x.new_ones(5, 3, dtype=torch.double) # new_* 方法來創建對象 x覆蓋 dtype,對象的 size 是相同的,只是值和類型發生了變化:
x = torch.randn_like(x, dtype=torch.float) x獲取張量的 size:
x.size()加法運算
加法1:
y = torch.rand(5, 3) x + y加法2:
torch.add(x, y)加法3:提供一個輸出張量作為參數
result = torch.empty(5, 3) torch.add(x, y, out=result) result加法4: 替換
y.add_(x) # 將 x 加到 y y關于張量的操作,還有轉置,索引,切片,數學運算,線性代數,隨機數等。
張量與 Numpy 的轉換
將 PyTorch 張量轉換為 NumPy 數組:
a = torch.ones(5) a b = a.numpy() bNumPy 數組轉換成 PyTorch 張量時,可以使用 from_numpy 完成:
import numpy as npa = np.ones(5) b = torch.from_numpy(a) np.add(a, 1, out=a) a, b自動微分模式
PyTorch 中所有神經網絡的核心是 autograd。我們先簡單介紹一下這個包,然后訓練一個神經網絡。
autograd為張量上的所有操作提供了自動求導。它是一個在運行時定義的框架,這意味著反向傳播是根據你的代碼來確定如何運行。torch.Tensor 是這個包的核心類。如果設置 .requires_grad 為 True,那么將會追蹤所有對于該張量的操作。當完成計算后通過調用 .backward() 會自動計算所有的梯度,這個張量的所有梯度將會自動積累到 .grad 屬性。這也就完成了自動求導的過程。
下面編寫代碼實際使用自動微分變量。
導入自動梯度的運算包,主要用Variable這個類
from torch.autograd import Variable創建一個Variable,包裹了一個2*2張量,將需要計算梯度屬性置為True
x = Variable(torch.ones(2, 2), requires_grad=True) x按照張量的方式進行計算
y = x + 2 y.grad_fn #每個Variable都有一個creator(創造者節點)也可以進行復合運算,比如求均值mean
z = torch.mean(y * y) z.data #.data屬性可以返回z所包裹的tensor如果需要計算導數,你可以在 Tensor 上調用 .backward()。 如果 Tensor 是一個標量(即它包含一個元素數據)則不需要為 backward() 指定任何參數。但是,如果它有多個元素,你需要指定一個 gradient 參數來匹配張量的形狀。
z.backward() #梯度反向傳播 print(z.grad) # 無梯度信息 print(y.grad) # 無梯度信息 print(x.grad)下面的例子中,會讓矩陣 x 反復作用在向量 s 上,系統會自動記錄中間的依賴關系和長路徑。
s = Variable(torch.FloatTensor([[0.01, 0.02]]), requires_grad = True) #創建一個1*2的Variable(1維向量) x = Variable(torch.ones(2, 2), requires_grad = True) #創建一個2*2的矩陣型Variable for i in range(10):s = s.mm(x) #反復用s乘以x(矩陣乘法),注意s始終是1*2的Variable z = torch.mean(s) #對s中的各個元素求均值,得到一個1*1的scalar(標量,即1*1張量)然后我們得到了一個復雜的“深度”計算圖。
z.backward() #在具有很長的依賴路徑的計算圖上用反向傳播算法計算葉節點的梯度 print(x.grad) #x作為葉節點可以獲得這部分梯度信息 print(s.grad) #s不是葉節點,沒有梯度信息神經網絡
PyTorch 中,我們可以使用 torch.nn來構建神經網絡。
前面已經講過了 autograd,torch.nn 依賴 autograd 來定義模型并求導。nn.Module 中包含了構建神經網絡所需的各個層和 forward(input) 方法,該方法返回神經網絡的輸出。
下面給出一個示例網絡結構,該網絡也是經典的 LeNet。
它是一個簡單的前饋神經網絡,它接受一個輸入,然后一層接著一層地傳遞,最后輸出計算的結果。
神經網絡的典型訓練過程如下:
下面,參照上面的過程完成神經網絡訓練。
首先,定義上圖示例的神經網絡結構:
import torch.nn as nn import torch.nn.functional as Fclass Net(nn.Module):def __init__(self):super(Net, self).__init__()# 1 input image channel, 6 output channels, 3x3 square convolution# kernelself.conv1 = nn.Conv2d(1, 6, 3)self.conv2 = nn.Conv2d(6, 16, 3)# an affine operation: y = Wx + bself.fc1 = nn.Linear(16 * 6 * 6, 120) # 6*6 from image dimensionself.fc2 = nn.Linear(120, 84)self.fc3 = nn.Linear(84, 10)def forward(self, x):# Max pooling over a (2, 2) windowx = F.max_pool2d(F.relu(self.conv1(x)), (2, 2))# If the size is a square you can only specify a single numberx = F.max_pool2d(F.relu(self.conv2(x)), 2)x = x.view(-1, self.num_flat_features(x))x = F.relu(self.fc1(x))x = F.relu(self.fc2(x))x = self.fc3(x)return xdef num_flat_features(self, x):size = x.size()[1:] # all dimensions except the batch dimensionnum_features = 1for s in size:num_features *= sreturn num_featuresnet = Net() net模型中必須要定義 forward 函數,backward 函數(用來計算梯度)會被 autograd 自動創建。可以在 forward 函數中使用任何針對 Tensor 的操作。
net.parameters() 返回可被學習的參數(權重)列表和值:
params = list(net.parameters()) print(len(params)) print(params[0].size()) # conv1's .weight測試隨機輸入 32 x 32。注意,網絡(LeNet)期望的輸入大小是 32 x 32,如果使用 MNIST 數據集(28 x 28)來訓練這個網絡,請把圖片大小重新調整到 32 x 32。
input = torch.randn(1, 1, 32, 32) out = net(input) out將所有參數的梯度緩存清零,然后進行隨機梯度的的反向傳播:
net.zero_grad() out.backward(torch.randn(1, 10))在繼續之前,我們回顧一下到目前為止用到的類。
- torch.Tensor:自動調用 backward() 實現支持自動梯度計算的多維數組,并且保存關于這個向量的梯度。
- nn.Module:神經網絡模塊。封裝參數、移動到 GPU 上運行、導出、加載等。
- nn.Parameter:變量,當把它賦值給一個 Module 時,被自動地注冊為一個參數。
- autograd.Function:實現自動求導操作的前向和反向定義,每個變量操作至少創建一個函數節點。
至此,我們以及完成:
- 定義一個網絡
- 處理輸入,調用 backword。
接下來還需要:
- 計算損失。
- 更新網絡權重。
損失函數
一個損失函數接受一對 (output, target) 作為輸入,計算一個值來估計網絡的輸出和目標值相差多少。
torch.nn 中有很多不同的 損失函數。nn.MSELoss 是一個比較簡單的損失函數,它可以用來計算輸出和目標間的 均方誤差,例如:
output = net(input) target = torch.randn(10) # 隨機值作為樣例 target = target.view(1, -1) # 使 target 和 output 的 shape 相同 criterion = nn.MSELoss()loss = criterion(output, target) loss當我們添加 loss 計算之后,如果使用它 .grad_fn 屬性,將得到如下所示的計算圖:
input → conv2d → relu → maxpool2d → conv2d → relu → maxpool2d→ view → linear → relu → linear → relu → linear→ MSELoss→ loss所以,當我們調用 loss.backward() 時,會針對整個圖執行微分操作。圖中所有具有 requires_grad=True 的張量的 .grad 梯度會被累積起來。為了說明該情況,我們回溯幾個步驟:
print(loss.grad_fn) # MSELoss print(loss.grad_fn.next_functions[0][0]) # Linear print(loss.grad_fn.next_functions[0][0].next_functions[0][0]) # ReLU反向傳播
調用 loss.backward() 獲得反向傳播的誤差。但是在調用前需要清除已存在的梯度,否則梯度將被累加到已存在的梯度。現在,我們將調用 loss.backward(),并查看 conv1 層的偏差(bias)項在反向傳播前后的梯度。下方的代碼只能執行一次。
net.zero_grad() # 清除梯度print('conv1.bias.grad before backward') print(net.conv1.bias.grad)loss.backward()print('conv1.bias.grad after backward') print(net.conv1.bias.grad)torch.nn 中包含了各種用來構成深度神經網絡構建塊的模塊和損失函數,你可以閱讀 官方文檔。
更新權重
至此,剩下的最后一件事,那就是更新網絡的權重。
在實踐中最簡單的權重更新規則是隨機梯度下降(SGD):
weight=weight?learning?rate?gradient\text{weight}=\text{weight}-\text{learning rate}*\text{gradient}weight=weight?learning?rate?gradient
我們可以使用簡單的 Python 代碼實現這個規則:
learning_rate = 0.01 for f in net.parameters():f.data.sub_(f.grad.data * learning_rate)當你想使用其他不同的優化方法,如 SGD、Nesterov-SGD、Adam、RMSPROP 等來更新神經網絡參數時。可以借助于 PyTorch 中的 torch.optim 快速實現。使用它們非常簡單:
import torch.optim as optim# 創建優化器 optimizer = optim.SGD(net.parameters(), lr=0.01)# 執行一次訓練迭代過程 optimizer.zero_grad() # 梯度置零 output = net(input) loss = criterion(output, target) loss.backward() optimizer.step() # 更新 loss多執行幾次,觀察損失值的變化情況。
訓練一個分類器
上面,你已經看到如何去定義一個神經網絡,計算損失值和更新網絡的權重。接下來,我們實現一個圖像分類神經網絡。
一般情況下處理圖像、文本、音頻和視頻數據時,可以使用標準的 Python 來加載數據為 NumPy 數組。然后把這個數組轉換成torch.*Tensor。
- 圖像可以使用 Pillow, OpenCV。
- 音頻可以使用 SciPy, librosa。
- 文本可以使用原始 Python 和 Cython 來加載,或者使用 NLTK 或 SpaCy 處理。
特別地,對于圖像任務,PyTorch 提供了專門的包 torchvision,它包含了處理一些基本圖像數據集的方法。這些數據集包括 Imagenet, CIFAR10, MNIST 等。除了數據加載以外,torchvision 還包含了圖像轉換器,torchvision.datasets 和 torch.utils.data.DataLoader 數據加載器。
torchvision不僅提供了巨大的便利,也避免了代碼的重復。接下來,我們使用 CIFAR10 數據集完成分類器訓練。該數據集有如下 10 個類別:airplane, automobile, bird, cat, deer, dog, frog, horse, ship, truck。CIFAR-10 的圖像都是 3×32×323 \times 32 \times 323×32×32 ,即 3 個顏色通道,32×3232 \times 3232×32 像素。
[外鏈圖片轉存失敗(img-vpqBKwiw-1565776272396)(https://pytorch.org/tutorials/_images/cifar10.png)]
訓練一個圖像分類器,基本流程如下:
讀取和歸一化 CIFAR10
使用 torchvision 可以非常容易地加載 CIFAR10。torchvision 的輸出是 [0,1] 的 PILImage 圖像,我們把它轉換為歸一化范圍為 [-1, 1] 的張量。
import torchvision import torchvision.transforms as transforms# 圖像預處理步驟 transform = transforms.Compose([transforms.ToTensor(), transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))]) # 訓練數據加載器 trainset = torchvision.datasets.CIFAR10(root='./data', train=True, download=True, transform=transform) trainloader = torch.utils.data.DataLoader(trainset, batch_size=4, shuffle=True, num_workers=2) # 測試數據加載器 testset = torchvision.datasets.CIFAR10(root='./data', train=False, download=True, transform=transform) testloader = torch.utils.data.DataLoader(testset, batch_size=4, shuffle=False, num_workers=2) # 圖像類別 classes = ('plane', 'car', 'bird', 'cat', 'deer','dog', 'frog', 'horse', 'ship', 'truck')trainloader, testloader我們可視化其中的一些訓練圖像。
import matplotlib.pyplot as plt %matplotlib inlinedef imshow(img):# 展示圖像的函數img = img / 2 + 0.5 # 反向歸一化npimg = img.numpy()plt.imshow(np.transpose(npimg, (1, 2, 0)))# 獲取隨機數據 dataiter = iter(trainloader) images, labels = dataiter.next()# 展示圖像 imshow(torchvision.utils.make_grid(images)) # 顯示圖像標簽 print(' '.join('%5s' % classes[labels[j]] for j in range(4)))定義一個卷積神經網絡
從之前的神經網絡一節復制神經網絡代碼,并修改輸入為 3 通道圖像。
import torch.nn as nn import torch.nn.functional as Fclass Net(nn.Module):def __init__(self):super(Net, self).__init__()self.conv1 = nn.Conv2d(3, 6, 5)self.pool = nn.MaxPool2d(2, 2)self.conv2 = nn.Conv2d(6, 16, 5)self.fc1 = nn.Linear(16 * 5 * 5, 120)self.fc2 = nn.Linear(120, 84)self.fc3 = nn.Linear(84, 10)def forward(self, x):x = self.pool(F.relu(self.conv1(x)))x = self.pool(F.relu(self.conv2(x)))x = x.view(-1, 16 * 5 * 5)x = F.relu(self.fc1(x))x = F.relu(self.fc2(x))x = self.fc3(x)return xnet = Net() net定義損失函數和優化器
我們使用交叉熵作為損失函數,使用帶動量的隨機梯度下降完成參數優化。
criterion = nn.CrossEntropyLoss() optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9) optimizer訓練網路
有趣的訓練過程開始了。只需在數據迭代器上循環,將數據輸入給網絡,并優化。由于使用了卷積神經網絡,該訓練時間較長,請耐心等待。
for epoch in range(1): # 迭代一次running_loss = 0.0for i, data in enumerate(trainloader, 0):# 獲取輸入inputs, labels = data# 梯度置 0optimizer.zero_grad()# 正向傳播,反向傳播,優化outputs = net(inputs)loss = criterion(outputs, labels)loss.backward()optimizer.step()# 打印狀態信息running_loss += loss.item()if i % 200 == 199: # 每 200 批次打印一次print('[%d, %5d] loss: %.3f' %(epoch + 1, i + 1, running_loss / 200))running_loss = 0.0 print('Finished Training.')在測試集上測試網絡
我們在整個訓練集上進行了訓練,但是需要檢查網絡是否從數據集中學習到有用的東西。一般情況下,可以通過預測神經網絡輸出的類別標簽與實際情況標簽進行對比來進行檢測。如果預測正確,我們把該樣本添加到正確預測列表。
第一步,顯示測試集中的圖片并熟悉圖片內容。
dataiter = iter(testloader) images, labels = dataiter.next()# 顯示圖片 imshow(torchvision.utils.make_grid(images)) print('GroundTruth: ', ' '.join('%5s' % classes[labels[j]] for j in range(4)))讓我們看看神經網絡認為以上圖片是什么。
outputs = net(images) outputs輸出是 10 個標簽的權重。一個類別的權重越大,神經網絡越認為它是這個類別。所以讓我們得到最高權重的標簽。
_, predicted = torch.max(outputs, 1)print('Predicted: ', ' '.join('%5s' % classes[predicted[j]] for j in range(4)))結果看來不錯。接下來讓看看網絡在整個測試集上的結果如何。
correct = 0 total = 0 with torch.no_grad():for data in testloader:images, labels = dataoutputs = net(images)_, predicted = torch.max(outputs.data, 1)total += labels.size(0)correct += (predicted == labels).sum().item()print('Accuracy of the network on the 10000 test images: %d%%' %(100 * correct / total))快速入門小教程到此為止,想要進一步深入了解 PyTorch 。可移步實驗樓學習《PyTorch 入門與實戰》。
本訓練營由實驗樓聯合集智學園共同制作,作為《深度學習原理與PyTorch實戰》書籍的配套實踐內容。首先通過 1 個實驗讓你快速入門 PyTorch 基礎,緊接著 9 個實戰案例的實驗講解。不僅循序漸進地讓你掌握PyTorch的基本使用、神經網絡的搭建、卷積神經網絡和循環神經網絡的實現,而且全面深入地讓你了解計算機視覺、自然語言處理、遷移學習,以及最新的對抗學習和深度強化學習等前沿技術。
總結
以上是生活随笔為你收集整理的60 分钟极速入门 PyTorch的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 大牛推荐的15本学习数据分析挖掘的好书
- 下一篇: 常见数据结构的 Python 实现(建议