写过一篇,numpy中dot()、outer()、multiply()以及matmul()的区别,介绍了numpy中的点积、外积、转置、矩阵-向量乘法、矩阵-矩阵乘法等计算方法。
在Pytorch中这些矩阵运算是很常见的,几乎存在于每个模型中。那么在Pytorch中如何实现的呢?同时面对这么多不同的计算方法,有没有一种统一的方法呢?接下来就详细介绍
一、概述
-
点乘(内积) :
torch.dot
计算两个张量的点乘(内乘),两个张量都为1-D 向量。 -
矩阵乘法:
torch.mm与torch.bmm
前者实现矩阵乘法,后者实现批量矩阵乘法。 -
集大成者:
torch.einsum
爱因斯坦求和约定(einsum)这个方法可以实现绝大多数的矩阵操作,包括点积、外积、转置、矩阵-向量乘法、矩阵-矩阵乘法。一旦理解并能利用einsum,除了不用记忆和频繁查找特定库函数这个好处以外,你还能够更迅速地编写更加紧凑、高效的代码。
二、点积: torch.dot
numpy中的np.dot可以实现很多矩阵运算,包括点积、外积、矩阵乘法等;torch.dot 只能实现张量的点积,张量都需要是一维向量。
>>> a = torch.arange(6)
>>> b = torch.arange(6)
>>> torch.dot(a,b)
tensor(55)
>>> torch.einsum('i,i->', [a, b])
tensor(55)
三、torch.mm 与 torch.bmm
这一组应该是在编写模型的时候用到的比较频繁的。
前者实现矩阵相乘,后者实现批量矩阵相乘。
>>> a = torch.randn(3,4)
>>> b = torch.randn(4,5)
>>> torch.mm(a,b)
tensor([[ 0.6086, -1.5173, 1.2769, -0.3830, 2.7696],
[ 0.0039, 0.2353, -2.7920, 1.1500, 0.0146],
[ 0.1890, 0.3608, 1.4639, -0.8955, 0.3424]])
>>> torch.einsum('ij,jk->ik',a,b)
tensor([[ 0.6086, -1.5173, 1.2769, -0.3830, 2.7696],
[ 0.0039, 0.2353, -2.7920, 1.1500, 0.0146],
[ 0.1890, 0.3608, 1.4639, -0.8955, 0.3424]])
以下带批量的矩阵乘法无法使用torch.mm
>>> a = torch.randn(10,3,4)
>>> b = torch.randn(10,4,5)
>>> c1 = torch.bmm(a,b)
>>> c2 = torch.einsum('bij,bjk->bik',a,b)
>>> c1.equal(c2)
True
四、爱因斯坦求和约定: torch.einsum
如果只是以上集中矩阵操作我们还能够理清什么时候该用什么。不过矩阵运算还有点积、外积、转置、矩阵-向量乘法、矩阵-矩阵乘法等很多,同时当张量的维度上升到四维及以上,情况就变得复杂了。此时 einsum
求和方法让这一切变得优雅。
einsum记法是一个表达以上这些运算,包括复杂张量运算在内的优雅方式,基本上,可以把einsum看成一种领域特定语言。一旦你理解并能利用einsum,除了不用记忆和频繁查找特定库函数这个好处以外,你还能够更迅速地编写更加紧凑、高效的代码。而不使用einsum的时候,容易出现引入不必要的张量变形或转置运算,以及可以省略的中间张量的现象。此外,einsum这样的领域特定语言有时可以编译到高性能代码,事实上,PyTorch最近引入的能够自动生成GPU代码并为特定输入尺寸自动调整代码的张量理解(Tensor Comprehensions)就基于类似einsum的领域特定语言。此外,可以使用opt einsum和tf einsum opt这样的项目优化einsum表达式的构造顺序。
以下是一些einsum可以实现的功能,我们需要基于实际需要灵活运用。
(1)转置
(2)求和
(3)列求和
(4)行求和
(5)矩阵-向量相乘
(6)矩阵-矩阵相乘
(7)点积
(8)哈达玛积
(9)外积
(10)batch矩阵相乘
(11)张量缩约
(12)双线性变换
有些人会问为什么没做代码示例呢。因为在einsum满足你一切需要:深度学习中的爱因斯坦求和约定 中,他对einsum介绍的特别详细,第一次我也是从这里学到的。所以就直接推荐大家去这里学习。他还提供了一些案例。
References:
1、einsum满足你一切需要:深度学习中的爱因斯坦求和约定
网友评论