XGBoost 多分类实战指南:基于 UCI Dermatology 数据集的 multi:softmax 与 multi:softprob 详解
2026/9/19 8:15:53 网站建设 项目流程

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:softmaxmulti: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.pyPython 版训练与评估脚本
train.RR 版等价实现(使用 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 预处理部分要解决的:

  1. 缺失值编码为?字符串:数据中?表示缺失,Python 端通过np.loadtxtconverters参数在读取时将其转换为数值:
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。

  1. 标签必须从 0 开始连续编号:XGBoost 多分类要求标签为0num_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:softmaxmulti: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:softmaxfalse(输出类别索引),multi:softprobtrue(输出概率分布)。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 维预测向量做softmaxcommon::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=Truemulti: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 常见踩坑清单

  1. 标签未归零:标签必须为0 ~ num_class-1,否则训练直接报错(multiclass_obj.cc)。
  2. 忘记设置num_class:缺省值为 1,会导致预测维度不匹配。
  3. multi:softprob输出形状误解:默认一维展平,必须 reshape 为(样本数, 类别数)
  4. 多标签(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),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询