- 机器学习
- AutoML
【免费下载链接】hyperopt
Distributed Asynchronous Hyperparameter Optimization in Python
本文是 Hyperopt 分布式异步超参数优化实战指南,围绕MongoTrials后端与hyperopt-mongo-worker工作进程,完整讲解如何利用 MongoDB 将单机串行的fmin搜索扩展为多进程并行、可持久化的异步搜索。读完本文,你将掌握 mongod 的启动与配置、fmin的异步调用改造、worker 的命令行参数与工作循环原理,以及实验数据的持久化复用方法。
为什么需要 MongoDB 后端
Hyperopt 在设计上支持多种不同的 trial 数据库(trial database)。默认的Trials对象基于 Python 列表与字典实现(见 hyperopt/base.py 中asynchronous = False的定义),它是官方提供的参考实现,易于使用,但不支持异步更新——也就是说,它无法支撑多个进程同时写入各自的评估结果,因此无法用于并行搜索。
为了并行化搜索,Hyperopt 在 hyperopt/mongoexp.py 中提供了MongoTrials实现:它以 MongoDB 作为进程间通信(IPC)与持久化存储介质,将搜索建议、评估结果、日志统一存入库中,天然支持异步更新。MongoTrials在源码中显式声明了asynchronous = True(mongoexp.py),fmin内部的FMinIter会从trials.asynchronous继承这一行为(fmin.py),从而走异步调度路径。
要运行并行化搜索,需要依次完成三件事(前提是已安装 MongoDB,参考 安装说明):
- 在网络可达的位置启动一个
mongod进程; - 把对
hyperopt.fmin的调用改为使用连接该 mongod 的MongoTrials后端; - 启动一个或多个
hyperopt-mongo-worker进程,同样连接到该 mongod,在fmin阻塞期间真正执行搜索。
第一步:启动 mongod 进程
MongoDB 安装完成后,启动数据库进程mongod非常简单,例如:
mongod --dbpath . --port 1234 # 或者为每个数据库分配独立目录,推荐方式: mongod --dbpath . --port 1234 --directoryperdb --journal --nohttpinterface # 或者以守护进程方式启动: mongod --dbpath . --port 1234 --directoryperdb --fork --journal --logpath log.log --nohttpinterface几条关键的部署建议(均来自原文档,实践中值得逐一落实):
- 注意磁盘预分配:Mongo 出于性能考虑会习惯性预分配数 GB 空间(可用
--noprealloc关闭),所以在创建数据库之前要想清楚把库放在哪里。 - 避免网络文件系统:把数据库建在网络文件系统上,不仅会让你的数据库性能极差,还会拖累网络上其他所有用户,务必谨慎。
- 注意安全暴露:如果机器对互联网可见,要么只绑定回环接口(loopback)并通过 ssh 连接,要么阅读 MongoDB 官方文档启用密码保护。
本教程后续内容均基于localhost 的 1234 端口上运行的 mongo。
第二步:使用 MongoTrials 改造 fmin 调用
假设一个最简单的场景:用 hyperopt 最小化math.sin函数。在进程内串行运行时,可以这样写:
import math from hyperopt import fmin, tpe, hp, Trials trials = Trials() best = fmin(math.sin, hp.uniform('x', -2, 2), trials=trials, algo=tpe.suggest, max_evals=10)若要改用 MongoDB 做实验的持久化存储,把Trials换成MongoTrials即可:
import math from hyperopt import fmin, tpe, hp from hyperopt.mongoexp import MongoTrials trials = MongoTrials('mongo://localhost:1234/foo_db/jobs', exp_key='exp1') best = fmin(math.sin, hp.uniform('x', -2, 2), trials=trials, algo=tpe.suggest, max_evals=10)MongoTrials 连接串的语义
- 第一个参数告诉
MongoTrials使用哪个 mongod 进程、以及该进程中的哪个数据库(这里为foo_db)。从源码看,连接串会被MongoJobs.new_from_connection_str解析为协议、用户名、密码、主机名、端口、数据库名与集合名(mongoexp.py),并支持带认证的 URI 格式,例如mongo://hyperopt:foobar@127.0.0.1:27017/hyperoptdb/jobs?authSource=db1(该格式由 test_mongoexp.py 中的test_parse_url用例验证)。 - 第二个参数
exp_key='exp1'用于在同一个数据库内部给某一组 trial 打标签,便于区分实验;该参数技术上可选。 - 注意(N.B.):当前实现有一个硬性要求——数据库名后面必须跟
/jobs(即形如<db>/jobs)。main_worker_helper中也有相应兜底逻辑:若连接串不含/jobs会自动补上(mongoexp.py)。
数据库 vs exp_key:两种组织策略
把 trial 放在不同数据库里,还是用 exp_key 区分,取决于你的偏好:
- 用独立数据库:可以从 shell 直接操作(每个库表现为独立文件),且实验之间的独立性/隔离性更强;
- 用 exp_key:
hyperopt-mongo-worker进程是在数据库级别轮询的,因此同一数据库下的多个实验可以同时被同一批 worker 支撑,资源复用更高效。
第三步:运行 hyperopt-mongo-worker
如果你直接运行上面的代码片段,会看到调用在fmin处阻塞(挂起)。原因在于:MongoTrials对fmin自述为一个异步(asynchronous)trial 对象,fmin在算法提出新的搜索点后并不会亲自评估目标函数,而是静静等待其他进程完成评估并把结果写回 mongodb(这正是FMinIter中block_until_done=self.asynchronous的行为,见 fmin.py)。
hyperopt-mongo-worker脚本位于 hyperopt 的bin目录,安装 hyperopt 时它应当已被放入你的$PATH。在fmin阻塞期间,另开一个 shell 执行:
hyperopt-mongo-worker --mongo=localhost:1234/foo_db --poll-interval=0.1该 worker 会从 mongodb 中取出(dequeue)一个工作任务,评估math.sin函数,再把结果写回数据库。当fmin尝试完足够多的搜索点后便会返回,上面的脚本随之终止;hyperopt-mongo-worker会继续在附近等待几分钟以发现新的任务,然后自行退出。
这里显式设置了--poll-interval=0.1,因为 worker 的默认轮询节奏是为单个任务耗时至少一两分钟的作业设计的(源码中默认值为 5 秒,见下节参数表);对math.sin这种微秒级任务,必须调小轮询间隔,否则大量时间浪费在空等上。
hyperopt-mongo-worker 完整参数表
main_worker使用optparse解析命令行参数(mongoexp.py),所有可用参数及默认值如下:
| 参数 | 默认值 | 说明 |
|---|---|---|
--mongo | localhost/hyperopt | <host>[:port]/<db>,用于 IPC 与任务存储的连接串 |
--poll-interval | 5 | 每 1 < T < N 秒检查一次工作队列(单位:秒) |
--exp-key | None | 本 worker 任务的标识符(按 exp_key 限定可领取的任务) |
--reserve-timeout | 120.0 | 轮询数据库领取任务的超时上限(秒),超时抛ReserveTimeout |
--max-jobs | sys.maxsize | 运行完这么多任务后停止(默认相当于无限) |
--max-jobs-in-db | sys.maxsize | 数据库中的任务数达到该阈值后停止 |
--max-consecutive-failures | 4 | 连续 N 个任务失败则停止 |
--last-job-timeout | None | 超过 T 秒后不再领取新任务 |
--workdir | None | 工作根目录(默认从 mongo 中加载) |
--no-subprocesses | 使用子进程(默认开启) | 关闭子进程模式,让目标函数与 worker 同进程运行,便于跨评估复用大块内存数据,但需警惕内存泄漏 |
其中--poll-interval同时控制 worker 空转时的随机休眠区间:MongoWorker在未领取到任务时,会按1 + random * (poll_interval - 1)秒随机休眠后再次轮询(mongoexp.py),避免多 worker 同时打满数据库。
源码级原理:worker 的工作循环与任务状态机
要理解hyperopt-mongo-worker为何能支撑并行搜索,需要从 hyperopt/mongoexp.py 顶部的模块文档与MongoWorker.run_one(mongoexp.py)看起。
IPC 依赖的三个集合
实验通过三个 MongoDB 集合完成进程间通信:
jobs:存储建议的 trial 及其结果的标准文档,关键字段包括:spec:搜索算法suggest返回的子文档;exp_key:哪个 driver 建议了该 trial 的标识;cmd:标识如何调用evaluate的(protocol, ...)元组;state:任务状态 0/1/2/3(新建、运行中、成功、失败);owner:新任务为None,被领取后为(hostname, pid);book_time/refresh_time:任务被保留的时间与运行进程最近一次签到时间;result:evaluate返回的结果子文档;error:失败(state=3)任务的原因;logs:由 ctrl 对象写入的 info/warn/error 消息序列字典。
fs:GridFS 存储集合,用于 pickle 序列化大对象;drivers:描述 driver 的文档,用于防止两个 driver 同时使用同一 exp_key,并可挂载实验类保存状态、pickle 的 bandit 重建参数等附件。
状态机与抢占式领取
任务状态常量定义在 hyperopt/base.py:JOB_STATE_NEW = 0、JOB_STATE_RUNNING = 1、JOB_STATE_DONE = 2、JOB_STATE_ERROR = 3。
worker保留(reserve)任务的方式是:在jobs集合中原子地找到一个owner为None且state为 0 的文档,并将其状态置为 1(mongoexp.py)。若找不到这样的任务,就随机休眠几秒后继续轮询。MongoWorker.run_one的完整流程是:循环mj.reserve()直到成功(受reserve_timeout限制)→ 从job['misc']还原 spec → 按cmd协议反序列化目标函数 → 在临时工作目录中执行worker_fn(spec, ctrl)→ 将结果SONify后通过ctrl.checkpoint(result)写回 → 最后把任务状态更新为JOB_STATE_DONE(失败则写JOB_STATE_ERROR并记录异常类型与信息)。
信号控制与子进程模式
main_worker_helper(mongoexp.py)提供了两类运维能力:
- 信号处理:
SIGINT/SIGTERM/SIGHUP触发干净关闭(Shutdown);非 Windows 平台下SIGUSR1触发“等当前子进程正常结束后退出”(WaitQuit)。 - 子进程隔离:默认开启子进程模式——每个任务通过递归
Popen以--max-jobs=1再启动一个独立进程执行,以防护内存泄漏与不良清理;代价是大型数据集需要在每个子进程中重新加载一次。这就是--no-subprocesses参数存在的意义。
MongoTrials 是持久化对象:重跑、扩展与只读分析
MongoTrials的数据全部落在 mongodb 中,因此它天然具备持久化语义。如果你把上面的示例再运行一次:
best = fmin(math.sin, hp.uniform('x', -2, 2), trials=trials, algo=tpe.suggest, max_evals=10)会看到它立刻返回、什么都没发生——因为所连接的数据库里已经有足够的 trial 了,第一次实验的结果早已计算并存储在其中。基于这一特性,可以灵活操作:
- 开始一次新搜索:更换数据库名或
exp_key; - 扩展当前搜索:用更大的
max_evals再次调用fmin,继续追加搜索点; - 只读分析:另起进程创建
MongoTrials,仅用于分析库中已有结果,完全不需要调用fmin。
从实现上看,MongoTrials本质上是MongoJobs集合的包装器(mongoexp.py),它面向“读取trial 数据库”设计;若需要直接修改集合中的文档,应取.handle属性(MongoJobs实例)使用底层方法或 pymongo 接口,写完后调用refresh()/refresh_tids()让本地视图与数据库同步。
验证与测试:仓库里的集成测试怎么做
仓库在 hyperopt/tests/integration/test_mongoexp.py 中提供了针对 MongoDB 后端的集成测试,可直接作为本地环境验证的参考:
TempMongo上下文管理器(test_mongoexp.py)会以mongod --dbpath ... --noprealloc --port=22334启动临时数据库实例,等待就绪后返回连接串localhost:22334/<db>/jobs,退出时清理进程与目录;test_parse_url覆盖了带用户名密码与authSource参数的连接串解析;- 测试中还通过
main_worker_helper、MongoWorker、ReserveTimeout等组件验证 worker 侧的领取与执行链路(test_mongoexp.py)。
你可以用pytest hyperopt/tests/integration/test_mongoexp.py运行这组测试,确认本机 mongod 与 hyperopt 的 mongo 后端配合正常。
小结:从串行到并行的三步改造
把一次 hyperopt 搜索从单进程串行改为 MongoDB 驱动的并行异步搜索,核心动作可以浓缩为三步:启动 mongod(注意预分配、网络文件系统与安全绑定问题)→ 将fmin的trials参数从Trials换成指向mongo://host:port/db/jobs的MongoTrials(可用exp_key在库内隔离实验)→ 启动一个或多个hyperopt-mongo-worker承担实际评估(按任务耗时调节--poll-interval,按资源情况决定是否使用子进程模式)。之后,MongoDB 会替你持久化每一次评估,搜索可以随时扩展、复用或由只读进程分析——这正是 Hyperopt 分布式异步超参数优化的核心工作方式。
- 机器学习
- AutoML
【免费下载链接】hyperopt
Distributed Asynchronous Hyperparameter Optimization in Python
相关推荐
DiffSynth Studio:重新定义扩散模型应用边界的全能引擎
DiffSynth Studio:重新定义扩散模型应用边界的全能引擎 在人工智能内容生成的浪潮中,你是否曾为复杂的模型部署而烦恼?是否曾因显存不足而无法体验最新
人工智能大模型媒体生成深度学习预训练微调使用 SparkTrials 在 Apache Spark 集群上分布式扩展 Hyperopt 超参数搜索
使用 SparkTrials 在 Apache Spark 集群上分布式扩展 Hyperopt 超参数搜索 Hyperopt 的 SparkTrials 类可以
机器学习AutoMLHyperopt MongoDB配置完整指南:从零开始搭建分布式参数优化系统
Hyperopt MongoDB配置完整指南:从零开始搭建分布式参数优化系统 Hyperopt是一个强大的Python库,专门用于分布式异步超参数优化。通过Mo
机器学习AutoML
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考