【问题标题】:evaluate multivariate Normal/Gaussian Density in c++在 C++ 中评估多元正态/高斯密度
【发布时间】:2017-01-08 21:28:13
【问题描述】:

现在我有以下函数来评估高斯密度:

double densities::evalMultivNorm(const Eigen::VectorXd &x, const Eigen::VectorXd &meanVec, const Eigen::MatrixXd &covMat)
{
    double inv_sqrt_2pi = 0.3989422804014327;
    double quadform  = (x - meanVec).transpose() * covMat.inverse() * (x-meanVec);
    double normConst = pow(inv_sqrt_2pi, covMat.rows()) * pow(covMat.determinant(), -.5);
    return normConst * exp(-.5* quadform);
}

这只是抄写formula。但是我得到了很多 0、nans 和 infs。我怀疑它来自covMat.determinant() 部分非常接近于零。

我听说将x-meanVec 与其协方差矩阵的“平方根”的倒数预乘起来更“稳定”。从统计上讲,这为您提供了一个均值为零的随机向量,并将单位矩阵作为其协方差矩阵。我的问题是:

  1. 这真的是最好的方法吗?
  2. 哪个是“最好的”平方根技术,以及
  3. 我该怎么做? (最好使用 Eigen)

【问题讨论】:

    标签: c++ eigen normal-distribution


    【解决方案1】:

    广告 1:“视情况而定”。例如,如果您的协方差矩阵具有特殊结构,可以轻松计算其逆矩阵,或者如果维度非常小,则 可以更快、更稳定地实际计算逆矩阵。

    广告 2:通常,Cholesky 分解可以完成这项工作。如果您的协方差确实是正定的(即,不接近半定矩阵),请分解 covMat = L*L^T 并计算 squaredNorm(L\(x-mu))(其中 x=A\b 表示“求解 A*x=b for x”)。当然,如果你的协方差是固定的,你应该只计算一次L(也许也可以反转它)。您也应该使用L 来计算sqrt(covMat.determinant()),因为计算行列式需要再次分解covMat。 小改进:而不是 pow(inv_sqrt_2pi, covMat.rows()) 计算 logSqrt2Pi=log(sqrt(2*pi)) 然后返回 exp(-0.5*quadform - covMat.rows()*logSqrt2Pi) / L.determinant()

    广告 3:这应该在 Eigen 3.2 或更高版本中运行:

    double foo(const Eigen::VectorXd &x, const Eigen::VectorXd &meanVec, const Eigen::MatrixXd &covMat)
    {
        // avoid magic numbers in your code. Compilers will be able to compute this at compile time:
        const double logSqrt2Pi = 0.5*std::log(2*M_PI);
        typedef Eigen::LLT<Eigen::MatrixXd> Chol;
        Chol chol(covMat);
        // Handle non positive definite covariance somehow:
        if(chol.info()!=Eigen::Success) throw "decomposition failed!";
        const Chol::Traits::MatrixL& L = chol.matrixL();
        double quadform = (L.solve(x - meanVec)).squaredNorm();
        return std::exp(-x.rows()*logSqrt2Pi - 0.5*quadform) / L.determinant();
    }
    

    【讨论】:

    • 不错。但是,直接使用来自cholesky 分解的L 真的更有效吗?我现在面临着类似的问题,我发现使用四边形作为 (x - mean).transpose() *covMat.llt().solve(x - mean) 到目前为止要快得多,还没有做过虽然很好的基准。你能想到任何理由更喜欢平方根方法吗(当所有关于 cov_mat 的已知信息是它是对称的和 psd 时)?
    • @Banana 如果您的方法更快(运行),我会感到非常惊讶,因为它实际上需要相同的分解,但需要两个三角求解,而不是一个。另外:在您的情况下,您如何计算sqrt(covMat.determinant())
    • 为什么“两个”三角形求解?尽管您是对的,但通过使用相同的分解来获得更快的运行时似乎很奇怪。我按照您刚刚在评论中写的确切方式计算了它。这些天我可能会做一个适当的运行时测试,我可能做错了什么。我只是在猜测,显式检索 Cholesky 矩阵的步骤可能会以某种方式影响小矩阵。
    • x=llt.solve(b) 本质上是x=llt.matrixL().solve(b); x=llt.matrixL().transpose().solve(x); 但是你是对的,计算分解通常会大大超过进行单独求解的成本。 (除非您多次重复使用分解)。
    猜你喜欢
    • 1970-01-01
    • 2021-01-21
    • 2015-07-26
    • 2013-01-01
    • 2015-02-21
    • 2012-04-06
    • 2021-11-13
    • 1970-01-01
    相关资源
    最近更新 更多