拓冰建站拓冰建站
首页 / 资讯中心 / 正文

Optuna 的 optuna.search_space 模块:交集搜索空间(IntersectionSearchSpace)的原理与实战

Optuna 的 optuna.search_space 模块交集搜索空间IntersectionSearchSpace的原理与实战【免费下载链接】optunaA hyperparameter optimization framework项目地址: https://gitcode.com/GitHub_Trending/op/optuna本文围绕 optuna.search_space 参考文档 展开系统讲解 Optuna 中交集搜索空间的语义、两个公开 APIintersection_search_space函数与IntersectionSearchSpace类的完整用法与参数细节并结合仓库源码深入剖析其增量缓存算法、状态过滤规则以及在 TPE/CMA-ES/NSGA-II 等采样器内部的真实调用链路帮助你在自定义采样器、参数重要性分析与优化终止判断中正确使用该模块。模块概览optuna.search_space 提供什么optuna.search_space模块用于计算和控制 Study 中参数的搜索空间其公开 API 由 optuna/search_space/init.py 导出名称可见性作用intersection_search_space公开无状态函数对给定 trial 列表直接计算交集搜索空间IntersectionSearchSpace公开可复用的有状态类带增量缓存反复calculate更高效_GroupDecomposedSearchSpace/_SearchSpaceGroup内部下划线前缀将搜索空间按参数集合完全一致的子空间进行分组分解供 TPE 的groupTrue模式使用核心文件位于 optuna/search_space/intersection.py 与 optuna/search_space/group_decomposed.py。什么是交集搜索空间在 Optuna 中每一次suggest调用都会在对应 trial 上记录一个参数分布如FloatDistribution(low, high, log, step)并保存在FrozenTrial.distributions中见 optuna/trial/_frozen.py 中distributions属性的定义。由于目标函数内部常有条件分支不同 trial 实际建议的参数集合可能不同。交集搜索空间的规则是取所有感兴趣的已记录 trial 的参数分布交集一个参数只有在其名称与分布对象在所有相关 trial 中都完全一致时才被保留如果同一名称在不同 trial 中对应了不同分布即动态值域该参数整体被排除一旦交集为空后续 trial 也不会使其恢复——交集只会越来越小这一点在测试 tests/search_space_tests/test_intersection.py 的注释 The search space remains empty once it is empty 中得到验证。API 一intersection_search_space函数签名见 optuna/search_space/intersection.pydef intersection_search_space( trials: list[optuna.trial.FrozenTrial], include_pruned: bool False, ) - dict[str, BaseDistribution]:trialsFrozenTrial列表通常来自study.get_trials(deepcopyFalse)include_pruned是否把 PRUNED 状态的 trial 纳入计算默认False返回值{参数名: 参数分布}字典按键名排序表示这些 trial 的交集搜索空间。文档中的 note 特别指出如果追求更低执行时间应改用IntersectionSearchSpace类它带有缓存函数版适合一次性计算的场景。可复制的最小示例import optuna from optuna.distributions import FloatDistribution from optuna.search_space import IntersectionSearchSpace, intersection_search_space study optuna.create_study() def objective(trial: optuna.Trial) - float: x trial.suggest_float(x, 0.0, 10.0) y trial.suggest_int(y, -5, 5) return x ** 2 y study.optimize(objective, n_trials10) # 方式一有状态类可反复调用、带缓存 space IntersectionSearchSpace() result space.calculate(study) print(sorted(result.keys())) # [x, y]按键名排序 print(result[x]) # FloatDistribution(low0.0, high10.0, ...) # 方式二无状态函数一次性计算 trials study.get_trials(deepcopyFalse) print(intersection_search_space(trials)) # {x: FloatDistribution(...), y: IntDistribution(low-5, high5, ...)}动态值域会被整体排除def dynamic_objective(trial: optuna.Trial) - float: # 不同 trial 用不同值域建议同名参数 x if trial.number % 2 0: x trial.suggest_float(x, 0.0, 1.0) else: x trial.suggest_float(x, 2.0, 3.0) return x study2 optuna.create_study() study2.optimize(dynamic_objective, n_trials4) print(IntersectionSearchSpace().calculate(study2)) # {}这与测试文件中suggest_float(y, -1, 1)与之前的FloatDistribution(-3, 3)冲突后搜索空间变为{}的用例完全一致tests/search_space_tests/test_intersection.py。API 二IntersectionSearchSpace类class IntersectionSearchSpace: def __init__(self, include_pruned: bool False) - None: ... def calculate(self, study: Study, use_cache: bool False) - dict[str, BaseDistribution]: ...关键行为源码见 optuna/search_space/intersection.py单 Study 约束一个实例只能用于一个 study。calculate首次调用会记录study._study_id之后传入不同 study 会抛出ValueError(IntersectionSearchSpacecannot handle multiple studies.)。测试 tests/search_space_tests/test_intersection.py 专门用 sqlite storage 下两个 study 验证了该异常。源码注释还指出在InMemoryStorage下该检查无意义因为它的create_new_study总是返回同一个 study IDuse_cache参数透传给study._get_trials(deepcopyFalse, use_cacheuse_cache)控制是否复用 study 侧的 trial 缓存从而减少数据库/存储查询开销返回值语义calculate返回按参数名排序的deepcopy副本调用方修改返回字典不会影响内部缓存。增量计算原理_calculate的缓存机制底层实现 optuna/search_space/intersection.py 中的_calculate(trials, include_pruned, search_space, cached_trial_number)是理解性能差异的关键状态过滤默认只统计COMPLETE、WAITING、RUNNING三种状态的 trialinclude_prunedTrue时追加PRUNED。FAILED 状态永远不参与逆序扫描 增量更新从最新 trial 向前遍历遇到第一个关心的 trial 时记录next_cached_trial_number trial.number 1作为下次缓存失效点若遇到未完成WAITING/RUNNING的 trial则把失效点设为该 trial 的编号——因为它一旦完成可能新增/改变分布缓存必须从它重算cached_trial_number提前终止当扫描到编号小于上一次缓存点的 trial 时直接break因此对同一个 study 反复调用calculate时只有新产生的 trial 需要参与交集运算成本近似 O(新 trial 数) 而非 O(全部 trial 数)。这就是文档 note 中类版比函数版快得多的原因交集运算本身第一个关心的已完 trial 以其trial.distributions为初始空间之后每个 trial 只保留同名且分布对象相等的条目search_space { name: distribution for name, distribution in search_space.items() if trial.distributions.get(name) distribution }因此同名不同分布的参数会在与第一个冲突 trial 相遇时被剔除。该模块在 Optuna 内部的真实用途交集搜索空间并非孤立工具它是 Optuna 相对采样relative sampling机制的基石。optuna/samplers/_base.py 中BaseSampler.infer_relative_search_space的文档明确建议参考intersection_search_space作为实现范本。仓库中可直接检索到的调用点包括调用方位置用途TPE 采样器optuna/samplers/_tpe/sampler.pyinfer_relative_search_space中通过self._search_space.calculate(study, use_trial_cache)得到多变量 TPE 的相对搜索空间并跳过distribution.single()的退化参数groupTrue时改用_GroupDecomposedSearchSpaceCMA-ES 采样器optuna/samplers/_cmaes.py每轮基于交集搜索空间决定哪些参数交给 CMA-ES 优化NSGA-II / NSGA-IIIoptuna/samplers/nsgaii/_sampler.py、optuna/samplers/_nsgaiii/_sampler.py多目标遗传算法中确定本代操作的参数子空间GP 采样器optuna/samplers/_gp/sampler.py直接持有IntersectionSearchSpace()实例并calculate参数重要性分析optuna/importance/_base.pyintersection_search_space(study.get_trials(deepcopyFalse))决定 FANOVA/PEAANOVA 等分析器覆盖哪些参数优化终止判断Terminatoroptuna/terminator/improvement/evaluator.py、optuna/terminator/improvement/emmr.py基于交集搜索空间构建搜索空间用于改进潜力估计自定义采样器中的典型用法官方教程 tutorial/20_recipes/005_user_defined_sampler.py 给出了一段完整可运行的示例基于退火Simulated Annealing的采样器直接调用函数版 API 实现infer_relative_search_spaceclass SimulatedAnnealingSampler(optuna.samplers.BaseSampler): def infer_relative_search_space(self, study, trial): return optuna.search_space.intersection_search_space(study.get_trials(deepcopyFalse)) def sample_independent(self, study, trial, param_name, param_distribution): independent_sampler optuna.samplers.RandomSampler() return independent_sampler.sample_independent(study, trial, param_name, param_distribution)教程还特别提示了一个易错点当 study 中还没有任何 COMPLETE trial 时交集搜索空间为空字典{}此时sample_relative拿不到参数Optuna 会退回sample_independent逐参数采样第一个 trial 就是如此。分组变体_GroupDecomposedSearchSpaceoptuna/search_space/group_decomposed.py 中的内部类把整个搜索空间拆分为若干参数集合互不重叠的子空间_SearchSpaceGroup.add_distributions每纳入一个 trial 的分布就按键集合并集/差集对现有子空间做拆分最终得到一组结构稳定的子搜索空间。它服务于 TPE 的groupTrue模式见 optuna/samplers/_tpe/sampler.py 的_group分支用于缓解分支导致搜索空间频繁变化时多变量模型难以收敛的问题。由于带下划线它不属于公开文档承诺的 API 范围不建议在业务代码中直接依赖。行为边界来自测试用例的完整清单tests/search_space_tests/test_intersection.py 覆盖了以下值得写进设计文档的边界行为每一条都可在源码中找到对应实现无 trial 时返回{}且函数版与类版结果始终相等WAITING 状态的入队 trialstudy.enqueue_trial带 distributions单独存在时不参与交集计算返回{}首个完成 trial 后搜索空间等于该 trial 的distributions拷贝返回值按键名排序list(result.keys()) [x, y]后续 trial 建议的参数集合变少时第二个 trial 只建议y交集收缩为仅含yFAILED抛异常被catch与 PRUNED trial 默认不参与测试中先suggest_float(z, 0, 1)再抛RuntimeError/TrialPruned结果中始终没有z同名不同分布冲突后交集立即清空且永久保持为空一个类实例跨 study 使用时抛出ValueError。实践建议反复查询选类一次性查询选函数优化循环内每个 trial 都要推断相对搜索空间时如自定义采样器持有IntersectionSearchSpace()实例并打开use_cacheTrue利用增量缓存避免重复扫描历史 trial离线批处理如重要性分析则用intersection_search_space更简单直接注意include_pruned的取舍默认只统计 COMPLETE trial如果你的目标函数大量早停且分支结构稳定可显式开启include_prunedTrue以纳入 PRUNED trial 的分布记录使交集更不容易因完成样本不足而收缩动态超参值域随分支变化天然被排除这是设计使然而非 bug——交集搜索空间无法表达同名不同分布的参数对这类参数采样器会退化为sample_independent独立采样或TPE group 模式下被拆入独立子空间处理单 Study 绑定约束若需要同时分析多个 study请为每个 study 创建独立的IntersectionSearchSpace实例而不是复用同一个实例。综上optuna.search_space虽然公开 API 只有两个入口但它是 Optuna 采样器、重要性分析器与终止判断器共同依赖的参数空间事实来源理解其交集语义、状态过滤规则与增量缓存机制是定制采样算法与排查搜索空间相关行为例如为什么我的条件分支参数没被纳入相对采样的前提。【免费下载链接】optunaA hyperparameter optimization framework项目地址: https://gitcode.com/GitHub_Trending/op/optuna创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门