许多用户都将其作为切换到Pytorch的原因,但我还没有找到牺牲/最渴望的实用质量,速度和执行力的理由/解释。

以下是TF1与TF2的代码基准测试性能-TF1在上的运行速度从快47%到276%。

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

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

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

基准测试结果:



更新:根据以下代码禁用急切执行无济于事。但是,该行为是不一致的:有时以图形方式运行会有所帮助,而其他时候其运行速度要比Eager慢。

由于TF开发人员没有出现在任何地方,因此我将自己进行调查-可以跟踪相关的Github问题的进展。

更新2 :分享大量实验结果,并附有说明;应该在今天完成。

基准代码:

# 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))

最佳答案

更新8/1730/2020 :TF 2.3终于做到了:所有情况下的运行速度都比以前的任何版本都快,或者明显更快。
此外,我之前的更新对TF不公平;我的GPU值得指责,最近一直过热。如果您看到迭代时间的上升趋势图,这是一个可靠的症状。最后,查看关于Eager vs Graph的开发者说明。
这可能是我对此答案的最新更新。您只能在设备上找到模型速度的真实统计信息。

更新2020年5月19日:TF 2.2,使用相同的测试:急切速度只有很小的提高。下面的大号Numpy train_on_batch情况的图,x轴是连续拟合迭代;我的GPU尚未达到满负荷运行,因此怀疑它是否在节流,但是随着时间的推移,迭代速度确实会变慢。
python - 为什么TensorFlow 2比TensorFlow 1慢得多?-LMLPHP
按照上述方法,Graph和Eager的 1.56x 1.97x 分别比其TF1慢。不确定我是否会进一步调试,因为我正在考虑根据TensorFlow对自定义/底层功能的不良支持切换到Pytorch。但是,我确实打开了Issue以获取开发者的反馈。

更新2/18/2020 :我每晚排练2.1和2.1;结果好坏参半。除了一个配置(模型和数据大小)外,其他配置都快于或快于TF2和TF1的最佳配置。速度较慢且急剧下降的是大型-尤其是。在图形执行中( 1.6x到2.5x慢)。
此外,对于我测试的大型模型,Graph和Eager之间存在极大的可重复性差异-无法通过随机性/计算并行性来解释这一差异。我目前无法按时间限制显示这些声明的可重现代码,因此我强烈建议您针对自己的模型进行测试。
尚未针对这些问题打开Git问题,但我确实对original发表了评论-尚未回复。取得进展后,我将更新答案。

VERDICT :如果您知道自己在做什么,则不是。但是,如果您不这样做,则可能会花费大量成本-平均而言,需要进行几次GPU升级,而在最坏的情况下,则需要多个GPU。

答案:旨在提供对该问题的高级描述,以及有关如何根据您的需求决定培训配置的指南。有关详细的低级描述(包括所有基准测试结果和所使用的代码),请参阅我的其他答案。
如果我学到更多信息,我将更新我的答案,并提供更多信息-可以为该问题添加书签/“加注星标”以供引用。

问题摘要:作为TensorFlow开发人员Q.Scott Zhu的confirmed,TF2专注于Eager执行和带有Keras的紧密集成的开发,这涉及到TF源的全面更改-包括图形级。好处:大大扩展了处理,分发,调试和部署功能。但是,其中一些的成本是速度。
但是,这个问题要复杂得多。不仅仅是TF1和TF2-导致火车速度显着差异的因素包括:

  • TF2与TF1
  • 渴望与图表模式
  • kerastf.keras
  • numpytf.data.Dataset与...
  • train_on_batch()fit()
  • GPU与CPU
  • model(x)model.predict(x)与...

  • 不幸的是,以上几乎没有一个是彼此独立的,并且每个相对于另一个可以至少使执行时间加倍。幸运的是,您可以确定哪些是系统上最有效的方法,并提供一些捷径-如我所展示的。

    我应该怎么做? 当前,唯一的方法是-针对您的特定模型,数据和硬件进行实验。没有一个单一的配置总能达到最佳效果-但是有一个可以简化您的搜索的步骤:
    >> DO:
  • 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倍,并且带有许多错误; more info
  • 这包括layersmodelsoptimizers和相关的“即用型”用法导入; ops,utils和相关的“私有(private)”导入都可以-但可以肯定的是,请检查alt,以及是否在tf.keras
  • 中使用了它们


    请参阅我的其他答案底部的代码,以获取基准测试设置示例。上面的列表主要基于其他答案中的“BENCHMARKS”表。

    上面的“可做与不可做”中的:
  • 这个问题的标题是“为什么TF2比TF1慢得多?”,虽然它的主体明确地涉及训练,但问题并不局限于此。即使在相同的TF版本,导入,数据格式等中,推理也将受到主要速度差异的影响-参见this answer
  • RNN可能会显着改变其他答案中的数据网格,因为它们已在TF2中得到了改进
  • 模型主要使用Conv1DDense-无RNN,稀疏数据/目标,4/5D输入和其他配置
  • 输入数据仅限于numpytf.data.Dataset,同时存在许多其他格式;例如,查看其他答案
  • 使用
  • GPU;结果在CPU上会有所不同。实际上,当我问这个问题时,我的CUDA配置不正确,并且某些结果是基于CPU的。

  • 为什么TF2为了急切执行而牺牲了最实用的质量,速度? 显然没有,它仍然可用。但是如果问题是“为什么要渴望”:
  • 高级调试:您可能会遇到许多问题,询问“如何获取中间层输出”或“如何检查权重”;渴望,它(几乎)像.__dict__一样简单。相比之下,Graph需要熟悉特殊的后端功能-极大地增加了调试和自省(introspection)的整个过程。
  • 更快的原型(prototype)制作:与上面类似的想法;更快的理解=剩下更多的时间用于实际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中产生误导;参见here

    其他信息:
  • 小心TF2中的_on_batch()方法;根据TF开发人员的说法,他们仍然使用较慢的实现方式,但并非有意为之-即必须解决。有关详细信息,请参见其他答案。

  • 请求张力转换:
  • 请修复train_on_batch(),以及迭代调用fit()的性能方面;定制火车循环对许多人尤其是我来说很重要。
  • 添加有关这些性能差异的文档/文档字符串,以供用户了解。
  • 提高一般执行速度,以防止窥视跳到Pytorch。

  • 致谢:感谢
  • 问:TensorFlow开发人员Scott Zhu,关于此事的detailed clarification
  • P. Andrey分享了useful testing和讨论。

  • 更新:
  • 11/14/19-找到了一个模型(在我的实际应用程序中),该模型在带有Numpy输入数据的所有*配置的TF2上运行速度较慢。差异范围为13-19%,平均为17%。但是,kerastf.keras之间的差异更为明显: 18-40%,平均。 32%(TF1和2)。 (*-渴望者(TF2 OOM'd除外))
  • 11/17/19-开发人员在recent commit中更新了on_batch()方法,指出速度有所提高-将在TF 2.1中发布,或者现在可以作为tf-nightly使用。由于我无法让后者运行,因此将替补席推迟到2.1。
  • 2/20/20-预测性能也值得借鉴;例如,在TF2中,CPU预测时间可能涉及periodic spikes
  • 关于python - 为什么TensorFlow 2比TensorFlow 1慢得多?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/58441514/

    10-11 19:21