不能将梯度组合用于多输出 keras 模型的多个损失函数

问题描述

我正在使用 CNN 和 EHR 数据集在 Keras 中进行时间序列预测。目标是预测给予患者什么分子以及距离下一次患者就诊的时间。我必须实现基于 this paper 的双目标梯度下降。要实现的算法在这里(第 7 页结束,第 8 页开始):

algorithm at end of page 7

algorithm at the beginning of page 8

我选择的模型是这个:

model to train

以长度为 3 的时间序列作为输入(对应客户连续访问 3 次) 和 2 个输出

  • atc 代码(要预测的分子代码
  • 等待下一次访问的时间(按月份分类:0、1、2、3、4 表示 >=4)

两个输出都使用 SparseCategoricalCoRSSentropy 损失函数

当我开始执行第一个操作时:gs - gl 我有这个错误

errir

我的渐变中的某些值在 None 处,我不知道为什么。我的优化器定义如下:optimizer=tf.Keras.optimizers.Adam(learning_rate=1e-3 编译我的模型时。

此外,当我尝试对梯度进行一些操作以查看其工作原理时,我遇到了另一个问题:只考虑了一个输入,这在以后会带来问题,因为我必须分别考虑每个损失函数

使用此代码我有以下输出消息:WARNING:tensorflow:Gradients do not exist for variables ['outputWaitTime/kernel:0','outputWaitTime/bias:0'] when minimizing the loss.

EPOCHS = 1

for epoch in range(EPOCHS):
    with tf.GradientTape() as ATCTape,tf.GradientTape() as WTTape:
        predictions = model(xTrain,training=False)
        ATCLoss = loss(yTrain[:,:,0],predictions[ATC_CODE])
        WTLoss = loss(yTrain[:,1],predictions[WAIT_TIME])

    ATCGrads = ATCTape.gradient(ATCLoss,model.trainable_variables)
    WTGrads  = WTTape.gradient(WTLoss,model.trainable_variables)
    grads = ATCGrads + WTGrads

    model.optimizer.apply_gradients(zip(grads,model.trainable_variables))

有了这个代码,没问题,但是两个损失合并为一个,而我需要分别考虑两个损失

EPOCHS = 1

for epoch in range(EPOCHS):
    with tf.GradientTape() as tape:
        predictions = model(xTrain,predictions[WAIT_TIME])
        lossValue = ATCLoss + WTLoss

    grads = tape.gradient(lossValue,model.trainable_variables)

    model.optimizer.apply_gradients(zip(grads,model.trainable_variables))

我需要帮助来理解为什么我会遇到所有这些问题。

包含所有代码的笔记本在这里https://colab.research.google.com/drive/1b6UorAAEddNKFQCxaK1Wsuj09U645KhU?usp=sharing

实现从 Model Creation 部分开始

解决方法

你在NoneATCGrads中得到WTGrads的原因是因为两个梯度对应的损失是不同的输出outputATCoutputWaitTime,如果 输出值不用于计算损失,那么输出将没有梯度,因此您将获得该输出层的 None 梯度。这也是你得到 WARNING:tensorflow:Gradients do not exist for variables ['outputWaitTime/kernel:0','outputWaitTime/bias:0'] when minimizing the loss 的原因,因为你没有这些梯度 wrt 每次损失。如果您将损失合并为一个,则两个输出都用于计算损失,因此没有 WARNING

所以如果你想做一个列表元素的减法,你可以在减法之前先将 None 转换为 0.,你不能使用 tf.math.subtract(gs,gl) 因为它要求所有输入的形状必须匹配,所以:

import tensorflow as tf

gs = [tf.constant([1.,2.]),tf.constant(3.),None]
gl = [tf.constant([3.,4.]),None,tf.constant(4.)]

to_zero = lambda i : 0. if i is None else i
gs = list(map(to_zero,gs))
gl = list(map(to_zero,gl))
sub = [s_i - l_i for s_i,l_i in zip(gs,gl)]
print(sub)

输出:

[<tf.Tensor: shape=(2,),dtype=float32,numpy=array([-2.,-2.],dtype=float32)>,<tf.Tensor: shape=(),numpy=3.0>,numpy=-4.0>]

还要注意 tape.gradient() 将返回 张量(或 IndexedSlices,或 None)的列表或嵌套结构,源中的每个元素一个。返回结构与源结构相同;在 python 中添加两个列表 [1,2] + [3,4] 不会像在 numpy 数组中那样给你 [4,6],相反它会组合两个列表并给你 [1,2,3,4]