使用Scikit-learn训练HistGradientBoosting分类器
引言
在机器学习中,梯度提升决策树(GBDT)一直是处理表格数据的利器。然而,传统的 GradientBoostingClassifier 在面对大规模数据集时,训练速度往往不尽如人意。为了解决这一问题,Scikit-learn 在 0.21 版本中引入了 HistGradientBoostingClassifier 和 HistGradientBoostingRegressor。这两个基于直方图的梯度提升实现,灵感来源于 LightGBM,在保持出色预测性能的同时,大幅提升了训练效率。
本文将带你全面了解 HistGradientBoostingClassifier 的核心特性、关键参数,并通过一个完整的代码示例演示如何使用它训练分类模型。
为什么选择 HistGradientBoostingClassifier?
极致的训练速度
HistGradientBoostingClassifier 最显著的优势在于其训练速度。对于样本量超过一万的数据集,它比传统的 GradientBoostingClassifier 快几个数量级。这种速度提升源于其核心算法——直方图算法:模型在训练前将每个特征的值分箱为整数(默认 256 个分箱),从而极大地减少了需要评估的分裂点数量,并允许算法利用基于整数的数据结构(直方图)来构建树,而不是依赖排序后的连续值。
有实际测试表明,在百万级样本上训练时,GradientBoostingClassifier 需要约 3.2 分钟,而 HistGradientBoostingClassifier 仅需约 7 秒。
原生支持缺失值
处理缺失值通常是数据预处理中的一大难题,往往需要借助插补器(Imputer)来填充。而 HistGradientBoostingClassifier 内置了对缺失值(NaN)的原生支持。在训练过程中,树的学习器会在每个分裂点根据潜在增益自动学习含有缺失值的样本应该去向左子节点还是右子节点。如果在训练过程中某个特征从未遇到缺失值,那么预测时遇到缺失值则会被映射到样本数更多的那个子节点。
这一特性让数据预处理工作变得更加简洁高效。
原生支持分类特征
从 Scikit-learn 1.4 版本开始,HistGradientBoostingClassifier 可以原生处理 DataFrame 中的分类类型(categorical dtype)列。只需设置 categorical_features="from_dtype",模型就会自动将具有分类数据类型的列视为分类特征进行处理。相比传统的手动独热编码(One-Hot Encoding),这种方式训练速度更快,生成的树深度也更浅。
核心参数详解
下面是 HistGradientBoostingClassifier 的一些关键参数:
| 参数 | 默认值 | 说明 |
|---|---|---|
loss |
'log_loss' |
损失函数,二分类使用 logistic loss,多分类使用 categorical crossentropy |
learning_rate |
0.1 |
学习率(收缩系数),控制每棵树的贡献程度 |
max_iter |
100 |
boosting 的最大迭代次数,即树的数量 |
max_leaf_nodes |
31 |
每棵树的最大叶子节点数 |
max_depth |
None |
每棵树的最大深度,默认不限制 |
min_samples_leaf |
20 |
每个叶子节点的最小样本数 |
max_bins |
255 |
特征分箱的最大数量,不能超过 255 |
l2_regularization |
0.0 |
L2 正则化参数,用于防止过拟合 |
early_stopping |
'auto' |
是否启用早停,'auto' 表示自动启用 |
validation_fraction |
0.1 |
用于早停的验证集比例 |
n_iter_no_change |
10 |
早停的耐心值,连续多少次迭代验证集评分无提升则停止 |
categorical_features |
'from_dtype' |
指定分类特征 |
random_state |
None |
随机种子,用于结果复现 |
注意:早期版本(0.21-1.0)中,
loss参数支持'auto'、'binary_crossentropy'和'categorical_crossentropy',但这些选项在 1.1 版本中已被弃用,统一使用'log_loss'即可。
完整代码示例
下面通过一个完整的示例,演示如何使用 HistGradientBoostingClassifier 训练分类模型。
1. 导入必要的库
import numpy as np
import pandas as pd
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split, cross_val_score
from sklearn.ensemble import HistGradientBoostingClassifier
from sklearn.metrics import accuracy_score, classification_report, ConfusionMatrixDisplay
import matplotlib.pyplot as plt
2. 准备数据
这里使用 make_classification 生成一个模拟的二分类数据集:
# 生成模拟数据:10000个样本,20个特征
X, y = make_classification(
n_samples=10000,
n_features=20,
n_informative=15,
n_redundant=5,
random_state=42
)
# 人为引入一些缺失值,演示原生缺失值处理能力
rng = np.random.RandomState(42)
missing_mask = rng.random(X.shape) < 0.05
X[missing_mask] = np.nan
# 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42
)
print(f"训练集大小: {X_train.shape[0]} 样本")
print(f"测试集大小: {X_test.shape[0]} 样本")
print(f"特征数量: {X_train.shape[1]}")
print(f"训练集中缺失值数量: {np.isnan(X_train).sum()}")
3. 创建并训练模型
# 创建 HistGradientBoostingClassifier
model = HistGradientBoostingClassifier(
max_iter=100, # 100棵树
learning_rate=0.1, # 学习率
max_leaf_nodes=31, # 每棵树最大叶子节点数
min_samples_leaf=20, # 叶子节点最小样本数
max_bins=255, # 分箱数量
l2_regularization=0.1, # 轻微L2正则化防止过拟合
early_stopping='auto', # 自动早停
validation_fraction=0.1, # 10%的数据用于早停验证
n_iter_no_change=10, # 连续10次无提升则停止
random_state=42,
verbose=1 # 打印训练进度
)
# 训练模型
model.fit(X_train, y_train)
4. 模型评估
# 在测试集上预测
y_pred = model.predict(X_test)
y_pred_proba = model.predict_proba(X_test)
# 计算准确率
accuracy = accuracy_score(y_test, y_pred)
print(f"测试集准确率: {accuracy:.4f}")
# 详细分类报告
print("\n分类报告:")
print(classification_report(y_test, y_pred))
# 绘制混淆矩阵
ConfusionMatrixDisplay.from_estimator(model, X_test, y_test)
plt.title("混淆矩阵")
plt.show()
5. 交叉验证
# 使用交叉验证评估模型稳定性
cv_scores = cross_val_score(model, X, y, cv=5, scoring='accuracy')
print(f"5折交叉验证准确率: {cv_scores.mean():.4f} (+/- {cv_scores.std():.4f})")
6. 特征重要性
# 查看特征重要性
importances = model.feature_importances_
feature_names = [f"feature_{i}" for i in range(X.shape[1])]
# 排序并显示最重要的特征
indices = np.argsort(importances)[::-1]
print("\n特征重要性排名:")
for i in range(min(10, len(indices))):
print(f" {i+1}. {feature_names[indices[i]]}: {importances[indices[i]]:.4f}")
# 可视化特征重要性
plt.figure(figsize=(10, 6))
plt.bar(range(10), importances[indices[:10]])
plt.xticks(range(10), [feature_names[i] for i in indices[:10]], rotation=45)
plt.title("Top 10 特征重要性")
plt.tight_layout()
plt.show()
处理分类特征的示例
如果你的数据中包含分类特征,可以这样处理:
# 假设数据中包含分类列
# 方式一:将列转换为 category 类型,并设置 categorical_features="from_dtype"
df['category_col'] = df['category_col'].astype('category')
model = HistGradientBoostingClassifier(
categorical_features="from_dtype",
random_state=42
)
# 方式二:通过布尔掩码或索引指定
model = HistGradientBoostingClassifier(
categorical_features=[True, False, True, ...], # 与特征数量对应
random_state=42
)
# 方式三:通过列名指定(适用于 DataFrame)
model = HistGradientBoostingClassifier(
categorical_features=["col1", "col3"],
random_state=42
)
使用独立的验证集进行早停
从 Scikit-learn 1.7 版本开始,fit 方法支持直接传入独立的验证集用于早停:
# X_train, y_train: 训练集
# X_val, y_val: 独立的验证集
model.fit(X_train, y_train, X_val=X_val, y_val=y_val)
- 点赞
- 收藏
- 关注作者
评论(0)