📚 课程目录

第14课:图像分割基础

📖 本课概述

图像分割是像素级的视觉理解——对每个像素分类。本课从语义分割到实例分割,理解U-Net等经典架构。

🎯 一、分割问题分类

类型任务输出
语义分割每个像素分类H×W类别图
实例分割区分同类不同实例H×W实例ID
全景分割语义+实例两者结合

🏗️ 二、U-Net架构

2.1 编码器-解码器结构

编码器(下采样):捕获上下文信息

解码器(上采样):恢复空间分辨率

跳跃连接:融合细节和语义信息

2.2 转置卷积

普通卷积:大图→小图(下采样)

转置卷积:小图→大图(上采样)

输出尺寸 = (输入-1)×stride - 2×padding + kernel_size

2.3 PyTorch实现U-Net


import torch
import torch.nn as nn

# U-Net实现
class DoubleConv(nn.Module):
    def __init__(self, in_ch, out_ch):
        super().__init__()
        self.conv = nn.Sequential(
            nn.Conv2d(in_ch, out_ch, 3, padding=1), nn.BatchNorm2d(out_ch), nn.ReLU(),
            nn.Conv2d(out_ch, out_ch, 3, padding=1), nn.BatchNorm2d(out_ch), nn.ReLU(),
        )
    def forward(self, x):
        return self.conv(x)

class UNet(nn.Module):
    def __init__(self, in_ch=1, out_ch=1):
        super().__init__()
        # 编码器
        self.enc1 = DoubleConv(in_ch, 64)
        self.enc2 = DoubleConv(64, 128)
        self.enc3 = DoubleConv(128, 256)
        self.pool = nn.MaxPool2d(2)
        
        # 瓶颈层
        self.bottleneck = DoubleConv(256, 512)
        
        # 解码器
        self.up3 = nn.ConvTranspose2d(512, 256, 2, stride=2)
        self.dec3 = DoubleConv(512, 256)
        self.up2 = nn.ConvTranspose2d(256, 128, 2, stride=2)
        self.dec2 = DoubleConv(256, 128)
        self.up1 = nn.ConvTranspose2d(128, 64, 2, stride=2)
        self.dec1 = DoubleConv(128, 64)
        
        self.final = nn.Conv2d(64, out_ch, 1)
    
    def forward(self, x):
        # 编码
        e1 = self.enc1(x)      # 64
        e2 = self.enc2(self.pool(e1))  # 128
        e3 = self.enc3(self.pool(e2))  # 256
        
        # 瓶颈
        b = self.bottleneck(self.pool(e3))  # 512
        
        # 解码 + 跳跃连接
        d3 = self.up3(b)
        d3 = self.dec3(torch.cat([d3, e3], dim=1))
        d2 = self.up2(d3)
        d2 = self.dec2(torch.cat([d2, e2], dim=1))
        d1 = self.up1(d2)
        d1 = self.dec1(torch.cat([d1, e1], dim=1))
        
        return self.final(d1)

# 测试
model = UNet(in_ch=1, out_ch=2)
x = torch.randn(2, 1, 64, 64)
out = model(x)
params = sum(p.numel() for p in model.parameters())
print(f"U-Net: 输入{list(x.shape)} → 输出{list(out.shape)}, 参数量={params:,}")

# 转置卷积验证
print("\n=== 转置卷积尺寸变化 ===")
x_small = torch.randn(1, 1, 4, 4)
conv_trans = nn.ConvTranspose2d(1, 1, 3, stride=2, padding=1)
out_up = conv_trans(x_small)
print(f"转置卷积(4×4→{out_up.shape[-2]}×{out_up.shape[-1]})")

# 不同上采样方法对比
print("\n=== 上采样方法对比 ===")
x_test = torch.randn(1, 64, 4, 4)
methods = {
    "最近邻插值": nn.Upsample(scale_factor=2, mode='nearest'),
    "双线性插值": nn.Upsample(scale_factor=2, mode='bilinear', align_corners=False),
    "转置卷积": nn.ConvTranspose2d(64, 64, 2, stride=2),
}
for name, method in methods.items():
    out = method(x_test)
    params = sum(p.numel() for p in method.parameters()) if hasattr(method, 'parameters') else 0
    print(f"{name}: {list(x_test.shape)} → {list(out.shape)}, 参数量={params:,}")

# 分割损失:Dice Loss
def dice_loss(pred, target, smooth=1.0):
    pred = torch.sigmoid(pred)
    intersection = (pred * target).sum()
    return 1 - (2 * intersection + smooth) / (pred.sum() + target.sum() + smooth)

pred = torch.randn(1, 1, 8, 8)
target = torch.randint(0, 2, (1, 1, 8, 8)).float()
print(f"\nDice Loss: {dice_loss(pred, target).item():.4f}")
print(f"BCE Loss: {nn.BCEWithLogitsLoss()(pred, target).item():.4f}")
🟢 运行结果 ✅验证通过 U-Net: 输入[2, 1, 64, 64] → 输出[2, 2, 64, 64], 参数量=7,701,890 === 转置卷积尺寸变化 === 转置卷积(4×4→7×7) === 上采样方法对比 === 最近邻插值: [1, 64, 4, 4] → [1, 64, 8, 8], 参数量=0 双线性插值: [1, 64, 4, 4] → [1, 64, 8, 8], 参数量=0 转置卷积: [1, 64, 4, 4] → [1, 64, 8, 8], 参数量=16,448 Dice Loss: 0.4341 BCE Loss: 0.7484

🔬 深入拓展

核心要点回顾

本课的核心知识点构成了CNN学习的重要一环。让我们从更高维度审视这些知识之间的关系:

常见陷阱与最佳实践

⚠️ 初学者常犯的错误:

💡 最佳实践:

与前后课程的联系

深度学习是一个整体——每一课的知识都不是孤立的:

📖 延伸阅读

💡 继续深入的学习路径:

📝 练习

练习1:在分割数据上训练U-Net

生成简单的圆形/矩形分割数据,训练U-Net。

练习2:Dice Loss vs BCE

对比两种损失函数在类别不平衡分割任务上的效果。

练习3:DeepLab v3+

理解空洞空间卷积池化金字塔(ASPP)的实现和作用。

💡 图像分割实践

分割任务选择

常见错误与解决方案

⚠️ 需要避免的典型错误:

  1. 不了解模型假设就盲目使用
  2. 没有建立基线就追求复杂方法
  3. 忽视数据质量和预处理
  4. 过度调参而不理解原理
  5. 只看训练指标忽略验证指标

性能优化建议

💡 提升模型性能的系统方法:

  1. 确保数据管道正确且高效
  2. 建立简单但正确的基线模型
  3. 分析基线的错误类型
  4. 针对性地改进(数据/模型/训练)
  5. 持续迭代,每步验证

🔬 深度拓展:从理论到实践

核心概念网络

理解本课内容需要将其置于更大的知识网络中。每个核心概念都不是孤立的,它们相互关联、相互支撑:

关键洞察与直觉

建立正确的直觉比记住公式更重要:

1. 维度直觉:高维空间中,数据分布与低维直觉截然不同(维度诅咒)

2. 信息瓶颈:神经网络逐层压缩信息,保留任务相关的、丢弃无关的

3. 流形假设:高维数据实际分布在低维流形上,学习即发现流形结构

4. 偏差-方差权衡:简单模型欠拟合(高偏差),复杂模型过拟合(高方差)

与其他课程的关联

本课作为CNN阶段的核心内容,与前后课程紧密关联:

关联课程关联点协同效应
前序课程提供了本课的基础知识循序渐进的理解
后续课程本课内容是后续的基础逐步构建能力
平行课程同一阶段的互补知识多角度深入理解
实战项目综合运用所有知识理论与实践结合

面试常见问题

💡 准备面试时,确保能回答以下问题:

  1. 用简单语言解释本课核心概念
  2. 画出关键算法/结构的流程图
  3. 比较不同方法的优缺点
  4. 举出实际应用场景
  5. 讨论常见误解和注意事项

进阶学习路径

掌握本课内容后,可以通过以下方式继续深入:

✂️

成就解锁:像素雕刻家

图像分割是像素级的理解——你掌握了精细视觉分析!