【问题标题】:MATLAB: How to set random seed in parfor to produce same results as serial for?MATLAB:如何在 parfor 中设置随机种子以产生与串行相同的结果?
【发布时间】:2015-11-29 01:22:34
【问题描述】:

我设置了以下最小示例:

rng(0);

randseedoffset = random('unid', 10^5) + 1;

t = cell(10,1);
for i = 1:10
    rng(randseedoffset+i);
    t{i} = random('unid', 1000);
end

disp(t);

这将生成 10 个随机数并将它们存储在 t 中。它总是会可靠地产生相同的随机数,因为我在 for 循环中使用 rng 设置了种子。

如果我现在将for 更改为parfor,我会得到不同的结果! 尽管它们也将始终是可重现的。

我想用 parfor 加速我的代码,并且仍然获得与 for 完全相同的随机数...

【问题讨论】:

    标签: matlab random parallel-processing parfor


    【解决方案1】:

    好的,我刚刚找到原因了:

    MATLAB 支持不同的随机数生成算法。 在当前版本的通常设置中,这是 Mersenne Twister。 当您进入 parfor 循环时,这将变为他们所谓的“组合递归方法”。

    可以通过在循环中将类型显式设置为'twister' 来解决此问题:

    parfor i = 1:10
        rng(randseedoffset+i, 'twister');
        t{i} = random('unid', 1000);
    end
    

    【讨论】:

    • 有趣。我找不到文档中任何地方提到的默认生成器的更改。你知道这是否也会影响randrandn?顺便说一句,您可以接受自己对问题的回答。
    • +1 非常有趣。不过,在更大的代码上下文中要小心这种 hack - 看起来并行工作者现在已设置为具有近乎并行的 RNG 状态,并且未来的 parfor 或其他不重新设置种子的 DCT 调用可能会很奇怪结果。如果您需要完全可重复性,在循环外预先生成随机数可能会更干净。
    • 我的情况正好相反。你能回答我的问题吗:stackoverflow.com/questions/40190243/…
    【解决方案2】:

    试试这个:

    p = gcp; % Get or open a pool
    
    numWork = p.NumWorkers; % Get the number of workers
    
    stream = RandStream('mrg32k3a','seed',mydata.seed);
    RandStream.setGlobalStream(stream);
    
    % s = RandStream.create('mrg32k3a','NumStreams',numWork,'CellOutput',true,'Seed',mydata.seed); % create numWork independent streams
    
    n = 200; % number of values to generate on each worker
    spmd
    RandStream.setGlobalStream(stream);
    x = rand(1,n);
    end
    

    【讨论】:

      【解决方案3】:

      我觉得有必要详细说明这一点。不要在parfor 循环中重置种子,而且不要并行使用 Mersenne Twister 算法(你会得到很差的统计独立性结果)。

      您得到不同结果的原因是由于这些数字应保持的统计属性,算法不同。在并行池中,MATLAB 将算法设置为“combRecursive”,并在每个工作人员上设置不同的子流,所以对于随机数,你很高兴。此外,parfor 循环不保证——

      • 循环进行的顺序,
      • 哪些工作人员将执行每个部分,或者
      • 每个工作人员执行了多少次迭代。

      因此,在 parfor 循环中生成随机数通常不会返回相同的随机数,即使每个 worker 的状态相同。而是用 combRecursive 算法的 subStreams 创建一个 RandStream,在 spmd 块中为每个 worker 设置全局流,然后在 spmd 块中为每个 worker 生成数字:

      p = gcp; % Get or open a pool
      
      numWork = p.NumWorkers; % Get the number of workers
      
      s = RandStream.create('mrg32k3a','NumStreams',numWork,...
          'CellOutput',true); % create numWork independent streams
      
      n = 200; % number of values to generate on each worker
      spmd
          RandStream.setGlobalStream(s{labindex});
          x = rand(1,n);
      end
      
      % I generate row vectors as the Composite matrix x will return a 
      % comma-separated list using the syntax, x{:}, which can then be 
      % concatenated into a single vector:
      randVals2 = [x{:}]'; 
      

      【讨论】:

      【解决方案4】:

      集群中从事同一工作的每个工作人员都有一个独立的随机数生成器流。因此,默认情况下,池中的每个工作人员以及 parfor 循环中的每次迭代都具有唯一的、独立的随机数集。 parfor 循环的后续运行会生成不同的数字。

      在 parfor 循环中,您无法控制迭代执行的顺序,也无法控制哪个工作人员运行哪些迭代。因此,即使您重置随机数生成器,parfor-loop 也可以以不同的顺序生成相同的值。

      要在每次循环运行时在 parfor 循环中重现同一组随机数,您必须通过为每次迭代分配特定的子流来控制随机生成。

      首先,使用支持子流的生成器创建您要使用的流。将流创建为 parallel.pool.Constant 允许所有工作人员访问流。

      sc = parallel.pool.Constant(RandStream('Threefry'))
      

      在 parfor-loop 内部,您可以通过循环索引设置子流索引。这确保了每次迭代都使用其特定的一组随机数,而不管哪个工作人员运行该迭代或运行什么序列迭代。

      r = zeros(1,16);
      parfor i = 1:16
          stream = sc.Value;        % Extract the stream from the Constant
          stream.Substream = i;
          r(i) = rand(stream);
      end
      

      https://www.mathworks.com/help/parallel-computing/repeat-random-numbers-in-parfor-loops.html

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2015-10-12
        • 1970-01-01
        • 2018-06-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-05-31
        相关资源
        最近更新 更多