GridSearchCV未采用XGBRegressor指定评分方法的问题及解决需求
你遇到的问题核心在于:GridSearchCV默认使用的是XGBRegressor自带的score()方法,而这个方法返回的是R²分数,和你在fit()里指定的eval_metric完全无关。
你在reg.fit()中传入的eval_metric只是告诉XGBoost在训练过程中监控哪个指标,但并不会改变GridSearchCV用来评估模型性能的逻辑——当scoring=None时,它只会调用模型的score()方法,而XGBRegressor的score()方法(继承自sklearn的回归器接口)默认返回的是决定系数R²,所以不管你传mae还是rmse,得到的测试分数都是同一个R²值。
要让GridSearchCV返回与eval_metric对应的结果,你需要显式指定scoring参数,或者自定义评分函数,让GridSearchCV用你想要的指标来评估模型。下面是两种可行的方法:
方法1:使用sklearn内置的负向评分器
sklearn的评分器默认遵循"分数越高越好"的逻辑,而MAE/RMSE是误差指标(数值越小模型越好),所以需要使用它们的负向版本:
- MAE对应
neg_mean_absolute_error - RMSE对应
neg_root_mean_squared_error
修改你的GridSearchCV初始化代码,加上scoring参数即可:
import numpy as np from sklearn.model_selection import GridSearchCV, KFold from sklearn.datasets import load_boston import xgboost as xgb rng = np.random.RandomState(31337) boston = load_boston() y = boston['target'] X = boston['data'] kf = KFold(n_splits=2, random_state=42, shuffle=True) # 加shuffle让划分更合理,可选 folds = list(kf.split(X)) # 针对MAE的GridSearchCV xgb_model = xgb.XGBRegressor(objective='reg:squarederror', verbose=False) reg_mae = GridSearchCV( estimator=xgb_model, param_grid={'max_depth': [2], 'n_estimators': [50]}, cv=folds, verbose=False, scoring='neg_mean_absolute_error' # 指定MAE的负向评分 ) reg_mae.fit(X, y, **{'eval_metric': 'mae', 'verbose': False}) # 取负数得到实际MAE值 print('GridSearchCV mean(mae): ', -reg_mae.cv_results_['mean_test_score'][0]) # 针对RMSE的GridSearchCV reg_rmse = GridSearchCV( estimator=xgb_model, param_grid={'max_depth': [2], 'n_estimators': [50]}, cv=folds, verbose=False, scoring='neg_root_mean_squared_error' # 指定RMSE的负向评分 ) reg_rmse.fit(X, y, **{'eval_metric': 'rmse', 'verbose': False}) # 取负数得到实际RMSE值 print('GridSearchCV mean(rmse): ', -reg_rmse.cv_results_['mean_test_score'][0])
方法2:自定义评分函数(更灵活)
如果需要完全匹配XGBoost计算的eval_metric结果,你可以用make_scorer自定义评分函数,适配自己的计算逻辑:
from sklearn.metrics import make_scorer, mean_absolute_error, mean_squared_error # 自定义MAE评分函数 def xgb_mae(y_true, y_pred): return mean_absolute_error(y_true, y_pred) # 自定义RMSE评分函数 def xgb_rmse(y_true, y_pred): return np.sqrt(mean_squared_error(y_true, y_pred)) # 使用自定义评分器 reg_mae_custom = GridSearchCV( estimator=xgb_model, param_grid={'max_depth': [2], 'n_estimators': [50]}, cv=folds, verbose=False, scoring=make_scorer(xgb_mae, greater_is_better=False) # MAE越小越好,所以设为False ) reg_mae_custom.fit(X, y, **{'eval_metric': 'mae', 'verbose': False}) print('GridSearchCV mean(mae) (custom): ', -reg_mae_custom.cv_results_['mean_test_score'][0]) reg_rmse_custom = GridSearchCV( estimator=xgb_model, param_grid={'max_depth': [2], 'n_estimators': [50]}, cv=folds, verbose=False, scoring=make_scorer(xgb_rmse, greater_is_better=False) ) reg_rmse_custom.fit(X, y, **{'eval_metric': 'rmse', 'verbose': False}) print('GridSearchCV mean(rmse) (custom): ', -reg_rmse_custom.cv_results_['mean_test_score'][0])
修改后,你会得到和XGBRegressor单独计算的MAE/RMSE平均值一致的结果:
GridSearchCV mean(mae): 1.1392865 GridSearchCV mean(rmse): 1.469283
这和你单独计算的(1.273626 + 1.004947)/2 ≈ 1.139、(1.647694 + 1.290872)/2 ≈ 1.469完全匹配。
内容的提问来源于stack exchange,提问作者Geir Inge

