【问题标题】:Random number seeds for Julia cluster computing用于 Julia 集群计算的随机数种子
【发布时间】:2021-04-18 21:17:45
【问题描述】:

我有一个 Julia 代码,我想通过并行运行大量作业(即大约 10,000 个并行作业)将此代码提交到远程计算集群。这段代码的工作方式是,主函数(称为“main.jl”)调用另一个函数(称为“generator.jl”),该函数利用诸如 rand(Float64) 等随机数。我通过 bash 文件提交 main.jl,并通过包含并行运行 N 个作业

#PBS -t 1-N

我想确保为 N 个作业提交中的每一个都使用不同的随机数生成器,但我不确定如何执行此操作。我正在考虑根据环境变量设置随机种子;即,通过设置

@everywhere import Random.Random
@everywhere using Random.Random

Random.seed!(ENV_VAR)

在 main.jl 中。但是,我不确定如何获取环境变量 ENV_VAR。在 MATLAB 中,我知道我可以通过

NUM = getenv( 'PBS_ARRAYID' )

但我不知道如何在 Julia 中执行此操作。如果我设法在 main.jl 中设置这个新的随机种子,每次 bash 脚本将 main.jl 提交到集群时,这会生成不同的随机种子吗?同样,考虑到 Julia RNG 使用 MersenneTwister,我什至需要在 Julia 中执行此操作吗?

以防万一,我一直在远程机器上使用 Julia 1.5.1。

【问题讨论】:

  • 相同的种子将给出相同的(随机)序列,因此作为种子的时间戳通常就足够了。问题是两种不同的种子是否可以相互依赖?一个种子在几个步骤后按顺序改变,就像另一个种子开始一样。我把这个留给其他不太懒惰的人回答。
  • @JoopEggen 我想我明白你在说什么。例如,我可以在 main.jl 中以毫秒为单位获取时间戳,然后将随机种子设置为该值。但是,如果我按照上述问题的描述在 .pbs 文件中提交 N 个作业,那么至少其中一些作业不会同时提交吗?
  • 由于生日悖论,这种情况经常发生。您不能将时间戳用于集群中的随机数种子。
  • @PrzemyslawSzufel 是否有一种简单、安全的方法可以在 Julia 中为每个作业提交构建不同的随机种子,而不会冒(多次)重复的风险?
  • 我正在写答案让你耐心等待......

标签: random julia random-seed


【解决方案1】:

这里有两个问题:

  1. 获取作业编号
  2. 在随机数生成中使用作业号。

每个问题都有两个解决方案 - 一个更优雅,另一个不太优雅,但也可以。

广告 1。 要管理工作编号,请考虑将 PBS 与 ClusterManagers.jl 一起使用。那里有一个命令addprocs_pbs(np::Integer; qsub_flags=""),可以在 Julia 中管理运行编号和编排集群。在许多情况下,您会发现这种方法更舒适。在这里,您可以使用返回工人编号的随机数生成器(稍后会详细介绍)myid() 播种。无论如何,在这种情况下,您很可能会使用 @distributed 循环运行您的计算,并且您可以使用该值来播种 RNG。 如果您宁愿通过 bash 脚本在外部编排数组作业,那么最好的办法是通过参数将作业编号传递给 Julia 进程并从 ARGS 变量中读取它,或者使用一个设置 bash 脚本导出一个环境参数,该参数可以从ENV 变量中读取。

广告 2。 这里有两种方法。首先,您可以简单地在每个工作人员处创建一个新的 MersseneTwister,然后在流中使用它。举个例子(这里我假设你使用了一些变量jobid):

using Random
rnd = MersenneTwister(jobid)
rand(rnd, 4)

这基本上没问题,随机流已知不相关。但是,您可能会担心这种方法会在您的模拟中引入一些工件。如果您想更加小心,您可以使用单个随机流并将其划分为多个进程。这也许也是最先进的解决方案:

using Random, Future
rnd = Future.randjump(MersenneTwister(0), jobid*big(10)^20)

这将使所有进程共享同一个巨大的随机数流(注意,Mersenne Twister 的状态是 19937 位,并且是 2^19937 – 1 的周期,所以这个跳转的大小一点也不大,big(10)^20 是推荐的跳转步骤,因为它已经在 randjump 函数实现中预先计算)。

【讨论】:

  • 我相信我现在看得更清楚了;谢谢。但是,我将如何获得唯一的 jobid 以便将其传递给 ARGS?例如,在我的 .pbs 脚本中,如果我有 #PBS -t 1-36,我应该将什么变量传递给 ARGS 以确保获得 36 个唯一种子?我可以传入 $PBS_ARRAYID 吗?
  • 我现在没有 PBS 来测试,但我相信你会在 ENV["PBS_ARRAYID"] 中找到这个值。您还可以考虑使用 PBS 来启动一个 bash 脚本,该脚本会生成类似 julia run.jl "${PBS_ARRAYID}" 的内容。从长远来看,使用 ClusterMagaers.jl 可能会更优雅。
  • 是的,我刚刚检查过;将 $PBS_ARRAYID 输入 ARGS 就可以解决问题,并为 Julia 代码提供该工作的数值。最后一个问题:鉴于您上面使用 Random 和 Future 的示例,如果我在定义 rnd 后立即设置 Random.seed!(rnd),那么此后任何时候我调用 rand、randperm 等,我都会自动使用该作业的种子,对吗?
  • 或者我是否需要担心 generator.jl 中定义/使用的随机变量(main.jl 调用)?
  • 我建议您拥有自己的MersenneTwister 对象(在我的代码中为rnd)并将其传递给您曾经使用过的每个随机函数。 Random.seed!(rnd) 做了不同的事情 - 将你的状态 rnd 设置为系统时钟,这是你不想要的。
猜你喜欢
  • 2014-09-20
  • 1970-01-01
  • 2022-01-06
  • 2018-05-15
  • 2017-08-22
  • 2015-01-07
  • 1970-01-01
  • 2013-03-01
  • 1970-01-01
相关资源
最近更新 更多