注:本文是筆者結合自己閱讀和使用pytorch的經驗,又系統學習了一遍https://github.com/chenyuntc/pytorch-book的過程中,将自己認為有必要掌握和記住的知識整理成的學習筆記,并非系統的教程,主要目的是為了友善自己梳理、記憶知識,以及友善有相同需求的讀者查閱某些知識。
逐元素操作
1、在pytorch中,不僅僅針對逐元素的操作,tensor的計算往往具有兩種形式,比如torch.mul(a, b)與a.mul(b)二者是等價的
2、記住這些逐元素運算對應的符号:
絕對值 / 平方根 / 取模 / 求幂 / 乘法 / 除法 / 以e為底求指數 / 取ln / 向上取整 / 向下取整 / 四舍五入 / 取整數部分 / 取小數部分 / 正弦 / 餘弦
abs / sqrt / fmod / pow / mul / div / exp / log / ceil / floor / round / trunc / frac / sin / cos
3、mul和div表示的是逐元素乘或逐元素除,不要把mul和矩陣乘搞混了;mul和div在pytorch中被重載為了*和/,也就是說*和/表示的是逐元素乘或者逐元素除,不要把*和矩陣乘搞混了;另外,mul和div要求參與運算的要麼是形狀完全一緻的兩個tensor,要麼是前者是一個tensor,而後者是一個數字
4、取對數隻有torch.log(),torch.log2(),torch.log10(),其他底可以換底公式換出來;求指數exp預設以e為底,但是torch.pow很靈活,假如a = torch.tensor([1,2,3]),既可以torch.pow(a,2)表示tensor([1^2,2^2,3^2]),也可以torch.pow(2,a)表示tensor([2^1,2^2,2^3])。
5、逐元素操作還有torch.sigmoid和torch.tanh等激活函數,但是這些激活函數在torch.nn中也存在
歸并操作
1、記住這些歸并操作對應的符号:
均值 / 求和 / 中位數 / 衆數 / 範數 / 歐式距離 / 标準差 / 方差
mean / sum / median / mode / norm / dist / std / var
2、使用歸并操作的時候需要考慮兩個參數,dim和keep_dim,當這兩個參數都被設定的時候,輸出的形狀如下:
假設輸入形狀為(m, n, k),那麼根據不同的dim和keep_dim得到的tensor形狀如下表
keep_dim=True | keep_dim=False | |
dim=0 | (1, n, k) | (n, k) |
dim=1 | (m, 1, k) | (m, k) |
dim=2 | (m, n, 1) | (m, n) |
3、如果不指定dim,那麼得到的會是一個隻有一個元素的tensor,比如tensor(100);如果不指定keep_dim,keep_dim預設為False
4、對于dim的了解:假設輸入的tensor A形狀為(m, n, k),假設做的是求和操作,且指定dim=1,根據2,得到的tensor B的形狀是(m, k),A和B滿足
In [1]: a = t.arange(24)
In [2]: b = a.view(2, 3, 4)
In [3]: b
Out[3]: tensor([[[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]],
[[12, 13, 14, 15],
[16, 17, 18, 19],
[20, 21, 22, 23]]])
In [4]: c = t.sum(b, dim=1)
In [5]: c, c.size()
Out[5]: (tensor([[12, 15, 18, 21],
[48, 51, 54, 57]]), torch.Size([2, 4]))
比較操作
1、gt(大于)/ lt(小于)/ ge(大于等于)/ le(小于等于)/ eq(等于)/ ne(不等于),這些分别被重載為了> / < / >= / <= / == / !=,需要注意的是前面提到過的一點,兩個tensor通過比較操作得到的是一個ByteTensor(即dtype=torch.uint8),這種tensor是可以以mask的方式直接作為同形狀tensor的索引的
2、torch.max和torch.min的使用,以torch.max為例,根據參數不同共有三種用法
(1) torch.max(tensor) 傳回的是隻含一個元素的tensor,這個元素是輸入tensor中最大的元素
(2)torch.max(tensor, dim) 傳回指定維上較大的數,以及其所在下标;torch.max在這種輸入和上一種輸入放在一起看,可以看做是一個歸并操作,隻是多傳回一個較大數所在的下标
(2)torch.max(tensor, tensor) 要求輸入的兩個tensor同形狀,輸出也是同形狀的,輸出的每個位置對應的是兩個tensor中那個位置上較大的數
In [1]: a = t.randn(2, 3)
In [2]: a
Out[2]: tensor([[ 1.0437, 0.6610, -0.1947],
[-1.7523, 1.7876, 0.6388]])
In [3]: b = t.randn(2, 3)
Out[3]: tensor([[-0.3039, 1.0680, -0.2654],
[ 0.5515, -1.3597, -0.8332]])
In [4]: t.max(a)
Out[4]: tensor(1.7876)
In [5]: t.max(a, dim=1) # 注意輸出是一個元組,分别是較大的數和所在的下标
Out[5]: (tensor([1.0437, 1.7876]), tensor([0, 1]))
In [6]: t.max(a, b)
Out[6]: tensor([[ 1.0437, 1.0680, -0.1947],
[ 0.5515, 1.7876, 0.6388]])
3、torch.topk(input, k, dim=None, largest=True, sorted=True)
參數:
input(Tensor)
k(int)
dim(int):假設輸入的tensor A形狀是(m, n, p),且dim=1,做topk之後得到的tensor B的形狀是(m, k, p),其中B[i,:,j]中的k個元素是A[i,:,j]中n個元素中挑出的前k個;不指定dim時,dim預設為最後一維
largest(Boolean):預設為True,為True表示取前k大,為False表示取前k小
sorted(Boolean):預設為True,假設挑出的是前k大,為True時這k個元素按從大到小的順序排序,為False時隻是保證是前k大,并不一定有序
輸出:輸出一個tuple,裡面是兩個tensor,前一個tensor是topk的元素,後一個tensor是topk的下标,這兩個tensor是同形狀的。假設輸入的tensor形狀為(p, q, m),k=K,dim=1,largest和sorted為True,那麼輸出的這兩個tensor形狀均為(p, K, m)。
In [1]: a = t.randn(2, 3)
In [2]: a
Out[2]: tensor([[ 0.5339, 0.2233, 2.5735],
[-2.0343, -1.5293, -0.1112]])
In [3]: b = t.topk(a, k=2, dim=1)
In [4]: b
Out[4]: (tensor([[ 2.5735, 0.5339],
[-0.1112, -1.5293]]),
tensor([[2, 0],
[2, 1]]))
In [5]: c = t.topk(a, k=1, dim=0)
In [6]: c
Out[6]: ( tensor([[0.5339, 0.2233, 2.5735]]), tensor([[0, 0, 0]]) )
4、torch.sort(input, dim=-1, descending=False)
參數:
input(Tensor)
dim(int):假設輸入的tensor A的形狀為(m, n, p),且dim=1,那麼排序後的得到的tensor B中,B[i, :, j]是排序後的A[i, :, j],不顯式指定時dim預設為最後一維
descending(Boolean):預設為False,為False時元素升序排列,否則降序排列
輸出:輸出一個tuple,裡面有兩個tensor,前一個是排序的結果,後一個是排序後tensor對應排序前的下标,兩個tensor是同形狀的。假設輸入tensor的形狀為(m, n, p),則輸出的兩個tensor都是(m, n, p)的。
In [1]: a = t.randn(2, 3)
In [2]: a
Out[2]: tensor([[-0.1060, -0.2755, 1.2596],
[ 1.2688, 0.1467, -0.4233]])
In [3]: t.sort(a, dim=0)
Out[3]: (tensor([[-0.1060, -0.2755, -0.4233],
[ 1.2688, 0.1467, 1.2596]]),
tensor([[0, 0, 1],
[1, 1, 0]]))
In [4]: t.sort(a, dim=1)
Out[4]: (tensor([[-0.2755, -0.1060, 1.2596],
[-0.4233, 0.1467, 1.2688]]),
tensor([[1, 0, 2],
[2, 1, 0]]))
線性代數
1、矩陣轉置
對于一個2D的tensor A,A.t()得到其轉置,需要注意的是轉置後空間不再連續,需要調用.contiguous()方法令其連續
2、mm和bmm和matmul
(1) mm:兩個2D的tensor A和B相乘,其中A的形狀是(n, p),B的形狀是(p, m),輸出的形狀是(n, m)
(2) bmm:兩個3D的tensor A和B相乘,其中A的形狀是(batch_size, n, p),B的形狀是(batch_size, p, m),輸出的形狀是(batch_size , n, m),一個batch裡的每個矩陣對應相乘
(3) matmul:根據參與運算的兩個tensor A和B的形狀,有很多種情況,但記憶的時候可以分四類記:
①A和B都是1D的tensor(向量)時,傳回的是向量的内積;
②A和B分别是2D的n*p和p*m的矩陣時,傳回的矩陣乘法的結果(n*m的矩陣);
③A和B分别是3D的batch_size*n*p和batch_size*p*m的矩陣時,傳回的是每個batch中對應矩陣相乘的結果(batch_size*n*m的矩陣)
④其餘情況均是A和B維數不等的情況,此時根據廣播法則來确定計算方法
廣播機制
1、廣播機制在pytorch和numpy中均是存在的,廣播機制指的是在下面的兩個條件成立時,會自動擴充一個向量進行運算:
(1)兩個tensor之間進行的操作是一個逐元素操作(當然,大于小于這類比較也算)
(2)兩個tensor的次元中尾部的幾個次元是一樣的,如果tensor A的形狀是(Adim1, Adim2, ..., AdimN),tensor B的形狀是(Bdim1, Bdim2, ..., BdimM),次元中尾部的幾個次元一緻是指Adim(N-M+1)=Bdim1, Adim(N-M+2)=Bdim2, ..., AdimN=BdimM
In [1]: a = t.arange(24).view(2, 3, 4)
In [2]: a
Out[2]: tensor([[[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]],
[[12, 13, 14, 15],
[16, 17, 18, 19],
[20, 21, 22, 23]]])
In [3]: b = t.ones(4, dtype=t.int64)
In [4]: a + b
Out[4]: tensor([[[ 1, 2, 3, 4],
[ 5, 6, 7, 8],
[ 9, 10, 11, 12]],
[[13, 14, 15, 16],
[17, 18, 19, 20],
[21, 22, 23, 24]]])
2、但是,在pytorch中,許多非逐元素的操作也是支援廣播機制的,比如上面提到的matmul操作:對于兩個參與運算的tensor A和B,如果他們的維數不相等(情況④),那麼首先在維數較短的tensor(假設是B)前面補1使得維數一緻,然後把B擴充為之前的幾份使得補1位置的形狀與tensor A一緻,最後如果A和B符合的①②③中的任一條,那麼就可以進行運算
In [1]: a = t.randn(3, 4)
In [2]: b = t.randn(4)
In [3]: t.matmul(a, b).size()
Out[3]: torch.Size([3])
In [4]: a = t.randn(10, 3, 4)
In [5]: b = t.randn(4, 5)
In [6]: t.matmul(a, b)
Out[6]: torch.Size([10, 3, 5])
In [7]: a = t.randn(2, 10, 3, 4)
In [8]: b = t.randn(4, 5)
In [9]: t.matmul(a, b)
Out[9]: torch.Size([2, 10, 3, 5])
tensor和numpy對象轉換
1、如果a是numpy對象,可以用b = t.Tensor(a)或b = t.tensor(a)得到tensor,注意前者ab共享記憶體,後者是拷貝得到不共享記憶體
2、如果a是tensor對象,可以用b = a.numpy()得到numpy對象,此時ab共享記憶體