2016-03-04 149 views
6

我有一個由維數爲(n,k)的n個矩陣和維數爲(k,n)的矩陣V組成的張量U.與numpy tensordot的張量相乘

我想乘它們,使得結果返回尺寸(d,n)的,其中第j列是U的矩陣j和V.

的列j之間的矩陣乘法的結果的矩陣

enter image description here

一種可能的方式來獲得,這是:

for j in range(n): 
    res[:,j] = U[:,:,j] * V[:,j] 

我想知道是否有使用numpy庫更快的方法。特別是我正在考慮np.tensordot()函數。

這個小片段使我可以用一個標量乘以一個單一的矩陣,但是明顯泛化到一個矢量並沒有返回我所希望的。

a = np.array(range(1, 17)) 
a.shape = (4,4) 
b = np.array((1,2,3,4,5,6,7)) 
r1 = np.tensordot(b,a, axes=0) 

有什麼建議嗎?

+0

你用什麼軟件來繪製你的作品? – hlin117

+1

@ hlin117 - 我使用了主題演講。 – Matteo

回答

6

有幾種方法可以做到這一點。 ,想到的第一件事是np.einsum

# some fake data 
gen = np.random.RandomState(0) 
ni, nj, nk = 10, 20, 100 
U = gen.randn(ni, nj, nk) 
V = gen.randn(nj, nk) 

res1 = np.zeros((ni, nk)) 
for k in range(nk): 
    res1[:,k] = U[:,:,k].dot(V[:,k]) 

res2 = np.einsum('ijk,jk->ik', U, V) 

print(np.allclose(res1, res2)) 
# True 

np.einsum使用Einstein notation表示張量收縮。在上述表達式'ijk,jk->ik'中,i,jk是對應於UV的不同維度的下標。每個逗號分隔的分組對應於傳遞到np.einsum的其中一個操作數(在這種情況下,U的尺寸爲ijkV的尺寸爲jk)。 '->ik'部分指定輸出數組的尺寸。任何不包含在輸出字符串中的下標的尺寸都會加在一起。

np.einsum對於執行復雜的張量收縮非常有用,但它可能需要一段時間才能完全掌握它的工作原理。您應該查看文檔中的示例(上面鏈接)。


一些其他選項:與broadcasting

  1. 逐元素乘法,然後求和:

    res3 = (U * V[None, ...]).sum(1) 
    
  2. inner1d與轉置的負載:

    from numpy.core.umath_tests import inner1d 
    
    res4 = inner1d(U.transpose(0, 2, 1), V.T) 
    

一些性能測試:

In [1]: ni, nj, nk = 100, 200, 1000 

In [2]: %%timeit U = gen.randn(ni, nj, nk); V = gen.randn(nj, nk) 
    ....: np.einsum('ijk,jk->ik', U, V) 
    ....: 
10 loops, best of 3: 23.4 ms per loop 

In [3]: %%timeit U = gen.randn(ni, nj, nk); V = gen.randn(nj, nk) 
(U * V[None, ...]).sum(1) 
    ....: 
10 loops, best of 3: 59.7 ms per loop 

In [4]: %%timeit U = gen.randn(ni, nj, nk); V = gen.randn(nj, nk) 
inner1d(U.transpose(0, 2, 1), V.T) 
    ....: 
10 loops, best of 3: 45.9 ms per loop 
+0

感謝您的回答!你能否添加關於函數如何工作的解釋?例如,如果U而不是'(ni,nj,nk)'是'(nk,ni,nj)',函數調用會如何改變? – Matteo

+0

很好的回答!非常感謝! – Matteo