本文含 10083 字,18 图表截屏
建议阅读 52 分钟
本文是 Python 系列的特别篇的第二十三篇
特别篇 11 - 异常处理
特别篇 23 - 爱因斯坦求和 einsum
0
引言
最近我以电子版的形式出了第二本书《Python 从入门到入迷》,然后定期更新书中的内容,最先想到的便是 einsum。
在 NumPy 包中,有一个函数叫做 einsum,它做的事情就是加总 (summation),但是是以爱因斯坦加总惯例 (Einstein's summation convention) 进行,因此得以此名。在深度学习框架 Tensorflow 和 PyTorch 也有这个函数,而且用法几乎一样,使用 einsum 首先需要从各自包中引用:
from numpy import einsum
from torch import einsum
from tensorflow import einsum
本文只拿 NumPy 包中的 einsum 来举例,并按照 what-how-why 主线来讲解,首先介绍什么 (what) 是 einsum,再展示怎么 (how) 用 einsum,最后来说明为什么 (why) 会有 einsum。相信这三部曲过后,我们可以把 einsum 整得明明白白的。
1
What is einsum?
1.1
爱因斯坦标记法
以下是一个矩阵相乘的具体例子,我们都知道结果矩阵第 2 行第 1 列的元素 4 是由“第一个矩阵第 2 行的元素”依次乘以“第二个矩阵第 1 列的元素”再加总,即 4 = 2*0 + 2*1 + 2*1。
矩阵相乘的通用形式如下,用字母代替数字得到 c21 = a21*b11 + a22*b21 + a23*b31。
写成通式就是
上式中的下指标可分成两类:
出现两次的指标被称作哑指标 (dummy index),比如 j
在单项式中只出现一次的指标被称作自由指标 (free index),比如 i 和 k
爱因斯坦对于式中出现的哑指标,约定默认对其进行求和。有了这个约定之后,上面表达式可简化成:
有了爱因斯坦约定得到的简写 (注意上面表达式的下标) ,用 einsum('ij,jk->ik',A,B)可以表达矩阵相乘,其中参数
'ij,jk->ik' 是表示在爱因斯坦约定下的矩阵相乘字符串,箭头 -> 把字符串分成两部分,左侧部分表示输入矩阵,'ij' 标记 A 以及 'jk' 标记 B;右侧部分 'ik' 标记输出矩阵 C
A 和 B 是用于相乘的两个矩阵
下面用代码来看几个例子。
1.2
代码展示
首先创建矩阵 A 和 B。
A = np.array([[1, 1, 1],
[2, 2, 2],
[5, 5, 5]])
B = np.array([[0, 1, 0],
[1, 1, 0],
[1, 1, 1]])
用 einsum 函数来求矩阵相乘。
einsum('ij,jk->ik', A, B)
array([[ 2, 3, 1],
[ 4, 6, 2],
[10, 15, 5]])
用 np.matmul(A,B) 验证上面的语法确实做的是矩阵相乘。
np.matmul( A, B )
array([[ 2, 3, 1],
[ 4, 6, 2],
[10, 15, 5]])
自由指标和哑指标用任何字母字符都可以的,只要哑指标的位置写对即可,比如:
einsum('bF,FG->bG', A, B)
array([[ 2, 3, 1],
[ 4, 6, 2],
[10, 15, 5]])
用 'ij,jk->ik' 只不过字母 i,j 和 k 在数学中的下标表示中更常见。
爱因斯坦求和容易吧,你觉得你会了么?觉得会的话来看看下面的各种组合
'ij,jk->ki' </