快速入門Pytorch(1)--安裝、張量以及梯度

原文連接:mp.weixin.qq.com/s/WZdBm2JQ4…html

這是翻譯自官方的入門教程,教程地址以下:python

DEEP LEARNING WITH PYTORCH: A 60 MINUTE BLITZgit

雖然教程名字是 60 分鐘入門,可是內容仍是比較多,雖然以前屢次更新了好多篇有接近 1 萬字的文章,但此次仍是分紅大概 4 篇來介紹,這是第一篇,目錄以下:程序員


1. Pytorch 是什麼

Pytorch 是一個基於 Python 的科學計算庫,它面向如下兩種人羣:github

  • 但願將其代替 Numpy 來利用 GPUs 的威力;
  • 一個能夠提供更加靈活和快速的深度學習研究平臺。

1.1 安裝

pytorch 的安裝能夠直接查看官網教程,以下所示,官網地址:pytorch.org/get-started…算法

根據提示分別選擇系統(Linux、Mac 或者 Windows),安裝方式(Conda,Pip,LibTorch 或者源碼安裝)、使用的編程語言(Python 2.7 或者 Python 3.5,3.6,3.7 或者是 C++),若是是 GPU 版本,就須要選擇 CUDA 的 版本,因此,若是如上圖所示選擇,安裝的命令是:編程

conda install pytorch torchvision cudatoolkit=9.0 -c pytorch 
複製代碼

這裏推薦採用 Conda 安裝,即便用 Anaconda,主要是能夠設置不一樣環境配置不一樣的設置,關於 Anaconda 能夠查看我以前寫的 Python 基礎入門--簡介和環境配置數組

固然這裏會安裝最新版本的 Pytorch,也就是 1.1 版本,若是但願安裝以前的版本,能夠點擊下面的網址:bash

pytorch.org/get-started…微信

以下圖所示,安裝 0.4.1 版本的 pytorch,在不一樣版本的 CUDA 以及沒有 CUDA 的狀況。

而後還有其餘的安裝方式,具體能夠本身點擊查看。

安裝後,輸入下列命令:

from __future__ import print_function
import torch
x = torch.rand(5, 3)
print(x)
複製代碼

輸出結果相似下面的結果即安裝成功:

tensor([[0.3380, 0.3845, 0.3217],
        [0.8337, 0.9050, 0.2650],
        [0.2979, 0.7141, 0.9069],
        [0.1449, 0.1132, 0.1375],
        [0.4675, 0.3947, 0.1426]])
複製代碼

而後是驗證可否正確運行在 GPU 上,輸入下列代碼,這份代碼中 cuda.is_available() 主要是用於檢測是否可使用當前的 GPU 顯卡,若是返回 True,固然就能夠運行,不然就不能。

import torch
torch.cuda.is_available()
複製代碼

1.2 張量(Tensors)

Pytorch 的一大做用就是能夠代替 Numpy 庫,因此首先介紹 Tensors ,也就是張量,它至關於 Numpy 的多維數組(ndarrays)。二者的區別就是 Tensors 能夠應用到 GPU 上加快計算速度。

首先導入必須的庫,主要是 torch

from __future__ import print_function
import torch
複製代碼
1.2.1 聲明和定義

首先是對 Tensors 的聲明和定義方法,分別有如下幾種:

  • torch.empty(): 聲明一個未初始化的矩陣。
# 建立一個 5*3 的矩陣
x = torch.empty(5, 3)
print(x)
複製代碼

輸出結果以下:

tensor([[9.2737e-41, 8.9074e-01, 1.9286e-37],
        [1.7228e-34, 5.7064e+01, 9.2737e-41],
        [2.2803e+02, 1.9288e-37, 1.7228e-34],
        [1.4609e+04, 9.2737e-41, 5.8375e+04],
        [1.9290e-37, 1.7228e-34, 3.7402e+06]])
複製代碼
  • torch.rand():隨機初始化一個矩陣
# 建立一個隨機初始化的 5*3 矩陣
rand_x = torch.rand(5, 3)
print(rand_x)
複製代碼

輸出結果:

tensor([[0.4311, 0.2798, 0.8444],
        [0.0829, 0.9029, 0.8463],
        [0.7139, 0.4225, 0.5623],
        [0.7642, 0.0329, 0.8816],
        [1.0000, 0.9830, 0.9256]])
複製代碼
  • torch.zeros():建立數值皆爲 0 的矩陣
# 建立一個數值皆是 0,類型爲 long 的矩陣
zero_x = torch.zeros(5, 3, dtype=torch.long)
print(zero_x)
複製代碼

輸出結果以下:

tensor([[0, 0, 0],
        [0, 0, 0],
        [0, 0, 0],
        [0, 0, 0],
        [0, 0, 0]])
複製代碼

相似的也能夠建立數值都是 1 的矩陣,調用 torch.ones

  • torch.tensor():直接傳遞 tensor 數值來建立
# tensor 數值是 [5.5, 3]
tensor1 = torch.tensor([5.5, 3])
print(tensor1)
複製代碼

輸出結果:

tensor([5.5000, 3.0000])
複製代碼

除了上述幾種方法,還能夠根據已有的 tensor 變量建立新的 tensor 變量,這種作法的好處就是能夠保留已有 tensor 的一些屬性,包括尺寸大小、數值屬性,除非是從新定義這些屬性。相應的實現方法以下:

  • tensor.new_ones():new_*() 方法須要輸入尺寸大小
# 顯示定義新的尺寸是 5*3,數值類型是 torch.double
tensor2 = tensor1.new_ones(5, 3, dtype=torch.double)  # new_* 方法須要輸入 tensor 大小
print(tensor2)
複製代碼

輸出結果:

tensor([[1., 1., 1.],
        [1., 1., 1.],
        [1., 1., 1.],
        [1., 1., 1.],
        [1., 1., 1.]], dtype=torch.float64)
複製代碼
  • torch.randn_like(old_tensor):保留相同的尺寸大小
# 修改數值類型
tensor3 = torch.randn_like(tensor2, dtype=torch.float)
print('tensor3: ', tensor3)
複製代碼

輸出結果,這裏是根據上個方法聲明的 tensor2 變量來聲明新的變量,能夠看出尺寸大小都是 5*3,可是數值類型是改變了的。

tensor3:  tensor([[-0.4491, -0.2634, -0.0040],
        [-0.1624,  0.4475, -0.8407],
        [-0.6539, -1.2772,  0.6060],
        [ 0.2304,  0.0879, -0.3876],
        [ 1.2900, -0.7475, -1.8212]])
複製代碼

最後,對 tensors 的尺寸大小獲取能夠採用 tensor.size() 方法:

print(tensor3.size())  
# 輸出: torch.Size([5, 3])
複製代碼

注意torch.Size 其實是元組(tuple)類型,因此支持全部的元組操做

1.2.2 操做(Operations)

操做也包含了不少語法,但這裏做爲快速入門,僅僅以加法操做做爲例子進行介紹,更多的操做介紹能夠點擊下面網址查看官方文檔,包括轉置、索引、切片、數學計算、線性代數、隨機數等等:

pytorch.org/docs/stable…

對於加法的操做,有幾種實現方式:

  • +
  • torch.add(tensor1, tensor2, [out=tensor3])
  • tensor1.add_(tensor2):直接修改 tensor 變量
tensor4 = torch.rand(5, 3)
print('tensor3 + tensor4= ', tensor3 + tensor4)
print('tensor3 + tensor4= ', torch.add(tensor3, tensor4))
# 新聲明一個 tensor 變量保存加法操做的結果
result = torch.empty(5, 3)
torch.add(tensor3, tensor4, out=result)
print('add result= ', result)
# 直接修改變量
tensor3.add_(tensor4)
print('tensor3= ', tensor3)
複製代碼

輸出結果

tensor3 + tensor4=  tensor([[ 0.1000,  0.1325,  0.0461],
        [ 0.4731,  0.4523, -0.7517],
        [ 0.2995, -0.9576,  1.4906],
        [ 1.0461,  0.7557, -0.0187],
        [ 2.2446, -0.3473, -1.0873]])

tensor3 + tensor4=  tensor([[ 0.1000,  0.1325,  0.0461],
        [ 0.4731,  0.4523, -0.7517],
        [ 0.2995, -0.9576,  1.4906],
        [ 1.0461,  0.7557, -0.0187],
        [ 2.2446, -0.3473, -1.0873]])

add result=  tensor([[ 0.1000,  0.1325,  0.0461],
        [ 0.4731,  0.4523, -0.7517],
        [ 0.2995, -0.9576,  1.4906],
        [ 1.0461,  0.7557, -0.0187],
        [ 2.2446, -0.3473, -1.0873]])

tensor3=  tensor([[ 0.1000,  0.1325,  0.0461],
        [ 0.4731,  0.4523, -0.7517],
        [ 0.2995, -0.9576,  1.4906],
        [ 1.0461,  0.7557, -0.0187],
        [ 2.2446, -0.3473, -1.0873]])
複製代碼

注意:能夠改變 tensor 變量的操做都帶有一個後綴 _, 例如 x.copy_(y), x.t_() 均可以改變 x 變量

除了加法運算操做,對於 Tensor 的訪問,和 Numpy 對數組相似,可使用索引來訪問某一維的數據,以下所示:

# 訪問 tensor3 第一列數據
print(tensor3[:, 0])
複製代碼

輸出結果:

tensor([0.1000, 0.4731, 0.2995, 1.0461, 2.2446])
複製代碼

對 Tensor 的尺寸修改,能夠採用 torch.view() ,以下所示:

x = torch.randn(4, 4)
y = x.view(16)
# -1 表示除給定維度外的其他維度的乘積
z = x.view(-1, 8)
print(x.size(), y.size(), z.size())
複製代碼

輸出結果:

torch.Size([4, 4]) torch.Size([16]) torch.Size([2, 8])
複製代碼

若是 tensor 僅有一個元素,能夠採用 .item() 來獲取相似 Python 中整數類型的數值:

x = torch.randn(1)
print(x)
print(x.item())
複製代碼

輸出結果:

tensor([0.4549])
0.4549027979373932
複製代碼

更多的運算操做能夠查看官方文檔的介紹:

pytorch.org/docs/stable…

1.3 和 Numpy 數組的轉換

Tensor 和 Numpy 的數組能夠相互轉換,而且二者轉換後共享在 CPU 下的內存空間,即改變其中一個的數值,另外一個變量也會隨之改變。

1.3.1 張量轉換爲 Numpy 數組

實現 Tensor 轉換爲 Numpy 數組的例子以下所示,調用 tensor.numpy() 能夠實現這個轉換操做。

a = torch.ones(5)
print(a)
b = a.numpy()
print(b)
複製代碼

輸出結果:

tensor([1., 1., 1., 1., 1.])
[1. 1. 1. 1. 1.]
複製代碼

此外,剛剛說了二者是共享同個內存空間的,例子以下所示,修改 tensor 變量 a,看看從 a 轉換獲得的 Numpy 數組變量 b 是否發生變化。

a.add_(1)
print(a)
print(b)
複製代碼

輸出結果以下,很明顯,b 也隨着 a 的改變而改變。

tensor([2., 2., 2., 2., 2.])
[2. 2. 2. 2. 2.]
複製代碼
1.3.2 Numpy 數組轉換爲張量

轉換的操做是調用 torch.from_numpy(numpy_array) 方法。例子以下所示:

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)
複製代碼

CPU 上,除了 CharTensor 外的全部 Tensor 類型變量,都支持和 Numpy 數組的相互轉換操做。

1.4. CUDA 張量

Tensors 能夠經過 .to 方法轉換到不一樣的設備上,即 CPU 或者 GPU 上。例子以下所示:

# 當 CUDA 可用的時候,可用運行下方這段代碼,採用 torch.device() 方法來改變 tensors 是否在 GPU 上進行計算操做
if torch.cuda.is_available():
    device = torch.device("cuda")          # 定義一個 CUDA 設備對象
    y = torch.ones_like(x, device=device)  # 顯示建立在 GPU 上的一個 tensor
    x = x.to(device)                       # 也能夠採用 .to("cuda") 
    z = x + y
    print(z)
    print(z.to("cpu", torch.double))       # .to() 方法也能夠改變數值類型
複製代碼

輸出結果,第一個結果就是在 GPU 上的結果,打印變量的時候會帶有 device='cuda:0',而第二個是在 CPU 上的變量。

tensor([1.4549], device='cuda:0')

tensor([1.4549], dtype=torch.float64)
複製代碼

本小節教程:

pytorch.org/tutorials/b…

本小節的代碼:

github.com/ccc013/Deep…

2. autograd

對於 Pytorch 的神經網絡來講,很是關鍵的一個庫就是 autograd ,它主要是提供了對 Tensors 上全部運算操做的自動微分功能,也就是計算梯度的功能。它屬於 define-by-run 類型框架,即反向傳播操做的定義是根據代碼的運行方式,所以每次迭代均可以是不一樣的。

接下來會簡單介紹一些例子來講明這個庫的做用。

2.1 張量

torch.Tensor 是 Pytorch 最主要的庫,當設置它的屬性 .requires_grad=True,那麼就會開始追蹤在該變量上的全部操做,而完成計算後,能夠調用 .backward() 並自動計算全部的梯度,獲得的梯度都保存在屬性 .grad 中。

調用 .detach() 方法分離出計算的歷史,能夠中止一個 tensor 變量繼續追蹤其歷史信息 ,同時也防止將來的計算會被追蹤。

而若是是但願防止跟蹤歷史(以及使用內存),能夠將代碼塊放在 with torch.no_grad(): 內,這個作法在使用一個模型進行評估的時候很是有用,由於模型會包含一些帶有 requires_grad=True 的訓練參數,但實際上並不須要它們的梯度信息。

對於 autograd 的實現,還有一個類也是很是重要-- Function

TensorFunction 兩個類是有關聯並創建了一個非循環的圖,能夠編碼一個完整的計算記錄。每一個 tensor 變量都帶有屬性 .grad_fn ,該屬性引用了建立了這個變量的 Function (除了由用戶建立的 Tensors,它們的 grad_fn=None )。

若是要進行求導運算,能夠調用一個 Tensor 變量的方法 .backward() 。若是該變量是一個標量,即僅有一個元素,那麼不須要傳遞任何參數給方法 .backward() ,當包含多個元素的時候,就必須指定一個 gradient 參數,表示匹配尺寸大小的 tensor,這部分見第二小節介紹梯度的內容。

接下來就開始用代碼來進一步介紹。

首先導入必須的庫:

import torch
複製代碼

開始建立一個 tensor, 並讓 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=<AddBackward>)
複製代碼

y 是一個操做的結果,因此它帶有屬性 grad_fn

print(y.grad_fn)
複製代碼

輸出結果:

<AddBackward object at 0x00000216D25DCC88>
複製代碼

繼續對變量 y 進行操做:

z = y * y * 3
out = z.mean()

print('z=', z)
print('out=', out)
複製代碼

輸出結果:

z= tensor([[27., 27.],
        [27., 27.]], grad_fn=<MulBackward>)

out= tensor(27., grad_fn=<MeanBackward1>)
複製代碼

實際上,一個 Tensor 變量的默認 requires_gradFalse ,能夠像上述定義一個變量時候指定該屬性是 True,固然也能夠定義變量後,調用 .requires_grad_(True) 設置爲 True ,這裏帶有後綴 _ 是會改變變量自己的屬性,在上一節介紹加法操做 add_() 說明過,下面是一個代碼例子:

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)
複製代碼

輸出結果以下,第一行是爲設置 requires_grad 的結果,接着顯示調用 .requires_grad_(True),輸出結果就是 True

False

True

<SumBackward0 object at 0x00000216D25ED710>
複製代碼

2.2 梯度

接下來就是開始計算梯度,進行反向傳播的操做。out 變量是上一小節中定義的,它是一個標量,所以 out.backward() 至關於 out.backward(torch.tensor(1.)) ,代碼以下:

out.backward()
# 輸出梯度 d(out)/dx
print(x.grad)
複製代碼

輸出結果:

tensor([[4.5000, 4.5000],
        [4.5000, 4.5000]])
複製代碼

結果應該就是獲得數值都是 4.5 的矩陣。這裏咱們用 o 表示 out 變量,那麼根據以前的定義會有:

o = \frac{1}{4}\sum_iz_i,\\
z_i = 3(x_i + 2)^2, \\
z_i|_{x_i=1} = 27

詳細來講,初始定義的 x 是一個全爲 1 的矩陣,而後加法操做 x+2 獲得 y ,接着 y*y*3, 獲得 z ,而且此時 z 是一個 2*2 的矩陣,因此總體求平均獲得 out 變量應該是除以 4,因此獲得上述三條公式。

所以,計算梯度:

\frac{\partial o}{\partial x_i} = \frac{3}{2}(x_i+2),\\
\frac{\partial o}{\partial x_i}|_{x_i=1} = \frac{9}{2} = 4.5

從數學上來講,若是你有一個向量值函數:

\vec{y}=f(\vec{x})

那麼對應的梯度是一個雅克比矩陣(Jacobian matrix):

\begin{split}J=\left(\begin{array}{ccc}
 \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{array}\right)\end{split}

通常來講,torch.autograd 就是用於計算雅克比向量(vector-Jacobian)乘積的工具。這裏略過數學公式,直接上代碼例子介紹:

x = torch.randn(3, requires_grad=True)

y = x * 2
while y.data.norm() < 1000:
    y = y * 2

print(y)
複製代碼

輸出結果:

tensor([ 237.5009, 1774.2396,  274.0625], grad_fn=<MulBackward>)
複製代碼

這裏獲得的變量 y 再也不是一個標量,torch.autograd 不能直接計算完整的雅克比行列式,但咱們能夠經過簡單的傳遞向量給 backward() 方法做爲參數獲得雅克比向量的乘積,例子以下所示:

v = torch.tensor([0.1, 1.0, 0.0001], dtype=torch.float)
y.backward(v)

print(x.grad)
複製代碼

輸出結果:

tensor([ 102.4000, 1024.0000,    0.1024])
複製代碼

最後,加上 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
複製代碼

更多有關 autogradFunction 的介紹:

pytorch.org/docs/autogr…

本小節教程:

pytorch.org/tutorials/b…

本小節的代碼:

github.com/ccc013/Deep…


小結

第一篇主要簡單介紹 Pytorch 的兩大做用,替代 Numpy 以及一個新的深度學習工具,固然主要仍是後者讓其可以在短短兩三年內快速發展,而且因爲 Tensorflow 的一些缺點,愈來愈多人會選擇採用 Pytorch 工具,特別是對於學術界的科研學者來講,Pytorch 其實會上手更加快。

另外,還介紹了最重要也是最基礎的張量的知識,其方法、操做和 Numpy 的數組很是類似,二者還能夠互相轉換,稍微不一樣的是張量能夠應用到 GPU 上加快計算速度。

最後簡單介紹了 autograd 這個庫,對於深度學習很是重要,它能夠自動計算梯度,很是有用。

歡迎關注個人微信公衆號--機器學習與計算機視覺,或者掃描下方的二維碼,你們一塊兒交流,學習和進步!

往期精彩推薦

機器學習系列
Github項目 & 資源教程推薦
相關文章
相關標籤/搜索