0 前言
由于ParameterServerStrategy策略,会在各个ps上进行创建变量,因此涉及到变量分片、分布式数据集创建和输入的问题,本文主要对以下官方文档进行研读,以期能够完成对自己demo的特定化修改。
由于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.Dataset
API的通常使用模式:
- 根据输入的数据创建源数据集
- 应用数据集转换来预处理数据
- 遍历数据集并处理元素
迭代是以流的方式进行的,因此不需要将完整的数据集装入内存。
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 总述
要进行分布式训练,就需要跨多台计算机训练,因此数据集输入也必须是分布式。
我的理解:MirrioredStrategy
和MultiWorkerMirroredStrategy
为什么没有这么麻烦的处理分布式数据集的步骤呢?原因是策略内部已经采用了自动分片策略AutoShardPolicy
,所以不用手动分片。而ParameterServerStrategy
不支持自动分片,因此需要自己手动创建分布式数据集输入。
本文档一开头就说了不介绍如何将分布式输入与 Keras API 一起使用,而我的demo主要使用的就是keras的Model.fit
API,故看看原理就行。
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
开始。