Python 许多分布式dask工作人员在一次评估后空闲,或者在有更多任务时从未收到任何工作

Python 许多分布式dask工作人员在一次评估后空闲,或者在有更多任务时从未收到任何工作,python,dask,dask-distributed,Python,Dask,Dask Distributed,我们正在使用dask来优化深度学习器(DL)架构,方法是生成设计,然后将它们发送给dask工作人员,然后由dask工作人员使用pytorch进行培训。我们观察到一些工人似乎没有开始工作,而那些完成DL评估的工人不会立即开始评估下一个等待的DL 我们已经在橡树岭国家实验室的Summit超级计算机上实现了这一点。对于我们的原型,我们提交了一个批处理作业,该批处理作业分配92个节点,启动一个dask调度程序和92个dask工作进程,每个节点有一个工作进程。每个节点有6个Nvidia Volta V10

我们正在使用dask来优化深度学习器(DL)架构,方法是生成设计,然后将它们发送给dask工作人员,然后由dask工作人员使用pytorch进行培训。我们观察到一些工人似乎没有开始工作,而那些完成DL评估的工人不会立即开始评估下一个等待的DL

我们已经在橡树岭国家实验室的Summit超级计算机上实现了这一点。对于我们的原型,我们提交了一个批处理作业,该批处理作业分配92个节点,启动一个dask调度程序和92个dask工作进程,每个节点有一个工作进程。每个节点有6个Nvidia Volta V100、两个IBM POWER9和512 GB DDR4+96GB HMB@内存。然后,每个工作人员使用pytorch训练DL,并将其验证精度作为“适合度”返回。但是,如果所提出的DL体系结构不可行,则会引发异常,并且相关的适合度变为-MAXINT

在只有两名工作人员的初始试运行中,我们注意到,如果一名工作人员评估了一个格式错误的DL设计,则会立即为其分配一个新的DL进行评估。这两个工人中的任何一个在跑步结束前都没有闲着

这是实际代码的浓缩版本

来自dask.distributed import客户端的
,已完成
client=client(scheduler\u file='scheduler.json')
#Posited_dl_Design是随机dl架构的列表,
#eval_dl是pytorch培训的切入点
worker\u futures=client.map(eval\u dl,posed\u dl\u设计)
对于已完成的res(工人期货):
评估结果_dl=res.result()
#池是按验证排序的已评估DLL的队列
#准确度;因此,update_pool()将替换最不精确的DL
#使用新评估的DL
更新池(已评估池,池)
#如果我们达到某种预算,就让工人们精疲力竭吧
#用于生成DL设计;否则,生成一个新的DL和
#将其交给工人进行培训/评估
如果不停止():
#create_new_dl()从中选择一个更好的DLL
#池,克隆它,并稍微改变它,从而
#创建新的DL设计
新建dl=创建新dl(池)
#现在评估/培训新的DL
new\u future=client.submit(eval\u dl,new\u dl)
迭代器.add(新的未来)
这就是我们调用调度程序和工作程序的方式:

#调度程序不需要GPU。它只需要一个孤独的核心运行。
jsrun--gpu per_rs 0--nrs 1--tasks_per_rs 1--cpu per_rs 1--rs_per_主机1 dask调度程序--接口ib0--no bokeh--no show--scheduler file$scheduler\u file&
#为每个工人安排一个单独的任务。由于dask不使用MPI,请指定smpiargs none。
对于{0..91}中的i;做
jsrun--smpiargs=“none”--nrs 1-e个人--stdio_stdout${RUN_DIR}/worker_out.%h.%j.%t.%p--stdio_stderr${RUN_DIR}/工作线程错误。%h.%j.%t.%p--每线程1个任务--每线程14个cpu--每线程6个gpu--每线程主机1个dask工作线程--第n线程1--nprocs 1--内存限制512e9--接口ib0--无错误--重新连接--调度程序文件$调度程序文件--资源“MEM=512e9”&
完成
#省略对控制器进程的调用
当我们将运行规模扩大到雇用92名工人时,我们发现几分钟后只有五六名工人在运行——这些工人拥有可行的DLs,可以作为他们的第一个DL设计候选者进行培训

闲散的工人分为两类。大多数闲置工人显然已经评估了破损的DL设计,并忠实地返回了适当的特殊值,表明:;但后来,一个新的DL从未被重新分配给现在自由的工人。另一类工人从未评估过任何DLs,以下是他们的典型产出:

distributed.nanny - INFO -         Start Nanny at: 'tcp://10.41.18.55:45941'
distributed.diskutils - INFO - Found stale lock file and directory '/gpfs/alpine/csc342/proj-shared/may/delemera_first_trial_run/worker-c4o0rsb3', purging
distributed.worker - INFO -       Start worker at:    tcp://10.41.18.55:44107
distributed.worker - INFO -          Listening to:    tcp://10.41.18.55:44107
distributed.worker - INFO -              nanny at:          10.41.18.55:45941
distributed.worker - INFO - Waiting to connect to:     tcp://10.41.18.54:8786
distributed.worker - INFO - -------------------------------------------------
distributed.worker - INFO -               Threads:                          1
distributed.worker - INFO -                Memory:                  512.00 GB
distributed.worker - INFO -       Local Directory: /gpfs/alpine/csc342/proj-shared/may/delemera_first_trial_run/worker-kqf62513
distributed.worker - INFO - -------------------------------------------------
distributed.worker - INFO -         Registered to:     tcp://10.41.18.54:8786
distributed.worker - INFO - -------------------------------------------------
distributed.core - INFO - Starting established connection

因此,对于这类空闲工作人员,在与调度程序建立通信时存在问题。我们没有注意到任何其他可能相关的消息。

事实证明,问题不在于dask,而在于我们如何调用代码

也就是说,在我们的顶级脚本中有一个运行时参数,该参数指示发送给工人的初始填充的大小。如果没有指定默认值,我们最初的实现将使用默认值,并且在作业提交脚本中,我们省略了相应的命令行参数,这意味着默认值5用于初始填充的大小

正如上面代码片段中所述,我们配置设置的方式是,无论分配的工作人员数量或更新的个人池的大小如何,初始总体大小都将决定实际繁忙工作人员的数量。也就是说,因为我们使用了默认值5,所以这些是调度程序给出的第一个任务。然后,当每个工人评估一个DL时,它将被添加到池中,另一个工人将被分配一个新的DL进行评估。(不一定是刚刚完成最新DL评估的同一个工人,这是一个最初的混乱来源。)永远不可能为剩余的空闲工人分配更多的工作

我们现在删除了此参数的默认值,以强制用户为每次运行指定此关键信息

一些教训包括:

  • 为命令行参数提供默认值可能会成为一种无声且难以找到的错误源
  • 花点时间为运行时参数添加健全性检查(例如,如果初始填充大小小于池大小,代码现在会抱怨并失败)
  • 不要认为这是一个依赖包的错误;错误可能在您自己的代码中
  • 磁盘空间很便宜,因此请真正打开日志记录,以获得适当且有用的诊断信息
  • 进行多次、部分运行,以观察病理行为模式(例如,我们注意到,无论工人数量和评估人员数量大小