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

使用Scikit-learn 1.4.1.post1堆叠已训练模型时遇未拟合错误

已训练模型堆叠为StackingClassifier时出现NotFittedError问题

问题描述

我有4个已训练完成的模型:2个VotingClassifier和2个StackingClassifier,都用joblib格式保存。加载后调用predict方法能正常用于生产预测,但把它们堆叠成新的StackingClassifier时,出现未拟合错误。

代码实现

class JoblibModelWrapper(BaseEstimator, ClassifierMixin):
    def __init__(self, model):
        self.model = model

    def fit(self, X, y=None):
        # dont fit because self.model is already fitted
        return self

    def predict(self, X):
        return self.model.predict(X)

    def predict_proba(self, X):
        if hasattr(self.model, "predict_proba"):
            return self.model.predict_proba(X)
        else:
            raise RuntimeError("Le modèle sous-jacent ne supporte pas predict_proba")


import os
import joblib
import pandas as pd

models_directory = '/content/drive/MyDrive/X/preprod_models'
joblib_models = {}
files = os.listdir(models_directory)
total_models_count = sum(1 for filename in files if filename.endswith('.joblib'))
current_model_index = 0

for filename in files:
        model_path = os.path.join(models_directory, filename)
        model_name = filename[:-7]  # remove .joblib
        # load trained models already saved in joblib format (they are used in prod today, and works perfectly when we call .predict method)
        model = joblib.load(model_path)
        joblib_models[model_name] = model
        current_model_index += 1
        print(f'Modèle {current_model_index} / {total_models_count} processed: {model_name}')

print('Total processed: {total_models_count}')

wrapped_joblib_models = [
    (name.replace(' ', '_').replace('__', '_'), JoblibModelWrapper(model)) for name, model in joblib_models.items()
]

cv = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)
stacking_models = StackingClassifier(
    estimators=wrapped_joblib_models ,
    final_estimator=LogisticRegression(solver='saga', max_iter=10000),
    cv=cv,
    verbose=3,
    passthrough=True,
    stack_method='predict',
    # n_jobs=-1
)


def evaluate_model(model_name, X_train, y_train, X_test, y_test, X_train_no_encoded, model = None):
    print(f'======================================{model_name}=============================')
    print(mapping)

    threshold = 0.0005
    model_path = f'/content/drive/MyDrive/X/prod_models/{model_name}.joblib'

    model.fit(X_train, y_train)
    dump(model, f'/content/drive/MyDrive/X/prod_models/{model_name}.joblib')
     
    print('modèle sauvegardé')


    plot_confusion_matrix_normalized(model, X_test, y_test)
    plot_confusion_matrix_normalized(model, X_train, y_train)

    # Courbe ROC
    n_classes = len(np.unique(y_train))  # Nombre de classes uniques dans y_train
    if hasattr(model, "predict_proba"):
        plot_multiclass_roc_curve(model, X_test, y_test, n_classes)
        plot_multiclass_roc_curve(model, X_train, y_train, n_classes)

evaluate_model('PROD_ALL_TRAINED_MODELS_STACKED', X_train_balanced, y_train_balanced, X_test_scaled, y_test, X_train, model=stacking_models)

报错信息

---------------------------------------------------------------------------
======================================PROD_ALL_TRAINED_MODELS_STACKED=============================
{'X': 0, 'Y': 1, 'Z': 2}
模型已保存
---------------------------------------------------------------------------
NotFittedError                            Traceback (most recent call last)
<ipython-input-19-89e6497ba9b5> in <cell line: 30>()
     28 )
     29 
---> 30 evaluate_model('PROD_ALL_TRAINED_MODELS_STACKED', X_train_balanced, y_train_balanced, X_test_scaled, y_test, X_train, model=stacking_models)
     31 
     32 # all_models[0]

8 frames
/usr/local/lib/python3.10/dist-packages/sklearn/utils/validation.py in check_is_fitted(estimator, attributes, msg, all_or_any)
   1620 
   1621     if not _is_fitted(estimator, attributes, all_or_any):
-> 1622         raise NotFittedError(msg % {"name": type(estimator).__name__})
   1623 
   1624 

NotFittedError: 该StackingClassifier实例尚未拟合。请先使用合适的参数调用'fit'方法再使用此估计器。

问题原因

JoblibModelWrapper类未正确实现scikit-learn估计器的拟合状态标记机制。scikit-learn通过检查模型的特定属性(如classes_)或_is_fitted标记来判断模型是否已拟合,你的包装类虽然跳过了拟合步骤,但没有传递原模型的拟合状态,导致StackingClassifier在交叉验证生成特征时认为基础模型未拟合。

解决方法

修改JoblibModelWrapper类,添加拟合状态标记,兼容scikit-learn的拟合检查逻辑:

class JoblibModelWrapper(BaseEstimator, ClassifierMixin):
    def __init__(self, model):
        self.model = model
        # 复制原模型的分类标签属性,确保和scikit-learn流程兼容
        if hasattr(model, 'classes_'):
            self.classes_ = model.classes_
        # 手动标记模型已拟合
        self._is_fitted = True

    def fit(self, X, y=None):
        # 原逻辑不变,直接返回自身
        return self

    def predict(self, X):
        return self.model.predict(X)

    def predict_proba(self, X):
        if hasattr(self.model, "predict_proba"):
            return self.model.predict_proba(X)
        else:
            raise RuntimeError("基础模型不支持predict_proba方法")

    # 实现scikit-learn的拟合检查接口
    def _check_is_fitted(self):
        if not getattr(self, '_is_fitted', False):
            raise NotFittedError("该包装器模型未拟合")

额外注意事项

  • 确保所有加载的基础模型都具备classes_属性(VotingClassifier和StackingClassifier训练完成后都会自动生成此属性)。
  • 若StackingClassifier使用stack_method='predict_proba',需保证所有基础模型都支持predict_proba方法,否则会触发运行时错误。
  • 代码中使用的dump方法需从joblib导入,需添加from joblib import dump语句。

内容的提问来源于stack exchange,提问作者Mohamed Kounda SACKO

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 14:24:58