在 SPEAR3 这样的加速器上,优化问题在控制室里,通常是一段 Matlab 脚本,它设几块磁铁,再把束流读回来;优化算法则在各人的笔记本电脑上,其中不少是 Python 写的。要试一种新算法,就得把它拷到控制室的电脑上,手工接到脚本上,在那里装它的运行环境往往还需要管理员权限(装的过程也可能弄坏控制系统自己的配置),换下一种算法又要再来一遍。我想让优化器留在它原来的地方运行,脚本也一行不动,于是有了 Teeport。2019 到 2021 年我在 SLAC 写了它的服务器,仪表盘,插件和大部分适配器。2021 年,Xiaobiao Huang,Minghao Song 和我把它写成论文,发表在 Frontiers in Big Data 上。
- 2 行典型情况下,把一种算法或一个问题接到 Teeport 上,自己的代码要改的行数
- 6整个 API 的函数个数
- 2测试并部署过它的实验室,SLAC 和 ANL
算法与问题之间的墙
在我 2020 年的幻灯片里,算法与问题之间隔着一堵砖墙,墙上写着三个词。距离和语言指的就是上面说的笔记本电脑和 Matlab,保密指的则是脚本本身:评估脚本里可能存着控制系统的令牌(token),或者一些不希望别人看到的旋钮,因此它应当留在原地。Teeport 不动墙两边的代码,只让数据穿过这堵墙,主要是 X(要尝试的设定值)往一边走,Y(测到的结果)往另一边回。
run_ 与 use_
还是拿那段 Matlab 脚本来说。在 Teeport 看来它只是一个函数,Y = evaluate(X);算法则是一个以这样的函数为参数的函数,optimize(evaluate)。在控制室里,teeport.runEvaluator 把脚本发布出去并给出一个 id。在笔记本上,use_evaluator 拿着这个 id,交回一个普通的本地函数:
from teeport import Teeport
teeport = Teeport('ws://localhost:8080/')
evaluate = teeport.use_evaluator('c4oiY1_oe')
你的优化器像调用其他任何函数一样调用它,看上去它就是本地的。测量仍然在控制室里完成,而且脚本什么时候运行由发布它的人决定,因此你的优化器除了通过这个脚本以外接触不到机器。优化器也有同样的一对调用,于是有问题要解的人也可以按 id 借用一种算法。
隐形的优化器
use_evaluator 是花工夫最多的一个函数(它在 Python 客户端里)。在服务器上,每一次运行都是一个任务(task),而一个任务需要一个优化器客户端,也即一个持续运行并不断产生 X 的函数。这里并没有这样的函数,只有你自己的循环,它什么时候调用 evaluate(X) 都可以。因此你的第一次调用会悄悄在客户端里启动一个私有的优化器,并创建任务。这个优化器的循环永不结束:它等待一个 future 交来下一个 X,把 X 发出去,再把回来的 Y 放进第二个 future,也就是你的 evaluate(X) 正在等的那一个。对你来说它是一个阻塞的函数,于是现成的算法一行不改就能在远端的问题上运行。
待发队列
服务器是一个很小的 Node.js WebSocket 服务,做的事尽可能少。来 SLAC 之前我在 Visual3D 写过同一类服务器,混合现实手术导航系统里的每台头显和工作站都通过它通信。Teeport 的服务器把 X 交给评估器,把 Y 送回优化器,旁观的客户端都收到一份 Y,历史记录也由它保存。每一个 X 都先进入待发队列(pending queue),只有任务处于运行状态时才继续往前送,暂停和恢复的全部机制就是这一个判断:暂停,这一批 X 就在队列里等着;恢复,服务器把队列重放给评估器。算法和问题两边都不知道中间发生过什么,也都不必为此加一行代码。
所有这些都放在内存里。服务器一重启,运行记录就没有了,除非有人事先把它导出成了 JSON。我画过服务器后面的归档数据库,也画过把一次运行回滚到之前某一点的功能,两样都没有做出来。
仪表盘
仪表盘(React 和 Plotly)也只是又一个客户端。任务页以监视器(monitor)的身份订阅,并且声明只要 Y,于是服务器在发送前把每一代里的 X 抹掉,X 要到你下载这次运行时才一起下来。这样设计是为了大问题:NSGA-II 在一个 500 维的问题上收敛时,历史记录已经堆到 1 GB 左右,浏览器的一个标签页装不下。在任务列表里选几次运行,按 Compare 就能把它们叠在一张图上,其中有的还在运行中也可以;同一个优化器反复运行几次,也可以这样做基准测试(benchmark)。


在 SPEAR3 上
我们三个人用它做 SPEAR3 储存环上的优化,Minghao 是第一个用户,他测试了原型,Matlab 适配器也有一部分是他写的。在 2021 年 1 月给 SSRL 的报告里我们写道,报告中的在线优化,不论是模拟的还是在环上做的,大部分都经过 Teeport,上面的束损率运行就在其中。做法是在控制室里开着的 Matlab 中把脚本发布出去,然后在笔记本上做优化;一次运行可以先对着模拟的储存环跑,再换到真机上,只需改一个 id。
这堵墙也出现在一种算法的内部。MG-GPO 是 Xiaobiao 的算法,Minghao 和我与他一起开发,全部用 Matlab 写成。我们找不到好用的 Matlab 高斯过程(Gaussian process,GP)程序包,而 Python 有 GPy。于是 GPy 作为 Teeport 的处理器(processor,一种无状态的辅助函数,两边都可以调用)运行。teeport.useProcessor 给 Matlab 代码交回一个 predict 函数,其余部分都留在 Matlab 里。调机的结果在 MG-GPO 页上。


Teeport 之后
Teeport 擅长的是远程运行和算法的基准测试,控制室里手动的调机则需要另一种工具,2021 年我为此开始写 Badger。teeport.info 上的公开实例现在还能打开,任务列表是空的,因为一切都只在内存里。
资料来源:论文(Zhang,Huang 和 Song,Frontiers in Big Data,2021 年),我们给 SSRL 的报告(Huang,Song 和 Zhang,2021 年 1 月),我 2020 年的幻灯片,以及公开的 SPEAR3-ML 仓库。