解决为GaussianProcessRegressor调参多优化器时的getattr类型错误
问题:GPR自定义优化器结合RandomizedSearchCV时触发TypeError
我基于GaussianProcessRegressor实现了自定义优化器,用于时间序列目标预测,同时用RandomizedSearchCV进行超参数寻优,运行时出现TypeError: getattr(): attribute name must be string错误,相关代码及报错信息如下:
关键代码片段(HelperFunctions.py)
模型与搜索参数定义
def intermediate_models(kernel): dtr_dic = dict( ccp_alpha=uniform(loc=0.0, scale=10.0), max_features=randint(low=1, high=100), max_depth=randint(low=1, high=100), criterion=["squared_error", "friedman_mse", "absolute_error", "poisson"], ) optimizer_names = [ "minimize_wrapper", "least_squares_wrapper", "differential_evolution_wrapper", "basinhopping_wrapper", "dual_annealing_wrapper", ] model_dist_pairs = [] for optimizer_name in optimizer_names: gpr = GPRWithCustomOptimizer(kernel=kernel, optimizer=optimizer_name) gpr_dic = dict( optimizer=optimizer_names, # 错误根源 n_restarts_optimizer=np.arange(0, 20 + 1), normalize_y=[False, True], copy_X_train=[True, False], random_state=np.arange(0, 10 + 1), ) model_dist_pairs.append((gpr, gpr_dic)) return [(DecisionTreeRegressor(), dtr_dic)] + model_dist_pairs
自定义GPR类
class GPRWithCustomOptimizer(GaussianProcessRegressor): def __init__( self, optimizer="minimize", initial_theta=None, bounds=None, random_state=None, normalize_y=True, n_restarts_optimizer=0, copy_X_train=True, **kwargs, ): self.initial_theta = initial_theta self.bounds = bounds self.custom_optimizers = CustomOptimizers(self, self.initial_theta, self.bounds) self.optimizer_func = getattr(self.custom_optimizers, optimizer) # 触发错误的位置 super().__init__( optimizer=self.optimizer_func, random_state=random_state, normalize_y=normalize_y, n_restarts_optimizer=n_restarts_optimizer, copy_X_train=copy_X_train, **kwargs, )
核心错误信息
TypeError: getattr(): attribute name must be string
错误原因分析
- 参数类型不匹配:在
intermediate_models函数中,gpr_dic的optimizer字段被赋值为整个列表optimizer_names,而非单个字符串。当RandomizedSearchCV克隆GPR实例时,会将这个列表传入GPRWithCustomOptimizer的构造函数,而getattr要求第二个参数必须是字符串,因此抛出TypeError。 - 逻辑冗余:循环遍历每个优化器名称创建GPR实例,却给每个实例的搜索参数设置为遍历所有优化器,导致重复搜索,完全没必要。
- 默认值不匹配:
GPRWithCustomOptimizer的默认optimizer值是"minimize",但自定义优化器类中对应的方法名是minimize_wrapper,默认初始化也会报错。
修复方案
1. 修正搜索参数的optimizer字段
将gpr_dic中的optimizer=optimizer_names改为optimizer=[optimizer_name],确保每个GPR实例仅搜索当前指定的优化器,同时符合RandomizedSearchCV的参数格式要求。
修改后的intermediate_models函数(二选一即可):
方案一:每个优化器单独搜索
def intermediate_models(kernel): dtr_dic = dict( ccp_alpha=uniform(loc=0.0, scale=10.0), max_features=randint(low=1, high=100), max_depth=randint(low=1, high=100), criterion=["squared_error", "friedman_mse", "absolute_error", "poisson"], ) optimizer_names = [ "minimize_wrapper", "least_squares_wrapper", "differential_evolution_wrapper", "basinhopping_wrapper", "dual_annealing_wrapper", ] model_dist_pairs = [] for optimizer_name in optimizer_names: gpr = GPRWithCustomOptimizer(kernel=kernel, optimizer=optimizer_name) gpr_dic = dict( optimizer=[optimizer_name], # 改为当前优化器名称的单元素列表 n_restarts_optimizer=np.arange(0, 20 + 1), normalize_y=[False, True], copy_X_train=[True, False], random_state=np.arange(0, 10 + 1), ) model_dist_pairs.append((gpr, gpr_dic)) return [(DecisionTreeRegressor(), dtr_dic)] + model_dist_pairs
方案二:单实例搜索所有优化器(更高效)
def intermediate_models(kernel): dtr_dic = dict( ccp_alpha=uniform(loc=0.0, scale=10.0), max_features=randint(low=1, high=100), max_depth=randint(low=1, high=100), criterion=["squared_error", "friedman_mse", "absolute_error", "poisson"], ) optimizer_names = [ "minimize_wrapper", "least_squares_wrapper", "differential_evolution_wrapper", "basinhopping_wrapper", "dual_annealing_wrapper", ] model_dist_pairs = [] # 仅创建一个GPR实例,一次性搜索所有优化器 gpr = GPRWithCustomOptimizer(kernel=kernel) gpr_dic = dict( optimizer=optimizer_names, n_restarts_optimizer=np.arange(0, 20 + 1), normalize_y=[False, True], copy_X_train=[True, False], random_state=np.arange(0, 10 + 1), ) model_dist_pairs.append((gpr, gpr_dic)) return [(DecisionTreeRegressor(), dtr_dic)] + model_dist_pairs
2. 修复自定义GPR类的默认值与鲁棒性
class GPRWithCustomOptimizer(GaussianProcessRegressor): def __init__( self, optimizer="minimize_wrapper", # 修正默认值为存在的方法名 initial_theta=None, bounds=None, random_state=None, normalize_y=True, n_restarts_optimizer=0, copy_X_train=True, **kwargs, ): self.initial_theta = initial_theta self.bounds = bounds # 添加参数校验,避免非字符串参数传入 if not isinstance(optimizer, str): raise ValueError(f"optimizer必须是字符串类型,当前传入类型为{type(optimizer)}") self.custom_optimizers = CustomOptimizers(self, self.initial_theta, self.bounds) self.optimizer_func = getattr(self.custom_optimizers, optimizer) super().__init__( optimizer=self.optimizer_func, random_state=random_state, normalize_y=normalize_y, n_restarts_optimizer=n_restarts_optimizer, copy_X_train=copy_X_train, **kwargs, )
内容的提问来源于stack exchange,提问作者Rebel
相关产品推荐
相关产品推荐

