使用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
相关产品推荐
相关产品推荐

