语义分割:基于 TensorFlow 对 FCN 与迁移学习的探究

欢迎来到计算机视觉的世界,在这里,计算机学会"看"懂并理解图像。想象一下,计算机不仅能识别物体,还能在它们周围画出精确的轮廓线------这就是图像分割的强大之处。

生成式AI vs 预测式AI:揭秘人工智能领域的两大技术

在当今这个技术驱动的时代,TensorFlow 这一机器学习工具已成为必不可少的存在。再配合 Python 语言的简洁性,我们便能通过一个有趣的项目来揭开图像分割的神秘面纱。

无论你是新手还是编程高手,本文都将是你的向导。在深入动手实践之前,让我们先花点时间了解图像分割的不同类型。

图像分割的类型:基础概念
  • 语义分割(Semantic Segmentation):将图像中的每个像素分类到预定义的类别中。例如,在街景中,它会将每个像素标记为汽车、行人、道路或建筑物的一部分。其目标是理解场景的总体上下文,而无需区分不同的个体实例。
  • 实例分割(Instance Segmentation):更进一步,它不仅为每个像素分配一个类别,还能精确地识别并勾勒出该类别中每个不同对象实例的边界。在一张拥挤的图片中,它能出色地区分出不同的人,即使他们属于同一类别,也能以极高的精度识别并勾勒出群体中的每一个人。
  • 全景分割(Panoptic Segmentation):语义分割与实例分割的结合体。它不仅提供了对场景的详细语义理解,还分离了这些类别中的每个实例。此外,它还为背景指定了一个特殊类别,从而捕捉到整个视觉环境的整体视图。这就好比理解了一个场景的完整故事,既包括了主角,也包括了他们身后的背景。

既然我们已经了解了基础概念,让我们深入探讨实践部分。我们将重点关注语义分割,这是一种在像素级别上提供对图像详细理解的激动人心的技术。

到最后,你不仅会掌握理论知识,还可以使用 TensorFlow 和 Python 构建自己的语义分割模型。

解读openAI的文本图像模型-CLIP

图像分割的高层架构:拆解分析

在图像分割领域,我们遵循一种被称为encoder-decoder结构的高层架构。

Encoder ------ 揭示特征

Encoder充当特征提取器,通常使用卷积神经网络(CNN)来实现。这一部分从图像中提取特征,形成我们所说的"feature map"。初始层专注于线条等低级特征,然后逐渐将它们聚合成眼睛和耳朵等更复杂的特征。这种聚合是通过下采样(downsampling)来实现的,类似于减少像素表示------就像你的视频聊天画面变得有点颗粒感,但你仍然可以通过那些关键特征认出你的朋友。

Decoder ------ 创造预测

现在,让我们转向Decoder。它的作用是获取这些提取出的特征并生成模型的输出或预测。和编码器一样,解码器也是一个卷积神经网络。它为缩小尺寸的特征图中的每个像素分配中间类别标签。然后,它开始进行上采样(upsampling)的复杂任务,逐渐恢复原始图像的精细细节。这个过程一直持续,直到图像恢复到其原始尺寸。

最后的修饰 ------ 像素级标签

最终的结果是一张像素级的标签图。每个像素都被分配了一个最终的类别标签,捕捉到了该像素在图像更广泛背景中所代表的本质。

简单来说,你可以把编码器想象成一名侦探,勤奋地从图像中提取线索(特征)。解码器则是讲故事的人,将这些线索拼凑起来,逐像素地揭示出完整的叙事。这种架构中编码器和解码器的配合,构成了图像分割算法的骨干,为我们提供了关于视觉数据的详细、像素级的洞察。

在本文中,我们将剖析用于语义分割的两个强大的架构:全卷积网络(FCN)和 U-Net。我们将与您一起深入探讨这些框架的核心,并使用 Python 和 TensorFlow 来实现并揭开它们的神秘面纱。我们将提供清晰、简洁的代码片段,并辅以富有见地的解释,以满足初学者和资深从业者的需求。准备好进行一次理论与实践相结合的动手探索,让您能够在项目中有效地利用 FCN 和 U-Net。无论您是好奇的爱好者还是经验丰富的开发者,本文都是您掌握语义分割的指南。

全卷积网络(FCN)

在传统的卷积神经网络(CNN)中,最后几层通常是全连接层,这意味着它们将每个神经元连接到其他的每个神经元。对于图像分类等任务来说,这没问题,因为在这些任务中空间信息并不那么关键。

介绍基于 C++ 的运动恢复结构(SfM)的三维重建

然而,对于语义分割等任务,目标是将图像中的每个像素分类到特定的类别(例如,人、车、背景)中,保留空间信息至关重要。Long、Shelhamer 和 Darrell 在他们的论文《Fully Convolutional Neural Networks for Semantic Segmentation》中提出了全卷积神经网络(FCN)。FCN 通过用卷积层替换全连接层来解决这个问题。这使得网络可以接受任何大小的输入,并产生相应大小的输出。

在语义分割中,FCN 通过在整个网络中保留空间信息,实现了像素级的预测。网络学习将输入图像中的每个像素与特定类别相关联,从而生成详细的分割图。这对于计算机视觉应用特别有用,例如目标检测、场景理解和图像分割。

在继续探索全卷积网络(FCN)的过程中,让我们深入研究一下被称为 FCN-8、FCN-16 和 FCN-32 的变体。这些都是 FCN 架构的不同配置,各自拥有独特的特性。

FCN-8、FCN-16 和 FCN-32:配置解析

FCN-32

FCN-32 在最后一层采用了 32 像素的步幅,导致输出分辨率仅为输入的 1/32。这种配置优先考虑计算效率,而非详细的空间信息。FCN-32 在处理速度上更快,使其适用于对速度要求极高、而对精细分割细节要求不那么严格的场景。

FCN-8

FCN-8 指的是最后一层具有 8 像素步幅的全卷积网络。换句话说,最后一层产生的输出分辨率是输入分辨率的 1/8。这种配置能够实现更精细的预测,捕捉分割图中更细微的差别。FCN-8 的解码器涉及融合来自不同层(包括空间信息较粗糙的层)的预测结果。这种融合过程提升了整体的分割质量。

FCN-16

FCN-16 则是在最后一层采用了 16 像素的步幅。这意味着输出分辨率是输入分辨率的 1/16。虽然与 FCN-8 相比,FCN-16 牺牲了一些空间细节,但它在分辨率和计算效率之间取得了很好的平衡。中间层有助于最终的分割图,提供了精细细节和计算资源之间的折衷方案。

选择合适的配置

FCN-8 通过 8 像素的步幅实现详细预测,捕捉细微差别;FCN-16 通过 16 像素的步幅在分辨率和效率之间取得平衡;而 FCN-32 则通过 32 像素的步幅优先考虑计算效率。

这些配置之间的选择取决于项目需求。在接下来的内容中,我们将使用 TensorFlow 和 Python 来实现每一种配置,从而释放 FCN 变体在语义分割中的潜力。

但这还不是全部------让我们通过结合迁移学习,利用 VGG16 或 ResNet 等预训练编码器,来为我们的 FCN 模型"超级充电"。这一变革性方法可以加速训练并提高准确性,尤其是在标记数据有限的情况下。

万字详解具身智能用的VLA:视觉-语言-动作模型(VLA)概述与Octo、OpenVLA、π0、GR00T N1等模型介绍

接下来是动手实践环节,我将提供代码片段和解释,提升您在计算机视觉领域的理解和熟练度。利用迁移学习的额外优势,释放 FCN-8、FCN-16 和 FCN-32 的全部潜力!

FCN-编码器:VGG16 迁移学习

VGG16(Visual Geometry Group 16)是一种著名的卷积神经网络,以其简单性和在图像分类中的高效性而闻名。它拥有统一的结构和 16 层,擅长学习分层特征。在我们的 FCN-编码器中,我们将利用 VGG16 进行迁移学习,利用其预训练权重来增强语义分割任务中的特征提取能力。以下是获取 VGG-16 预训练权重的链接。

随后,我们利用 VGG16 函数基于上述图像创建 VGG16 网络(仅包含卷积部分 conv1、conv2、conv3、conv4 和 conv5)。接着,我们加载下载的权重,并获取 FCN 解码器部分所需的关键层。

下载权重 vgg16_weights_tf_dim_ordering_tf_kernels_notop.h5

权重文件

代码:

复制代码
def VGG_16(image_input):    x = tf.keras.layers.Conv2D(filters=64, kernel_size=(3,3), activation='relu', padding='same', name='conv1-1')(image_input)    x = tf.keras.layers.Conv2D(filters=64, kernel_size=(3,3), activation='relu', padding='same', name='conv1-2')(x)    x = tf.keras.layers.MaxPooling2D(pool_size=(2,2), strides=(2,2), name='max1')(x)    p1 = x    x = tf.keras.layers.Conv2D(filters=128, kernel_size=(3,3), activation='relu', padding='same', name='conv2-1')(x)    x = tf.keras.layers.Conv2D(filters=128, kernel_size=(3,3), activation='relu', padding='same', name='conv2-2')(x)    x = tf.keras.layers.MaxPooling2D(pool_size=(2,2), strides=(2,2), name='max2')(x)    p2 = x    x = tf.keras.layers.Conv2D(filters=256, kernel_size=(3,3), activation='relu', padding='same', name='conv3-1')(x)    x = tf.keras.layers.Conv2D(filters=256, kernel_size=(3,3), activation='relu', padding='same', name='conv3-2')(x)    x = tf.keras.layers.Conv2D(filters=256, kernel_size=(3,3), activation='relu', padding='same', name='conv3-3')(x)    x = tf.keras.layers.MaxPooling2D(pool_size=(2,2), strides=(2,2), name='max3')(x)    p3 = x    x = tf.keras.layers.Conv2D(filters=512, kernel_size=(3,3), activation='relu', padding='same', name='conv4-1')(x)    x = tf.keras.layers.Conv2D(filters=512, kernel_size=(3,3), activation='relu', padding='same', name='conv4-2')(x)    x = tf.keras.layers.Conv2D(filters=512, kernel_size=(3,3), activation='relu', padding='same', name='conv4-3')(x)    x = tf.keras.layers.MaxPooling2D(pool_size=(2,2), strides=(2,2), name='max4')(x)    p4 = x    x = tf.keras.layers.Conv2D(filters=512, kernel_size=(3,3), activation='relu', padding='same', name='conv5-1')(x)    x = tf.keras.layers.Conv2D(filters=512, kernel_size=(3,3), activation='relu', padding='same', name='conv5-2')(x)    x = tf.keras.layers.Conv2D(filters=512, kernel_size=(3,3), activation='relu', padding='same', name='conv5-3')(x)    x = tf.keras.layers.MaxPooling2D(pool_size=(2,2), strides=(2,2), name='max5')(x)    p5 = x    vgg = tf.keras.Model(image_input, p5)    vgg.load_weights('vgg16_weights_tf_dim_ordering_tf_kernels_notop.h5')    return (p1, p2, p3, p4, p5)

FCN-8-解码器:上采样

fcn8_decoder 函数利用 Conv2DTranspose 进行上采样。此操作对于增加特征图的空间分辨率至关重要。与通过步幅(stride)降低分辨率的传统卷积不同,Conv2DTranspose 使用步幅来提高分辨率。

简单来说,它"填充"了现有像素之间的间隙,有效地扩大了图像尺寸。这在语义分割中至关重要,因为它有助于恢复在编码器下采样过程中丢失的精细细节。

在代码 o = tf.keras.layers.Conv2DTranspose(n_classes , kernel_size=(4,4) , strides=(2,2) , use_bias=False)(f5) 中,转置卷积操作使空间维度加倍,随后的操作进一步优化了分割图。

总而言之,Conv2DTranspose 通过扩大特征图的分辨率,成为了在语义分割中重建详细信息的关键。

复制代码
def fcn8_decoder(convs, n_classes):    """    FCN-8解码器:将编码器特征图逐步上采样并与浅层特征融合    参数:        convs: 编码器的五个特征图 [f1, f2, f3, f4, p5]        n_classes: 分割类别数    """    f1, f2, f3, f4, p5 = convs    n = 4096  # 卷积层的通道数    # 对编码器输出进行两次卷积    c6 = tf.keras.layers.Conv2D(n, (7, 7), activation='relu', padding='same', name="conv6")(p5)    c7 = tf.keras.layers.Conv2D(n, (1, 1), activation='relu', padding='same', name="conv7")(c6)    f5 = c7    # 对编码器输出进行2倍上采样,并裁剪多余的像素    o = tf.keras.layers.Conv2DTranspose(n_classes, kernel_size=(4, 4), strides=(2, 2), use_bias=False)(f5)    o = tf.keras.layers.Cropping2D(cropping=(1, 1))(o)    # 处理pool4特征:通过1x1卷积调整通道数与上采样结果一致    o2 = f4    o2 = tf.keras.layers.Conv2D(n_classes, (1, 1), activation='relu', padding='same')(o2)    # 将上采样结果与pool4预测结果相加(特征融合)    o = tf.keras.layers.Add()([o, o2])    # 对融合结果进行2倍上采样    o = tf.keras.layers.Conv2DTranspose(n_classes, kernel_size=(4, 4), strides=(2, 2), use_bias=False)(o)    o = tf.keras.layers.Cropping2D(cropping=(1, 1))(o)    # 处理pool3特征:通过1x1卷积调整通道数    o2 = f3    o2 = tf.keras.layers.Conv2D(n_classes, (1, 1), activation='relu', padding='same')(o2)    # 将上采样结果与pool3预测结果相加(特征融合)    o = tf.keras.layers.Add()([o, o2])    # 最终8倍上采样,恢复到原始图像尺寸    o = tf.keras.layers.Conv2DTranspose(n_classes, kernel_size=(8, 8), strides=(8, 8), use_bias=False)(o)    # 添加softmax激活函数获取类别概率    o = tf.keras.layers.Activation('softmax')(o)    return o

FCN-16-解码器:上采样

FCN-16s 架构结合了最终层和步幅为 16 的 pool4 层的预测结果。这使得网络能够在保留最终层高层语义信息的同时,利用来自 pool4 的高分辨率信息来预测更精细的细节。它在细节信息和语义上下文之间取得了平衡。

复制代码
def fcn16_decoder(convs, n_classes):    """    FCN-16解码器:将编码器特征图与中间特征融合,直接上采样到原始尺寸    参数:        convs: 编码器的五个特征图 [f1, f2, f3, f4, p5]        n_classes: 分割类别数    """    f1, f2, f3, f4, p5 = convs    n = 4096  # 卷积层的通道数    # 对编码器输出进行两次卷积    c6 = tf.keras.layers.Conv2D(n, (7, 7), activation='relu', padding='same', name="conv6")(p5)    c7 = tf.keras.layers.Conv2D(n, (1, 1), activation='relu', padding='same', name="conv7")(c6)    f5 = c7    # 对编码器输出进行2倍上采样,并裁剪多余的像素    o = tf.keras.layers.Conv2DTranspose(n_classes, kernel_size=(4, 4), strides=(2, 2), use_bias=False)(f5)    o = tf.keras.layers.Cropping2D(cropping=(1, 1))(o)    # 处理pool4特征:通过1x1卷积调整通道数与上采样结果一致    o2 = f4    o2 = tf.keras.layers.Conv2D(n_classes, (1, 1), activation='relu', padding='same')(o2)    # 将上采样结果与pool4预测结果相加(特征融合)    o = tf.keras.layers.Add()([o, o2])    # 直接16倍上采样,恢复到原始图像尺寸    o = tf.keras.layers.Conv2DTranspose(n_classes, kernel_size=(8, 8), strides=(16, 16), use_bias=False)(o)    # 添加softmax激活函数获取类别概率    o = tf.keras.layers.Activation('softmax')(o)    return o

FCN-32-解码器:上采样

在 FCN-32s 中,单流网络直接通过步幅为 32 的上采样,一步到位地将预测结果还原到像素级别。这种方法简化了上采样过程,为每个像素提供了整体的预测。

复制代码
def fcn32_decoder(convs, n_classes):    """    FCN-32解码器:直接将编码器特征图上采样到原始尺寸    参数:        convs: 编码器的五个特征图 [f1, f2, f3, f4, p5]        n_classes: 分割类别数    """    f1, f2, f3, f4, p5 = convs    n = 4096  # 卷积层的通道数    # 对编码器输出进行两次卷积    c6 = tf.keras.layers.Conv2D(n, (7, 7), activation='relu', padding='same', name="conv6")(p5)    c7 = tf.keras.layers.Conv2D(n, (1, 1), activation='relu', padding='same', name="conv7")(c6)    f5 = c7    # 直接32倍上采样,恢复到原始图像尺寸    o = tf.keras.layers.Conv2DTranspose(n_classes, kernel_size=(8, 8), strides=(32, 32), use_bias=False)(f5)    # 添加softmax激活函数获取类别概率    o = tf.keras.layers.Activation('softmax')(o)    return o

在上采样的背景下,步幅(stride)至关重要,因为它决定了采样点之间的间距,从而影响输出尺寸。另一方面,卷积核大小(kernel size)影响滤波器的大小以及每个单元收集的信息,但在上采样期间它并不直接影响输出的空间分辨率。选择合适的步幅是控制网络在像素级别预测精细程度的关键。

计算掩码评估指标

该函数计算预测掩码与真实标签掩码之间的两个核心指标------交并比(Intersection over Union, IOU)和 Dice 系数(Dice Score)。这些指标由以下公式表示:

为了方便您使用,提供的代码会自动执行此计算。此外,我们在分母中加入了一个微小的平滑因子,以防止可能出现的除零情况。

复制代码
def IOU_diceScore(y_true, y_pred):    class_wise_iou = []    class_wise_dice_score = []    smoothening_factor = 0.00001    for i in range(12):        intersection = np.sum((y_pred==i) * (y_true==i))        y_true_area = np.sum((y_true==1))        y_pred_area = np.sum((y_pred==i))        combined_area = y_true_area + y_pred_area        iou = (intersection + smoothening_factor) / (combined_area-intersection+smoothening_factor)        class_wise_iou.append(iou)        dice_score = 2 * ((intersection+smoothening_factor)) / (combined_area + smoothening_factor)        class_wise_dice_score.append(dice_score)    return class_wise_iou, class_wise_dice_score
结语

在结束对 TensorFlow 语义分割的探索时,我们涵盖了基础知识,解析了图像分割的类型,并深入探讨了 FCN 的高层架构。文章剖析了 FCN 的不同变体,强调了根据项目需求在 FCN-8、FCN-16 和 FCN-32 之间进行战略性选择的重要性。

通过引入迁移学习(特别是使用 VGG16 作为编码器),我们的 FCN 模型得到了增强,这不仅提升了训练效率和准确性,尤其在标记数据有限的情况下更是如此。

我们对 FCN-8、FCN-16 和 FCN-32 解码器的详细剖析,加上用于模型评估的 IOU 和 Dice Score 等指标,使读者能够有效地实现这些架构。

无论您是初学者还是经验丰富的开发者,本文都提供了一份简洁的指南,包含清晰的代码片段和动手实践环节。要获取所有的实现代码和数据预处理细节,请私信我。

什么是无监督学习?理解人工智能中无监督学习的机制、各类算法的类型与应用

相关推荐
cookqq2 小时前
基于Spring AI+本地大模型+MongoDB实现私密化与记忆能力-企业级免费大模型应用
人工智能·mongodb·spring
云卓SKYDROID2 小时前
无人机飞行模式详解
人工智能·无人机·高科技·云卓科技·技术解析、
数字游民95272 小时前
小程序上新,猜对了么更新110组素材
人工智能·ai·小程序·ai绘画·自媒体·数字游民9527
泰迪智能科技2 小时前
分享|联合编写教材入选第二批“十四五”职业教育国家规划教材名单
大数据·人工智能
模型时代3 小时前
热力学计算技术或将大幅降低AI图像生成能耗
人工智能
企业老板ai培训3 小时前
从九尾狐AI实战案例拆解AI短视频获客的架构设计:智能矩阵如何提升企业效率?
人工智能
龙腾AI白云3 小时前
知识图谱如何在制造业实际落地应用
人工智能·知识图谱
力学与人工智能3 小时前
“高雷诺数湍流数据库的构建及湍流机器学习集成研究”湍流重大研究计划集成项目顺利结题
数据库·人工智能·机器学习·高雷诺数·湍流·重大研究计划·项目结题
娟宝宝萌萌哒3 小时前
智能体设计模式重点
人工智能·设计模式