使用sklearn指标作为XGBClassifier评估指标触发ValueError问题
问题:XGBClassifier搭配sklearn评估指标与验证集训练时触发类型不匹配错误
复现代码
import numpy as np from xgboost import XGBClassifier from sklearn.metrics import accuracy_score x_train, y_train = np.random.rand(10,3), np.where(np.random.rand(10,)>0.5, 1, 0) x_valid, y_valid = np.random.rand(5,3), np.where(np.random.rand(5,)>0.5, 1, 0) model = XGBClassifier( n_estimators=100, eval_metric=accuracy_score ) model.fit( X=x_train, y=y_train, eval_set=[(x_train, y_train), (x_valid, y_valid)] )
报错信息
--------------------------------------------------------------------------- ValueError Traceback (most recent call last) <ipython-input-5-b63cd5cfabda> in <cell line: 1>() ----> 1 model.fit( 2 X=x_train, y=y_train, 3 eval_set=[(x_train, y_train), (x_valid, y_valid)] 4 ) 9 frames /usr/local/lib/python3.10/dist-packages/sklearn/metrics/_classification.py in _check_targets(y_true, y_pred) 93 94 if len(y_type) > 1: ---> 95 raise ValueError( 96 "Classification metrics can't handle a mix of {0} and {1} targets".format( 97 type_true, type_pred ValueError: Classification metrics can't handle a mix of binary and continuous targets
错误原因
直接将sklearn的分类指标(如accuracy_score)传给XGBoost的eval_metric参数时,XGBoost会把模型输出的连续概率值(二分类场景下是样本属于正类的概率)传入sklearn指标函数,但sklearn的分类指标要求预测值是离散的类别标签(0/1),而非连续概率,因此触发了「binary真实标签与continuous预测值不匹配」的错误。
当使用eval_metric="error"时,XGBoost内部会自动处理概率到标签的转换,因此可以正常运行;注释eval_set时,验证逻辑不执行,自然也不会触发错误。
解决方法
自定义评估函数,先将XGBoost输出的概率转换为类别标签,再调用sklearn的指标函数。XGBoost要求自定义eval_metric的函数签名为(y_true, y_pred) -> (metric_name, metric_value, is_higher_better),其中:
y_true是真实标签数组y_pred是模型输出的概率数组(二分类下形状为(n_samples,),多分类下为(n_samples, n_classes))
示例代码
1. 二分类场景适配(支持accuracy、balanced_accuracy、recall)
import numpy as np from xgboost import XGBClassifier from sklearn.metrics import accuracy_score, balanced_accuracy_score, recall_score # 自定义准确率评估函数 def custom_accuracy(y_true, y_pred): # 二分类下将概率转换为0/1标签(阈值设为0.5) y_pred_labels = (y_pred > 0.5).astype(int) return 'accuracy', accuracy_score(y_true, y_pred_labels), True # 自定义平衡准确率评估函数 def custom_balanced_accuracy(y_true, y_pred): y_pred_labels = (y_pred > 0.5).astype(int) return 'balanced_accuracy', balanced_accuracy_score(y_true, y_pred_labels), True # 自定义召回率评估函数(默认针对正类1) def custom_recall(y_true, y_pred): y_pred_labels = (y_pred > 0.5).astype(int) return 'recall', recall_score(y_true, y_pred_labels), True x_train, y_train = np.random.rand(10,3), np.where(np.random.rand(10,)>0.5, 1, 0) x_valid, y_valid = np.random.rand(5,3), np.where(np.random.rand(5,)>0.5, 1, 0) model = XGBClassifier( n_estimators=100, # 可传入多个自定义评估函数,以列表形式传入 eval_metric=[custom_accuracy, custom_balanced_accuracy, custom_recall] ) model.fit( X=x_train, y=y_train, eval_set=[(x_train, y_train), (x_valid, y_valid)] )
2. 多分类场景扩展
如果是多分类任务,需要将y_pred转换为类别标签时使用np.argmax(y_pred, axis=1),例如:
def custom_multi_accuracy(y_true, y_pred): y_pred_labels = np.argmax(y_pred, axis=1) return 'multi_accuracy', accuracy_score(y_true, y_pred_labels), True
注意事项
- 自定义函数的返回值必须是三元组:(指标名称字符串, 指标数值, 是否越高越好的布尔值)
- 概率转标签的阈值(如0.5)可根据业务需求调整
- 多分类场景下,sklearn的指标可能需要指定
average参数,例如recall_score(y_true, y_pred_labels, average='macro')
内容的提问来源于stack exchange,提问作者paquique
相关产品推荐
相关产品推荐

