小编典典

为什么 TensorFlow 2 比 TensorFlow 1 慢很多?

all

许多用户将其作为切换到 Pytorch 的原因,但我还没有找到一个理由/解释来牺牲最重要的实用质量和速度,以实现急切的执行。

下面是代码基准测试性能,TF1 与 TF2 - TF1 的运行 速度提高了 47% 到 276%

我的问题是: 在图形或硬件级别上,是什么导致了如此显着的减速?


寻找详细的答案 - 我已经熟悉广泛的概念。相关的
Git

规格 :CUDA 10.0.130、cuDNN 7.4.2、Python 3.7.4、Windows 10、GTX 1070


基准测试结果


更新 :根据以下代码禁用 Eager Execution 无济于事 。然而,这种行为是不一致的:有时在图形模式下运行有很大帮助,有时它相对于
Eager运行 速度较慢。


基准代码

# use tensorflow.keras... to benchmark tf.keras; used GPU for all above benchmarks
from keras.layers import Input, Dense, LSTM, Bidirectional, Conv1D
from keras.layers import Flatten, Dropout
from keras.models import Model
from keras.optimizers import Adam
import keras.backend as K
import numpy as np
from time import time

batch_shape = (32, 400, 16)
X, y = make_data(batch_shape)

model_small = make_small_model(batch_shape)
model_small.train_on_batch(X, y)  # skip first iteration which builds graph
timeit(model_small.train_on_batch, 200, X, y)

K.clear_session()  # in my testing, kernel was restarted instead

model_medium = make_medium_model(batch_shape)
model_medium.train_on_batch(X, y)  # skip first iteration which builds graph
timeit(model_medium.train_on_batch, 10, X, y)

使用的功能

def timeit(func, iterations, *args):
    t0 = time()
    for _ in range(iterations):
        func(*args)
    print("Time/iter: %.4f sec" % ((time() - t0) / iterations))

def make_small_model(batch_shape):
    ipt   = Input(batch_shape=batch_shape)
    x     = Conv1D(128, 400, strides=4, padding='same')(ipt)
    x     = Flatten()(x)
    x     = Dropout(0.5)(x)
    x     = Dense(64, activation='relu')(x)
    out   = Dense(1,  activation='sigmoid')(x)
    model = Model(ipt, out)
    model.compile(Adam(lr=1e-4), 'binary_crossentropy')
    return model

def make_medium_model(batch_shape):
    ipt   = Input(batch_shape=batch_shape)
    x     = Bidirectional(LSTM(512, activation='relu', return_sequences=True))(ipt)
    x     = LSTM(512, activation='relu', return_sequences=True)(x)
    x     = Conv1D(128, 400, strides=4, padding='same')(x)
    x     = Flatten()(x)
    x     = Dense(256, activation='relu')(x)
    x     = Dropout(0.5)(x)
    x     = Dense(128, activation='relu')(x)
    x     = Dense(64,  activation='relu')(x)
    out   = Dense(1,   activation='sigmoid')(x)
    model = Model(ipt, out)
    model.compile(Adam(lr=1e-4), 'binary_crossentropy')
    return model

def make_data(batch_shape):
    return np.random.randn(*batch_shape), np.random.randint(0, 2, (batch_shape[0], 1))

阅读 99

收藏
2022-07-18

共1个答案

小编典典

2020年 8 月 17 日 30 日更新:TF 2.3 终于做到了:所有案例的运行速度都比任何以前的版本都快,或者明显快。

此外,我之前的更新对 TF 不公平;我的 GPU
是罪魁祸首,最近一直过热。如果您看到迭代时间的上升茎图,这是一个可靠的症状。最后,请参阅开发人员关于Eager vs
Graph
的说明。

这可能是我对这个答案的最后一次更新。 您的 模型速度的真实统计数据只能由您在您的设备上找到。


2020 年 5 月 19 日更新 :TF 2.2,使用相同的测试:Eager 速度仅略有改进。下面绘制 Large-Large
Numpytrain_on_batch情况,x 轴是连续拟合迭代;我的 GPU
没有接近其全部容量,所以怀疑它是否在节流,但随着时间的推移迭代确实会变慢。

在此处输入图像描述

如上所述,Graph 和 Eager 分别比 TF1 慢 1.56 倍1.97倍。 不确定我是否会进一步调试,因为我正在考虑切换到
Pytorch,因为 TensorFlow
对自定义/低级功能的支持不佳。但是,我确实打开了一个问题以获取开发人员的反馈。


2020 年 2 月 18 日更新 :我每晚都替补 2.1 和 2.1;结果好坏参半。除了一个配置(模型和数据大小)之外的所有配置都与 TF2 和
TF1 的最佳配置一样快或快得多。较慢且显着减慢的是Large-Large - 尤其是。在图形执行中( 慢 1.6 倍到 2.5 倍 )。

此外,对于我测试的一个大型模型,Graph 和 Eager 之间存在 极大
的可重复性差异——无法通过随机性/计算并行性来解释。我目前无法根据时间限制为这些声明提供可重现的代码,因此我强烈建议您对自己的模型进行测试。

尚未就这些问题打开 Git
问题,但我确实对原始问题发表了评论-
还没有回复。一旦取得进展,我会更新答案。


VERDICT :它 不是 ,如果你知道你在做什么。但是,如果您 不这样做 ,则可能会花费您很多钱-平均要花费一些 GPU
升级,而最坏的情况是要花费多个 GPU。


此答案 :旨在提供问题的高级描述,以及如何根据您的需求决定培训配置的指南。有关详细的低级描述,包括所有基准测试结果 +
使用的代码,请参阅我的其他答案。

如果我学到任何信息,我将更新我的答案和更多信息 - 可以收藏/“星标”这个问题以供参考。


问题摘要 :正如TensorFlow 开发人员 Q. Scott Zhu
证实的那样,TF2
的开发重点是 Eager 执行和与 Keras 的紧密集成,这涉及到 TF
源代码的彻底变化——包括图形级别的变化。好处:大大扩展了处理、分发、调试和部署能力。然而,其中一些成本是速度。

然而,这件事要复杂得多。不仅仅是 TF1 与 TF2 - 导致列车速度显着差异的因素包括:

  1. TF2 与 TF1
  2. Eager vs. Graph 模式
  3. keras对比tf.keras
  4. numpy对比tf.data.Dataset对比……
  5. train_on_batch()对比fit()
  6. GPU 与 CPU
  7. model(x)对比model.predict(x)对比……

不幸的是,上面几乎没有一个是相互独立的,并且每个都至少可以使执行时间相对于另一个增加一倍。幸运的是,您可以通过一些捷径来系统地确定最有效的方法 -
正如我将展示的那样。


我应该怎么办? 目前,唯一的方法是 - 试验您的特定模型、数据和硬件。没有单一的配置总是最有效 - 但是有 一些可以
做和不应该做的事情来简化您的搜索:

> > 做:

  • train_on_batch()+ numpy+ tf.keras+ TF1 + 渴望/图表
  • train_on_batch()+ numpy+ tf.keras+ TF2 + 图表
  • fit()+ numpy+ tf.keras+ TF1/TF2 + 图表 + 大模型和数据

> > 不要:

  • fit()+ numpy+keras适用于中小型模型和数据

  • fit()+ numpy+ tf.keras+ TF1/TF2 + 渴望

  • train_on_batch()+ numpy+ keras+ TF1 + 渴望

  • [专业] tf.python.keras ; 它的运行速度可以慢 10-100 倍,并且有很多错误;

    • 这包括layers, models, optimizers, & 相关的“开箱即用”使用导入;ops、utils 和相关的“私有”导入很好 - 但可以肯定的是,检查 alt 以及它们是否用于tf.keras

有关示例基准测试设置,请参阅我的其他答案底部的代码。上面的列表主要基于另一个答案中的“基准”表。


*上述做与不做的 *限制:

  • 这个问题的标题是“为什么 TF2 比 TF1 慢很多?”,虽然它的主体明确涉及训练,但问题不仅限于此; 即使 在相同的 TF 版本、导入、数据格式等内, 推理 也受到主要速度差异的影响 -
  • RNN 可能会显着改变另一个答案中的数据网格,因为它们在 TF2 中得到了改进
  • 主要使用Conv1D的模型Dense- 没有 RNN、稀疏数据/目标、4/5D 输入和其他配置
  • 输入数据仅限于numpyand tf.data.Dataset,而存在许多其他格式;看其他答案
  • 使用了 GPU;结果在 CPU 上 有所不同。事实上,当我问这个问题时,我的 CUDA 没有正确配置,并且一些结果是基于 CPU 的。

为什么 TF2 牺牲了最实用的质量和速度,换取了急切的执行力? 它没有,很明显 - 图表仍然可用。但如果问题是“为什么急于求成”:

  • 出色的调试 :您可能会遇到很多问题,询问“我如何获得中间层输出”或“我如何检查权重”;急切,它(几乎)像.__dict__. 相比之下,Graph 需要熟悉特殊的后端功能 - 使调试和自省的整个过程大大复杂化。
  • 更快的原型设计 :根据与上述类似的想法;更快的理解 = 更多的时间留给实际的 DL。

如何启用/禁用 EAGER?

tf.enable_eager_execution()  # TF1; must be done before any model/tensor creation
tf.compat.v1.disable_eager_execution() # TF2; above holds

TF2中的误导
;看这里


附加信息

  • 小心_on_batch()TF2 中的方法;根据 TF 开发人员的说法,他们仍然使用较慢的实现,但 不是故意 的 - 即它需要修复。有关详细信息,请参阅其他答案。

对 TensorFlow 开发人员的要求

  1. 请修复, 以及 迭代train_on_batch()调用的性能方面;fit()自定义火车循环对许多人来说很重要,尤其是对我而言。
  2. 添加文档/文档字符串提及这些性能差异以供用户了解。
  3. 提高一般执行速度,以防止窥视者跳到 Pytorch。

致谢 :感谢


更新

  • 19 年 11 月 14 日- 找到了一个模型(在我的实际应用程序中),它在 TF2 上运行速度较慢, _适用于所有配置_ w/Numpy 输入数据。差异范围为 13-19%,平均为 17%。然而,keras和之间的差异更为显着: 18-40% ,平均。32%(TF1 和 2)。( - 除了 Eager,TF2 OOM’d 除外)tf.keras ****

  • 2019 年 11 月 17 日- 开发人员在最近的一次提交on_batch()中更新了方法,声称提高了速度 - 将在 TF 2.1 中发布,或者现在以. 由于我无法让后者运行,因此将把 benching 延迟到 2.1。tf-nightly

  • 2/20/20 - 预测性能也值得一试;例如,在 TF2 中,CPU 预测时间可能涉及周期性尖峰

2022-07-18