多任务学习关键在共享结构设计、损失组合与梯度协调;须用函数式api显式分离共享骨干和任务头,损失不可简单相加,需动态加权(如gradnorm),并独立监控各任务指标。

多任务学习不是“加几个输出层”就能跑通的事,关键在共享结构设计、损失组合方式和梯度协调机制。直接套用单任务模型加 tf.keras.layers.Dense 多头输出,大概率训练崩溃或某个任务完全不收敛。
共享骨干 + 任务头必须显式分离
TensorFlow 的函数式 API 是唯一推荐路径;顺序模型(tf.keras.Sequential)无法表达分支结构。共享部分必须封装为独立 Model 或 Layer,不能靠重复调用同一组层实现——否则反向传播时梯度会错乱叠加。
- 错误写法:
x = Conv2D(...)(inputs); x = Conv2D(...)(x)然后两次接不同头 → 实际创建了两套权重 - 正确写法:用
build_shared_backbone()返回一个可复用的Model,再分别传给各任务头函数 - 注意输入 shape 一致性:分类头常用
GlobalAveragePooling2D,检测/分割头必须保留空间维度,直接接Conv2D
损失函数不能简单相加
loss = loss1 + loss2 是最常见陷阱。当两个任务量纲、梯度幅值差异大(比如 MSE 回归 vs sparse_categorical_crossentropy 分类),强任务会主导更新,弱任务梯度被淹没。
- 必须引入可学习或手动调节的权重:如
total_loss = 0.7 * loss_cls + 0.3 * loss_reg - 更鲁棒的做法是用 GradNorm 或 Uncertainty Weighting:通过
tf.Variable学习每个任务的 logσ²,动态调整损失权重 - 验证时务必分别打印
loss_cls和loss_reg,不能只看总 loss 下降
样本权重在多输出场景下极易出 shape 错误
当使用 sample_weight 时,Keras 要求它与对应输出张量的 batch 维度和时间/空间维度对齐,但**不校验通道数**。你遇到的 InvalidArgumentError: cannot squeeze dim [2], expected a dimension of 1 就是因为传入了 shape (1, 14, 5) 的权重,而 Keras 默认按最后一维为类别数处理。
- 解决方法一:对每个输出单独指定
sample_weight_mode,如model.compile(..., sample_weight_mode={'cls_output': 'temporal', 'reg_output': None}) - 解决方法二:不用全局
sample_weight,改用自定义 loss 函数,在内部做加权计算,明确控制广播行为 - 切记:如果某任务输出是 one-hot(shape
(N, C)),其sample_weight必须是(N,)或(N, 1);如果是序列标注((N, T, C)),则需(N, T)
真正难的从来不是搭出多输出结构,而是让两个任务在训练中“和平共处”——它们的梯度方向可能天然冲突,共享层更新一次,一个任务受益,另一个受损。没有通用解法,只能靠 loss 权重调参、梯度裁剪、甚至分阶段冻结共享层来试探。别跳过验证集上各任务指标的独立监控,那是唯一能告诉你模型是否真在“多任务”而非“单任务主导”的信号。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











