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

TensorFlow/Keras多分类模型训练报错:无法计算梯度的解决咨询

问题:多分支NLP分类模型训练时梯度无法计算的解决方法

模型结构代码

Input = keras.layers.Input((1368, ))
embedding = keras.layers.Embedding(1386, 50)(Input)

# 卷积分支
convent_1 = keras.layers.Conv1D(128, 10, activation = "relu")(embedding)
convent_2 = keras.layers.Conv1D(64, 7, activation = "relu")(convent_1)
convent_3 = keras.layers.Conv1D(32, 5, activation = "relu")(convent_2)
maxpool_1 = keras.layers.MaxPool1D(2)(convent_3)
maxpool_2 = keras.layers.MaxPool1D(2)(maxpool_1)
flatten_1 = keras.layers.Flatten()(maxpool_2)
reducing_size_dense = keras.layers.Dense(128, activation = "relu")(flatten_1)

# RNN分支
rnn_1 = keras.layers.GRU(128, activation = "relu", return_sequences = True)(embedding)
rnn_2 = keras.layers.GRU(128, activation = "relu", return_sequences = False)(rnn_1)
flatten_2 = keras.layers.Flatten()(rnn_2)

# 分类器
sum_flatten = keras.layers.Add()([reducing_size_dense, flatten_2])
dense_1 = keras.layers.Dense(128, activation = "relu")(sum_flatten)
dense_2 = keras.layers.Dense(128, activation = "relu")(dense_1)
dense_3 = keras.layers.Dense(128, activation = "relu")(dense_2)
dense_4 = keras.layers.Dense(128, activation = "relu")(dense_3)
output_layer = keras.layers.Dense(6, activation = "softmax")(dense_4)

# 自定义Argmax层
class Argmax_layer(keras.layers.Layer):
    def call(self, input):
        return (tf.argmax(input, axis = -1) + 1)

argmax_layer = Argmax_layer()(output_layer)
model = keras.Model(Input, argmax_layer)
keras.utils.plot_model(model, show_shapes=True)

训练代码

callback_list = [keras.callbacks.EarlyStopping(monitor = "val_accuracy", patience = 10),
                 keras.callbacks.ModelCheckpoint(monitor = "val_accuracy", save_best_only = True, filepath = "scoring_model.keras"),
                 keras.callbacks.ReduceLROnPlateau(monitor = "val_accuracy", patience = 5, factor = 0.25)]

model.compile(optimizer = "rmsprop", loss = kappa_weighted_quad_loss, metrics = ["accuracy"])
history_model = model.fit(training_data, training_targets, validation_data = (validation_data, validation_targets), epochs = 100, callbacks = callback_list, batch_size = 20)
plot = dm.plotter(history = history_model.history)
plot.plot_loss()
plot.plot_acc()

自定义加权Kappa损失函数

def kappa_weighted_quad_loss(targets, preds):
    targets = targets - 1
    preds = preds - 1

    targets = tf.cast(targets, tf.int16)
    preds = tf.cast(preds, tf.int16)

    confusion_matrix = np.zeros(shape = (6, 6))
    for i, j in zip(targets, preds):
        confusion_matrix[i, j] += 1

    w_i_j = lambda i, j: (i - j)**2/25
    O_i_dot = lambda i: np.sum(confusion_matrix[i, :])
    O_dot_j = lambda j: np.sum(confusion_matrix[:, j])
    E_i_j = lambda i, j: np.outer(O_i_dot(i) , O_dot_j(j)) / np.sum(confusion_matrix)

    numerator = np.sum([w_i_j(i, j) * confusion_matrix[i, j] for i in range(6) for j in range(6)])
    denominator = np.sum([w_i_j(i, j) * E_i_j(i , j) for i in range(6) for j in range(6)])
    return 1 - numerator/denominator

错误信息

---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
Cell In[41], line 6
      1 callback_list = [keras.callbacks.EarlyStopping(monitor = "val_accuracy", patience = 10),
      2                  keras.callbacks.ModelCheckpoint(monitor = "val_accuracy", save_best_only = True, filepath = "scoring_model.keras"),
      3                  keras.callbacks.ReduceLROnPlateau(monitor = "val_accuracy", patience = 5, factor = 0.25)]
      5 model.compile(optimizer = "rmsprop", loss = kappa_weighted_quad_loss, metrics = ["accuracy"])
----> 6 history_model = model.fit(training_data, training_targets, validation_data = (validation_data, validation_targets), epochs = 100, callbacks = callback_list, batch_size = 20)
      7 plot = dm.plotter(history = history_model.history)
      8 plot.plot_loss()

File /opt/conda/lib/python3.10/site-packages/keras/src/utils/traceback_utils.py:122, in filter_traceback.<locals>.error_handler(*args, **kwargs)
    119     filtered_tb = _process_traceback_frames(e.__traceback__)
    120     # To get the full stack trace, call:
    121     # `keras.config.disable_traceback_filtering()`
--> 122     raise e.with_traceback(filtered_tb) from None
    123 finally:
    124     del filtered_tb

File /opt/conda/lib/python3.10/site-packages/keras/src/optimizers/base_optimizer.py:662, in BaseOptimizer._filter_empty_gradients(self, grads, vars)
    659         missing_grad_vars.append(v.name)
    661 if not filtered_grads:
--> 662     raise ValueError("No gradients provided for any variable.")
    663 if missing_grad_vars:
    664     warnings.warn(
    665         "Gradients do not exist for variables "
    666         f"{list(reversed(missing_grad_vars))} when minimizing the loss."
    667         " If using `model.compile()`, did you forget to provide a "
    668         "`loss` argument?"
    669     )

ValueError: No gradients provided for any variable.

问题分析

你猜测的方向完全正确,导致梯度无法计算有两个核心原因:

  1. Argmax层的不可导性:tf.argmax是离散操作,没有连续梯度,一旦模型输出是Argmax的结果,梯度会在这一层彻底中断,前面所有可训练层都无法更新。
  2. 损失函数的不可导设计:原损失函数混用了Numpy数组操作、显式循环,还将预测值转为整数类型,这些操作都会脱离TensorFlow的计算图,导致无法追踪梯度。

解决方法

1. 移除模型中的Argmax层

训练阶段模型必须输出softmax的概率分布(连续值),Argmax只在推理阶段用来生成最终分类结果:

# 移除Argmax层,直接用softmax输出作为模型输出
model = keras.Model(Input, output_layer)
keras.utils.plot_model(model, show_shapes=True)

# 推理时单独处理分类结果
def get_predicted_classes(model, data):
    probs = model.predict(data)
    return tf.argmax(probs, axis=-1) + 1  # 转回1-6的类别编号

2. 重写可导的加权Kappa损失函数

用TensorFlow原生张量操作实现,避免任何脱离计算图的操作:

import tensorflow as tf
from tensorflow import keras

def weighted_kappa_loss(y_true, y_pred):
    num_classes = 6
    # 将标签转为0-5的浮点型
    y_true = tf.cast(y_true - 1, tf.float32)
    # 生成加权矩阵
    weight_matrix = tf.square(tf.expand_dims(tf.range(num_classes), 0) - tf.expand_dims(tf.range(num_classes), 1))
    weight_matrix = weight_matrix / tf.square(num_classes - 1)

    # 将真实标签转为one-hot格式
    y_true_onehot = tf.one_hot(tf.cast(y_true, tf.int32), num_classes)
    # 计算真实分布和预测分布的边缘和
    true_sum = tf.reduce_sum(y_true_onehot, axis=0)
    pred_sum = tf.reduce_sum(y_pred, axis=0)
    total_samples = tf.reduce_sum(true_sum)

    # 计算期望混淆矩阵
    expected_matrix = tf.matmul(tf.expand_dims(true_sum, 1), tf.expand_dims(pred_sum, 0)) / total_samples
    # 计算观测混淆矩阵(用概率加权,避免硬分类)
    observed_matrix = tf.matmul(tf.transpose(y_true_onehot), y_pred)

    # 计算加权Kappa并返回损失(1-Kappa,因为要最小化损失)
    numerator = tf.reduce_sum(weight_matrix * observed_matrix)
    denominator = tf.reduce_sum(weight_matrix * expected_matrix)
    return 1 - numerator / denominator

3. 调整训练配置

因为标签是1-6的整数,改用SparseCategoricalAccuracy作为评估指标更合适:

callback_list = [
    keras.callbacks.EarlyStopping(monitor="val_sparse_categorical_accuracy", patience=10),
    keras.callbacks.ModelCheckpoint(monitor="val_sparse_categorical_accuracy", save_best_only=True, filepath="scoring_model.keras"),
    keras.callbacks.ReduceLROnPlateau(monitor="val_sparse_categorical_accuracy", patience=5, factor=0.25)
]

model.compile(
    optimizer="rmsprop",
    loss=weighted_kappa_loss,
    metrics=[keras.metrics.SparseCategoricalAccuracy(name="sparse_categorical_accuracy")]
)

# 正常训练
history_model = model.fit(
    training_data, training_targets,
    validation_data=(validation_data, validation_targets),
    epochs=100,
    callbacks=callback_list,
    batch_size=20
)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 17:45:54