DataCamp的Sayak Paul帶你入門張量、PyTorch。
在深度學習中,常常看到張量是數據結構的基石這一說法。Google的機器學習庫TensorFlow甚至都以張量(tensor)命名。張量是線性代數中用到的一種數據結構,類似向量和矩陣,你可以在張量上進行算術運算。
PyTorch(Facebook創建的python包,提供兩個高層特性:1) 類似Numpy的基于GPU加速的張量運算 2) 在基于回放(tape-based)的自動微分系統之上構建的深度神經網絡。
本教程將介紹什么是張量,如何在PyTorch中操作張量:
張量介紹
PyTorch介紹
PyTorch安裝步驟
PyTorch下的一些張量操作
基于PyTorch實現一個簡單的神經網絡
閑話少敘,讓我們開始介紹張量吧。
張量介紹
張量是向量和矩陣的推廣,可以理解為多維數組。知名的《深度學習》(Goodfellow等編寫)是這樣介紹張量的:
在一般意義上,以基于可變數目的軸的規則網格組織的一組數字稱為張量。
標量是零階張量。向量是一階張量,矩陣是二階張量。
下面是張量的示意圖:
現在讓我們以更清晰易懂的方式構建張量背后的直覺。
張量是現代機器學習的基本構建。它是一個數據容器,大多數情況下包含數字,有時可能包含字符串(不過這罕見)。所以可以把張量想象成一桶數字。
人們經常混用張量和多維數組。不過有時需要嚴格區分兩者,如StackExchange指出:
張量和多維數組是不同類型的對象。前者是一種函數,后者是適宜在坐標系統中表示張量的一種數據結構。
在數學上,張量由多元線性函數定義。一個多元線性函數包含多個向量變量。張量域是張量值函數。更嚴謹的數學解釋,可以參考https://math.stackexchange.com/q/10282
所以,張量是需要定義的函數或容器。實際上,當數據傳入時,計算才真正發生。當不需要嚴格區分數組和張量的時候,數組或多維數組(1D, 2D, …, ND)一般可以視作張量。
現在我們稍微講下張量表述(Tensor notation)。
張量表述和矩陣類似,一般用大寫字母表示張量,帶整數下標的小寫字母表示張量中的標量值。
標量、向量、矩陣的許多運算同樣適用于張量。
張量和張量代數是物理和工程領域廣泛使用的工具。機器學習的許多技術,深度學習模型的訓練和操作,常常使用張量這一術語進行描述。
PyTorch介紹
PyTorch是一個非常靈活的基于Python的深度學習研究平臺。
PyTorch特性
提供各種張量的常規操作。
基于回放的自動微分系統。
不同于TensorFlow、Theano、Caffe、CNTK等大多數框架采用的靜態圖系統,PyTorch采用動態圖系統。
最小化框架開銷,可基于GPU加速。
相比Torch等替代品,PyTorch的內存使用非常高效。這讓你可以訓練比以往更大的深度學習模型。
Kirill Dubovikov寫的PyTorch vs TensorFlow?—?spotting the difference比較了PyTorch和TensorFlow這兩個框架。如果你想了解TensorFlow,可以看看Karlijn Willems寫的教程TensorFlow Tutorial For Beginners。
PyTorch安裝步驟
PyTorch的安裝很簡單。如果你的顯卡支持,可以安裝GPU版本的PyTorch。
你可以使用pip安裝torch、torchvision這兩個包,也可以使用conda安裝pytorch torchvision這兩個包。注意,Windows平臺上,PyTorch不支持Python 2.7,需要基于Python 3.5以上的版本安裝。
具體的安裝命令可以通過PyTorch官網查詢: https://pytorch.org/get-started/locally/
好了,下面讓我們直接深入PyTorch下的一些張量算術。
PyTorch下的一些張量操作
首先,導入所需的庫:
import torch
如果出現報錯,說明PyTorch沒有安裝成功,請參考上一節重新安裝。
現在,我們構造一個5×3的矩陣:
x = torch.rand(5, 3)
print(x)
輸出:
tensor([[ 0.5991, 0.9365, 0.6120],
[ 0.3622, 0.1408, 0.8811],
[ 0.6248, 0.4808, 0.0322],
[ 0.2267, 0.3715, 0.8430],
[ 0.0145, 0.0900, 0.3418]])
再構造一個5×3的矩陣,不過這次用零初始化,并指定數據類型為long:
x = torch.zeros(5, 3, dtype=torch.long)
print(x)
輸出:
tensor([[ 0, 0, 0],
[ 0, 0, 0],
[ 0, 0, 0],
[ 0, 0, 0],
[ 0, 0, 0]])
構造張量時直接提供數據:
x = torch.tensor([5.5, 3])
print(x)
輸出:
tensor([ 5.5000, 3.0000])
如果你想檢驗下自己是否理解了PyTorch中的張量,那可以思考下上面的張量x是什么類別的。
基于已有張量,可以創建新張量——新張量會復用輸入張量的屬性,比如dtype(數據類型),除非另外給出新值:
x = x.new_ones(5, 3, dtype=torch.double)
print(x)
x = torch.randn_like(x, dtype=torch.float)
print(x)
輸出:
tensor([[ 1., 1., 1.],
[ 1., 1., 1.],
[ 1., 1., 1.],
[ 1., 1., 1.],
[ 1., 1., 1.]], dtype=torch.float64)
tensor([[-1.2174, 1.1807, 1.4249],
[-1.1114, -0.8098, 0.4003],
[ 0.0780, -0.5011, -1.0985],
[ 1.8160, -0.3778, -0.8610],
[-0.7109, -2.0509, -1.2079]])
獲取張量的尺寸:
print(x.size())
輸出:
torch.Size([5, 3])
注意,torch.Size事實上是一個元組,支持所有元組操作。
現在,讓我們看下張量的加法。
張量加法
兩個張量分素相加,得到維度一致的張量,結果張量中每個標量的值是相應標量的和。
y = torch.rand(5, 3)
print(x)
print(y)
print(x + y)
輸出:
tensor([[-1.2174, 1.1807, 1.4249],
[-1.1114, -0.8098, 0.4003],
[ 0.0780, -0.5011, -1.0985],
[ 1.8160, -0.3778, -0.8610],
[-0.7109, -2.0509, -1.2079]])
tensor([[ 0.8285, 0.7619, 0.1147],
[ 0.1624, 0.8994, 0.6119],
[ 0.2802, 0.2950, 0.7098],
[ 0.8132, 0.3382, 0.4383],
[ 0.6738, 0.2022, 0.3264]])
tensor([[-0.3889, 1.9426, 1.5396],
[-0.9490, 0.0897, 1.0122],
[ 0.3583, -0.2061, -0.3887],
[ 2.6292, -0.0396, -0.4227],
[-0.0371, -1.8487, -0.8815]])
除了使用+運算符外,也可以調用torch.add方法(兩者是等價的):
print(torch.add(x, y))
下面我們來看張量減法。
張量減法
兩個張量分素相減,得到維度一致的張量,結果張量中每個標量的值是相應標量之差。
接著我們將討論張量相乘。
張量乘法
假設mat1是一個(n×m)的張量,mat2是一個(m×p)的張量,兩者相乘,將得到一個(n×p)的張量。
mat1 = torch.randn(2, 3)
mat2 = torch.randn(3, 3)
print(mat1)
print(mat2)
print(torch.mm(mat1, mat2))
輸出:
tensor([[ 1.9490, -0.6503, -1.9448],
[-0.7126, 1.0519, -0.4250]])
tensor([[ 0.0846, 0.4410, -0.0625],
[-1.3264, -0.5265, 0.2575],
[-1.3324, 0.6644, 0.3528]])
tensor([[ 3.6185, -0.0901, -0.9753],
[-0.8892, -1.1504, 0.1654]])
注意,torch.mm()不支持廣播(broadcast)。
廣播
“廣播”這一術語用于描述如何在形狀不一的數組上應用算術運算。在滿足特定限制的前提下,較小的數組“廣播至”較大的數組,使兩者形狀互相兼容。廣播提供了一個向量化數組操作的機制,這樣遍歷就發生在C層面,而不是Python層面。廣播可以避免不必要的數據復制,通常導向高效的算法實現。不過,也存在不適用廣播的情形(可能導致拖慢計算過程的低效內存使用)。
可廣播的一對張量需滿足以下規則:
每個張量至少有一個維度。
迭代維度尺寸時,從尾部的維度開始,維度尺寸或者相等,或者其中一個張量的維度尺寸為一,或者其中一個張量不存在這個維度。
讓我們通過幾段代碼來理解PyTorch的廣播機制。
x=torch.empty(5,7,3)
y=torch.empty(5,7,3)
相同形狀的張量總是可廣播的,因為總能滿足以上規則。
x=torch.empty((0,))
y=torch.empty(2,2)
不可廣播(x不滿足第一條規則)。
# 為了清晰易讀,可以對齊尾部
x=torch.empty(5,3,4,1)
y=torch.empty( 3,1,1)
x和y可廣播:
倒數第一個維度:兩者的尺寸均為1
倒數第二個維度:y尺寸為1
倒數第三個維度:兩者尺寸相同
倒數第四個維度:y該維度不存在
但下面一對就不可廣播了:
x=torch.empty(5,2,4,1)
y=torch.empty( 3,1,1)
這是因為倒數第三個維度:2 != 3
現在你對“可廣播”這一概念已經有所了解了,讓我們看下,廣播后的張量是什么樣的。
如果張量x和張量y是可廣播的,那么廣播后的張量尺寸按照如下方法計算:
如果x和y的維數不等,在維數較少的張量上添加尺寸為1的維度。結果維度尺寸是x和y相應維度尺寸的較大者。例如:
x=torch.empty(5,1,4,1)
y=torch.empty( 3,1,1)
(x+y).size()
輸出:
torch.Size([5, 3, 4, 1])
再如:
x=torch.empty(1)
y=torch.empty(3,1,7)
(x+y).size()
輸出:
torch.Size([3, 1, 7])
再看一個不可廣播的例子:
x=torch.empty(5,2,4,1)
y=torch.empty(3,1,1)
(x+y).size()
報錯:
---------------------------------------------------------------------------
RuntimeError Traceback (most recent call last)
1 x=torch.empty(5,2,4,1)
2 y=torch.empty(3,1,1)
----> 3 (x+y).size()
RuntimeError: The size of tensor a (2) must match the size of tensor b (3) at non-singleton dimension 1
你現在應該已經掌握了廣播這個概念了!
張量乘積是最常見的張量乘法,但也存在其他種類的張量乘法,例如張量點積和張量縮并。
借助Numpy橋,PyTorch張量和NumPy數組之間的互相轉換極其迅速。下面就讓我們來了解一下這個概念。
NumPy橋
NumPy橋使得PyTorch張量和NumPy數組共享底層內存地址,對其中之一的修改會反映到另一個上。
轉換PyTorch張量至NumPy數組。
a = torch.ones(5)
b = a.numpy()
print(a)
print(b)
輸出:
tensor([ 1., 1., 1., 1., 1.])
[1.1.1.1.1.]
在這一節中,我們討論了一些基本的張量算術,例如加法、減法、張量乘積。下一節我們將使用PyTorch實現一個基本的神經網絡。
基于PyTorch實現一個簡單的神經網絡
如果你想要溫習一下神經網絡的概念,可以參考以下文章:
初窺神經網絡內部機制
從頭開始搭建三層神經網絡
基于Numpy實現神經網絡:反向傳播、梯度下降
在實現神經網絡之前,我們先來討論一下自動微分,這是PyTorch下所有神經網絡的核心,在進行反向傳播計算梯度時尤其有用。
PyTorch的autograd模塊為張量的所有運算提供了自動微分。這是一個define-by-run框架,也就是說,反向傳播由代碼如何運行定義,每個迭代都可以不一樣。
讓我們直接用代碼展示自動微分是如何工作的。
x = torch.ones(2, 2, requires_grad=True)
print(x)
輸出:
tensor([[ 1., 1.],
[ 1., 1.]])
進行加法運算:
y = x + 2
print(y)
輸出:
tensor([[ 3., 3.],
[ 3., 3.]])
再進行一些運算:
z = y * y * 3
out = z.mean()
print(z)
print(out)
輸出:
tensor([[ 27., 27.],
[ 27., 27.]])
tensor(27.)
現在讓我們進行反向傳播:
out.backward()
print(x.grad)
自動微分給出的梯度為:
tensor([[ 4.5000, 4.5000],
[ 4.5000, 4.5000]])
感興趣的讀者可以手工驗證下梯度。
了解了PyTorch如何進行自動微分之后,讓我們使用PyTorch編碼一個簡單的神經網絡。
我們將創建一個簡單的神經網絡,包括一個隱藏層,一個輸出層。隱藏層使用ReLU激活,輸出層使用sigmoid激活。
構建神經網絡需要引入torch.nn模塊:
import torch.nn as nn
接著定義網絡層尺寸和batch尺寸:
n_in, n_h, n_out, batch_size = 10, 5, 1, 10
現在生成一些輸入數據x和目標數據y,并使用PyTorch張量存儲這些數據。
x = torch.randn(batch_size, n_in)
y = torch.tensor([[1.0], [0.0], [0.0], [1.0], [1.0], [1.0], [0.0], [0.0], [1.0], [1.0]])
接下來,只需一行代碼就可以定義我們的模型:
model = nn.Sequential(nn.Linear(n_in, n_h),
nn.ReLU(),
nn.Linear(n_h, n_out),
nn.Sigmoid())
我們創建了一個輸入 -> 線性 -> relu -> 線性 -> sigmoid的模型。對于需要更多自定義功能的更加復雜的模型,可以定義一個類,具體請參考PyTorch文檔。
現在,我們需要構造損失函數。我們將使用均方誤差:
criterion = torch.nn.MSELoss()
然后定義優化器。我們將使用強大的隨機梯度下降算法,學習率定為0.01.model.parameters()會返回一個模型參數(權重、偏置)上的迭代器。
optimizer = torch.optim.SGD(model.parameters(), lr=0.01)
下面我們跑50個epoch,這依次包括前向傳播、損失計算、反向傳播和參數更新。
for epoch in range(50):
# 前向傳播
y_pred = model(x)
# 計算并打印損失
loss = criterion(y_pred, y)
print('epoch: ', epoch,' loss: ', loss.item())
# 梯度歸零
optimizer.zero_grad()
# 反向傳播
loss.backward()
# 更新參數
optimizer.step()
輸出:
epoch: 0 loss: 0.2399429827928543
epoch: 1 loss: 0.23988191783428192
epoch: 2 loss: 0.23982088267803192
epoch: 3 loss: 0.2397598922252655
epoch: 4 loss: 0.23969893157482147
epoch: 5 loss: 0.23963800072669983
epoch: 6 loss: 0.23957709968090057
epoch: 7 loss: 0.23951618373394012
epoch: 8 loss: 0.23945537209510803
epoch: 9 loss: 0.23939454555511475
epoch: 10 loss: 0.23933371901512146
epoch: 11 loss: 0.23927298188209534
epoch: 12 loss: 0.23921218514442444
epoch: 13 loss: 0.23915143311023712
epoch: 14 loss: 0.2390907108783722
epoch: 15 loss: 0.23903003334999084
epoch: 16 loss: 0.23896940052509308
epoch: 17 loss: 0.23890872299671173
epoch: 18 loss: 0.23884813487529755
epoch: 19 loss: 0.23878750205039978
epoch: 20 loss: 0.23872694373130798
epoch: 21 loss: 0.2386663407087326
epoch: 22 loss: 0.2386058121919632
epoch: 23 loss: 0.23854532837867737
epoch: 24 loss: 0.23848481476306915
epoch: 25 loss: 0.23842433094978333
epoch: 26 loss: 0.2383638620376587
epoch: 27 loss: 0.23830339312553406
epoch: 28 loss: 0.2382429838180542
epoch: 29 loss: 0.23818258941173553
epoch: 30 loss: 0.2381247729063034
epoch: 31 loss: 0.2380656749010086
epoch: 32 loss: 0.23800739645957947
epoch: 33 loss: 0.2379491776227951
epoch: 34 loss: 0.2378900945186615
epoch: 35 loss: 0.23783239722251892
epoch: 36 loss: 0.23777374625205994
epoch: 37 loss: 0.23771481215953827
epoch: 38 loss: 0.23765745759010315
epoch: 39 loss: 0.23759838938713074
epoch: 40 loss: 0.23753997683525085
epoch: 41 loss: 0.2374821901321411
epoch: 42 loss: 0.23742322623729706
epoch: 43 loss: 0.23736533522605896
epoch: 44 loss: 0.23730707168579102
epoch: 45 loss: 0.23724813759326935
epoch: 46 loss: 0.23719079792499542
epoch: 47 loss: 0.23713204264640808
epoch: 48 loss: 0.23707345128059387
epoch: 49 loss: 0.2370160073041916
PyTorch的寫法很清晰,配上注釋,應該不難理解。如果仍有不解之處,可以參考下面的講解:
y_pred獲取模型一次前向傳播的預測值。y_pred和目標變量y一起傳給criterion以計算損失。
接著,optimizer.zero_grad()清空上一次迭代的梯度。
接下來的loss.backward()集中體現了PyTorch的神奇之處——這里用到了PyTorch的Autograd(自動計算梯度)特性。Autograd基于動態創建的計算圖自動計算所有參數上的梯度。總的來說,這一步進行的是梯度下降和反向傳播。
最后,我們調用optimizer.step(),使用新的梯度更新一次所有參數。
恭喜你讀到了這篇長文的結尾。這篇文章從張量講到了自動微分,同時基于PyTorch及其張量系統實現了一個簡單的神經網絡。
如果你想了解更多關于PyTorch的內容,或想進一步深入,請閱讀PyTorch的官方文檔和教程,這些文檔和教程寫得非常好。你可以從PyTorch官網找到這些文檔和教程。
撰寫這篇教程的時候,我參考了以下內容:
Daniel A. Fleisch的《A Student's Guide to Vectors and Tensors》
PyTorch官方文檔
Jason Brownlee寫的A Gentle Introduction to Tensors for Machine Learning with NumPy
如果有問題要問,或者有想法要討論,歡迎留言!
如果你打算進一步學習Python,可以參加DataCamp的Statistical Thinking in Python課程。
-
張量
+關注
關注
0文章
7瀏覽量
2597 -
pytorch
+關注
關注
2文章
808瀏覽量
13377
原文標題:從張量到自動微分:PyTorch入門教程
文章出處:【微信號:jqr_AI,微信公眾號:論智】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
RK3568國產處理器 + TensorFlow框架的張量創建實驗案例分享
TensorFlow獲取張量形狀的操作tfshape屬性shape及方法
TensorFlow教程|張量的階、形狀、數據類型
一種張量總變分的模糊圖像盲復原算法
![一種<b class='flag-5'>張量</b>總變分的模糊圖像盲復原算法](https://file.elecfans.com/web2/M00/49/79/poYBAGKhwLqARV6pAAAVU0PVJKI831.jpg)
基于TTr1SVD的張量奇異值分解
谷歌宣布開源張量計算庫TensorNetwork及其API
![谷歌宣布開源<b class='flag-5'>張量</b>計算庫TensorNetwork及其API](https://file.elecfans.com/web1/M00/97/CA/pIYBAF0O2_SAOpLVAAAevxHakEI325.png)
基于張量的車輛交通數據缺失估計方法
什么是張量處理單元(TPU)
如何使用張量核在CUDA C++設備代碼中編程
![如何使用<b class='flag-5'>張量</b>核在CUDA C++設備代碼<b class='flag-5'>中</b>編程](https://file.elecfans.com//web2/M00/3F/E1/poYBAGJqVDeALjDyAABHkgRIl4s172.png)
PyTorch的簡單實現
![<b class='flag-5'>PyTorch</b>的簡單實現](https://file.elecfans.com/web2/M00/8A/AF/pYYBAGO-czGAdp2XAADlHPKmqTo933.png)
張量類Tensor的實現
深度學習框架pytorch介紹
GPU的張量核心: 深度學習的秘密武器
![GPU的<b class='flag-5'>張量</b>核心: 深度學習的秘密武器](https://file.elecfans.com/web2/M00/4E/DC/poYBAGLCjeiALm_WAAAYmfR7Qec474.png)
評論