XGBoost解析系列-准备


0.前言

  研究生期间有幸和各路大腿参加过些机器学习与数据挖掘的比赛,发现XGBoost单模型分类与回归效果往往比其他模型LR、RF、SVM、GDBT模型要好上不少,现在Microsoft还出LightGBM,首秀就在滴滴大赛中摘得10w美刀桂冠。当然也不好一概而论,比赛场景有限,数据特征处理偏稠密方式,比如图像领域CNN效果模型更好,而计算广告业界更喜欢使用FM/FFM做预估,前者是高维像素级特征,后者是千亿级别的离散特征。
  也正是体验过XGBoost强大之处,满怀着对陈天奇大神的钦佩之情,最近强行腾出时间阅读&Debug完陈天奇实现XGBoost的源码,对于饥渴已久的我来说,读完最大感受是:实现算法需要这么多硬技能!值得花时间去钻研学习,不仅对XGBoost的理解更深刻了,也对日后算法工程实现都有指导意义。接下来该系列会慢慢剖析XGBoost的工程实现,敬请期待!

  对于有心想阅读&debug陈天奇版本XGBoost源码的读者建议看完该博客。

1.代码准备

  笔者以macOS系统(macOS Sierra Version:10.12.1)作为开发环境进行说明,不同系统请读者自行google相关替代过程,首先下载github代码:

git clone --recursive https://github.com/dmlc/xgboost 

  加上参数–recursive,会把xgboost依赖的submodule都clone下来,并放在xgboost主目录下。目前笔者基于git库的log commit:d9d5293cdbbbf67dc8ff9d4a3f171d0990fdd1ee (2017.10.26 17:31:10提交的commit),xgboost依赖以下4个模块:

  • dmlc-core:用于支撑DMLC机器学习项目的公共代码库
  • rabit:封装了高度可依赖的Allreduce与Broadcast接口,用于分布式消息同步
  • nccl:优化后的多GPU通信基础库
  • cub:CUDA编程相关库

2. 编译准备

  1. mac系统原生编译工具基于clang与lldb,其中编译工具clang使用的stl等库实现与GNU是不同的,还是建议安装GNU的编译工具,笔者mac下安装其他软件使用HomeBrew。

  • gcc/g++编译链接,笔者安装gcc 6.0版本、目前有7.0+版本
  • gdb调试工具,笔者安装的8.0.1版本,高版本有新特性支持
brew install gcc6 --without-multilib
brew install gdb

   注意:gdb在mac系统下因为权限问题不能直接调试程序,第一次使用会出现错误Unable to find Mach task port,需要安装信任证书并codesign下:
  1. command+空格输入“钥匙串访问”,点击左上角的“钥匙串访问”菜单选择:证书助理->创建证书->名称填写gdb-cert,证书类型选代码签名,勾选”让我覆盖这些默认值”,然后一直next,直到“指定用于该证书的位置”选择“系统”。
  2. 选择“系统”、“我的证书”,找到刚刚的证书,选择“始终信任”。
  3. 运行以下shell命令,重启系统,证书才能生效

  注意:如果创建系统证书的时候出现未知错误Unknown Error = -2,147,414,007时,解决方法:可以先“指定用于该证书的位置”选择“登录”(可以执行成功),然后导出创建证书到本地,删除刚创建的登录证书,再把导出的文件导入到系统证书中。笔者基于macOS Sierra 10.13版本会出现这种问题,也通过该方法解决。最后,重启按住command + R进入恢复模式,打开终端,输入csrutil enable --without debug,再重启系统。

codesign -s gdb-cert /usr/local/bin/gdb

  gdb调试工具功能非常强大,早前M$下做开发,基于VS界面调试直观、方便,几乎没花时间去研究。现在基于Linux开发必须得会,尽管gdb无良好界面看上去很不方便,但是习惯后发现还是很顺手的。记得以前都不知道如何用gdb打印输出string、vector等stl的变量值,后来才发现可以通过配置.gdbinit定义打印函数+pretty功能(高版本gdb支持)来格式化打印stl各种容器,甚至是C++11的unorder map。究其原因,其实是自己对stl容器类实现过程,内部成员不够熟悉,读者可以移步vector stl库实现详解来了解其具体实现以及gdb配置教程来配置升级下gdb(最近会更新,并添加链接)。

  2. 建议检查下

which gcc-6 # 是否为 /usr/local/bin/gcc-6
which g++-6 # 是否为 /usr/local/bin/g++-6

  如果有误,配置~/.bashrc环境变量path:export PATH=/usr/local/bin:$PATH

  3. 修改make编译配置

cd <你的directory>/xgboost
cp make/config.mk ./config.mk
vim ./config.mk
# 将其中CC与CXX注释的2句话打开,并修改为
export CC = gcc-6
export CXX = g++-6

  4. 执行make编译

cd <你的directory>/xgboost
make -j4

   make程序的-j是并行度设置,-j8会更快,读者可以根据机器的cpu核数自行调整,编译完成会生成各种xgboost需要的库以及可执行文件。

  5. 为python安装xgboost模块

cd python-package
sudo python setup.py install

  至此,已完成mac系统下的xgboost安装,但是如果打算阅读源码,并debug代码细节,需要以下设置

3. debug编译配置

  1. 目前工程下默认编译设置是release模式,直接debug会问题:

  • g++编译优化级别为-O3,-O3为高度优化参数,debug会有问题:1)变量容易被优化而不可见,2)指令编译重排,不容易理解,3)宏定义与inline函数会被优化。
  • g++编译没有-g参数,也就是说直接调试程序是拿不到debug符号symbol

   通过以下语句对所有编译来增加debug支持

# 在xgboost主目录下
grep '\-O3' -rl -R . |xargs  sed -i '' 's/-O3/-O0 -g -gdwarf-3/g'
sed -i '' 's/crv/crvs/g' Makefile
make -j4

  第一句对带有-O3的编译参数修改为-O0(完全不优化),-g生成debug符号,-gdwarf-3生成兼容可读的debug symbol文件,如果没有这个参数,gdb print依赖库的变量时会出现错误:error reading variable: Could not find the frame base。第二句是对于依赖静态库libxgboost.a改用ar打包参数crvs,否则不能debug静态库源代码。

  2. xgboost主目录下提供很多测试demo案例和模块测试代码:1)demo文件夹存放着各种案例的测试数据与脚本。有二分类、多分类、回归、LearningToRank排序。其中runexp.sh脚本可以测试xgboost启动的整个过程,涉及train、predict、模型dump以及树的可视化输出。2)tests文件夹下提供很多模块的测试代码,调试他们对模块功能理解也有帮助。
  下面以binary_classification案例为例作为说明例子,对应的配置文件为:mushroom.conf,注意其中配置的数据文件路径,由于运行runexp.sh脚本路径是当前路径,因此运行runexp.sh脚本是不会出错的,但是我们后期debug xgboost,工作目录区间为xgboost主目录,需要修改路径为绝对路径:

data = "agaricus.txt.train"修改为"<你的directory>/xgboost/demo/data/agaricus.txt.train"
eval[test] = "agaricus.txt.test"修改为"<你的directory>xgboost/demo/data/agaricus.txt.test"
test:data = "agaricus.txt.test"修改为"<你的directory>xgboost/demo/data/agaricus.txt.test"
增加配置项nthread = 1,默认情况下xgboost是直接基于cpu数设置OMP并行线程,debug过程中多线程运行切换容易干扰调试执行逻辑。
原本gdb调试支持多线程调试:通过设置set scheduler-locking on来阻塞其他线程的运行,只运行当前线程的运行。
但笔者在测试中会出现错误:Target 'native' cannot support this command.目前还未解决。

  3. 开始gdb debug

gdb xgboost
# 进入gdb界面后,设置conf路径
set args demo/binary_classification/mushroom.conf
# b 设置相关断点
run

  4. 查看变量名出现位置的Tips:

git grep xxx
# 前者是基于当前git库下,遍历当前目录下所有找出出现xxx变量名的位置,后者是找出当前目录下的情况。
# 因为在xgboost主目录下还存在其他的库,所以使用前者无法查到其他库出现的位置,而后者可以
grep xxx -R .

VS Code可视化调试

  原本使用vim+gdb已经能满足调试需求,但是笔者还是习惯于使用Visual Studio Code进行代码阅读,使用VS可视化界面与gdb混合调试,mac版本下的VS code算业界良心,界面干净清爽,启动速度快,支持插件。混合调试主要为了追求效率:1)VS设置断点方便,而且具备断点保存功能,但是对于print打印输出复杂类型string、vector目前没找到很好的解决方案。2)配置后的gdb,几乎能print打印输出任何结构,任何变量的数据。不过调试需要以下步骤:

  1. 官网下载VS code:https://code.visualstudio.com/download
  2. 安装C++开发插件:C/C++ Clang、cpptools、C++ Intellisense三大插件,方便代码跳转以及调试
  3. 配置make和run_test任务tasks.json:点击“任务”->配置任务,主要是使用shell来编译与运行测试
  4. 配置调试设置launch.json,会依赖步骤3配置的make任务:点击“调试” -> 增加配置

tasks.json配置文件:

{
    // See https://go.microsoft.com/fwlink/?LinkId=733558
    // for the documentation about the tasks.json format
    "version": "2.0.0",
    "tasks": [
        {
            "taskName": "make",
            "type": "shell",
            "command": "make -j8"
        },
        {
            "taskName": "run_test",
            "type": "shell",
            "command": "${workspaceRoot}/xgboost demo/binary_classification/mushroom.conf"
        }
    ]
}

launch.json配置文件:

{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "gdb",
            "type": "cppdbg",
            "request": "launch",
            "program": "${workspaceRoot}/xgboost",
            "args": ["${workspaceRoot}/demo/binary_classification/mushroom.conf"],
            "stopAtEntry": false,
            "cwd": "${workspaceRoot}",
            "environment": [],
            "externalConsole": true,
            "preLaunchTask": "make",
            "MIMode": "gdb"
        }
    ]
}

VS code配置效果图:


这里写图片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章