Optuna 试验被意外杀死后卡在 RUNNING 状态怎么用 heartbeat 机制自动标记失败
【免费下载链接】optunaA hyperparameter optimization framework项目地址: https://gitcode.com/GitHub_Trending/op/optuna
在集群环境里跑 Optuna 优化时,一个常见问题是:正在执行某个 trial 的进程被任务调度器或外部条件意外杀死,而 storage 里这个 trial 的状态会一直停留在RUNNING,直到你手动删除或更新它的状态。Optuna 针对这种情况提供了 heartbeat(心跳)机制:只要用支持心跳的RDBStorage并开启heartbeat_interval,当一个进程死亡后,该进程上正在运行的 trial 会在心跳超时后自动从RUNNING变为FAIL。本文说明如何配置这一机制、如何验证它生效,以及在ask/tell工作流下该怎么处理。
前置条件:哪些存储和工作流支持 heartbeat
- 必须使用支持 heartbeat 的存储后端。文档中明确给出的是 RDBStorage(optuna/storages/_rdb/storage.py);
InMemoryStorage、JournalStorage等没有该能力。 - heartbeat 机制标注为 experimental,API 未来可能变化(FAQ 与
RDBStorage文档字符串中都有此说明)。 - 心跳机制设计上配合
Study.optimize使用。FAQ 明确警告:如果你用的是Study.ask和Study.tell,心跳自动标记失效,需要自己显式调用study.tell更新状态(见下文"ask/tell 工作流下的替代做法")。 - FAQ 另有一条相关提示:文档不推荐用 SQLite3 做并行优化,可参考 docs/source/faq.rst 中
sqlite_concurrency一节;如果确实要在多进程间共享存储,FAQ 建议考虑JournalFileBackend等替代方案(但这与 heartbeat 机制不兼容,heartbeat 只在 RDB 后端上工作)。
配置方法:开启 heartbeat 的 RDBStorage
heartbeat 有三个相关参数,都在RDBStorage的构造参数里(参数含义来自 optuna/storages/_rdb/storage.py 的文档字符串):
heartbeat_interval:心跳记录间隔(秒),必须是正整数,None表示不启用;grace_period:最后一次心跳之后,经过多少秒才把仍在运行的 trial 判为失败(stale)。必须是正整数或None;为None时取2 * heartbeat_interval;heartbeat_stale_trial_callback:可选回调,在每个stale trial 被标记失败之后被调用,签名是(study: optuna.study.Study, trial: FrozenTrial) -> None。文档说明该检查流程发生在一个新 trial 被 ask 出来之前,也就是"下一轮 trial 启动时"才处理上一轮的僵尸 trial。
FAQ 给出的最小可用示例(sqlite:///:memory:仅用于演示,实际使用时替换为你自己的数据库 URL):
import optuna def objective(trial): (Very time-consuming computation) # 这里放你真实的目标函数计算 # Recording heartbeats every 60 seconds. # Other processes' trials where more than 120 seconds have passed # since the last heartbeat was recorded will be automatically failed. storage = optuna.storages.RDBStorage(url="sqlite:///:memory:", heartbeat_interval=60, grace_period=120) study = optuna.create_study(storage=storage) study.optimize(objective, n_trials=100)也就是说:心跳每 60 秒记录一次;如果某个其他进程上的 trial 超过 120 秒没有新心跳,就会被自动失败化。
执行后如何验证
判断依据是文档描述的状态迁移:被杀死进程的 trial 会从TrialState.RUNNING变为TrialState.FAIL。你可以用study.get_trials(states=[optuna.trial.TrialState.RUNNING])查看仍标记为运行的 trial——进程被杀且超过grace_period之后,再次进入优化循环(新 trial 启动时触发 stale 检查)再查,僵尸 trial 不应再出现在RUNNING列表中,而应在FAIL状态里。
内部实现上,study.optimize在每轮 trial 前会调用optuna.storages.fail_stale_trials(study)(见 optuna/study/_optimize.py 与 optuna/storages/_heartbeat.py 中fail_stale_trials的实现):它把所有心跳超时的运行中 trial 置为FAIL,然后触发你注册的失败回调。因此"新 trial 启动"是触发时机:如果你杀掉了唯一在跑的进程但优化循环不再继续,stale 检查也不会再执行。
可选增强:自动重试被标记失败的 trial
如果僵尸 trial 被失败化后你希望自动重跑,而不是仅仅留下一个FAIL记录,可以在RDBStorage上挂 RetryHeartbeatStaleTrialCallback。FAQ 给出的示例:
import optuna from optuna.storages import RetryHeartbeatStaleTrialCallback storage = optuna.storages.RDBStorage( url="sqlite:///:memory:", heartbeat_interval=60, grace_period=120, heartbeat_stale_trial_callback=RetryHeartbeatStaleTrialCallback(max_retry=3), ) study = optuna.create_study(storage=storage)该回调的作用是把 stale trial 重新创建为TrialState.WAITING排入队列再跑一次(文档描述其适用于 worker 被抢占、进程意外退出等外部条件导致 trial 变 stale 的环境)。几个可用细节:
max_retry:最多重试次数,None(默认)表示无限重试,设为整数则只重试该次数;inherit_intermediate_values:重试的新 trial 是否继承原 trial 通过trial.report上报的intermediate_values,默认False;- 原 trial 与重试 trial 的对应关系可通过
RetryHeartbeatStaleTrialCallback.retried_trial_number(trial)取到原始 trial 编号,retry_history(trial)取到完整的重试链列表(含原始 trial 编号)。
注意文档说明回调是在每个新 trial 开始评估时检查并处理 stale trial,所以重试同样依赖优化循环继续运行。
ask/tell 工作流下的替代做法
如上所述,heartbeat 只对optimize生效。FAQ 给出了ask/tell场景下手动处理僵尸 trial 的示例(原文说明该示例中的grace_period需要用户按自身场景调整,例子里假设"运行超过 1 天大概可视为僵尸"):
from datetime import datetime import optuna study = optuna.create_study(storage=...) # 替换为你的 storage # User needs to tweak here. For example, the case below assumes that if trial is running # for 1 day, this trial is probably a zombie. grace_period = 3600*24 for t in study.get_trials(states=[optuna.trial.TrialState.RUNNING]): if (datetime.now() - t.datetime_start).total_seconds() > grace_period: study.tell(t, state=optuna.trial.TrialState.FAIL)限制与已知边界
- heartbeat 机制是 experimental 的,FAQ 和
RDBStorage参数文档都提示 API 可能变化。 RDBStorage还有一个旧参数failed_trial_callback:在 v4.9.0 起被弃用、计划在 v6.0.0 移除,文档明确要求改用heartbeat_stale_trial_callback;两个参数同时传会直接抛ValueError。- 心跳只解决"检测并标记",不会恢复被杀 trial 的现场;需要恢复中间值时只能依赖
RetryHeartbeatStaleTrialCallback的inherit_intermediate_values(且前提是原 trial 已经通过trial.report上报过 intermediate values)。 - 自动标记的触发点是"下一次有新 trial 被 ask 出来时"(
optimize循环内部),因此它不是独立的后台巡检进程,优化循环停止后不会再有自动状态更新。
相关文档入口:FAQ heartbeat 监控一节、optuna.storages API 参考、RDBStorage 实现、心跳实现、重试回调实现。
【免费下载链接】optunaA hyperparameter optimization framework项目地址: https://gitcode.com/GitHub_Trending/op/optuna
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考