如何提高 XGBoost Pipeline 的分类准确率并正确重训?
当前 Pipeline 的结构基本合理。将预处理与模型放在一起拟合,可以让缺失值填补和类别编码只从当前训练数据中学习。要提高准确率,建议先检查数据划分和特征含义,再用交叉验证比较参数配置。由于没有数据集和当前分数,无法保证某项调整一定有效。
确定预处理和模型参数后,常见做法是用全部带标签的训练数据重新拟合模型,再预测未标注的测试集。这样最终模型可以使用更多训练样本,但准确率不一定会提高。此前的验证集分数也不能作为全量重训后模型的独立测试结果。
先建立可靠的验证方式
原代码只做了一次随机划分,没有使用 stratify=y。如果样本相互独立,且训练数据与预测数据的分布相近,可以先采用分层划分,尽量保持训练集和验证集的类别比例一致。
建议保留一份独立验证集,在其余训练数据上使用 StratifiedKFold 搜索参数。确定配置后,再用这份验证集检查结果。不要根据同一验证集反复修改特征、参数和阈值,否则分数会逐渐偏向这份数据。
使用分层随机划分时,还要注意以下情况:
- 如果同一患者有多条记录,应按患者分组,避免同一患者同时出现在训练集和验证集。
- 如果任务是根据历史数据预测未来,应按照时间划分。
- 如果少数类别的样本很少,需要减少交叉验证折数,确保各折有足够的样本。
eval_metric='logloss' 指定的是 XGBoost 的评估指标,不会改变外部使用的 accuracy_score。原代码没有传入 eval_set 或设置 early stopping,因此也不会因为指定了 logloss 就自动提前结束训练。
检查特征类型和预处理
对于默认的树模型,StandardScaler 通常不是必需的。可以移除它,再比较交叉验证结果,但不要预期仅靠取消标准化就能明显提高准确率。
原代码中还有几处需要检查:
- 数值填补实际使用的是
strategy='median',即中位数;对应注释中的 mean 与代码不一致。 select_dtypes只根据存储类型选择列。用整数表示的类别仍会进入数值分支,应根据字段含义手动调整。- 当前选择条件没有覆盖布尔列、日期列等所有可能的数据类型。
ColumnTransformer默认丢弃未选中的列,需要确认是否遗漏了有效特征。 OneHotEncoder(handle_unknown='ignore')可以处理预测时出现的新类别,但新类别在该特征对应的编码列中会全部为零。
特征工程应围绕字段含义展开。例如,清理类别拼写差异、统一缺失值表示,或在有业务依据时构造比例和组合特征。使用的特征都应在实际预测时可以获得,避免使用由目标结果推导出来的信息。
需要从数据中学习统计量的特征处理,应放入 Pipeline,在每个训练折中单独拟合。
用交叉验证搜索参数
可以优先比较以下参数:
| 参数 | 主要影响 |
|---|---|
n_estimators、learning_rate | 树的数量和每轮更新幅度,需要结合比较 |
max_depth、min_child_weight | 树的复杂度及继续分裂的条件 |
subsample、colsample_bytree | 每轮使用的样本与特征比例 |
reg_alpha、reg_lambda | 正则化强度 |
下面的代码可以替换原代码中从数据划分到生成提交文件的部分,沿用已经定义的 preprocessor、X、y 和测试数据变量。代码先保留独立验证集,在训练部分搜索参数,最后使用选定配置进行全量重训。
示例假设样本可以进行分层随机划分、每个类别的训练样本足以支持 5 折交叉验证,且目标标签符合 XGBClassifier 的要求。参数范围仅作为搜索起点。
from sklearn.base import clone
from sklearn.model_selection import (
train_test_split,
StratifiedKFold,
RandomizedSearchCV,
)
X_train, X_val, y_train, y_val = train_test_split(
X,
y,
test_size=0.2,
random_state=42,
stratify=y,
)
pipeline = Pipeline(steps=[
('preprocessor', clone(preprocessor)),
('classifier', XGBClassifier(
random_state=42,
eval_metric='logloss',
n_jobs=1,
)),
])
param_distributions = {
'classifier__n_estimators': [150, 300, 600],
'classifier__learning_rate': [0.03, 0.05, 0.1],
'classifier__max_depth': [2, 3, 4, 5],
'classifier__min_child_weight': [1, 3, 5, 10],
'classifier__subsample': [0.7, 0.85, 1.0],
'classifier__colsample_bytree': [0.7, 0.85, 1.0],
'classifier__reg_alpha': [0.0, 0.1, 1.0],
'classifier__reg_lambda': [1.0, 3.0, 10.0],
}
cv = StratifiedKFold(
n_splits=5,
shuffle=True,
random_state=42,
)
search = RandomizedSearchCV(
estimator=pipeline,
param_distributions=param_distributions,
n_iter=30,
scoring='accuracy',
cv=cv,
refit=True,
random_state=42,
n_jobs=-1,
error_score='raise',
)
search.fit(X_train, y_train)
print('Best parameters:', search.best_params_)
print(f'Best CV accuracy: {search.best_score_:.4f}')
# refit=True has already fitted this pipeline on X_train.
validation_model = search.best_estimator_
val_preds = validation_model.predict(X_val)
print(
f'Holdout accuracy: '
f'{accuracy_score(y_val, val_preds):.4f}'
)
# Fit the selected configuration on all labeled training data.
final_model = clone(validation_model)
final_model.fit(X, y)
final_predictions = final_model.predict(X_test_submission)
submission = pd.DataFrame({
ID_COL: test_ids,
TARGET_COL: final_predictions,
})
submission.to_csv('classification_submission.csv', index=False)
这里使用 classifier__参数名 设置 Pipeline 中分类器的参数。搜索器并行运行不同的拟合任务,分类器则设置 n_jobs=1,以免两层同时大量占用 CPU。内存不足时,应降低搜索器的并行数。
best_score_ 是搜索过程中选出的最佳交叉验证均值,可能因模型选择而偏高。独立验证集可以提供另一份检查依据,但如果持续依据它调整方案,它也会参与模型选择。
根据评估目标决定下一步
如果最终评价指标确实是 accuracy,上面的 scoring='accuracy' 就与目标一致。还应查看类别分布和混淆矩阵,判断模型是否主要依靠预测多数类获得高分。
如果关注某一类别的漏判情况,可以补充查看该类别的 recall、precision,以及适合任务的其他指标。不要在没有比较结果的情况下直接启用类别权重:它可能改善少数类识别,也可能降低总体 accuracy。
对于二分类,调整概率阈值有时会改变 accuracy,但阈值也需要验证。应使用训练内部的验证预测选择阈值,并保留独立数据检查效果。
如果使用 early stopping,需要注意传入 XGBoost 的 eval_set 特征也必须经过相同的预处理。不能假定普通 Pipeline 会自动转换额外传入的验证数据。具体接入方式取决于所用版本和配置。上面的搜索代码通过比较 n_estimators 控制树的数量,没有启用 early stopping。
全量重训前需要固定哪些内容
全量重训前,应确定特征集合、预处理规则、超参数,以及可能使用的分类阈值。随后重新拟合整个 Pipeline,让填补器、编码器和分类器都使用完整的带标签训练数据。
此时原验证集已经参与最终训练,不能再用于评价最终模型的泛化表现。如果需要独立报告最终模型的效果,应另外保留一份未参与模型选择和训练的带标签测试集。
还要检查目标标签。如果标签是字符串或非连续整数,需要先映射为符合分类器要求的类别编号,生成提交文件时再映射回原始标签。还应确认测试集包含模型需要的特征列、字段类型一致,并检查提交要求使用类别标签还是预测概率。
备注:内容仅供参考。
