2.4 張量的相關函數

接下來開始學習各類張量的api,主要參考官方文檔,通過右邊目錄欄可以看出有以下幾個部分。

  • torchTensors
  • Generators
  • Random sampling
  • Serialization
  • Parallelism
  • Locally disabling gradient computation
  • Math operations
  • Utilities

裡面有上百個函數,這裡只挑高頻使用的進行講解,建議大家自行流覽一遍官方文檔,看看都有哪些功能,便於今後使用到的時候不必重複造輪子。

張量的創建

直接創建

torch.tensor

torch.tensor(data, dtype=None, device=None, requires_grad=False, pin_memory=False)

  • data(array_like) - tensor的初始資料,可以是list, tuple, numpy array, scalar或其他類型。
  • dtype(torch.dtype, optional) - tensor的資料類型,如torch.uint8, torch.float, torch.long
  • device (torch.device, optional) – 決定tensor位於cpu還是gpu。如果為None,將會採用預設值,預設值在torch.set_default_tensor_type()中設置,默認為 cpu
  • requires_grad (bool, optional) – 決定是否需要計算梯度。
  • pin_memory (bool, optional) – 是否將tensor存於鎖頁記憶體。這與記憶體的存儲方式有關,通常為False
  • import torch
  • import numpy as np
  • l = [[1., -1.], [1., -1.]]
  • t_from_list = torch.tensor(l)
  • arr = np.array([[1, 2, 3], [4, 5, 6]])
  • t_from_array = torch.tensor(arr)
  • print(t_from_list, t_from_list.dtype)
  • print(t_from_array, t_from_array.dtype)

Copy

tensor([[ 1., -1.],

​ [ 1., -1.]]) torch.float32

tensor([[1, 2, 3],

​ [4, 5, 6]]) torch.int64

可以看到t_from_listfloat32類型,而t_from_arrayint64類型。如果想讓tensor是其他資料類型,可以在創建tensor時使用dtype參數確定資料類型。

import torch

import numpy as np

arr = np.array([[1, 2, 3], [4, 5, 6]])

t_from_array = torch.tensor(arr, dtype=torch.uint8)

print(t_from_array)

Copy

tensor([[1, 2, 3],

​ [4, 5, 6]], dtype=torch.uint8)

torch.from_numpy

還有一種常用的通過numpy創建tensor方法是torch.from_numpy()。這裡需要特別注意的是,創建的tensor和原array共用同一塊記憶體(The returned tensor and ndarray share the same memory. ),即當改變array裡的數值,tensor中的數值也會被改變。

import torch

import numpy as np

arr = np.array([[1, 2, 3], [4, 5, 6]])

t_from_numpy = torch.from_numpy(arr)

print("numpy array: ", arr)

print("tensor : ", t_from_numpy)

print("\n修改arr")

arr[0, 0] = 0

print("numpy array: ", arr)

print("tensor : ", t_from_numpy)

print("\n修改tensor")

t_from_numpy[0, 0] = -1

print("numpy array: ", arr)

print("tensor : ", t_from_numpy)

Copy

> >

numpy array: [[1 2 3]

[4 5 6]]

tensor : tensor([[1, 2, 3],

​ [4, 5, 6]])

修改arr

numpy array: [[0 2 3]

[4 5 6]]

tensor : tensor([[0, 2, 3],

​ [4, 5, 6]])

修改tensor

numpy array: [[-1 2 3]

[ 4 5 6]]

tensor : tensor([[-1, 2, 3],

​ [ 4, 5, 6]])

可以看到雖然只改變了arr的值,但是tensor中的data也被改變了,這一點在使用過程中需要注意。

依數值創建

torch.zeros

torch.zeros(*size, out=None, dtype=None, layout=torch.strided, device=None, requires_grad=False)

功能:依給定的size創建一個全0tensor,默認資料類型為torch.float32(也稱為torch.float)。

主要參數:

layout(torch.layout, optional) - 參數表明張量在記憶體中採用何種佈局方式。常用的有torch.strided, torch.sparse_coo等。

out(tensor, optional) - 輸出的tensor,即該函數返回的tensor可以通過out進行賦值,請看例子。

example:

import torch

o_t = torch.tensor([1])

t = torch.zeros((3, 3), out=o_t)

print(t, '\n', o_t)

print(id(t), id(o_t))

Copy

> >

tensor([[0, 0, 0],

​ [0, 0, 0],

​ [0, 0, 0]])

tensor([[0, 0, 0],

​ [0, 0, 0],

​ [0, 0, 0]])

4925603056 4925603056

可以看到,通過torch.zeros創建的張量不僅賦給了t,同時賦給了o_t,並且這兩個張量是共用同一塊記憶體,只是變數名不同。

torch.zeros_like

torch.zeros_like(input, dtype=None, layout=None, device=None, requires_grad=False)

功能:依inputsize創建全0tensor

主要參數:

input(Tensor) - 創建的tensorintput具有相同的形狀。

example:

import torch

t1 = torch.tensor([[1., -1.], [1., -1.]])

t2 = torch.zeros_like(t1)

print(t2)

Copy

tensor([[0., 0.],

​ [0., 0.]])

除了創建全0還有創建全1tensor,使用方法是一樣的,這裡就不贅述。

torch.ones(*size, out=None, dtype=None, layout=torch.strided, device=None, requires_grad=False)

功能:依給定的size創建一個全1tensor

torch.ones_like(input, dtype=None, layout=None, device=None, requires_grad=False)

功能:依inputsize創建全1tensor

torch.full(size, fill_value, out=None, dtype=None, layout=torch.strided, device=None, requires_grad=False)

功能:依給定的size創建一個值全為fill_valuetensor

主要參數:

siz (int...) - tensor的形狀。

fill_value - 所創建tensor的值

out(tensor, optional) - 輸出的tensor,即該函數返回的tensor可以通過out進行賦值。

example:

import torch

print(torch.full((2, 3), 3.141592))

Copy

torch.full_like(input, fill_value, out=None, dtype=None, layout=torch.strided, device=None, requires_grad=False)

torch.full_like之於torch.full等同於torch.zeros_like之於torch.zeros,因此不再贅述。

torch.arange(start=0, end, step=1, out=None, dtype=None, layout=torch.strided, device=None, requires_grad=False)

功能:創建等差的1維張量,長度為 (end-start)/step,需要注意數值區間為[start, end)

主要參數:

start (Number) – 數列起始值,預設值為0the starting value for the set of points. Default: 0.

end (Number) – 數列的結束值。

step (Number) – 數列的等差值,預設值為1

out (Tensor, optional) – 輸出的tensor,即該函數返回的tensor可以通過out進行賦值。

example:

import torch

print(torch.arange(1, 2.51, 0.5))

Copy

torch.range()函數就不推薦了,因為官網說了“This function is deprecated in favor of torch.arange().”

torch.linspace(start, end, steps=100, out=None, dtype=None, layout=torch.strided, device=None, requires_grad=False)

功能:創建均分的1維張量,長度為steps,區間為[start, end]

主要參數:

start (float) – 數列起始值。

end (float) – 數列結束值。

steps (int) – 數列長度。

example:

print(torch.linspace(3, 10, steps=5))

print(torch.linspace(1, 5, steps=3))

Copy

torch.logspace(start, end, steps=100, base=10.0, out=None, dtype=None, layout=torch.strided, device=None, requires_grad=False)

功能:創建對數均分的1維張量,長度為steps, 底為base

主要參數:

start (float) – 確定數列起始值為base^start

end (float) – 確定數列結束值為base^end

steps (int) – 數列長度。

base (float) - 對數函數的底,預設值為10,此參數是在pytorch 1.0.1版本之後加入的。

example:

torch.logspace(start=0.1, end=1.0, steps=5)

torch.logspace(start=2, end=2, steps=1, base=2)

Copy

torch.eye(n, m=None, out=None, dtype=None, layout=torch.strided, device=None, requires_grad=False)**

功能:創建單位對角矩陣。

主要參數:

n (int) - 矩陣的行數

m (int, optional) - 矩陣的列數,預設值為n,即默認創建一個方陣

example:

import torch

print(torch.eye(3))

print(torch.eye(3, 4))

Copy

torch.empty(*size, out=None, dtype=None, layout=torch.strided, device=None, requires_grad=False, pin_memory=False)

功能:依size創建張量,這裡的指的是不會進行初始化賦值操作。

主要參數:

size (int...) - 張量維度

pin_memory (bool, optional) - pinned memory 又稱page locked memory,即鎖頁記憶體,該參數用來指示是否將tensor存於鎖頁記憶體,通常為False,若記憶體足夠大,建議設置為True,這樣在轉到GPU時會快一些。

torch.empty_like(input, dtype=None, layout=None, device=None, requires_grad=False)

功能:torch.empty_like之於torch.empty等同於torch.zeros_like之於torch.zeros,因此不再贅述。

torch.empty_strided(size, stride, dtype=None, layout=None, device=None, requires_grad=False, pin_memory=False)

功能:依size創建張量,這裡的指的是不會進行初始化賦值操作。

主要參數:

stride (tuple of python:ints) - 張量存儲在記憶體中的步長,是設置在記憶體中的存儲方式。

size (int...) - 張量維度

pin_memory (bool, optional) - 是否存於鎖頁記憶體。

依概率分佈創建

torch.normal(mean, std, out=None)

功能:為每一個元素以給定的meanstd用高斯分佈生成亂數

主要參數:

mean (Tensor or Float) - 高斯分佈的均值,

std (Tensor or Float) - 高斯分佈的標準差

特別注意事項:

meanstd的取值分別有2種,共4種組合,不同組合產生的效果也不同,需要注意

mean為張量,std為張量,torch.normal(mean, std, out=None),每個元素從不同的高斯分佈採樣,分佈的均值和標準差由meanstd對應位置元素的值確定;

mean為張量,std為標量,torch.normal(mean, std=1.0, out=None),每個元素採用相同的標準差,不同的均值;

mean為標量,std為張量,torch.normal(mean=0.0, std, out=None) 每個元素採用相同均值,不同標準差;

mean為標量,std為標量,torch.normal(mean, std, size, *, out=None) ,從一個高斯分佈中生成大小為size的張量;

案例1

import

mean = torch.arange(1, 11.)

std = torch.arange(1, 0, -0.1)

normal = torch.normal(mean=mean, std=std)

print("mean: {}, \nstd: {}, \nnormal: {}".format(mean, std, normal))

Copy

mean: tensor([ 1., 2., 3., 4., 5., 6., 7., 8., 9., 10.]),

std: tensor([1.0000, 0.9000, 0.8000, 0.7000, 0.6000, 0.5000, 0.4000, 0.3000, 0.2000,

​ 0.1000]),

normal: tensor([ 1.3530, -1.3498, 3.0021, 5.1200, 3.9818, 5.0163, 6.9272, 8.1171,

​ 9.0623, 10.0621])

1.3530是通過均值為1,標準差為1的高斯分佈採樣得來,

-1.3498是通過均值為2,標準差為0.9的高斯分佈採樣得來,以此類推

torch.rand(*size, out=None, dtype=None, layout=torch.strided, device=None, requires_grad=False)

功能:在區間[0, 1)上,生成均勻分佈。

主要參數:

size (int...) - 創建的張量的形狀

torch.rand_like(input, dtype=None, layout=None, device=None, requires_grad=False)

torch.rand_like之於torch.rand等同於torch.zeros_like之於torch.zeros,因此不再贅述。

torch.randint(low=0, high, size, out=None, dtype=None, layout=torch.strided, device=None, requires_grad=False)

功能:在區間[low, high)上,生成整數的均勻分佈。

主要參數:

low (int, optional) - 下限。

high (int) – 上限,主要是開區間。

size (tuple) – 張量的形狀。

example

print(torch.randint(3, 10, (2, 2)))

Copy

torch.randint_like(input, low=0, high, dtype=None, layout=torch.strided, device=None, requires_grad=False)

功能:torch.randint_like之於torch.randint等同於torch.zeros_like之於torch.zeros,因此不再贅述。

torch.randn(*size, out=None, dtype=None, layout=torch.strided, device=None, requires_grad=False)

功能:生成形狀為size的標準正態分佈張量。

主要參數:

size (int...) - 張量的形狀

torch.randn_like(input, dtype=None, layout=None, device=None, requires_grad=False)

功能:torch.rafndn_like之于torch_randn等同於torch.zeros_like之於torch.zeros,因此不再贅述。

torch.randperm(n, out=None, dtype=torch.int64, layout=torch.strided, device=None, requires_grad=False)

功能:生成從0n-1的隨機排列。perm == permutation

torch.bernoulli(input, *, generator=None, out=None)

功能:以input的值為概率,生成伯努力分佈(0-1分佈,兩點分佈)。

主要參數:

input (Tensor) - 分佈的概率值,該張量中的每個值的值域為[0-1]

example:

import torch

p = torch.empty(3, 3).uniform_(0, 1)

b = torch.bernoulli(p)

print("probability: \n{}, \nbernoulli_tensor:\n{}".format(p, b))

Copy

probability:

tensor([[0.7566, 0.2899, 0.4688],

​ [0.1662, 0.8341, 0.9572],

​ [0.6060, 0.4685, 0.6366]]),

bernoulli_tensor:

tensor([[0., 0., 1.],

​ [1., 1., 1.],

​ [1., 1., 1.]])

張量的操作

熟悉numpy的朋友應該知道,Tensornumpy的資料結構很類似,不僅資料結構類似,操作也是類似的,接下來介紹Tensor的常用操作。由於操作函數很多,這裡就不一一舉例,僅通過表格說明各個函數作用,詳細介紹可查看官方文檔

cat

將多個張量拼接在一起,例如多個特徵圖的融合可用。

concat

cat, cat()的別名。

conj

返回共軛複數。

chunk

tensor在某個維度上分成n份。

dsplit

類似numpy.dsplit(). 將張量按索引或指定的份數進行切分。

column_stack

水準堆疊張量。即第二個維度上增加,等同於torch.hstack

dstack

沿第三個軸進行逐圖元(depthwise)拼接。

gather

高級索引方法,目標檢測中常用於索引bbox。在指定的軸上,根據給定的index進行索引。強烈推薦看example

hsplit

類似numpy.hsplit(),將張量按列進行切分。若傳入整數,則按等分劃分。若傳入list,則按list中元素進行索引。例如:[2, 3] and dim=0 would result in the tensors input[:2], input[2:3], and input[3:].

hstack

水準堆疊張量。即第二個維度上增加,等同於torch.column_stack

index_select

在指定的維度上,按索引進行選擇資料,然後拼接成新張量。可知道,新張量的指定維度上長度是index的長度。

masked_select

根據mask0/1, False/True 形式的mask)索引資料,返回1-D張量。

movedim

移動軸。如01軸交換:torch.movedim(t, 1, 0) .

moveaxis

movedimAlias for torch.movedim().(這裡發現pytorch很多地方會將dimaxis混用,概念都是一樣的。)

narrow

變窄的張量?從功能看還是索引。在指定軸上,設置起始和長度進行索引。例如:torch.narrow(x, 0, 0, 2) 從第0個軸上的第0元素開始,索引2個元素。x[0:0+2, ...]

nonzero

返回非零元素的indextorch.nonzero(torch.tensor([1, 1, 1, 0, 1])) 返回tensor([[ 0], [ 1], [ 2], [ 4]])。建議看example,一看就明白,尤其是對角線矩陣的那個例子,太清晰了。

permute

交換軸。

reshape

變換形狀。

row_stack

按行堆疊張量。即第一個維度上增加,等同於torch.vstackAlias of torch.vstack().

scatter

scatter_(dim, index, src, reduce=None) Tensor。將src中資料根據index中的索引按照dim的方向填進input中。這是一個十分難理解的函數,其中index是告訴你哪些位置需要變,src是告訴你要變的值是什麼。這個就必須配合例子講解,請跳轉到本節底部進行學習。

scatter_add

scatter一樣,對input進行元素修改,這裡是 += scatter是直接替換。

split

按給定的大小切分出多個張量。例如:torch.split(a, [1,4]) torch.split(a, 2)

squeeze

移除張量為1的軸。如t.shape=[1, 3, 224, 224]. t.squeeze().shape -> [3, 224, 224]

stack

在新的軸上拼接張量。與hstack\vstack不同,它是新增一個軸。默認從第0個軸插入新軸。

swapaxes

Alias for torch.transpose().交換軸。

swapdims

Alias for torch.transpose().交換軸。

t

轉置。

take

取張量中的某些元素,返回的是1D張量。torch.take(src, torch.tensor([0, 2, 5]))表示取第0,2,5個元素。

take_along_dim

取張量中的某些元素,返回的張量與index維度保持一致。可搭配torch.argmax(t)torch.argsort使用,用於對最大概率所在位置取值,或進行排序,詳見官方文檔的example

tensor_split

切分張量,核心看indices_or_sections變數如何設置。

tile

將張量重複X遍,X遍表示可按多個維度進行重複。例如:torch.tile(y, (2, 2))

transpose

交換軸。

unbind

移除張量的某個軸,並返回一串張量。如[[1], [2], [3]] --> [1], [2], [3] 。把行這個軸拆了。

unsqueeze

增加一個軸,常用於匹配資料維度。

vsplit

垂直切分。

vstack

垂直堆疊。

where

根據一個是非條件,選擇x的元素還是y的元素,拼接成新張量。看案例可瞬間明白。

scater_

scater是將input張量中的部分值進行替換。公式如下:

self[index[i][j][k]][j][k] = src[i][j][k]  # if dim == 0

self[i][index[i][j][k]][k] = src[i][j][k]  # if dim == 1

self[i][j][index[i][j][k]] = src[i][j][k]  # if dim == 2

Copy

設計兩個核心問題:

  1. input哪個位置需要替換?
  2. 替換成什麼?

答:

  1. 從公式可知道,依次從index中找到元素放到dim的位置,就是input需要變的地方。
  2. 變成什麼呢? src中找,src中與index一樣位置的那個元素值放到input中。

案例1

>>> src = torch.arange(1, 11).reshape((2, 5))

>>> src

tensor([[ 12345],

        [ 6789, 10]])

>>> index = torch.tensor([[0, 1, 2, 0]])

>>> torch.zeros(3, 5, dtype=src.dtype).scatter_(0, index, src)

tensor([[1, 0, 0, 4, 0],

        [0, 2, 0, 0, 0],

        [0, 0, 3, 0, 0]])

Copy

dim=0, 所以行號跟著index的元素走。其它跟index的索引走。

第一步:找到index的第一個元素index[0, 0]0 那麼把src[0, 0](是1)放到input[0, 0]
第二步:找到index的第二個元素index[0, 1]1 那麼把src[0, 1](是2)放到input[1, 1]
第三步:找到index的第三個元素index[0, 2]2 那麼把src[0, 2](是3)放到input[2, 2]
第四步:找到index的第四個元素index[0, 3]0 那麼把src[0, 3](是4)放到input[0, 3]

案例2

 

>>> src = torch.arange(1, 11).reshape((2, 5))

>>> src

tensor([[ 1,  2,  3,  4,  5],

        [ 6,  7,  8,  9, 10]])

>>> index = torch.tensor([[0, 2, 4], [1, 2, 3]])

>>> index

tensor([[0, 2, 4],

        [1, 2, 3]])

>>> torch.zeros(3, 5, dtype=src.dtype).scatter_(1, index, src)

tensor([[1, 0, 2, 0, 3],

        [0, 6, 7, 8, 0],

        [0, 0, 0, 0, 0]])

Copy

dim=1:告訴input(零矩陣)的索引,沿著列進行索引,行根據index index2*3,告訴input(零矩陣),你的哪些行是要被替換的。 srcinput要替換成什麼呢?從src裡找,怎麼找?通過index的索引對應的找。

第一步:找到index的第一個元素index[0, 0]0 那麼把src[0, 0](是1)放到input[0, 0]
第二步:找到index的第二個元素index[0, 1]2 那麼把src[0, 1](是2)放到input[0, 2]
第三步:找到index的第三個元素index[0, 2]4 那麼把src[0, 2](是3)放到input[0, 4]
第四步:找到index的第四個元素index[1, 0]1 那麼把src[1, 0](是6)放到input[1, 1]
第五步:找到index的第五個元素index[1, 1]2 那麼把src[1, 1](是7)放到input[1, 2]
第六步:找到index的第六個元素index[1, 2]3 那麼把src[1, 2](是8)放到input[1, 3]

這裡可以看到

  • index的元素是決定input的哪個位置要變
  • 變的值是從src上對應於index的索引上找。可以看到src的索引與index的索引保持一致的

案例3one-hot的生成

>>> label = torch.arange(3).view(-1, 1)

>>> label

tensor([[0],

        [1],

        [2]])

>>> torch.zeros(3, 3).scatter_(1, label, 1)

tensor([[1., 0., 0.],

        [0., 1., 0.],

        [0., 0., 1.]])

Copy

第一步:找到index的第一個元素index[0, 0]0 那麼把src[0, 0](是1)放到input[0, 0]

第二步:找到index的第二個元素index[1, 0]1 那麼把src[1, 0](是1)放到input[1, 1]

第三步:找到index的第三個元素index[2, 0]2 那麼把src[2, 0](是1)放到input[2, 2]

one-hot的案例不利於理解scater函數,因為它的行和列是一樣的。。。其實input[x, y] 中的x,y是有區別的,x是根據index走,y是根據index的元素值走的,而具體的值是根據src的值。)

張量的隨機種子

隨機種子(random seed)是程式設計語言中基礎的概念,大多數程式設計語言都有隨機種子的概念,它主要用於實驗的複現。針對隨機種子pytorch也有一些設置函數。

seed

獲取一個隨機的隨機種子。Returns a 64 bit number used to seed the RNG.

manual_seed

手動設置隨機種子,建議設置為42,這是近期一個玄學研究。說42有效的提高模型精度。當然大家可以設置為你喜歡的,只要保持一致即可。

initial_seed

返回初始種子。

get_rng_state

獲取亂數產生器狀態。Returns the random number generator state as a torch.ByteTensor.

set_rng_state

設定亂數產生器狀態。這兩怎麼用暫時未知。Sets the random number generator state.

以上均是設置cpu上的張量隨機種子,在cuda上是另外一套隨機種子,如torch.cuda.manual_seed_all(seed) 這些到cuda模組再進行介紹,這裡只需要知道cpucuda上需要分別設置隨機種子。

張量的數學操作

張量還提供大量數學操作,估計了一下,有快一百個函數,這裡就不再一一分析,只需要知道有哪幾大類,用到的時候來查吧。

  • Pointwise Ops 逐元素的操作,如abs, cos, sin, floor, floor_divide, pow
  • Reduction Ops: 減少元素的操作,如argmax, argmin, all, any, mean, norm, var
  • Comparison Ops:對比操作, ge, gt, le, lt, eq, argsort, isnan, topk,
  • Spectral Ops: 譜操作,如短時傅裡葉變換等各類信號處理的函數。
  • Other Operations:其它, clone diagflip
  • BLAS and LAPACK OperationsBLASBasic Linear Algebra Subprograms)基礎線性代數)操作。如, addmm, dot, inner, svd等。

小結

本節介紹了張量主要的操作函數,並歸類到各個小結,這些僅是張量的部分操作,更多操作還請大家多多看官方文檔。對於張量,主要是要理解2.3小節中張量的結構以及作用,對於它的操作就像numpy一樣簡單易用。

下一節就開始講解pytorch的核心——autogradautograd也是現代深度學習框架的核心,是實現自動微分的具體實現。

 

arrow
arrow
    全站熱搜
    創作者介紹
    創作者 HCHUNGW 的頭像
    HCHUNGW

    HCHUNGW的部落格

    HCHUNGW 發表在 痞客邦 留言(0) 人氣()