原始工程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; dmlc-core/include/dmlc/data.h: template 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& data, const std::vector & 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 ; 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/bentuwuying/p/6616680.html
定义在src/linear/loss.h: templateclass 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
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函数