tensorflow分布式流程中的任务分配

时间:2016-12-09 19:16:23

标签: tensorflow

我对张量流中的分布式培训过程感到困惑。

我认为tensorflow将一个batch_size数据提供给一个worker,然后该worker更新了ps服务器,这是对的吗?

但是在训练时,我注意到日志中的步骤编号可能很奇怪。

如果我只有2名工人,我认为正确的过程应该是

[worker1] step 0 xxxxxxx
[worker2] step 100 xxxxxxx
[worker1] step 200 xxxxxxx
[worker2] step 300 xxxxxxx

..... 每个工人都应该打印不同的步骤进行记录。

实际上,日志如下:

[worker1] step 0 xxxxxxx
[worker2] step 100 xxxxxxx
[worker1] step 100 xxxxxxx
[worker2] step 200 xxxxxxx
[worker1] step 300 xxxxxxx

... 为什么worker1 dosn打印步骤200?

我对工作分配感到困惑。

张量流如何进行分发培训? 首席工作人员将数据拆分为batch_size,然后将批处理提供给工人然后更新ps服务器? 或者,每个工作人员都将运行整个数据,并更新ps服务器?

如果可能的话,提供一个可重复性最小的示例(我们通常没有时间阅读数百行代码)

```

with tf.device(tf.train.replica_device_setter(
        worker_device="/job:worker/task:%d" % FLAGS.task_index,
        cluster=cluster)):
    # Read TFRecords files for training
    filename_queue = tf.train.string_input_producer(
        tf.train.match_filenames_once(FLAGS.train),
        num_epochs=epoch_number)
    serialized_example = read_and_decode(filename_queue)
    batch_serialized_example = tf.train.shuffle_batch(
        [serialized_example],
        batch_size=batch_size,
        num_threads=thread_number,
        capacity=capacity,
        min_after_dequeue=min_after_dequeue)
    features = tf.parse_example(
        batch_serialized_example,
        features={
            "label": tf.FixedLenFeature([], tf.float32),
            "ids": tf.VarLenFeature(tf.int64),
            "values": tf.VarLenFeature(tf.float32),
        })
    batch_labels = features["label"]
    batch_ids = features["ids"]
    batch_values = features["values"]

    # Read TFRecords file for validatioin
    validate_filename_queue = tf.train.string_input_producer(
        tf.train.match_filenames_once(FLAGS.eval),
        num_epochs=epoch_number)
    validate_serialized_example = read_and_decode(validate_filename_queue)
    validate_batch_serialized_example = tf.train.shuffle_batch(
        [validate_serialized_example],
        batch_size=validate_batch_size,
        num_threads=thread_number,
        capacity=capacity,
        min_after_dequeue=min_after_dequeue)
    validate_features = tf.parse_example(
        validate_batch_serialized_example,
        features={
            "label": tf.FixedLenFeature([], tf.float32),
            "ids": tf.VarLenFeature(tf.int64),
            "values": tf.VarLenFeature(tf.float32),
        })
    validate_batch_labels = features["label"]
    validate_batch_ids = features["ids"]
    validate_batch_values = features["values"]
    logits = inference(batch_ids, batch_values)
    batch_labels = tf.to_int64(batch_labels)
    cross_entropy = tf.nn.sparse_softmax_cross_entropy_with_logits(logits,
                                                                   batch_labels)
    loss = tf.reduce_mean(cross_entropy, name='loss')

    print("Use the optimizer: {}".format(FLAGS.optimizer))

    optimizer = tf.train.FtrlOptimizer(learning_rate)

    global_step = tf.Variable(0, name='global_step', trainable=False)
    train_op = optimizer.minimize(loss, global_step=global_step)




    # Initialize saver and summary
    steps_to_validate = FLAGS.steps_to_validate
    init_op = tf.initialize_all_variables()

    saver = tf.train.Saver(max_to_keep = 2)
    keys_placeholder = tf.placeholder("float")
    keys = tf.identity(keys_placeholder)
    tf.add_to_collection("inputs", json.dumps({'key': keys_placeholder.name}))
    tf.add_to_collection("outputs", json.dumps({'key': keys.name,
                                                'softmax': inference_softmax.name,
                                                'prediction': inference_op.name}))

    summary_op = tf.merge_all_summaries()


sv = tf.train.Supervisor(is_chief=(FLAGS.task_index == 0),
                         logdir="./train_process/",
                         init_op=init_op,
                         summary_op=summary_op,
                         saver=saver,
                         global_step=global_step,
                         save_model_secs=60)

# Create session to run graph
with sv.managed_session(server.target) as sess:

    while not sv.should_stop():
        # Get coordinator and run queues to read data
        coord = tf.train.Coordinator()
        threads = tf.train.start_queue_runners(coord=coord, sess=sess)

        start_time = datetime.datetime.now()

        try:
            while not coord.should_stop():
                _, loss_value, step = sess.run([train_op, loss, global_step])
                if step % steps_to_validate == 0:
                    accuracy_value, auc_value, summary_value = sess.run(
                        [accuracy, auc_op, summary_op])
                    end_time = datetime.datetime.now()
                    print("[{}] Task: {}, Step: {}, loss: {}, accuracy: {}, auc: {}".format(
                        end_time - start_time,
                        FLAGS.task_index,
                        step, loss_value, accuracy_value,
                        auc_value))

                    start_time = end_time
        except tf.errors.OutOfRangeError:
            print("Done training after reading all data")
        finally:
            coord.request_stop()
            print("coord stopped")

        # Wait for threads to exit
        coord.join(threads)

```

有用的日志或其他输出

(如果日志很大,请上传为附件或提供链接)。 ```

[0:00:17.115814] Task: 0, Step: 74600, loss: 0.303285002708, accuracy: 0.910000026226, auc: 0.946377456188
[0:00:03.804889] Task: 1, Step: 74700, loss: 0.287385582924, accuracy: 0.879999995232, auc: 0.946395516396
[0:00:03.778589] Task: 0, Step: 74800, loss: 0.247096762061, accuracy: 0.860000014305, auc: 0.946370542049
[0:00:03.772320] Task: 1, Step: 74900, loss: 0.264987647533, accuracy: 0.899999976158, auc: 0.946406364441
[0:00:03.795459] Task: 0, Step: 75000, loss: 0.228719010949, accuracy: 0.899999976158, auc: 0.946437120438
[0:00:01.902293] Task: 1, Step: 75000, loss: 0.217391207814, accuracy: 0.910000026226, auc: 0.946473121643
[0:00:01.942055] Task: 1, Step: 75100, loss: 0.284583866596, accuracy: 0.889999985695, auc: 0.946496844292
[0:00:03.860608] Task: 0, Step: 75200, loss: 0.273199081421, accuracy: 0.850000023842, auc: 0.946503221989
[0:00:03.800881] Task: 1, Step: 75300, loss: 0.189931258559, accuracy: 0.930000007153, auc: 0.946559965611

```

1 个答案:

答案 0 :(得分:18)

除了HowTo之外,还没有真正的官方文档,所以通过学习示例来了解事情是如何运作的好方法。

要理解的基本概念是有3种张量流过程。

  1. 客户端 - 这是Python流程,它构建图表,连接到本地主服务器(Session())或远程主服务器(Session("grpc://..."))并发出session.run个调用。

  2. 有一个主人,这是客户连接到的过程,它指出了如何在工人之间分配工作。

  3. 有工人,做实际的工作。如果您的图表有with tf.device(job:worker/task:0):,则阻止,那么该块中的计算应该在任务上执行:0

  4. 使用server = tf.train.Server创建新服务器时,启动的进程既是worker又是master,但了解调试的区别很有用。

    分布式TF的最简单示例是当您拥有一个启动进程内主服务器的单个客户端和多个工作服务器时。这是一个example。在此用法中,与非分布式版本的主要区别在于,您with tf.device("worker1")代替tf.device("gpu1")告诉它在worker1

    上执行该部分图表

    当您拥有多个客户端时会变得更加复杂,就像“图之间复制”一样。参数服务器示例,您有多个并行训练循环,其中每个循环对应一个单独的客户端,这是一个发出运行调用的python进程。要查看ops实际所在的工作人员,您可以查看with tf.device注释。

    在您的示例中,您的代码段中没有明确的with.device("job:worker/task")块,但此部分由tf.device(tf.train.replica_device_setter(完成。本质上,代码不是为块中的所有操作都设置固定设备,代码为每个操作运行replica_device_setter以生成设备以将其置于其中。它将所有变量放在/job:ps/task个工作者上,其余的操作放在当前工作者上。 replica_device_setter的代码随着时间的推移变得有点复杂,但您可以使用更简单的实现来获得与下面相同的效果

    def simple_setter(ps_device="/job:ps/task:0"):
        def _assign(op):
            node_def = op if isinstance(op, tf.NodeDef) else op.node_def
            if node_def.op == "Variable":
                return ps_device
            else:
                return "/job:worker/task:%d" % (FLAGS.task)
        return _assign
     ...
    with tf.device(simple_setter):
        ...
    

    当你运行它时,每个python进程将创建稍微不同的图形版本,除了Variable节点,它们在每个进程中看起来都相同(用tf.get_default_graph()检查.as_graph_def())

    当您有多个客户端运行训练循环时,一个问题是 - 谁执行需要为所有客户端执行一次的任务?例如,某人需要为所有变量运行初始值设定项。您可以将sess.run(tf.initialize_all_variables...)放在客户端主体中,但是并行运行多个客户端,这意味着op初始化会多次运行。所以解决方案是将一个工人指定为“主要”工人,并且只让该工人执行该操作。

    此外,workerps设备之间没有内置区别 - 只是将变量分配给ps设备,并将操作分配给{{1 }} 设备。您也可以只拥有worker个设备,并将worker版本的变量放到第0个工作人员中。

    这是一个准系统examplereplica_device_setter工作人员更新了m PS任务分片的变量,它使用显式设备分配而不是replica_device_setter

    总结一下,在您的情况下n确保您的replica_device_setter是存储在global_step工作人员上的变量,因此可以在所有训练循环中共享此变量。至于为什么你在两个工作人员中都得到同样的ps - 你的图表中没有任何内容强制{em>在增加之后读取。因此,如果您在两个不同的工作人员上并行运行global_step,您可能会看到

    global_step