AI 术语通俗词典:U-Net
U-Net 是深度学习、卷积神经网络、图像分割、医学影像分析和计算机视觉中非常经典的一个模型结构。它用来描述一种以“编码器—解码器”为主体、并通过跳跃连接保留细节信息的卷积神经网络。换句话说U-Net 是在回答模型怎样既理解图像的整体语义又尽量恢复每个像素位置的精细边界。如果说普通图像分类模型主要回答“这张图是什么”那么 U-Net 更关注“图像中每个位置属于什么”。它不是只输出一个类别而是输出一张与输入图像空间位置对应的预测图用于判断每个像素或区域的类别。因此U-Net 常用于医学图像分割、遥感图像分析、缺陷检测、细胞分割、道路提取、语义分割、扩散模型中的噪声预测网络等任务是理解图像分割和编码器—解码器结构的重要基础概念。一、基本概念什么是 U-NetU-Net 是一种典型的图像到图像模型结构。它的输入通常是一张图像输出通常是一张分割结果图。例如在医学影像中• 输入一张 CT / MRI / 显微图像• 输出每个像素属于器官、病灶、细胞或背景的预测结果U-Net 的名称来自它的整体结构形状。它通常由两部分组成• 左侧编码器逐步压缩图像提取高层语义特征• 右侧解码器逐步恢复空间尺寸生成像素级预测结果中间还通过跳跃连接把编码器中的浅层细节特征传给解码器。从结构上看它像一个 U 形输入图像 ↓编码器下采样提取语义 ↓瓶颈层压缩后的高层特征 ↓解码器上采样恢复空间尺寸 ↓输出分割图从通俗角度看U-Net 像一个先“缩小看整体”、再“放大找边界”的图像理解模型。它先通过编码器理解图像中有什么再通过解码器把这种理解恢复到每个像素位置。二、为什么需要 U-NetU-Net 之所以重要是因为图像分割任务不同于图像分类任务。图像分类只需要判断整张图的类别例如这张图是猫还是狗而图像分割需要判断每个像素的类别例如哪些像素属于猫哪些像素属于背景哪些像素属于器官、病灶或道路这类任务对模型提出了两个要求• 需要理解全局语义• 需要保留局部细节如果模型只关注局部纹理可能无法判断某个区域属于什么对象。如果模型只关注高层语义经过多次下采样后又容易丢失边缘、轮廓和小目标细节。U-Net 的价值就在于同时处理这两件事• 编码器负责提取语义信息• 解码器负责恢复空间分辨率• 跳跃连接负责补回细节信息从通俗角度看图像分割既要“看懂图像内容”又要“画准对象边界”。U-Net 正是为这种需求设计的结构。这也是为什么 U-Net 在医学图像分割中非常经典。医学影像中病灶、细胞、血管等目标可能很小边界也很重要。U-Net 通过跳跃连接保留浅层空间细节因此适合这类像素级预测任务。三、U-Net 的整体结构U-Net 的整体结构可以概括为三部分编码器 → 瓶颈层 → 解码器同时编码器和解码器之间有多条跳跃连接。图 1U-Net 整体结构示意图1、编码器逐步提取语义特征编码器位于 U-Net 的左侧。它通常由多组卷积层和下采样操作组成。常见形式是卷积 → 激活函数 → 卷积 → 激活函数 → 下采样随着网络逐层深入特征图的空间尺寸逐渐变小通道数通常逐渐增加。例如输入图像256 × 256 × 3↓特征图128 × 128 × 64↓特征图64 × 64 × 128↓特征图32 × 32 × 256↓特征图16 × 16 × 512从通俗角度看编码器不断压缩图像把局部像素信息逐步变成更抽象的语义信息。2、瓶颈层压缩后的高层表示瓶颈层位于 U-Net 的底部。它连接编码器和解码器是空间尺寸较小、语义信息较强的部分。从通俗角度看瓶颈层像是模型对整张图像的高度概括。它知道图像中大致有什么但空间细节已经被压缩。3、解码器逐步恢复空间尺寸解码器位于 U-Net 的右侧。它通过上采样或转置卷积逐步恢复特征图尺寸。常见形式是上采样 → 拼接跳跃连接特征 → 卷积 → 激活函数例如16 × 16 × 512↓32 × 32 × 256↓64 × 64 × 128↓128 × 128 × 64↓256 × 256 × 类别数从通俗角度看解码器把压缩后的语义信息重新展开使模型能够为每个像素生成预测结果。四、跳跃连接U-Net 的关键设计跳跃连接是 U-Net 最重要的结构特点之一。它会把编码器中某一层的特征直接传给解码器中对应尺度的层。例如编码器中的 128 × 128 特征 ↓跳跃连接 ↓解码器中的 128 × 128 特征这样做的原因是编码器在下采样过程中会丢失一部分空间细节例如边界、纹理和小目标位置。解码器虽然可以恢复空间尺寸但如果只依赖瓶颈层中的高层语义信息细节可能不够准确。跳跃连接可以把浅层特征直接补给解码器。从通俗角度看编码器像是在不断压缩图像。压缩后虽然理解了整体内容但细节丢了。跳跃连接就像把压缩前的局部细节“备份”下来在恢复图像时再拿出来使用。在 U-Net 中跳跃连接通常使用拼接concatenate而不是简单相加。如果解码器某层特征为 A编码器对应层特征为 B那么拼接后可以写为其中• F 表示拼接后的特征• A 表示解码器当前特征• B 表示来自编码器的同尺度特征• Concat 表示沿通道维度拼接例如解码器特征64 × 64 × 128编码器特征64 × 64 × 128拼接后特征64 × 64 × 256跳跃连接让 U-Net 同时拥有• 深层语义信息• 浅层空间细节这正是它适合图像分割的重要原因。五、U-Net 的输出像素级预测U-Net 的输出通常是一张预测图。图 2U-Net 中跳跃连接与像素级预测如果是二分类分割例如前景 / 背景输出可以是H × W × 1表示每个像素属于前景的概率。如果是多类别分割输出可以是H × W × C其中 C 表示类别数。例如有 4 类背景 / 器官 / 病灶 / 其他组织那么每个像素位置都会输出 4 个类别分数。对于输入图像U-Net 的输出可以写为其中• X 表示输入图像• H 表示图像高度• W 表示图像宽度• C_in 表示输入通道数• Y 表示输出预测图• C_out 表示输出类别数对于每个像素位置模型会预测它属于某个类别的概率。二分类分割中常使用 Sigmoid其中• pᵢⱼ 表示位置 (i, j) 属于前景的概率• zᵢⱼ 表示该位置的模型输出分数• σ 表示 Sigmoid 函数多类别分割中常使用 Softmax其中• pᵢⱼc 表示位置 (i, j) 属于第 c 类的概率• zᵢⱼc 表示该位置第 c 类的输出分数• C 表示类别总数从通俗角度看U-Net 不是只给整张图一个答案而是给每个像素位置一个答案。六、U-Net 与图像分割任务U-Net 最经典的应用是图像分割。图像分割可以大致分为几类。1、语义分割语义分割是给每个像素分配语义类别。例如天空 / 道路 / 建筑 / 车辆 / 行人语义分割不区分同一类别中的不同个体。如果图中有两辆车它们都被标成“车”。2、医学图像分割医学图像分割是 U-Net 最重要的应用场景之一。常见任务包括• 器官分割• 肿瘤分割• 细胞分割• 血管分割• 病灶区域检测医学图像分割常常样本数量有限但对边界精度要求较高。U-Net 的编码器—解码器结构和跳跃连接设计正好适合这类任务。3、实例分割与相关任务严格来说标准 U-Net 更常用于语义分割或二值分割。实例分割不仅要判断像素类别还要区分不同个体。例如两辆车不能只都标成“车”还要区分车 1 和车 2。实例分割通常需要更复杂的结构或后处理方法。不过在细胞分割、目标区域提取等任务中U-Net 也经常作为基础分割网络使用。七、U-Net 与普通编码器—解码器的区别U-Net 属于编码器—解码器结构但它不是普通的编码器—解码器。普通编码器—解码器通常是输入 → 编码器 → 压缩表示 → 解码器 → 输出它的问题是中间压缩表示可能丢失大量空间细节。U-Net 在此基础上加入了跳跃连接编码器浅层特征 → 跳跃连接 → 解码器对应层这使得解码器不必只依赖最底部的压缩特征。从通俗角度看普通编码器—解码器像是先把图像压缩成一段摘要再根据摘要重建结果。U-Net 则是在重建时还参考了不同层级的原始细节笔记。因此U-Net 更适合需要精细定位的任务。可以概括为• 普通编码器—解码器重语义容易丢细节• U-Net语义 细节适合像素级预测这也是 U-Net 在分割任务中长期有效的核心原因。八、U-Net 的优势、局限与使用注意事项1、U-Net 的主要优势U-Net 最大的优势是适合像素级预测。它可以输出与输入图像位置对应的分割图适合图像分割任务。其次U-Net 能同时利用语义信息和细节信息。编码器提取深层语义跳跃连接补充浅层细节解码器恢复空间分辨率。再次U-Net 对中小规模数据相对友好。在医学图像等数据较难获取的任务中U-Net 仍然可以作为强有力的基础结构。从通俗角度看U-Net 的优势在于它既能看懂图像中有什么也能尽量画准它在哪里。2、U-Net 的主要局限U-Net 也有局限。首先它主要依赖卷积结构原始版本对全局长距离关系建模能力有限。如果目标之间存在复杂远距离依赖普通 U-Net 可能不够强。其次U-Net 的效果依赖数据标注质量。图像分割需要像素级标注如果标注不准确模型很容易学习到错误边界。再次U-Net 对显存有一定要求。由于跳跃连接需要保留编码器多层特征训练时显存占用可能较高。此外U-Net 并不自动解决类别不平衡问题。在医学分割中病灶区域可能远小于背景区域如果损失函数设计不当模型可能倾向于预测背景。3、使用 U-Net 时需要注意的问题使用 U-Net 时需要注意• 输入图像和标签尺寸要对齐• 下采样次数会影响最小特征图尺寸• 跳跃连接处的特征尺寸需要匹配• 二分类分割常用 Sigmoid 输出• 多类别分割常用 Softmax 输出• 小目标分割要注意类别不平衡• 医学图像常配合 Dice Loss 或 BCE Dice Loss• 数据增强对分割任务非常重要• 预测结果通常需要阈值化或后处理从实践角度看U-Net 是图像分割的基础结构但要取得好效果还需要合适的数据增强、损失函数、评价指标和后处理策略。九、常见损失函数与评价指标图像分割不同于普通分类常需要关注像素级重叠程度。1、二分类交叉熵二分类分割中常用二分类交叉熵损失其中• L_BCE 表示二分类交叉熵损失• n 表示像素数量• yᵢ 表示第 i 个像素的真实标签取值为 0 或 1• pᵢ 表示第 i 个像素预测为前景的概率BCE 适合像素级二分类但在前景区域很小、背景区域很大时可能受到类别不平衡影响。2、Dice 系数Dice 系数常用于衡量预测区域与真实区域的重叠程度其中• P 表示预测区域• G 表示真实标注区域• |P ∩ G| 表示预测区域和真实区域的交集大小• |P| 和 |G| 分别表示预测区域和真实区域大小Dice 越接近 1说明预测越接近真实标注。3、Dice Loss训练时常使用 Dice Loss其中• L_Dice 表示 Dice 损失• pᵢ 表示第 i 个像素的预测概率• yᵢ 表示第 i 个像素的真实标签• ε 是一个很小的正数用于避免分母为 0Dice Loss 对小目标和类别不平衡任务较常用。4、IoUIoU 也是分割中常见指标其中• IoU 表示交并比• P 表示预测区域• G 表示真实区域• |P ∩ G| 表示交集• |P ∪ G| 表示并集从通俗角度看Dice 和 IoU 都是在看“预测区域和真实区域重合得有多好”。十、Python 示例下面给出一个简化版 U-Net 示例用来帮助理解它的基本结构。一个简化版 U-Net 模块import torchimport torch.nn as nn # 双卷积块两次 (Conv ReLU)用于特征提取class DoubleConv(nn.Module): def __init__(self, in_channels, out_channels): super().__init__() self.block nn.Sequential( nn.Conv2d(in_channels, out_channels, kernel_size3, padding1), nn.ReLU(inplaceTrue), nn.Conv2d(out_channels, out_channels, kernel_size3, padding1), nn.ReLU(inplaceTrue) ) def forward(self, x): return self.block(x) # 简易 U-Net编码器-解码器结构带跳跃连接用于图像分割class SimpleUNet(nn.Module): def __init__(self, in_channels3, num_classes1): super().__init__() # 编码器下采样 self.enc1 DoubleConv(in_channels, 64) # 64通道 self.pool1 nn.MaxPool2d(2) # 尺寸减半 self.enc2 DoubleConv(64, 128) # 128通道 self.pool2 nn.MaxPool2d(2) # 尺寸再减半 # 瓶颈层最深特征图 self.bottleneck DoubleConv(128, 256) # 256通道 # 解码器上采样 跳跃连接 self.up2 nn.ConvTranspose2d(256, 128, kernel_size2, stride2) # 上采样 self.dec2 DoubleConv(256, 128) # 拼接后128128→128 self.up1 nn.ConvTranspose2d(128, 64, kernel_size2, stride2) self.dec1 DoubleConv(128, 64) # 拼接后6464→64 # 输出层1x1卷积输出类别数二分类为1 self.out_conv nn.Conv2d(64, num_classes, kernel_size1) def forward(self, x): # 编码路径 e1 self.enc1(x) # 特征图1用于跳跃连接 p1 self.pool1(e1) # 下采样 e2 self.enc2(p1) # 特征图2 p2 self.pool2(e2) # 下采样 # 瓶颈 b self.bottleneck(p2) # 解码路径 跳跃连接 u2 self.up2(b) # 上采样 u2 torch.cat([u2, e2], dim1) # 与编码器对应层拼接 d2 self.dec2(u2) # 双卷积 u1 self.up1(d2) u1 torch.cat([u1, e1], dim1) d1 self.dec1(u1) logits self.out_conv(d1) # 最终输出分割图 return logits这个简化模型中• enc1、enc2 是编码器部分• bottleneck 是瓶颈层• up2、up1 负责上采样• torch.cat 实现跳跃连接• out_conv 输出像素级预测结果示例 1查看输入输出形状# 实例化简易 U-Net 模型输入3通道RGB输出1通道二分类分割图model SimpleUNet(in_channels3, num_classes1) # 模拟一批输入2张 RGB 图像尺寸 128x128x torch.randn(2, 3, 128, 128) # 前向传播得到分割 logitslogits model(x) print(输入形状, x.shape) print(输出形状, logits.shape)输出形状通常为输入形状 torch.Size([2, 3, 128, 128])输出形状 torch.Size([2, 1, 128, 128])其中• 2 表示 batch size• 3 表示输入图像通道数• 128 × 128 表示图像空间尺寸• 1 表示二分类分割输出通道这说明 U-Net 可以把输入图像映射为同样空间尺寸的预测图。示例 2二分类分割中的损失计算# 实例化 U-Net 模型输入3通道RGB输出1通道二分类分割图model SimpleUNet(in_channels3, num_classes1) # 模拟输入图像2张RGB图像尺寸128x128x torch.randn(2, 3, 128, 128) # 生成随机二分类分割标签mask0背景1前景形状与输出匹配mask torch.randint(0, 2, (2, 1, 128, 128)).float() # 前向传播得到 logits未经过Sigmoidlogits model(x) # 损失函数结合 Sigmoid 二值交叉熵数值稳定性更好loss_fn nn.BCEWithLogitsLoss()loss loss_fn(logits, mask) # 计算损失 print(logits 形状, logits.shape) # (2,1,128,128)print(mask 形状, mask.shape) # (2,1,128,128)print(loss, loss.item()) # 损失值这个例子中• logits 是模型原始输出• mask 是真实分割标签• BCEWithLogitsLoss 适合二分类分割• 训练时不需要手动先对 logits 做 Sigmoid如果要得到最终前景概率可以在推理时使用prob torch.sigmoid(logits)再根据阈值得到二值分割图pred_mask prob 0.5 小结U-Net 是一种用于图像分割的经典编码器—解码器结构。它通过编码器提取语义信息通过解码器恢复空间分辨率并利用跳跃连接补充浅层细节。相比普通编码器—解码器U-Net 更适合像素级预测任务。对初学者而言可以把 U-Net 理解为一种先压缩理解图像、再恢复细节边界的分割网络。“点赞有美意赞赏是鼓励”