用犰狳加载大型矩阵

Posted

技术标签:

【中文标题】用犰狳加载大型矩阵【英文标题】:Loading large matrix with Armadillo 【发布时间】:2017-04-20 04:41:05 【问题描述】:

我有一个非常稀疏的矩阵,密度约为0.01,维度为20000 x 500000。我正在尝试将其加载到犰狳中

sp_mat V;
V.load(filename, coord_ascii);

文件格式为

row column value

但这需要的时间太长了。 Python 可以比犰狳创建这个矩阵更快地解析文件并用它填充字典方式。我应该如何正确地做到这一点?

矩阵将被整数填充。

任何建议将不胜感激!

更新:

这只是犰狳独有的问题。 C++ 在逐行读取时会毫无问题地迭代文件,但是将值分配给arma::sp_mat 非常慢。

【问题讨论】:

在 C++ 和 Python 中处理稀疏矩阵非常不同。由于您的矩阵非常大,因此在 C++ 中这将需要一些时间。 但我不明白为什么会有如此巨大的差异。使用 C++ 时,速度几乎降低了 100 倍。我觉得导入 Python C API 来调用 Python 函数会更快,该函数可以将映射返回到 C++,然后使用映射创建稀疏矩阵。这不仅仅是犰狳:当我使用 ifstream 时,我也遇到了同样的减速。 根据犰狳SpMat 的文档,唯一支持的格式是arma_binary。因此,您正在使用不受支持的代码,这些代码可能尚未完成或未优化。该文档有一个statement,关于什么被认为是稳定的:公共 API 文档中未明确描述的任何函数、类、常量或其他代码都被视为底层内部实现细节的一部分,并且可能会更改或被删除恕不另行通知。 那么这是鸡和蛋的问题:/我无法以arma_binary 格式保存,因为我需要先构建矩阵然后调用保存。但我也无法加载矩阵,因为我需要arma_binary 格式。 【参考方案1】:

犰狳文档指定

“使用批量插入构造函数通常比使用元素访问运算符连续插入值快得多”

所以这是我能想到的最好的了

sp_mat get(const char *filename)          
    vector<long long unsigned int> location_u;
    vector<long long unsigned int> location_m;
    vector<double> values;                    

    ifstream file(filename);                  
    int a, b, c;                              
    while(file >> a >> b >> c)                                    
        location_u.push_back(a);              
        location_m.push_back(b);              
        values.push_back(c);                  
                                             

    umat lu(location_u);                      
    umat lm(location_m);                      
    umat location(join_rows(lu, lm).t());     

    return V(location, vec(values));                                         
                                             

它现在以合理的速度运行,大约每秒 100 万行。

【讨论】:

【参考方案2】:

我今天在尝试使用 Armadillo 的 .load() 加载 100MB CSV 时遇到了同样的问题。实在是太慢了。

由于@Enrico Borba 回答说他正在使用 std::ifstream 进行自己的文件读取,结果非常惊人,这是我自己的代码,也可以使用 ifstream 将 CSV 文件加载到犰狳的垫子类型。

例如,如果您尝试这样做,加载文件将花费大量时间:

arma::mat A;
A.load("file.csv", arma::csv_ascii);

所以这是一个替代方案,比上面的代码快一千:

arma::mat readCSV(const std::string &filename, const std::string &delimeter = ",")

    std::ifstream csv(filename);
    std::vector<std::vector<double>> datas;

    for(std::string line; std::getline(csv, line); ) 

        std::vector<double> data;

        // split string by delimeter
        auto start = 0U;
        auto end = line.find(delimeter);
        while (end != std::string::npos) 
            data.push_back(std::stod(line.substr(start, end - start)));
            start = end + delimeter.length();
            end = line.find(delimeter, start);
        
        data.push_back(std::stod(line.substr(start, end)));
        datas.push_back(data);
    

    arma::mat data_mat = arma::zeros<arma::mat>(datas.size(), datas[0].size());

    for (int i=0; i<datas.size(); i++) 
        arma::mat r(datas[i]);
        data_mat.row(i) = r.t();
    

    return data_mat;

然后你可以像下面这样替换它:

arma::mat A = readCSV("file.csv");

【讨论】:

以上是关于用犰狳加载大型矩阵的主要内容,如果未能解决你的问题,请参考以下文章

犰狳中的矩阵(向量)在从文件加载后获得新的小数位

在 C++ 中存储大矩阵(犰狳)

使用 RcppArmadillo 时无法加载犰狳立方体<uword>

在 Octave 中保存/加载大型矩阵

使用 carma(犰狳矩阵和 numpy 数组)用 pybind11 包装 c++ 类时出错

犰狳矩阵中的多种数据类型