加快马氏距离的计算

时间:2019-03-11 02:45:53

标签: numpy vectorization mahalanobis

背景

我正在实现顺序向后选择算法,以从数据集中选择要素。有问题的数据集是MNIST。我有60000个长度为784的向量。

该算法要求我从总共784个特征中省略一个特征fi,然后选择其余783个特征,在下面的代码中称为selection。然后,我必须计算每个向量的Mahalanobis,以其为尊重等级的平均值。完成此迭代后,我将省略两个功能,然后再删除三个,依此类推。这些迭代每次都需要3分钟。

我必须选择500个特征,因此以上操作重复了500次,因此总共计算了Mahalanobis距离500 x 784 = 392,000次。这需要我计算协方差矩阵的逆。该协方差矩阵的逆不存在,因为它是奇异的,所以我使用的是numpy的伪逆。

问题

您可以想象上面的过程非常慢。计算伪逆是最慢的过程。我以为我可以预先计算伪逆,然后删除与fi关联的相应列和行。但是,事实证明,该伪逆矩阵不等于直接从已经删除fi的向量中直接计算出的伪逆矩阵。

我尝试过的事情

我尝试将其向量化,并处理数组堆栈只是为了发现分解方法较慢。我已经尝试过np.einsum,cdist甚至numexpr。没有什么真正的帮助。

这使我相信,加快速度的最佳机会是将协方差和伪逆计算移出此循环。这是我当前的代码:

def mahalanobis(self, data, lbls, selection):
    subset data[:,tuple(selection)]

    for n in range(10):
        class_rows = subset[np.where(y == n)]
        mean = np.mean(class_rows, axis = )
        pseudoInverse = pinv(covariance(class_rows))
        delta = C - u
        d[n] = np.mean(np.sum(((delta @ pseudoInverse) * delta), axis = -1))
    return np.mean(d)

问题

如何加快计算速度?根据我过去一周进行的测试,看来计算最慢的部分是pseudoInverse = pinv(covariance(class_rows))行。

1 个答案:

答案 0 :(得分:0)

现在,您的代码本质上是:

def mahalanobis(delta, cov):
    ci = np.linalg.pinv(cov)
    return np.sum(((delta @ ci) * delta), axis=-1)

您可以通过以下方法来加快速度:

  • 直接使用svd而不是pinv,并消除不使用的共轭。
  • 使用eigh代替svd,这利用了协方差矩阵的对称性
def mahalanobis_eigh(delta, cov):
    s, u = np.linalg.eigh(cov)
    # note: missing filtering of small s, which you might want to consider adding - pinv already does this for you
    ic = u @ (1/s[...,None] * u.T)
    return np.sum(((delta @ ci) * delta), axis=-1)

值得注意的是,对于复杂的值,此函数或您的函数都无法正常工作。