You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在sklearn中为训练集未出现的标签模拟零概率预测?

时间序列拆分下多分类ROC AUC评分:训练集缺失标签导致predict_proba维度不匹配的解决办法

问题说明

我正在进行标签为[-1, 0, 1]的3分类任务,使用以下代码配置ROC AUC评分器:

make_scorer(roc_auc_score, needs_proba=True, average="macro", multi_class='ovo', labels=[-1, 0, 1])

由于采用时间序列拆分器划分训练/测试数据(无法打乱顺序,无stratify参数),某一拆分的训练数据完全不含0标签,导致模型.fit后仅识别到2个标签,predict_proba仅输出2列概率结果。执行评分时触发ValueError:

Number of given labels, 3, not equal to the number of columns in 'y_score', 2

我希望模型对缺失的0类输出零概率,同时寻求其他可行的解决方案。


解决方案

1. 包装predict_proba方法,补充缺失标签的零概率列

这是最直接的处理方式,通过自定义模型包装器,强制predict_proba输出与目标标签数量一致的结果:

from sklearn.base import BaseEstimator, ClassifierMixin
import numpy as np

class LabelCompleteClassifier(BaseEstimator, ClassifierMixin):
    def __init__(self, base_estimator, target_labels):
        self.base_estimator = base_estimator
        self.target_labels = target_labels  # 完整的目标标签列表,如[-1,0,1]
    
    def fit(self, X, y):
        self.base_estimator.fit(X, y)
        self.trained_labels_ = self.base_estimator.classes_  # 记录训练时识别到的标签
        return self
    
    def predict_proba(self, X):
        proba = self.base_estimator.predict_proba(X)
        # 初始化全零概率矩阵,匹配目标标签的列数
        full_proba = np.zeros((proba.shape[0], len(self.target_labels)))
        
        # 将训练得到的概率映射到对应标签的列
        for idx, label in enumerate(self.trained_labels_):
            target_idx = self.target_labels.index(label)
            full_proba[:, target_idx] = proba[:, idx]
        
        # 缺失标签的列保持零概率
        return full_proba
    
    # 复用基础模型的predict方法
    def predict(self, X):
        return self.base_estimator.predict(X)

使用示例:

# 初始化你的基础模型,例如RandomForestClassifier
base_model = RandomForestClassifier()
# 包装模型并传入完整目标标签
model = LabelCompleteClassifier(base_model, target_labels=[-1,0,1])

# 后续正常调用model.fit()、model.predict_proba()即可

2. 调整时间序列拆分逻辑,确保训练集覆盖全部标签

在拆分阶段提前检查训练集的标签覆盖情况,若未包含所有目标标签,则扩大训练窗口(增加训练数据量),直到覆盖全部3个标签:

from sklearn.model_selection import TimeSeriesSplit
import numpy as np

def valid_time_series_split(X, y, n_splits=5, target_labels=[-1,0,1]):
    tscv = TimeSeriesSplit(n_splits=n_splits)
    valid_splits = []
    
    for train_idx, test_idx in tscv.split(X):
        y_train = y[train_idx]
        # 检查训练集是否包含所有目标标签
        if set(target_labels).issubset(set(y_train)):
            valid_splits.append((train_idx, test_idx))
        else:
            # 逐步扩大训练窗口,直到覆盖全部标签
            expanded_train_idx = train_idx.copy()
            while not set(target_labels).issubset(set(y[expanded_train_idx])):
                # 往前多取一个样本(可根据时间序列粒度调整步长)
                if expanded_train_idx[0] > 0:
                    expanded_train_idx = np.insert(expanded_train_idx, 0, expanded_train_idx[0]-1)
                else:
                    break  # 无法再扩大则跳过该拆分
            if set(target_labels).issubset(set(y[expanded_train_idx])):
                valid_splits.append((expanded_train_idx, test_idx))
    
    return valid_splits

该方案从源头避免了训练集缺失标签的问题,保证后续模型训练和评分的维度一致性。

3. 动态调整评分器的标签参数(不推荐)

如果确实无法避免训练集缺失标签的情况,可以在评分时临时调整labels参数,仅传入训练集实际存在的标签。但这种做法会导致不同拆分的ROC AUC评分标准不一致,削弱结果的可比性,因此不推荐使用。


标准库是否有现成实现?

目前scikit-learn等主流机器学习标准库中,没有直接处理“训练集缺失目标标签时自动补零概率”的工具类,上述自定义包装器是行业内最常用的解决方案。

内容的提问来源于stack exchange,提问作者Nikolay Zakirov

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.17 13:25:15