u-net在tensorflow中语义分割的核心是输入/输出形状匹配、损失函数精准选择、数据增强不破坏mask对齐;输入需为(batch,h,w,3)且h/w被16整除,mask须为uint8单通道并从0连续编号,损失函数须与激活函数严格配对(二类用sigmoid+binarycrossentropy,多类用softmax+sparsecategoricalcrossentropy)。

U-Net 在 TensorFlow 中做图像语义分割,核心不是“能不能跑通”,而是“输入/输出形状对不对、损失函数选不选得准、数据增强会不会破坏 mask 对齐”——这三个点错一个,训练就全白搭。
U-Net 输入张量必须是 (batch, height, width, 3),且 height 和 width 需被 16 整除
U-Net 的下采样路径含 4 次 MaxPooling2D(默认 pool_size=2),总共降维 $2^4 = 16$ 倍。若原始图像是 512×512,没问题;但如果是 500×500 或 513×513,最后一层特征图尺寸会变成非整数(如 31.25),TensorFlow 会直接报错 InvalidArgumentError: Negative dimension size caused by subtracting 2 from 1。
- 预处理时用
tf.image.resize将图像和 mask 同步缩放到最接近的 16 倍数(如512或384),别用cv2.resize单独处理再拼 numpy —— 容易错位 - 若必须保留原始分辨率,可在编码器末尾加
tf.keras.layers.ZeroPadding2D补零,但解码器对应位置要Cropping2D剪掉,否则预测图边缘全是无效值 - 验证方法:打印模型中间层输出 shape,确认所有
Conv2D后的MaxPooling2D输出 shape 的h和w均为整数
mask 必须是 uint8 单通道,且类别值从 0 开始连续编号
TensorFlow 的 tf.keras.losses.SparseCategoricalCrossentropy(推荐用于多类分割)要求真值 mask 是整型,每个像素值代表类别 ID。若你用 Photoshop 手动标注,导出成 RGB 图,再用 np.argmax(mask, axis=-1) 转换,极易混入 255 或 -1 这类非法类别,训练时 loss 突然爆到 nan。
- 加载 mask 后立刻执行:
mask = np.clip(mask, 0, num_classes - 1).astype(np.uint8) - 用
np.unique(mask)检查实际出现的值,确保是[0, 1, 2]而非[0, 1, 255] - 若用
CategoricalCrossentropy,需先tf.one_hot(mask, depth=num_classes),但显存翻倍,小显卡慎用
tf.keras.metrics.IoU 默认不支持 batch 维度外的动态 shape,训练时 metric 会静默失效
你在 model.compile() 里写了 metrics=[tf.keras.metrics.IoU(num_classes=4, name='iou')],但训练日志里 iou 始终是 0.0000,loss 却在降——大概率是 mask 的 batch 内 shape 不一致(比如用了 pad_to_batch 但没对 mask 同步 pad),导致 IoU 内部的 confusion_matrix 计算失败,返回全零。
- 统一用
tf.keras.utils.pad_sequences处理图像和 mask,或改用自定义 metric:
def iou_metric(y_true, y_pred):
y_true = tf.cast(y_true, tf.int32)
y_pred = tf.cast(tf.argmax(y_pred, axis=-1), tf.int32)
return tf.keras.metrics.sparse_categorical_accuracy(y_true, y_pred) # 先用 acc 快速验证 pipeline
- 真正要用 IoU,优先上
tfa.metrics.IoU(TensorFlow Addons),它对变长 batch 更鲁棒
训练时 loss 下降但预测图全是单色块,八成是 sigmoid / softmax 激活和损失函数没配对
二类分割(前景/背景)用 sigmoid + BinaryCrossentropy;多类分割(≥3 类)必须用 softmax + SparseCategoricalCrossentropy。混用会导致 logits 被错误归一化,模型学不会区分相似纹理。
- 检查最后一层:
Conv2D(filters=num_classes, kernel_size=1, activation='softmax')(多类)或activation='sigmoid'(二类) -
sigmoid输出范围是[0,1],但输出 channel 数必须是1;softmax输出 channel 数必须等于num_classes - 用
model.predict(sample_img[None,...])查看输出值分布:若多类任务输出里每像素最大值都接近1.0,说明 softmax 过早饱和,可尝试在最后加tf.keras.layers.Lambda(lambda x: x / 0.1)缩放 logits
U-Net 结构本身很稳定,真正卡住人的永远是数据和 loss 的隐式耦合——mask 少一个 astype,loss 少一个 from_logits=True,或者 resize 时插值方式在图像和 mask 上不一致,都会让模型在看似正常的 loss 曲线下,默默输出完全不可用的结果。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











