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

使用Keras CV微调DeepLabV3Plus语义分割模型的精度与警告问题

问题解决:DeepLabV3Plus微调精度无提升及形状警告修复

问题背景

使用Open Images V7数据集(通过FiftyOne访问),基于KerasCV库以MobileNetV3Small为骨干网络微调DeepLabV3Plus模型时,出现以下问题:

  • 训练过程中训练集精度完全固定不变,验证集精度也无任何提升
  • 训练时弹出多个形状相关警告
  • 提示数据集提前耗尽,中断训练流程

错误分析

从输出日志和代码逻辑来看,核心问题集中在以下几点:

  1. 单通道误用Softmax激活:当num_classes=1时,KerasCV的DeepLabV3Plus默认使用Softmax激活,但单通道的Softmax输出恒为1,完全无法学习有效特征,这是精度不提升的核心原因。
  2. 输入结构不匹配:模型预期输入为命名Keras张量结构,但数据集输出是普通张量,导致框架抛出结构不匹配警告。
  3. 数据集提前耗尽:训练集的batch数量不足,每个epoch仅迭代13次就耗尽数据,且未设置重复生成,影响训练稳定性。
  4. 图像未归一化:输入图像未做0-1范围归一化,而MobileNetV3的预训练权重基于归一化数据训练,特征分布不匹配会严重阻碍模型收敛。
  5. Mask处理隐患:遍历检测框取第一个车牌mask的逻辑可能遗漏无车牌样本,且mask未确保严格二值化(0/1),会干扰损失计算。

修复步骤

  1. 替换激活函数为Sigmoid:二分类分割任务中,单通道输出应使用Sigmoid激活,而非Softmax。
  2. 添加图像归一化:将输入图像除以255,缩放到0-1范围,匹配预训练权重的输入分布。
  3. 修复数据集输入结构:调整模型初始化逻辑,使其兼容普通张量输入,避免结构不匹配警告。
  4. 设置数据集重复:在数据集后添加.repeat(),避免每个epoch提前耗尽数据,同时指定steps_per_epoch控制迭代次数。
  5. 确保Mask二值化:处理mask时将值严格转为0(背景)和1(目标),避免模糊值影响损失计算。
  6. 优化Mask提取逻辑:处理无车牌的样本,避免报错或返回无效mask。

修改后的完整代码

数据集准备代码

import tensorflow as tf
import numpy as np

def preprocess_sample(sample):
    # 图像读取与预处理
    img = tf.io.read_file(sample["filepath"])
    img = tf.image.decode_jpeg(img, channels=3)
    img.set_shape([None, None, 3])
    img = tf.image.resize(img, (512, 512))
    img = img / 255.0  # 必须添加归一化,匹配预训练权重的输入分布
    
    # Mask提取与二值化处理
    mask = np.zeros((img.shape[0], img.shape[1]), dtype=np.float32)
    for detection in sample.ground_truth.detections:
        if detection.label == 'Vehicle registration plate':
            mask = detection.mask.astype(np.float32)
            # 确保Mask严格二值化
            mask = np.where(mask > 0.5, 1.0, 0.0)
            break
    mask = tf.expand_dims(mask, axis=-1)
    mask.set_shape([None, None, 1])
    mask = tf.image.resize(mask, (512, 512), method="nearest")

    return img, mask

# 转换为TF Dataset
tf_train_dataset = tf.data.Dataset.from_generator(
    lambda: (preprocess_sample(s) for s in train_dataset),
    output_signature=(
        tf.TensorSpec(shape=(512, 512, 3), dtype=tf.float32),
        tf.TensorSpec(shape=(512, 512, 1), dtype=tf.float32),
    )
)
tf_val_dataset = tf.data.Dataset.from_generator(
    lambda: (preprocess_sample(s) for s in val_dataset),
    output_signature=(
        tf.TensorSpec(shape=(512, 512, 3), dtype=tf.float32),
        tf.TensorSpec(shape=(512, 512, 1), dtype=tf.float32),
    )
)

# 数据集增强:shuffle、batch、repeat、prefetch
tf_train_dataset = tf_train_dataset.shuffle(32).batch(8).repeat().prefetch(tf.data.AUTOTUNE)
tf_val_dataset = tf_val_dataset.batch(8).repeat().prefetch(tf.data.AUTOTUNE)

微调代码

import keras_cv
import tensorflow as tf

# 初始化模型,直接指定sigmoid激活
model = keras_cv.models.DeepLabV3Plus.from_preset(
    "mobilenet_v3_small",
    input_shape=(512, 512, 3),
    num_classes=1,
    activation="sigmoid"  # 替换默认的softmax,适配二分类任务
)

model.compile(
    optimizer=tf.keras.optimizers.Adam(learning_rate=1e-4),
    loss="binary_crossentropy",
    metrics=["binary_accuracy"]
)

# 训练时指定steps_per_epoch,避免无限迭代
model.fit(
    tf_train_dataset,
    validation_data=tf_val_dataset,
    epochs=5,
    steps_per_epoch=len(train_dataset)//8,  # 根据训练集大小计算每个epoch的迭代次数
    validation_steps=len(val_dataset)//8,
    callbacks=[
        tf.keras.callbacks.EarlyStopping(patience=3, restore_best_weights=True),
    ],
)

关键说明

  • 激活函数修改:直接在from_preset中指定activation="sigmoid",既解决了单通道Softmax的问题,也避免了后续添加层导致的输入结构不匹配。
  • 数据集重复与step控制:添加.repeat()后必须指定steps_per_epoch,否则训练会无限进行,同时保证每个epoch遍历完整的训练集。
  • Mask二值化:严格的0/1值是二元交叉熵损失有效计算的前提,避免模糊值导致模型学习偏差。
  • 图像归一化:预训练模型微调的必要步骤,能让模型快速利用预训练特征,加速收敛。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 00:17:33