注:本文是笔者结合自己阅读和使用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])
1、如果a是numpy对象,可以用b = t.Tensor(a)或b = t.tensor(a)得到tensor,注意前者ab共享内存,后者是拷贝得到不共享内存
2、如果a是tensor对象,可以用b = a.numpy()得到numpy对象,此时ab共享内存