Hydra Optuna Sweeper 插件使用指南:从单目标到多目标超参数优化
【免费下载链接】hydraHydra is a framework for elegantly configuring complex applications项目地址: https://gitcode.com/GitHub_Trending/hyd/hydra
本指南聚焦 Hydra 官方插件hydra-optuna-sweeper,系统讲解如何在 Hydra 应用中接入 Optuna,通过--multirun模式自动完成超参数搜索与优化。读完本文,你将掌握插件的安装与启用、采样器与搜索空间(search space)的完整配置语法(命令行与配置文件两种方式)、单目标与多目标优化的完整实践,以及从源码层面理解其运行机制与结果序列化逻辑。
插件简介与安装
hydra-optuna-sweeper是一个让 Hydra 应用能够调用 Optuna 进行实验参数优化的插件。Optuna 是专注于超参数优化的框架,而该插件将其采样、调优能力接入 Hydra 的--multirun批量运行机制,从而在保留 Hydra 配置管理能力的同时获得智能的搜索策略。
该插件要求hydra-core>=1.1.0,安装前先升级 Hydra 核心:
pip install hydra-core --upgrade然后通过 pip 安装插件:
pip install hydra-optuna-sweeper --upgrade关于配置插件的几种标准方式(如通过defaults列表、Config Store 注册等),可参考仓库文档 配置插件指南。
启用 Optuna Sweeper
在应用的配置文件(或命令行)中,将 Hydra 的 sweeper 切换为optuna:
defaults: - override hydra/sweeper: optuna也可以在命令行追加hydra/sweeper=optuna参数完成切换。
插件的默认配置定义在 config.py,其结构化配置类OptunaSweeperConf通过ConfigStore.instance().store(group="hydra/sweeper", name="optuna", ...)注册到 Hydra 的配置仓库中(见 config.py)。核心字段含义如下:
| 配置项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
sampler | SamplerConfig | tpe | 采样算法,默认 TPE(Tree-structured Parzen Estimator) |
direction | Direction/List[Direction] | minimize | 优化方向,多目标时传方向列表 |
storage | Optional[Any] | null | Optuna 存储后端 URL,如sqlite:///example.db,用于持久化优化结果 |
study_name | Optional[str] | null | 优化研究(study)的名称 |
n_trials | int | 20 | 函数评估总次数 |
n_jobs | int | 2 | 并行 worker 数量 |
max_failure_rate | float | 0.0 | 单批参数允许的最大失败率(0~1) |
params | Optional[Dict[str, str]] | null | 配置文件形式的搜索空间定义 |
custom_search_space | Optional[str] | null | 指向自定义搜索空间配置函数的 dotpath |
可以用以下命令查看当前生效的 sweeper 全部参数:
python example/sphere.py hydra/sweeper=optuna --cfg hydra -p hydra.sweeper示例 1:单目标优化(Sphere 基准函数)
示例目录 plugins/hydra_optuna_sweeper/example 中的sphere.py实现了一个待最小化的简单基准函数。其核心逻辑(见 sphere.py):
@hydra.main(config_path="conf", config_name="config") def sphere(cfg: DictConfig) -> float: x: float = cfg.x y: float = cfg.y if cfg.get("error", False): raise RuntimeError("cfg.error is True") return x**2 + y**2@hydra.main()装饰的函数返回一个 float 作为目标值,我们要最小化它。该函数的最小值为 0,在x: 0, y: 0处取得。
运行优化(需在plugins/hydra_optuna_sweeper目录下):
python example/sphere.py --multirun对应的默认配置输出(通过--cfg hydra -p hydra.sweeper可查看)如下:
# @package hydra.sweeper sampler: _target_: optuna.samplers.TPESampler seed: 123 consider_prior: true prior_weight: 1.0 consider_magic_clip: true consider_endpoints: false n_startup_trials: 10 n_ei_candidates: 24 multivariate: false warn_independent_sampling: true _target_: hydra_plugins.hydra_optuna_sweeper.optuna_sweeper.OptunaSweeper direction: minimize storage: null study_name: sphere n_trials: 20 n_jobs: 1 search_space: x: type: float low: -5.5 high: 5.5 step: 0.5 'y': type: categorical choices: - -5 - 0 - 5对应示例仓库中的配置文件 example/conf/config.yaml:搜索空间定义在hydra.sweeper.params下,x使用range(-5.5, 5.5, step=0.5),y使用choice(-5, 0, 5)。
也可以在命令行直接覆盖搜索空间参数化方式:
python example/sphere.py --multirun 'x=interval(-5.0, 5.0)' 'y=interval(0, 10)'优化结束后,multirun日志目录下会生成optimization_results.yaml,记录最优参数与最优值:
name: optuna best_params: x: 0.0 'y': 0 best_value: 0.0从源码看,该文件的序列化逻辑位于 optuna_sweeper.py 中OptunaSweeper类,它通过instantiate()实例化采样器(白名单限定hydra_plugins.hydra_optuna_sweeper.*与optuna.samplers.*),并将实际逻辑委托给OptunaSweeperImpl。单目标情况下,sweep()结束时调用study.best_trial取出最优试验,写出best_params与best_value(见 optuna_sweeper.py)。
采样器(Sampler)配置
插件支持 Optuna 的全部采样器。可通过覆盖hydra/sweeper/sampler切换采样器类型,或在hydra.sweeper.sampler下调整其内部参数。
从 config.py 可以看出,插件以结构化配置方式预注册了以下采样器,均挂载在hydra/sweeper/sampler配置组下:
| 配置名 | 对应 Optuna 采样器 | 关键参数(默认值) |
|---|---|---|
tpe | optuna.samplers.TPESampler | seed=None、n_startup_trials=10、n_ei_candidates=24、multivariate=False |
random | optuna.samplers.RandomSampler | seed=None |
cmaes | optuna.samplers.CmaEsSampler | seed=None、sigma0=None、restart_strategy=None |
nsgaii | optuna.samplers.NSGAIISampler | seed=None、population_size=50、crossover_prob=0.9、swapping_prob=0.5 |
grid | optuna.samplers.GridSampler | 搜索空间在运行时根据hydra.sweeper.params填充(_partial_=True) |
gp | optuna.samplers.GPSampler | seed=None、n_startup_trials=10 |
qmc | optuna.samplers.QMCSampler | seed=None、qmc_type="sobol"、scramble=False |
此外,由于 Optuna 4.0 移除了motpe采样器,插件保留了motpe配置名但将其_target_指向 config.py 中的raise_motpe_removed函数——一旦使用该配置会抛出明确错误,提示改用hydra/sweeper/sampler=tpe(TPESampler 已原生支持多目标优化)。
搜索空间配置(Search Space)
插件支持用 Optuna 的分布(distributions)来配置搜索空间,既可以通过命令行覆盖定义,也可以在配置文件中定义。
命令行覆盖方式
Hydra 提供了语法丰富的覆盖解析器,其词法/语法定义位于 hydra/grammar/OverrideLexer.g4 与 hydra/grammar/OverrideParser.g4。三种常用的搜索空间覆盖语法如下。
interval 覆盖
默认情况下,interval被转换为FloatDistribution。将区间端点强制为int并使用log标签,可分别得到IntDistribution、LogUniformDistribution语义(在 Optuna 当前 API 中体现为IntDistribution(log=True)/FloatDistribution(log=True))。
python example/sphere.py --multirun 'x=int(interval(-5.0, 5.0))' 'y=tag(log, interval(1, 10))'运行输出示例:
[HYDRA] Study name: sphere [HYDRA] Storage: None [HYDRA] Sampler: TPESampler [HYDRA] Directions: ['minimize'] [HYDRA] Launching 1 jobs locally [HYDRA] #0 : x=-3 y=1.6859762540733367 [HYDRA] Launching 1 jobs locally [HYDRA] #1 : x=1 y=5.237816870668193 ... [HYDRA] Best parameters: {'x': 0, 'y': 1.0929184723430116} [HYDRA] Best value: 1.1944707871885822range 覆盖
range默认转换为IntDistribution(整数均匀分布)。如果对其应用shuffle,则改用CategoricalDistribution,即先枚举区间内的整数再打乱作为类别选择。
python example/sphere.py --multirun 'x=range(-5.0, 5.0)' 'y=shuffle(range(-5, 5))'运行输出示例:
[HYDRA] Study name: sphere [HYDRA] Storage: None [HYDRA] Sampler: TPESampler [HYDRA] Directions: ['minimize'] [HYDRA] Launching 1 jobs locally [HYDRA] #0 : x=-3 y=-4 [HYDRA] Launching 1 jobs locally [HYDRA] #1 : x=1 y=-1 ... [HYDRA] Best parameters: {'x': 0, 'y': -1} [HYDRA] Best value: 1.0choice 覆盖
choice被转换为CategoricalDistribution。
python example/sphere.py --multirun 'x=choice(-5.0, 0.0, 5.0)' 'y=choice(0, 1, 2, 3, 4, 5)'运行输出示例:
[HYDRA] Study name: sphere [HYDRA] Storage: None [HYDRA] Sampler: TPESampler [HYDRA] Directions: ['minimize'] [HYDRA] Launching 1 jobs locally [HYDRA] #0 : x=5.0 y=5 [HYDRA] Launching 1 jobs locally [HYDRA] #1 : x=5.0 y=2 ... [HYDRA] Best parameters: {'x': 0.0, 'y': 0} [HYDRA] Best value: 0.0从源码看,命令行覆盖到 Optuna 分布的转换逻辑集中在 _impl.py 的create_optuna_distribution_from_override函数中:interval结合log标签与端点类型决定分布类型;range依据端点/步长是否为 float 决定FloatDistribution或IntDistribution,遇shuffle则转CategoricalDistribution;choice直接构造类别分布。
配置文件方式
配置文件方式支持三类参数,字段含义如下。
int 参数
type:intlow: 下界high: 上界step: 离散化步长(可选)log: 若为true,搜索空间转换到对数域
当log=false时映射为IntUniformDistribution(即IntDistribution),否则映射为IntLogUniformDistribution(即IntDistribution(log=True))。注意:log=true时不能设置step。
float 参数
type:floatlow: 下界high: 上界step: 离散化步长log: 若为true,搜索空间转换到对数域
当log=false时,依据是否存在step字段分别映射为UniformDistribution(即FloatDistribution,无step)或DiscreteUniformDistribution(即FloatDistribution(step=...),有step);否则映射为LogUniformDistribution(即FloatDistribution(log=True))。同样,log=true时不能设置step。
categorical 参数
type:categoricalchoices: 候选值列表
映射为CategoricalDistribution。
配置示例(对应--cfg hydra -p hydra.sweeper的输出):
search_space: x: type: float low: -5.5 high: 5.5 step: 0.5 'y': type: categorical choices: [-5, 0, 5]固定参数与删除覆盖
命令行解析器(create_params_from_overrides,见 _impl.py)还会把非 sweep 覆盖视为固定参数(fixed params),把~删除覆盖透传为固定覆盖(fixed overrides)。每个 trial 生成时,_configure_trials会调用trial._suggest()采样搜索空间参数、trial.set_user_attr()设置固定参数,并检测搜索空间参数与固定参数是否重叠(重叠会抛出ValueError)。固定参数在创建 study 前会被从 Optuna 搜索空间中移除(见 _impl.py),确保它们不参与采样。
自定义搜索空间扩展
除声明式搜索空间外,插件还支持通过custom_search_space指向一个 instantiate 风格的 dotpath,其签名应为Callable[[DictConfig, optuna.trial.Trial], None],用于在每次 trial 中按 Python 逻辑动态追加参数。仓库示例 example/custom-search-space-objective.py 演示了该能力:
def configure(cfg: DictConfig, trial: Trial) -> None: x_value = trial.params["x"] trial.suggest_float( "z", x_value - cfg.max_z_difference_from_x, x_value + cfg.max_z_difference_from_x, ) trial.suggest_float("+w", 0.0, 1.0) # note +w here, not w as w is a new parameter这里z的取值区间依赖同一个 trial 中已采样的x,+w前缀表示追加一个全新参数。对应的配置见 example/custom-search-space/config.yaml,其中custom_search_space: custom-search-space-objective.configure指向该函数。源码中,该函数在 _impl.py 中通过get_method(custom_search_space)解析,并在_configure_trials中对每个 trial 调用。
示例 2:多目标优化(Binh-and-Korn 基准函数)
同一示例目录下的multi-objective.py实现了具有两个目标值的基准函数,我们要同时最小化这两个目标。其核心逻辑(见 multi-objective.py):
@hydra.main(config_path="multi-objective-conf", config_name="config") def binh_and_korn(cfg: DictConfig) -> Tuple[float, float]: x: float = cfg.x y: float = cfg.y v0 = 4 * x**2 + 4 * y**2 v1 = (x - 5) ** 2 + (y - 5) ** 2 return v0, v1运行优化(在plugins/hydra_optuna_sweeper目录下):
python example/multi-objective.py --multirun对应的多目标配置(通过--cfg hydra -p hydra.sweeper查看):
# @package hydra.sweeper sampler: _target_: optuna.samplers.NSGAIISampler seed: 123 population_size: 50 mutation_prob: null crossover_prob: 0.9 swapping_prob: 0.5 constraints_func: null _target_: hydra_plugins.hydra_optuna_sweeper.optuna_sweeper.OptunaSweeper direction: - minimize - minimize storage: null study_name: multi-objective n_trials: 20 n_jobs: 1 search_space: x: type: float low: 0 high: 5 step: 0.5 'y': type: float low: 0 high: 3 step: 0.5对应的仓库配置文件为 example/multi-objective-conf/config.yaml,其中通过override hydra/sweeper/sampler: nsgaii切换为多目标演化采样器 NSGA-II,direction: [minimize, minimize]声明两个目标均最小化。
对于目标之间存在权衡(trade-off)的问题,往往不存在同时最小化两个目标的单一解,而是得到一组最优解,即 Pareto 最优解,它们展示了各目标之间可能的最佳权衡。下图蓝点即为优化结果中的 Pareto 最优解:
多目标优化示例中的 Pareto 最优解,蓝点为 Pareto 前沿
从源码看,多目标分支的处理在 _impl.py:当direction数量大于 1 时,sweeper 不再取单一best_trial,而是收集study.best_trials组成 Pareto 前沿,序列化到optimization_results.yaml的solutions字段(每个解包含params与values),并在日志中输出 Pareto 解的数量与明细。相应地,被优化的任务函数必须返回与目标数量一致的 float 可转换值列表/元组(见 _impl.py),否则会抛出明确的 ValueError。
底层运行机制:sweep 主循环
理解插件的运行闭环有助于排查问题。从 optuna_sweeper.py 的sweep()实现可以看到整体流程:
- 合并配置文件中的
params与命令行参数,经create_params_from_overrides解析为搜索空间分布、固定参数与固定覆盖; - 若使用
grid采样器,先将离散分布展开为候选值列表(_to_grid_sampler_choices),并把n_trials裁剪为网格组合数; - 通过
optuna.create_study(study_name=..., storage=..., sampler=..., directions=..., load_if_exists=True)创建或复用 study; - 按
n_jobs分批:每批调用study.ask()生成 trial,_configure_trials将分布与固定参数翻译为 Hydra 覆盖串,再交给launcher.launch()批量启动任务; - 收集返回的目标值后
study.tell()回报给 Optuna;单目标要求返回值可转 float,多目标要求返回值与目标数量一致; - 若失败批次占比超过
max_failure_rate,则触发错误并委托给JobReturn抛出真实 traceback; - 循环直至
n_trials耗尽,最后将optimization_results.yaml写入hydra.sweep.dir目录。
这种「study.ask / launcher.launch / study.tell」的闭环设计,使得 Optuna 的采样策略与 Hydra 的并行启动、配置隔离机制解耦,用户自定义 launcher(如 Submitit、Ray 等)也可无缝复用该优化流程。
小结
- 通过
override hydra/sweeper: optuna一行配置即可将 Hydra 应用接入 Optuna 优化; - 搜索空间既可在配置文件中声明(
int/float/categorical三种类型),也可用interval/range/choice在命令行灵活覆盖; - 采样器覆盖 TPE、随机、CMA-ES、NSGA-II、网格、GP、QMC 等多种算法,默认 TPE;
- 多目标优化通过
direction列表 + NSGA-II 等采样器实现,结果以 Pareto 前沿形式写入optimization_results.yaml; - 自定义搜索空间可通过
custom_search_space挂载 Python 回调函数,满足参数间存在依赖关系的复杂场景。
相关文件索引:插件默认配置 | 插件主实现 | 底层实现 | 单目标示例 | 多目标示例
【免费下载链接】hydraHydra is a framework for elegantly configuring complex applications项目地址: https://gitcode.com/GitHub_Trending/hyd/hydra
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考