☰
Python+Unet语义分割实战:从原理到训练预测的完整工程指南
2026/9/28 15:07:56 网站建设 项目流程

简介:基于Python与U-Net模型的图像语义分割项目,面向初入深度学习及计算机视觉领域的学习者,也适合有基础者作为毕设、课程设计或工程实训的参考,帮助解决像素级目标区域自动划分问题。资源包共24个文件,主要包含4个Python脚本,分别负责数据集生成、模型训练、图像预测及结果合并,另附训练完成的U-Net模型权重(h5)、教学PPT、14张用于演示和测试的PNG图像,以及少量XML/OVR辅助文件,整体压缩包约478.98MB。已有204人学习浏览。通过该资源,学习者可以对照PPT理解U-Net原理与训练流程,直接运行脚本从准备数据、训练模型到输出分割结果完整走一遍;也可调用已训练好的权重快速预测,或在其基础上调整网络结构进行二次开发,是兼顾理论讲解与代码实操的实用资料。

1. 基于 Python+Unet 的图像语义分割:这份资源能替你跑通训练到预测的完整链路

拿到 Segmentation_Unet-master.zip 时,多数人心里只有一个问题:这东西能在我电脑上跑起来吗?答案是能。这份基于 Python+Unet 模型的图像语义分割资源,把语义分割的完整链路——原始图与标签的生成、Unet 训练、单张图预测、分块结果合并——封装成了几个可直接运行的脚本,还附带一个训练好的 Trained_Unet_Model.h5 权重。就算你只装过 Python 没碰过深度学习,配好 TensorFlow 和 OpenCV 后,也能在半小时内跑出一张测试图的分割结果。它适合两类人:一是课程设计、毕设或工程实训需要交付可演示项目的人;二是想用代码理解 Unet、而不是从论文硬啃的入门者。下面按原理、数据、训练、预测、避坑、进阶的顺序逐个拆解。

2. Unet 原理与资源包拆解:先看懂训练到预测的完整链路,再动手改代码

2.1 为什么语义分割的入门项目几乎都是 Unet:编码器、解码器与跳跃连接

语义分割要解决的问题是给每个像素一个类别标签。早期滑窗分类的办法是每个像素周围取一个 patch 送进分类网络,效率低且相邻 patch 之间容易产生锯齿边界。Unet 之所以成为入门首选,是因为它在结构上同时解决了「特征够抽象」和「边界够精细」这对矛盾。

网络形状像字母 U,左边编码器、右边解码器。编码器通过卷积加池化把输入逐步压小,让网络看到更大感受野,回答「这块区域属于什么物体」;解码器通过上采样把特征图放大回原始分辨率,恢复空间信息。关键在跳跃连接:解码器每一层都把编码器对应层的特征图拼接过来,浅层的边缘纹理信息直接喂给深层,恢复分辨率时不丢细节。没有跳跃连接的 U 形网络,分割结果通常边缘模糊、小目标丢失。

对比 FCN 这类早期全卷积网络,Unet 在训练数据量不大时明显更稳。医学影像、遥感影像这类标注成本很高的场景,几百对图像就能训出一个能用的模型,这也是它常年出现在课程设计和毕设里的原因。这份资源里的 unet_train.py 用的是最经典的 Unet 结构,代码是 Keras 函数式 API 写的,逐层看下来并不难。

2.2 资源包文件清单与职责划分

解压 zip 后,目录里除了 .DS_Store 这种 macOS 系统自动生成的隐藏文件,真正和项目相关的文件不算多。我建议先执行一条命令,把文件树拉出来:

find Segmentation_Unet-master -type f -not -name '.DS_Store' | sort

这条命令会列出所有非 .DS_Store 文件,目的是确认数据目录里有没有混入系统垃圾文件,也方便你核对每个文件的相对路径。常见输出包括 gen_dataset.py、unet_train.py、unet_predict.py、combind.py、Trained_Unet_Model.h5、data/src、data/label、test 等。各文件职责如下:

文件 / 目录职责运行阶段
instruction.pptx项目说明与环境配置文档,建议先看准备
gen_dataset.py读取 data/src 与 data/label,生成训练用数组训练前
unet_train.py定义 Unet、加载数据、执行训练并保存 h5训练
Trained_Unet_Model.h5训练好的权重文件预测
unet_predict.py加载 h5,对 test 目录图片推理并输出掩码预测
combind.py把分块预测结果拼回完整大图预测后处理
data/src原始输入图像数据
data/label与 src 一一对应的标签掩码数据
test测试图片:1.png、2.png、3.png预测
plot.png训练过程的 loss 曲线图训练产物

test 目录里除了三个 png,还有 1.png.ovr 和 1.png.aux.xml。这两个是 GIS 软件(QGIS 这类)为影像生成的伴生文件,ovr 是金字塔概览,aux.xml 记录坐标系信息,说明这批测试图很可能来自遥感影像。预测时只读 png 本体即可,伴生文件可以直接忽略,不用删也不用管。

2.3 训练数据管线:data/src、data/label 与 gen_dataset.py 做了什么

训练分割模型,最繁琐的往往不是模型本身,而是数据对齐。data/src 放原始图像,data/label 放对应掩码,两份文件文件名必须一一对应,顺序一旦错位,模型完全学不到正确的东西。gen_dataset.py 做的事就是把两组文件读进来,统一缩放、归一化、二值化,存成 numpy 数组。这类脚本常见写法如下:

# gen_dataset.py 的核心逻辑(常见实现) import cv2 import numpy as np from glob import glob IMG_SIZE = 256 # 统一缩放到 256x256,要和训练脚本保持一致 src_files = sorted(glob('data/src/*.png')) # 原始图列表 label_files = sorted(glob('data/label/*.png')) # 标签图列表 assert len(src_files) == len(label_files), 'src 与 label 数量不一致' imgs, masks = [], [] for src_path, label_path in zip(src_files, label_files): img = cv2.imread(src_path) # BGR 三通道读入 img = cv2.resize(img, (IMG_SIZE, IMG_SIZE)) img = img / 255.0 # 像素归一化到 [0,1] mask = cv2.imread(label_path, cv2.IMREAD_GRAYSCALE) # 单通道读入 mask = cv2.resize(mask, (IMG_SIZE, IMG_SIZE)) mask = (mask > 127).astype(np.float32) # 二值化:前景为 1 mask = mask[..., np.newaxis] # (h,w) -> (h,w,1) imgs.append(img) masks.append(mask) np.save('train_imgs.npy', np.array(imgs)) np.save('train_masks.npy', np.array(masks)) print(f'共处理 {len(imgs)} 对图像,已保存为 npy 文件')

几个关键点。cv2.imread 按 BGR 读入,模型训练一般不关心通道顺序,但后续可视化预测结果时,如果颜色不对,记得用 cv2.cvtColor 转回 RGB。mask 用 IMREAD_GRAYSCALE 读成单通道,避免把三通道彩色标签直接当训练目标。mask[..., np.newaxis] 补上通道维度,让标签 shape 和模型输出 shape 对齐,少了这一步训练时大概率报维度错误。mask > 127 是二值化阈值,标签图里非零像素就是前景。

sorted() 在这里不是装饰,是保命用的。glob 返回的文件列表在不同系统上顺序不稳定,不排序就 zip,图像和标签错位的概率极高。我一般还会在循环里加断言,名字对不上就直接报错,不让错位数据静默流进训练。

3. 训练自己的 Unet:unet_train.py 的模型定义与参数调优

3.1 模型定义与关键写法

unet_train.py 是这份资源的核心。它通常包含三步:加载上一步生成的 npy、构建 Unet 模型、编译并训练。模型定义部分常见写法是基于 Keras 函数式 API 逐层搭出 U 形结构:

# unet_train.py 中的模型定义(Keras 函数式 API) from tensorflow.keras.layers import (Input, Conv2D, MaxPooling2D, UpSampling2D, concatenate) from tensorflow.keras.models import Model def build_unet(input_shape=(256, 256, 3)): inputs = Input(shape=input_shape) # ---- 编码器:逐层下采样,通道数翻倍 ---- c1 = Conv2D(64, 3, activation='relu', padding='same')(inputs) c1 = Conv2D(64, 3, activation='relu', padding='same')(c1) p1 = MaxPooling2D(pool_size=(2, 2))(c1) c2 = Conv2D(128, 3, activation='relu', padding='same')(p1) c2 = Conv2D(128, 3, activation='relu', padding='same')(c2) p2 = MaxPooling2D(pool_size=(2, 2))(c2) c3 = Conv2D(256, 3, activation='relu', padding='same')(p2) c3 = Conv2D(256, 3, activation='relu', padding='same')(c3) p3 = MaxPooling2D(pool_size=(2, 2))(c3) c4 = Conv2D(512, 3, activation='relu', padding='same')(p3) c4 = Conv2D(512, 3, activation='relu', padding='same')(c4) p4 = MaxPooling2D(pool_size=(2, 2))(c4) # ---- 底部瓶颈层 ---- c5 = Conv2D(1024, 3, activation='relu', padding='same')(p4) c5 = Conv2D(1024, 3, activation='relu', padding='same')(c5) # ---- 解码器:上采样,跳跃连接拼接,通道数减半 ---- u6 = UpSampling2D(size=(2, 2))(c5) u6 = concatenate([u6, c4]) c6 = Conv2D(512, 3, activation='relu', padding='same')(u6) c6 = Conv2D(512, 3, activation='relu', padding='same')(c6) u7 = UpSampling2D(size=(2, 2))(c6) u7 = concatenate([u7, c3]) c7 = Conv2D(256, 3, activation='relu', padding='same')(u7) c7 = Conv2D(256, 3, activation='relu', padding='same')(c7) u8 = UpSampling2D(size=(2, 2))(c7) u8 = concatenate([u8, c2]) c8 = Conv2D(128, 3, activation='relu', padding='same')(u8) c8 = Conv2D(128, 3, activation='relu', padding='same')(c8) u9 = UpSampling2D(size=(2, 2))(c8) u9 = concatenate([u9, c1]) c9 = Conv2D(64, 3, activation='relu', padding='same')(u9) c9 = Conv2D(64, 3, activation='relu', padding='same')(c9) # ---- 输出层:1x1 卷积 + sigmoid,单通道前景概率 ---- outputs = Conv2D(1, 1, activation='sigmoid')(c9) return Model(inputs, outputs)

几个设计选择值得记住。第一,连续两个 3×3 卷积等价于一个 5×5 感受野,但参数量更少、非线性更强。第二,通道数按 64→128→256→512→1024 翻倍再减半,配合跳跃连接把同尺度浅层特征拼回深层。第三,输出层用 1×1 卷积加 sigmoid,输出单通道概率图,对应二分类分割。如果要分多个类别,输出层要改成 softmax、通道数改成类别数,这点第五章再细说。

3.2 训练参数表与调参建议

训练脚本的入口参数一般集中在文件开头的常量定义区域,常见参数与推荐取值整理如下:

参数常见取值说明
IMG_SIZE256输入分辨率,遥感大图建议先缩小再看显存
batch_size2 ~ 8显存 8G 以下建议 2,否则容易 OOM
epochs50 ~ 200建议配合 early stopping 回调
learning_rate1e-4Adam 默认 1e-3 对分割偏大,容易震荡
lossbinary_crossentropy二分类默认;前景占比悬殊时换 Dice 损失
optimizerAdam主流选择,收敛稳定
validation_split0.1 ~ 0.2从训练集切一部分做验证

调参顺序我一般建议先固定输入尺寸和 batch_size,把 learning_rate 调到一个训练曲线稳定下降的值,再考虑换损失函数。分割任务的前景和背景像素往往严重不平衡,如果训练 loss 一直降但预测全是背景,大概率是前景在损失函数里的贡献被稀释了,换成 Dice 损失或加类别权重更有效。

注意:Trained_Unet_Model.h5 是训练产物。如果你重新跑 unet_train.py,原文件会被覆盖,第一次动手前记得先备份。

3.3 训练执行与环境准备

跑 unet_train.py 之前先把 Python 环境准备好。这份资源基于 Keras/TensorFlow,h5 权重对版本敏感,环境配置一步错后面全错:

# 建议用 conda 创建独立环境,避免污染系统 Python conda create -n unet python=3.8 conda activate unet # TensorFlow 2.10 是最后一个原生支持 Windows GPU 的版本,比较稳妥 pip install tensorflow==2.10.0 opencv-python numpy h5py # 先跑数据集生成,再跑训练;训练前先备份原 h5 python gen_dataset.py python unet_train.py

如果你的机器已经装了其他版本 TensorFlow 或 PyTorch 环境,不要混着用,h5 权重对 Keras 版本很敏感。训练过程如果脚本里有 early stopping,你会看到每个 epoch 输出 loss 和 val_loss;训练结束后生成两个产物:Trained_Unet_Model.h5 和 plot.png,后者画的是训练与验证 loss 曲线。

plot.png 值得仔细看。两条曲线都平滑下降且最终接近,说明训练正常;训练 loss 很低、验证 loss 反而上升,说明过拟合,优先加数据增强或减小模型容量,而不是继续加 epoch;两条曲线从一开始就剧烈震荡,先检查 learning_rate,再检查 batch_size,这两项对稳定性影响最大。

4. 用预训练权重做推理:unet_predict.py 与 Trained_Unet_Model.h5 的使用

4.1 预测脚本的输入输出约定

训练不是目的,能拿模型对没见过的图做推理才是验证模型价值的唯一方式。资源包自带的 Trained_Unet_Model.h5 就是一份已经训练好的权重,unet_predict.py 负责加载它,对 test 目录下的图片逐张推理,输出与输入同尺寸的掩码图。

这里有个容易忽略的约定:模型输入尺寸是训练时定死的 256×256,预测时输入的图片必须先缩放到同样尺寸,否则 TensorFlow 会直接报 shape 不匹配。test 目录里那些 .ovr、.aux.xml 伴生文件,predict 脚本不会读取,你也不用在代码里处理它们,读取 png 本体时 OpenCV 会自动跳过非图片文件。

4.2 单张图片预测与结果保存

unet_predict.py 的核心流程可以概括为以下代码:

# unet_predict.py 核心流程 from tensorflow.keras.models import load_model import cv2 import numpy as np # 加载训练好的权重;compile=False 跳过编译信息,兼容不同 Keras 版本 model = load_model('Trained_Unet_Model.h5', compile=False) # 读取测试图,缩放并归一化,与训练时保持一致 img = cv2.imread('test/1.png') img = cv2.resize(img, (256, 256)) img_norm = img / 255.0 # predict 需要 batch 维度,np.newaxis 把 (h,w,3) 扩成 (1,h,w,3) pred = model.predict(img_norm[np.newaxis, ...]) print('预测输出形状:', pred.shape) # 通常是 (1, 256, 256, 1) # 取第一张图的第 0 通道,按 0.5 阈值二值化成 0 / 255 mask = (pred[0, ..., 0] > 0.5).astype(np.uint8) * 255 cv2.imwrite('result_1.png', mask)

几个写法要解释清楚。model.predict 输入必须是四维张量 (batch, height, width, channels),单张图没有 batch 维度,所以 img_norm[np.newaxis, ...] 扩维。pred 输出是 (1, 256, 256, 1),pred[0, ..., 0] 取出单通道概率图。阈值 0.5 是二分类默认分界,大于 0.5 判为前景,输出 0 和 255 是为了让掩码图肉眼可视,255 在灰度图里显示为白色。

提示:如果只想先看效果,跳过训练,直接用 unet_predict.py 加载 Trained_Unet_Model.h5 跑 test 目录即可,这条路最快出结果。

4.3 大图分块预测与 combind.py 的拼接逻辑

遥感影像实际尺寸往往几千甚至上万像素,直接整图缩放到 256×256,小目标会被压缩到几乎看不见。工程上常见做法是分块预测:把大图裁成若干 256×256 的 patch,逐块送进模型,再把预测结果拼回去。combind.py 干的就是拼接这一步。

分块预测的坑在边缘。相邻 patch 之间缺少上下文,会出现拼接缝,尤其是跨越物体边界的地方。我一般会让相邻 patch 重叠 32~64 像素,预测后丢弃边缘、只保留中心区域再拼接:

# combind.py 中常见的重叠拼接思路 overlap = 32 # 重叠像素数 patch_size = 256 # 与训练输入一致 step = patch_size - overlap # 滑窗步长 # 对每个 patch 预测后,只取中心区域写入大图对应位置 for i, row in enumerate(range(0, H - patch_size + 1, step)): for j, col in enumerate(range(0, W - patch_size + 1, step)): patch = big_img[row:row + patch_size, col:col + patch_size] pred = model.predict(patch[np.newaxis, ...])[0, ..., 0] center = pred[overlap // 2:-overlap // 2, overlap // 2:-overlap // 2] result[row + overlap // 2:row + step + overlap // 2, col + overlap // 2:col + step + overlap // 2] = center

这段代码的核心是 step = patch_size - overlap,保证相邻 patch 有重叠;写入时只写中心区域,相当于对边界预测做了软丢弃。实际跑的时候注意最后一行、最后一列往往不足一个 patch,要对原图做边缘填充,常见做法是用 cv2.copyMakeBorder 做常数填充,或者对最后一块单独缩小步长。这段逻辑在 combind.py 里通常会写成函数封装,你只需要传入大图和模型两个参数。

5. Unet 避坑实录:数据对齐、通道维度与权重加载的五个典型翻车场景

这一章是整份资源最容易翻车的五个场景,按「现象 → 原因 → 解决」写清楚。每一条都是这个项目里真实会出现的问题,建议动手前先通读一遍。

5.1 训练 loss 正常下降,但预测结果全是噪声或全黑

现象:训练日志里 loss 曲线很正常,验证 loss 也在降,但用训练好的模型预测,输出掩码要么全是黑色,要么是毫无意义的噪声点。

原因:最常见的是训练数据和标签没有对齐。data/src 与 data/label 文件名顺序不一致,或目录混入 .DS_Store 这类系统文件,glob 匹配时把隐藏文件也算进去,模型看到的是「图像 A 配标签 B」的错位数据。这种错误很隐蔽,loss 照样下降,网络只是在学一个永远无法收敛的映射。

解决:在 gen_dataset.py 里加断言强制校验,并把 .DS_Store 排除在 glob 之外:

src_files = sorted(glob('data/src/*.png')) label_files = sorted(glob('data/label/*.png')) assert len(src_files) == len(label_files), 'src 与 label 数量不一致' for s, l in zip(src_files, label_files): assert s.split('/')[-1] == l.split('/')[-1], f'文件名不匹配: {s} vs {l}'

我还会在打印信息里输出每个文件的 basename,肉眼扫一遍再进入训练。这一步 30 秒,能省掉后面几小时的无效训练。

5.2 model.fit 报 ValueError:Shapes (None, 256, 256, 1) 与 (None, 256, 256) 不兼容

现象:训练一启动就报维度不兼容,错误信息里可以看到模型输出是四维,标签却是三维。

原因:标签数组少了通道维度。Unet 输出是 (batch, h, w, 1),而 mask 数组在 numpy 里是 (batch, h, w),Keras 计算损失时对不上。另一个来源是 mask 在 resize 时用了默认线性插值,标签像素变成 0 到 255 之间的连续值,二值化没生效,标签不再是干净的 0/1。

解决:在 gen_dataset.py 里给 mask 补通道维度,并强制二值化:

mask = cv2.resize(mask, (IMG_SIZE, IMG_SIZE)) mask = (mask > 127).astype(np.float32) # 先二值化 mask = mask[..., np.newaxis] # 再补通道

补维度要在二值化之后做,顺序不能反。如果 label 图里有中间灰度值,先用 Otsu 或固定阈值做一次分割再二值化,不要直接拿连续值当标签。

5.3 load_model 加载 h5 报错,或加载成功但预测结果全错

现象:运行 unet_predict.py 时 load_model 抛出异常,报自定义层找不到或配置解析失败;或者能加载但预测输出形状、数值明显不对。

原因:h5 权重文件和当前 Keras 版本不兼容。这份资源的 h5 很可能是早期 TensorFlow 2.x 保存的,而你本地装的是新版本 TensorFlow 或 Keras 3。Keras 3 对旧 h5 的加载策略变化较大,直接 load_model 容易踩配置解析的坑。

解决:加载时用 compile=False 跳过编译信息,只取权重:

model = load_model('Trained_Unet_Model.h5', compile=False)

如果仍然报错,用 h5py 直接看 h5 内部结构,确认权重键名与你的模型层顺序一致:

import h5py with h5py.File('Trained_Unet_Model.h5', 'r') as f: print(list(f.keys())) # 查看顶层键,确认是 model_weights 结构

更稳妥的做法是训练和预测在同一环境里完成,不要训练完升级 TensorFlow 再去预测。跨版本实在绕不开时,用 h5py 读出权重数组,再按 build_unet 逐层 set_weights,能绕开大部分版本问题。

5.4 标签有多类目标,却用 sigmoid 当二分类训练,类别互相混淆

现象:标签图里有好几种颜色分别代表不同地物,训练时 loss 在降,但预测结果里类别之间互相串,道路和建筑分不开。

原因:任务定义和模型不匹配。sigmoid 输出单通道概率图,只能表达「是前景」和「是背景」两类。如果标签本身是多类别,模型没有能力同时区分多个类,除非你把多分类拆成多个二分类,或者改输出结构。

解决:多类别分割要把输出层改成 softmax,通道数改成类别数,损失函数换成 categorical_crossentropy,标签做 one-hot:

# 多类别分割时输出层改为: outputs = Conv2D(num_classes, 1, activation='softmax')(c9) # 标签 one-hot 化,mask 每个像素是 0 到 num_classes-1 的编号 mask_onehot = tf.keras.utils.to_categorical(mask, num_classes=num_classes)

注意 to_categorical 要求 mask 是类别编号而不是 RGB 颜色值。如果标签是彩色图,先在 gen_dataset.py 里建一张颜色到编号的映射表,逐像素替换,再进 one-hot。这一步漏了,后面所有指标计算都会是错的。

5.5 训练时显存溢出,或 CPU 训练慢到怀疑人生

现象:显存不足报错 OOM,或者用 CPU 训练一个 epoch 要十几分钟,256×256 输入加 batch_size 8 直接跑不动。

原因:Unet 显存消耗由输入分辨率、batch_size、通道数三者相乘决定。256×256 输入、batch_size 8、瓶颈层 1024 通道,显存占用很可观。入门显卡 4G 显存默认参数大概率扛不住。

解决:先降 batch_size 到 2,再考虑降 IMG_SIZE 到 192 或 128。优先降 batch_size,因为分辨率影响定位精度,batch_size 只影响梯度估计噪声,对最终精度影响相对小。另外设置显存按需增长,避免一上来占满全部显存:

import tensorflow as tf gpus = tf.config.experimental.list_physical_devices('GPU') if gpus: tf.config.experimental.set_memory_growth(gpus[0], True)

这段代码加在训练脚本最开头,能在同一台机器上避免和其他程序抢显存。这是我从第一个项目开始就养成的习惯,每次都先写进去。

6. 进阶方向:把 Unet 迁移到自己的数据集,并用 mIoU 验证效果

6.1 替换成自己的数据集最少要改的三处

把这份资源迁移到自己的数据,最小改动是三个地方。第一,data/src 和 data/label 换成你自己的图像对,文件名一一对应。第二,gen_dataset.py 里的 IMG_SIZE 和类别定义改成与你的数据匹配,如果标签是多类彩色图,加一张颜色到类别编号的映射表。第三,unet_train.py 里的输出通道数、损失函数与类别数对齐。这三处改完,大多数二分类和遥感场景都能直接跑,模型结构不用动。如果你下载这份资源是为了做毕设或课程设计,我的建议是先按第三章把原始链路完整跑通一遍,中间不跳步,再谈迁移和改造。

6.2 用 mIoU 和 Dice 评估分割结果,别只靠肉眼

验收分割项目,光看几张图说「差不多」是不够的,要用数值说话。最常用的两个指标是 mIoU(平均交并比)和 Dice 系数。mIoU 对每个类别计算预测与真值交集面积除以并集面积,再对所有类取平均;Dice 更关注重叠区域,对类别不平衡更宽容。计算代码如下:

import numpy as np def compute_miou(pred_mask, true_mask, num_classes=2): ious = [] for cls in range(num_classes): pred = (pred_mask == cls) true = (true_mask == cls) inter = np.logical_and(pred, true).sum() union = np.logical_or(pred, true).sum() if union == 0: continue # 真值里没有该类,跳过 ious.append(inter / union) return np.mean(ious) def compute_dice(pred_mask, true_mask): inter = np.logical_and(pred_mask, true_mask).sum() return 2 * inter / (pred_mask.sum() + true_mask.sum() + 1e-6)

+1e-6 是防分母为零的,Dice 的空值保护尤其重要。跑测试集之前,把预测结果 resize 回原图尺寸,再和原尺寸真值对比,算出的 mIoU 才有意义。拿着 256×256 的预测图去和原始大图标签对比,尺寸不一致会让交并比虚高或虚低,这个坑我第一次写评估脚本时踩过。

6.3 模型改进方向与我的使用习惯

想在毕设里对这份资源做改进,三个方向比较现实。第一,在跳跃连接处加 attention gate,让解码器自动学习哪些浅层特征值得保留;第二,把编码器层改成残差块,训练更稳定、收敛更快;第三,加数据增强,旋转、翻转、色彩抖动能明显缓解小样本过拟合。三个方向的改动都集中在 build_unet 函数内部,不影响数据管线。

我自己的工作习惯是,每次拿到新的分割资源,先不碰模型,先跑最小链路——用十对左右的小数据、batch_size 1、训练十个 epoch,确认数据管线没有对齐问题,再上完整数据。从那以后我每次跑分割项目都强制走一遍这个流程,之后换数据集、改损失函数、加注意力模块,都是在这个已验证的基线上做增量。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询