在GridSearchCV中使用自定义评分函数报错:无法序列化生成器对象
问题解决:TypeError: cannot pickle 'generator' object
错误根源
启用多进程(n_jobs=-1)时,GridSearchCV需要将所有相关对象序列化后传递给子进程,但你代码中的自定义生成器iterator_scoring属于不可序列化的对象,同时依赖生成器获取折索引的逻辑在多进程下会导致状态混乱,双重问题引发报错。
解决方案
步骤1:移除生成器依赖,提前保存折索引
删除所有生成器相关代码,提前将10折的训练/测试索引保存到列表中,后续直接调用:
import numpy as np from sklearn.model_selection import PredefinedSplit, KFold, GridSearchCV from sklearn.base import clone from sklearn.metrics import make_scorer from mlxtend.evaluate import bias_variance_decomp from sklearn.neighbors import KNeighborsRegressor # 生成10折划分并保存索引 folds = 0 * np.ones(len(X_train_knn)) kf_train = [] kf_test = [] kf = KFold(10, shuffle=True, random_state=0) for i, (train_ind, test_ind) in enumerate(kf.split(X_train_knn)): kf_train.append(train_ind) kf_test.append(test_ind) folds[test_ind] = i ps = PredefinedSplit(folds)
步骤2:用可序列化的类实现自定义评分逻辑
通过类来存储折索引和当前处理状态,类实例支持序列化,适配多进程场景:
class BiasVarianceScorer: def __init__(self, X, y, train_indices, test_indices): self.X = X self.y = y self.train_indices = train_indices self.test_indices = test_indices self.current_fold = 0 # 记录当前处理的折序号 def __call__(self, estimator, X, y): # 获取当前折的训练/测试数据 train_idx = self.train_indices[self.current_fold] test_idx = self.test_indices[self.current_fold] X_train_fold = self.X[train_idx] y_train_fold = self.y[train_idx] X_test_fold = self.X[test_idx] y_test_fold = self.y[test_idx] # 克隆模型避免污染原模型实例 cloned_estimator = clone(estimator) # 计算偏差-方差分解的期望损失 avg_expected_loss, _, _ = bias_variance_decomp( cloned_estimator, X_train_fold, y_train_fold, X_test_fold, y_test_fold, loss='mse', random_seed=0 ) # 更新折序号,循环处理下一个折 self.current_fold = (self.current_fold + 1) % len(self.train_indices) return -avg_expected_loss # 取负号适配GridSearchCV最大化评分的逻辑
步骤3:配置并运行GridSearchCV
创建scorer实例,定义参数网格后启动搜索:
# 创建自定义scorer scorer = BiasVarianceScorer(X_train_knn, Y_train_ar, kf_train, kf_test) # 定义模型与参数网格(修正原代码中参数范围过窄的问题) estimator = KNeighborsRegressor() param_grid = { 'n_neighbors': np.arange(1, 21), 'weights': ['uniform', 'distance'], 'p': np.arange(1, 3) } # 配置多评分指标 scoring_metrics = { 'neg_root_mean_squared_error': 'neg_root_mean_squared_error', 'r2': 'r2', 'score_func': scorer } # 启动网格搜索 grid = GridSearchCV( estimator, param_grid, cv=ps, scoring=scoring_metrics, refit='score_func', verbose=2, n_jobs=-1 ) grid_search = grid.fit(X_train_knn, Y_train_ar)
关键说明
- 类实例可被正常序列化,解决多进程下的对象传递问题。
- 每次调用scorer时自动切换到对应折的数据集,保证偏差方差计算的准确性。
- 返回负的期望损失,适配GridSearchCV默认最大化评分的逻辑,确保最终选择期望损失最小的模型。
内容的提问来源于stack exchange,提问作者Brian
相关产品推荐
相关产品推荐

