DataCamp的Sayak Paul帶你入門張量、PyTorch。
在深度學(xué)習(xí)中,常??吹綇埩渴菙?shù)據(jù)結(jié)構(gòu)的基石這一說法。Google的機器學(xué)習(xí)庫TensorFlow甚至都以張量(tensor)命名。張量是線性代數(shù)中用到的一種數(shù)據(jù)結(jié)構(gòu),類似向量和矩陣,你可以在張量上進行算術(shù)運算。
PyTorch(Facebook創(chuàng)建的python包,提供兩個高層特性:1) 類似Numpy的基于GPU加速的張量運算 2) 在基于回放(tape-based)的自動微分系統(tǒng)之上構(gòu)建的深度神經(jīng)網(wǎng)絡(luò)。
本教程將介紹什么是張量,如何在PyTorch中操作張量:
張量介紹
PyTorch介紹
PyTorch安裝步驟
PyTorch下的一些張量操作
基于PyTorch實現(xiàn)一個簡單的神經(jīng)網(wǎng)絡(luò)
閑話少敘,讓我們開始介紹張量吧。
張量介紹
張量是向量和矩陣的推廣,可以理解為多維數(shù)組。知名的《深度學(xué)習(xí)》(Goodfellow等編寫)是這樣介紹張量的:
在一般意義上,以基于可變數(shù)目的軸的規(guī)則網(wǎng)格組織的一組數(shù)字稱為張量。
標(biāo)量是零階張量。向量是一階張量,矩陣是二階張量。
下面是張量的示意圖:
現(xiàn)在讓我們以更清晰易懂的方式構(gòu)建張量背后的直覺。
張量是現(xiàn)代機器學(xué)習(xí)的基本構(gòu)建。它是一個數(shù)據(jù)容器,大多數(shù)情況下包含數(shù)字,有時可能包含字符串(不過這罕見)。所以可以把張量想象成一桶數(shù)字。
人們經(jīng)?;煊脧埩亢投嗑S數(shù)組。不過有時需要嚴(yán)格區(qū)分兩者,如StackExchange指出:
張量和多維數(shù)組是不同類型的對象。前者是一種函數(shù),后者是適宜在坐標(biāo)系統(tǒng)中表示張量的一種數(shù)據(jù)結(jié)構(gòu)。
在數(shù)學(xué)上,張量由多元線性函數(shù)定義。一個多元線性函數(shù)包含多個向量變量。張量域是張量值函數(shù)。更嚴(yán)謹(jǐn)?shù)臄?shù)學(xué)解釋,可以參考https://math.stackexchange.com/q/10282
所以,張量是需要定義的函數(shù)或容器。實際上,當(dāng)數(shù)據(jù)傳入時,計算才真正發(fā)生。當(dāng)不需要嚴(yán)格區(qū)分?jǐn)?shù)組和張量的時候,數(shù)組或多維數(shù)組(1D, 2D, …, ND)一般可以視作張量。
現(xiàn)在我們稍微講下張量表述(Tensor notation)。
張量表述和矩陣類似,一般用大寫字母表示張量,帶整數(shù)下標(biāo)的小寫字母表示張量中的標(biāo)量值。
標(biāo)量、向量、矩陣的許多運算同樣適用于張量。
張量和張量代數(shù)是物理和工程領(lǐng)域廣泛使用的工具。機器學(xué)習(xí)的許多技術(shù),深度學(xué)習(xí)模型的訓(xùn)練和操作,常常使用張量這一術(shù)語進行描述。
PyTorch介紹
PyTorch是一個非常靈活的基于Python的深度學(xué)習(xí)研究平臺。
PyTorch特性
提供各種張量的常規(guī)操作。
基于回放的自動微分系統(tǒng)。
不同于TensorFlow、Theano、Caffe、CNTK等大多數(shù)框架采用的靜態(tài)圖系統(tǒng),PyTorch采用動態(tài)圖系統(tǒng)。
最小化框架開銷,可基于GPU加速。
相比Torch等替代品,PyTorch的內(nèi)存使用非常高效。這讓你可以訓(xùn)練比以往更大的深度學(xué)習(xí)模型。
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官網(wǎng)查詢: https://pytorch.org/get-started/locally/
好了,下面讓我們直接深入PyTorch下的一些張量算術(shù)。
PyTorch下的一些張量操作
首先,導(dǎo)入所需的庫:
import torch
如果出現(xiàn)報錯,說明PyTorch沒有安裝成功,請參考上一節(jié)重新安裝。
現(xiàn)在,我們構(gòu)造一個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]])
再構(gòu)造一個5×3的矩陣,不過這次用零初始化,并指定數(shù)據(jù)類型為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]])
構(gòu)造張量時直接提供數(shù)據(jù):
x = torch.tensor([5.5, 3])
print(x)
輸出:
tensor([ 5.5000, 3.0000])
如果你想檢驗下自己是否理解了PyTorch中的張量,那可以思考下上面的張量x是什么類別的。
基于已有張量,可以創(chuàng)建新張量——新張量會復(fù)用輸入張量的屬性,比如dtype(數(shù)據(jù)類型),除非另外給出新值:
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事實上是一個元組,支持所有元組操作。
現(xiàn)在,讓我們看下張量的加法。
張量加法
兩個張量分素相加,得到維度一致的張量,結(jié)果張量中每個標(biāo)量的值是相應(yīng)標(biāo)量的和。
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]])
除了使用+運算符外,也可以調(diào)用torch.add方法(兩者是等價的):
print(torch.add(x, y))
下面我們來看張量減法。
張量減法
兩個張量分素相減,得到維度一致的張量,結(jié)果張量中每個標(biāo)量的值是相應(yīng)標(biāo)量之差。
接著我們將討論張量相乘。
張量乘法
假設(shè)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)。
廣播
“廣播”這一術(shù)語用于描述如何在形狀不一的數(shù)組上應(yīng)用算術(shù)運算。在滿足特定限制的前提下,較小的數(shù)組“廣播至”較大的數(shù)組,使兩者形狀互相兼容。廣播提供了一個向量化數(shù)組操作的機制,這樣遍歷就發(fā)生在C層面,而不是Python層面。廣播可以避免不必要的數(shù)據(jù)復(fù)制,通常導(dǎo)向高效的算法實現(xiàn)。不過,也存在不適用廣播的情形(可能導(dǎo)致拖慢計算過程的低效內(nèi)存使用)。
可廣播的一對張量需滿足以下規(guī)則:
每個張量至少有一個維度。
迭代維度尺寸時,從尾部的維度開始,維度尺寸或者相等,或者其中一個張量的維度尺寸為一,或者其中一個張量不存在這個維度。
讓我們通過幾段代碼來理解PyTorch的廣播機制。
x=torch.empty(5,7,3)
y=torch.empty(5,7,3)
相同形狀的張量總是可廣播的,因為總能滿足以上規(guī)則。
x=torch.empty((0,))
y=torch.empty(2,2)
不可廣播(x不滿足第一條規(guī)則)。
# 為了清晰易讀,可以對齊尾部
x=torch.empty(5,3,4,1)
y=torch.empty( 3,1,1)
x和y可廣播:
倒數(shù)第一個維度:兩者的尺寸均為1
倒數(shù)第二個維度:y尺寸為1
倒數(shù)第三個維度:兩者尺寸相同
倒數(shù)第四個維度:y該維度不存在
但下面一對就不可廣播了:
x=torch.empty(5,2,4,1)
y=torch.empty( 3,1,1)
這是因為倒數(shù)第三個維度:2 != 3
現(xiàn)在你對“可廣播”這一概念已經(jīng)有所了解了,讓我們看下,廣播后的張量是什么樣的。
如果張量x和張量y是可廣播的,那么廣播后的張量尺寸按照如下方法計算:
如果x和y的維數(shù)不等,在維數(shù)較少的張量上添加尺寸為1的維度。結(jié)果維度尺寸是x和y相應(yīng)維度尺寸的較大者。例如:
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
你現(xiàn)在應(yīng)該已經(jīng)掌握了廣播這個概念了!
張量乘積是最常見的張量乘法,但也存在其他種類的張量乘法,例如張量點積和張量縮并。
借助Numpy橋,PyTorch張量和NumPy數(shù)組之間的互相轉(zhuǎn)換極其迅速。下面就讓我們來了解一下這個概念。
NumPy橋
NumPy橋使得PyTorch張量和NumPy數(shù)組共享底層內(nèi)存地址,對其中之一的修改會反映到另一個上。
轉(zhuǎn)換PyTorch張量至NumPy數(shù)組。
a = torch.ones(5)
b = a.numpy()
print(a)
print(b)
輸出:
tensor([ 1., 1., 1., 1., 1.])
[1.1.1.1.1.]
在這一節(jié)中,我們討論了一些基本的張量算術(shù),例如加法、減法、張量乘積。下一節(jié)我們將使用PyTorch實現(xiàn)一個基本的神經(jīng)網(wǎng)絡(luò)。
基于PyTorch實現(xiàn)一個簡單的神經(jīng)網(wǎng)絡(luò)
如果你想要溫習(xí)一下神經(jīng)網(wǎng)絡(luò)的概念,可以參考以下文章:
初窺神經(jīng)網(wǎng)絡(luò)內(nèi)部機制
從頭開始搭建三層神經(jīng)網(wǎng)絡(luò)
基于Numpy實現(xiàn)神經(jīng)網(wǎng)絡(luò):反向傳播、梯度下降
在實現(xiàn)神經(jīng)網(wǎng)絡(luò)之前,我們先來討論一下自動微分,這是PyTorch下所有神經(jīng)網(wǎng)絡(luò)的核心,在進行反向傳播計算梯度時尤其有用。
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.)
現(xiàn)在讓我們進行反向傳播:
out.backward()
print(x.grad)
自動微分給出的梯度為:
tensor([[ 4.5000, 4.5000],
[ 4.5000, 4.5000]])
感興趣的讀者可以手工驗證下梯度。
了解了PyTorch如何進行自動微分之后,讓我們使用PyTorch編碼一個簡單的神經(jīng)網(wǎng)絡(luò)。
我們將創(chuàng)建一個簡單的神經(jīng)網(wǎng)絡(luò),包括一個隱藏層,一個輸出層。隱藏層使用ReLU激活,輸出層使用sigmoid激活。
構(gòu)建神經(jīng)網(wǎng)絡(luò)需要引入torch.nn模塊:
import torch.nn as nn
接著定義網(wǎng)絡(luò)層尺寸和batch尺寸:
n_in, n_h, n_out, batch_size = 10, 5, 1, 10
現(xiàn)在生成一些輸入數(shù)據(jù)x和目標(biāo)數(shù)據(jù)y,并使用PyTorch張量存儲這些數(shù)據(jù)。
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())
我們創(chuàng)建了一個輸入 -> 線性 -> relu -> 線性 -> sigmoid的模型。對于需要更多自定義功能的更加復(fù)雜的模型,可以定義一個類,具體請參考PyTorch文檔。
現(xiàn)在,我們需要構(gòu)造損失函數(shù)。我們將使用均方誤差:
criterion = torch.nn.MSELoss()
然后定義優(yōu)化器。我們將使用強大的隨機梯度下降算法,學(xué)習(xí)率定為0.01.model.parameters()會返回一個模型參數(shù)(權(quán)重、偏置)上的迭代器。
optimizer = torch.optim.SGD(model.parameters(), lr=0.01)
下面我們跑50個epoch,這依次包括前向傳播、損失計算、反向傳播和參數(shù)更新。
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()
# 更新參數(shù)
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īng)該不難理解。如果仍有不解之處,可以參考下面的講解:
y_pred獲取模型一次前向傳播的預(yù)測值。y_pred和目標(biāo)變量y一起傳給criterion以計算損失。
接著,optimizer.zero_grad()清空上一次迭代的梯度。
接下來的loss.backward()集中體現(xiàn)了PyTorch的神奇之處——這里用到了PyTorch的Autograd(自動計算梯度)特性。Autograd基于動態(tài)創(chuàng)建的計算圖自動計算所有參數(shù)上的梯度。總的來說,這一步進行的是梯度下降和反向傳播。
最后,我們調(diào)用optimizer.step(),使用新的梯度更新一次所有參數(shù)。
恭喜你讀到了這篇長文的結(jié)尾。這篇文章從張量講到了自動微分,同時基于PyTorch及其張量系統(tǒng)實現(xiàn)了一個簡單的神經(jīng)網(wǎng)絡(luò)。
如果你想了解更多關(guān)于PyTorch的內(nèi)容,或想進一步深入,請閱讀PyTorch的官方文檔和教程,這些文檔和教程寫得非常好。你可以從PyTorch官網(wǎng)找到這些文檔和教程。
撰寫這篇教程的時候,我參考了以下內(nèi)容:
Daniel A. Fleisch的《A Student's Guide to Vectors and Tensors》
PyTorch官方文檔
Jason Brownlee寫的A Gentle Introduction to Tensors for Machine Learning with NumPy
如果有問題要問,或者有想法要討論,歡迎留言!
如果你打算進一步學(xué)習(xí)Python,可以參加DataCamp的Statistical Thinking in Python課程。
-
張量
+關(guān)注
關(guān)注
0文章
7瀏覽量
2643 -
pytorch
+關(guān)注
關(guān)注
2文章
809瀏覽量
13974
原文標(biāo)題:從張量到自動微分:PyTorch入門教程
文章出處:【微信號:jqr_AI,微信公眾號:論智】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
RK3568國產(chǎn)處理器 + TensorFlow框架的張量創(chuàng)建實驗案例分享
無法調(diào)用GPU插件推理的遠程張量API怎么解決?
TensorFlow獲取張量形狀的操作tfshape屬性shape及方法
TensorFlow教程|張量的階、形狀、數(shù)據(jù)類型
一種張量總變分的模糊圖像盲復(fù)原算法

基于TTr1SVD的張量奇異值分解
谷歌宣布開源張量計算庫TensorNetwork及其API

基于張量的車輛交通數(shù)據(jù)缺失估計方法
什么是張量處理單元(TPU)
如何使用張量核在CUDA C++設(shè)備代碼中編程

PyTorch的簡單實現(xiàn)

張量類Tensor的實現(xiàn)
GPU的張量核心: 深度學(xué)習(xí)的秘密武器

評論