1 einsum简介

使用爱因斯坦求和约定,可以以简单的方式表示许多常见的多维线性代数数组运算。

给定两个矩阵A和B,我们想对它们做一些操作,比如 multiply、sum或者transpose等。虽然numpy里面有可以直接使用的接口,能够实现这些功能,但是使用enisum可以做的更快、更节省空间。

举例说明,我们现在有两个矩阵A和B。我们想计算A和B的哈达玛乘积(即逐元素乘积),然后按行求和。

import numpy as np
A = np.array([0, 1, 2])
B = np.array([[ 0,  1,  2,  3],[ 4,  5,  6,  7],[ 8,  9, 10, 11]])

如果我们不适用einsum的话,也不是不能计算,就是需要写几步来完成:

A.reshape(-1,1)
'''
array([[0],[1],[2]])
'''A.reshape(-1,1)*B
'''
array([[ 0,  0,  0,  0],[ 4,  5,  6,  7],[16, 18, 20, 22]])
'''(A.reshape(-1,1)*B).sum(axis=1)
#array([ 0, 22, 76])

如果我们使用einsum的话,一行就可以实现:

np.einsum('i,ij->i', A, B)
#array([ 0, 22, 76])

2 einsum原理

使用einsum的关键是,正确地labelling(标记)输入数组和输出数组的axes(轴)。

我们可以使用字符串(比如:ijk,这种表示方式更常用)或者一个整数列表(比如:[0,1])来标记axes。

比如,为了实现矩阵乘法,我们可以用einsum这么写(至于为什么这个是矩阵乘法,我们在后面会说明)

np.einsum('ij,jk->ik', A, B)

字符串'ij,jk->ik'可以根据'->'的位置来切分,左边的部分('ij,jk')标记了输入的axes,右边的('ik')标记了输出的axes。

输入标记又根据','的位置进行切分,'ij'标记了第一个输入A的axes,'jk'标记了第二个输入B的axes。

'ij'、'jk'的字符长度都是2,对应着A和B为2D数组,'ik'的长度也为2,因此输出也是2D数组。

给定输入

A = np.array([[1, 3, 5],[7, 9, -7],[-5, -3, -1]])
B = np.array([[0, 2,4],[6, 8, 6],[4, 2, 0]])

np.einsum('ij,jk->ik', A, B)可以看作是:

  • 在输入数组的标记之间,重复字母表示沿这些轴的值将相乘,这些乘积构成输出数组的值。比如图中沿着j轴做乘积。
  • 从输出标记中省略的字母表示沿该轴的值将被求和。比如图中的输出没有包含j轴,因此沿着j轴求和得到了输出数组中的每一项。
    • 如果输出的标记是'ijk',那么会得到一个 3x3x3 的矩阵。 

    A = np.array([[1, 3, 5],[7, 9, -7],[-5, -3, -1]])
    B = np.array([[0, 2,4],[6, 8, 6],[4, 2, 0]])
    np.einsum('ij,jk->ijk', A, B)'''
    array([[[  0,   2,   4],[ 18,  24,  18],[ 20,  10,   0]],[[  0,  14,  28],[ 54,  72,  54],[-28, -14,   0]],[[  0, -10, -20],[-18, -24, -18],[ -4,  -2,   0]]])
    '''

 输出标记是'ik'的时候,并不会创建中间的 3x3x3 的矩阵,而是直接将总和累加到2D数组中。

A = np.array([[1, 3, 5],[7, 9, -7],[-5, -3, -1]])
B = np.array([[0, 2,4],[6, 8, 6],[4, 2, 0]])
np.einsum('ij,jk->ik', A, B)
'''
array([[ 38,  36,  22],[ 26,  72,  82],[-22, -36, -38]])
'''

如果输出的标记是空,那么输出整个矩阵的和

A = np.array([[1, 3, 5],[7, 9, -7],[-5, -3, -1]])
B = np.array([[0, 2,4],[6, 8, 6],[4, 2, 0]])
np.einsum('ij,jk->', A, B)
#180

我们可以按任意顺序排序不求和的轴。

A = np.array([[1, 3, 5],[7, 9, -7],[-5, -3, -1]])
B = np.array([[0, 2,4],[6, 8, 6],[4, 2, 0]])
np.einsum('ij,jk->kji', A, B)'''
array([[[  0,   0,   0],[ 18,  54, -18],[ 20, -28,  -4]],[[  2,  14, -10],[ 24,  72, -24],[ 10, -14,  -2]],[[  4,  28, -20],[ 18,  54, -18],[  0,   0,   0]]])
'''

3 einsum分析

3.1 'ij,jk->ijk'  与 'ij,jk->kji'

我们一个一个分析一下

A = np.array([[1, 3, 5],[7, 9, -7],[-5, -3, -1]])
B = np.array([[0, 2,4],[6, 8, 6],[4, 2, 0]])
np.einsum('ij,jk->ijk', A, B)'''
array([[[  0,   2,   4],[ 18,  24,  18],[ 20,  10,   0]],[[  0,  14,  28],[ 54,  72,  54],[-28, -14,   0]],[[  0, -10, -20],[-18, -24, -18],[ -4,  -2,   0]]])
'''

首先,这几个数字是怎么得到的?

0=1*0 2=1*2 4=1*4
18=3*6 24=3*8 18=3*6
20=5*4 10=5*2 0=5*0
0=7*0 14=7*2 28=7*4
54=9*6 72=9*8 54=9*6
-28=-7*4 -14=-7*2 0=-7*0
0=-5*0 -10=-5*2 -20=-5*4
-18=-3*6 -24=-3*8 -18=-3*6
-4=-1*4 -2=-1*2 0=-1*0

转换成坐标,有:

[0,0]*[0,0] [0,0]*[0,1] [0,0]*[0,2]
[0,1]*[1,0] [0,1]*[1,1] [0,1]*[1,2]
[0,2]*[2,0] [0,2]*[2,1] [0,2]*[2,2]
[1,0]*[0,0]

[1,0]*[0,2]

[1,0]*[0,4]
[1,1]*[1,0] [1,1]*[1,1] [1,1]*[1,2]
[1,2]*[2,0] [1,2]*[2,1] [1,2]*[2,2]
[2,0]*[0,0] [2,0]*[0,1] [2,0]*[0,2]
[2,1]*[1,0] [2,1]*[1,1] [2,1]*[1,2]
[2,2]*[2,0] [2,2]*[2,1] [2,2]*[2,2]
A = np.array([[1, 3, 5],[7, 9, -7],[-5, -3, -1]])
B = np.array([[0, 2,4],[6, 8, 6],[4, 2, 0]])
np.einsum('ij,jk->kji', A, B)'''
array([[[  0,   0,   0],[ 18,  54, -18],[ 20, -28,  -4]],[[  2,  14, -10],[ 24,  72, -24],[ 10, -14,  -2]],[[  4,  28, -20],[ 18,  54, -18],[  0,   0,   0]]])
'''

与上面类似,我们就看第一个3*3的矩阵吧

0=1*0 0=7*0 0=-5*0
18=3*6 54=9*6 -18=-3*6
20=5*4 -28=-7*4 -4=-1*4
[0,0]*[0,0] [1,0]*[0,0] [2,0]*[0,0]
[0,1]*[1,0] [1,1]*[1,0] [2,1]*[1,0]
[0.2]*[2,0] [1,2]*[2,0] [2,2]*[2,0]

可以这么考虑 对于 结果矩阵(比如ijk),第【i,j,k】元素的结果等于【i,j】乘以【j,k】

3.2 'ij,jk->ik'

A = np.array([[1, 3, 5],[7, 9, -7],[-5, -3, -1]])
B = np.array([[0, 2,4],[6, 8, 6],[4, 2, 0]])
np.einsum('ij,jk->ik', A, B)
'''
array([[ 38,  36,  22],[ 26,  72,  82],[-22, -36, -38]])
'''

我们前面 'ij,jk->ijk'的结果是

0=1*0 2=1*2 4=1*4
18=3*6 24=3*8 18=3*6
20=5*4 10=5*2 0=5*0
0=7*0 14=7*2 28=7*4
54=9*6 72=9*8 54=9*6
-28=-7*4 -14=-7*2 0=-7*0
0=-5*0 -10=-5*2 -20=-5*4
-18=-3*6 -24=-3*8 -18=-3*6
-4=-1*4 -2=-1*2 0=-1*0

这边相当于

38=0+18+20 36=2+24+10 22=4+18
26=54-28 72=14+72-14 82=54+28
-22=-18-4 -36=-10-24-2 -38=-20-18

可以这么考虑 对于 结果矩阵(比如ik),第【i,k】元素的结果等于:对所有的j,【i,j】乘以【j,k】的结果的和

4 常用的Einsum

4.1 向量篇

('i',A)

向量A的一个视图

可以看成'i->i',即结果的第i位,是A的第i位

('i->', A)

sum(A)

可以看成'i->0',即结果的第0位,是A的第i位的和

('i,i->i', A,B)

向量A,B对应位置相乘

'i,i->i':结果的第i位,是A和B的第i位的积

('i,i->', A,B)

向量A,B的内积

'i,i->':可以看成'i,i->0' 结果的第0位,是A和B的第i位的积 再求和

('i,j->ij', A,B)

向量A,B的外积

'i,j->ij':结果的第i行第j列,是A的第i个元素和B的第j个元素的乘积

4.2 矩阵篇

('ij', A)

返回矩阵A

看成'ij->ij' ,结果的第i行第j列,是A的第i行第j列

('ji->ij', A)

返回矩阵A的转置

结果的第i行第j列的元素是A的第j行第i列的元素

('ii->i', A)

矩阵A的对角线元素

结果的第i个元素是A的第i行第i列的元素

('ij->', A)

矩阵A的元素之和

可以看成'ij->0' 结果的第0位是A的第i行第j列的元素,再求和

('ij->j', A)

A纵向求和

结果的第j个元素是,对所有的i,A的第(i,j)个元素的和

('ij->i', A)

A横向求和

结果的第i个元素是,对所有的j,A的第(i,j)个元素的和

('ij,ij->ij', A,B)

矩阵A,B相应位置的乘积

结果第i,j个元素,是A的第(i,j)个元素和B的第(i,j)个元素的乘积

('ij,ji->ij', A,B)

矩阵A和  矩阵B的转置   相应位置的乘积

结果第i,j个元素,是A的第(i,j)个元素和B的第(j,i)个元素的乘积

('ij,jk->ik', A,B)

A,B的矩阵乘积

结果的第(i,k)个元素等于A的第(i,j)个元素乘以B的第(j,k)个元素

('ij,kj->ik', A,B)

矩阵A和矩阵B的内积

('ij,kl->jikl', A,B)

A的每个元素乘以矩阵B

结果的 第(j,i,k,l)个元素是A的(i,j)和B的(k,l)的乘积

('dn,nd->',A,B)

相当于tr(AB)

5 显示表明和隐式表明

我们将指定'->'和输出标记称为 explicit mode。

如果不指定'->'和输出标记,numpy会将输入标记中只出现一次的标记按照字母表顺序,作为输出标记(也就是 implicit mode)。

'ij,jk->ik' 等价于 'ij,jk'

参考文章:einsum初探 - 知乎 (zhihu.com)

python 笔记:爱因斯坦求和 einsum相关推荐

  1. 对于论文中Pytorch代码爱因斯坦求和einsum的理解

    论文代码中经常会用到torch.einsum(),其本质是对相同下标求和 举例 torch.einsum('nctv, vtq -> ncqv', (x,y)) ' -> '左边是两个或多 ...

  2. einsum爱因斯坦求和(numpy)

    0. 爱因斯坦求和约定(Einstein Notation) 在数学中,爱因斯坦求和约定是一种标记法,也称为Einstein Summation Convention,在处理关于坐标的方程式时十分有效 ...

  3. 爱因斯坦求和约定在Python扩展库Numpy中的实现

    推荐教材: <Python数据分析.挖掘与可视化>(慕课版)(ISBN:978-7-115-52361-7),董付国,人民邮电出版社,定价49.8元,2020年1月出版,2021年12月第 ...

  4. np.einsum(爱因斯坦求和约定)

    欢迎关注我的微信公号:小张Python einsum 全称 Einstein summation convention(爱因斯坦求和约定),用简单的方式来代表多维数组运算: 矩阵求各元素之和 A=∑i ...

  5. TensorRT - 喜大普奔,TensorRT8.2 EA起开始支持Einsum爱因斯坦求和算子

    1 TensorRT 8.2 EA版本支持爱因斯坦求和算子Einsum NVIDIA在2021年10月6日发布的TensorRT新版本 8.2 Early Access版本终于开始支持爱因斯坦求和算子 ...

  6. 【深度学习】爱因斯坦求和约定(einsum)

    import tensorflow as tf print(tf.__version__) 2.0.0 一.爱因斯坦求和约定(einsum)的介绍 爱因斯坦求和约定是一种对复杂张量运算的优雅表达方式. ...

  7. tf.einsum—爱因斯坦求和约定

    1. einsum记法 如果你像我一样,发现记住PyTorch/TensorFlow中那些计算点积.外积.转置.矩阵-向量乘法.矩阵-矩阵乘法的函数名字和签名很费劲,那么einsum记法就是我们的救星 ...

  8. einsum方法详解(爱因斯坦求和)

    einsum方法详解(爱因斯坦求和) einsum是pytorch.numpy中一个十分优雅的方法,如果利用得当,可完全代替所有其他的矩阵计算方法,不过这需要一定的学习成本.本文旨在详细解读einsu ...

  9. 一文掌握爱因斯坦求和约定 einsum

    爱因斯坦跟 NumPy 有关系吗?没有,但他提出了一个针对数学公式的符号简化办法,即爱因斯坦求和约定(Einstein Summation Convention)或者叫爱因斯坦标记法(Einstein ...

最新文章

  1. HarmonyOS Text超出部分末尾显示...
  2. 我早年在Google学到的10条经验
  3. 谈谈NTFS数据流文件
  4. IIS短文件/文件夹泄露漏洞
  5. VS2019编译 当前最新版chromium
  6. 线性表:链栈算法实现
  7. 数据库自增主键用完了怎么办
  8. 防盗链与token运用
  9. Python+Opencv图像处理新手入门教程(三):阈值与二值化
  10. hbm配置文件 hibernate中
  11. 爬虫实例十 爬取百度贴吧小姐姐照片
  12. sqlserver2005身份验证
  13. 【前端小技能】Vue集成百度离线地图
  14. chrome浏览器安装油猴插件全过程
  15. 远程桌面3389加固
  16. html 播放360全景图,讯网360全景展示产品-html5全景图-flash全景展示-360度全景图制作...
  17. Redis - 0、几款可视化工具
  18. docker镜像迁移mysql启动报错_README.md
  19. 微信小程序之图片压缩
  20. spring-boot-mvc启动流程

热门文章

  1. HTML5本地存储——Web SQL Database
  2. Away3D 的实体收集器Bug
  3. SQL Server 2012笔记分享-52:可用性指标
  4. Java:Comparable接口
  5. 编译detours注意
  6. Windows 性能问题诊断(5)
  7. 理解SQL Server中的锁
  8. ST-Link VCP Ctrl驱动安装失败解决(win7 64bits)
  9. PAT甲级1048 Find Coins :[C++题解]哈希表、两个硬币之和为定值
  10. Leetcode1689. 十-二进制数的最少数目[C++题解]:贪心、找规律简单题