python - 为什么在tensorflow 2中使用tf.GradientTape进行训练与使用fit API进行训练有不同的行为?

标签 python tensorflow keras deep-learning tensorflow2.0

我刚开始使用tensorflow 2

我熟悉在tensorflow 1中使用keras。并且我通常使用fit方法API来训练模型。但最近在 Tensorflow 2 中,他们引入了急切执行。因此,我在 CiFAR-10 数据集上实现并比较了 fittf.GradientTape 上的简单图像分类器,并分别训练了 20 个时期

经过多次运行,结果如下

  • 使用 fit API 训练的模型
    • 训练数据集,损失约为 0.61-0.65,准确度为 76% - 80%
    • 验证数据集,损失约为 0.8,准确度为 72% - 75%
  • 使用 tf.GradientTape 训练的模型
    • 训练数据集,损失约为 0.15-0.2,准确度为 91% - 94%
    • 验证数据集,损失约为 1.8-2,准确度为 64% - 67%

我不确定为什么模型表现出不同的行为。我想我可能会实现一些错误的事情。我觉得很奇怪的是,在 tf.GradientTape 中,模型开始更快地过度拟合训练数据集

这里有一些片段

  1. 使用fit API
model = SimpleClassifier(10)
model.compile(
    optimizer=Adam(),
    loss=tf.keras.losses.CategoricalCrossentropy(),
    metrics=[tf.keras.metrics.CategoricalAccuracy()]
)
model.fit(X[:split_idx, :, :, :], y[:split_idx, :], batch_size=256, epochs=20, validation_data=(X[split_idx:, :, :, :], y[split_idx:, :]))
  • 使用tf.GradientTape
  • with tf.GradientTape() as tape:
        y_pred = model(tf.stop_gradient(train_X))
        loss = loss_fn(train_y, y_pred)
        gradients = tape.gradient(loss, model.trainable_weights)
    model.optimizer.apply_gradients(zip(gradients, model.trainable_weights))
    

    完整代码可见here in Colab

    引用文献

    最佳答案

    tf.GradientTape 代码中的一些内容可能需要修复:
    1) trainable_variables 不是 trainable_weights。您想要对所有可训练变量应用梯度,而不仅仅是模型权重

    # gradients = tape.gradient(loss, model.trainable_weights)
    gradients = tape.gradient(loss, model.trainable_variables)
    
    # and
    
    # model.optimizer.apply_gradients(zip(gradients, model.trainable_weights))
    model.optimizer.apply_gradients(zip(gradients, model.trainable_variables))
    

    2) 从输入张量中删除tf.stop_gradient

    with tf.GradientTape() as tape:
    #    y_pred = model(tf.stop_gradient(train_X))
        y_pred = model(train_X, training=True)
    

    请注意,我还添加了训练参数。它还应该包含在模型定义中,以包含依赖于阶段的层(例如 BatchNormalization 和 Dropout):

        def call(self, X, training=None):
            X = self.cnn_1(X)
            X = self.bn_1(X, training=training)
            X = self.cnn_2(X)
            X = self.max_pool_2d(X)
            X = self.dropout_1(X)
    
            X = self.cnn_3(X)
            X = self.bn_2(X, training=training)
            X = self.cnn_4(X)
            X = self.bn_3(X, training=training)
            X = self.cnn_5(X)
            X = self.max_pool_2d(X)
            X = self.dropout_2(X)
    
            X = self.flatten(X)
            X = self.dense_1(X)
            X = self.dropout_3(X, training=training)
            X = self.dense_2(X)
            return self.out(X)
    

    通过这几项更改,我设法获得了稍微更好的分数,与 keras.fit 结果更具可比性:

    [19/20] loss: 0.64020, acc: 0.76965, val_loss: 0.71291, val_acc: 0.75318: 100%|██████████| 137/137 [00:12<00:00, 11.25it/s]
    [20/20] loss: 0.62999, acc: 0.77649, val_loss: 0.77925, val_acc: 0.73219: 100%|██████████| 137/137 [00:12<00:00, 11.30it/s]
    

    答案: 区别可能在于 Keras.fit 在幕后完成了大部分这些事情。

    最后,为了清晰和可重复性,我使用的部分训练/评估代码:

    for bIdx, (train_X, train_y) in enumerate(train_batch):
                if bIdx < epoch_max_iter:
                    with tf.GradientTape() as tape:
                        y_pred = model(train_X, training=True)
                        loss = loss_fn(train_y, y_pred)
                        total_loss += (np.sum(loss.numpy()) * train_X.shape[0])
                        total_num += train_X.shape[0]
                        # gradients = tape.gradient(loss, model.trainable_weights)
                        gradients = tape.gradient(loss, model.trainable_variables)
                    total_acc += (metrics(train_y, y_pred) * train_X.shape[0])
    
                    running_loss = (total_loss/total_num)
                    running_acc = (total_acc/total_num)
                    # model.optimizer.apply_gradients(zip(gradients, model.trainable_weights))
                    model.optimizer.apply_gradients(zip(gradients, model.trainable_variables))
    
                    pbar.set_description("[{}/{}] loss: {:.5f}, acc: {:.5f}".format(e, epochs, running_loss, running_acc))
                    pbar.refresh()
                    pbar.update()
    

    以及评估一:

    # Eval loop
            # Calculate something wrong here
            val_total_loss = 0
            val_total_acc = 0
            total_val_num = 0
            for bIdx, (val_X, val_y) in enumerate(val_batch):
                if bIdx >= max_val_iterations:
                    break
                y_pred = model(val_X, training=False)
    

    关于python - 为什么在tensorflow 2中使用tf.GradientTape进行训练与使用fit API进行训练有不同的行为?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59731667/

    相关文章:

    python:从数据中随机抽样但保持相同的分布

    python - 模拟来自同一个 python 模块的相对导入

    python - 如何正确地将 tflite_graph.pb 转换为 detect.tflite

    linux - anaconda env 的 TensorFlow 问题

    python - 如何使用 keras flow_from_directory shuffled 方法检索图像的文件名?

    python - 如何解决 "iterator should return strings, not bytes"

    python - 运行时重载运算符

    python - Tensorflow Dataset.from_generator 在tensorflow 2.0中是否已弃用?它抛出 tf.py_func 弃用错误

    python - 我会尝试更改 keras 预训练模型的 channel

    python - 我如何使用 Mean_Squared_Error (Keras) 知道我的神经网络是否表现良好