欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

dmlc分布式线性模型编译笔记

程序员文章站 2022-07-04 12:03:58
...
1、使用第三方修改后的wormhole工程


原始工程wormhole:https://github.com/dmlc/wormhole
文档:https://media.readthedocs.org/pdf/wormhole/latest/wormhole.pdf
在线文档:https://wormhole.readthedocs.io/en/latest/


下面的这个虽然比较新,但是。。。根本没开发完成!!!
difacto: https://github.com/dmlc/difacto

第三方修改过的:
https://github.com/CNevd/Difacto_DMLC
因为tracker原因,该项目在我自己搭建的hadoop(单机)上没问题,但是公司平台上运行不起来
tracker最终使用的是dmlc core的tracker(直接复制tracker文件夹即可,但是参数名称有变化);

最终使用的修改过tracker后的工程:https://github.com/cherishlc/Difacto_DMLC

2、找不到glog库的问题
注意:也可使用编译选项make USE_GLOG=0 从而不依赖glog
https://github.com/google/glog 下载一下,编译
注意:如果./autogen.sh && ./configure && make && make install命令的最后一步安装因权限问题无法解决,可以通过设置LD_LIBRARY_PATH, LIBRARY_PATH 等环境变量解决:
export GLOG_HOME=/home/user/glog

# g++ 引用的头文件路径
export EXTRA_INCLUDES=$GLOG_HOME/src
# g++ 引用的lib文件路径
export LIBRARY_PATH=$LIBRARY_PATH:$GLOG_HOME/.libs
# 运行程序时查找的动态链接库路径
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$GLOG_HOME/.libs



3、编译
以下编译选项打开了HDFS开关,并且不依赖glog
make USE_HDFS=1 USE_GLOG=0 -j8

export MAKEDMLC="make USE_HDFS=1 USE_GLOG=0 -j8"
cd dmlc-core; $MAKEDMLC; cd -
cd ps-lite; $MAKEDMLC; cd -
cd src/linear; $MAKEDMLC


4、yarn任务提交
调用链:
TODO


/Difacto_DMLC/dmlc-core/yarn/src/org/apache/hadoop/yarn/dmlc/Client.java

5、logistic regression代码阅读

#训练数据的内部存储格式; 以稀疏矩阵形式存储
src/solver/minibatch_solver.h:  using Minibatch = dmlc::RowBlock<FeaID>;
dmlc-core/include/dmlc/data.h:  template<typename IndexType> struct RowBlock {

#index 比如 第0个特征的 FeaID为0
src/linear/async_sgd.h:using FeaID = ps::Key;



#真正执行训练的地方
src/linear/async_sgd.h:  virtual void ProcessMinibatch(const Minibatch& mb, const Workload& wl) {
线性模型的计算是在ProcessMinibatch函数中的loss->Init函数:
src/linear/loss.h: loss->Init(data->GetBlock(), *val, nt_);
void Init(const RowBlock<unsigned>& data,
    const std::vector<V>& w, int nt) {
data_ = data;
Xw_.resize(data_.size);
SpMV::Times(data_, w, &Xw_, nt_); // linear model here
nt_ = nt;
init_ = true;
}

线性部分(不包括sigmoid)的计算是在SpMV::Times函数,其实就是个 乘法+累加的运算
其定义在:
src/base/spmv.h
关键代码: 
if (D.value) {//D为dmlc::RowBlock<unsigned>;  D.value不为空,说明在libsvm 格式的训练文件中, 0 1:0.5 2:1中权重有不为1的情况; 这时,线性模型就会考虑这些权重!!!
  for (size_t j = D.offset[i]; j < D.offset[i+1]; ++j)
    y_i += x[D.index[j]] * D.value[j];
} else {
  for (size_t j = D.offset[i]; j < D.offset[i+1]; ++j)
    y_i += x[D.index[j]];
}



6、关于logistic loss
从下面的代码中可以看到,输入中的label只区分是否大于0,大于0则认为是正样本,否则为负样本
至于logistic regression中标签取0\1和取1,-1的区别(实际上没有区别,只是loss的形式不同,但loss的值是完全一样的),
可以参考博客:https://www.cnblogs.com/bentu*ng/p/6616680.html
定义在src/linear/loss.h: template <typename V> class LogitLoss
最终调用的是:src/base/binary_class_evaluation.h:  V LogitObjv()
  V LogitObjv() {
    V objv = 0;
#pragma omp parallel for reduction(+:objv) num_threads(nt_)
    for (size_t i = 0; i < size_; ++i) {
      V y = label_[i] > 0 ? 1 : -1;
      V score = y * predict_[i];
      if (score < -30)
        objv += -score;
      else if (score <= 30)
        objv += log( 1 + exp( - score ));
    }
    return objv;
  }


7、数据读取
0、在工程目录搜索libsvm文件名,可以发现libsvm格式的解析类位于dmlc-core/src/data/libsvm_parser.h中,解析方法为ParseBlock()

但是解析函数是被谁调用的呢?博主按如下方式进行的查找:
1、上文提到,真正执行训练的是src/linear/async_sgd.h中的AsgdWorker类,训练函数为ProcessMinibatch();在其中没有找到数据读取的相关函数
2、AsgdWorker类的父类为solver::MinibatchWorker, 位于src/solver/minibatch_solver.h中
3、ProcessMinibatch()虚函数在solver::MinibatchWorker的Process()方法中被调用,这里可以看到dmlc::data::MinibatchIter<FeaID> reader; 这个reader就是读取数据的类了,该类位于src/base/minibatch_iter.h中。
4、reader传递给ProcessMinibatch()函数的数据为reader.Value()
5、reader.Value()返回reader的成员变量out_blk_;  out_blk_在成员函数Next()的末尾被设为out_blk_ = mb_.GetBlock();
6、继续追溯,mb_在成员函数Next()中通过Push()方法被设置,在Push方法中,mb_通过in_blk_设置;而Next方法中有一句in_blk_ = parser_->Value();

至此,数据流分析完毕
  reader(比如LibSVMParser)
  => solver::MinibatchWorker的 in_blk_ => mb_ => out_blk_
  => AsgdWorker::ProcessMinibatch() mb 通过localize=> data
  => loss->Init(data->GetBlock(), *val, nt_); 声明loss,并将data赋值到其中

因而,如果想修改libsvm数据的解析方式及使用方式,需要修改的地方有:
  数据读取:LibSVMParser类中的ParseBlock()函数
  将读取的数据分片: MinibatchIter类中的Push函数
  数据本地化:Localizer类中的RemapIndex()函数
  计算梯度时使用样本权重:LogitLoss中的CalcGrad函数