np.einsum(愛因斯坦求和約定)


einsum 全稱 Einstein summation convention(愛因斯坦求和約定),用簡單的方式來代表多維數組運算;

矩陣求各元素之和

\[A = \sum_{i=0}^n\sum_{j=0}^na_ia_j \]

如果用 einsum函數可表示為

A = np.einsum('i',a)

多矩陣相乘

\[C_{ik} = \sum_{j=1}^nA_{ij}B_{jk} \]

表示為

C_i = np.einsum('ij,jk',a,b)

求矩陣的跡

\[trace = \sum_{i=1}^nA_{ii} \]

用 einsum函數可表示為

trace = np.einsum('ii',a)

對於數組間運算例如矩陣乘積和、沿某一軸點積和;enisum 為多維數組運算提供另外一種表示方式,指定的下標標簽列表,用逗號隔開;enisum 函數在 numpy ,Pytorch,Tensorflow 都有實現,使用方式如下

np.einsum(subscripts:str,operands:list of array_like)

函數中參數 subscripts 為字符串類型,表示運算命令,例如 "ii","ij,jk" 參數 operands 代表需要計算的數組或數組列表;

enisum 中 subscrpts 參數的字符串形式有兩種方式:

1,implicit(隱式模式) 不包含-> 標識符和輸出標簽;輸出數組會根據選擇的下標順序進行排序,例如 np.einsum('ij',a) 得到的二維數組無變化,但 np.einsum('ji',a) 需要對輸出數組進行轉置 (i ,j 軸互換)操作

2,explicit(顯式模式) 包含 標識符->及輸出標簽,能夠增加函數的靈活性,例如調用 np.einsum('i->',a) 效果類似於 np.sum(a,axis = -1) ;而 np.einsum('ii->i',a) 等同於 np.diag(a) ;另外在顯式模式中,會直接指定輸出數組下標順序,例如 np.einsum('ij,jh->ih',a,b) 表示矩陣相乘;目前下面運算都可用 enisum 函數表示;

  • 求一個數組的跡,numpy.trace;
  • 返回對角線,numpy.diag
  • 對數組沿某個軸求和,numpy.sum
  • 數組轉置,numpy.transpose
  • 矩陣相乘或點積,numpy.matmul,numpy.dot
  • 向量內積外積,numpy.inner,numpy.outer
  • 廣播,逐像素相乘,或標量相乘,numpy.multiply
  • 張量收縮,numpy.tensorbot

關於 np.enisum() 函數舉幾個栗子:

>>> a = np.arange(25).reshape(5,5)
>>> b = np.arange(5)
>>> c = np.arange(6).reshape(2,3)

1,計算矩陣的跡

>>> np.einsum('ii...->...i',a)
array([-0.796318  ,  0.08363816, -0.79171551,  0.36235911])
>>> np.einsum('i...i',a)
-1.1420362461348776
>>> a
array([[-0.796318  ,  1.54759498, -0.744291  ,  0.02107445],
       [ 0.03826498,  0.08363816,  0.92709203,  0.04769788],
       [ 0.39088153, -0.85566069, -0.79171551, -1.50750047],
       [-1.16165527,  0.77327936,  0.44133708,  0.36235911]])
>>> np.trace(a)
-1.1420362461348776

2,矩陣相乘

>>> b =np.random.rand(4,5)
>>> np.einsum('ij...,jk...->ik...',a,b)
array([[-0.207485  ,  0.37929742, -1.14191507, -0.30398675, -0.59431733],
       [ 0.03389816,  0.10101184,  0.78917293,  0.41502013,  0.37634113],
       [-0.3499671 , -0.45043889, -1.67700784, -0.68798725, -0.49966522],
       [-0.2186732 , -0.30529867, -0.21863002,  0.02300085, -0.50540297]])
>>> np.matmul(a,b)
array([[-0.207485  ,  0.37929742, -1.14191507, -0.30398675, -0.59431733],
       [ 0.03389816,  0.10101184,  0.78917293,  0.41502013,  0.37634113],
       [-0.3499671 , -0.45043889, -1.67700784, -0.68798725, -0.49966522],
       [-0.2186732 , -0.30529867, -0.21863002,  0.02300085, -0.50540297]])

3,對角線 diag

>>> np.einsum('ii->i',a)
array([ 0,  6, 12, 18, 24])
>>> np.diag(a)
array([ 0,  6, 12, 18, 24])

4,沿數組某一軸求和(需要在 explicit 模式下運行)

>>> # 沿着某一軸求和,顯示模式運行
>>> np.einsum('ij->i',a)
array([ 10,  35,  60,  85, 110])
d
>>> np.sum(a,axis = 1)
array([ 10,  35,  60,  85, 110])

5,數組轉置,改變軸順序

>>> # 計算數組轉置,對某些軸重新排序
>>> c
array([[0, 1, 2],
       [3, 4, 5]])
>>> np.einsum('ji',c)
array([[0, 3],
       [1, 4],
       [2, 5]])
>>> np.transpose(c)
array([[0, 3],
       [1, 4],
       [2, 5]])

6,inner(計算內積)

>>> np.einsum('i,i',b,b)
30
>>> b
array([0, 1, 2, 3, 4])
>>> np.inner(b,b)# 一維數組,逐像素乘積和
30

7,矩陣點乘

>>> np.einsum('ij,j',a,b)
array([ 30,  80, 130, 180, 230])
>>> np.dot(a,b)
array([ 30,  80, 130, 180, 230])
>>> a
array([[ 0,  1,  2,  3,  4],
       [ 5,  6,  7,  8,  9],
       [10, 11, 12, 13, 14],
       [15, 16, 17, 18, 19],
       [20, 21, 22, 23, 24]])
>>> b
array([0, 1, 2, 3, 4])
>>> np.dot(a[-1],b)# Test
230

8,張量數組相乘

>>> np.einsum(',ij',3,c)
array([[ 0,  3,  6],
       [ 9, 12, 15]])
>>> np.multiply(3,c)
array([[ 0,  3,  6],
       [ 9, 12, 15]])
>>> # 向量外積

8,廣播機制,outer

>>> b
array([0, 1, 2, 3, 4])
>>> np.einsum('i,j',np.arange(2)+1,b)
array([[0, 1, 2, 3, 4],
       [0, 2, 4, 6, 8]])
>>> np.outer(np.arange(2)+1,b)
array([[0, 1, 2, 3, 4],
       [0, 2, 4, 6, 8]])

9,Tensor contraction,數組收縮沿着某一軸相乘

>>> a = np.arange(60).reshape(3,4,5)
>>> b = np.arange(24).reshape(4,3,2)
>>> np.einsum('ijk,jil->kl',a,b)
array([[4400, 4730],
       [4532, 4874],
       [4664, 5018],
       [4796, 5162],
       [4928, 5306]])
>>> np.tensordot(a,b,axes = [[1,0],[0,1]])
array([[4400, 4730],
       [4532, 4874],
       [4664, 5018],
       [4796, 5162],
       [4928, 5306]])

:在 numpy 版本 1.12.0 之后,einsum 加入了 optimize 參數,用來優化 contraction 操作,對於 contraction 運算部分,操作的數組包含三個或三個以上,optimize 參數設置能提高計算效率,減小內存占比;


Reference:

1,https://zhuanlan.zhihu.com/p/71639781

2,https://numpy.org/doc/stable/reference/generated/numpy.einsum.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM