ChatGPT解决这个技术问题 Extra ChatGPT

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

许多用户将其作为切换到 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 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))
你有没有用过 cProfile 这样的工具来分析哪个部分让它们如此不同?
@zihaozhihao I have,虽然不是专门为此;根据之前的链接并编写自定义优化器,我已经熟悉调用的差异,但不理解为什么一个人比另一个人慢 - 任何非 TF 专家也无法从源头上理解它,除了是一团乱麻之外,它并没有记录相对的表现。需要图形/硬件级别的英特尔,分析器不会提供(只要我能够使用它们)
两个测试中的 numpy 版本是否相同?
哎呀......如果单独的旧 Keras 已经比 PyTorch 慢得多,现在想象一下。
问题与模型大小有关吗?您是否也尝试在其他操作系统上运行相同的基准测试?

O
OverLordGoldDragon

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

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

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

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

https://i.stack.imgur.com/u74bU.png

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

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

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

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

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

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

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

问题摘要:作为 TensorFlow 开发人员 Q. Scott Zhu 的confirmed,TF2 专注于 Eager execution 和与 Keras 的紧密集成,这涉及到 TF 源的全面变化——包括图形级别的变化。好处:大大扩展了处理、分发、调试和部署能力。然而,其中一些成本是速度。

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

TF2 vs. TF1 Eager vs. Graph mode keras vs. tf.keras numpy vs. tf.data.Dataset vs. ... train_on_batch() vs. fit() GPU vs. CPU model(x) vs. model.predict( x) 与 ...

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

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

>> 做:

train_on_batch() + numpy + tf.keras + TF1 + Eager/Graph

train_on_batch() + numpy + tf.keras + TF2 + Graph

fit() + numpy + tf.keras + TF1/TF2 + Graph + 大型模型和数据

>> 不要:

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

fit() + numpy + tf.keras + TF1/TF2 + Eager

train_on_batch() + numpy + keras + TF1 + Eager

[专业] tf.python.keras;它的运行速度可以慢 10-100 倍,并且有很多错误;更多信息 这包括层、模型、优化器和相关的“开箱即用”使用导入; ops、utils 和相关的“私有”导入很好 - 但可以肯定的是,检查 alt 以及它们是否在 tf.keras 中使用

这包括层、模型、优化器和相关的“开箱即用”使用导入; ops、utils 和相关的“私有”导入很好 - 但可以肯定的是,检查 alt 以及它们是否在 tf.keras 中使用

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

上述做与不做的限制:

这个问题的标题是“为什么 TF2 比 TF1 慢很多?”,虽然它的主体明确涉及训练,但问题不仅限于此;即使在相同的 TF 版本、导入、数据格式等内,推理也受到主要速度差异的影响 - 请参阅此答案。

RNN 可能会显着改变另一个答案中的数据网格,因为它们在 TF2 中得到了改进

模型主要使用 Conv1D 和 Dense - 没有 RNN、稀疏数据/目标、4/5D 输入和其他配置

输入数据仅限于 numpy 和 tf.data.Dataset,而存在许多其他格式;看其他答案

使用了 GPU;结果在 CPU 上会有所不同。事实上,当我问这个问题时,我的 CUDA 没有正确配置,并且一些结果是基于 CPU 的。

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

高级调试:您可能遇到过很多问题,询问“我如何获得中间层输出”或“我如何检查权重”;使用 Eager,它(几乎)就像 .__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 中的误导;见here

附加信息:

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

对 TensorFlow 开发人员的要求:

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

致谢:感谢

Q. TensorFlow 开发人员 Scott Zhu 对此事的详细说明。

P. Andrey 分享有用的测试和讨论。

更新:

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

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

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


fit_generator 呢? ...我几乎从不想要 train_on_batch 并且跨批次管理我自己的训练循环是一个巨大的、巨大的反模式,即使付出巨大的代价也要避免。
@ely 正如我在其他答案中所指出的那样,它仍有待测试 - 但如果有的话,我预测它是 fit 并带有少量额外的数据处理开销。至于火车循环,我编写了自己的自定义循环,最终变成了一种 API; fit_generator 缺乏内省、可定制性和保存/加载 - 所以对我来说绝对不行。我最终会在 Github 上发布我的训练循环。
缺乏内省和可定制性对我来说是一个特性,而不是一个错误。 IDK 保存/加载评论指的是什么?在不受数据生成器控制的循环期间进行中间保存/加载? (我个人也很高兴只依赖回调,并且认为需要任何进一步的可定制性作为我的训练循环设计错误的代码味道)。
@ely 这并不简单,但对于使用复杂的输入数据管道、目标函数和非 API 模型配置(例如集成)进行训练是必要的。内省是许多调试和特征工程目的。缺乏外部保存/加载和训练循环pausability & resumability 用于计算昂贵的模型 - 一场噩梦。无论如何,最终取决于您的特定需求,以及跑题;使用 fit_generator 为您的应用程序测试性能的最可靠方法是测试它。
M
M.Innat

此答案:旨在提供对该问题的详细的图形/硬件级描述 - 包括 TF2 与 TF1 训练循环、输入数据处理器以及 Eager 与 Graph 模式执行。有关问题摘要和解决指南,请参阅我的其他答案。

性能判断:有时一个更快,有时另一个更快,具体取决于配置。就 TF2 和 TF1 而言,它们的平均水平差不多,但基于配置的显着差异确实存在,而且 TF1 胜过 TF2 的频率高于反之亦然。请参阅下面的“基准测试”。

渴望VS。图:对于某些人来说,整个答案的核心:根据我的测试,TF2 的渴望比 TF1 慢。详情往下看。

两者之间的根本区别在于:Graph 主动建立了一个计算网络,并在“被告知”时执行——而 Eager 在创建时执行所有操作。但故事才从这里开始:

Eager 并非没有 Graph,实际上可能主要是 Graph,与预期相反。它主要是执行图表 - 这包括模型和优化器权重,构成图表的很大一部分。

Eager 在执行时重建自己图的一部分; Graph 未完全构建的直接后果 - 请参阅分析器结果。这具有计算开销。

Eager 在使用 Numpy 输入时速度较慢;根据这个 Git 评论和代码,Eager 中的 Numpy 输入包括将张量从 CPU 复制到 GPU 的开销成本。单步调试源代码,数据处理差异一目了然; Eager 直接传递 Numpy,而 Graph 传递张量,然后对 Numpy 求值;不确定确切的过程,但后者应该涉及 GPU 级别的优化

TF2 Eager 比 TF1 Eager 慢 - 这是……出乎意料的。请参阅下面的基准测试结果。差异从可忽略不计到显着,但始终如一。不确定为什么会这样 - 如果 TF 开发人员澄清,将更新答案。

TF2 与 TF1:引用 TF 开发者 Q. Scott Zhu 的相关部分,response - 带有我的重点和改写:

在 Eager 中,运行时需要执行操作并返回每行 Python 代码的数值。单步执行的性质导致它很慢。

在 TF2 中,Keras 利用 tf.function 构建其用于训练、评估和预测的图。我们称它们为模型的“执行功能”。在 TF1 中,“执行函数”是一个 FuncGraph,它与 TF 函数共享一些通用组件,但实现方式不同。

在此过程中,我们不知何故为 train_on_batch()、test_on_batch() 和 predict_on_batch() 留下了不正确的实现。它们在数值上仍然是正确的,但是 x_on_batch 的执行函数是纯 python 函数,而不是 tf.function 包装的 python 函数。这将导致缓慢

在 TF2 中,我们将所有输入数据转换为一个 tf.data.Dataset,通过它我们可以统一我们的执行函数来处理单一类型的输入。数据集转换可能会有一些开销,我认为这是一次性开销,而不是每批成本

上面最后一段的最后一句,以及下一段的最后一句:

为了克服 Eager 模式下的缓慢,我们有 @tf.function,它将 python 函数转换为图形。像 np 数组一样输入数值时,tf.function 的 body 被转换成静态图,经过优化,返回最终值,速度快,性能应该和 TF1 图模式差不多。

我不同意 - 根据我的分析结果,这表明 Eager 的输入数据处理比 Graph 慢得多。此外,特别不确定 tf.data.Dataset,但 Eager 确实重复调用多个相同的数据转换方法 - 请参阅分析器。

最后,开发者的链接提交:Significant number of changes to support the Keras v2 loops

训练循环:取决于 (1) Eager vs. Graph; (2) 输入数据格式,在 TF2 中,训练会以不同的训练循环进行 - 在 TF2 中,_select_training_loop()training.py,其中之一:

training_v2.Loop()
training_distributed.DistributionMultiWorkerTrainingLoop(
              training_v2.Loop()) # multi-worker mode
# Case 1: distribution strategy
training_distributed.DistributionMultiWorkerTrainingLoop(
            training_distributed.DistributionSingleWorkerTrainingLoop())
# Case 2: generator-like. Input is Python generator, or Sequence object,
# or a non-distributed Dataset or iterator in eager execution.
training_generator.GeneratorOrSequenceTrainingLoop()
training_generator.EagerDatasetOrIteratorTrainingLoop()
# Case 3: Symbolic tensors or Numpy array-like. This includes Datasets and iterators 
# in graph mode (since they generate symbolic tensors).
training_generator.GeneratorLikeTrainingLoop() # Eager
training_arrays.ArrayLikeTrainingLoop() # Graph

每个都以不同的方式处理资源分配,并对性能和能力产生影响。

Train Loops:fit vs train_on_batchkeras vs. tf.keras:四个中的每一个都使用不同的 train loops,尽管可能不是所有可能的组合。例如,keras' fit 使用 fit_loop 的形式,例如 training_arrays.fit_loop(),它的 train_on_batch 可能使用 K.function()tf.keras 具有更复杂的层次结构,在前面部分中进行了部分描述。

训练循环:文档 -- 一些不同执行方法的相关source docstring

与其他 TensorFlow 操作不同,我们不会将 python 数值输入转换为张量。此外,为每个不同的 python 数值生成一个新图

函数为每组独特的输入形状和数据类型实例化一个单独的图形。

单个 tf.function 对象可能需要映射到引擎盖下的多个计算图。这应该仅作为性能可见(跟踪图具有非零计算和内存成本)

输入数据处理器:与上面类似,处理器是根据运行时配置(执行模式、数据格式、分配策略)设置的内部标志逐个选择的。最简单的情况是 Eager,它直接与 Numpy 数组一起工作。有关一些具体示例,请参阅 this answer

模型大小,数据大小:

是决定性的;没有任何一种配置能够超越所有模型和数据大小。

相对于模型大小的数据大小很重要;对于小数据和模型,数据传输(例如 CPU 到 GPU)开销可能占主导地位。同样,小开销处理器可以在每个数据转换时间占主导地位的大数据上运行更慢(请参阅“PROFILER”中的 convert_to_tensor)

每个火车循环的速度不同,输入数据处理器处理资源的方式也不同。

BENCHMARKS:磨碎的肉。 -- Word Document -- Excel Spreadsheet

术语:

%-less 数字都是秒

计算为 (1-longer_time/shorter_time)*100;理由:我们感兴趣的是哪个因素比另一个更快;更短/更长实际上是非线性关系,对于直接比较没有用处

符号确定:TF2 vs TF1:+ 如果 TF2 更快 GvE(Graph vs. Eager):+ 如果 Graph 更快

TF2 vs TF1:+ 如果 TF2 更快

GvE(Graph vs. Eager):+ 如果 Graph 更快

TF2 = TensorFlow 2.0.0 + Keras 2.3.1; TF1 = TensorFlow 1.14.0 + Keras 2.2.5

轮廓仪:

PROFILER - 说明:Spyder 3.3.6 IDE 分析器。

一些功能在其他功能的嵌套中重复;因此,很难找到“数据处理”和“训练”功能之间的确切区别,因此会有一些重叠 - 正如最后一个结果中所表明的那样。

% 数字计算 wrt 运行时减去构建时间

通过将所有(唯一)运行时相加计算的构建时间,这些运行时被调用 1 或 2 次

训练时间是通过将所有(唯一)运行时相加来计算的,这些运行时被称为与迭代次数相同的次数,以及它们的一些嵌套运行时

不幸的是,函数是根据它们的原始名称进行分析的(即 _func = func 将分析为 func),这在构建时间中混合在一起 - 因此需要排除它

测试环境:

在底部执行代码,运行最少的后台任务

正如这篇文章所建议的,GPU 在计时迭代之前经过几次迭代“热身”

从源代码构建的 CUDA 10.0.130、cuDNN 7.6.0、TensorFlow 1.14.0 和 TensorFlow 2.0.0 以及 Anaconda

Python 3.7.4、Spyder 3.3.6 IDE

GTX 1070、Windows 10、24GB DDR4 2.4-MHz RAM、i7-7700HQ 2.8-GHz CPU

方法:

基准“小”、“中”和“大”模型和数据大小

修复每个模型大小的参数数量,与输入数据大小无关

“更大”的模型有更多的参数和层

“更大”的数据有更长的序列,但相同的 batch_size 和 num_channels

模型只使用 Conv1D,密集的“可学习”层;每个 TF 版本的实现都避免了 RNN。差异

始终在基准测试循环之外运行一列适合的火车,以省略模型和优化器图形构建

不使用稀疏数据(例如 layers.Embedding())或稀疏目标(例如 SparseCategoricalCrossEntropy()

限制:一个“完整”的答案可以解释所有可能的火车循环和迭代器,但这肯定超出了我的时间能力、不存在的薪水或一般必要性。结果与方法论一样好 - 以开放的心态进行解释。

代码:

import numpy as np
import tensorflow as tf
import random
from termcolor import cprint
from time import time

from tensorflow.keras.layers import Input, Dense, Conv1D
from tensorflow.keras.layers import Dropout, GlobalAveragePooling1D
from tensorflow.keras.models import Model
from tensorflow.keras.optimizers import Adam
import tensorflow.keras.backend as K
#from keras.layers import Input, Dense, Conv1D
#from keras.layers import Dropout, GlobalAveragePooling1D
#from keras.models import Model 
#from keras.optimizers import Adam
#import keras.backend as K

#tf.compat.v1.disable_eager_execution()
#tf.enable_eager_execution()

def reset_seeds(reset_graph_with_backend=None, verbose=1):
    if reset_graph_with_backend is not None:
        K = reset_graph_with_backend
        K.clear_session()
        tf.compat.v1.reset_default_graph()
        if verbose:
            print("KERAS AND TENSORFLOW GRAPHS RESET")

    np.random.seed(1)
    random.seed(2)
    if tf.__version__[0] == '2':
        tf.random.set_seed(3)
    else:
        tf.set_random_seed(3)
    if verbose:
        print("RANDOM SEEDS RESET")

print("TF version: {}".format(tf.__version__))
reset_seeds()

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

def make_model_small(batch_shape):
    ipt   = Input(batch_shape=batch_shape)
    x     = Conv1D(128, 40, strides=4, padding='same')(ipt)
    x     = GlobalAveragePooling1D()(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_model_medium(batch_shape):
    ipt = Input(batch_shape=batch_shape)
    x = ipt
    for filters in [64, 128, 256, 256, 128, 64]:
        x  = Conv1D(filters, 20, strides=1, padding='valid')(x)
    x     = GlobalAveragePooling1D()(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_model_large(batch_shape):
    ipt   = Input(batch_shape=batch_shape)
    x     = Conv1D(64,  400, strides=4, padding='valid')(ipt)
    x     = Conv1D(128, 200, strides=1, padding='valid')(x)
    for _ in range(40):
        x = Conv1D(256,  12, strides=1, padding='same')(x)
    x     = Conv1D(512,  20, strides=2, padding='valid')(x)
    x     = Conv1D(1028, 10, strides=2, padding='valid')(x)
    x     = Conv1D(256,   1, strides=1, padding='valid')(x)
    x     = GlobalAveragePooling1D()(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))
           
def make_data_tf(batch_shape, n_batches, iters):
    data = np.random.randn(n_batches, *batch_shape),
    trgt = np.random.randint(0, 2, (n_batches, batch_shape[0], 1))
    return tf.data.Dataset.from_tensor_slices((data, trgt))#.repeat(iters)

batch_shape_small  = (32, 140,   30)
batch_shape_medium = (32, 1400,  30)
batch_shape_large  = (32, 14000, 30)

batch_shapes = batch_shape_small, batch_shape_medium, batch_shape_large
make_model_fns = make_model_small, make_model_medium, make_model_large
iterations = [200, 100, 50]
shape_names = ["Small data",  "Medium data",  "Large data"]
model_names = ["Small model", "Medium model", "Large model"]

def test_all(fit=False, tf_dataset=False):
    for model_fn, model_name, iters in zip(make_model_fns, model_names, iterations):
        for batch_shape, shape_name in zip(batch_shapes, shape_names):
            if (model_fn is make_model_large) and (batch_shape == batch_shape_small):
                continue
            reset_seeds(reset_graph_with_backend=K)
            if tf_dataset:
                data = make_data_tf(batch_shape, iters, iters)
            else:
                data = make_data(batch_shape)
            model = model_fn(batch_shape)

            if fit:
                if tf_dataset:
                    model.train_on_batch(data.take(1))
                    t0 = time()
                    model.fit(data, steps_per_epoch=iters)
                    print("Time/iter: %.4f sec" % ((time() - t0) / iters))
                else:
                    model.train_on_batch(*data)
                    timeit(model.fit, iters, *data, _verbose=1, verbose=0)
            else:
                model.train_on_batch(*data)
                timeit(model.train_on_batch, iters, *data, _verbose=1)
            cprint(">> {}, {} done <<\n".format(model_name, shape_name), 'blue')
            del model

test_all(fit=True, tf_dataset=False)

我不确定您的代码是否正确。我认为您的模型始终在图形模式下运行,因为您调用 model.compile 而没有 run_eagerly=True 参数。如果处于渴望模式,您可以使用 tf.function 在图形模式下运行部分代码。因此,我认为 compile 的默认实现是创建计算图,而不是出于性能原因急切地运行它。另请注意,如果您的模型是卷积模型,那么您在图形模式下看不到加速,因为 python 交互是最小的。如果你做了很多数学运算,它会产生很大的不同(也在内存利用率方面)。
@OverLordGoldDragon 但在 TF 2 中,默认为渴望模式,但没有 run_eagerly=Truemodel.compile 确保图形模式,或者不是?
@OverLordGoldDragon 我同意并非所有导入的方法都以图形模式运行,但我认为 model.compilemodel.fit 必须确保训练在内部以图形模式运行。
@OverLordGoldDragon TRUE - “tf.keras.Model.compile 采用三个重要参数:......此外,为了确保模型训练和评估热切,您可以确保将 run_eagerly=True 作为参数传递给编译。” (来源 tensorflow.org/guide/keras/overview)因此,如果您不通过 run_eagerly=True 模型,我可以在图形模式下运行。我不确定决定因素是什么,但如果它比渴望更有效,为什么它不会在图形模式下运行。
你想要更多的证据吗? :) “默认情况下,我们将尝试将您的模型编译为静态图,以提供最佳执行性能。” (github.com/tensorflow/tensorflow/blob/r2.0/tensorflow/python/…)