ParameterServerStrategy 中分布式数据集创建和输入相关问题

0 前言

由于ParameterServerStrategy策略,会在各个ps上进行创建变量,因此涉及到变量分片、分布式数据集创建和输入的问题,本文主要对以下官方文档进行研读,以期能够完成对自己demo的特定化修改。

  1. tf.data.Dataset
  2. tf.distribute.DistributedDataset
  3. tf.keras.utils.experimental.DatasetCreator
  4. 分布式输入

由于ParameterServerStrategy必须用到tf.keras.utils.experimental.DatasetCreator,因此重点研读!

1 tf.data.Dataset

1.1 总述

tf.data.Dataset我暂时理解它为一种数据集类型。

学习过程中看到这篇文档,里边提到了如何使用tf.data.Dataset加载NumPy数组,感觉我的demo结合这个可以很好地符合ps策略里边示例的形式。了解的不深,先mark下来。

假设您有一个示例数组和相应的标签数组,请将两个数组作为元组传递给 tf.data.Dataset.from_tensor_slices 以创建 tf.data.Dataset

train_dataset = tf.data.Dataset.from_tensor_slices((train_examples, train_labels))
test_dataset = tf.data.Dataset.from_tensor_slices((test_examples, test_labels))

tf.data.DatasetAPI的通常使用模式:

  1. 根据输入的数据创建源数据集
  2. 应用数据集转换来预处理数据
  3. 遍历数据集并处理元素
    迭代是以流的方式进行的,因此不需要将完整的数据集装入内存。

1.2 源数据集

创建数据集最简单的方式就是通过python中的’列表’来创建:

dataset = tf.data.Dataset.from_tensor_slices([1, 2, 3])
for element in dataset:
  print(element)

1.3 数据集转换

一旦你有了一个数据集,你可以应用数据集转换来为你的model准备数据:

dataset = tf.data.Dataset.from_tensor_slices([1, 2, 3])
dataset = dataset.map(lambda x: x*2)
list(dataset.as_numpy_iterator())

[2, 4, 6]

2 tf.distribute.DistributedDataset

2.1 总述

表示一个在设备和机器之间的分布式数据集。

tf.data.Dataset只适合非分布式情境。

2.2 创建

有两种APIs来创建tf.distribute.DistributedDataset实例:tf.distribute.Strategy.experimental_distribute_dataset(dataset)tf.distribute.Strategy.distribute_datasets_from_function(dataset_fn)
当你已经有一个tf.data.Dataset时,适合用前者;否则,需要将创建数据集的逻辑封装在dataset_fn中。

乍看ps策略文档,感觉好像得用第二种方法,其实dataset_fn也是可以返回一个tf.distribute.Dataset实例的,因此需按情况考虑。

2.3 用法

主要有两种用法:搞不懂在讲啥,先摘录下来。

    global_batch_size = 4
    strategy = tf.distribute.MirroredStrategy(["GPU:0", "GPU:1"])
    dataset = tf.data.Dataset.from_tensors(([1.],[1.])).repeat(4).batch(global_batch_size)
    dist_dataset = strategy.experimental_distribute_dataset(dataset)
    @tf.function
    def train_step(input):
      features, labels = input
      return labels - 0.3 * features
    for x in dist_dataset:
      # train_step trains the model using the dataset elements
      loss = strategy.run(train_step, args=(x,))
      print("Loss is", loss)
    global_batch_size = 4
    strategy = tf.distribute.MirroredStrategy(["GPU:0", "GPU:1"])
    train_dataset = tf.data.Dataset.from_tensors(([1.],[1.])).repeat(50).batch(global_batch_size)
    train_dist_dataset = strategy.experimental_distribute_dataset(train_dataset)
    @tf.function
    def distributed_train_step(dataset_inputs):
      def train_step(input):
        loss = tf.constant(0.1)
        return loss
      per_replica_losses = strategy.run(train_step, args=(dataset_inputs,))
      return strategy.reduce(tf.distribute.ReduceOp.SUM, per_replica_losses,axis=None)
    EPOCHS = 2
    STEPS = 3
    for epoch in range(EPOCHS):
      total_loss = 0.0
      num_batches = 0
      dist_dataset_iterator = iter(train_dist_dataset)
      for _ in range(STEPS):
        total_loss += distributed_train_step(next(dist_dataset_iterator))
        num_batches += 1
      average_train_loss = total_loss / num_batches
      template = ("Epoch {}, Loss: {:.4f}")
      print (template.format(epoch+1, average_train_loss)) 

总之是会返回一个tf.distribute.DistributedDataset 实例。

3 tf.keras.utils.experimental.DatasetCreator

3.1 总述

是一个返回tf.data.Dataset的对象。

参考代码框架:

tf.keras.utils.experimental.DatasetCreator(
    dataset_fn, input_options=None
)

专门为Model.fit设计的数据输入API,传入input_context参数,返回tf.data.Dataset

model = tf.keras.Sequential([tf.keras.layers.Dense(10)])
model.compile(tf.keras.optimizers.SGD(), loss="mse")

def dataset_fn(input_context):
  global_batch_size = 64
  batch_size = input_context.get_per_replica_batch_size(global_batch_size)
  dataset = tf.data.Dataset.from_tensors(([1.], [1.])).repeat()
  dataset = dataset.shard(
      input_context.num_input_pipelines, input_context.input_pipeline_id)
  dataset = dataset.batch(batch_size)
  dataset = dataset.prefetch(2)
  return dataset

input_options = tf.distribute.InputOptions(
    experimental_fetch_to_device=True,
    experimental_per_replica_buffer_size=2)
model.fit(tf.keras.utils.experimental.DatasetCreator(
    dataset_fn, input_options=input_options), epochs=10, steps_per_epoch=10)

不明白的一点是,定义dataset_fn函数时需传入一个input_context参数,但实际用到时却没有参数,为啥呢?

在ParameterServerStrategy中使用范例如下:

strategy = tf.distribute.experimental.ParameterServerStrategy(
    cluster_resolver)
with strategy.scope():
  model = tf.keras.Sequential([tf.keras.layers.Dense(10)])
model.compile(tf.keras.optimizers.SGD(), loss="mse")

def dataset_fn(input_context):
  ...

input_options = ...
model.fit(tf.keras.utils.experimental.DatasetCreator(
    dataset_fn, input_options=input_options), epochs=10, steps_per_epoch=10)

3.2 参数

参数描述
dataset_fn一个可调用的tf.distribute.InputContext 类型的参数,用来计算batch size以及跨worker分布式输入流水线,最后返回一个tf.data.Dataset
input_options可选非必须参数,仅在分布式训练时有用

4 分布式输入

4.1 总述

要进行分布式训练,就需要跨多台计算机训练,因此数据集输入也必须是分布式。

我的理解:MirrioredStrategyMultiWorkerMirroredStrategy为什么没有这么麻烦的处理分布式数据集的步骤呢?原因是策略内部已经采用了自动分片策略AutoShardPolicy,所以不用手动分片。而ParameterServerStrategy不支持自动分片,因此需要自己手动创建分布式数据集输入。

本文档一开头就说了不介绍如何将分布式输入与 Keras API 一起使用,而我的demo主要使用的就是keras的Model.fitAPI,故看看原理就行。

4.2 分布式数据集

在非分布式训练循环中,用户首先创建一个 tf.data.Dataset 实例,然后迭代各个元素。例如:

import tensorflow as tf

# Helper libraries
import numpy as np
import os

global_batch_size = 16
# Create a tf.data.Dataset object.
dataset = tf.data.Dataset.from_tensors(([1.], [1.])).repeat(100).batch(global_batch_size)

@tf.function
def train_step(inputs):
  features, labels = inputs
  return labels - 0.3 * features

# Iterate over the dataset using the for..in construct.
for inputs in dataset:
  print(train_step(inputs))

#---------------输出------------------#
tf.Tensor(
[[0.7]
 [0.7]
 [0.7]
 [0.7]
 [0.7]
 [0.7]
 [0.7]
 [0.7]
 [0.7]
 [0.7]
 [0.7]
 [0.7]
 [0.7]
 [0.7]
 [0.7]
 [0.7]], shape=(16, 1), dtype=float32)
tf.Tensor(
[[0.7]
 [0.7]
 [0.7]
 [0.7]
 [0.7]
 [0.7]
 [0.7]
 [0.7]
 [0.7]
 [0.7]
 [0.7]
 [0.7]
 [0.7]
 [0.7]
 [0.7]
 [0.7]], shape=(16, 1), dtype=float32)
tf.Tensor(
[[0.7]
 [0.7]
 [0.7]
 [0.7]
 [0.7]
 [0.7]
 [0.7]
 [0.7]
 [0.7]
 [0.7]
 [0.7]
 [0.7]
 [0.7]
 [0.7]
 [0.7]
 [0.7]], shape=(16, 1), dtype=float32)
tf.Tensor(
[[0.7]
 [0.7]
 [0.7]
 [0.7]
 [0.7]
 [0.7]
 [0.7]
 [0.7]
 [0.7]
 [0.7]
 [0.7]
 [0.7]
 [0.7]
 [0.7]
 [0.7]
 [0.7]], shape=(16, 1), dtype=float32)
tf.Tensor(
[[0.7]
 [0.7]
 [0.7]
 [0.7]
 [0.7]
 [0.7]
 [0.7]
 [0.7]
 [0.7]
 [0.7]
 [0.7]
 [0.7]
 [0.7]
 [0.7]
 [0.7]
 [0.7]], shape=(16, 1), dtype=float32)
tf.Tensor(
[[0.7]
 [0.7]
 [0.7]
 [0.7]
 [0.7]
 [0.7]
 [0.7]
 [0.7]
 [0.7]
 [0.7]
 [0.7]
 [0.7]
 [0.7]
 [0.7]
 [0.7]
 [0.7]], shape=(16, 1), dtype=float32)
tf.Tensor(
[[0.7]
 [0.7]
 [0.7]
 [0.7]], shape=(4, 1), dtype=float32)

5 总结

由于主要根据教程进行,必须先定义一个dataset_fn再通过tf.keras.utils.experimental.DatasetCreator(dataset_fn=...)来创建数据集。

有个疑问就是:这种方法返回的是tf.data.Dataset类型,不需要是tf.data.DistributedDataset类型吗?

因此,从定义dataset_fn开始。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值