2016-01-22 7 views
5

Grundsätzlich habe ich 2 Tensoren: A, wo A.Shape = (N, H, D), und B, wo B.shape = (K, H, D). Was Ich mag würde zu tun ist, einen Tensor zu bekommen, C, mit Form (N, K, D, H), so dass:Theano: Wie man ein "Matrix äußeres Produkt" nimmt, wo die Elemente Matrizen sind

C[i, j, :, :] = A[i, :, :] * B[j, :, :]. 

Kann diese effizient in Theano getan werden?

Randbemerkung: Die tatsächliche Endergebnis, das ich erreichen möchte einen Tensor haben, E, von Form (N, K, D), so dass:

E[i, j, :] = (A[i, :, :]*B[j, :, :]).sum(0) 

Also, wenn es eine Möglichkeit, dies direkt zu bekommen, Ich würde es bevorzugen (spart Platz hoffentlich).

+0

Welcher Dimension wollen Sie über zusammenzuzufassen? Die erste, 0? oder 'H', das ist das zweitletzte in den ursprünglichen Arrays? – hpaulj

+0

In 'numpy' könnte dies ausgedrückt werden als 'np.einsum (' nhd, khd-> nkd ', A, B)' – hpaulj

+0

Ich würde es gerne über H machen. Das sollte Summe sein (1) unter der Annahme, dass der Tensor hat vor der Addition die Form (1, H, D). – Theo

Antwort

2

Ein Ansatz vorgeschlagen werden könnte, broadcasting verwendet - beachten

(A[:,None]*B).sum(2) 

Bitte, dass die Zwischen Array erstellt wird von Form wäre (N, K, H, D) vor Summenreduktion auf axis=2 reduziert sie auf (N,K,D).

+0

Ja, am Ende dachte ich an so etwas, aber ich hatte Angst, dass es diesen riesigen Tensor erzeugen würde, der bei der Arbeit an der GPU unerschwinglich sein könnte. Aber nicht sicher, ob es schon ist. – Theo

+0

@Theo Das ist richtig, es gibt hier eine erhebliche Menge an Speicheraufwand. – Divakar

+0

Ja, ich denke, ich werde mit dieser Lösung beginnen und sehen, ob es für die typischen Größen funktioniert, mit denen ich arbeiten werde. Vielen Dank. – Theo

0

Sie bekommen das endgültige dreidimensionale Ergebnis E ohne das Erzeugen des großen Zwischenarrays unter Verwendung von batched_dot:

import theano.tensor as tt 
A = tt.tensor3('A') # A.shape = (D, N, H) 
B = tt.tensor3('B') # B.shape = (D, H, K) 
E = tt.batched_dot(A, B) # E.shape = (D, N, K) 

Leider erfordert dies, dass Sie die Dimensionen auf Ihren Eingabe- und Ausgabearrays permutieren. Obwohl dies kann mit dimshuffle in Theano geschehen scheint es batched_dot nicht willkürlich strided Arrays bewältigen können und so stellt sich die folgenden ein ValueError: Some matrix has no unit stride wenn E ausgewertet:

import theano.tensor as tt 
A = tt.tensor3('A') # A.shape = (N, H, D) 
B = tt.tensor3('B') # B.shape = (K, H, D) 
A_perm = A.dimshuffle((2, 0, 1)) # A_perm.shape = (D, N, H) 
B_perm = B.dimshuffle((2, 1, 0)) # B_perm.shape = (D, H, K) 
E_perm = tt.batched_dot(A_perm, B_perm) # E_perm.shape = (D, N, K) 
E = E_perm.dimshuffle((1, 2, 0)) # E.shape = (N, K, D) 

batched_dot verwendet scan entlang der ersten (Größe D) Dimension . Da scan sequentiell durchgeführt wird, könnte dies rechnerisch weniger effizient sein als die parallele Berechnung aller Produkte, wenn diese auf einer GPU laufen.

Sie können zwischen der Speichereffizienz des batched_dot Ansatzes und der Parallelität im Broadcast-Ansatz unter Verwendung von scan explizit abwägen. Idee würde das vollständige Produkt C für Chargen von Größe M parallel zu berechnen sein (unter der Annahme, M ist ein exakter Faktor D), iterieren Chargen mit scan:

import theano as th 
import theano.tensor as tt 
A = tt.tensor3('A') # A.shape = (N, H, D) 
B = tt.tensor3('B') # B.shape = (K, H, D) 
A_batched = A.reshape((N, H, M, D/M)) 
B_batched = B.reshape((K, H, M, D/M)) 
E_batched, _ = th.scan(
    lambda a, b: (a[:, :, None, :] * b[:, :, :, None]).sum(1), 
    sequences=[A_batched.T, B_batched.T] 
) 
E = E_batched.reshape((D, K, N)).T # E.shape = (N, K, D) 
Verwandte Themen