使用 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 建议的方式实现的。