使用 np.tensordot 的矩阵的 Khatri 乘积
Khatri product of matrices using np.tensordot
我正在尝试将张量 (m, n, o) 分解为矩阵 A(m, r)、B (n, r) 和 C (k, r)。这被称为 PARAFAC 分解。 Tensorly 已经做了这种分解。
一个重要的步骤是将 A、B 和 C 相乘得到形状为 (m, n, o) 的张量。
Tensorly 按如下方式执行此操作:
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不支持的方面。但是,autograd 确实支持 np.tensordot
。
我想知道如何使用np.tensordot
来获得这个乘法。我认为 Tensorflow 的 tf.tensordot
也会有类似的功能。
预期的解决方案应该是这样的:
def tensordot_multplication(A, B, C):
"""
use np.tensordot
"""
不要认为 np.tensordot
会在这里帮助你,因为它需要 展开 不参与和减少的轴,因为我们具有在执行乘法时保持最后一个轴在三个输入之间对齐的对齐要求。因此,使用 tensordot
,您将需要额外的处理并需要更多的内存。
我建议两种方法 - 一种是 broadcasting
and another with np.einsum
.
方法 #1: 与 broadcasting
-
(A[:,None,None,:]*B[:,None,:]*C).sum(-1)
解释:
将 A
扩展到 4D
,方法是在 axis=(1,2)
引入新坐标轴 None/np.newaxis.
通过在 axis=(1)
.
处引入新轴,类似地将 B
扩展到 3D
保持 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中使用的actual code是:
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 以概括 Divakar 建议的方式实现的。
我正在尝试将张量 (m, n, o) 分解为矩阵 A(m, r)、B (n, r) 和 C (k, r)。这被称为 PARAFAC 分解。 Tensorly 已经做了这种分解。
一个重要的步骤是将 A、B 和 C 相乘得到形状为 (m, n, o) 的张量。
Tensorly 按如下方式执行此操作:
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不支持的方面。但是,autograd 确实支持 np.tensordot
。
我想知道如何使用np.tensordot
来获得这个乘法。我认为 Tensorflow 的 tf.tensordot
也会有类似的功能。
预期的解决方案应该是这样的:
def tensordot_multplication(A, B, C):
"""
use np.tensordot
"""
不要认为 np.tensordot
会在这里帮助你,因为它需要 展开 不参与和减少的轴,因为我们具有在执行乘法时保持最后一个轴在三个输入之间对齐的对齐要求。因此,使用 tensordot
,您将需要额外的处理并需要更多的内存。
我建议两种方法 - 一种是 broadcasting
and another with np.einsum
.
方法 #1: 与 broadcasting
-
(A[:,None,None,:]*B[:,None,:]*C).sum(-1)
解释:
将
A
扩展到4D
,方法是在axis=(1,2)
引入新坐标轴 None/np.newaxis.通过在
axis=(1)
. 处引入新轴,类似地将 保持
C
不变并执行逐元素乘法生成4D
数组。最后,总和减少沿着
4D
数组的最后一个轴。
B
扩展到 3D
示意-
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中使用的actual code是:
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 以概括 Divakar 建议的方式实现的。