无法使用 GradientTape 重现 model.fit

Posted

技术标签:

【中文标题】无法使用 GradientTape 重现 model.fit【英文标题】:can't reproduce model.fit with GradientTape 【发布时间】:2020-03-07 03:07:39 【问题描述】:

我一直在尝试调查原因(例如,通过在训练期间检查权重、梯度和激活)为什么具有 0.001 学习率的 SGD 在训练中有效,而 Adam 却没有这样做。 (请参阅我之前的帖子 [这里](Why is my loss (binary cross entropy) converging on ~0.6? (Task: Natural Language Inference)"为什么我的损失(二进制交叉熵)收敛到 ~0.6?(任务:自然语言推理)"))

注意:我在这里也使用了与我之前的帖子相同的模型。


使用 tf.keras,我使用 model.fit() 训练了神经网络:

model.compile(optimizer=SGD(learning_rate=0.001),
            loss='binary_crossentropy',
            metrics=['accuracy'])

model.fit(x=ds,
        epoch=80,
        validation_data=ds_val)

这导致了如下图所示的 epoch loss,在第一个 epoch 内,它达到了 0.46 的 train_loss,然后最终导致 train_loss 为 0.1241 和 val_loss 为 0.2849。

我会使用tf.keras.callbacks.Tensorboard(histogram_freq=1) 来训练网络,同时使用 SGD(0.001) 和 Adam 进行调查,但它在变量:0 上抛出 InvalidArgumentError,这是我无法破译的。所以我尝试使用 GradientTape 编写一个自定义训练循环并绘制值。


使用 tf.GradientTape(),我尝试使用完全相同的模型和数据集来重现结果,但是 epoch 损失的训练速度非常慢,在 15 个 epoch 后达到了 0.676 的训练损失(参见下图),我的实现有问题吗? (代码如下)

@tf.function
def compute_grads(train_batch: Dict[str,tf.Tensor], target_batch: tf.Tensor, 
                 loss_fn: Loss, model: tf.keras.Model):
    with tf.GradientTape(persistent=False) as tape:
        # forward pass
        outputs = model(train_batch)
        # calculate loss
        loss = loss_fn(y_true=target_batch, y_pred=outputs)

    # calculate gradients for each param
    grads = tape.gradient(loss, model.trainable_variables)
    return grads, loss

BATCH_SIZE = 8
EPOCHS = 15

bce = BinaryCrossentropy()
optimizer = SGD(learning_rate=0.001)

for epoch in tqdm(range(EPOCHS), desc='epoch'):
    # - accumulators
    epoch_loss = 0.0

    for (i, (train_batch, target_dict)) in tqdm(enumerate(ds_train.shuffle(1024).batch(BATCH_SIZE)), desc='step'):

        (grads, loss) = compute_grads(train_batch, target_dict['target'], bce, model)
        optimizer.apply_gradients(zip(grads, model.trainable_variables))

        epoch_loss += loss

    avg_epoch_loss = epoch_loss/(i+1)
    tensorboard_scalar(writer, name='epoch_loss', data=avg_epoch_loss, step=epoch)  # custom helper function
    print("Epoch : epoch_loss = ".format(epoch, avg_epoch_loss))

提前致谢!

【问题讨论】:

【参考方案1】:

检查您是否对数据集进行了混洗,那么问题可能来自使用 tf.Dataset 方法进行的混洗。它当时只在数据集一个桶中洗牌。使用 Keras.Model.fit 会产生更好的结果,因为它可能会增加另一个洗牌。 通过添加numpy.random.shuffle 的改组,它可以提高训练性能。 From this reference.

将其应用于数据集生成的示例是:

numpy_data = np.hstack([index_rows.reshape(-1, 1), index_cols.reshape(-1, 1), index_data.reshape(-1, 1)])

np.random.shuffle(numpy_data)

indexes = np.array(numpy_data[:, :2], dtype=np.uint32)
labels = np.array(numpy_data[:, 2].reshape(-1, 1), dtype=np.float32)

train_ds = data.Dataset.from_tensor_slices(
    (indexes, labels)
).shuffle(100000).batch(batch_size, drop_remainder=True)

如果这不起作用,您可能需要使用 Dataset .repeat(epochs_number) and .shuffle(..., reshuffle_each_iteration=True):

train_ds = data.Dataset.from_tensor_slices(
    (np.hstack([index_rows.reshape(-1, 1), index_cols.reshape(-1, 1)]), index_data)
    ).shuffle(100000, reshuffle_each_iteration=True
    ).batch(batch_size, drop_remainder=True
    ).repeat(epochs_number)

for ix, (examples, labels) in train_ds.enumerate():
    train_step(examples, labels)
    current_epoch = ix // (len(index_data) // batch_size)

这种解决方法既不美观也不自然,目前您可以使用它来随机播放每个时期。这是一个已知问题,将得到修复,将来您可以使用for epoch in range(epochs_number) 而不是.repeat()

here 提供的解决方案也可能有很大帮助。你可能想检查一下。

如果不是这样,您可能需要加速 TF2.0 GradientTape。这可以是解决方案: TensorFlow 2.0 引入了functions 的概念,将 Eager 代码转换为图形代码。

用法非常简单。唯一需要更改的是所有相关函数(like compute_lossapply_gradients)都必须用 @tf.function. 注释

【讨论】:

以上是关于无法使用 GradientTape 重现 model.fit的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 tf.GradientTape 模拟 ReLU 梯度

在 Tensorflow 2.0 中使用 GradientTape() 和 jacobian() 时出错

如何使用Tensorflows GradientTape()计算偏差

Tensorflow 强化学习 RNN 在使用 GradientTape 优化后返回 NaN

tf.GradientTape 为渐变返回 None

AttributeError: 'Tensor' 对象没有属性'_in_graph_mode'。