tf.GradientTape() 位置对模型训练时间的影响

gau*_*ani 3 python gradient training-data tensorflow eager-execution

我试图更新每个时期的权重,但我正在批量处理数据。问题是,为了规范损失,我需要在训练循环之外录制 TensorFlow 变量(要跟踪和规范化)。但是当我这样做时,训练时间是巨大的。

我认为,它将所有批次的变量累积到图中并在最后计算梯度。

我已经开始跟踪 for 循环外和 for 循环内的变量,后者比第一次要快。我很困惑为什么会发生这种情况,因为无论我做什么,我的模型的可训练变量和损失都保持不变。

# Very Slow

loss_value = 0
batches = 0

with tf.GradientTape() as tape:
    for inputs, min_seq in zip(dataset, minutes_sequence):
        temp_loss_value = my_loss_function(inputs, min_seq)
        batches +=1
        loss_value = loss_value + temp_loss_value

# The following line takes huge time.
grads = tape.gradient(loss_value, model.trainable_variables)

Run Code Online (Sandbox Code Playgroud)
# Very Fast

loss_value = 0
batches = 0

for inputs, min_seq in zip(dataset, minutes_sequence):
    with tf.GradientTape() as tape:
        temp_loss_value = my_loss_function(inputs, min_seq)
        batches +=1
        loss_value = loss_value + temp_loss_value

# If I do the following line, the graph will break because this are out of tape's scope.
    loss_value = loss_value / batches

# the following line takes huge time
grads = tape.gradient(loss_value, model.trainable_variables)

Run Code Online (Sandbox Code Playgroud)

当我在 for 循环内部声明 tf.GradientTape() 时,它非常快但我在外面它很慢。

PS - 这是针对自定义损失的,该架构仅包含一个大小为 10 的隐藏层。

我想知道,tf.GradientTape() 的位置的区别以及它应该如何用于批处理数据集中的每个时期权重更新。

Sun*_*nny 5

磁带变量主要用于观察可训练的张量变量(记录变量的先前值和变化值),以便我们可以根据损失函数计算训练时期的梯度。它是这里用来记录变量状态的 python 上下文管理器构造的实现。关于 python 上下文管理器的优秀资源在这里. 因此,如果在循环内部,它将记录该前向传递的变量(权重),以便我们可以一次计算所有这些变量的梯度(而不是像在没有像 tensorflow 这样的库的幼稚实现中那样基于堆栈的梯度传递) . 如果它在循环之外,它将记录所有时期的状态,并且根据 Tensorflow 源代码,如果使用 TF2.0,它也会刷新,与模型开发人员必须处理刷新的 TF1.x 不同。在您的示例中,您没有设置任何编写器,但如果设置了任何编写器,它也会这样做。因此,对于上面的代码,它将继续记录(内部使用 Graph.add_to_collection 方法)所有权重,随着 epochs 的增加,您应该会看到减速。减速率将与网络的大小(可训练变量)和当前的纪元数成正比。

所以把它放在循环里面是正确的。此外,梯度应该应用在 for 循环内部而不是外部(与 with 相同的缩进级别),否则您仅在训练循环结束时(在最后一个时期之后)应用梯度。我看到您的训练对于梯度检索的当前位置可能不是那么好(之后它被应用到您的代码中,尽管您在代码段中省略了它)。

我刚刚找到的关于gradienttape 的另一种好资源