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

基于TensorFlow.js tfjs-node-gpu 0.2.1的FCN训练异常问询

针对你的FCN训练无进展问题的排查建议

我之前在做类似的单类分割FCN任务时也踩过几乎一样的坑,结合你的代码和描述,给你几个核心的排查和修正方向:

1. 标签格式与损失/输出激活的严重不匹配

你提到是单类分割,这里存在一个关键理解误区:

  • 如果你的掩码是前景(1)和背景(0)的二值图,本质是二分类任务,而非多类分类:
    • 最后一层的激活函数应该用sigmoid,而不是softmax(softmax是为多类互斥场景设计的,单类下它的输出永远为1,完全失去概率区分的意义)
    • 损失函数要改用binaryCrossentropy,而非logLoss或categoricalCrossentropy(categoricalCrossentropy是给独热编码的多类标签用的,单类场景下完全不适用)
  • 如果你强行把单类标签做成[batch, h, w, 1]的独热形式,模型根本没有可学习的区分度,自然只会输出固定的0或1。

2. 网络结构缺失FCN核心:跳连接

你参考的FCN论文核心就是跳连接(Skip Connection)——用浅层的高分辨率特征弥补深层上采样丢失的细节,但你的网络完全没有做这一步:

  • 只靠最后一层conv2dTranspose直接把最底层特征上采样32倍到原图尺寸,梯度很难传递到浅层,模型无法学习到边缘、细节等关键分割特征,最终只能输出全局均值类别的结果。
  • 建议修改结构:把fcn_3_2、fcn_5_2、fcn_7_2这些浅层特征,经过1x1卷积调整通道数后,和对应上采样阶段的特征做拼接或相加,再继续上采样。

3. 批归一化的错误使用

你定义了batchNorm_0但完全没有用到,而且正确的批归一化用法应该是卷积层之后、激活函数之前:

const fcn_1_0 = tf.layers.conv2d({
  kernelSize: [3, 3], strides: [1, 1], padding: 'same', filters: 64
}).apply(input);
const fcn_1_bn = tf.layers.batchNormalization().apply(fcn_1_0);
const fcn_1_relu = tf.layers.activation({activation: 'relu'}).apply(fcn_1_bn);

直接在输入上加BN意义不大,反而可能破坏输入数据的原始分布。

4. 训练参数的合理性调整

  • 你的学习率0.00001可能过小,虽然你调整过,但可以尝试从0.0001开始逐步降低,观察损失曲线的变化;
  • 优化器可以试试带动量的SGD,有时候Adam在分割任务里的稳定性不如SGD;
  • 检查batch size是否过小,太小的batch会导致BN的统计量不准,梯度波动剧烈,模型难以收敛。

5. 输入与输出的形状匹配校验

确认你的输入张量形状[batch, height, width, 1]和最后输出的形状完全一致:

  • 你做了5次2x下采样(总共32倍),conv2dTranspose的strides=[32,32]是对的,但要确保输入的height/width是32的倍数,否则上采样后尺寸不匹配会导致损失计算错误,模型根本无法学习。

建议先从标签格式、激活函数、损失函数的匹配开始修改,这是最容易快速见效的点,之后再补全跳连接,应该能解决输出固定类别的问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.11 08:53:21