SMAC
文章目录
入口类
- Facade
- 文档最完善的入口类:
smac.facade.smac_ac_facade.SMAC4AC
程序入口
Facade.optimize()–>SMBO.run()(Facade.solver().run())SMBO.run()迭代循环Intensifier.get_next_run()(SMBO.run()–> SMBO.intensifier.get_next_run()) –>AbstractRacer._next_challenger()- AbstractRacer 是 Intensifier 的基类
- 选择下一个 challenger (候选配置config)
方法:
- 使用 EPM(代理模型) EPMChooser 选择
参数:
- SMBO.epm_chooser
- runhistory
- num_workers
- challengers: 如果手动提供 challengers 使用他们,否则使用 epm_chooser 生成
AbstractRacer._next_challenger()–>EPMChooser.choose_next()(SMBO.epm_chooser.choose_next())内部:
- 使用 acquisition_function 采集函数,排序参数 config
EPMChooser.choose_next() —> :
- 初次迭代: 随机搜索 –> EPMChooser._random_search.maximize() (
AcquisitionFunctionMaximizer.maximize()) - 后续迭代: –> 自定义的 Acquisition_function_Maximizer.maximize() (
AcquisitionFunctionMaximizer.maximize())
- 初次迭代: 随机搜索 –> EPMChooser._random_search.maximize() (
EPMChooser.choose_next() 内部
获取历史数据:通过 EPMChooser.runhistory 获取 (X, Y, X_configs) 迭代历史数据,
EPMChooser._collect_data_to_train_model()使用
EPMChooser.rh2EPM.transform(runhistory)把RunHistory 变成 (X, Y) 代理模型友好的数据格式EPMChooser.rh2EPM:
AbstractRunHistory2EPM抽象基类- 内部处理 多目标 multit-object 数据的Cost 转换 cost 数组 –> 单个 float cost 数值
epm_chooser.choose_next() 中的参数
- X_configurations: 所有的 configuration 以 np.array 形式存储的数据,包括失败的和成功的参数, 和 X 类似
- X, Y: 通过最大 budget 筛选出来的成功 和 timeout 的 Config
- best_observation: 是 cost
- x_best_array: incumbent_value 对应的 X 取值
_get_x_best()–> self.model.predict_marginalized_over_instances(X) -> mean, var
- 训练代理模型: EPMChooser.model.train(X, Y)
更新 incumbent (最好参数), 存储在
EPMChooser.acquisition_function实例内部- 如果传入和 incumbent_value, 使用这个 incumbent_value
否则,通过
(EPMChooser) self._get_x_best(predict, X), 计算出最佳值,incumbent_valueself._get_x_best(predict, X)使用X + 代理模型,计算出Y_predict- Y_predict –> mean, var 均值和方差
- 使用 Y_predict 的 mean 作为 costs –> 计算 x_best, incumbent_value
_get_x_best()–> self.model.predict_marginalized_over_instances(X) -> mean, var对于不需要 instance_features 情况:
直接返回 proxymodel.predict(X) –> mean, var
1 2 3 4 5 6 7 8if self.instance_features is None or \ len(self.instance_features) == 0: mean_, var = self.predict(X) assert var is not None # please mypy var[var < self.var_threshold] = self.var_threshold var[np.isnan(var)] = self.var_threshold return mean_, var- 使用 mean_ –> 作为 cost 对 X 排序获得 x_best 和 best_observation(incumbent_value)
- 采集函数 最好值更新
self.acquisition_function() 获取新生成X, 作为下一轮的 chanllengers
self.acq_optimizer.maximize(runhistory, stats, num_points, random_config_chooser)- 基于(1)采集函数(2)历史数据(3)最佳样本点(4)随机选择器(5)maximizer 生成新的 configs(参数 Xs)
- 去重: 去掉以前已经运行过的参数 config
maximizer.maximize()–> Iterator[Configuration]
- 通过生成 (RunInfoIntent, config) 返回值判断是否需要运行
RunInfoIntent.RUN:
Runner.submit_run()负责运行,存储结果到 Runner.results 字段中- SMBO.run() –>
Runner.submit_run()(SMBO.tae_runner.submit_run(run_info)) Runner.submit_run()–>Runner.run_wrapper(run_info)1def run_wrapper( self, run_info: RunInfo, ) -> Tuple[RunInfo, RunValue]:- 内部负责调用 Runner.run()
负责确定运行结果状态 status
- StatusType.STOP: 退出整个 SMBO 循环(实际源码中没有发现主动赋值 StatusType.STOP 的情况)
Runner.submit_run()–>Runner.run()Runner: 所有 runnner 的基类- smac.tae.execute_func.AbstractTAFunc: 用来执行算法的 Runner 抽象类
1 2def run(self, config: Configuration, instance: str, cutoff: Optional[float] = None, seed: int = 12345, budget: Optional[float] = None, instance_specific: str = "0") -> Tuple[StatusType, float, float, Dict]: # Tuple[status: int, cost: np.ndarrray, runtime: float, additional_info: dict]运行结果存储:
- Runner.results: Dict[RunInfo, RunValue]
- SMBO.run() –>
结果处理: –>
SMBO._incorporate_run_results- 把结果存储到
SMBO.runhistory(smac.runhistory.Runhistory) 中 intensifier: 处理 racer 和 incumbent 竞争问题
- 替换和裁定最新的 incumbent (最好结果)
SMBO.intensifier.process_results(run_info: RnInfo, incumbent, run_history, result: RunValue)
- 功能: 添加当前点(RunInfo, RunValue)
AbstractRacer.
_compare_configs(previous_incumbent, challenger, runhistory)- 功能:
更新最好点,比较以前的最好点 previous_incumbent,和新添加的点 challenger cost 获得方法:- 通过 RunHistory.average_cost(config, …) 方法获得
- 先正规化,再求平均值
- 多目标
multi-objectives处理: 通过直接平均,把多个 ojectives 转换成单个 cost
- chall_cost < previous_incumbent_cost —> 挑战成功
- 功能:
- callback 回调函数调用
- 把结果存储到
结束SMBO循环
- 资源耗尽 budget 或者 result.status == StatusType.STOP,
最好点 best_point / incumbent / incumbent_perf 和 multi-objectives
内部涉及到了两种场景:
添加样本点时: SMBO._incorporate_run_results() –> SMBO.intensifier.process_results(RunInfo, RunValue, …) –> SMBO.intensifier._compare_configs(previous_incumbent, challenger)
内部真正执行比较的是:
SMBO.intensifier._compare_configs(previous_incumbent, challenger)- 通过正规化 +
简单平均把 multi-objectives 转换成单个cost, 评估最好点
- 通过正规化 +
- 存储到 SMBO.incumbent 中
生成下一个样本点时:在 SMBO.run() –> SMBO.get_next_run() –> SMBO.intensifier.EPMChooser.choose_next() 为了获得下一个 challengers 时
在更新代理模型之前,把 runhistory 转换成 X, Y,
通过 multi_objective_algorithm 把 multi-objectives 转换成单个 cost- 调用位置: 在
SMBO.epmchooser._collect_data_to_train_model()内部 - x 是 configs array –> list[x] –> X
- y 是 转换后的 单个 cost 列表 –> list[y] –> Y
- 调用位置: 在
更新代理模型
代理模型的单目标 Vs 多目标- 默认情况下,内部代理函数实现是 X –> y 模型,预测单个 cost (单目标)
如果需要实现多目标预测,需要手动改写 代理模型的实现,重载
AbstractEPM.train(), AbstractRacer._train(), AbstractEPM.predict(), AbstractEPM._predict()- 在保持内部 外部接口的 X –> y 基础上,内部能够实现 X –> Y –> y 的两种预测模式
获取最好点
- 可以通过传参传入 到 choose_next() 中
如果没有,通过
EPMChooser._get_x_best(X)通过代理模型 + X(历史 configs) 筛选出来代理模型的单目标 Vs 多目标- 这里默认使用 X –> y 单个cost 的代理模型
- 如果是预测多目标的代理模型,需要手动实现代理函数内部的多目标 —> 单目标(X –> Y –> y)
- 使用 (代理模型, best_point, random_chooser) + EPMChooser.acq_optimizer 生成下一个 challengers
概率代理模型
- 基础类型:
AbstractEPM 存储位置
- facade.solver (SBMO) –> emp_chooser (EPMChooser) –> model (AbstractEPM)
相关类
Facade: eg: SMAC4HPO, SMAC4AC- 入口类
SMBO:- 管理贝叶斯循环
EPMChooser- 训练概率代理模型 EPM, 生成下一个超参数 config (
epm_chooser.choose_next()) choose_next()中(1)先会完成训练,再(2)生成下一个 config- 利用runhistory 获取历史数据
- 训练概率代理模型 EPM, 生成下一个超参数 config (
epm mode 创建流程:
- 在 Facade 类中创建
在 SMBO 中作为 EPMChooser 的参数用来创建 EPMChooser
- 创建后只有和 EPMChooser 发生了实际的关系
替换model(代理模型) 方法:
- 直接修改
facade.solver.emp_chooser.model即可
- 直接修改
替换 epm_chooser 方法:
- 直接替换
facade.solver.epm_chooser即可 - 注意里面引用的 scenario, stats, runhistory, model 等对象的引用,不要使用新建的,要是在 facade, smbo 中引用的
- 直接替换
多目标优化 multi-objective
涉及的部件
multi_objective_algorithm 多目标优化的多 cost 转换算法
使用流程:
Facade(multi_objective_algorithm)Facade.rh2epm 工具
AbstractRunHistory2EPM的初始化参数AbstractRunHistory2EPM抽象基类用途不同,使用不同的子类:
- scenario.run_obj –> "runtime":
RunHistory2EPM4LogCost scenario.run_obj –> "quality": 根据 y 的转换方式而定
- scenario.transform_y == "NONE":
RunHistory2EPM4Cost - scenario.transform_y == "LOG":
RunHistory2EPM4LogCost - scenario.transform_y == "LOGS":
RunHistory2EPM4ScaledCost - scenario.transform_y == "INV":
RunHistory2EPM4InvScaledCost
- scenario.transform_y == "NONE":
- scenario.run_obj –> "runtime":
- 入口方法:
AbstractRunHistory2EPM.transform(runhistory) --> AbstractRunHistory2EPM._build_matrix() 实际调用的重载入口函数
AbstractRunHistory2EPM._build_matrix(run_dict, runhistory) -> tuple[X_ndarray, Y_ndarray]- 把 runhistory 中的数据转换成
代理模型优化的数组格式 - 在 X, Y 中添加上 feature_dict/feature_instances 数据
- 把 runhistory 中的数据转换成
AbstractRunHistory2EPM作为EPMChooser.runhistory2epm --> EPMChooser.rh2EPM参数用来初始化AbstractRunHistory2EPM在 EPMChooser._collect_data_to_train_model() 中处理训练数据AbstractRunHistory2EPM在 EPMChooser._get_x_best() 中 最好值 best_observation 的转换
传输流程:
- Facade.__init__.multi_objective_algorithm
- …
- Facade.__init__.r2e_def_kwargs
Facade.__init__.RunHistory2EPM4Cost.multi_objective_algorithm–>Facade.__init__.rh2epm.multi_objective_algorithm- Facade.__init__.RunHistory2EPM4Cost –> Facade.__init__.rh2epm
- Facade.__init__.smbo_args.runhistory2epm
- SMBO.__init__.runhistory2epm
- SMBO.epm_chooser.__init__.runhistory2epm (EPMChooser) –> EPMChooser.rh2EPM
使用:SMBO.epm_chooser.rh2EPM.transform(runhistory, budget_subset) –> X,Y- SMBO.epm_chooser.rh2EPM.RunHistory2EPM4Cost._build_matrix()
- SMBO.epm_chooser.rh2EPM.RunHistory2EPM4Cost.multi_objective_algorithm(y_)
- 这里的Y 就是使用 multi_objective_algorithm(multi_object) 运行后生成的
- 综上,只需要在 Facade 中传入一个 multi_objective_algorithm 算法实现即可
- 实现一个
AggregationStrategy的子类,传入 Facade 的 multi_objective_algorithm 参数,即可
转换多目标到单 cost 的工具
AggregationStrategy抽象类ParEGO子类MeanAggregationStrategy子类
在SMBO 中 multi_objective_algorithm 被调用的过程
在 SMBO.run() 中的调用(优化)流程:
SMBO.internsifier.get_next_run(challengers, incubent)获取新的 chanllengers (configs)- AbstractRacer._next_chanllenger()
- SMBO.intensifier.
_next_challenger(challengers, chooser) EPMChooser.
choose_next(), 这个是从 chooser 是从 SMBO 传递过来的收集和转换 runhistory 格式:
功能:
- 收集到 X, Y
- 如果是多目标优化,使用 multi_objective_algorithm, 把多个值转换成单个值
X, Y, X_configs = EPMChooser._collect_data_to_train_model()
- 说明:这里的 train model 是训练代理模
- X 被添加 instance_features 相关数据后的 config array
- X_configs 是没有添加 instance_features 相关数据的原生 config array
X, Y = EPMChooser.rh2EPM.transform(runhistory, budget_subset)
EPMChooser.rh2EPM._build_matrix()
- 把 runhistory 转换成矩阵
- 参考: runhistory2epm.py: RunHistory2EPM4Cost._build_maxtrix() 实现
- 作用: 把 [n_row, n_target] 形状的 multi_objectives
[[y00, y01], [y10, y11]]转换成 –>[[c0], [c1]] 内部实现:
1 2 3 4 5 6 7# 下面这个是伪代码 Ys = [] for run in runs: y_ = normalize_costs([run.cost], runhistory.objective_bounds) y_ = self.multi_objective_algorithm(y_) Ys.append(y_) return Xs, Ys- 注:
这里实际调用了 multi_objective_algorithm 算法实现
代理模型训练:EPMChooser.model.train(X, Y)- 注意: 代理模型内部接收到的永远是 单目标数据,因为 在 _collect_data_to_train_model 阶段已经把多目标转换成了单目标数据(单个cost)
选择下一个,最好 x,y: 使用体力模型生成
- 用户代理模型内部选取新的样本点的生成机制
- EPMChooser.acquisition_function.update(model=proxy_model, best_observation, best_x_arry, …)
SMBO._incorporate_run_results处理加入的样本点
SMBO.internsifier.process_results
- eg: simple_intensifier.py: SimpleIntensifier.process_results()
- SMBO.intensifier._compare_configs(challegners, previous_incumbent)
相关依赖
ConfigSpace: automl 系列工具
- Quickstart - ConfigSpace
用途:
- 限制取值范围:
ConfigurationSpace - 参数之间的相互依赖关系:
EqualsCondition 记录多次迭代参数变更历史
- 类似实例化包含一个 ConfigurationSpace
- 调用不同的模型
- 限制取值范围:
概念
参数: config
- 在 smac 中,parameter 参数或者 HyperParamter 超参数被称作 configuration | config
challenger
- smac 选出的候选参数
- 相关工具: ChallengerList
trial
- 一次黑盒函数的运行
- 包括整合相关要素:config, seed, budget, instance
incumbent, current incumbent
- 当前效果最好的参数 best_config
- 注意: 如果是 multi-objective 多目标优化,可能有多组最好参数 multiple current incumbent
- incumbent 存储在
Intensifier中 - incumbent 值存储在 AquisitionFunction.eta 中 (best_observation), 参数在 AquisitionFunction.incumbent_array=x_best_array
optimizer: SMBO
- 贝叶斯优化循环工具类实例: SMBO 对象
- Facade.optimizer() –> SMBO
instance: 数据集
- smac 允许被优化模型使用多个数据集,一个数据集或数据子集,被称为一个 instance
- 需要在
Scenario类中声明 instance 列表
hyperband:
- 一种超参数调优,资源分配策略,可以用来筛选出性能较好的参数
- Hyperband的核心思想是结合了随机搜索和贝叶斯优化的优点,通过动态地分配资源来快速识别出性能较好的超参数配置
imputator
- 数据缺失填充器:
RFRImputator
- 数据缺失填充器:
epm_chooser
- 负责 epm(代理模型)、采集函数、acquisition function maximizer 、run_history 、 scenario 之间的互相调用
作用:
- 使用 run_history 中的数据,训练代理模型
- 使用代理模型 + 采集函数 + maximizer –> 生成新的参数 config
run_obj:
实际使用到的地方
- Scenario: 只是存储这个参数
- Runner:
censored: 截断数据
- Censored 数据是指在超参数优化过程中,由于某些超参数组合的评估成本过高或耗时过长,导致其评估结果被提前终止或截断。这些被截断的评估结果通常只包含部分信息,如部分迭代次数的训练结果。
组件
参考:
smac.epm (Surrogat Model 代理模型) AbstractEPM
- epm (Empirical Performance Model)
smac 中的代理模型包括:
- 高斯过程 Gaussian Processes
- 随机森林 Random Forest
属性解释
self.instance_features
参考资料:
- smace v 1.2: <project-root>/test/test_epm/test_base_emp.py: TestRFWithInstances.test_apply_pca
详解:
创建来源:
scenario.feature_array<— scenario.feature_dict数据创建流程:
- 创建 Scenario 时,实际传入的是
feature_dict: List[Dict[str, np.array(一维数组)]] - scenario 内部生成
scenario.feature_array: np.ndarray, 二维数组 - 在 Facade 中调用 scenario.feature_array 用来生成–>
AbstractEPM.feature_instances: np.ndarray, 二维数组
- 创建 Scenario 时,实际传入的是
feautre_dict: dict[str, np.ndarray] 例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18self.rng = np.random.RandomState(seed=42) self.scen_fn = 'test/test_files/validation/scenario.txt' self.train_insts = ['0', '1', '2'] self.test_insts = ['3', '4', '5'] # 下面这个其实是 scenario.instance_specific self.inst_specs = {'0': 'null', '1': 'one', '2': 'two', '3': 'three', '4': 'four', '5': 'five'} self.feature_dict = {'0': np.array((1, 2, 3)), '1': np.array((1, 2, 3)), '2': np.array((1, 2, 3)), '3': np.array((1, 2, 3)), '4': np.array((1, 2, 3)), '5': np.array((1, 2, 3))} self.output_rh = 'test/test_files/validation/' scen = Scenario(self.scen_fn, cmd_options={'run_obj': 'quality'}) # config space # x1 [-5,10] [0] # x2 [0,15] [0]instance_features: np.ndarray, 一维数组, 例子:
1instance_features = np.array([np.random.rand(10) for _ in range(5)])关联工具:
smac.epm.util_funcs.get_types –> Tuple[types, bounds]
- 生成数据类型,和类型的取值范围
- types: (len(hyperparameters) + len(instance_features.shape[1]))*[0]
单个 instance_features
1np.array([1,2,3])包含多个 instance 的 instance_features (AbstractEPM 中的实际情况)
1 2 3 4np.array( [1,2,3] [4,5,6] )- 表示有2个 instance
每个 instance 有三个 feature (特征)
- instance1: feature –> [1,2,3]
- instance2: feature –> [4,5,6]
AbstractEPM 中 predict_marginalized_over_instances() 方法中 关于 instance_features 的代码解读
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36# 没有定义 instance_features 的情况,直接使用 超参数数据集 X if self.instance_features is None or \ len(self.instance_features) == 0: mean, var = self.predict(X) assert var is not None # please mypy var[var < self.var_threshold] = self.var_threshold var[np.isnan(var)] = self.var_threshold return mean, var else: n_instances = len(self.instance_features) mean = np.zeros(X.shape[0]) var = np.zeros(X.shape[0]) for i, x in enumerate(X): # (1) 把 X 中的 一行 row, 按 n_instance 先复制成n_instance(实例个数) 行(n_instance, n_instance_features) # (2) 把 self.instance_features 水平拼接到上面扩展的 x 的新数据上, (x_rows, self.instance_features) # In [27]: np.hstack((np.tile([1,2,3], (3, 1)), [[1,2,3], [4,5,5], [6,6,6]])) # Out[27]: # array([[1, 2, 3, 1, 2, 3], # [1, 2, 3, 4, 5, 5], # [1, 2, 3, 6, 6, 6]]) # 上面的例子数据: x --> [1, 2, 3], instance_features --> [[1,2,3], [4,5,5], [6,6,6]] X_ = np.hstack( (np.tile(x, (n_instances, 1)), self.instance_features)) means, vars = self.predict(X_) assert vars is not None # please mypy # VAR[1/n (X_1 + ... + X_n)] = # 1/n^2 * ( VAR(X_1) + ... + VAR(X_n)) # for independent X_1 ... X_n var_x = np.sum(vars) / (len(vars) ** 2) if var_xs < self.var_threshold: var_x = self.var_threshold var[i] = var_x mean[i] = np.mean(means)
Acquisition Function 采集函数
数据:
| |
Acquisition Maximizer
功能:使用采集函数获取能够最大化 objective 的参数 (config | parameter)
特点:
使用搜索算法,使用采集函数来获取可能的最好参数(即下一次迭代使用的参数,config | challenger)
- local search: 局部近邻搜索
- (sorted) random search: 定义域空间config space 中随机搜索,使用采集函数排序
- local (sorted) random search
- Differential Search: 差分进化,类似遗传算法
Initial Design 初始化
功能:第一次迭代参数的生成
特点:
包含了多种数据生成方法
- 随机设计(random):这是一种简单的方法,通过随机选择参数值来生成数据点。
- 拉丁超立方抽样(latin hypercube):这是一种统计方法,用于从多维分布中生成近似随机的参数值样本。
- 索伯序列(sobol):这是一种准随机的低差异序列,用于生成数据点,以减少随机抽样可能带来的偏差。
- 因子设计(factorial):这种方法生成配置空间的角点点,即在每个维度的极端值处生成数据点。
- 默认设计(default):使用配置空间的默认配置来生成数据点。
- 与 config selector 结合使用,可以避免生成重复的参数
配置流程:
| |
参考: smac v1.2 smac/facade/smac_ac_facade.py: 541~570 行
Intensifier
功能:
- 用来确定那个参数 config, 值得使用花费更多的资源 (budget, 迭代运算) 计算
Intensifier.get_next_challenger() 生成下一个需要运行的参数 config
通过 self.configs_to_run 字段存储用来采样的 configs
生成方法: Intensifier._generate_challengers()
真正的config 来源:传入的 challengers 或者 Interface.chooser.choose_next()
- self.to_run 字段存储的是已经安排要运行的 config
- 等待运行的configs, 消耗完后, Intensifier._next_iteration() –> 重置状态,清空 self.configs_to_run 等
- 调用
特点:
- 直接调用 Configuration Selector
configuration Selector
功能:生成参数
步骤:
- 生成参数 config
- 训练代理模型 Surrogat Model, 使用历史数据 RunHistory
- 生成下一次的参数 config, 使用 Acquisition Function/ Maximizer
smac.runhistory RunHistory
参考:
dict[Trial_Info, Trial_Value]:
Trial_Info:
- 一次迭代的 初始化数据
- config: 参数
- seed
- budget
- instance 等等
Trial_Value:
- 一次迭代的运行 结果信息
- cost: 损失函数的值
- time
- status
- starttime
- endtime
- ….
RunHistory Encoder
把 RunHistory 中存储的数据处理成 代理模型 可以使用的格式
CallBack
一次迭代的不同时间段调用的回调函数
ConfigSpace
类型
OridinalHyperparameter
特点:离散有序数据类型(优先的取值)
例子:
- [1, 2, 3, 5]
- [0.1, 0.6, 0.8, 1.0]
- ['small', 'medium', 'large']
eg:
OrdinalHyperparameter('hp', sequence=['small', 'medium', 'large'], default_value='medium')
CategoricalHyperparameter
类别类型数据,相比于 OrdinalHyperparameter, 不需要有序
UniformFloatHyperparameter
均匀分别 float
UniformIntegerHyperparameter
均匀分布 int
NormalIntegerHyperparameter
正态分布 int
如果生成 config
| |
主要类
SMBO
功能:
- Sequential Model-Based Bayesian Optimization
- BayesOpt 优化迭代循环的实现
使用的组件:
class smac.main.smbo.SMBO(scenario, runner, runhistory, intensifier, overwrite=False)[source] (scenario, runner, runhistory, intensifier)
运行结果的处理
SMBO._incorporate_run_results(run_info: RunInfo, result: RunValue, time_left: float)- RunInfo: config, instance, seed, …
- RunValue: cost, time, status, ….
功能:
- 添加运行结果到
SMBO.runhistory 保存状态
- SMBO.stats
- SMBO.runhistory
更新运行状态和 通过 intensifier.processe_results() 计算得到最好内
incumbent(最好结果)可以参考:
simple_intensifier中processe_results实现方法- 内部调用
AbstractRacer._compare_configs()
- 内部调用
- 存储在 SMBO.incumbent
AbstractRacer._compare_configs(): 真正确定哪一个 config 是最优点的方法内部通过 cost = RunHistory.average_cost(config) 类比较哪一个更小,选择下一个 incument
注意:这个只适合单目标,如果需要做多目标,需要在生成的RunValue 结果就要把多目标转换成单目标形式
评价机制:
- 执行的参数相等,或者更多
- 使用相同 (instance, seed) 的情况下,执行的结果, cost 更小
- 添加运行结果到
RunHistory 内部的
self._cost(config) –> costs –> self.average_cost(config) –> normalize_costs(costs) –> mean(costs) –> 多次运行的平均值
获取 costs 的方法:
通过获得一个config 对应的多个
InstSeedBudgetKey–> tuple[intances, seed, budget]- self._configid_to_inst_seed_budget Dict[Tuple[instance, seed]] = List[budget]
- 内部只取 budget 最大的那个
- 获得多个相关(实际上是多次运行的相同的 config)的数据, 获取 RunKey, 进而获取到 多次运行的 RunValue.cost
- tuple[instance, seed]表示的是一种运行初始化数据, budget 是实际消耗的费用或者时间, RunValue.cost 是黑盒函数的效果的评估(比如模型的召回率)
- costs 的 normalize_costs 涉及到 objective 的取值范围, self.objective_bounds
Facade (AbstractFacade) || smac.facade 模块
功能:
- SMBO 贝叶斯优化循环的包裹类,用来整合 SMACv3 中的各个组件
Facade 类别
算法优化(Algorithm): SMAC4AC, smac.facade.smac_ac_facade.SMAC4AC
算法、模型的优化
要点: SMAC4AC 类的init 方法文档中包含了各种类型参数的说明
黑盒函数优化(blackbox function): SMAC4BB, smac.facade.smac_ac_facade.SMAC4BB
特点:
- 使用高斯过程
超参数优化(hyperparameter): SMAC4HPO, smac.facade.smac_ac_facade.SMAC4HPO
特点:
- 很大程度上类似 SMAC4AC(algorithm configuration)
TrajLogger 优化记录保留
- 在 Facade 中初始化
Scenario
作用:
- smac 各个工具初始化使用的参数储存容器,它自己并不会直接使用这些参数
存储的参数:
Runner 相关
- 参考位置: BaseRunner
- ta :: target algorithm
Runner
作用:
- 负责实际算法(黑盒函数)或者实际目标训练模型的调用
- 把超参数转换成 –> 运行结果 (损失函数,运行状态数据)
参数:
run_obj:
取值:
- quality
- 优化 cost_function 损失函数,提升算法的质量,如精度
- runtime
- 优化算法的运行时间,提高算法的执行时间(速度)
特点:
- 调用顺序: self.submit_run() –> self.run_wrapper() –> self.run()
- 结果存储: self.results (一个列表)
- 自定义一个Runner需要实现 self.run() 方法
self.run()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23@abstractmethod def run( self, config: Configuration, instance: str, cutoff: Optional[float] = None, seed: int = 12345, budget: Optional[float] = None, instance_specific: str = "0", ) -> Tuple[StatusType, float, float, Dict]: """Runs target algorithm <self.ta> with configuration <config> on """ pass # 调用例子 status, cost, runtime, additional_info = self.run( config=run_info.config, instance=run_info.instance, cutoff=cutoff, seed=run_info.seed, budget=run_info.budget, instance_specific=run_info.instance_specific, )
类型:
- 黑盒函数:
ExecuteTAFuncDict,smac.tae.execute_func.ExecuteTAFuncDict
Runhistory
数据:
Runhistory.data:
1 2 3 4 5 6 7 8OrderedDict([ (RunKey(config_id=1, instance_id=None, seed=0, budget=0.0), RunValue(cost=-2.2500963017546054, time=3.719329833984375e-05, status=<StatusType.SUCCESS: 1>, starttime=1736151483.725089, endtime=1736151483.7251318, additional_info={})), (RunKey(config_id=2, instance_id=None, seed=0, budget=0.0), RunValue(cost=-2.0912465314361626, time=3.1948089599609375e-05, status=<StatusType.SUCCESS: 1>, starttime=1736151483.8881495, endtime=1736151483.8881872, additional_info={})), (RunKey(config_id=3, instance_id=None, seed=0, budget=0.0), RunValue(cost=-2.8037686916529245, time=3.4332275390625e-05, status=<StatusType.SUCCESS: 1>, starttime=1736151484.017562, endtime=1736151484.017602, additional_info={})) ])Runhistory.ids_config:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19{1: Configuration(values={ 'x0': 0.764471385627985, 'x1': 0.03850575536489487, 'x2': 0.400804003700614, 'x3': 0.22312777861952782, }) , 2: Configuration(values={ 'x0': 0.7365197642826136, 'x1': 0.5236543953848458, 'x2': 0.6667633501189131, 'x3': 0.10099340067826357, }) , 3: Configuration(values={ 'x0': 0.8637879149190518, 'x1': 0.08961023240372623, 'x2': 0.20420678034113104, 'x3': 0.2707453344500017, }) }
数据遍历方法:
history.data.key –> config_id –> history.id_configs[config_id] –> config
- 损失函数: history.data.value.cost –> cost
- 超参数: history.id_configs[history.key.config_id]
结果保存
保存文件夹中的 runhistory.json
| |
结果保存数据
- runhistory.json : config 和 cost 记录
- stats : 运行的总体状况数据汇总,用时、迭代次数
- configspace: 参数取值范围
- traj.json: 每一次黑盒函数运行的具体运行数据
deepseek 对话 prompt:
- smac epm中的featture 是什么,感觉不是 超参数(config)
- 举一个同时包含config和figure的epm 训练数据的例子
FAQ
命名方式中的 def 是什么意思
- default
eg: runhistory_def_kwargs Vs runhistory_kwargs
1 2 3 4 5 6 7 8 9 10 11runhistory_def_kwargs = {} if runhistory_kwargs is not None: runhistory_def_kwargs.update(runhistory_kwargs) if runhistory is None: runhistory = RunHistory(**runhistory_def_kwargs) elif inspect.isclass(runhistory): runhistory = runhistory(**runhistory_def_kwargs) # type: ignore[operator] # noqa F821 elif isinstance(runhistory, RunHistory): pass else: raise ValueError("runhistory has to be a class or an object of RunHistory")
scenario.rand_prob | rand_conf_chooser_kwargs
可能的含意: 参数探测的探测 Vs 挖掘倾向性比率
关联类: ChooserProbe
输出目录: output_dir
Scenario({"output_dir": …})
| |
实际输出目录
create_output_directory(output_dir, run_id) 生成
即使 output_dir 和 run_id 一致,也不会使用原来的那个,而是重命名旧的,创建新的
smac 如何把 Configuration 转换成 np.ndarray 的?
参考:
- smac v1.2: smac/runhistory/runhistory2epm.py
方法:
使用 Configuration.get_array():
np.array([config.get_array() for config in configs], dtype=np.float64)
- 注意:这种方法,会让数据防缩到0~1之间
源码:
| |
依赖兼容性
smac 1.2
| |
文章作者
上次更新 2025-09-24 (360d44c)