解决使用keras提供的损失函数API时,梯度无法反向传播, 损失函数不下降

部署运行你感兴趣的模型镜像

在使用keras提供的损失函数API时,梯度无法反向传播, 损失函数不下降

问题:

在使用keras提供的损失函数API时,梯度无法反向传播

代码:

from tensorflow.keras.losses import categorical_crossentropy

def train_generator(x, y, z, eps, dcgan, siamese_model, loss=None):

    with tf.GradientTape(persistent=True) as t:
        fake_x = dcgan.generator([z, y])
        loss_G = -tf.reduce_mean(dcgan.discriminator(fake_x)) 
        preds = aux_model(fake_x)
        aux_mean = categorical_crossentropy(y, preds)
        aux_loss = tf.reduce_mean(aux_mean)
        total_loss  = aux_loss + loss_G
        gradient_g = t.gradient(total_loss, dcgan.generator.trainable_variables)

    dcgan.optimizer_G.apply_gradients(zip(gradient_g, dcgan.generator.trainable_variables))

猜测原因:

Keras接口有时候会先对数据进行预处理,然后再调用tensorflow的backend,这样会导致函数梯度链断开,无法通过链式求导来进行梯度下降

查看keras源码是怎么定义categorical_crossentropy的:

root@Ie1c58c4ee0020126c:~# find / -iname keras
/usr/local/lib/python3.7/dist-packages/keras
/usr/local/lib/python3.7/dist-packages/tensorflow_core/contrib/keras
/usr/local/lib/python3.7/dist-packages/tensorflow_core/contrib/keras/api/keras
/usr/local/lib/python3.7/dist-packages/tensorflow_core/python/keras
/usr/local/lib/python3.7/dist-packages/tensorflow_core/python/keras/api/_v1/keras
/usr/local/lib/python3.7/dist-packages/tensorflow_core/python/keras/api/_v2/keras
/usr/local/lib/python3.7/dist-packages/tensorflow_core/python/keras/api/keras

vim /usr/local/lib/python3.7/dist-packages/tensorflow_core/python/keras/losses.py

losses.py

def categorical_crossentropy(y_true,
                             y_pred,
                             from_logits=False,
                             label_smoothing=0):
  """Computes the categorical crossentropy loss.

  Args:
    y_true: tensor of true targets.
    y_pred: tensor of predicted targets.
    from_logits: Whether `y_pred` is expected to be a logits tensor. By default,
      we assume that `y_pred` encodes a probability distribution.
    label_smoothing: Float in [0, 1]. If > `0` then smooth the labels.

  Returns:
    Categorical crossentropy loss value.
  """
  y_pred = ops.convert_to_tensor(y_pred)
  y_true = math_ops.cast(y_true, y_pred.dtype)
  label_smoothing = ops.convert_to_tensor(label_smoothing, dtype=K.floatx())
  def _smooth_labels():
    num_classes = math_ops.cast(array_ops.shape(y_true)[1], y_pred.dtype)
    return y_true * (1.0 - label_smoothing) + (label_smoothing / num_classes)

  y_true = smart_cond.smart_cond(label_smoothing,
                                 _smooth_labels, lambda: y_true)
  return K.categorical_crossentropy(y_true, y_pred, from_logits=from_logits)

可以看到在keras的接口里面对数据做了一些预处理然后再调用了tensorflow backend(K)的categorical_crossentropy接口,所以导致了梯度链的断裂,无法通过链式求导和反向传播了更新梯度

解决方法:

自己实现损失函数,或者取keras的losses.py文件中找到源码,直接调用tensorflow backend提供的接口作为损失函数。

最终将代码改为:

from tensorflow.keras import backend as K

def train_generator(x, y, z, eps, dcgan, siamese_model, loss=None):

    with tf.GradientTape(persistent=True) as t:
        fake_x = dcgan.generator([z, y])
        loss_G = -tf.reduce_mean(dcgan.discriminator(fake_x)) 
        preds = aux_model(fake_x)
        aux_mean = K.categorical_crossentropy(y, preds)
        aux_loss = tf.reduce_mean(aux_mean)
        total_loss  = aux_loss + loss_G
        gradient_g = t.gradient(total_loss, dcgan.generator.trainable_variables)

    dcgan.optimizer_G.apply_gradients(zip(gradient_g, dcgan.generator.trainable_variables))

问题完美解决, 相同问题的可以参考下。

您可能感兴趣的与本文相关的镜像

TensorFlow-v2.15

TensorFlow-v2.15

TensorFlow

TensorFlow 是由Google Brain 团队开发的开源机器学习框架,广泛应用于深度学习研究和生产环境。 它提供了一个灵活的平台,用于构建和训练各种机器学习模型

### 原理 - **反向传播**:基于链式法则,从输出层开始,逆向计算损失函数关于每个参数的梯度。在神经网络中,损失函数衡量了模型预测值与真实值之间的差异。通过反向传播,能够得知每个参数对损失函数的影响程度,为后续的参数更新提供依据。 - **梯度下降**:是一种优化算法,其核心思想是沿着损失函数的负梯度方向更新参数,使得损失函数的值断减小,最终达到局部或全局最优解。梯度表示了函数在某一点上升最快的方向,那么负梯度方向就是下降最快的方向。 ### 训练过程 1. **前向传播**:输入数据在神经网络中从输入层依次经过各个隐藏层,最终到达输出层,得到模型的预测输出。然后根据预测输出和真实标签计算损失函数的值,用于衡量模型的预测效果。 2. **反向传播**:从输出层开始,利用链式法则逐层计算损失函数关于每个参数的梯度。这些梯度反映了参数的微小变化对损失函数的影响程度。 3. **参数更新**:使用梯度下降算法,根据计算得到的梯度更新神经网络中的参数。断重复前向传播、反向传播和参数更新的过程,直到损失函数收敛或达到预设的训练轮数。 ### 参数更新方式 梯度下降有多种更新方式,常见的有: - **批量梯度下降(Batch Gradient Descent)**:在每一次迭代中,使用整个训练数据集来计算梯度并更新参数。这种方式的优点是能够保证收敛到全局最优解,但计算成本较高,尤其是当数据集较大。 ```python import numpy as np # 定义损失函数梯度计算函数 def gradient(X, y, theta): m = len(y) return (1/m) * X.T.dot(X.dot(theta) - y) # 批量梯度下降函数 def batch_gradient_descent(X, y, theta, alpha, num_iters): m = len(y) for iter in range(num_iters): theta = theta - alpha * gradient(X, y, theta) return theta ``` - **随机梯度下降(Stochastic Gradient Descent)**:在每一次迭代中,随机选择一个样本计算梯度并更新参数。这种方式的优点是计算速度快,能够更快地跳出局部最优解,但收敛过程可能会比较稳定。 ```python import numpy as np # 随机梯度下降函数 def stochastic_gradient_descent(X, y, theta, alpha, num_iters): m = len(y) for iter in range(num_iters): for i in range(m): random_index = np.random.randint(m) xi = X[random_index:random_index+1] yi = y[random_index:random_index+1] theta = theta - alpha * gradient(xi, yi, theta) return theta ``` - **小批量梯度下降(Mini-Batch Gradient Descent)**:结合了批量梯度下降和随机梯度下降的优点,在每一次迭代中,使用一小部分样本(称为一个小批量)来计算梯度并更新参数。 ```python import numpy as np # 小批量梯度下降函数 def mini_batch_gradient_descent(X, y, theta, alpha, num_iters, batch_size): m = len(y) for iter in range(num_iters): for i in range(0, m, batch_size): xi = X[i:i+batch_size] yi = y[i:i+batch_size] theta = theta - alpha * gradient(xi, yi, theta) return theta ``` ### 学习率的作用 学习率控制着参数更新的步长。如果学习率设置过大,参数更新的步长就会过大,可能导致模型跳过最优解,无法收敛;如果学习率设置过小,参数更新的步长就会过小,模型的收敛速度会非常缓慢,训练间会很长 [^2]。 ### 学习率的调整策略 - **固定学习率**:在整个训练过程中,学习率保持变。这种方法简单,但可能无法适应同阶段的训练需求 [^2]。 - **学习率衰减**:随着训练的进行,逐渐降低学习率。例如,可以在每训练一定的轮数后,将学习率乘以一个小于 1 的常数。这样在训练初期,学习率较大,模型可以快速收敛;在训练后期,学习率较小,模型可以更精确地收敛到最优解 [^2]。 ```python # 学习率衰减示例 initial_learning_rate = 0.1 decay_rate = 0.9 decay_steps = 100 step = 0 for epoch in range(num_epochs): learning_rate = initial_learning_rate * (decay_rate ** (step // decay_steps)) step += 1 # 使用 learning_rate 进行参数更新 ``` - **自适应学习率**:根据模型的训练情况自动调整学习率。例如,Adagrad、Adadelta、Adam 等优化算法都可以自适应地调整学习率,它们会根据每个参数的梯度情况,动态地调整每个参数的学习率 [^2]。 ```python import tensorflow as tf # 使用 Adam 优化器,它会自适应调整学习率 optimizer = tf.keras.optimizers.Adam(learning_rate=0.001) ```
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值