You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用自定义fairnessLoss时出现No gradients provided报错

批量梯度下降结合自定义公平性损失函数训练TensorFlow模型时梯度缺失问题

问题现象

使用批量梯度下降(batch gradient descent)结合自定义fairnessLoss函数训练模型时,第一个epoch后出现以下报错:

optimizer.apply_gradients(zip(grads, model_2.trainable_weights))

No gradients provided for any variable: (['dense_22/kernel:0', 'dense_22/bias:0', 'dense_23/kernel:0', 'dense_23/bias:0', 'dense_24/kernel:0', 'dense_24/bias:0'],).

但使用BinaryCrossEntropy损失函数时代码可正常运行。

问题根源

自定义损失函数fairnessLoss存在两个破坏TensorFlow自动微分机制的问题:

  1. 使用Python原生for循环遍历样本,无法被TensorFlow的计算图追踪,导致梯度无法反向传播。
  2. 使用tf.Variable存储中间计算结果(如cse_min、n_min等),这类变量的assign_add操作不属于计算图的正向传播流程,梯度无法关联到模型参数。

TensorFlow的自动微分依赖于可追踪的张量运算,必须避免Python原生循环和独立变量的赋值操作,改用TensorFlow的向量化运算实现逻辑。

修复后的完整代码

# 导入必要库
import numpy as np
import tensorflow as tf
from tensorflow import keras
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import MinMaxScaler
import matplotlib.pyplot as plt

lambda_par = tf.constant(0.5)

def fairnessLoss(y_true, y_pred):
    # 分离少数类(y_true[i][0]==1)和多数类样本
    mask_min = tf.equal(y_true[:, 0], 1.0)
    mask_maj = tf.logical_not(mask_min)
    
    # 计算少数类的交叉熵和样本数
    cse_min = tf.reduce_sum(tf.math.log(y_pred[mask_min]))
    n_min = tf.cast(tf.reduce_sum(tf.cast(mask_min, tf.float32)), tf.float32)
    
    # 计算多数类的交叉熵和样本数
    cse_maj = tf.reduce_sum(tf.math.log(1 - y_pred[mask_maj]))
    n_maj = tf.cast(tf.reduce_sum(tf.cast(mask_maj, tf.float32)), tf.float32)
    
    # 计算公平性误差项
    tem1 = tf.divide(cse_min, n_min)
    tem2 = tf.divide(cse_maj, n_maj)
    fe = tf.square(tem1 - tem2)
    
    # 总损失:交叉熵总和 + lambda*公平性误差
    total_loss = cse_min + cse_maj + lambda_par * fe
    return total_loss

# 定义模型
model = tf.keras.Sequential([
    tf.keras.layers.Dense(8, activation='sigmoid'),
    tf.keras.layers.Dense(8, activation='sigmoid'),
    tf.keras.layers.Dense(1, activation='sigmoid')
])

# 假设train_X和train_y已提前定义并预处理
batch_size = len(train_X)
train_yy = tf.expand_dims(train_y, axis=1)  # 替换手动列表转换为TensorFlow原生操作
train_dataset = tf.data.Dataset.from_tensor_slices((train_X, train_yy))
train_dataset = train_dataset.shuffle(buffer_size=1024).batch(batch_size)

# 初始化评估指标
train_acc_metric = keras.metrics.BinaryAccuracy()

epochs = 500
optimizer = keras.optimizers.Adam()
losses = []
accuracy = []

for epoch in range(epochs):
    print(f"\nStart of epoch {epoch}")
    epoch_loss_avg = tf.keras.metrics.Mean()

    for step, (x_batch_train, y_batch_train) in enumerate(train_dataset):
        with tf.GradientTape() as tape:
            logits = model(x_batch_train, training=True)
            loss_value = fairnessLoss(y_batch_train, logits)

        # 计算梯度并更新参数
        grads = tape.gradient(loss_value, model.trainable_weights)
        optimizer.apply_gradients(zip(grads, model.trainable_weights))

        # 更新指标
        epoch_loss_avg.update_state(loss_value)
        train_acc_metric.update_state(y_batch_train, logits)

        losses.append(epoch_loss_avg.result().numpy())
        accuracy.append(train_acc_metric.result().numpy())

        if step % 200 == 0:
            print(f"Training loss (for one batch) at step {step}: {float(loss_value):.4f}")
            print(f"Seen so far: {(step + 1) * batch_size} samples")
    
    print(f"Training accuracy: {train_acc_metric.result().numpy():.4f}")
    train_acc_metric.reset_states()

关键修复点

  • 替换Python循环为TensorFlow向量化操作:使用tf.equal、tf.logical_not生成掩码,通过张量索引直接筛选少数类和多数类样本,避免循环破坏计算图追踪。
  • 移除tf.Variable中间变量:全部使用tf.reduce_sum等张量运算直接计算结果,确保所有操作都在计算图中,梯度可正常反向传播。
  • 简化标签转换:用tf.expand_dims替代手动列表转换,保持张量操作一致性。
  • 将lambda_par改为tf.constant:若无需动态调整该参数,使用常量更符合计算图要求;若需动态调整,可改为可训练变量并添加到模型的可训练权重中。

内容的提问来源于stack exchange,提问作者KESHAV SINGHAL

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.16 21:10:37