使用numpy矢量化方程式

时间:2014-05-11 11:59:18

标签: python python-2.7 python-3.x numpy scipy

enter image description here

enter image description here

我正在尝试将上述公式实现为矢量化形式。 K=3此处X150x4 numpy数组。 mu3x4 numpy数组。 Gamma是一个150x3 numpy数组。 Sigma是一个kx4x4 numpy数组。因此Sigma[k]是一个4x4 numpy数组。 N=150

N_k = np.sum(Gamma, axis=0)
for k in range(K): # Correct
         x_new = X - mu[k] #Correct
         a = np.dot(x_new.T, x_new) #Incorrect from here I feel
         for i in range(len(data)):
             sigma[k] = Gamma[i][k] * a
         sigma[k]=sigma[k]/N_k #totally incorrect

如何解决这个问题?

2 个答案:

答案 0 :(得分:8)

产品总和?听起来像是np.einsum的工作:

import numpy as np
N = 150
K = 3
M = 4
x = np.random.random((N,M))
mu = np.random.random((K,M))
gamma = np.random.random((N,K))

xbar = x-mu[:,None,:] # shape (3, 150, 4)
sigma = np.einsum('nk,knm,kno->kmo', gamma, xbar, xbar)
sigma /= gamma.sum(axis=0)[:,None,None]

解码'nk,knm,kno->kmo'

此下标规范在数组左侧有三个组件(->),后面是右侧的一个组件。

左侧的三个组件对应gammaxbarxbar的下标,操作数将传递给np.einsum

gamma有下标nk,就像您发布的公式一样。 xbar有形状(3,150,4)。您可以将其视为具有下标knm,其中kn具有与您发布的公式中相同的含义,m是表示轴的下标长度4,未在公式中明确提及,但显然已经给出了对数组形状的描述。

现在第三个下标组件是kno。使用o下标是因为o扮演的角色与m下标相同,但我们不希望求m 。实际上,我们希望mo下标可以独立迭代,而不是锁定步骤。因此我们给第三个下标不同的字母。

请注意n出现在左侧的下标(nk, knm, kno)中,但未出现在右侧(kmo中)。这告诉np.einsum总结n

k出现在左侧的下标和右侧的下标中。这告诉np.einsum我们希望在锁定步骤中推进k下标,但是(因为它出现在右侧)我们不想总结k

由于kmo出现在右侧,因此这些下标仍保留在结果中。这导致sigma具有形状(K,M,M)(即(3,4,4))。

答案 1 :(得分:2)

在unubtu的最佳答案之上只有几个性能指标。

np.einsum无法使用两个以上的参数优化调用。只要有可能,通常将计算手动拆分为两个参数组就会更快,例如:

def unubtu():
    xbar = x-mu[:,None,:] # shape (3, 150, 4)
    sigma = np.einsum('nk,knm,kno->kmo', gamma, xbar, xbar)
    sigma /= gamma.sum(axis=0)[:,None,None]
    return sigma

def faster():
    xbar = x-mu[:,None,:] # shape (3, 150, 4)
    sigma = np.einsum('knm,kno->kmo', gamma.T[..., None] * xbar, xbar)
    sigma /= gamma.sum(axis=0)[:,None,None]
    return sigma

In [50]: %timeit unubtu()
10000 loops, best of 3: 147 µs per loop

In [51]: %timeit faster()
10000 loops, best of 3: 129 µs per loop

12%的改善并不多,但是对于更大的阵列,差异会更大(更大)。

此外,尽管np.einsum是一个很好的工具,但这使得非常简单的事情变得非常简单,如果你的numpy是用一个好的线性代数库构建的,它绝不像np.dot那样优化。在您的情况下,假设K很小,使用np.dot和Python循环甚至更快:

def even_faster():
    sigma = np.empty((K, M, M))
    for k in xrange(K):
        x_ = x - mu[k]
        sigma[k] = np.dot((x_ * gamma[:, k, None]).T, x_)
    sigma /= gamma.sum(axis=0)[:,None,None]
    return sigma

In [52]: %timeit even_faster()
10000 loops, best of 3: 101 µs per loop