einsum:爱因斯坦求和约定
在Tensorflow、Numpy和PyTorch中都提供了使用einsum
的api,einsum是一种能够简洁表示点积、外积、转置、矩阵-向量乘法、矩阵-矩阵乘法等运算的领域特定语言。在Tensorflow等计算框架中使用einsum,操作矩阵运算时可以免于记忆和使用特定的函数,并且使得代码简洁,高效。
如对矩阵\(A\in \mathbb{R}^{I×K}\)和矩阵\(B\in \mathbb{R}^{K×J}\)做矩阵乘,然后对列求和,最终得到向量\(c\in \mathbb{R}^J\),即:
\]
使用爱因斯坦求和约定表示为:
\]
在Tensorflow、Numpy和PyTorch中对应的einsum字符串为:
ik,kj->j
在上面的字符串中,隐式地省略了重复的下标\(k\),表示在该维度矩阵乘;另外输出中未指明下标\(i\),表示在该维度累加。
Numpy、PyTorch和Tensorflow中的einsum
einsum在Numpy中的实现为np.einsum
,在PyTorch中的实现为torch.einsum
,在Tensorflow中的实现为tf.einsum
,均使用同样的函数签名einsum(equation,operands)
,其中,equation
传入爱因斯坦求和约定的字符串,而operands
则是张量序列。在Numpy、Tensorflow中是变长参数列表,而在PyTorch中是列表。上述例子中,在Tensorflow中可写作:
tf.einsum('ik,kj->j',mat1,mat2)
其中,mat1、mat2为执行该运算的两个张量。注意:这里的(i,j,k)
的命名是任意的,但在一个表达式中要一致。
PyTorch和Tensorflow像Numpy支持einsum的好处之一就是,einsum可以用于深度网络架构的任意计算图,并且可以反向传播。在Numpy和Tensorflow中的调用格式如下:
\]
其中,\(\square\)是占位符,表示张量维度;arg1,arg3
是矩阵,arg2
是三阶张量,运算结果是矩阵。注意:einsum处理可变数量的输入。上面例子中,einsum制定了三个参数的操作,但同样可以操作一个参数、两个参数和三个参数及以上的操作。
典型的einsum表达式
前置知识
内积
又称点积、点乘,对应位置数字相乘,结果是一个标量,有见向量内积和矩阵内积等。
向量\(\vec a\)和向量\(\vec b\)的内积:
\[\vec a=[a_1,a_2,...,a_n]\\
\vec b=[b_1,b_2,...,b_n]\\
\vec a\cdot \vec b^T=a_1b_1+a_2b_2+...+a_nb_n
\]内积几何意义:
\[\vec a \cdot \vec b^T=|\vec a||\vec b|\mathop{cos}\theta
\]外积
又称叉乘、叉积、向量积,行向量矩阵乘列向量,结果是二阶张量。注意到:
张量的外积
作为张量积
的同义词。外积是一种特殊的克罗内克积。向量\(\vec a\)和向量\(\vec b\)的外积:
\[\begin{bmatrix}
b_1
\\b_2
\\ b_3
\\ b_4
\end{bmatrix}\bigotimes[a_1,a_2,a_3]=\begin{bmatrix}
a_1b_1 & a_2b_1 & a_3b_1 \\
a_1b_2 & a_2b_2 & a_3b_2 \\
a_1b_3 & a_2b_3 & a_3b_3 \\
a_1b_4 & a_2b_4 & a_3b_4 \\
\end{bmatrix}
\]外积的几何意义:
\[\vec a=(x_1,y_1,z_1)\\
\vec b=(x_2,y_2,z_2)\\
\vec a\bigotimes\vec b=\begin{vmatrix}
i & j & k\\
x_1 & y_1 & z_1\\
x_2 & y_2 & z_2
\end{vmatrix}=(y_1z_2-y_2z_1)\vec i-(x_1z_2-x_2z_1)\vec j+(x_1y_2-x_2y_1)\vec k
\]其中,
\[\vec i=(1,0,0)\\
\vec j=(0,1,0)\\
\vec k=(0,0,1)
\]
由于PyTorch可以实时输出运算结果,以PyTorch使用einsum表达式为例。
矩阵转置
\[B_{ji}=A_{ij}
\]a=torch.arange(6).reshape(2,3)
>>>tensor([[0, 1, 2],
[3, 4, 5]])
torch.einsum('ij->ji',[a])
>>>tensor([[0, 3],
[1, 4],
[2, 5]])
求和
\[b=\sum_{i}\sum_{j}A_{ij}
\]a=torch.arange(6).reshape(2,3)
>>>tensor([[0, 1, 2],
[3, 4, 5]])
torch.einsum('ij->',[a])
>>>tensor(15)
列求和(列维度不变,行维度消失)
\[b_j=\sum_iA_{ij}
\]a=torch.arange(6).reshape(2,3)
>>>tensor([[0, 1, 2],
[3, 4, 5]])
torch.einsum('ij->j',[a])
>>>tensor([ 3., 5., 7.])
列求和(列维度不变,行维度消失)
\[b_i=\sum_jA_{ij}
\]a=torch.arange(6).reshape(2,3)
>>>tensor([[0, 1, 2],
[3, 4, 5]])
torch.einsum('ij->i', [a])
>>>tensor([ 3., 12.])
矩阵-向量相乘
\[c_i=\sum_k A_{ik}b_k
\]a=torch.arange(6).reshape(2,3)
>>>tensor([[0, 1, 2],
[3, 4, 5]])
torch.einsum('ik,k->i',[a,b])
>>>tensor([ 5., 14.])
矩阵-矩阵乘法
\[C_{ij}=\sum_{k}A_{ik}B_{kj}
\]a=torch.arange(6).reshape(2,3)
b=torch.arange(15).reshape(3,5)
>>>tensor([[0, 1, 2],
[3, 4, 5]])
>>>tensor([[ 0, 1, 2, 3, 4],
[ 5, 6, 7, 8, 9],
[10, 11, 12, 13, 14]])
torch.einsum('ik,kj->ij',[a,b])
>>>tensor([[ 25, 28, 31, 34, 37],
[ 70, 82, 94, 106, 118]])
点积
向量
\[c=\sum_i a_i b_i
\]a=torch.arange(3)
b=torch.arange(3,6)
>>>tensor([0, 1, 2])
>>>tensor([3, 4, 5])
torch.einsum('i,i->',[a,b])
>>>tensor(14.)
矩阵
\[c=\sum_i\sum_j A_{ij}B_{ij}
\]a=torch.arange(6).reshape(2,3)
b=torch.arange(6,12).reshape(2,3)
>>>tensor([[0, 1, 2],
[3, 4, 5]])
>>>tensor([[ 6, 7, 8],
[ 9, 10, 11]])
torch.einsum('ij,ij->',[a,b])
>>>tensor(145.)
外积
\[C_{ij}=a_i b_j
\]a=torch.arange(3)
b=torch.arange(3,7)
>>>tensor([0, 1, 2])
>>>tensor([3, 4, 5, 6])
torch.einsum('i,j->ij',[a,b])
>>>tensor([[ 0., 0., 0., 0.],
[ 3., 4., 5., 6.],
[ 6., 8., 10., 12.]])
batch矩阵乘
\[C_{ijl}=\sum_{k}A_{ijk}B_{ikl}
\]a=torch.randn(3,2,5)
b=torch.randn(3,5,3)
>>>tensor([[[-1.4131e+00, 3.8372e-02, 1.2436e+00, 5.4757e-01, 2.9478e-01],
[ 1.3314e+00, 4.4003e-01, 2.3410e-01, -5.3948e-01, -9.9714e-01]],
[[-4.6552e-01, 5.4318e-01, 2.1284e+00, 9.5029e-01, -8.2193e-01],
[ 7.0617e-01, 9.8252e-01, -1.4406e+00, 1.0071e+00, 5.9477e-01]],
[[-1.0482e+00, 4.7110e-02, 1.0014e+00, -6.0593e-01, -3.2076e-01],
[ 6.6210e-01, 3.7603e-01, 1.0198e+00, 4.6591e-01, -7.0637e-04]]])
>>>tensor([[[-2.1797e-01, 3.1329e-04, 4.3139e-01],
[-1.0621e+00, -6.0904e-01, -4.6225e-01],
[ 8.5050e-01, -5.8867e-01, 4.8824e-01],
[ 2.8561e-01, 2.6806e-01, 2.0534e+00],
[-5.5719e-01, -3.3391e-01, 8.4069e-03]],
[[ 5.2877e-01, 1.4361e+00, -6.4232e-01],
[ 1.0813e+00, 8.5241e-01, -1.1759e+00],
[ 4.9389e-01, -1.7523e-01, -9.5224e-01],
[-1.3484e+00, -5.4685e-01, 8.5539e-01],
[ 3.7036e-01, 3.4368e-01, -4.9617e-01]],
[[-2.1564e+00, 3.0861e-01, 3.4261e-01],
[-2.3679e+00, -2.5035e-01, 1.8104e-02],
[ 1.1075e+00, 7.2465e-01, -2.0981e-01],
[-6.5387e-01, -1.3914e-01, 1.5205e+00],
[-1.6561e+00, -3.5294e-01, 1.9589e+00]]])
torch.einsum('ijk,ikl->ijl',[a,b])
>>>tensor([[[ 1.3170, -0.7075, 1.1067],
[-0.1569, -0.2170, -0.6309]],
[[-0.1935, -1.3806, -1.1458],
[-0.4135, 1.7577, 0.3293]],
[[ 4.1854, 0.5879, -2.1180],
[-1.4922, 0.7846, 0.7267]]])
张量缩约
batch矩阵相乘是张量缩约的一个特例,比如有两个张量,一个n阶张量\(A\in \mathbb{R}^{I_1×l_2×...×I_n}\),一个m阶张量\(B\in \mathbb{R}^{J_1×J_2×...×J_m}\)。取n=4,m=5,假定维度\(I_2=J_3\)且\(I_3=J_5\),将这两个张量在这两个维度上(A张量的第2、3维度,B张量的第3、5维度)相乘,获得新张量\(C\in \mathbb{R}^{I_1×I_4×J_1×J_2×J_4}\),如下所示:
\[C_{I_1×I_4×J_1×J_2×J_4}=\sum_{I_2==J_3}\sum_{I_3==J_5}A_{I_1×I_2×I_3×I_4}B_{J_1×J_2×J_3×J_4×J_5}
\]a=torch.randn(2,3,5,7)
b=torch.randn(11,13,3,17,5)
torch.einsum('pqrs,tuqvr->pstuv', [a, b]).shape
>>>torch.Size([2, 7, 11, 13, 17])
多张量计算
如前所述,einsum可用于超过两个张量的计算,以
双线性变换
为例:\[D_ij=\sum_k\sum_lA_{ik}B_{jkl}C_{il}
\]a=torch.randn(2,3)
b=torch.randn(5,3,7)
c=torch.randn(2,7)
torch.einsum('ik,jkl,il->ij',[a,b,c]).shape
>>>torch.Size([2,5])
在kimiyoung/transformer-xl的tf部分大量使用了einsum表达式。
einsum:爱因斯坦求和约定的更多相关文章
- 爱因斯坦求和约定 (Einstein summation convention)
- MindSpore尝鲜之爱因斯坦求和
技术背景 在前面的博客中,我们介绍过关于numpy中的张量网络的一些应用,同时利用相关的张量网络操作,我们可以实现一些分子动力学模拟中的约束算法,如LINCS等.在最新的nightly版本的MindS ...
- einsum函数介绍-张量常用操作
einsum函数说明 pytorch文档说明:\(torch.einsum(equation, **operands)\) 使用基于爱因斯坦求和约定的符号,将输入operands的元素沿指定的维数求和 ...
- NumPy v1.15手册汉化
NumPy参考 数组创建 零 和 一 empty(shape[, dtype, order]):返回给定形状和类型的新数组,而不初始化条目 empty_like(prototype[, dtype, ...
- numpy函数查询手册
写了个程序,对Numpy的绝大部分函数及其说明进行了中文翻译. 原网址:https://docs.scipy.org/doc/numpy/reference/routines.html#routine ...
- NumPy之:ndarray中的函数
NumPy之:ndarray中的函数 目录 简介 简单函数 矢量化数组运算 条件逻辑表达式 统计方法 布尔数组 排序 文件 线性代数 随机数 简介 在NumPy中,多维数组除了基本的算数运算之外,还内 ...
- Differential Geometry之第四章标架与曲面论的基本定理
第四章.标架与曲面论的基本定理 1.活动标架 2.自然标架的运动方程 爱因斯坦求和约定(Einstein summation convention) 3.曲面的结构方程 4.曲面的存在唯一性定理 5. ...
- 记号(notation)的学习
数学的记号(notation) 记号具体代表什么含义,取决于你的定义: 比如这样的 d⃗ 一个向量,每个分量 d(i) 表示的是从初始结点 v 到当前节点 vi 的最短路径:也即这样的一个向量的每一 ...
- 如何基于MindSpore实现万亿级参数模型算法?
摘要:近来,增大模型规模成为了提升模型性能的主要手段.特别是NLP领域的自监督预训练语言模型,规模越来越大,从GPT3的1750亿参数,到Switch Transformer的16000亿参数,又是一 ...
随机推荐
- 【t064】最勇敢的机器人
Time Limit: 1 second Memory Limit: 128 MB [问题描述] [背景] Wind设计了很多机器人.但是它们都认为自己是最强的,于是,一场比赛开始了~ [问题描述] ...
- 手动安装MySQL8.0
首先跟大家唠一唠家常,随着MySQL迅速的更新,MySQL突飞猛进已经更新到了8.0版本,那么它和我们之前用的5.X版本有什么明显的区别那? 首先给大家看下MySQL5.X自带表的查询速度 之后献上M ...
- C# 静态构造函数,静态变量执行顺序(精华版)(规正版)
一.成员初始化整体顺序 1.成员赋值初始化先于构造函数: 2.成员赋值初始先从子类再到基类: 3.构造函数初始化先从基类再到子类: 4.静态成员初始化优先于实例成员初始化: 二.对类型静态成员构造的大 ...
- C# await 高级用法
原文:C# await 高级用法 本文告诉大家 await 的高级用法,包括底层原理. 昨天看到太子写了一段代码,我开始觉得他修改了编译器,要不然下面的代码怎么可以编译通过 await "林 ...
- C++调用IDL程序的做法(二)
作者:朱金灿 来源:http://blog.csdn.net/clever101 上次提到使用IDLDrawWidget Control 3.0来调用IDL程序,但是我们还有一些问题没有解决,比如C+ ...
- R 语言基本操作(基本信息的查看、与本地文件系统交互、编译器版本升级)
0. 信息及路径的查看 getwd() ⇒ 查看路径: setwd() ⇒ 设置路径: .libPaths() ⇒ R 语言库所在的位置: sessionInfo() ⇒ 查询版本及系统和库等信息: ...
- C++中的模板编程
一,函数模板 1.函数模板的概念 C++中提供了函数模板,所谓函数模板,实际上是建立一个通用函数,其函数的返回值类型和函数的参数类型不具体指定,用一个虚拟的类型来表示.这个通用函数就被称为函数的模板. ...
- .net 程序员 两年工作总结
2013 年7月毕业,算一算从开始在现任的公司实习到现在已经有小两年的时间了.公司的工作虽然不忙,但也一直没有时间思考一下. 现在决定辞职了,忽然一下轻松的让人想思考. 普通程序员.普通本科生.普通的 ...
- 微信公众平台通用接口API指南
微信公众平台 通用接口 消息接口 开发模式 作者:方倍工作室原文:http://www.doucube.com/index.php?m=Article&a=show&id=5 微信公众 ...
- 获取当前电脑的cpu使用率、内存使用率
https://www.cnblogs.com/Chary/p/7771365.html http://www.cnblogs.com/zl1991/p/4679461.html 要关注几个类 Per ...