xgboost.QuantileDMatrix分位数变换的数学定义及异常行为问询
XGBoost的xgboost.QuantileDMatrix函数支持接收numpy.ndarray或pandas.DataFrame作为输入,通过分位数变换将数据转换为稀疏格式存储以提升性能。按照预期,当参数max_bin设置为大于等于输入样本数(max_bin >= number_of_samples)时,每个数据点应该由自身的中位数表示,分位数变换理论上不会改变原始数据。但实际通过QuantileDMatrix.get_data().data查看数据时,发现每个特征的最低值都会被替换为一个完全不同的数值。
需要明确:
xgboost.QuantileDMatrix的实际工作机制是什么?- 该量化过程的数学定义是怎样的?
复现代码
import xgboost as xgb import pandas as pd import numpy as np # 用numpy定义数据 feature1 = np.array([1,2,3,4]) # 转换为pandas DataFrame a = pd.DataFrame({'feature1': feature1}) quantized_a = xgb.QuantileDMatrix(a, max_bin = 4) # 验证numpy和pandas输入的表现一致 quantized_feature1 = xgb.QuantileDMatrix(feature1.reshape(-1, 1), max_bin = 4) print(quantized_a.get_data().data) print(quantized_feature1.get_data().data) # 输出: [-1.e-05, 2.e+00, 3.e+00, 4.e+00 ] # 另一组数据同样出现问题 feature2 = np.array([10399., 34552., -48585., 70.]) quantized_feature2 = xgb.QuantileDMatrix(feature2.reshape(-1, 1), max_bin = 4) print(quantized_feature2.get_data().data) np.testing.assert_almost_equal(feature2, quantized_feature2.get_data().data) # 断言失败:数组在7位小数精度下不相等 # 不匹配元素:1 / 4 (25%) # 最大绝对差:48585. # 最大相对差:0.5 # x: array([ 10399., 34552., -48585., 70.]) # y: array([ 1.0399e+04, 3.4552e+04, -9.7170e+04, 7.0000e+01], dtype=float32) # 这里最低值-48585被修改;如果把所有值改为正数,被修改的会是新的最低值70
环境要求
xgboost>=1.7.6 numpy>=1.23.5 pandas>=1.5.7
解答
1. QuantileDMatrix的实际工作机制
QuantileDMatrix的核心目标是通过分位数分箱将连续特征转换为离散化表示,同时保持数据稀疏性以优化XGBoost的训练效率,工作流程如下:
- 分位数计算:对每个特征,基于样本数据计算指定数量的分位数(由
max_bin控制),这些分位数构成分箱边界。 - 数据映射:将每个原始数据点映射到对应分箱,存储分箱的代表值(通常是分箱中位数或边界值)。
- 稀疏存储:仅存储非默认值的映射结果,减少内存占用并加速后续树构建过程。
你观察到的“最低值被修改”现象,源于QuantileDMatrix对最小分箱下界的额外处理:为避免分箱边界与样本值完全重合导致的边界判断歧义,XGBoost会将最小分箱的下界设置为原始最小值减去微小偏移量(或基于数据范围的扩展值)。当max_bin等于样本数时,每个样本理论上对应一个分箱,但最小分箱的下界调整会导致该样本的映射值被替换为扩展后的下界,而非原始值。
2. 量化过程的数学定义
QuantileDMatrix的量化基于分位数分箱,数学描述如下:
对于单个特征的样本集合 ( X = {x_1, x_2, ..., x_n} ),先排序得到 ( X_{sorted} = {x_{(1)}, x_{(2)}, ..., x_{(n)}} ),其中 ( x_{(1)} \leq x_{(2)} \leq ... \leq x_{(n)} )。
当设置max_bin = k时:
- 计算 ( k-1 ) 个分位数点 ( q_1, q_2, ..., q_{k-1} ),满足第 ( i ) 个分位数 ( q_i ) 对应至少 ( \frac{i}{k} \times 100% ) 的样本小于等于它。
- 构建 ( k ) 个分箱:( (-\infty, q_1], (q_1, q_2], ..., (q_{k-1}, +\infty) )。实际实现中,XGBoost会将第一个分箱的下界调整为 ( x_{(1)} - \epsilon )(( \epsilon ) 为极小正数,如 ( 1e-5 ))或数据范围扩展值(如第二个例子中的 ( -97170 = -48585 \times 2 )),确保最小样本点能被正确包含。
- 每个样本 ( x_j ) 被映射到对应分箱,使用分箱代表值作为量化后的值。当
max_bin = n(样本数)时,除第一个分箱的代表值被调整为扩展下界,其余分箱的代表值就是样本本身。
最低值被替换的原因
测试案例中max_bin = 4等于样本数,XGBoost为第一个分箱设置了比原始最小值更小的下界,并将该分箱的代表值设为这个下界而非原始最小值。这种设计是为了处理后续可能出现的、比训练样本最小值更小的输入数据(如在线预测场景),确保这类数据能被正确映射到第一个分箱中。
内容的提问来源于stack exchange,提问作者phydev

