分布式TensorFlow - 没有运行一些工作者

时间:2017-03-23 20:47:04

标签: python tensorflow

我试图得到一个分布式TensorFlow工作的一个非常简单的例子。但是,我有一个在运行之间出现非确定性的错误。在某些运行中,它完美地运行。输出以下内容:

Worker 2 | step 0
Worker 0 | step 0
Worker 1 | step 0
Worker 3 | step 0
Worker 2 | step 1
Worker 0 | step 1
Worker 1 | step 1
Worker 3 | step 1
...

但是,每隔一段时间,一个或多个工作人员就无法运行,导致输出如下:

Worker 0 | step 0
Worker 3 | step 0
Worker 0 | step 1
Worker 3 | step 1
Worker 0 | step 2
Worker 3 | step 2
...

如果我无限期地运行循环,似乎失踪的工人总是在某个时刻启动,但仅仅几分钟后,这是不切实际的。

我发现有两件事情会导致问题消失(但使程序失效):1。不在with tf.device(tf.train.replica_device_setter())范围内声明任何变量。如果我甚至声明一个变量(例如下面的nasty_var),问题就会开始出现。 2.为所有工作人员is_chief设置tf.train.MonitoredTrainingSession()参数为True。即使声明了变量,这也会导致bug消失,但是让所有的工作者成为首席执行官似乎是错误的。我目前在下面设置的方式 - is_chief=(task_index == 0) - 直接来自TensorFlow教程。

这是我可以复制问题的最简单的代码。 (您可能需要多次运行以查看错误,但它几乎总是在5次运行中显示

from multiprocessing import Process
import tensorflow as tf
from time import sleep
from numpy.random import random_sample

cluster = tf.train.ClusterSpec({'ps': ['localhost:2222'],
                                'worker': ['localhost:2223',
                                           'localhost:2224',
                                           'localhost:2225',
                                           'localhost:2226']})


def create_worker(task_index):
    server = tf.train.Server(cluster, job_name='worker', task_index=task_index)

    with tf.device(tf.train.replica_device_setter(worker_device="/job:worker/task:%d" % task_index, cluster=cluster)):
        nasty_var = tf.Variable(0)  # This line causes the problem. No issue when this is commented out.

    with tf.train.MonitoredTrainingSession(master=server.target, is_chief=(task_index == 0)):
        for step in xrange(10000):
            sleep(random_sample())  # Simulate some work being done.
            print 'Worker %d | step %d' % (task_index, step)


def create_ps(task_index):
    param_server = tf.train.Server(cluster, job_name='ps',
                                   task_index=task_index)
    param_server.join()

# Launch workers and ps in separate processes.
processes = []
for i in xrange(len(cluster.as_dict()['worker'])):
    print 'Forking worker process ', i
    p = Process(target=create_worker, args=[i])
    p.start()
    processes.append(p)

for i in xrange(len(cluster.as_dict()['ps'])):
    print 'Forking ps process ', i
    p = Process(target=create_ps, args=[i])
    p.start()
    processes.append(p)

for p in processes:
    p.join()

2 个答案:

答案 0 :(得分:5)

我猜这里的原因是tf.train.MonitoredTrainingSession如何启动的隐式协调协议,实现here

  • 如果这次会议是主席:

    • 运行变量初始化程序op。
  • 否则(如果本次会议不是主席):

    • 运行op以检查变量是否已初始化。
    • 虽然尚未初始化任何变量。
      • 等待30秒。
      • 尝试创建新会话,并检查变量是否已初始化。

(我在video about Distributed TensorFlow中讨论了该协议的基本原理。)

当每个会话都是主要会话,或者没有要初始化的变量时,tf.train.MonitoredTrainingSession将始终立即开始。但是,一旦有一个变量,而你只有一个负责人,你会发现非首席工作人员必须等待主管采取行动。

使用此协议的原因在于它对各种进程失败具有鲁棒性,并且与在典型分布式培训作业的预期运行时间相比,在单个进程上运行所有内容时的延迟非常明显。 / p>

查看the implementation again,这个30秒的超时似乎是可配置的(作为tf.train.SessionManager()recovery_wait_secs参数),但目前无法设置此超时当您创建tf.train.MonitoredTrainingSession时,因为它使用了一组硬编码的参数for creating a session manager。 这似乎是API的疏忽,所以请随时在GitHub issues page上打开功能请求!

答案 1 :(得分:0)

正如mrry所说,问题的存在是因为:

  1. 非首席执行官依靠首席执行官来初始化模型。
  2. 如果未初始化,则等待30秒。
  3. 在表现方面,在接下来的30年代等待首席执行官并没有任何区别。但是,我最近正在做一项研究,要求我强制执行严格的同步更新,这个问题需要处理。

    此处的关键是使用屏障,具体取决于您的分布式设置。假设你使用thread-1来运行ps,而使用thread-2~5来运行worker,那么你只需要:

    1. 使用tf.train.Supervisor而不是使用MonitoredTrainingSession,它允许您设置recovery_wait_secs,默认值= 30s。将其更改为1以减少您的等待时间。
    2.   

      sv = tf.train.Supervisor(is_chief = is_chief,                                    LOGDIR = ...                                    init_op = ...                                    ...                                    recovery_wait_secs = 1秒)

           

      sess = sv.prepare_or_wait_for_session(server.target,   配置= sess_config)

      1. 使用barrier。假设您正在使用线程:
      2. 主要:

        barrier = threading.Barrier(parties=num_workers)
        
        for i in range(num_workers):
            threads.append(threading.Thread(target=run_model, args=("worker", i, barrier, )))
        threads.append(threading.Thread(target=run_model, args=("ps", 0, barrier, )))
        

        在实际的训练功能中:

        _ = sess.run([train_op], feed_dict=train_feed)
        barrier.wait()
        

        然后快乐地继续前进。障碍将确保所有模型都达到这一步,并确保没有竞争条件。