keras搭建unet模型—语义分割

在前一篇文章基于keras的全卷积网络FCN—语义分割中,博主用keras搭建了fcn模型,使用猫狗数据集做了训练。本文在此基础上搭建了unet模型,数据介绍请看上面这篇文章,本文直接展示unet的模型。

本文的环境是tensorflow2.4,gpu版本。

unet:

unet是u型结构,前面的层不断下采样,后面的层在不断上采样,同stage的跳接以保存信息。unet的层数不是很深,在医学领域上用于细胞检测效果显著。
在这里插入图片描述

搭建unet

def unet(pretrained_weights=None, input_size=(256, 256, 3)):
    inputs = Input(input_size)

    conv1 = Conv2D(64, 3, activation='relu', padding='same', kernel_initializer='he_normal')(inputs)
    conv1 = Conv2D(64, 3, activation='relu', padding='same', kernel_initializer='he_normal')(conv1)
    pool1 = MaxPooling2D(pool_size=(2, 2))(conv1)

    conv2 = Conv2D(128, 3, activation='relu', padding='same', kernel_initializer='he_normal')(pool1)
    conv2 = Conv2D(128, 3, activation='relu', padding='same', kernel_initializer='he_normal')(conv2)
    pool2 = MaxPooling2D(pool_size=(2, 2))(conv2)

    conv3 = Conv2D(256, 3, activation='relu', padding='same', kernel_initializer='he_normal')(pool2)
    conv3 = Conv2D(256, 3, activation='relu', padding='same', kernel_initializer='he_normal')(conv3)
    pool3 = MaxPooling2D(pool_size=(2, 2))(conv3)

    conv4 = Conv2D(512, 3, activation='relu', padding='same', kernel_initializer='he_normal')(pool3)
    conv4 = Conv2D(512, 3, activation='relu', padding='same', kernel_initializer='he_normal')(conv4)
    drop4 = Dropout(0.5)(conv4)
    pool4 = MaxPooling2D(pool_size=(2, 2))(drop4)

    conv5 = Conv2D(1024, 3, activation='relu', padding='same', kernel_initializer='he_normal')(pool4)
    conv5 = Conv2D(1024, 3, activation='relu', padding='same', kernel_initializer='he_normal')(conv5)
    drop5 = Dropout(0.5)(conv5)

    up6 = Conv2D(512, 2, activation='relu', padding='same', kernel_initializer='he_normal')(
        UpSampling2D(size=(2, 2))(drop5))
    merge6 = concatenate([drop4, up6], axis=3)
    conv6 = Conv2D(512, 3, activation='relu', padding='same', kernel_initializer='he_normal')(merge6)
    conv6 = Conv2D(512, 3, activation='relu', padding='same', kernel_initializer='he_normal')(conv6)

    up7 = Conv2D(256, 2, activation='relu', padding='same', kernel_initializer='he_normal')(
        UpSampling2D(size=(2, 2))(conv6))
    merge7 = concatenate([conv3, up7], axis=3)
    conv7 = Conv2D(256, 3, activation='relu', padding='same', kernel_initializer='he_normal')(merge7)
    conv7 = Conv2D(256, 3, activation='relu', padding='same', kernel_initializer='he_normal')(conv7)

    up8 = Conv2D(128, 2, activation='relu', padding='same', kernel_initializer='he_normal')(
        UpSampling2D(size=(2, 2))(conv7))
    merge8 = concatenate([conv2, up8], axis=3)
    conv8 = Conv2D(128, 3, activation='relu', padding='same', kernel_initializer='he_normal')(merge8)
    conv8 = Conv2D(128, 3, activation='relu', padding='same', kernel_initializer='he_normal')(conv8)

    up9 = Conv2D(64, 2, activation='relu', padding='same', kernel_initializer='he_normal')(
        UpSampling2D(size=(2, 2))(conv8))
    merge9 = concatenate([conv1, up9], axis=3)
    conv9 = Conv2D(64, 3, activation='relu', padding='same', kernel_initializer='he_normal')(merge9)
    conv9 = Conv2D(64, 3, activation='relu', padding='same', kernel_initializer='he_normal')(conv9)
    conv9 = Conv2D(2, 3, activation='relu', padding='same', kernel_initializer='he_normal')(conv9)
    conv10 = Conv2D(1, 1, activation='sigmoid')(conv9)

    model = Model(inputs=inputs, outputs=conv10)
    model.summary()

    if (pretrained_weights):
        model.load_weights(pretrained_weights)

    return model

看一下模型结构
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
博主不会截长图,将就看吧。

训练

训练这部分用的数据集准备可以详细参考这里,你也可以查看附录中的全部代码,内容是相同的。

训练fcn时我们使用的是adam优化器,loss函数选择的是sparse_categorical_crossentropy。训练unet时,adagrad更适合做unet的优化器,如果用adam可能需要调整一下学习率,loss函数选择binary_crossentropy较好。这是博主调试代码后选择的,你也可以根据自己的想法更换它们。

# 调整显存使用情况,避免显存占满
config = ConfigProto()
config.gpu_options.allow_growth = True
session = InteractiveSession(config=config)

data_train, data_test = dataset()

# unet
model = unet()
model.compile(optimizer='adagrad', loss='binary_crossentropy', metrics=['acc'])

tensorboard_callback = tf.keras.callbacks.TensorBoard(log_dir="E:\jupyter\log2", histogram_freq=1)
model.fit(data_train, epochs=1, batch_size = 8, validation_data=data_test, callbacks = [tensorboard_callback])

model.save('FCN_model.h5')

训练结果

在这里插入图片描述仍然只训练了一个epoch
在这里插入图片描述
tensorboard的使用方法如果有疑问可以参考win10+keras+tensorboard的使用

全部代码

dataset_my.py

import tensorflow as tf
import numpy as np
import glob
"""数据整理,用的是猫狗数据集"""


def read_jpg(path):
    img=tf.io.read_file(path)
    img=tf.image.decode_jpeg(img,channels=3)
    return img


def read_png(path):
    img=tf.io.read_file(path)
    img=tf.image.decode_png(img,channels=1)
    return img


#现在编写归一化的函数
def normal_img(input_images,input_anno):
    input_images=tf.cast(input_images,tf.float32)
    input_images=input_images/127.5-1
    input_anno-=1
    return input_images,input_anno


#加载函数
def load_images(input_images_path,input_anno_path):
    input_image=read_jpg(input_images_path)
    input_anno=read_png(input_anno_path)
    input_image=tf.image.resize(input_image,(256,256))
    input_anno=tf.image.resize(input_anno,(256,256))
    return normal_img(input_image,input_anno)


def dataset():
    # 读取图像和目标图像
    images = glob.glob(r"images\*.jpg")
    anno = glob.glob(r"annotations\trimaps\*.png")

    # 现在对读取进来的数据进行制作batch
    np.random.seed(1)
    index = np.random.permutation(len(images))  # 随机打乱7390个数
    images = np.array(images)[index]
    anno = np.array(anno)[index]

    # 创建dataset
    dataset = tf.data.Dataset.from_tensor_slices((images, anno))

    # 测试数据量和训练数据量,20%测试。
    test_count = int(len(images) * 0.2)
    train_count = len(images) - test_count

    # 取出训练数据和测试数据
    data_train = dataset.skip(test_count)  # 跳过前test的数据
    data_test = dataset.take(test_count)  # 取前test的数据
    data_train = data_train.map(load_images, num_parallel_calls=tf.data.experimental.AUTOTUNE)
    data_test = data_test.map(load_images, num_parallel_calls=tf.data.experimental.AUTOTUNE)

    # 现在开始batch的制作,不制作batch会使维度由4维降为3维
    BATCH_SIZE = 3
    data_train = data_train.shuffle(100).batch(BATCH_SIZE)
    data_test = data_test.batch(BATCH_SIZE)

    return data_train, data_test

model_my.py

import tensorflow as tf
from tensorflow.keras.models import *
from tensorflow.keras.layers import *
from tensorflow.keras.applications import VGG16
"""模型架构,fcn和unet"""


def fcn(pretrained_weights=None, input_size=(256, 256, 3)):
    # 加载vgg16
    conv_base = VGG16(weights='imagenet', input_shape=input_size, include_top=False)

    # 现在创建多输出模型,三个output
    layer_names = [
        'block5_conv3',
        'block4_conv3',
        'block3_conv3',
        'block5_pool']

    # 得到这几个曾输出的列表,为了方便就直接使用列表推倒式了
    layers_output = [conv_base.get_layer(layer_name).output for layer_name in layer_names]

    # 创建一个多输出模型,这样一张图片经过这个网络之后,就会有多个输出值了
    multiout_model = Model(inputs=conv_base.input, outputs=layers_output)

    multiout_model.trainable = True
    inputs = Input(shape=input_size)

    # 这个多输出模型会输出多个值,因此前面用多个参数来接收即可。
    out_block5_conv3, out_block4_conv3, out_block3_conv3, out = multiout_model(inputs)

    # 现在将最后一层输出的结果进行上采样,然后分别和中间层多输出的结果进行相加,实现跳级连接
    x1 = Conv2DTranspose(512, 3, strides=2, padding='same', activation='relu')(out)

    # 上采样之后再加上一层卷积来提取特征
    x1 = Conv2D(512, 3, padding='same', activation='relu')(x1)

    # 与多输出结果的倒数第二层进行相加,shape不变
    x2 = tf.add(x1, out_block5_conv3)

    # x2进行上采样
    x2 = Conv2DTranspose(512, 3, strides=2, padding='same', activation='relu')(x2)
    # 直接拿到x3,不使用
    x3 = tf.add(x2, out_block4_conv3)

    # x3进行上采样
    x3 = Conv2DTranspose(256, 3, strides=2, padding='same', activation='relu')(x3)

    # 增加卷积提取特征
    x3 = Conv2D(256, 3, padding='same', activation='relu')(x3)
    x4 = tf.add(x3, out_block3_conv3)

    # x4还需要再次进行上采样,得到和原图一样大小的图片,再进行分类
    x5 = Conv2DTranspose(128, 3, strides=2, padding='same', activation='relu')(x4)

    # 继续进行卷积提取特征
    x5 = Conv2D(128, 3, padding='same', activation='relu')(x5)

    # 最后一步,图像还原
    preditcion = tf.keras.layers.Conv2DTranspose(3, 3, strides=2, padding='same', activation='softmax')(x5)

    model = Model(inputs=inputs, outputs=preditcion)
    model.summary()

    # 加载预训练模型
    if (pretrained_weights):
        model.load_weights(pretrained_weights)

    return model



def unet(pretrained_weights=None, input_size=(256, 256, 3)):
    inputs = Input(input_size)

    conv1 = Conv2D(64, 3, activation='relu', padding='same', kernel_initializer='he_normal')(inputs)
    conv1 = Conv2D(64, 3, activation='relu', padding='same', kernel_initializer='he_normal')(conv1)
    pool1 = MaxPooling2D(pool_size=(2, 2))(conv1)

    conv2 = Conv2D(128, 3, activation='relu', padding='same', kernel_initializer='he_normal')(pool1)
    conv2 = Conv2D(128, 3, activation='relu', padding='same', kernel_initializer='he_normal')(conv2)
    pool2 = MaxPooling2D(pool_size=(2, 2))(conv2)

    conv3 = Conv2D(256, 3, activation='relu', padding='same', kernel_initializer='he_normal')(pool2)
    conv3 = Conv2D(256, 3, activation='relu', padding='same', kernel_initializer='he_normal')(conv3)
    pool3 = MaxPooling2D(pool_size=(2, 2))(conv3)

    conv4 = Conv2D(512, 3, activation='relu', padding='same', kernel_initializer='he_normal')(pool3)
    conv4 = Conv2D(512, 3, activation='relu', padding='same', kernel_initializer='he_normal')(conv4)
    drop4 = Dropout(0.5)(conv4)
    pool4 = MaxPooling2D(pool_size=(2, 2))(drop4)

    conv5 = Conv2D(1024, 3, activation='relu', padding='same', kernel_initializer='he_normal')(pool4)
    conv5 = Conv2D(1024, 3, activation='relu', padding='same', kernel_initializer='he_normal')(conv5)
    drop5 = Dropout(0.5)(conv5)

    up6 = Conv2D(512, 2, activation='relu', padding='same', kernel_initializer='he_normal')(
        UpSampling2D(size=(2, 2))(drop5))
    merge6 = concatenate([drop4, up6], axis=3)
    conv6 = Conv2D(512, 3, activation='relu', padding='same', kernel_initializer='he_normal')(merge6)
    conv6 = Conv2D(512, 3, activation='relu', padding='same', kernel_initializer='he_normal')(conv6)

    up7 = Conv2D(256, 2, activation='relu', padding='same', kernel_initializer='he_normal')(
        UpSampling2D(size=(2, 2))(conv6))
    merge7 = concatenate([conv3, up7], axis=3)
    conv7 = Conv2D(256, 3, activation='relu', padding='same', kernel_initializer='he_normal')(merge7)
    conv7 = Conv2D(256, 3, activation='relu', padding='same', kernel_initializer='he_normal')(conv7)

    up8 = Conv2D(128, 2, activation='relu', padding='same', kernel_initializer='he_normal')(
        UpSampling2D(size=(2, 2))(conv7))
    merge8 = concatenate([conv2, up8], axis=3)
    conv8 = Conv2D(128, 3, activation='relu', padding='same', kernel_initializer='he_normal')(merge8)
    conv8 = Conv2D(128, 3, activation='relu', padding='same', kernel_initializer='he_normal')(conv8)

    up9 = Conv2D(64, 2, activation='relu', padding='same', kernel_initializer='he_normal')(
        UpSampling2D(size=(2, 2))(conv8))
    merge9 = concatenate([conv1, up9], axis=3)
    conv9 = Conv2D(64, 3, activation='relu', padding='same', kernel_initializer='he_normal')(merge9)
    conv9 = Conv2D(64, 3, activation='relu', padding='same', kernel_initializer='he_normal')(conv9)
    conv9 = Conv2D(2, 3, activation='relu', padding='same', kernel_initializer='he_normal')(conv9)
    conv10 = Conv2D(1, 1, activation='sigmoid')(conv9)

    model = Model(inputs=inputs, outputs=conv10)
    model.summary()

    if (pretrained_weights):
        model.load_weights(pretrained_weights)

    return model

main.py

from tensorflow.compat.v1 import ConfigProto
from tensorflow.compat.v1 import InteractiveSession
from model_my import fcn, unet
from dataset_my import dataset
import tensorflow as tf


# 调整显存使用情况,避免显存占满
config = ConfigProto()
config.gpu_options.allow_growth = True
session = InteractiveSession(config=config)

data_train, data_test = dataset()

# unet
model = unet()
model.compile(optimizer='adagrad', loss='binary_crossentropy', metrics=['acc'])
# fcn
# model = fcn()
# model.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics=['acc'])

tensorboard_callback = tf.keras.callbacks.TensorBoard(log_dir="E:\jupyter\log2", histogram_freq=1)
model.fit(data_train, epochs=1, batch_size = 8, validation_data=data_test, callbacks = [tensorboard_callback])

model.save('FCN_model.h5')
# 加载保存的模型
# new_model=tf.keras.models.load_model('FCN_model.h5')
  • 9
    点赞
  • 39
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
UNet是一种深度卷积神经网络,用于图像分割任务。它的结构类似于自编码器,但在中间加入了跳跃连接,可以保留更多的上下文信息。在语义分割任务中,UNet可以将输入图片分割成不同的语义区域。 以下是使用UNet构建语义分割模型的步骤: 1. 数据准备:准备语义分割任务所需的数据集,包括输入图片和标签。标签通常是一张与输入图片大小相同的二进制图像,每个像素表示该像素所属的语义类别。 2. 数据预处理:对输入图片进行预处理,包括裁剪、缩放、标准化等操作。对标签进行预处理,将二进制图像转换为独热编码或像素级别的标签。 3. 构建UNet模型:使用Keras或PyTorch等深度学习框架构建UNet模型UNet的结构包括编码器、解码器和跳跃连接。编码器由多个卷积层和池化层组成,可以提取输入图片的特征。解码器由多个反卷积层和上采样层组成,可以将特征图恢复到原始大小。跳跃连接将编码器和解码器的特征图连接起来,可以保留更多的上下文信息。 4. 训练模型:使用训练集对UNet模型进行训练,包括设置损失函数、优化器和学习率等超参数。损失函数通常是交叉熵或Dice系数,优化器可以选择Adam或SGD等。训练过程中可以使用数据增强技术,如随机旋转、翻转和裁剪等,增加训练数据的多样性。 5. 模型评估:使用验证集对训练好的模型进行评估,包括计算准确率、精确率、召回率和F1分数等指标。可以使用混淆矩阵和可视化工具来分析模型的性能。 6. 模型应用:使用测试集对训练好的模型进行测试,可以将输入图片分割成不同的语义区域。可以使用Python图像处理库将分割结果可视化,以便人工检查。 以上是使用UNet构建语义分割模型的主要步骤,需要根据具体任务和数据集进行调整和优化。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值