news 2026/9/18 14:01:38

注意力机制如何赋能街景语义分割?从SE到CBAM实践

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
注意力机制如何赋能街景语义分割?从SE到CBAM实践

简介:面向自动驾驶感知中的街景语义分割任务,提出基于注意力机制的改进方法,聚焦现有模型分割精度不足与参数量大的痛点。文档从问题分析入手,给出残差网络提取特征、空间注意力模块与通道注意力模块并行细化特征图的设计,并阐述两个模块让网络关注信息丰富区域和通道、增强表示能力的作用;通过Cityscapes和CamVid数据集实验,验证了该方法在较少参数下可获得较好的分割效果,也讨论了其在自动驾驶、医学图像分析等场景的应用价值。文档共1个docx文件,约367KB,涵盖方法原理、网络结构对比、注意力模块设计、实验验证及应用分析,适合计算机视觉与自动驾驶方向的学生、算法工程师阅读。目前已有240人学习,建议下载后用阅读器批注对照学习,便于快速掌握注意力语义分割的技术思路与实验设计。

1. 注意力机制为什么是街景分割的破局点

做街景图像语义分割的人,大概率都遇到过同一个问题:模型在公开数据集上精度不错,一到自己采集的车载或路侧数据上,就把停在树荫下的深色车辆连成一片,把远处的人行道和马路牙子混在一起。这类错误不是卷积核不够多,而是特征表达缺少“选择性”。街景场景的难点在于目标尺度跨度极大、类别分布极不均衡、光照和遮挡随时变化,单纯堆深网络或增大感受野,收益会迅速衰减。注意力机制解决的就是这个“选择性”问题:让网络自己决定该看哪里、该强调哪个通道、该忽略哪些背景干扰。

语义分割本质上是逐像素的分类任务,它比目标检测更依赖空间细节和上下文信息的结合。注意力机制在其中的角色不是替代卷积,而是对卷积提取的特征做重标定和动态选择。无论是 SE 模块对通道维度的全局建模,还是 CBAM 在通道和空间两个维度上的协同筛选,本质都是引入一个可学习的“权重场”,让网络在训练过程中学会聚焦。这正好对上了街景分割的痛点:车辆、行人、交通标志这些前景目标需要高分辨率细节,而天空、道路、建筑这些背景类别需要大范围上下文,注意力机制可以在同一套特征里兼顾两者。

这篇文章从注意力机制的三种典型范式讲起,落到街景分割的模型选型和代码实现,再给出一套完整的训练和推理方案。涉及的具体内容包括:SE、CBAM、自注意力在分割任务中的适配方式,Cityscapes 数据集上的训练配置,损失函数和数据增强的搭配策略,以及多尺度推理和模型导出时需要注意的精度问题。适合正在做自动驾驶感知、智慧城市或安防监控方向分割任务的工程师,也适合想把注意力机制真正用进分割项目的研究生和算法从业者。

2. 通道注意力与空间注意力:从 SE 到 CBAM 的演进逻辑

2.1 SE 模块:全局平均池化为什么能改善街景分割

SE 模块是通道注意力的开山之作。它的核心操作不复杂:对输入特征图做全局平均池化,得到一个通道描述向量,再经过两个全连接层(中间带降维)得到每个通道的权重,最后与原特征图逐通道相乘。放到街景分割的场景里理解:一张街景图中,道路和天空往往占据大片区域,而行人、车辆只占少量像素。普通卷积对每个通道的重视程度是均等的,这就导致模型把大量表达能力浪费在背景通道上。SE 模块通过全局平均池化统计每个通道的响应强度,相当于让网络知道“哪些通道对当前这张图的分类更重要”。

但 SE 有一个在分割任务中容易被忽视的问题:全局平均池化会把空间信息压缩成一个点,对街景这种强空间依赖的任务来说,某些通道的判别信息可能只出现在图像的局部区域。比如“车道线”这个类别,它的通道响应只在图像下半部分显著,全局池化会把这种局部响应稀释掉。因此,在街景分割中直接叠加 SE 模块,效果往往不如在检测任务中明显。常见的做法是把 SE 模块放在 Backbone 的每个 Stage 之后,而不是放在解码器的最后,这样可以在不下采样太狠的情况下保留空间信息。

import torch import torch.nn as nn class SEBlock(nn.Module): def __init__(self, channels, reduction=16): super().__init__() self.squeeze = nn.AdaptiveAvgPool2d(1) self.excitation = nn.Sequential( nn.Linear(channels, channels // reduction, bias=False), nn.ReLU(inplace=True), nn.Linear(channels // reduction, channels, bias=False), nn.Sigmoid() ) def forward(self, x): b, c, h, w = x.size() y = self.squeeze(x).view(b, c) y = self.excitation(y).view(b, c, 1, 1) return x * y.expand_as(x)

这段代码里,reduction是降维比例,默认 16,控制中间全连接层的宽度。降维比例越大,新增参数量越少,但通道间的非线性拟合能力也越弱。在轻量级分割网络里,reduction设为 8 或 4 更稳妥,因为 Backbone 本身较窄,强行降到 1/16 会让通道描述向量丢失太多信息。Sigmoid输出的权重在 0 到 1 之间,可以被理解为“软门控”——不是直接丢弃某些通道,而是压低它们的贡献,这对保持训练的稳定性是有益的。

2.2 CBAM 模块:通道和空间注意力如何协同筛选特征

CBAM 在 SE 的基础上补上了空间维度。它的结构是串行的:先过通道注意力,再过空间注意力。通道注意力和 SE 类似,但把池化方式从单一的平均池化改成了平均池化与最大池化并行,两者得到的特征向量相加后再过共享的全连接层。最大池化捕捉的是通道里响应最强的位置,平均池化捕捉的是整体统计水平,两者互补,对街景中目标尺度差异大的场景尤其有用——小目标(如远处的行人)在最大池化下更容易被保留。

空间注意力部分的做法是:把通道注意力输出的特征图沿通道维度分别做平均池化和最大池化,得到两个二维特征图,拼在一起后过一个 7×7 的卷积,再用 Sigmoid 生成空间权重图。这个空间权重图可以理解为“告诉网络哪些像素位置值得重点关注”。在街景图像里,它通常会学习到:路面上方的区域权重更高,因为那里集中了行人、车辆、交通标志等前景目标;而天空区域即使面积大,权重也不会太高。

class SpatialAttention(nn.Module): def __init__(self, kernel_size=7): super().__init__() self.conv = nn.Conv2d(2, 1, kernel_size, padding=kernel_size // 2, bias=False) self.sigmoid = nn.Sigmoid() def forward(self, x): avg_out = torch.mean(x, dim=1, keepdim=True) max_out, _ = torch.max(x, dim=1, keepdim=True) x_cat = torch.cat([avg_out, max_out], dim=1) return self.sigmoid(self.conv(x_cat))

kernel_size的选择对空间注意力的效果有明显影响。7×7 覆盖范围大,适合捕捉街景中的大目标(如公交车、建筑立面);如果场景里小目标多,可以改用 3×3,减少周围像素对中心像素判断的干扰。有一点需要说明:空间注意力是在全图范围内生成一张权重图,不会区分实例。两个紧挨着的行人,如果它们的颜色和纹理相似,空间注意力可能把它们当做一个整体来看待,这是语义分割本身的边界问题,不属于注意力机制的缺陷。

2.3 自注意力与坐标注意力的场景适配

SE 关注通道、CBAM 兼顾通道与空间,但它们依然是局部算子,感受野受卷积核大小限制。自注意力机制通过计算特征图上任意两个位置之间的相似度来建模长距离依赖,这对街景分割中的“大尺度上下文”任务非常关键——比如判断一块区域到底是“道路”还是“停车场”,需要看到更远的周围环境才能下结论。

自注意力在分割中的典型用法是 Non-local 模块和轴向注意力。Non-local 计算量太大,输入 512×1024 的街景图时,特征图 1/8 分辨率也有 64×128=8192 个位置,两两计算相似度会直接把显存放爆。常见的缓解手段是先在通道维度降维(比如压缩到 512 或 256),再在空间维度做采样或分块。轴向注意力把二维注意力拆成行方向和列方向两次一维注意力,计算量从 O(N²) 降到 O(N^1.5),在 DeepLab 系列和 SegFormer 里都有变体实现。

坐标注意力(Coordinate Attention)是另一个值得关注的思路。它把位置信息编码进通道注意力:分别对特征图的每一行和每一列做池化,得到一对方向感知的特征向量,拼接后过卷积和激活函数,再切回两个方向分别生成注意力权重。这样做的好处是,模型不仅能知道“哪些通道重要”,还能知道“这些通道在水平和垂直方向上各自该关注哪里”。对街景来说,这特别适合处理“天空永远在图像上半部分、路面永远在下半部分”这类强先验位置信息。

3. 街景分割的数据准备与模型选型

3.1 Cityscapes 数据集的类别分布与标签处理

街景分割绕不开 Cityscapes,它是目前评估自动驾驶语义分割算法最常用的基准数据集之一。Cityscapes 提供 5000 张精细标注图像,分辨率是 2048×1024,覆盖 50 个城市的街道场景。标注类别有 34 类,但官方标准评测只使用其中 19 类,其余归为 ignore 类别。这 19 类包括:道路、人行道、建筑、围墙、篱笆、电线杆、交通信号灯、交通标志、植被、地形、天空、行人、骑手、汽车、卡车、公交车、火车、摩托车、自行车。

类别分布极度不均衡是第一个要面对的问题。以像素占比计算,道路在训练集里可能占到接近 30% 的面积,而摩托车、火车这类类别往往只有几百个标注实例。如果直接用 CrossEntropyLoss 训练,模型会倾向把所有像素都预测为高频类别,导致那些小类别完全学不出来。处理方式有两种:一是用类别权重的中位数频率平衡策略,给低频类别更高的损失权重;二是在数据加载阶段做类别采样,确保每个 batch 里都能看到低频类别的样本。

另一个问题是标签的原始格式。Cityscapes 官方提供的是彩色标注图,每种类别对应一个固定的 RGB 三元组。训练前必须做 color-to-id 映射,把 19 类映射到 0-18 的连续整数,其他像素设为 255(忽略索引)。这个映射表写错一个值,mIoU 就会莫名其妙掉几个点,而且很难排查。我的做法是写一个独立脚本来验证映射结果——把映射后的 id 图像重新转回彩色图,和原始标签做逐像素对比,确认一致再进入训练流程。

import os import numpy as np from PIL import Image # Cityscapes 19 类对应的 RGB 值 cityscapes_classes = [ (128, 64, 128), (244, 35, 232), (70, 70, 70), (102, 102, 156), (190, 153, 153), (153, 153, 153), (250, 170, 30), (220, 220, 0), (107, 142, 35), (152, 251, 152), (70, 130, 180), (220, 20, 60), (255, 0, 0), (0, 0, 142), (0, 0, 70), (0, 60, 100), (0, 80, 100), (0, 0, 230), (119, 11, 32) ] def rgb_to_train_id(mask_rgb): h, w, _ = mask_rgb.shape train_id = np.full((h, w), 255, dtype=np.uint8) for idx, rgb in enumerate(cityscapes_classes): match = (mask_rgb == np.array(rgb)).all(axis=-1) train_id[match] = idx return train_id

这段代码遍历 19 个类别的 RGB 值,把匹配到的像素赋予对应的训练 id。注意要在训练前统一确认类别顺序,不能直接使用官方labelIds图像,因为它的索引是稀疏的(不是从 0 连续排列)。255作为忽略索引,在损失计算时需要显式排除。映射完成后的标签图建议保存成 PNG 格式,避免 JPEG 压缩带来的边缘颜色混叠。

3.2 分割模型的选型对比:DeepLabV3+、PSPNet 与注意力变体

注意力机制要发挥作用,必须挂在一个合理的分割骨架上。三个经常被拿出来对比的模型是 DeepLabV3+、PSPNet 和带注意力模块的 U-Net 变体。DeepLabV3+ 使用空洞卷积结合 ASPP 模块,通过多个不同膨胀率的并行卷积捕捉多尺度上下文,解码器部分恢复空间细节。它是目前街景分割任务中精度和显存消耗比较均衡的选择。ASPP 里的各个分支可以理解为一种手工设计的“注意力”——每个膨胀率关注不同尺度的上下文,但它们之间没有交互权重,这是可以加注意力机制的地方。

PSPNet 的特色是金字塔池化模块,把特征图划分成不同尺寸的网格,每个格子内做池化,相当于从 1×1 到 6×6 的区域级上下文建模。它的问题是边界细节恢复不足,因为池化操作天然丢失位置信息。给 PSPNet 的每个金字塔层加上通道注意力(类似 SE)是常见改进,让每个池化尺度学习不同的通道侧重。U-Net 的跳跃连接在街景中也很实用,它把编码器的高分辨率特征直接传给解码器,解决了深层次特征空间分辨率不足的问题。在 U-Net 的跳跃连接处加空间注意力,可以让模型选择性地传递与前景目标相关的特征图,过滤掉背景噪声。

模型上下文建模方式优点街景适配建议
DeepLabV3+ASPP 多膨胀率并行卷积多尺度能力强,边界较细在 ASPP 各分支后加 SE 或协调注意力
PSPNet金字塔池化多尺度特征区域上下文特征丰富每个池化层加通道注意力,缓解细节损失
U-Net + 注意力编码器-解码器跳跃连接高分辨率细节保留好跳跃连接处加空间注意力或 CBAM

做实际项目时,我的选择标准是:如果目标是刷 Cityscapes 的 SOTA,直接上 DeepLabV3+ 配合 ResNet-101 或 Swin Transformer Backbone,再在 ASPP 输出后加一层坐标注意力;如果目标是车载嵌入式设备上的实时推理,用 MobileNetV2 或 MobileNetV3 做 Backbone,配上轻量的 SE 模块,比堆空间注意力更划算。

3.3 带 CBAM 的 DeepLabV3+ 网络实现

这里给出一段可以直接拼接的 PyTorch 代码,实现在 DeepLabV3+ 的 ASPP 模块输出后接 CBAM 模块。骨架使用 ResNet-101 的预训练权重,输入的街景图像尺寸统一缩放到 512×1024。

import torch import torch.nn as nn import torchvision.models as models class CBAM(nn.Module): def __init__(self, channels, reduction=16, spatial_kernel=7): super().__init__() self.channel_att = ChannelAttention(channels, reduction) self.spatial_att = SpatialAttention(spatial_kernel) def forward(self, x): x = self.channel_att(x) * x x = self.spatial_att(x) * x return x class ASPPWithCBAM(nn.Module): def __init__(self, in_channels, out_channels=256): super().__init__() self.convs = nn.ModuleList() for dilation in [1, 6, 12, 18]: if dilation == 1: self.convs.append(nn.Sequential( nn.Conv2d(in_channels, out_channels, 1, bias=False), nn.BatchNorm2d(out_channels), nn.ReLU(inplace=True) )) else: self.convs.append(nn.Sequential( nn.Conv2d(in_channels, out_channels, 3, padding=dilation, dilation=dilation, bias=False), nn.BatchNorm2d(out_channels), nn.ReLU(inplace=True) )) self.image_pool = nn.Sequential( nn.AdaptiveAvgPool2d(1), nn.Conv2d(in_channels, out_channels, 1, bias=False), nn.ReLU(inplace=True) ) self.cbam = CBAM(out_channels * 5) self.project = nn.Conv2d(out_channels * 5, out_channels, 1, bias=False) def forward(self, x): size = x.shape[-2:] conv_results = [conv(x) for conv in self.convs] image_feat = self.image_pool(x) image_feat = nn.functional.interpolate(image_feat, size=size, mode='bilinear', align_corners=False) conv_results.append(image_feat) feat = torch.cat(conv_results, dim=1) feat = self.cbam(feat) return self.project(feat)

ASPPWithCBAM里的四个膨胀卷积分别处理 1、6、12、18 的感受野,覆盖从局部细节到大范围上下文的不同尺度。把它们的结果拼起来后先过 CBAM 再做投影,是让注意力机制在整个多尺度特征融合后的层面上发挥作用,而不是对每个分支单独加权——后者会破坏分支之间的互补性。image_pool对应 ASPP 的图像级特征分支,使用全局池化再上采样,补充最全局的上下文信息。CBAM 的输入通道是out_channels * 5,因为五个分支(四个膨胀卷积加图像池化)拼接后通道数翻倍。如果显存吃紧,可以在拼接前先对各分支分别投影到较低维度,再拼接。

4. 训练配置、损失函数和关键参数调优

4.1 损失函数组合:交叉熵与 Dice Loss 的搭配策略

街景分割的类别不均衡问题,决定了单纯使用交叉熵很难达到理想效果。交叉熵对每个像素独立计算损失,没有考虑类别间像素数量的悬殊差异。一个常见的改进是使用带权重的交叉熵,即根据每个类别在训练集中出现的频率计算权重,出现频率越低的类别权重越高。这种方法的缺点是权重是静态的,训练过程中模型对各类别的预测能力发生变化后,权重不能自适应调整。

Dice Loss 的思路完全不同。它直接优化预测结果和真实标签的重叠度,本质上是在衡量两个集合的相似程度,对类别不均衡相对不敏感。在街景分割中,Dice Loss 对“火车”“摩托车”这类稀有类别的学习帮助明显,但单独使用 Dice Loss 会导致训练不稳定,尤其是在训练初期,梯度信号可能过于剧烈。推荐的做法是把带权重的交叉熵和 Dice Loss 按比例相加,例如 0.6 的交叉熵加 0.4 的 Dice Loss。公式为:TotalLoss = 0.6 * CE + 0.4 * Dice

import torch.nn.functional as F def combined_loss(pred, target, class_weights, alpha=0.6, smooth=1.0): ce_loss = F.cross_entropy(pred, target, weight=class_weights, ignore_index=255) pred_prob = F.softmax(pred, dim=1) target_onehot = F.one_hot(target, num_classes=pred.size(1)) target_onehot = target_onehot.permute(0, 3, 1, 2).float() intersection = (pred_prob * target_onehot).sum(dim=(0, 2, 3)) union = pred_prob.sum(dim=(0, 2, 3)) + target_onehot.sum(dim=(0, 2, 3)) dice = (2.0 * intersection + smooth) / (union + smooth) dice_loss = 1.0 - dice.mean() return alpha * ce_loss + (1 - alpha) * dice_loss

class_weights是一个长度为类别数的张量,可以按中位数频率平衡策略计算。alpha控制两种损失的比例,如果某个类别的像素占比极低,可以适当提高alpha让交叉熵权重更多,或者提高 Dice 占比让网络更关注稀有类别。smooth是平滑项,防止分母为零,一般设为 1.0。ignore_index=255表示标签中该值对应的像素不参与损失计算,这种做法在语义分割中必须保留,否则背景像素会被当作一个额外类别学进去,干扰模型。

4.2 数据增强策略:街景特有的几何与颜色扰动

街景图像的分布相对固定——视角都是前视或环视,道路在下方,天空在上方。但真实道路场景的复杂程度远超数据集,训练时做合适的数据增强能让模型适应更多光照和天气条件。颜色扰动方面,随机调整亮度、对比度、饱和度是标配,但幅度要控制在合理范围内。街景图像如果过度提高对比度,会让道路和建筑物的纹理失真,反而影响模型对真实场景的泛化能力。

几何增强方面,随机水平翻转几乎可以无脑加上,因为城市道路虽然没有严格左右对称,但行人、车辆、建筑在左右方向上的形态是相似的。随机缩放作为多尺度训练的增强方式也很有用,裁出一块区域后缩放到固定尺寸,相当于人为制造了目标尺度的变化——这对街景中“近处大目标、远处小目标”的现实情况非常契合。需要注意的是,语义分割的标签是逐像素的,做几何变换时标签和图像必须使用完全相同的变换参数,不能对图像和标签分别做随机的不同变换。

from albumentations import Compose, HorizontalFlip, RandomBrightnessContrast, RandomScale, RandomCrop train_transform = Compose([ RandomScale(scale_limit=0.5, p=0.5), RandomCrop(height=512, width=1024), HorizontalFlip(p=0.5), RandomBrightnessContrast(brightness_limit=0.2, contrast_limit=0.2, p=0.5) ])

RandomScalescale_limit设为 0.5,意味着图像可以在原始尺寸的 0.5 倍到 1.5 倍之间随机缩放。缩放后必须接一个RandomCrop,把图像裁回训练尺寸,保证 batch 内张量形状一致。如果你的显存足够,height=512, width=1024可以提升到768×1536或直接用原始分辨率 1024×2048,但要注意,高分辨率输入配合注意力模块会显著增加显存消耗。裁剪区域最好集中在图像的下半部分和中间部分,这样可以减少把大量天空裁进来导致的训练效率下降。

4.3 训练参数配置与 mIoU 监控实验

训练一个街景分割模型通常需要 80 到 160 个 epoch,具体取决于 Backbone 的规模和预训练权重的情况。使用 ResNet-101 做 Backbone 时,建议初始学习率为 0.01,使用多项式衰减策略,power 设为 0.9,weight decay 设为 0.0001,momentum 设为 0.9。优化器选择 SGD 比 Adam 更稳妥,因为分割任务中自适应学习率的优化器在小 batch 下容易产生较大的梯度波动。batch size 设置为 8,单卡训练时如果显存不够(例如 RTX 3090 24GB),可以把输入尺寸缩到 512×1024,或者开启梯度累积,每 4 个 batch 累计一次梯度。

训练过程中需要重点监控的指标有三个:mIoU(平均交并比)、类别 IoU、以及验证集上的损失曲线。mIoU 是所有类别 IoU 的算术平均值,这个数字只能反映整体精度,不能体现某几个特定类别的好坏。假设 mIoU 到了 75%,但你的场景最关心“行人”和“骑手”两类,你单独看这两类的 IoU,如果低于 60%,就需要针对性地调整——比如提高这两个类别在损失函数里的权重,或在数据加载时对包含这两类目标的训练图像做上采样。以下是一个简化的训练循环示意:

python train.py \ --backbone resnet101 \ --model deeplabv3plus \ --dataset cityscapes \ --train-size 512 1024 \ --crop-size 512 1024 \ --lr 0.01 \ --epochs 120 \ --batch-size 8 \ --loss ce+dice \ --val-every 4

这段命令行展示了典型的分割训练配置。--lr 0.01是多项式衰减下的初始学习率,如果你的 batch size 更小,学习率也应该相应调低。--val-every 4表示每 4 个 epoch 在验证集上评估一次,观察 mIoU 是否还在上升。当验证集 mIoU 连续 8 个 epoch 没有提升时,应该停止训练或把学习率降一个数量级,避免过拟合。训练日志里如果发现验证损失在下降但 mIoU 停滞,通常意味着模型对高频类别(如道路、建筑)的预测没什么问题,但对低频类别的边界处理不好,这时要检查损失权重和数据增强的搭配是否合理。

5. 推理优化、模型部署与精度验证技巧

5.1 多尺度推理与 TTA 对 mIoU 的提昇逻辑

训练完成后,推理阶段还可以继续利用多尺度信息提升精度。街景图像中目标尺度变化极大,单尺度推理往往对过小或过大的目标不够稳定。多尺度推理的做法是:把输入图像分别缩放到多个尺寸(比如 0.5 倍、1.0 倍、1.5 倍),分别送入模型得到预测结果,再把所有结果上采样到统一分辨率,取平均值或加权平均值作为最终预测。在 Cityscapes 验证集上,这种方法通常可以提升 1 到 2 个百分点的 mIoU,对“行人”“自行车”这类小目标尤其明显。

实现多尺度推理时要注意一个细节:缩放倍数越大,GPU 显存占用越高。如果 1.5 倍输入在验证时导致显存溢出,可以分批次推理,逐个尺度计算后把概率图累加,而不是一次性把所有尺度的输入拼在同一个 batch 里。另外,对预测概率求平均后,取 argmax 得到最终的类别索引,这一步使用的是浮点概率的平均值而不是直接对类别索引做投票,后者会把每个尺度的置信度信息丢失。

5.2 导出 ONNX 与半精度推理的精度对比

部署场景下,模型导出和推理加速是绕不开的环节。PyTorch 模型需要先导出为 ONNX 格式,再转换为 TensorRT 或 OpenVINO 的推理引擎。导出时最大的坑是动态尺寸问题。街景图像的分辨率比较固定,如果视频流的输入分辨率不变,直接使用固定尺寸导出会减少很多麻烦。如果希望模型能适应不同大小的输入,则需要把 ONNX 的动态轴打开,这会导致 TensorRT 在构建 engine 时需要额外的 profile 设置。

半精度推理在 NVIDIA 显卡上可以显著提速,但精度会有轻微损失。实测经验是,FP16 推理对语义分割的 mIoU 影响通常在 0.2 到 0.5 个百分点之间,类别边界处可能出现略微粗糙的预测结果。如果你的场景对安全要求很高(比如自动驾驶),建议先在验证集上做一次 FP16 和 FP32 的对比测试。hallucination 风险在注意力机制模型上略高,因为注意力权重在低精度下可能产生更大的数值波动。导出 ONNX 的基本命令如下:

python export_onnx.py \ --checkpoint best_model.pth \ --backbone resnet101 \ --input-size 1 3 512 1024 \ --output deeplabv3plus_cbam.onnx

导出脚本内部要做的事情包括:加载权重、切换到 eval 模式、构造一个形状为(1, 3, 512, 1024)的随机输入、调用torch.onnx.export并设置opset_version=11。导出的 ONNX 模型建议用onnxruntime跑一遍相同的验证集数据,对比 PyTorch 原模型的输出误差。最大误差超过 1e-3 通常说明某个算子导出有问题,最常见的是双线性插值里的align_corners参数不兼容,需要在导出前修正。

5.3 验证预测结果的三个检查点

模型部署到实际场景前,用一组多样化的街景图像做目视检查比只看 mIoU 数字更有用。第一个检查点是远处的细小目标:电线杆、交通标志、行人。在 512 分辨率下,这些目标往往只有几个像素宽,模型容易把它们归入背景。把预测结果放大 200% 仔细观察这些区域的边界是否连续,如果边界断裂严重,说明解码器的细节恢复不够,可以尝试在损失函数中加入边界感知损失或提高输入分辨率。

第二个检查点是类别混淆:人行道和道路、植被和地形这两对类别在 Cityscapes 里最容易互相混淆。它们的颜色相近、纹理相似,注意力机制如果过于关注全局上下文,反而可能忽略局部纹理差异。如果验证集中出现系统性的人行道误判为道路,一个可行的方案是在空间注意力图上叠加一个针对下半区域的先验 mask,强制模型更关注底部区域。第三个检查点是光照急剧变化的场景,比如逆光或夜晚。如果预测结果在强光照下出现大面积错误,说明训练数据里缺少类似样本,应该补充数据或做更强的颜色扰动,而不是继续调整模型结构。

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/18 14:01:31

Objx 详解:用 Go 优雅读写 map、slice 与 JSON 数据的实用指南

Objx 详解:用 Go 优雅读写 map、slice 与 JSON 数据的实用指南 【免费下载链接】karmada Open, Multi-Cloud, Multi-Cluster Kubernetes Orchestration 项目地址: https://gitcode.com/GitHub_Trending/ka/karmada Objx 是一个专注于简化 map[string]interfa…

作者头像 李华
网站建设 2026/9/18 13:58:16

DNN-GOP3抗噪口语评测:中学生英语发音鲁棒性建模

简介:本资源是一篇聚焦教育智能化落地的学术论文,面向英语教学研究者、语音技术开发者及中高考考试系统建设者,着力解决大规模口语考试中因设备差异、环境噪音与非母语发音特征导致的自动评分不准问题。论文提出两种基于深度神经网络声学模型…

作者头像 李华
网站建设 2026/9/18 13:53:14

Flutter跨平台开发家庭药箱App的健康报告功能实践

1. 项目概述:家庭药箱管理App的健康报告功能作为一名长期从事移动应用开发的工程师,我最近在基于Flutter for OpenHarmony平台开发一款家庭药箱管理应用时,遇到了一个有趣的需求:如何将零散的健康数据转化为有价值的健康洞察&…

作者头像 李华