加快马氏距离计算

Speeding up Mahalanobis distance calculation

背景:

我正在对数据集中的 select 特征实施顺序向后选择算法。有问题的数据集是 MNIST。我有 60000 个长度为 784 的向量。

该算法要求我从总共 784 个特征中删除一个特征 fi 和 select 其余 783 个特征,在下面的代码中称为 selection。然后我必须根据 类' 的意思计算每个向量的 Mahalanobis。完成此迭代后,我将省略两个功能,然后是三个,依此类推。这些迭代中的每一个都需要 3 分钟。

我必须 select 500 个特征,所以上面重复了 500 次,所以总共计算了 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)).

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

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
    ci = u @ (1/s[...,None] * u.T)
    return np.sum(((delta @ ci) * delta), axis=-1)

值得注意的是,此函数和您的函数都无法正确处理复数值。