C++特征值/向量分解,快速仅需前n个向量。

20

我有一个 ~3000x3000 的类似协方差的矩阵,在上面进行特征值-特征向量分解(它是一个 OpenCV 矩阵,我使用 cv::eigen() 来完成这项工作)。

然而,实际上我只需要前30个特征值/特征向量,我不关心其他的。理论上,这应该可以显著加快计算速度,对吗?也就是说,有2970个无需计算的特征向量。

哪个 C++ 库可以让我做到这一点?请注意,OpenCV的 eigen() 方法确实有针对此功能的参数,但文档中说它们被忽略了,我亲自测试了,它们确实被忽略了:D

更新: 我用 ARPACK 完成了这个任务。我设法将其编译为 Windows 版本,甚至使用了它。结果看起来很有希望,可以在这个玩具示例中看到:

#include "ardsmat.h"
#include "ardssym.h"
int     n = 3;           // Dimension of the problem.
    double* EigVal = NULL;  // Eigenvalues.
    double* EigVec = NULL; // Eigenvectors stored sequentially.


    int lowerHalfElementCount = (n*n+n) / 2;
    //whole matrix:
    /*
    2  3  8
    3  9  -7
    8  -7 19
    */
    double* lower = new double[lowerHalfElementCount]; //lower half of the matrix
    //to be filled with COLUMN major (i.e. one column after the other, always starting from the diagonal element)
    lower[0] = 2; lower[1] = 3; lower[2] = 8; lower[3] = 9; lower[4] = -7; lower[5] = 19;
    //params: dimensions (i.e. width/height), array with values of the lower or upper half (sequentially, row major), 'L' or 'U' for upper or lower
    ARdsSymMatrix<double> mat(n, lower, 'L');

    // Defining the eigenvalue problem.
    int noOfEigVecValues = 2;
    //int maxIterations = 50000000;
    //ARluSymStdEig<double> dprob(noOfEigVecValues, mat, "LM", 0, 0.5, maxIterations);
    ARluSymStdEig<double> dprob(noOfEigVecValues, mat);

    // Finding eigenvalues and eigenvectors.

    int converged = dprob.EigenValVectors(EigVec, EigVal);
    for (int eigValIdx = 0; eigValIdx < noOfEigVecValues; eigValIdx++) {
        std::cout << "Eigenvalue: " << EigVal[eigValIdx] << "\nEigenvector: ";

        for (int i = 0; i < n; i++) {
            int idx = n*eigValIdx+i;
            std::cout << EigVec[idx] << " ";
        }
        std::cout << std::endl;
    }

结果如下:

9.4298, 24.24059

对于特征值,以及

-0.523207, -0.83446237, -0.17299346
0.273269, -0.356554, 0.893416

每行分别表示2个特征向量(每行一个特征向量)。 在这种情况下,代码无法找到3个特征向量(它只能找到1-2个,在这种情况下使用assert()确保了这一点,但是好吧,这不是问题)。


2
“通过‘前30个特征值/特征向量’,您是指具有最大模数、最大实部或其他什么特征值吗?在谷歌搜索后,看起来SLEPc可能有您需要的内容。” - James
2
我会使用ARPACK来完成这个任务。你将立即获得30个特征向量。 - David Heffernan
5
从理论上讲,这应该可以显著加快计算速度,对吧?这取决于所使用的算法,至少不是显然正确的。但是,是的,有一些算法可以在某些特定特征值范围内快速计算仅特征向量,其中 Arnoldi/Lanczos 是最著名的算法,因此 ARPACK 算法是一种经典的选择。它很老并不表示它不好用,如果您真的需要高性能,那么它肯定非常棒;但我同意这些 Fortran 库确实难以使用。 - leftaroundabout
2
啊,我想你的意思是 eigen 并不符合你的需求,而不是 ARPACK。抱歉。无论如何,我只能说以前我使用的直接算法可能需要很多分钟,而且经常消耗所有系统内存才能产生任何结果。使用 ARPACK,我可以在一两秒钟内获得前几百个特征值对,而不需要太多的系统资源。 - David Heffernan
10
@NameZero912,如果你知道答案而且没有人提供它,请自己回答并接受它。这样可以把这个问题从未回答的列表中移除。 :) - Yakk - Adam Nevraumont
显示剩余12条评论
2个回答

1
此文中,Simon Funk展示了一种简单有效的方法来估算一个非常大的矩阵的奇异值分解(SVD)。在他的例子中,矩阵是稀疏的,尺寸为:17,000 x 500,000。
现在,在这里描述了特征值分解与SVD密切相关。因此,如果您的矩阵是稀疏的,那么您可能会受益于考虑Simon Funk方法的修改版本。此外,您的矩阵不仅是方形的,而且也是对称的(如果这是您所说的协方差),这可能会导致额外的简化。
...只是一个想法 :)

0

看起来Spectra可以以良好的性能完成工作。

以下是他们文档中的示例,用于计算密集对称矩阵M(类似于您的协方差矩阵)的前3个特征值:

#include <Eigen/Core>
#include <Spectra/SymEigsSolver.h>
// <Spectra/MatOp/DenseSymMatProd.h> is implicitly included
#include <iostream>
using namespace Spectra;
int main()
{
    // We are going to calculate the eigenvalues of M
    Eigen::MatrixXd A = Eigen::MatrixXd::Random(10, 10);
    Eigen::MatrixXd M = A + A.transpose();
    // Construct matrix operation object using the wrapper class DenseSymMatProd
    DenseSymMatProd<double> op(M);
    // Construct eigen solver object, requesting the largest three eigenvalues
    SymEigsSolver< double, LARGEST_ALGE, DenseSymMatProd<double> > eigs(&op, 3, 6);
    // Initialize and compute
    eigs.init();
    int nconv = eigs.compute();
    // Retrieve results
    Eigen::VectorXd evalues;
    if(eigs.info() == SUCCESSFUL)
        evalues = eigs.eigenvalues();
    std::cout << "Eigenvalues found:\n" << evalues << std::endl;
    return 0;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接