当 n,p 都很大时,PCA 太慢:替代方案?

机器算法验证 主成分分析 降维 高维 爪哇 特纳
2022-03-06 17:32:28

问题设置

我有高维(4096)的数据点(图像),我试图在 2D 中可视化。为此,我以类似于 Karpathy 的以下示例代码的方式使用 t-sne

scikit-learn 文档建议先使用 PCA 来降低数据的维度

如果特征数量非常多,强烈建议使用另一种降维方法(例如,用于密集数据的 PCA 或用于稀疏数据的 TruncatedSVD)以将维数减少到合理的数量(例如 50)。

我正在使用 Darks.Liu 的这段代码在 Java 中执行 PCA:

//C=X*X^t / m
DoubleMatrix covMatrix = source.mmul(source.transpose()).div(source.columns);
ComplexDoubleMatrix eigVal = Eigen.eigenvalues(covMatrix);
ComplexDoubleMatrix[] eigVectorsVal = Eigen.eigenvectors(covMatrix);
ComplexDoubleMatrix eigVectors = eigVectorsVal[0];
//Sort sigen vector from big to small by eigen values 
List<PCABean> beans = new ArrayList<PCA.PCABean>();
for (int i = 0; i < eigVectors.columns; i++) {
    beans.add(new PCABean(eigVal.get(i).real(), eigVectors.getColumn(i)));
}
Collections.sort(beans);
DoubleMatrix newVec = new DoubleMatrix(dimension, beans.get(0).vector.rows);
for (int i = 0; i < dimension; i++) {
    ComplexDoubleMatrix dm = beans.get(i).vector;
    DoubleMatrix real = dm.getReal();
    newVec.putRow(i, real);
}
return newVec.mmul(source);

它使用jblas进行线性代数运算,据我所知,这应该是最快的选择。然而,计算特征向量和特征值(第 3,4 行)被证明是一个巨大的瓶颈(大约 10 分钟,这比我在这个阶段所能承受的要长得多)。

我读过有关 Kernel PCA 的文章,它应该适用于维度非常大的情况,但它的运行时间是,这可能是有问题的,因为我还想处理维度数量的情况的例子很大。O(n3)

正如我所看到的,我的选择要么是“优化”PCA,要么是选择另一种本质上更快的降维方法。

我的问题

  1. 是否有希望以“离线”方式使用 PCA?即,使用大量图像数据集,对它们执行 PCA,然后使用为它们计算的主成分来减少其他(新!)数据点的维度?
  2. 假设我提前知道我只对前 100 个主成分感兴趣,我可以加快特征向量计算吗?
  3. 是否有适合我的情况的替代降维方法(即在应用 t-sne 之前)比 PCA 更快?我正在寻找可以在 Java 中轻松实现的东西。
3个回答

问题 1:假设您观察到一个数据矩阵由此您可以计算特征分解现在的问题是:如果我们从同一个群体中获得新数据,也许收集到矩阵中,会接近的理想正交旋转吗?戴维斯-卡汉定理和一般的矩阵微扰理论解决了这类问题(如果你能拿到副本,斯图尔特和孙 1990 年的教科书是标准参考)。XRn×pXTX=QΛQTZRm×pZQZ

个特征向量,你肯定可以加快速度在 RI中用于此;我确信有一个 Java 等价物,因为无论如何它们都是 fortran 包装器。krARPACK

问题 3:我对 Java 实现一无所知,但是这个线程讨论加速 PCA 和这个CV 线程一样。有大量关于这类事情的研究,并且有大量使用低秩近似或随机化等方法的方法。

您使用的代码将反转整个矩阵。这可能已经是 O(p^3) 了。您可以在 O(p^2) 中近似结果,但这仍然会很慢(但可能快 100 倍)。本质上,采用任意向量并进行幂迭代。很有可能,您将获得第一个特征向量的良好近似值。然后从矩阵中删除这个因子,重复得到第二个。等等。

但是您是否尝试过 ELKI 中的快速 Barnes Hut tSNE 实现是否可能仅使用诸如覆盖树之类的索引处理您的数据?当其他人失败时,我的实施工作很好。

如果您的目标只是以简单直接的方式实现降维,您可以尝试交替最小二乘 (ALS) 技术。例如 Apache Sparkmlib有一个 ALS 实现,我相信它提供了一个 Java api。这应该给你一个矩阵和一个矩阵。矩阵将包含可视化的行向量n×KK×pK×p