【问题标题】:Improving cross-validation throughput in TensorFlow, Keras提高 TensorFlow、Keras 中的交叉验证吞吐量
【发布时间】:2018-11-27 20:48:05
【问题描述】:

我正在研究旨在根据氨基酸序列预测蛋白质结构的 CNN 模型。我正在 Keras 中实现我的 CNN。 Keras API 是与 TensorFlow 1.4.0 捆绑在一起的,所以显然 TensorFlow 是我的后端。我已经安装了 TensorFlow 的 GPU 版本,并且我已经验证正在使用 GPU。我的 GPU 有点老,是 NVidia GTX 760。

当我执行 3X 交叉验证以帮助选择架构和超参数时,我的训练折叠中有 50K 示例,我的验证折叠中有 25K 样本。这些是相当大的数据集,但与我的计算机 (16 GB) 或我的 GPU (2 GB) 上的可用 RAM 相比它们很小。完全解包并表示为 float32 值,由于滑动窗口而引入了冗余,所有折叠加在一起,输入加目标值,占用 316 MB。我已经预先计算了我的折叠,并将每个折叠的文件保存到磁盘。当我尝试架构和超参数时,每次试验都使用相同的折叠。

我从包含单个隐藏层的网络开始,看看我能实现什么,然后切换到两个隐藏层。我在所有早期实验中都使用了 64 的固定批量大小。训练进行得足够快,以至于我并不关心速度。对给定架构执行 3X 交叉验证通常需要大约 12 分钟。

但在我对两层网络进行的最后一个实验中,我决定开始研究批量大小的影响。我了解到,在一定程度上,较小的批量给了我更好的结果。 8 的批次大小是我可以指望不会崩溃的最小批次。我的损失值偶尔会翻转为批量大小为 4 的 NaN,并且它们会频繁翻转为批量大小为 1 或 2 的 NaN。在此之后,网络变得无法训练。我知道梯度不稳定的可能性。我想我得到了一些。

那么为什么不直接使用 8 的批量大小并继续进行呢?问题是速度。使用两个隐藏层,八个批次我花了大约 35 分钟来交叉验证。正如我上面提到的,每批 64 批花费了三分之一的时间。我对三个隐藏层的第一次实验每次试验需要 45 到 65 分钟。我想使用更深的网络研究可能数百种架构和超参数。对于小批量,我可以看到 Keras 中的逐批进度条进度更慢。当一个纪元结束时,我可以看到更长的停顿。

是的,我可以将我的 GPU 升级到 10 系列。我认为这最多只会使我的吞吐量增加一倍?是的,我可以在云端租用 GPU 时间。最终我可能会这样做。但如果我的软件效率低下,我绝对不想把它放在云端烧掉我的钱。

我的理解是(如果我错了,请纠正我)当 GPU 用于正常的 TF / Keras 工作流程时,每个单独的批次都是从 GPU 单独发送到 CPU 的。如果我在 3X 交叉验证方案中训练 50 个网络,这意味着我将相同的数据发送到我的 GPU 150 次。正如我之前提到的,我所有的数据最多占用 316 MB,大约是 GPU 上可用 RAM 的 15%。我可以设计一个将这 316 MB 发送到 GPU 一次 的工作流吗?如果可以,这会对我的吞吐量产生有用的影响吗?直觉上,感觉应该是这样。

还有其他我应该考虑的瓶颈吗?有没有办法分析 TF 或 Keras 操作?

感谢您的任何建议!

【问题讨论】:

    标签: tensorflow keras profiling cross-validation


    【解决方案1】:

    好的。我知道您更关心 Keras 和您的硬件的吞吐量,但这里我想提几点:

    1. 较小的批量给了我更好的结果

    假设您正在运行固定数量的 epoch(例如 5 个)的训练,假设您的数据不是那么大,那么使用较小批量大小的训练自然会为您带来更好的结果,因为它会与更高的批量大小相比,意味着总体上更多的反向支持步骤。如果您正在训练固定数量的训练步骤,我不知道为什么会发生这种情况。

    1. 损失值偶尔会翻转为 NaN,批量大小为 4

    再次,我假设您在此处使用批量标准化和 CNN。在使用 BN 时,实际上从未建议使用像 2 或 4(甚至 8)这样的较小批量大小。并且可能,您可以面对具有较小批次大小的 NaN 的原因之一是,如果您在当前批次中具有低方差,并且如果您将 epsilon 值取得太小,则您的值可能非常小,可能会导致数值未来不稳定。但更一般地说,这可能是你提到的梯度不稳定的情况。考虑使用渐变剪裁看看是否有帮助。

    1. GPU 工作流程

    在这里,我假设您只有 1 个 GPU。不幸的是,您无法使用单 GPU 进行并行化。澄清一下,您不应该担心 GPU RAM 的数据大小。在大多数单 GPU 情况下,当前批次停留在 CPU 上,GPU 只会占用操作。相反,您应该关注 GPU 将计算的参数的大小。由于对于 1 层实验和 3 层实验,您的操作差异很大,我认为这是不可能的,因为您不能同时在同一设备上放置多个操作。对你来说最好的情况是使用更大的批量大小(不要太大——因为这会减少在固定时期训练的情况下的反向支持步骤的数量),这样你就可以覆盖更多的数据单程。

    只是超参数调优的一个小技巧,可以考虑使用Highway-CNNs。这些灵感来自 LSTM 的门控机制,您可以在其中指定大量隐藏层,并且网络会自行弄清楚如何控制层间的信息流。所以简而言之,这实际上会消除您调整网络深度的工作,并允许您调整其他超参数,如学习率或过滤器大小等。

    我希望其中至少有一些对您相关且对您有所帮助;)

    【讨论】:

    • 感谢您的回复,End-2-End。
    • (继续...)澄清是为了:请查看我在交叉验证(stats.stackexchange.com/questions/351890/…)上的这篇文章,它描述了我的停止过程。在停止之前,我正在寻求最小的验证错误。到目前为止,我还没有使用任何批量标准化,因为 1)输入中的每个可能的氨基酸都由 20 维单热向量编码,2)我只有 2-3 层深,3)我的激活函数是 Swish。我会按照你的建议调查 Highway-CNN。
    • 1.明白你对 BN 的看法。 2. 看到你在简历上的帖子。你提出的一个有趣的问题是什么时候停止训练。不幸的是,没有停止训练过程的黄金法则。至少现在还没有。与您提到的操场示例相反,我还看到许多情况下验证损失会上升并且(a)永远不会回来并且(b)会下降但不会下降到更低的点。所以这真的取决于你正在处理的问题和损失函数的形状。
    • 不知道你有没有看过这篇论文Early Stopping | But When?。根据本文,您可以等待验证损失回落到较低点,但计算成本通常会超过验证损失的差异及其在测试集上的重要性。所以他们的基本原则是为验证损失设置一个阈值,一旦 val_loss 在上升时越过它就停止它。看看有没有帮助。
    • 如果您不介意我的询问,您使用 Swish 激活功能的具体原因是什么?我一直在寻找有关 Swish 功能的更好用例。我尝试了各种用例,比较了很多激活函数,几乎所有这些用例中 Leaky-Relu 似乎都优于所有这些用例。 Here's其中一项实验的结果。让我知道您之前是否尝试过其他功能,或者是否有任何理由选择 Swish。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-08-03
    • 1970-01-01
    • 2018-06-13
    • 1970-01-01
    • 2017-04-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多