XGBoost稀疏矩阵训练后转数组预测结果不一致问题
问题复现
用于预测供应商类别的实现代码如下:
count_vect = CountVectorizer(ngram_range = (1,2), min_df = 5, binary = True) X_train_counts = count_vect.fit_transform(X_train) clf = XGBClassifier().fit(X_train_counts, y_train) my_data = pd.Series(['ebay', 'amazon', 'costco', 'uber']) sparse_count = count_vect.transform(my_data) print(clf.predict(sparse_count)) print(clf.predict(sparse_count.toarray()))
代码运行输出为:
[19 16 1 18] [20 20 20 20]
观测到的现象:
- 直接传入稀疏矩阵得到的预测结果符合预期,将特征转为稠密数组传入
predict方法后,预测结果出现显著异常 - 如果训练前先将稀疏矩阵转为数组再训练模型,后续预测时无论传入稀疏矩阵还是数组,得到的预测结果都完全一致,但该过程训练耗时会大幅增加
根本原因
该现象由XGBoost对稀疏矩阵、稠密数组的默认缺失值处理逻辑不匹配导致:
- XGBClassifier的missing参数默认值为
np.nan,作用是指定被判定为缺失值的特征取值。当输入scipy格式稀疏矩阵进行训练时,XGBoost会将稀疏矩阵中未显式存储的隐式0值,全部识别为missing参数定义的缺失值,按照缺失值分裂规则训练树模型。
代码中CountVectorizer设置了binary=True,输出的稀疏矩阵里仅存储了文本中出现过的n-gram对应位置(值为1),所有未出现的n-gram位置都是隐式0,这部分在训练时被XGBoost全部当作缺失值处理,模型学到的分裂规则是基于「出现的词特征值为1,其余特征为缺失」的逻辑。 - 预测阶段如果直接传入稀疏矩阵,输入处理逻辑和训练阶段完全对齐:隐式0被识别为缺失值,因此预测结果正常。
- 预测阶段如果传入
.toarray()转换后的稠密数组,所有原隐式0都会变成显式的数值0,这些0不是np.nan,不会被识别为缺失值,会被模型当作真实的特征取值0参与计算,和训练时的特征逻辑完全错位,因此输出异常结果。 - 如果训练阶段就把稀疏矩阵转为稠密数组输入,所有0都是显式数值,XGBoost训练时仅会将
np.nan识别为缺失值,0属于正常特征取值。后续预测时,无论传入稠密数组(显式0为正常特征)还是稀疏矩阵(隐式0会被XGBoost自动填充为数值0,匹配训练逻辑),结果自然保持一致。
处理方案
- 优先保证训练和预测阶段的输入格式统一,不要混用稀疏、稠密输入,从根源避免逻辑错位
- 如果需要兼容两种输入格式,可以在初始化XGBClassifier时显式设置
missing=0,让模型把0值统一识别为缺失值,对齐稀疏矩阵训练时的特征逻辑;注意该设置仅适配当前二值化文本特征场景,普通数值特征不要随意修改。
内容的提问来源于stack exchange,提问作者L Xandor
相关产品推荐
相关产品推荐

