我在c ++中的逻辑回归实现出了什么问题?

时间:2014-04-03 12:53:57

标签: c++ r armadillo logistic-regression

我使用犰狳线性代数图像用IRLS算法实现了一个简单的逻辑回归函数:

#include <iostream>
#include <string>
#include <boost/math/distributions/normal.hpp>
#include <boost/math/distributions/students_t.hpp>
#include <armadillo>
#include <cmath>

using namespace boost::math;
arma::mat getW(
    arma::mat& beta,
    arma::mat& X,
    std::string family,
    std::string link
)
{
    arma::mat w;
    if(family == "poisson") {
        if(link == "identity") {
            w = arma::diagmat(1/(X * beta));
        }
    }
    else if(family == "binomial") {
        if(link == "logit") {
            arma::colvec tmp = exp(X * beta);
            w = arma::diagmat(tmp/pow(1+tmp, 2));
        }
    }
    else {
        throw 1;
    }
    return w;
}

arma::mat getz(
    arma::mat& y,
    arma::mat& beta,
    arma::mat& X,
    std::string family,
    std::string link
)
{
    arma::mat z;
    if(family == "poisson") {
        if(link == "identity") {
            z = y;
        }
    }
    else if(family == "binomial") {
        if(link=="logit") {
            arma::mat tmp = exp(X * beta);
            z = X*beta + y % (pow(1+tmp, 2)/tmp) - 1 - tmp;
        }
    }
    else {
        throw 1;
    }
    return z;
}

inline arma::mat glmMat(
    arma::mat& y,
    arma::mat& x,
    std::string family,
    std::string link
)
{

    int n = x.n_rows;
    int k = x.n_cols;

    // add a col of all ones
    arma::mat allOne(n, 1, arma::fill::ones);
    x.insert_cols(0, allOne);
    ++k;

    arma::mat res(k, 4);

    if(family=="binomial" and link=="logit")
    {
        arma::mat coef(k, 1, arma::fill::zeros);
        arma::mat W = getW(coef, x, family, link);
        arma::mat z = getz(y, coef, x, family, link);

        try {
            arma::mat J = x.t() * W * x;
            arma::colvec coef1 = arma::solve(J, x.t()*W*z);
            double coefdiff = max(abs(coef - coef1));
            while(coefdiff >= 0.00001) {
                coef = coef1;
                W = getW(coef, x, family, link);
                z = getz(y, coef, x, family, link);
                J = x.t() * W * x;
                coef1 = arma::solve(J, x.t()*W*z);
                coefdiff = max(abs(coef - coef1));
            }


            arma::mat coefVarMatrix = J.i();
            arma::colvec coefVar = coefVarMatrix.diag();
            arma::colvec coefSe = pow(coefVar, .5);
            arma::colvec zscore = coef / coefSe;
            res.col(0) = coef;
            res.col(1) = coefSe;
            res.col(2) = zscore;

            // calculate p values
            auto d = normal_distribution<>();
            for(int i=0; i<k; i++) {
                double p = 2 * (1 - cdf(d, fabs(res(i, 2))));
                if(p < 0 or p > 1) {
                    std::cerr << "Pval is abnormal from glm, dumping data to /tmp/tmpx.csv and /tmp/tmpy.csv" << std::endl;
                    x.save("/tmp/tmpx.csv", arma::csv_ascii);
                    y.save("/tmp/tmpy.csv", arma::csv_ascii);
                    throw 1;
                }
                res(i, 3) = p;
            }
        }
        catch(...) {
            std::cout << "something wrong..." << std::endl;
        }
    }
    else {
        throw 1;
    }
    return res;
}

int main(int argc, char const* argv[])
{
    {
        int nr = 5000;
        int ncx = 50;
        arma::mat x(nr, ncx, arma::fill::randu);
        arma::mat y = arma::randi<arma::mat>(nr, 1, arma::distr_param(0, 1));
        arma::mat xcol;
        arma::mat res(ncx, 4);
        for(int i=0; i<ncx; i++) {
            xcol = x(arma::span::all, i);
            res.row(i) = (glmMat(y, xcol, "binomial", "logit")).row(1);
        }
        res.print("res..........");
    }
    return 0;
}

像这样编译:

g++ glm.cpp --std=c++11 -larmadillo -llapack -lblas -o bin

主要功能模拟5000x50数据集并对每个数据集执行逻辑回归,整个过程在我的笔记本电脑上花费大约23秒。

在R中做大致相同的事情,大约需要2秒钟:

testglm = function() {
        x = matrix(rnorm(5000*50), 5000)
        y = matrix(sample(0:1, 5000, repl=T), 5000)
        res = apply(x, 2, function(coli) summary(glm(y~coli, family=binomial))$coef[2, ])
        # print(res)
        }

system.time(testglm())
   user  system elapsed 
  2.049   0.000   2.049 

我想知道我的实施出了什么问题?

0 个答案:

没有答案