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

Keras多输出模型中分类交叉熵凸组合自定义损失函数问题

嘿,针对你在Keras里实现加权多输出损失时碰到的两个问题,我来一步步拆解解决:

问题1:多输出场景下是否需要多次传入my_loss?

不需要。你的需求是把两个输出的分类交叉熵按λ*L1 + (1-λ)*L2合并,这里有两种更合理的实现思路,比你之前的写法更规范:

思路一:用Keras内置的loss_weights直接实现(推荐)

这种方法不需要写自定义损失,利用Keras原生的多输出损失加权功能,简洁又好调试:

假设你的模型有两个输出:第一个是二分类任务(用binary_crossentropy),第二个是多分类任务(用categorical_crossentropy),代码示例如下:

from keras import losses, metrics, optimizers
from keras.models import Model
from keras.layers import Input, Dense

# 先搭建一个示例多输出模型
input_layer = Input(shape=(10,))
# 输出1:二分类,用sigmoid激活
output1 = Dense(1, activation='sigmoid', name='output1')(input_layer)
# 输出2:多分类(比如3类),用softmax激活
output2 = Dense(3, activation='softmax', name='output2')(input_layer)
model = Model(inputs=input_layer, outputs=[output1, output2])

# 设置你的λ值
lambda_val = 0.8

# 编译模型时指定每个输出的损失+权重
model.compile(
    optimizer=optimizers.Adam(),
    # 给每个输出指定对应的损失函数
    loss={'output1': losses.binary_crossentropy, 'output2': losses.categorical_crossentropy},
    # 给两个损失设置权重,自动计算λ*L1 + (1-λ)*L2
    loss_weights={'output1': lambda_val, 'output2': 1 - lambda_val},
    metrics=[metrics.categorical_accuracy]
)

这种方式的好处是Keras会自动帮你管理两个输出的损失,你还能在训练日志里看到每个输出的单独损失值,方便调试。

思路二:自定义支持传参的损失函数

如果你一定要用自定义损失函数,需要解决两个核心点:

  1. 多输出场景下,y_true和y_pred是列表,每个元素对应一个输出的真实标签/预测结果(不是你之前假设的拼接张量)
  2. 用闭包或类的方式把λ参数传入损失函数(因为Keras要求损失函数只能接收y_true和y_pred两个参数)

用闭包实现(简单场景推荐)

from keras import losses

def get_weighted_loss(lambda_val):
    # 外层函数接收λ,返回真正的损失函数
    def weighted_loss(y_true, y_pred):
        # y_true = [output1的真实标签, output2的真实标签]
        # y_pred = [output1的预测结果, output2的预测结果]
        l1 = losses.binary_crossentropy(y_true[0], y_pred[0])
        l2 = losses.categorical_crossentropy(y_true[1], y_pred[1])
        return lambda_val * l1 + (1 - lambda_val) * l2
    return weighted_loss

# 使用时先传入λ得到具体的损失函数
lambda_val = 0.8
my_loss = get_weighted_loss(lambda_val)

# 编译时只需传一次这个损失函数
model.compile(
    optimizer='Adam',
    loss=my_loss,
    metrics=[metrics.categorical_accuracy]
)

用类实现(复杂损失场景推荐)

如果你的损失逻辑以后可能扩展,用类继承keras.losses.Loss会更灵活:

from keras.losses import Loss

class WeightedCrossentropyLoss(Loss):
    def __init__(self, lambda_val, name='weighted_crossentropy'):
        super().__init__(name=name)
        self.lambda_val = lambda_val  # 保存λ参数

    def call(self, y_true, y_pred):
        # 同样处理列表形式的y_true和y_pred
        l1 = losses.binary_crossentropy(y_true[0], y_pred[0])
        l2 = losses.categorical_crossentropy(y_true[1], y_pred[1])
        return self.lambda_val * l1 + (1 - self.lambda_val) * l2

# 实例化时传入λ
my_loss = WeightedCrossentropyLoss(lambda_val=0.8)
model.compile(optimizer='Adam', loss=my_loss, metrics=[metrics.categorical_accuracy])
问题2:如何把λ参数传入自定义损失?

上面的闭包和类写法就是解决这个问题的标准方案:

  • 闭包通过外层函数接收λ,内层损失函数可以直接引用这个参数
  • 类通过__init__方法接收λ并保存为实例属性,在call方法中使用

另外要纠正你之前的一个小误区:如果你的数据集是把两个输出的标签拼接成了一个张量(比如y_true[:,0]是第一个输出的标签,y_true[:,1:]是第二个),虽然也可以调整自定义损失的写法,但这种方式不符合Keras多输出模型的规范,建议把标签拆分成两个单独的数组,用列表形式传入模型训练,这样代码更清晰。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.07 21:22:39