第一章:PyTorch 入門
- 1.1 Pytorch 簡介
-
- 1.1.1 PyTorch的由來
- 1.1.2 Torch是什麼?
- 1.1.3 重新介紹 PyTorch
- 1.1.4 對比PyTorch和Tensorflow
- 1.1.5 再次總結
- 1.2 Pytorch環境搭建
- 1.2.1 安裝Pytorch
-
- 1.2.2 配置 Jupyter Notebook
- 1.2.3 測試
- 1.2.4 問題解決
-
- 問題1:啟動python提示編碼錯誤
- 問題2 預設目錄設定不起效
- 1.3 PyTorch 深度學習:60分鐘快速入門 (官方)
-
- 1. 張量
-
- PyTorch是什麼?
- 開始
- NumPy 轉換
- CUDA 張量
- 2. Autograd: 自動求導
-
- Autograd: 自動求導機制
- 張量(Tensor)
- 梯度
1.1 Pytorch 簡介
1.1.1 PyTorch的由來
很多人都會拿PyTorch和Google的Tensorflow進行比較,這個肯定是沒有問題的,因為他們是最火的兩個深度學習架構了。但是說到PyTorch,其實應該先說Torch。
1.1.2 Torch是什麼?
Torch英譯中:火炬
A Tensor library like Numpy, unlike Numpy it has strong GPU support. Lua is a wrapper for Torch (Yes! you need to have a good understanding of Lua), and for that you will need LuaRocks package manager.
1
Torch是一個與Numpy類似的張量(Tensor)操作庫,與Numpy不同的是Torch對GPU支援的很好,Lua是Torch的上層包裝。
Torch is not going anywhere. PyTorch and Torch use the same C libraries that contain all the performance: TH, THC, THNN, THCUNN and they will continue to be shared.
We still and will have continued engineering on Torch itself, and we have no immediate plan to remove that.
2
PyTorch和Torch使用包含所有相同性能的C庫:TH, THC, THNN, THCUNN,并且它們将繼續共享這些庫。
這樣的回答就很明确了,其實PyTorch和Torch都使用的是相同的底層,隻是使用了不同的上層包裝語言。
注:LUA雖然快,但是太小衆了,是以才會有PyTorch的出現。
1.1.3 重新介紹 PyTorch
PyTorch is an open source machine learning library for Python, based on Torch, used for applications such as natural language processing. It is primarily developed by Facebook's artificial-intelligence research group, and Uber's "Pyro" software for probabilistic programming is built on it.
3
PyTorch是一個基于Torch的Python開源機器學習庫,用于自然語言處理等應用程式。 它主要由Facebook的人工智能研究小組開發。Uber的"Pyro"也是使用的這個庫。
PyTorch is a Python package that provides two high-level features:
Tensor computation (like NumPy) with strong GPU acceleration
Deep neural networks built on a tape-based autograd system
You can reuse your favorite Python packages such as NumPy, SciPy and Cython to extend PyTorch when needed.
4
PyTorch是一個Python包,提供兩個進階功能:
- 具有強大的GPU加速的張量計算(如NumPy)
- 包含自動求導系統的的深度神經網絡
1.1.4 對比PyTorch和Tensorflow
沒有好的架構,隻有合适的架構, 這篇知乎文章有個簡單的對比,是以這裡就不詳細再說了。
并且技術是發展的,知乎上的對比也不是絕對的,比如Tensorflow在1.5版的時候就引入了Eager Execution機制實作了動态圖,PyTorch的可視化,windows支援,沿維翻轉張量等問題都已經不是問題了。
1.1.5 再次總結
- PyTorch算是相當簡潔優雅且高效快速的架構
- 設計追求最少的封裝,盡量避免重複造輪子
- 算是所有的架構中面向對象設計的最優雅的一個,設計最符合人們的思維,它讓使用者盡可能地專注于實作自己的想法
- 大佬支援,與google的Tensorflow類似,FAIR的支援足以確定PyTorch獲得持續的開發更新
- 不錯的的文檔(相比FB的其他項目,PyTorch的文檔簡直算是完善了,參考Thrift),PyTorch作者親自維護的論壇 供使用者交流和求教問題
- 入門簡單
是以如果以上資訊有吸引你的内容,那麼請一定要讀完這本書:)
1.2 Pytorch環境搭建
PyTorch的安裝十分簡單,根據PyTorch官網,對系統選擇和安裝方式等靈活選擇即可。
這裡以anaconda為例,簡單的說一下步驟和要點。
國内安裝anaconda建議使用清華鏡像。
前些日子,由于合規問題中科大、清華鏡像都已經關閉。目前隻有清華鏡像恢複,是以目前可以繼續使用
1.2.1 安裝Pytorch
pytorch的安裝經過了幾次變化,請大家以官網的安裝指令為準。另外需要說明的就是在1.2版本以後,pytorch隻支援cuda 9.2以上了,是以需要對cuda進行更新,目前測試大部分顯示卡都可以用,包括筆記本的MX250也是可以順利更新到cuda 10.1。
我個人測試使用官網的安裝指令進行安裝時并不能安裝1.3版原因未知(如果大家conda安裝也有問題可以一起讨論下原因),是以這裡建議大家使用pip進行安裝,經過測試 pip是沒有任何問題的。
目前(2020/7)的穩定版本為1.5.1。
#預設 使用 cuda10.2
pip3 install torch===1.5.1 torchvision===0.6.1 -f https://download.pytorch.org/whl/torch_stable.html
#cuda 9.2
pip3 install torch==1.5.1+cu92 torchvision==0.6.1+cu92 -f https://download.pytorch.org/whl/torch_stable.html
#cpu版本
pip install torch==1.5.1+cpu torchvision==0.6.1+cpu -f https://download.pytorch.org/whl/torch_stable.html
驗證輸入python 進入
import torch
torch.__version__
# 得到結果'1.5.0'
1.2.2 配置 Jupyter Notebook
建立的環境是沒有安裝安裝ipykernel的是以無法注冊到Jupyter Notebook中,是以先要準備下環境
#安裝ipykernel
conda install ipykernel
#寫入環境
python -m ipykernel install --name pytorch --display-name "Pytorch for Deeplearning"
下一步就是定制 Jupyter Notebook
#切換回基礎環境
activate base
#建立jupyter notebook配置檔案
jupyter notebook --generate-config
## 這裡會顯示建立jupyter_notebook_config.py的具體位置
打開檔案,修改
c.NotebookApp.notebook_dir = '' 預設目錄位置
c.NotebookApp.iopub_data_rate_limit = 100000000 這個改大一些否則有可能報錯
1.2.3 測試
至此 Pytorch 的開發環境安裝完成,可以在開始菜單中打開Jupyter Notebook 在New 菜單中建立檔案時選擇
Pytorch for Deeplearning
建立PyTorch的相關開發環境了
1.2.4 問題解決
問題1:啟動python提示編碼錯誤
删除 .python_history 來源
問題2 預設目錄設定不起效
打開快捷方式,看看快捷方式是否跟這個截圖一樣,如果是則删除
%USERPROFILE%
改參數會覆寫掉notebook_dir設定,導緻配置不起效
如果你還發現其他問題,請直接留言
1.3 PyTorch 深度學習:60分鐘快速入門 (官方)
%matplotlib inline
1. 張量
PyTorch是什麼?
基于Python的科學計算包,服務于以下兩種場景:
- 作為NumPy的替代品,可以使用GPU的強大計算能力
- 提供最大的靈活性和高速的深度學習研究平台
開始
Tensors(張量)
Tensors與Numpy中的 ndarrays類似,但是在PyTorch中
Tensors 可以使用GPU進行計算.
from __future__ import print_function
import torch
建立一個 5x3 矩陣, 但是未初始化:
x = torch.empty(5, 3)
print(x)
tensor([[1.0745e-38, 1.1112e-38, 1.0286e-38],
[1.0653e-38, 1.0194e-38, 8.4490e-39],
[1.1112e-38, 9.5511e-39, 1.0102e-38],
[4.6837e-39, 5.0510e-39, 5.1429e-39],
[9.9184e-39, 9.0000e-39, 1.0561e-38]])
建立一個随機初始化的矩陣:
x = torch.rand(5, 3)
print(x)
tensor([[0.6972, 0.0231, 0.3087],
[0.2083, 0.6141, 0.6896],
[0.7228, 0.9715, 0.5304],
[0.7727, 0.1621, 0.9777],
[0.6526, 0.6170, 0.2605]])
建立一個0填充的矩陣,資料類型為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]])
建立tensor并使用現有資料初始化:
x = torch.tensor([5.5, 3])
print(x)
tensor([5.5000, 3.0000])
根據現有的張量建立張量。 這些方法将重用輸入張量的屬性,例如, dtype,除非設定新的值進行覆寫
x = x.new_ones(5, 3, dtype=torch.double) # new_* 方法來建立對象
print(x)
x = torch.randn_like(x, dtype=torch.float) # 覆寫 dtype!
print(x) # 對象的size 是相同的,隻是值和類型發生了變化
tensor([[1., 1., 1.],
[1., 1., 1.],
[1., 1., 1.],
[1., 1., 1.],
[1., 1., 1.]], dtype=torch.float64)
tensor([[ 0.5691, -2.0126, -0.4064],
[-0.0863, 0.4692, -1.1209],
[-1.1177, -0.5764, -0.5363],
[-0.4390, 0.6688, 0.0889],
[ 1.3334, -1.1600, 1.8457]])
擷取 size
譯者注:使用size方法與Numpy的shape屬性傳回的相同,張量也支援shape屬性,後面會詳細介紹
torch.Size([5, 3])
Note
``torch.Size`` 傳回值是 tuple類型, 是以它支援tuple類型的所有操作.
操作
操作有多種文法。
我們将看一下加法運算。
加法1:
y = torch.rand(5, 3)
print(x + y)
tensor([[ 0.7808, -1.4388, 0.3151],
[-0.0076, 1.0716, -0.8465],
[-0.8175, 0.3625, -0.2005],
[ 0.2435, 0.8512, 0.7142],
[ 1.4737, -0.8545, 2.4833]])
加法2
tensor([[ 0.7808, -1.4388, 0.3151],
[-0.0076, 1.0716, -0.8465],
[-0.8175, 0.3625, -0.2005],
[ 0.2435, 0.8512, 0.7142],
[ 1.4737, -0.8545, 2.4833]])
提供輸出tensor作為參數
result = torch.empty(5, 3)
torch.add(x, y, out=result)
print(result)
tensor([[ 0.7808, -1.4388, 0.3151],
[-0.0076, 1.0716, -0.8465],
[-0.8175, 0.3625, -0.2005],
[ 0.2435, 0.8512, 0.7142],
[ 1.4737, -0.8545, 2.4833]])
替換
# adds x to y
y.add_(x)
print(y)
tensor([[ 0.7808, -1.4388, 0.3151],
[-0.0076, 1.0716, -0.8465],
[-0.8175, 0.3625, -0.2005],
[ 0.2435, 0.8512, 0.7142],
[ 1.4737, -0.8545, 2.4833]])
Note
任何 以``_`` 結尾的操作都會用結果替換原變量. 例如: ``x.copy_(y)``, ``x.t_()``, 都會改變 ``x``.
你可以使用與NumPy索引方式相同的操作來進行對張量的操作
tensor([-2.0126, 0.4692, -0.5764, 0.6688, -1.1600])
torch.view
: 可以改變張量的次元和大小
譯者注:torch.view 與Numpy的reshape類似
x = torch.randn(4, 4)
y = x.view(16)
z = x.view(-1, 8) # size -1 從其他次元推斷
print(x.size(), y.size(), z.size())
torch.Size([4, 4]) torch.Size([16]) torch.Size([2, 8])
如果你有隻有一個元素的張量,使用
.item()
來得到Python資料類型的數值
x = torch.randn(1)
print(x)
print(x.item())
tensor([-0.2368])
-0.23680149018764496
Read later:
100+ Tensor operations, including transposing, indexing, slicing,
mathematical operations, linear algebra, random numbers, etc.,
are described
here <https://pytorch.org/docs/torch>
_.
NumPy 轉換
将一個Torch Tensor轉換為NumPy數組是一件輕松的事,反之亦然。
Torch Tensor與NumPy數組共享底層記憶體位址,修改一個會導緻另一個的變化。
将一個Torch Tensor轉換為NumPy數組
a = torch.ones(5)
print(a)
tensor([1., 1., 1., 1., 1.])
b = a.numpy()
print(b)
[1. 1. 1. 1. 1.]
觀察numpy數組的值是如何改變的。
a.add_(1)
print(a)
print(b)
tensor([2., 2., 2., 2., 2.])
[2. 2. 2. 2. 2.]
NumPy Array 轉化成 Torch Tensor
使用from_numpy自動轉化
import numpy as np
a = np.ones(5)
b = torch.from_numpy(a)
np.add(a, 1, out=a)
print(a)
print(b)
[2. 2. 2. 2. 2.]
tensor([2., 2., 2., 2., 2.], dtype=torch.float64)
所有的 Tensor 類型預設都是基于CPU, CharTensor 類型不支援到
NumPy 的轉換.
CUDA 張量
使用
.to
方法 可以将Tensor移動到任何裝置中
# is_available 函數判斷是否有cuda可以使用
# ``torch.device``将張量移動到指定的裝置中
if torch.cuda.is_available():
device = torch.device("cuda") # a CUDA 裝置對象
y = torch.ones_like(x, device=device) # 直接從GPU建立張量
x = x.to(device) # 或者直接使用``.to("cuda")``将張量移動到cuda中
z = x + y
print(z)
print(z.to("cpu", torch.double)) # ``.to`` 也會對變量的類型做更改
tensor([0.7632], device='cuda:0')
tensor([0.7632], dtype=torch.float64)
2. Autograd: 自動求導
本章是沖突的重災區,建議閱讀
%matplotlib inline
Autograd: 自動求導機制
PyTorch 中所有神經網絡的核心是
autograd
包。
我們先簡單介紹一下這個包,然後訓練第一個簡單的神經網絡。
autograd
包為張量上的所有操作提供了自動求導。
它是一個在運作時定義的架構,這意味着反向傳播是根據你的代碼來确定如何運作,并且每次疊代可以是不同的。
示例
張量(Tensor)
torch.Tensor
是這個包的核心類。如果設定
.requires_grad
為
True
,那麼将會追蹤所有對于該張量的操作。
當完成計算後通過調用
.backward()
,自動計算所有的梯度,
這個張量的所有梯度将會自動積累到
.grad
屬性。
要阻止張量跟蹤曆史記錄,可以調用
.detach()
方法将其與計算曆史記錄分離,并禁止跟蹤它将來的計算記錄。
為了防止跟蹤曆史記錄(和使用記憶體),可以将代碼塊包裝在
with torch.no_grad():
中。
在評估模型時特别有用,因為模型可能具有
requires_grad = True
的可訓練參數,但是我們不需要梯度計算。
在自動梯度計算中還有另外一個重要的類
Function
.
Tensor
and
Function
are interconnected and build up an acyclic
graph, that encodes a complete history of computation. Each tensor has
a
.grad_fn
attribute that references a
Function
that has created
the
Tensor
(except for Tensors created by the user - their
grad_fn is None
).
Tensor
和
Function
互相連接配接并生成一個非循環圖,它表示和存儲了完整的計算曆史。
每個張量都有一個
.grad_fn
屬性,這個屬性引用了一個建立了
Tensor
的
Function
(除非這個張量是使用者手動建立的,即,這個張量的
grad_fn
是
None
)。
如果需要計算導數,你可以在
Tensor
上調用
.backward()
。
如果
Tensor
是一個标量(即它包含一個元素資料)則不需要為
backward()
指定任何參數,
但是如果它有更多的元素,你需要指定一個
gradient
參數來比對張量的形狀。
譯者注:在其他的文章中你可能會看到說将Tensor包裹到Variable中提供自動梯度計算,Variable 這個在0.41版中已經被标注為過期了,現在可以直接使用Tensor,官方文檔在這裡:
https://pytorch.org/docs/stable/autograd.html#variable-deprecated
具體的後面會有詳細說明
import torch
建立一個張量并設定 requires_grad=True 用來追蹤他的計算曆史
x = torch.ones(2, 2, requires_grad=True)
print(x)
tensor([[1., 1.],
[1., 1.]], requires_grad=True)
對張量進行操作:
y = x + 2
print(y)
tensor([[3., 3.],
[3., 3.]], grad_fn=<AddBackward0>)
結果
y
已經被計算出來了,是以,
grad_fn
已經被自動生成了。
<AddBackward0 object at 0x000002004F7CC248>
對y進行一個操作
z = y * y * 3
out = z.mean()
print(z, out)
tensor([[27., 27.],
[27., 27.]], grad_fn=<MulBackward0>) tensor(27., grad_fn=<MeanBackward0>)
.requires_grad_( ... )
可以改變現有張量的
requires_grad
屬性。
如果沒有指定的話,預設輸入的flag是
False
。
a = torch.randn(2, 2)
a = ((a * 3) / (a - 1))
print(a.requires_grad)
a.requires_grad_(True)
print(a.requires_grad)
b = (a * a).sum()
print(b.grad_fn)
False
True
<SumBackward0 object at 0x000002004F7D5608>
梯度
反向傳播
因為
out
是一個純量(scalar),
out.backward()
等于
out.backward(torch.tensor(1))
。
print gradients d(out)/dx
tensor([[4.5000, 4.5000],
[4.5000, 4.5000]])
得到矩陣
4.5
.将
out
叫做
Tensor “ o o o”.
得到 o = 1 4 ∑ i z i o = \frac{1}{4}\sum_i z_i o=41∑izi,
z i = 3 ( x i + 2 ) 2 z_i = 3(x_i+2)^2 zi=3(xi+2)2 和 z i ∣ x i = 1 = 27 z_i\bigr\rvert_{x_i=1} = 27 zi∣∣xi=1=27.
是以,
∂ o ∂ x i = 3 2 ( x i + 2 ) \frac{\partial o}{\partial x_i} = \frac{3}{2}(x_i+2) ∂xi∂o=23(xi+2), 則
∂ o ∂ x i ∣ x i = 1 = 9 2 = 4.5 \frac{\partial o}{\partial x_i}\bigr\rvert_{x_i=1} = \frac{9}{2} = 4.5 ∂xi∂o∣∣xi=1=29=4.5.
在數學上,如果我們有向量值函數 y ⃗ = f ( x ⃗ ) ) \vec{y} = f(\vec{x})) y
=f(x
)) ,且 y ⃗ \vec{y} y
關于 x ⃗ \vec{x} x
的梯度是一個雅可比矩陣(Jacobian matrix):
J = ( ∂ y 1 ∂ x 1 ⋯ ∂ y 1 ∂ x n ⋮ ⋱ ⋮ ∂ y m ∂ x 1 ⋯ ∂ y m ∂ x n ) J = \begin{pmatrix} \frac{\partial y_{1}}{\partial x_{1}} & \cdots & \frac{\partial y_{1}}{\partial x_{n}} \\ \vdots & \ddots & \vdots \\ \frac{\partial y_{m}}{\partial x_{1}} & \cdots & \frac{\partial y_{m}}{\partial x_{n}} \end{pmatrix} J=⎝⎜⎛∂x1∂y1⋮∂x1∂ym⋯⋱⋯∂xn∂y1⋮∂xn∂ym⎠⎟⎞
一般來說,
torch.autograd
就是用來計算vector-Jacobian product的工具。也就是說,給定任一向量 v = ( v 1 v 2 ⋯ v m ) T v=(v_{1}\;v_{2}\;\cdots\;v_{m})^{T} v=(v1v2⋯vm)T ,計算 v T ⋅ J v^{T}\cdot J vT⋅J ,如果 v v v 恰好是标量函數 l = g ( y ⃗ ) l=g(\vec{y}) l=g(y
) 的梯度,也就是說 v = ( ∂ l ∂ y 1 ⋯ ∂ l ∂ y m ) T v=(\frac{\partial l}{\partial y_{1}}\;\cdots\;\frac{\partial l}{\partial y_{m}})^{T} v=(∂y1∂l⋯∂ym∂l)T,那麼根據鍊式法則,vector-Jacobian product 是 x ⃗ \vec{x} x
對 l l l 的梯度:
J T ⋅ v = ( ∂ y 1 ∂ x 1 ⋯ ∂ y m ∂ x 1 ⋮ ⋱ ⋮ ∂ y 1 ∂ x n ⋯ ∂ y m ∂ x n ) ( ∂ l ∂ y 1 ⋮ ∂ l ∂ y m ) = ( ∂ l ∂ x 1 ⋮ ∂ l ∂ x n ) J^{T}\cdot v = \begin{pmatrix} \frac{\partial y_{1}}{\partial x_{1}} & \cdots & \frac{\partial y_{m}}{\partial x_{1}} \\ \vdots & \ddots & \vdots \\ \frac{\partial y_{1}}{\partial x_{n}} & \cdots & \frac{\partial y_{m}}{\partial x_{n}} \end{pmatrix} \begin{pmatrix} \frac{\partial l}{\partial y_{1}}\\ \vdots \\ \frac{\partial l}{\partial y_{m}} \end{pmatrix} = \begin{pmatrix} \frac{\partial l}{\partial x_{1}}\\ \vdots \\ \frac{\partial l}{\partial x_{n}} \end{pmatrix} JT⋅v=⎝⎜⎛∂x1∂y1⋮∂xn∂y1⋯⋱⋯∂x1∂ym⋮∂xn∂ym⎠⎟⎞⎝⎜⎛∂y1∂l⋮∂ym∂l⎠⎟⎞=⎝⎜⎛∂x1∂l⋮∂xn∂l⎠⎟⎞
(注意, v T ⋅ J v^{T}\cdot J vT⋅J 給出了一個行向量,可以通過 J T ⋅ v J^{T}\cdot v JT⋅v 将其視為列向量)
vector-Jacobian product 這種特性使得将外部梯度傳回到具有非标量輸出的模型變得非常友善。
現在讓我們來看一個vector-Jacobian product的例子
x = torch.randn(3, requires_grad=True)
y = x * 2
while y.data.norm() < 1000:
y = y * 2
print(y)
tensor([ 293.4463, 50.6356, 1031.2501], grad_fn=<MulBackward0>)
在這個情形中,
y
不再是個标量。
torch.autograd
無法直接計算出完整的雅可比行列,但是如果我們隻想要vector-Jacobian product,隻需将向量作為參數傳入
backward
:
gradients = torch.tensor([0.1, 1.0, 0.0001], dtype=torch.float)
y.backward(gradients)
print(x.grad)
tensor([5.1200e+01, 5.1200e+02, 5.1200e-02])
如果
.requires_grad=True
但是你又不希望進行autograd的計算,
那麼可以将變量包裹在
with torch.no_grad()
中:
print(x.requires_grad)
print((x ** 2).requires_grad)
with torch.no_grad():
print((x ** 2).requires_grad)
True
True
False
稍後閱讀:
autograd
和
Function
的官方文檔 https://pytorch.org/docs/autograd