Tensorflow 梯度返回 nan 或 Inf

Ami*_*mir 1 python deep-learning keras tensorflow generative-adversarial-network

我正在尝试使用tensorflow和keras实现WGAN-GP模型(用于来自kaggle的信用卡欺诈数据)。

我主要遵循keras 网站中提供的示例代码以及互联网上的其他几个示例代码(但将它们从图像更改为我的数据),并且非常简单。

但是当我想更新批评家时,批评家权重的损失梯度nan在几批之后就变成了全部。这导致批评者的权重变成nan,然后生成器的权重变成nan,...所以一切都变成nan

在此输入图像描述

我使用tf.debugging.enable_check_numerics后发现问题的出现是因为-Inf经过一些迭代后a出现在梯度中。

这与损失中的梯度惩罚项直接相关,因为当我删除它时,问题就消失了。

请注意,它gp本身不是nan,但是当我获得损失批评家权重的梯度(c_grads在下面的代码中)时,它包含-Inf并以某种方式变成全部nan

我检查了数学和网络架构是否存在可能的错误(例如梯度消失的概率等),并且我花了好几个小时检查代码是否存在可能的错误。但我被困住了。

如果有人能找到问题的根源,我将非常感激

注意: 请记住,评论家的输出和损失函数与原始论文略有不同(因为我试图使其成为有条件的),但这与问题无关,因为正如我之前所说,整个问题消失了当我刚刚删除梯度惩罚项时

这是我的批评者:

critic = keras.Sequential([
        keras.layers.Input(shape=(x_dim,), name='c-input'),
        keras.layers.Dense(64, kernel_initializer=keras.initializers.he_normal(), name='c-hidden-1'),
        keras.layers.LeakyReLU(alpha=0.25, name='c-activation-1'),
        keras.layers.Dense(32, kernel_initializer=keras.initializers.he_normal(), name='c-hidden-2'),
        keras.layers.LeakyReLU(alpha=0.25, name='c-activation-2'),
        keras.layers.Dense(2, activation='tanh', name='c-output')
    ], name='critic')
Run Code Online (Sandbox Code Playgroud)

这是我的梯度惩罚函数:

def gradient_penalty(self, batch_size, x_real, x_fake):
    # get the random linear interpolation of real and fake data (x hat)
    alpha = tf.random.uniform([batch_size, 1], 0.0, 1.0)
    x_interpolated = x_real + alpha * (x_fake - x_real)
    with tf.GradientTape() as gp_tape:
        gp_tape.watch(x_interpolated)
        # Get the critic score for this interpolated data
        scores = 0.5 * (self.critic(x_interpolated, training=True) + 1.0)
    # Calculate the gradients w.r.t to this interpolated data
    grads = gp_tape.gradient(scores, x_interpolated)
    # Calculate the norm of the gradients
    # Gradient penalty enforces the gradient to stay close to 1.0 (1-Lipschitz constraint)
    gp = tf.reduce_mean(tf.square(tf.norm(grads, axis=-1) - 1.0))
    return gp
Run Code Online (Sandbox Code Playgroud)

这是评论家的更新代码

# Get random samples from latent space
z = GAN.random_samples((batch_size, self.latent_dim))

# Augment random samples with the class label (1 for class "fraud") for conditioning
z_conditioned = tf.concat([z, tf.ones((batch_size, 1))], axis=1)
# Generate fake data using random samples
x_fake = self.generator(z_conditioned, training=True)

# Calculate the loss and back-propagate
with tf.GradientTape() as c_tape:
    c_tape.watch(x_fake)
    c_tape.watch(x_real)

    # Get the scores for the fake data
    output_fake = 0.5 * (self.critic(x_fake) + 1.0)
    score_fake = tf.reduce_mean(tf.reduce_sum(output_fake, axis=1))
    # Get the scores for the real data
    output_real = 0.5 * (self.critic(x_real, training=True) + 1.0)
    score_real = tf.reduce_mean((1.0 - 2.0 * y_real) * (output_real[:, 0] - output_real[:, 1]))

# Calculate the gradient penalty
gp = self.gp_coeff * self.gradient_penalty(batch_size, x_real, x_fake)
# Calculate critic's loss (added 1.0 so its ideal value becomes zero)
c_loss = 1.0 + score_fake - score_real + gp
# Calculate the gradients
c_grads = c_tape.gradient(c_loss, self.critic.trainable_weights)
# back-propagate the loss
self.c_optimizer.apply_gradients(zip(c_grads, self.critic.trainable_weights))
Run Code Online (Sandbox Code Playgroud)

另请注意:正如您所看到的,我没有使用任何交叉熵或其他有被零除风险的自编写函数。

Ami*_*mir 6

因此,在对互联网进行更多研究之后,事实证明这是因为tf.norm(以及其他一些函数)的数值不稳定。

就函数而言norm,问题在于计算其梯度时,其值出现在分母中。所以d(norm(x))/dxatx = 0会变成(这就是我一直在寻找的0 / 0神秘!)division-by-zero

问题在于,计算图有时会出现诸如a / a数值a = 0未定义但存在极限之类的情况。由于张量流的工作方式(使用链式法则计算梯度),它会产生nans 或+/-Infs。

最好的方法可能是让张量流检测这些模式并将其替换为经过分析简化的等效模式。但在他们这样做之前,我们还有另一种方法,那就是使用名为的东西tf.custom_gradient来用我们的自定义渐变定义我们的自定义函数(他们的 github 上的相关问题

尽管就我而言,实际上有一个更简单的解决方案(尽管当我不知道它是tf.norm罪魁祸首时,它并不简单):

所以而不是:

tf.norm(x)
Run Code Online (Sandbox Code Playgroud)

您可以使用:

tf.sqrt(tf.reduce_sum(tf.square(x)) + 1.0e-12)
Run Code Online (Sandbox Code Playgroud)

注意:请注意维度(如果 x 是矩阵或张量,并且需要计算行或列范数)!这只是演示该概念的示例代码

希望它可以帮助某人