5分钟搞定Unet模型训练:手把手教你用TensorFlow2.2实现城市街景语义分割

张开发
2026/5/6 16:08:40 15 分钟阅读

分享文章

5分钟搞定Unet模型训练:手把手教你用TensorFlow2.2实现城市街景语义分割
5分钟搞定Unet模型训练手把手教你用TensorFlow2.2实现城市街景语义分割语义分割作为计算机视觉领域的核心技术之一正在自动驾驶、医疗影像分析等领域发挥着越来越重要的作用。而Unet凭借其独特的U型结构和出色的性能成为语义分割任务中的明星模型。本文将带你快速上手Unet模型训练使用TensorFlow2.2框架和Cityscapes数据集在短短几分钟内完成从环境配置到模型训练的全过程。1. 环境准备与数据加载在开始之前我们需要确保开发环境配置正确。推荐使用Python3.6和TensorFlow2.2版本这是经过验证的稳定组合。pip install tensorflow2.2.0 pip install numpy matplotlibCityscapes数据集是专门用于城市街景语义分割的大型数据集包含50个不同城市的高质量标注图像。数据集结构如下cityscapes/ ├── images/ │ ├── train/ │ └── val/ └── gtFine/ ├── train/ └── val/加载数据时我们需要特别注意以下几点图像和标签的对应关系数据增强策略内存优化处理import tensorflow as tf import numpy as np import glob # 加载图像和标签路径 img_paths sorted(glob.glob(cityscapes/images/train/*/*.png)) label_paths sorted(glob.glob(cityscapes/gtFine/train/*/*_gtFine_labelIds.png)) # 验证路径对应关系 for img, label in zip(img_paths[:5], label_paths[:5]): print(fImage: {img.split(/)[-1]} | Label: {label.split(/)[-1]})2. 数据预处理与增强高质量的数据预处理是模型成功的关键。Cityscapes数据集中的标签图像是单通道的每个像素值对应一个类别ID。数据增强策略随机水平翻转概率50%随机裁剪256x256像素归一化处理图像归一化到[-1,1]范围def load_and_preprocess(img_path, label_path): # 读取图像和标签 image tf.io.read_file(img_path) image tf.image.decode_png(image, channels3) label tf.io.read_file(label_path) label tf.image.decode_png(label, channels1) # 随机裁剪 concat tf.concat([image, label], axis-1) concat tf.image.resize(concat, [280, 280]) concat tf.image.random_crop(concat, [256, 256, 4]) image, label concat[:,:,:3], concat[:,:,3:] # 随机翻转 if tf.random.uniform(()) 0.5: image tf.image.flip_left_right(image) label tf.image.flip_left_right(label) # 归一化 image tf.cast(image, tf.float32) / 127.5 - 1 label tf.cast(label, tf.int32) return image, label提示数据增强可以显著提高模型泛化能力但要注意增强后的标签必须与图像同步变换。3. Unet模型构建Unet的核心思想是编码器-解码器结构通过跳跃连接保留空间信息。以下是TensorFlow2.2中的实现方式def conv_block(inputs, filters, kernel_size3): x tf.keras.layers.Conv2D(filters, kernel_size, paddingsame)(inputs) x tf.keras.layers.BatchNormalization()(x) x tf.keras.layers.ReLU()(x) return x def build_unet(input_shape(256,256,3)): inputs tf.keras.Input(shapeinput_shape) # 编码器部分 c1 conv_block(inputs, 64) p1 tf.keras.layers.MaxPool2D()(c1) c2 conv_block(p1, 128) p2 tf.keras.layers.MaxPool2D()(c2) c3 conv_block(p2, 256) p3 tf.keras.layers.MaxPool2D()(c3) c4 conv_block(p3, 512) p4 tf.keras.layers.MaxPool2D()(c4) # 桥接层 b conv_block(p4, 1024) # 解码器部分 u1 tf.keras.layers.Conv2DTranspose(512, 2, strides2, paddingsame)(b) u1 tf.keras.layers.concatenate([u1, c4]) u1 conv_block(u1, 512) u2 tf.keras.layers.Conv2DTranspose(256, 2, strides2, paddingsame)(u1) u2 tf.keras.layers.concatenate([u2, c3]) u2 conv_block(u2, 256) u3 tf.keras.layers.Conv2DTranspose(128, 2, strides2, paddingsame)(u2) u3 tf.keras.layers.concatenate([u3, c2]) u3 conv_block(u3, 128) u4 tf.keras.layers.Conv2DTranspose(64, 2, strides2, paddingsame)(u3) u4 tf.keras.layers.concatenate([u4, c1]) u4 conv_block(u4, 64) # 输出层 outputs tf.keras.layers.Conv2D(34, 1, activationsoftmax)(u4) return tf.keras.Model(inputsinputs, outputsoutputs)模型结构的关键特点组件说明参数数量编码器4个下采样块每块包含2个卷积层约70%参数桥接层最高维特征提取约15%参数解码器4个上采样块每块包含转置卷积和特征拼接约15%参数输出层1x1卷积输出34个类别概率少量参数4. 模型训练与评估配置好数据管道后我们就可以开始训练模型了。这里使用SparseCategoricalCrossentropy作为损失函数因为它可以直接处理类别ID形式的标签。# 创建数据管道 train_dataset tf.data.Dataset.from_tensor_slices((img_paths, label_paths)) train_dataset train_dataset.map(load_and_preprocess, num_parallel_callstf.data.AUTOTUNE) train_dataset train_dataset.shuffle(1000).batch(32).prefetch(tf.data.AUTOTUNE) # 初始化模型 model build_unet() model.compile(optimizeradam, losssparse_categorical_crossentropy, metrics[accuracy]) # 定义回调函数 callbacks [ tf.keras.callbacks.ModelCheckpoint(unet_cityscapes.h5, save_best_onlyTrue), tf.keras.callbacks.ReduceLROnPlateau(patience3) ] # 开始训练 history model.fit(train_dataset, epochs50, callbackscallbacks)训练过程中需要监控的关键指标训练损失应持续下降验证损失避免过拟合mIoU平均交并比语义分割的核心指标def visualize_results(image, label, prediction): plt.figure(figsize(15,5)) plt.subplot(1,3,1) plt.imshow((image1)/2) # 反归一化 plt.title(Original Image) plt.subplot(1,3,2) plt.imshow(np.squeeze(label)) plt.title(Ground Truth) plt.subplot(1,3,3) plt.imshow(np.argmax(prediction, axis-1)) plt.title(Prediction) plt.show() # 测试可视化 test_image, test_label next(iter(train_dataset.take(1))) pred model.predict(test_image) visualize_results(test_image[0], test_label[0], pred[0])在实际项目中我发现批量大小和学习率的选择对Unet训练影响很大。通常从较小的学习率如0.0001开始配合梯度裁剪可以稳定训练过程。

更多文章