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

在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 01:28:18