2017-04-06 88 views
4

我试图将矩阵A(m,r),B(n,r)和C(k,r)分解一个张量(m,n,o)。这就是所谓的PARAFAC分解。 Tensorly已经做了这种分解。使用np.tensordot的矩阵Khatri产品

一个重要的步骤是乘以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 
    """ 
+0

我很惊讶你的'ix_'评论。这已经是很长一段时间了。 'reduce'可能需要在PY3中导入。 – hpaulj

+0

@hpaulj:Numpy确实支持这些。但是,Autograd不允许使用.ix_来计算渐变。请参阅:https://github.com/HIPS/autograd/issues/210 –

+0

'ix_'只是Divakar第一种方法所做的维度扩展。 'reduce'应用乘法部分。 – hpaulj

回答

3

不要以为np.tensordot将帮助你在这里,因为它需要展开的的不参与总和,减少轴,如我们有对齐要求,在执行乘法时保持最后一个轴在三个输入之间对齐。因此,使用tensordot时,您需要额外的处理并且在那里有更多的内存需求。

我会建议两种方法 - 一种是broadcasting,另一种是np.einsum

方法#1:随着broadcasting -

(A[:,None,None,:]*B[:,None,:]*C).sum(-1) 

说明:

  • 扩展A4D,通过在与axis=(1,2)无/ 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可能不是。

+0

谢谢!这很棒!如果您可以添加几行解释每个解决方案,那将是非常棒的! –

+0

感谢您的编辑。这真的使它非常有用! –

1

您引用的代码实际上并不是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在一般化什么Divakar建议的方式使用numpy.einsum实现。

+0

非常感谢您的澄清! –