XGBoost 多分类实战指南:基于 UCI Dermatology 数据集的 multi:softmax 与 multi:softprob 详解
【免费下载链接】xgboostScalable, Portable and Distributed Gradient Boosting (GBDT, GBRT or GBM) Library, for Python, R, Java, Scala, C and more. Runs on single machine, Hadoop, Spark, Dask, Flink and DataFlow项目地址: https://gitcode.com/gh_mirrors/xg/xgboost
XGBoost 提供了multi:softmax与multi:softprob两种多分类目标函数,用于处理类别数大于 2 的分类任务。本文以仓库内 demo/multiclass_classification 目录下的官方演示为核心骨架,结合 UCI Dermatology(皮肤病理学)数据集,完整讲解数据预处理、参数配置、训练与预测的 Python 与 R 双语言实现,并从 multiclass_obj.cc 源码层面剖析 softmax 梯度的计算与预测输出变换的底层原理,帮助读者掌握 XGBoost 多分类的标准工作流与踩坑点。
一、Demo 概览:一份可直接运行的官方多分类样例
在 XGBoost 仓库中,多分类演示位于 demo/multiclass_classification,其官方说明见 README.md。该目录包含四个文件:
| 文件 | 作用 |
|---|---|
| README.md | 演示说明:基于 UCI Dermatology 数据集完成多分类任务 |
| runexp.sh | 一键脚本:自动下载数据并调用 Python 训练脚本 |
| train.py | Python 版训练与评估脚本 |
| train.R | R 版等价实现(使用 data.table + xgboost) |
运行前提:需要先将 XGBoost Python 模块安装好,或在 python-package 目录下完成本地构建并加入PYTHONPATH(README 中的 "Make sure you make xgboost python module in ../../python" 即指此意)。当前版本的官方安装方式更推荐通过pip install xgboost安装发布版,或参考 doc/install.rst 从源码构建。
执行整个演示只需一条命令:
cd demo/multiclass_classification ./runexp.sh脚本 runexp.sh 的逻辑非常直白:若本地不存在dermatology.data,则用wget从 UCI 下载该数据集,随后调用python train.py完成训练与评估。如果下载失败,可手动下载数据集放到当前目录后重跑脚本。
二、数据集与预处理:理解 Dermatology 数据的特殊之处
UCI Dermatology 数据集包含 366 条样本、34 个特征(33 个数值特征 + 1 个年龄特征,其中部分特征含缺失值),目标变量是 6 种皮肤病的类别标签。该数据集有两个经典"坑",正是 train.py 预处理部分要解决的:
- 缺失值编码为
?字符串:数据中?表示缺失,Python 端通过np.loadtxt的converters参数在读取时将其转换为数值:
data = np.loadtxt('./dermatology.data', delimiter=',', converters={33: lambda x:int(x == '?'), 34: lambda x:int(x) - 1})这里converters={33: ...}处理第 34 列(索引 33)的年龄缺失值;{34: ...}将第 35 列(索引 34,即标签列)的类别编号从 1~6 转换为 0~5。
- 标签必须从 0 开始连续编号:XGBoost 多分类要求标签为
0到num_class - 1的离散整数。源码 multiclass_obj.cc 在训练首轮会调用MulticlassValidationKernel校验标签,不满足会抛出:
SoftmaxMultiClassObj: label must be discrete values in the range of [0, num_class).R 版在 train.R 中做了等价处理:用fread读取 CSV,将第 34 列的?转为 0,将第 35 列标签整体减 1:
df[, `:=`(V34 = as.integer(ifelse(V34 == "?", 0L, V34)), V35 = V35 - 1L)]数据切分
Python 端按 7:3 顺序切分(前 70% 训练、后 30% 测试),标签列取第 35 列(索引 34):
sz = data.shape train = data[:int(sz[0] * 0.7), :] test = data[int(sz[0] * 0.7):, :] train_X = train[:, :33] train_Y = train[:, 34] test_X = test[:, :33] test_Y = test[:, 34]R 端则使用随机采样(sample无放回抽取 70% 作为训练集)。注意:随机切分每次结果不同,误差率会有小幅波动,属正常现象。
三、核心参数配置:多分类目标函数与超参数
3.1 两种多分类目标:multi:softmax与multi:softprob
官方参数文档 doc/parameter.rst 对两者的定义:
multi:softmax:使用 softmax 目标做多分类,直接输出类别索引(即每个样本的预测类别编号)。使用它必须同时设置num_class。multi:softprob:与 softmax 相同,但输出ndata * nclass长度的向量(预测概率),可 reshape 成ndata * nclass矩阵,其中每个元素是该样本属于各类别的概率。
从源码看,两者本质上是同一个目标函数的两个注册实例。multiclass_obj.cc 中:
XGBOOST_REGISTER_OBJECTIVE(SoftmaxMultiClass, "multi:softmax") .describe("Softmax for multi-class classification, output class index.") .set_body([]() { return new SoftmaxMultiClassObj(false); }); XGBOOST_REGISTER_OBJECTIVE(SoftprobMultiClass, "multi:softprob") .describe("Softmax for multi-class classification, output probability distribution.") .set_body([]() { return new SoftmaxMultiClassObj(true); });二者的唯一区别是构造参数output_prob_:multi:softmax为false(输出类别索引),multi:softprob为true(输出概率分布)。SaveConfig中也据此写回目标名(见 multiclass_obj.cc)。
3.2 Demo 中的参数清单
train.py 的配置如下:
param = {} param['objective'] = 'multi:softmax' # 使用 softmax 多分类 param['eta'] = 0.1 # 学习率/步长收缩 param['max_depth'] = 6 # 树最大深度 param['nthread'] = 4 # 并行线程数 param['num_class'] = 6 # 类别总数(必须与标签范围一致)R 版 train.R 的等价配置:
params <- list( objective = 'multi:softmax', num_class = 6, max_depth = 6, nthread = 4, eta = 0.1 )各参数要点:
num_class:必填。定义输出类别数,对应源码SoftmaxMultiClassParam中声明(见 multiclass_param.h),其下界为 1:DMLC_DECLARE_FIELD(num_class).set_lower_bound(1).describe( "Number of output class in the multi-class classification.");训练时
GetGradient会校验preds.Size() == n_classes * info.labels.Size(),即预测向量长度必须等于样本数 × 类别数(multiclass_obj.cc)。eta(learning_rate):学习率,默认 0.3,demo 取 0.1 以获得更稳的收敛。max_depth:单棵树最大深度,默认 6。nthread:线程数,默认取系统核数。
此外,自 XGBoost 2.0 起新增multi_strategy参数(见 doc/parameter.rst),可选one_output_per_tree(默认,每个类别一棵树)或multi_output_tree(多目标树),后者可加速训练,详见 doc/tutorials/multioutput.rst。
3.3 默认评估指标
SoftmaxMultiClassObj的默认评估指标为mlogloss(多分类对数损失),见 multiclass_obj.cc。训练时的watchlist会同时打印训练集与测试集上的该指标,便于观察过拟合趋势。
四、训练与 watchlist 观测
两种目标共用同一套训练流程:
watchlist = [(xg_train, 'train'), (xg_test, 'test')] num_round = 5 bst = xgb.train(param, xg_train, num_round, watchlist)DMatrix是 XGBoost 的核心数据接口:xgb.DMatrix(train_X, label=train_Y)封装特征矩阵与标签(对应 Python 包 python-package/xgboost/data.py 中的DMatrix类)。watchlist用于在每轮迭代后同时评估训练集与验证集,训练日志会输出类似[0] train-mlogloss:... test-mlogloss:...的信息,用于监控收敛与过拟合。num_round = 5仅为演示而设的较小轮数;实际应用中建议增大迭代轮数并配合早停(early_stopping_rounds,见 python-package/xgboost/callback.py 与 R 包 R-package/R/callbacks.R)。
R 端训练等价写法:
bst <- xgb.train( params = params, data = xg_train, watchlist = watchlist, nrounds = 5 )五、预测与评估:软标签与硬标签两种范式
5.1multi:softmax:直接输出类别索引
pred = bst.predict(xg_test) error_rate = np.sum(pred != test_Y) / test_Y.shape[0] print('Test error using softmax = {}'.format(error_rate))预测结果是一维数组,每个元素为预测的类别编号(0~5),直接与test_Y比较即可计算误分类率。
5.2multi:softprob:输出概率并取 argmax
param['objective'] = 'multi:softprob' bst = xgb.train(param, xg_train, num_round, watchlist) # 输出是 1D 数组,需 reshape 为 (ndata, nclass) pred_prob = bst.predict(xg_test).reshape(test_Y.shape[0], 6) pred_label = np.argmax(pred_prob, axis=1) error_rate = np.sum(pred_label != test_Y) / test_Y.shape[0] print('Test error using softprob = {}'.format(error_rate))multi:softprob的原始输出是一维向量(ndata * nclass),必须 reshape 成(样本数, 类别数)后再按行取argmax得到预测类别。源码中的变换逻辑见 multiclass_obj.cc 的MulticlassTransformCpu:
probability == true时:对每个样本的 nclass 维预测向量做softmax(common::Softmax),得到概率分布;probability == false时:用common::FindMaxIndex找到每行最大值的索引,输出类别编号。
5.3 底层梯度原理:softmax 交叉熵的解析解
两种目标的训练过程完全相同,因为它们使用同一个MulticlassGradientCpu计算一阶、二阶梯度(multiclass_obj.cc)。对每个样本、每个类别 k,先计算数值稳定的 softmax 概率:
wmax = max_k(point(k)); // 数值稳定性:减去最大值防溢出 wsum = sum_k exp(point(k) - wmax); probability = exp(point(k) - wmax) / wsum;随后得到梯度(与 softmax 交叉熵损失的一阶/二阶导数完全对应):
grad = (label == k) ? probability - 1.0f : probability; // 一阶梯度 hess = max(2.0f * probability * (1.0f - probability) * weight, 1e-16f); // 二阶梯度其中hess下限被钳制在1e-16以防数值除零。正是这套梯度,使得每个类别对应一棵独立的回归树,每轮迭代生成num_class棵树(pred_leaf输出维度也与之对应,见 doc/prediction.rst)。
R 端 softprob 预测与评估的等价写法(注意 R 中矩阵按列填充,需byrow = TRUE):
pred_prob <- predict(bst, xg_test) pred_mat <- matrix(pred_prob, ncol = 6, byrow = TRUE) pred_label <- apply(pred_mat, 1, which.max) - 1L error_rate <- sum(pred_label != test_y) / length(test_y) print(paste("Test error using softprob =", error_rate))5.4 输出形状速查
| 目标函数 | predict 原始输出 | 含义 | 取类别方式 |
|---|---|---|---|
multi:softmax | 一维(ndata,) | 每样本的类别索引 | 直接用 |
multi:softprob | 一维(ndata*nclass,) | 展平的类别概率 | reshape 后argmax |
需注意 Python 中若设置strict_shape=True,multi:softprob会输出二维数组(ndata, nclass);R 包在strict_shape下返回 column-major 的 array,维度顺序与 numpy 相反(详见 doc/prediction.rst 与 doc/prediction.rst)。
六、扩展讨论:从 Demo 到生产实战
6.1 其他接口中的多分类用法
multi:softprob是更推荐的目标:它保留概率信息,可用于 ROC/AUC 评估、阈值调整与不确定性分析。官方 Spark 教程 doc/jvm/xgboost4j_spark_tutorial.rst 中同样使用"objective" -> "multi:softprob", "num_class" -> 3的组合;sklearn 接口的XGBClassifier也默认使用该目标并自动推断num_class(见 python-package/xgboost/sklearn.py)。需注意:多分类场景下 AUC 指标要求使用multi:softprob,因为multi:softmax不输出概率(doc/parameter.rst)。
6.2 测试佐证
仓库测试 tests/python/test_basic.py 中亦有{"max_depth": 2, "eta": 1, "num_class": 2}这类多分类参数组合的回归验证;模型导出/切片测试 tests/python/test_basic_models.py 则印证了"每轮生成num_parallel_tree * num_classes棵树"的树数量规律——理解这一点有助于掌握pred_leaf输出维度与模型切片的正确性。
6.3 常见踩坑清单
- 标签未归零:标签必须为
0 ~ num_class-1,否则训练直接报错(multiclass_obj.cc)。 - 忘记设置
num_class:缺省值为 1,会导致预测维度不匹配。 multi:softprob输出形状误解:默认一维展平,必须 reshape 为(样本数, 类别数)。- 多标签(multi-label)不支持:源码明确
CHECK_LE(info.labels.Shape(1), 1),多标签分类暂不支持(multiclass_obj.cc)。
七、小结
通过 demo/multiclass_classification 这份官方演示,读者可以完整走通 XGBoost 多分类的五个环节:数据清洗与标签归零 →DMatrix封装 →multi:softmax/multi:softprob目标与num_class配置 →xgb.train+watchlist训练监控 → 预测与误分类率评估。结合 multiclass_obj.cc 的源码可以看到,两种目标共享同一套 softmax 交叉熵梯度实现,仅在预测输出变换上分叉为"类别索引"与"概率分布"两种形式。将这套流程迁移到真实业务时,建议使用multi:softprob保留概率、增大迭代轮数并配合早停,再结合 doc/parameter.rst 与 doc/prediction.rst 深入调参。
【免费下载链接】xgboostScalable, Portable and Distributed Gradient Boosting (GBDT, GBRT or GBM) Library, for Python, R, Java, Scala, C and more. Runs on single machine, Hadoop, Spark, Dask, Flink and DataFlow项目地址: https://gitcode.com/gh_mirrors/xg/xgboost
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考