scikit-learn的cross_validate返回NaN评估分数是什么原因?
问题原因
cross_validate返回NaN的核心原因是交叉验证拆分后单折运行报错/指标无法计算,具体对应你代码里的几个问题:
- 未知类别导致编码环节报错:你使用的
OneHotEncoder默认参数为handle_unknown='error',交叉验证拆分训练集、验证集后,如果验证集中的County、Deliveryperiod等onehot编码特征出现了当前训练拆分里不存在的类别,编码环节会直接抛出错误,对应折的评分就会返回NaN。
你调大折数、重复次数时偶尔能得到有效值,是因为折数越大每折的训练集占比越高,覆盖所有类别的概率更高,拆分是随机的所以无法复现该现象。 - 分类指标计算时分母为0:如果你的数据集本身样本量较小,欠采样后拆分的某折验证集可能出现某类样本数为0、或是模型预测结果全为同一类的情况,此时precision、f1等指标会因为除数为0返回NaN,scikit-learn默认会返回NaN而不是中断运行。
- 隐藏问题:数据泄露:你把随机欠采样放在交叉验证流程外部,欠采样用到了所有样本的标签信息,会导致最终评分虚高,虽然这不是返回NaN的直接原因,但属于流程逻辑错误。
修复方案
- 修改OneHotEncoder参数,遇到未知类别时不报错,对应位置修改为:
('onehot_cat', OneHotEncoder(handle_unknown='ignore'), onehot_cat),
- 给分类指标添加零除法处理,避免指标计算失败返回NaN,先导入依赖,再修改scoring定义:
from sklearn.metrics import make_scorer, f1_score, precision_score, recall_score scoring = {'acc': 'accuracy', 'f1': make_scorer(f1_score, zero_division=0), 'prec': make_scorer(precision_score, zero_division=0), 'rec': make_scorer(recall_score, zero_division=0)}
- 将随机欠采样移动到Pipeline内部,避免数据泄露:
- 删除最外层的
X, y = RandomUnderSampler(random_state=2).fit_resample(X,y)代码 - 修改full_pipeline定义,加入欠采样步骤:
full_pipeline = Pipeline([ ('encode',encode_pipeline), ('scale', StandardScaler()), ('undersample', RandomUnderSampler(random_state=2)), ('model', model) ])
内容的提问来源于stack exchange,提问作者J Xkcd
相关产品推荐
相关产品推荐

