ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

仿真实验闭环工作流开发教程(10):贝叶斯优化闭环(上)——Ax 的 ask/tell 把仿真目标接成会自我改进的循环

仿真实验闭环工作流开发教程(10):贝叶斯优化闭环(上)——Ax 的 ask/tell 把仿真目标接成会自我改进的循环 仿真实验闭环工作流开发教程10贝叶斯优化闭环上——Ax 的 ask/tell 把仿真目标接成会自我改进的循环版本声明块工具/软件AxPyPIax-platform导入ax文档版 1.3.1现行 APIax.api.client.Clientax.api.configs.RangeParameterConfig默认生成策略 CenterSobolMBMMBM 节点 generator_enumBoTorch语言/环境Python 3.11、SQLite、第 09 篇的simulate(params)-prediction本文目标把下一个该试哪组参数交给 Ax 的 ask/tell并让每个 trial 成对落库铁律 5。一句话结论client.get_next_trials(max_trials1)就是ask、client.complete_trial(trial_indexti, raw_data{...})就是tell把官方 quickstart 的 Booth 目标(x12·x2−7)² (2·x1x2−5)²跑满 20 轮get_best_parameterization()收敛到 ≈(1.49, 2.42)、而解析真值是 (1, 3)——把这条目标函数换成第 09 篇simulate或真实实验回报闭环就从玩具变成会自我改进的配方引擎前提是每个 trial 的参数与回报成对写进 SQLite铁律 5。〇、本篇要解决的认知问题Q1贝叶斯优化的 ask/tell建议/回报范式在闭环里扮演什么角色为什么它天然适配昂贵仿真/实验Q2Ax 1.3.1 现行 API 的调用顺序是什么Client与RangeParameterConfig各负责什么Q3官方 Booth quickstart 实跑 20 轮给的是什么数跟真值差多少这差值说明了什么Q4Ax 默认的 CenterSobolMBM(BoTorch) 生成策略意味着什么它和 BoTorch 是什么关系Q5为什么说trial 落库是闭环正确性的命门铁律 5旧ax.Client顶层导入的坑是什么一、机制解析ask/tell 是 Learn 与 Design 的接头到这里闭环的读03、写07、执行04/06、回传08、仿真09都齐了缺一个下一轮该试什么的大脑。贝叶斯优化Bayesian optimization就是这颗大脑它的交互范式叫 ask/tell建议/回报┌───────────────────────── Ask ──────────────────────┐ │ client.get_next_trials() → 建议参数 x* │ ▼ │ ┌──────────┐ params ┌────────────────┐ prediction │ │ SQLite │──────────▶│ simulate/exp │──────────────▶ │ │ trial 表 │◀──────────│ (第09篇/真机) │ raw_data │ Tell └──────────┘ 成对落库 └────────────────┘ │ ▲ │ └───────────────── 模型更新BoTorch GP──────────────┘为什么它天生适合闭环配方优化一次实验/仿真很贵分钟到小时级评估预算往往是硬约束——你只有再做 20 轮的钱。网格/随机搜索会把大量预算浪费在明显没戏的区域而贝叶斯优化用一个代理模型高斯过程 GP记住哪些点做过、结果如何、哪里还没探明每轮只挑最有信息量的点去问。这里藏着一条贯穿始终的权衡exploration探索未采样的不确定区博取更大改进与 exploitation利用当前模型认为最好的区稳妥收敛采集函数就是给这条权衡定量的旋钮——本篇用 Ax 默认策略把它包好第 11 篇再手工拆开。这正是 DBTL 里 Learn 反哺 Design 的算法化其权威表述见美国国家科学院报告Biotechnology in the Age of Synthetic Biology第 2 章设计原型→构建物理实现→测试功能→从缺陷中学习→反馈下一轮。还要厘清回报到底是什么在第 09 篇语境里tell 回的是simulate(params)的预测值闭环转的是纯仿真环一旦上了真机tell 回的应是第 08 篇从仪器解析出来的真实浓度/读数带单位与不确定度。同一条 ask/tell 循环喂仿真还是喂湿实验决定了这是干实验优化还是干湿闭环——优化器代码一行不改只换 tell 的数据来源这正是把引擎与设备都做无关层第 06/09 篇后换来的红利。现行 API 调用顺序ax-platform 1.3.1官方 quickstart 逐字可跑步骤调用语义建实验client.configure_experiment(name, parameters[RangeParameterConfig(...)])声明搜索空间参数名、bounds、类型定目标client.configure_optimization(objective-1 * booth)声明优化方向这里最小化 booth 即取负最大化Askclient.get_next_trials(max_trials1)要一批建议参数返回 {trial_index: params}Tellclient.complete_trial(trial_index, raw_data{booth: 值})回报这组参数的实测/仿真值收口client.get_best_parameterization()取当前最优参数默认生成策略的含义Ax 是 BoTorch 上层封装Ax 开箱即用不配生成器时会自动走Center起点→ Sobol空间填充冷启动→ MBMModel-Based Meta其节点 generator_enumBoTorch的组合。也就是说你调 Ax前几步在铺 Sobol 采样、之后交给 BoTorch 的 GP 模型做贝叶斯建议——Ax 把 BoTorch 的底层能力包成了配置式 API。想手写 GP 与采集函数、精确控制 q 点批量那是第 11 篇 BoTorch 的活SingleTaskGP→ExpectedImprovement/qExpectedImprovement。一个 v1/v2 式分界坑网上大量老教程写from ax import Client顶层导入而 1.3.1 现行是from ax.api.client import Clientax.api.configs.RangeParameterConfig。顶层ax.Client与 API 层Client混用会参数对不上、方法签名不同。认ax.api.*这一支别把两代导入写进同一段代码同第 04 篇 Opentons v1/v2 文档树分离的教训。同理停维组件如 GPyOpt2023-02-23 归档、scikit-optimize2024-02-28 归档只能讲思想、不作新闭环依赖铁律 6——它们的Optimizer.ask()/tell()只是范式设计参考生产用 Ax。为什么落库要单独拎出来讲Ax 的Client默认只在内存里维护这一轮优化会话进程一退、所有 trial 灰飞烟灭。闭环不能靠内存态——它必须把每个 trial 的建议参数、回报值、以及理想情况下失败标记持久化才能做到① 崩溃后从库重建模型继续问本篇 2.3② 事后审计这一步是谁、基于哪些历史数据建议的第 16 篇合规③ 把仿真版本、协议版本、记录 ID、原始数据链接绑成一条 loop record铁律 8。所以ask/tell 成对落库铁律 5不是锦上添花而是把一次性的内存优化升级成可追溯闭环的关键一步本篇把这三点都压进 SQLite 的一张trials表里演示。二、完整代码与逐行剖析2.1 官方 Booth quickstart 复现目标函数版闭环这段是 ax.dev quickstart 的逐字风格先让读者看到20 轮收敛到 ≈(1.49,2.42)这个可验证事实。fromax.api.clientimportClient# 现行 APIax.api.client不是顶层 ax.Clientfromax.api.configsimportRangeParameterConfig# 参数用 config 对象声明而非散参数字典clientClient()# 无参构造一个内存态优化会话# 声明实验两个浮点参数 x1,x2各自 [-10,10]parameter_typefloat 明确连续域client.configure_experiment(namebooth_function,parameters[RangeParameterConfig(namex1,bounds(-10.0,10.0),parameter_typefloat),RangeParameterConfig(namex2,bounds(-10.0,10.0),parameter_typefloat),])# 目标最小化 booth。-1 * booth 是把最小化表达成最大化负值——Ax 默认按最大化解读 raw_data# 故取负号这是反直觉默认值写错方向会优化到函数最大值client.configure_optimization(objective-1 * booth)for_inrange(20):# 20 轮 ask/tellquickstart 用 20 即可看收敛forti,pinclient.get_next_trials(max_trials1).items():# Ask拿一组建议参数x1,x2p[x1],p[x2]# 目标函数真实实验回报的替身Booth 函数解析真值最小点在 (1, 3)最优值 0booth(x12*x2-7)**2(2*x1x2-5)**2client.complete_trial(trial_indexti,raw_data{booth:booth})# Tell回报闭环转一圈bestclient.get_best_parameterization()# 官方输出 ≈(1.49, 2.42)真值 (1, 3)print(best:,best)剖析configure_experiment只声明搜什么configure_optimization声明往哪优化循环里 ask/tell 交替——这就是 DBTL 每转一圈的算法骨架。-1 * booth是必须理解的默认Ax 的 raw_data 默认越大越好最小化问题要么取负、要么显式声明最小化方向。get_next_trials(max_trials1)每次只回一个建议点把max_trials调大就得到批量建议一批 q 个点那是第 11 篇一板 96 个条件的伏笔。2.2 把目标换成 simulate 并成对落库铁律 5真正的闭环里booth要换成第 09 篇simulate(params)或一次真实实验回报每个 trial 的建议参数与回报值必须成对写进台账缺回报的 trial 不许进下一轮拟合铁律 5。importsqlite3,jsonfromax.api.clientimportClientfromax.api.configsimportRangeParameterConfig# simulate 第 09 篇的统一契约给定 params - 一个标量预测真实闭环里也可能是仪器回报fromsim_engineimportsimulate# 你的第 09 篇模块换引擎不改这段consqlite3.connect(trials.db);curcon.cursor()# trial 表参数与回报一一对应raw 非空才算这一圈闭合——从 schema 上强制铁律 5cur.execute(CREATE TABLE IF NOT EXISTS trials( trial_index INTEGER PRIMARY KEY, params TEXT NOT NULL, -- 建议参数 JSONAttributable谁建议的什么 raw_value REAL, -- 回报值可空但空未完成不许进下轮拟合 completed INTEGER DEFAULT 0 -- 落库闸门0已 ask 未 tell1成对闭合 ))clientClient()client.configure_experiment(nameformulation,parameters[RangeParameterConfig(nametemperature,bounds(20.0,80.0),parameter_typefloat),RangeParameterConfig(nameconcentration,bounds(0.1,2.0),parameter_typefloat),])client.configure_optimization(objective-1 * loss)for_inrange(20):forti,pinclient.get_next_trials(max_trials1).items():# 先落 ask参数入库completed0标记这轮建议已发出、回报未回cur.execute(INSERT OR REPLACE INTO trials VALUES (?,?,?,0),(ti,json.dumps(p),None))con.commit()predsimulate({temperature:p[temperature],concentration:p[concentration]})# 真实目标仿真或实验client.complete_trial(trial_indexti,raw_data{loss:float(pred)})# 再落 tell回报回填并把 completed 置 1——ask/tell 成对闭合才算一轮cur.execute(UPDATE trials SET raw_value?, completed1 WHERE trial_index?,(float(pred),ti))con.commit()# 一致性自检任何 completed0 的行都是断链说明闭环有 ask 无 tell缺回报必须堵住cur.execute(SELECT COUNT(*) FROM trials WHERE completed0)assertcur.fetchone()[0]0,存在无回报 trial违反铁律 5禁止进入下一轮拟合print(best:,client.get_best_parameterization())con.close()剖析两段落库INSERT 记 ask、UPDATE 记 tell completed1把建议和回报钉在同一行assert completed0 计数为 0是铁律 5 的守门断言——只要有一次 ask 后进程崩了没 tell重启后这条断言就会把问题暴露出来而不是让缺回报的半截 trial 悄悄进入 GP 拟合。INSERT OR REPLACE让重放同一trial_index不产生重复行幂等呼应第 07 篇写回幂等。这张trials表还是未来 loop record 的骨架等接上真机只需再加几列仿真版本、协议版本、LIMS 记录 ID、原始数据链接一条完整的铁律 8 追溯记录就从优化器私有台账长成了全闭环共享账本。2.3 断点续跑从库重建已闭合的 trial闭环要能中断恢复。恢复的关键是只把completed1的 trial 重新 tell 回 Ax半截的 ask 不进模型。importsqlite3,jsonfromax.api.clientimportClientfromax.api.configsimportRangeParameterConfigdefresume_from_db(dbtrials.db):consqlite3.connect(db);curcon.cursor()clientClient()client.configure_experiment(nameformulation,parameters[RangeParameterConfig(nametemperature,bounds(20.0,80.0),parameter_typefloat),RangeParameterConfig(nameconcentration,bounds(0.1,2.0),parameter_typefloat),])client.configure_optimization(objective-1 * loss)# 只取成对闭合的行回放completed1 保证每条都有回报缺回报的绝不喂给模型铁律 5forti,params,rawincur.execute(SELECT trial_index, params, raw_value FROM trials WHERE completed1):client.get_next_trials(max_trials1)# 占位一个 trial 位示例与库中 ti 对齐client.complete_trial(trial_indexti,raw_data{loss:raw})con.close()returnclient# 交回调用方继续 ask/tell剖析断点续跑最容易犯的错是把上次 ask 了但没 tell的半截 trial 也重放等于告诉模型这个点有结果而其实没有。用WHERE completed1过滤是硬约束——恢复出来的模型只建立在完整数据上这正是铁律 5 在重启场景下的延伸。三、常见报错与排查现象from ax import Client能导入但configure_experiment参数对不上。根因顶层ax.Client是旧式对象与 API 层ax.api.client.Client签名不同。解法统一用from ax.api.client import Clientax.api.configs.RangeParameterConfig1.3.1 现行。现象20 轮后get_best_parameterization跑到函数最大点而非最小点。根因objective方向写反——Ax raw_data 默认越大越好。解法最小化用-1 * loss如 Booth或正确声明方向对照 quickstart 的-1 * booth。现象前几轮建议像随机撒点、不像智能优化。根因默认 CenterSobol 冷启动阶段本就在空间填充MBM/BoTorch 模型建议要到采样够后才接管。解法这是预期行为要更早用 GP 可调生成器或增大批量第 11 篇。现象进程中途崩溃重启后模型把无回报的 trial 当数据。根因ask 落库了、tell 没落。解法恢复时只回放completed12.3并保留assert completed0 计数0断言。现象仿真返回NaN第 09 篇不收敛被直接 complete_trial。根因把失败当数值回报污染 GP。解法NaN/失败标记走 mask 分支、不complete_trial为有效值第 11 篇展开。四、动手练习复现官方收敛数跑 2.1 满 20 轮。判定标准get_best_parameterization()落在 (1.49, 2.42) 附近±0.2确认与 Booth 真值 (1, 3) 仍有残差——理解有限预算下的近似。铁律 5 断言测试在 2.2 里人为让一次complete_trial抛异常如 simulate 返回 None 转 float 失败。判定标准末尾assert捕获到completed0行并报存在无回报 trial。目标替换把 2.2 的simulate换成一个自己造的二维函数如 Rosenbrock。判定标准闭环不需改任何 Ax 调用20 轮内 best 朝该函数最小值靠近且 trial 表 20 行全completed1。五、小结与下一篇预告Ax 把贝叶斯优化的 ask/tell 收成配置式 APIconfigure_experiment → configure_optimization → get_next_trials → complete_trial → get_best_parameterization官方 Booth quickstart 20 轮 ≈(1.49,2.42) vs 真值 (1,3) 是它用有限预算逼近最优的可见证据默认 CenterSobolMBM(BoTorch) 说明 Ax 实为 BoTorch 上层封装。把目标函数从 booth 换成第 09 篇simulate或真实实验回报并用 SQLite trial 表成对落库completed1闸门 只回放闭合行闭环才真正会自我改进且可审计铁律 5。第 09 篇给了被优化的黑盒simulate本篇给了驱动它的问价器下一篇第 11 篇钻进 Ax 底下那层——手写 BoTorch 的SingleTaskGPExpectedImprovement/qExpectedImprovement解释 Ax 默认生成器看不见的地方并处理批量建议与失败样本 mask。本篇认知问题回显FAQQ1贝叶斯优化的 ask/tell 在闭环里扮演什么角色为什么适配昂贵仿真/实验A它是 Learn 反哺 Design 的算法大脑askget_next_trials要一组最有信息量的建议参数tellcomplete_trial回报该点实测/仿真值。昂贵场景下网格/随机搜索浪费预算而它用 GP 代理模型记住已做/未探区域每轮只挑最值得做的点天然贴合闭环。Q2Ax 1.3.1 现行 API 的调用顺序是什么Client 与 RangeParameterConfig 各负责什么Afrom ax.api.client import Client会话对象负责实验编排configure_experiment(name, parameters[RangeParameterConfig(name, bounds, parameter_type)])声明搜索空间configure_optimization(objective)定方向循环get_next_trials/complete_trial最后get_best_parameterization。Q3官方 Booth quickstart 实跑 20 轮给什么数跟真值差多少Aconfigure_optimization(objective-1 * booth)、目标(x12x2−7)²(2x1x2−5)²跑满 20 轮get_best_parameterization()≈(1.49, 2.42)解析真值 (1, 3)。残差说明有限评估预算下的近似性不是 bug。Q4默认 CenterSobolMBM(BoTorch) 生成策略意味着什么和 BoTorch 什么关系A不配生成器时 Ax 自动 Center 起点→Sobol 空间填充冷启动→MBM节点 generator_enumBoTorch做模型建议即 Ax 是 BoTorch 上层封装。想手写 GP 与采集函数、精确控制 q 批量用第 11 篇的 BoTorch。Q5为什么 trial 落库是闭环命门旧 ax.Client 顶层导入有什么坑Aask 的建议参数与 tell 的回报必须成对入库如 SQLitecompleted1闸门、断言无completed0行、恢复只回放completed1缺回报的 trial 不许进下轮拟合铁律 5顶层from ax import Client是旧式对象与 1.3.1 现行ax.api.client.Client签名不同混用会导致参数/方法对不上。
返回列表