Хатринское произведение матриц с использованием np.tensordot
Я пытаюсь разложить тензор (m, n, o) на матрицы A(m, r), B (n, r) и C (k, r). Это известно как разложение PARAFAC. Тензорно уже делает этот вид разложения.
Важным шагом является умножение A, B и C, чтобы получить тензор формы (m, n, o).
Тензорно делает это следующим образом:
def kt_to_tensor(A, B, C):
factors = [A, B, C]
for r in range(factors[0].shape[1]):
vecs = np.ix_(*[u[:, r] for u in factors])
if r:
res += reduce(np.multiply, vecs)
else:
res = reduce(np.multiply, vecs)
return res
Однако используемый мной пакет (Autograd) не поддерживает np.ix_
операции. Таким образом, я написал более простое определение следующим образом:
def new_kt_to_tensor(A, B, C):
m, n, o = A.shape[0], B.shape[0], C.shape[0]
out = np.zeros((m, n, o))
k_max = A.shape[1]
for alpha in range(0, m):
for beta in range(0, n):
for delta in range(0, o):
for k in range(0, k_max):
out[alpha, beta, delta]=out[alpha, beta, delta]+ A[alpha, k]*B[beta, k]*C[delta, k]
return out
Однако оказывается, что эта реализация также имеет некоторые аспекты, которые не поддерживает Autograd. Тем не менее, Autogra действительно поддерживает np.tensordot
,
Мне было интересно, как использовать np.tensordot
чтобы получить это умножение. Я думаю, что Tensorflow's tf.tensordot
также будет иметь аналогичную функциональность.
Предполагаемое решение должно быть что-то вроде:
def tensordot_multplication(A, B, C):
"""
use np.tensordot
"""
2 ответа
Не думай np.tensordot
здесь вам помогут, так как нужно распределить оси, которые не участвуют в уменьшении суммы, так как у нас есть требование выравнивания, чтобы последняя ось была выровнена между тремя входами при выполнении умножения. Таким образом, с tensordot
, вам потребуется дополнительная обработка и больше требований к памяти.
Я бы предложил два метода - один с broadcasting
и еще один с np.einsum
,
Подход № 1: с broadcasting
-
(A[:,None,None,:]*B[:,None,:]*C).sum(-1)
Пояснение:
простираться
A
в4D
, вводя новые оси вaxis=(1,2)
с None/np.newaxis.Аналогичным образом расширить
B
в3D
, введя новую ось вaxis=(1)
,Держать
C
как есть и выполнять поэлементное умножение, в результате чего4D
массив.Наконец, уменьшение суммы происходит вдоль последней оси
4D
массив.
Схематично поставил -
A : m r
B : n r
C : k r
=> A*B*C : m n k r
=> out : m n k # (sum-reduction along last axis)
Подход № 2: с np.einsum
-
np.einsum('il,jl,kl->ijk',A,B,C)
Идея здесь такая же, как и с предыдущей broadcasting
один, но с строковыми обозначениями, помогающими нам в передаче информации об осях в более сжатой форме.
Broadcasting
конечно доступно на tensorflow
так как у него есть инструменты для expand dimensions
, в то время как np.einsum
вероятно нет.
Код, на который вы ссылаетесь, на самом деле не то, как TensorLy реализует его, а просто альтернативная реализация, приведенная в документе.
Фактический код, используемый в TensorLy:
def kruskal_to_tensor(factors):
shape = [factor.shape[0] for factor in factors]
full_tensor = np.dot(factors[0], khatri_rao(factors[1:]).T)
return fold(full_tensor, 0, shape)
где khatri_rao реализован с использованием numpy.einsum способом, который обобщает то, что предложил Дивакар.