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_list是float32類型,而t_from_array是int64類型。如果想讓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創建一個全0的tensor,默認資料類型為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)
功能:依input的size創建全0的tensor。
主要參數:
input(Tensor) - 創建的tensor與intput具有相同的形狀。
example:
import torch
t1 = torch.tensor([[1., -1.], [1., -1.]])
t2 = torch.zeros_like(t1)
print(t2)
Copy
tensor([[0., 0.],
[0., 0.]])
除了創建全0還有創建全1的tensor,使用方法是一樣的,這裡就不贅述。
torch.ones(*size, out=None, dtype=None, layout=torch.strided, device=None, requires_grad=False)
功能:依給定的size創建一個全1的tensor。
torch.ones_like(input, dtype=None, layout=None, device=None, requires_grad=False)
功能:依input的size創建全1的tensor。
torch.full(size, fill_value, out=None, dtype=None, layout=torch.strided, device=None, requires_grad=False)
功能:依給定的size創建一個值全為fill_value的tensor。
主要參數:
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) – 數列起始值,預設值為0。the 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)
功能:為每一個元素以給定的mean和std用高斯分佈生成亂數
主要參數:
mean (Tensor or Float) - 高斯分佈的均值,
std (Tensor or Float) - 高斯分佈的標準差
特別注意事項:
mean和std的取值分別有2種,共4種組合,不同組合產生的效果也不同,需要注意
mean為張量,std為張量,torch.normal(mean, std, out=None),每個元素從不同的高斯分佈採樣,分佈的均值和標準差由mean和std對應位置元素的值確定;
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)
功能:生成從0到n-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的朋友應該知道,Tensor與numpy的資料結構很類似,不僅資料結構類似,操作也是類似的,接下來介紹Tensor的常用操作。由於操作函數很多,這裡就不一一舉例,僅通過表格說明各個函數作用,詳細介紹可查看官方文檔
將多個張量拼接在一起,例如多個特徵圖的融合可用。 |
|
---|---|
同cat, 是cat()的別名。 |
|
返回共軛複數。 |
|
將tensor在某個維度上分成n份。 |
|
類似numpy.dsplit()., 將張量按索引或指定的份數進行切分。 |
|
水準堆疊張量。即第二個維度上增加,等同於torch.hstack。 |
|
沿第三個軸進行逐圖元(depthwise)拼接。 |
|
高級索引方法,目標檢測中常用於索引bbox。在指定的軸上,根據給定的index進行索引。強烈推薦看example。 |
|
類似numpy.hsplit(),將張量按列進行切分。若傳入整數,則按等分劃分。若傳入list,則按list中元素進行索引。例如:[2, 3] and dim=0 would result in the tensors input[:2], input[2:3], and input[3:]. |
|
水準堆疊張量。即第二個維度上增加,等同於torch.column_stack。 |
|
在指定的維度上,按索引進行選擇資料,然後拼接成新張量。可知道,新張量的指定維度上長度是index的長度。 |
|
根據mask(0/1, False/True 形式的mask)索引資料,返回1-D張量。 |
|
移動軸。如0,1軸交換:torch.movedim(t, 1, 0) . |
|
同movedim。Alias for torch.movedim().(這裡發現pytorch很多地方會將dim和axis混用,概念都是一樣的。) |
|
變窄的張量?從功能看還是索引。在指定軸上,設置起始和長度進行索引。例如:torch.narrow(x, 0, 0, 2), 從第0個軸上的第0元素開始,索引2個元素。x[0:0+2, ...] |
|
返回非零元素的index。torch.nonzero(torch.tensor([1, 1, 1, 0, 1])) 返回tensor([[ 0], [ 1], [ 2], [ 4]])。建議看example,一看就明白,尤其是對角線矩陣的那個例子,太清晰了。 |
|
交換軸。 |
|
變換形狀。 |
|
按行堆疊張量。即第一個維度上增加,等同於torch.vstack。Alias of torch.vstack(). |
|
scatter_(dim, index, src, reduce=None) → Tensor。將src中資料根據index中的索引按照dim的方向填進input中。這是一個十分難理解的函數,其中index是告訴你哪些位置需要變,src是告訴你要變的值是什麼。這個就必須配合例子講解,請跳轉到本節底部進行學習。 |
|
同scatter一樣,對input進行元素修改,這裡是 +=, 而scatter是直接替換。 |
|
按給定的大小切分出多個張量。例如:torch.split(a, [1,4]); torch.split(a, 2) |
|
移除張量為1的軸。如t.shape=[1, 3, 224, 224]. t.squeeze().shape -> [3, 224, 224] |
|
在新的軸上拼接張量。與hstack\vstack不同,它是新增一個軸。默認從第0個軸插入新軸。 |
|
Alias for torch.transpose().交換軸。 |
|
Alias for torch.transpose().交換軸。 |
|
轉置。 |
|
取張量中的某些元素,返回的是1D張量。torch.take(src, torch.tensor([0, 2, 5]))表示取第0,2,5個元素。 |
|
取張量中的某些元素,返回的張量與index維度保持一致。可搭配torch.argmax(t)和torch.argsort使用,用於對最大概率所在位置取值,或進行排序,詳見官方文檔的example。 |
|
切分張量,核心看indices_or_sections變數如何設置。 |
|
將張量重複X遍,X遍表示可按多個維度進行重複。例如:torch.tile(y, (2, 2)) |
|
交換軸。 |
|
移除張量的某個軸,並返回一串張量。如[[1], [2], [3]] --> [1], [2], [3] 。把行這個軸拆了。 |
|
增加一個軸,常用於匹配資料維度。 |
|
垂直切分。 |
|
垂直堆疊。 |
|
根據一個是非條件,選擇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
設計兩個核心問題:
- input哪個位置需要替換?
- 替換成什麼?
答:
- 從公式可知道,依次從index中找到元素放到dim的位置,就是input需要變的地方。
- 變成什麼呢? 從src中找,src中與index一樣位置的那個元素值放到input中。
案例1:
>>> src = torch.arange(1, 11).reshape((2, 5))
>>> src
tensor([[ 1, 2, 3, 4, 5],
[ 6, 7, 8, 9, 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走。 index:2*3,告訴input(零矩陣),你的哪些行是要被替換的。 src:input要替換成什麼呢?從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的索引保持一致的
案例3:one-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也有一些設置函數。
獲取一個隨機的隨機種子。Returns a 64 bit number used to seed the RNG. |
|
---|---|
手動設置隨機種子,建議設置為42,這是近期一個玄學研究。說42有效的提高模型精度。當然大家可以設置為你喜歡的,只要保持一致即可。 |
|
返回初始種子。 |
|
獲取亂數產生器狀態。Returns the random number generator state as a torch.ByteTensor. |
|
設定亂數產生器狀態。這兩怎麼用暫時未知。Sets the random number generator state. |
以上均是設置cpu上的張量隨機種子,在cuda上是另外一套隨機種子,如torch.cuda.manual_seed_all(seed), 這些到cuda模組再進行介紹,這裡只需要知道cpu和cuda上需要分別設置隨機種子。
張量的數學操作
張量還提供大量數學操作,估計了一下,有快一百個函數,這裡就不再一一分析,只需要知道有哪幾大類,用到的時候來查吧。
- 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, diag,flip等
- BLAS and LAPACK Operations:BLAS(Basic Linear Algebra Subprograms)基礎線性代數)操作。如, addmm, dot, inner, svd等。
小結
本節介紹了張量主要的操作函數,並歸類到各個小結,這些僅是張量的部分操作,更多操作還請大家多多看官方文檔。對於張量,主要是要理解2.3小節中張量的結構以及作用,對於它的操作就像numpy一樣簡單易用。
下一節就開始講解pytorch的核心——autograd,autograd也是現代深度學習框架的核心,是實現自動微分的具體實現。