2016-03-04 45 views
6

Tôi có một tensor U gồm n ma trận kích thước (d, k) và ma trận V của kích thước (k, n).Phép nhân nhân với nửa số hàng chục

Tôi muốn nhân chúng để kết quả trả về ma trận thứ nguyên (d, n) trong đó cột j là kết quả phép nhân ma trận giữa ma trận j của U và cột j của V.

enter image description here

một cách tốt để có được điều này là:

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

tôi tự hỏi nếu có một cách tiếp cận nhanh hơn sử dụng numpy thư viện. Đặc biệt tôi đang nghĩ đến hàm np.tensordot().

Đoạn mã nhỏ này cho phép tôi nhân một ma trận đơn với vô hướng, nhưng sự khái quát hóa rõ ràng đối với véc-tơ không trả lại những gì tôi mong đợi.

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) 

Bất kỳ đề xuất nào?

+0

phần mềm gì bạn đang sử dụng để vẽ hình ảnh của bạn? – hlin117

+1

@ hlin117 - Tôi đã sử dụng bài phát biểu chính. – Matteo

Trả lời

6

Có một số cách bạn có thể thực hiện việc này. Việc đầu tiên mà nói đến cái tâm là 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 sử dụng Einstein notation thể hiện co thắt tensor. Trong biểu thức 'ijk,jk->ik' ở trên, i, jk là các bảng con tương ứng với các kích thước khác nhau của UV. Mỗi nhóm được phân cách bằng dấu phẩy tương ứng với một trong các toán hạng được chuyển đến np.einsum (trong trường hợp này là U có kích thước ijkV có kích thước jk). Phần '->ik' chỉ định kích thước của mảng đầu ra. Bất kỳ kích thước với subscripts mà không có trong chuỗi đầu ra được tổng hợp hơn.

np.einsum là cực kỳ hữu ích để thực hiện các cơn co thắt phức tạp, nhưng có thể mất một lúc để hoàn toàn quấn đầu của bạn xung quanh cách hoạt động. Bạn nên xem các ví dụ trong tài liệu (được liên kết ở trên).


Một số tùy chọn khác: nhân

  1. Element-khôn ngoan với broadcasting, tiếp theo là tổng kết:

    res3 = (U * V[None, ...]).sum(1) 
    
  2. inner1d với một tải của transposing:

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

Một số tiêu chuẩn:

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

Cảm ơn câu trả lời! Bạn có thể vui lòng thêm giải thích về cách chức năng hoạt động không?Ví dụ, hàm gọi thay đổi như thế nào nếu U thay vì là '(ni, nj, nk)' là '(nk, ni, nj)'? – Matteo

+0

Câu trả lời hay! Cảm ơn rất nhiều! – Matteo

Các vấn đề liên quan