【发布时间】:2020-01-12 14:50:11
【问题描述】:
我在 tensorflow 中实现了一个简单的培训师课程。我正在运行一些实验来检查代码性能,但我无法理解 tf.data.Dataset 和 tf.function 背后发生的事情。
下面我将介绍我运行的测试,最后会有一些关于我得到的结果的问题。
配置:Intel i3 cpu,tensorflow-cpu 2.1
class Trainer:
def __init__(self, model, optimizer, loss):
self.model = model
self.loss_function = loss
self.optimizer = optimizer
@tf.function
def train_step(self, inputs, targets):
with tf.GradientTape() as tape:
predictions = self.model(inputs)
loss = self.loss_function(targets, predictions)
gradients = tape.gradient(loss, self.model.trainable_variables)
self.optimizer.apply_gradients(zip(gradients, self.model.trainable_variables))
return loss
# fit using dataset
@tf.function
def fit0(self, dataset, epochs):
for epoch in tf.range(epochs):
for input_batch, target_batch in dataset:
self.train_step(input_batch, target_batch)
# fit using list of tensors
@tf.function
def fit1(self, inputs, targets, epochs):
for epoch in tf.range(epochs):
for input_batch, target_batch in zip(inputs, targets):
self.train_step(input_batch, target_batch)
在以下 train_step 中将始终包裹在 tf.function 中。
fit0、fit1 将在使用和不使用 tf.function 的情况下进行测试。
这是我用来运行测试的代码:
input_size = 10000
batch_size = 100
q = input_size // batch_size
# create random inputs (x) and outputs (y)
x = tf.random.normal((input_size, 1), dtype=tf.float32)
y = tf.random.normal((input_size, 1), dtype=tf.float32)
splits = tf.fill([q, ], batch_size)
# create a list of tensors rappresenting batches
x_list = tf.split(x, splits)
y_list = tf.split(y, splits)
# create datasets in the different ways
dataset0 = tf.data.Dataset.from_tensor_slices((x, y)).batch(batch_size)
dataset1 = tf.data.Dataset.from_tensor_slices((tf.stack(x_list), tf.stack(y_list)))
# model definition
model = tf.keras.Sequential([
tf.keras.layers.Dense(20, activation='tanh', input_shape=(1,)),
tf.keras.layers.Dense(1, activation='linear')])
# trainer initialization
trainer = Trainer(model=model, optimizer=tf.keras.optimizers.Adam(), loss=tf.keras.losses.MeanSquaredError())
# first run to perform initializations
time0 = time.time()
trainer.fit0(dataset=dataset0, epochs=tf.constant(1, dtype=tf.int32))
time0 = time.time() - time0
time1 = time.time()
trainer.fit0(dataset=dataset1, epochs=tf.constant(1, dtype=tf.int32))
time1 = time.time() - time1
time2 = time.time()
trainer.fit1(inputs=x_list, targets=y_list, epochs=tf.constant(1, dtype=tf.int32))
time2 = time.time() - time2
print("first fit0 with dataset0 took {} seconds".format(time0))
print("first fit0 with dataset1 took {} seconds".format(time1))
print("first fit1 with tensorlist took {} seconds".format(time2))
# measure performances
time0 = time.time()
trainer.fit0(dataset=dataset0, epochs=tf.constant(100, dtype=tf.int32))
time0 = time.time() - time0
time1 = time.time()
trainer.fit0(dataset=dataset1, epochs=tf.constant(100, dtype=tf.int32))
time1 = time.time() - time1
time2 = time.time()
trainer.fit1(inputs=x_list, targets=y_list, epochs=tf.constant(100, dtype=tf.int32))
time2 = time.time() - time2
print("fit0 with dataset0 took {} seconds".format(time0))
print("fit0 with dataset1 took {} seconds".format(time1))
print("fit1 with tensorlist took {} seconds".format(time2))
这里是测试结果:
第一次测试是 100 批,每批 100 个样品。
input_size = 10000
批量大小 = 100没有@tf.function:
第一次 fit0 与 dataset0 花了 0.9953532218933105 秒
第一次 fit0 与 dataset1 花了 0.07995295524597168 秒
第一次使用 tensorlist 的 fit1 耗时 0.05196571350097656 秒
fit0 和 dataset0 耗时 10.46957802772522 秒
fit0 和 dataset1 花了 7.822799205780029 秒
带有张量列表的 fit1 耗时 4.650130748748779 秒使用@tf.function:
第一次 fit0 与 dataset0 花了 1.4042332172393799 秒
第一次 fit0 与 dataset1 花了 0.46071624755859375 秒
首次使用 tensorlist 的 fit1 耗时 7.3524699211120605 秒
fit0 和 dataset0 花了 15.077088832855225 秒
fit0 和 dataset1 花了 9.136569738388062 秒
带有张量列表的 fit1 耗时 2.1366817951202393 秒
第二个是1批100000个样本。
input_size = 100000
批量大小 = 100000没有@tf.function:
第一次 fit0 与 dataset0 花了 1.1792669296264648 秒
第一次 fit0 与 dataset1 花了 0.027983427047729492 秒
第一次使用 tensorlist 的 fit1 耗时 0.020987749099731445 秒
fit0 和 dataset0 耗时 28.71895956993103 秒
fit0 和 dataset1 花了 2.730872869491577 秒
带有张量列表的 fit1 耗时 2.194814682006836 秒使用@tf.function:
第一次 fit0 与 dataset0 花了 1.5979444980621338 秒
第一次 fit0 与 dataset1 花了 0.4557182788848877 秒
第一次使用 tensorlist 的 fit1 耗时 0.3708038330078125 秒
fit0 和 dataset0 耗时 36.43854784965515 秒
fit0 和 dataset1 花了 9.819332122802734 秒
带有张量列表的 fit1 耗时 2.1136972904205322 秒
问题:
- 为什么 tf.data.Dataset 使用 tf.function 包装时性能最差?
- 即使 dataset0 和 dataset1 功能相同。两者之间的底层区别是什么?为什么数据集 1 的性能优于数据集 0?
-
fit1 和 tf.function 获得了最佳的长期性能。
- 是否可以使用 tf.data.Dataset 实现相同的性能?
- 为什么初始化需要这么长时间?
使用 100 个批次时,第一次运行耗时 7.3524699211120605 秒,这一次随着批次数量的增加而增加。
我猜是因为签名正在创建一个更大的图,展开不同批次的计算。不过,我看不到任何并行化的机会,因为每个批次都依赖于前一个批次的结果。
【问题讨论】:
标签: python tensorflow optimization