Julia集群计算的随机数种子

3

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

#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。


相同的种子将给出相同的(随机)序列,因此时间戳作为种子通常就足够了。问题是_两个不同的种子是否可以相互依赖;一个种子在另一个种子启动的序列中经过几步更改。我把这个问题留给其他不那么懒惰的人来回答。 - Joop Eggen
@JoopEggen 我想我明白你的意思了。例如,我可以在 main.jl 中以毫秒为单位获取时间戳,然后将随机种子设置为此值。但是,如果我按照上面问题中描述的方式在 .pbs 文件中提交 N 个作业,那么其中至少有一些作业会在完全相同的时间提交吗? - Joshuah Heath
1
这种情况很常见,因为生日悖论的存在。在集群中,您不能使用时间戳作为随机数种子。 - Przemyslaw Szufel
@PrzemyslawSzufel,有没有一种简单、安全的方法在每次作业提交时为Julia构建不同的随机种子,而不会冒(多个)重复的风险? - Joshuah Heath
1
我正在为您编写答案,请耐心等待... - Przemyslaw Szufel
1个回答

3
这里有两个问题:
  1. 获取作业编号。
  2. 在随机数生成中使用作业编号。
每个问题都有两个解决方案 - 一个更加简洁,另一个则不太优雅但也可以解决。
1. 管理作业编号建议考虑使用PBS和ClusterManagers.jl。这里有一个命令addprocs_pbs(np::Integer; qsub_flags=""),它将使得在Julia内部管理运行编号和编排集群成为可能。在许多情况下,您会发现这种方法更加舒适。在这种情况下,您可以使用myid()来为种子随机数生成器提供种子 (稍后详细介绍)。无论如何,最有可能的是,在此场景中,您会使用@distributed循环来运行计算,并且您可以为生成RNG的种子使用那个值。 如果您希望通过bash脚本外部编排数组作业,则最好的方法可能是通过参数将作业编号传递给Julia进程,并从ARGS变量中读取它,或者拥有一个设置bash脚本,该脚本导出可从ENV变量中读取的环境参数。
2. 这里有两种方法。首先,您可以在每个worker上简单地创建一个新的MersseneTwister,然后在streams中使用它。例如(这里假设您使用某个变量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 吗? - Joshuah Heath
1
我现在没有PBS进行测试,但我相信你会在ENV["PBS_ARRAYID"]中找到这个值。你也可以考虑使用PBS来启动一个bash脚本,然后生成类似于julia run.jl "${PBS_ARRAYID}"的东西。从长远来看,使用ClusterManagers.jl可能更加优雅。 - Przemyslaw Szufel
是的,我刚刚检查了一下;将$PBS_ARRAYID输入ARGS中可以解决问题,并为Julia代码提供该作业的数值。最后一个问题:在使用Random和Future的上面示例中,如果我在定义rnd之后立即设置Random.seed!(rnd),那么每次调用rand、randperm等时,我都会自动使用该作业的种子,对吗? - Joshuah Heath
我需要担心在generator.jl中定义/使用的随机变量(main.jl调用的)吗? - Joshuah Heath
1
我建议您拥有自己的MersenneTwister对象(在我的代码中为rnd),并将其传递给您使用的每个随机函数。 Random.seed!(rnd)执行不同的操作-将您的rnd状态设置为系统时钟,这是您不想要的。 - Przemyslaw Szufel

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接