【深度学习】U-Net系列(一·补):U-Net网络结构深度剖析
本文深入剖析U-Net网络结构,通过Graphviz可视化呈现完整架构。文章详细解析了编码器(蓝色系)、解码器(绿色系)和瓶颈层(紫色系)的层级结构,包括每层的特征图尺寸变化和参数配置。编码器包含4个下采样块,每块由3×3卷积+BN+ReLU组成,通过最大池化降维;解码器对应4个上采样块,使用转置卷积恢复尺寸并与编码器特征拼接。特别标注了跳跃连接(橙色虚线)和池化/上采样(红色)操作,完整展示了5
【深度学习】U-Net系列(一·补):U-Net网络结构深度剖析
🔖 系列导航:[前置知识] → [U-Net架构详解] → [网络结构深度剖析] → [医学图像应用] → [完整实战项目] → [FAQ与调优] → [变体与改进]
📌 关键词:网络结构、特征图变化、参数计算、数据流、Graphviz架构图
1. 前言
本文是 U-Net 架构详解的补充篇,将从数据流动、特征图尺寸变化、参数量计算等角度深入剖析 U-Net 的每一层结构,帮助读者建立清晰的网络全貌认知。
2. U-Net 完整架构图(Graphviz)
以下是使用 Graphviz DOT 语言绘制的 U-Net 完整架构图,可在支持 Graphviz 的环境中渲染:
digraph UNet {
// 全局设置
rankdir=TB;
node [shape=box, style="rounded,filled", fontname="Arial"];
edge [fontname="Arial", fontsize=10];
// 颜色定义
// 编码器: 蓝色系
// 解码器: 绿色系
// 跳跃连接: 橙色虚线
// 池化/上采样: 红色
// 输入
input [label="Input\n572×572×1", fillcolor="#E8E8E8"];
// 编码器第1层
subgraph cluster_enc1 {
label="Encoder Block 1";
style=dashed;
color="#4A90D9";
enc1_conv1 [label="Conv 3×3\n572→570\n1→64", fillcolor="#AED6F1"];
enc1_bn1 [label="BN + ReLU", fillcolor="#AED6F1"];
enc1_conv2 [label="Conv 3×3\n570→568\n64→64", fillcolor="#AED6F1"];
enc1_bn2 [label="BN + ReLU", fillcolor="#AED6F1"];
}
// 编码器第2层
subgraph cluster_enc2 {
label="Encoder Block 2";
style=dashed;
color="#4A90D9";
pool1 [label="MaxPool 2×2\n568→284", fillcolor="#F1948A"];
enc2_conv1 [label="Conv 3×3\n284→282\n64→128", fillcolor="#85C1E9"];
enc2_bn1 [label="BN + ReLU", fillcolor="#85C1E9"];
enc2_conv2 [label="Conv 3×3\n282→280\n128→128", fillcolor="#85C1E9"];
enc2_bn2 [label="BN + ReLU", fillcolor="#85C1E9"];
}
// 编码器第3层
subgraph cluster_enc3 {
label="Encoder Block 3";
style=dashed;
color="#4A90D9";
pool2 [label="MaxPool 2×2\n280→140", fillcolor="#F1948A"];
enc3_conv1 [label="Conv 3×3\n140→138\n128→256", fillcolor="#5DADE2"];
enc3_bn1 [label="BN + ReLU", fillcolor="#5DADE2"];
enc3_conv2 [label="Conv 3×3\n138→136\n256→256", fillcolor="#5DADE2"];
enc3_bn2 [label="BN + ReLU", fillcolor="#5DADE2"];
}
// 编码器第4层
subgraph cluster_enc4 {
label="Encoder Block 4";
style=dashed;
color="#4A90D9";
pool3 [label="MaxPool 2×2\n136→68", fillcolor="#F1948A"];
enc4_conv1 [label="Conv 3×3\n68→66\n256→512", fillcolor="#3498DB"];
enc4_bn1 [label="BN + ReLU", fillcolor="#3498DB"];
enc4_conv2 [label="Conv 3×3\n66→64\n512→512", fillcolor="#3498DB"];
enc4_bn2 [label="BN + ReLU", fillcolor="#3498DB"];
}
// 瓶颈层
subgraph cluster_bottleneck {
label="Bottleneck";
style=dashed;
color="#8E44AD";
pool4 [label="MaxPool 2×2\n64→32", fillcolor="#F1948A"];
bn_conv1 [label="Conv 3×3\n32→30\n512→1024", fillcolor="#D7BDE2"];
bn_bn1 [label="BN + ReLU", fillcolor="#D7BDE2"];
bn_conv2 [label="Conv 3×3\n30→28\n1024→1024", fillcolor="#D7BDE2"];
bn_bn2 [label="BN + ReLU", fillcolor="#D7BDE2"];
}
// 解码器第1层
subgraph cluster_dec1 {
label="Decoder Block 1";
style=dashed;
color="#27AE60";
up1 [label="UpConv 2×2\n28→56\n1024→512", fillcolor="#F5B041"];
crop1 [label="Crop & Concat\n56×56\n512+512→1024", fillcolor="#FAD7A0"];
dec1_conv1 [label="Conv 3×3\n56→54\n1024→512", fillcolor="#ABEBC6"];
dec1_bn1 [label="BN + ReLU", fillcolor="#ABEBC6"];
dec1_conv2 [label="Conv 3×3\n54→52\n512→512", fillcolor="#ABEBC6"];
dec1_bn2 [label="BN + ReLU", fillcolor="#ABEBC6"];
}
// 解码器第2层
subgraph cluster_dec2 {
label="Decoder Block 2";
style=dashed;
color="#27AE60";
up2 [label="UpConv 2×2\n52→104\n512→256", fillcolor="#F5B041"];
crop2 [label="Crop & Concat\n104×104\n256+256→512", fillcolor="#FAD7A0"];
dec2_conv1 [label="Conv 3×3\n104→102\n512→256", fillcolor="#82E0AA"];
dec2_bn1 [label="BN + ReLU", fillcolor="#82E0AA"];
dec2_conv2 [label="Conv 3×3\n102→100\n256→256", fillcolor="#82E0AA"];
dec2_bn2 [label="BN + ReLU", fillcolor="#82E0AA"];
}
// 解码器第3层
subgraph cluster_dec3 {
label="Decoder Block 3";
style=dashed;
color="#27AE60";
up3 [label="UpConv 2×2\n100→200\n256→128", fillcolor="#F5B041"];
crop3 [label="Crop & Concat\n200×200\n128+128→256", fillcolor="#FAD7A0"];
dec3_conv1 [label="Conv 3×3\n200→198\n256→128", fillcolor="#58D68D"];
dec3_bn1 [label="BN + ReLU", fillcolor="#58D68D"];
dec3_conv2 [label="Conv 3×3\n198→196\n128→128", fillcolor="#58D68D"];
dec3_bn2 [label="BN + ReLU", fillcolor="#58D68D"];
}
// 解码器第4层
subgraph cluster_dec4 {
label="Decoder Block 4";
style=dashed;
color="#27AE60";
up4 [label="UpConv 2×2\n196→392\n128→64", fillcolor="#F5B041"];
crop4 [label="Crop & Concat\n392×392\n64+64→128", fillcolor="#FAD7A0"];
dec4_conv1 [label="Conv 3×3\n392→390\n128→64", fillcolor="#2ECC71"];
dec4_bn1 [label="BN + ReLU", fillcolor="#2ECC71"];
dec4_conv2 [label="Conv 3×3\n390→388\n64→64", fillcolor="#2ECC71"];
dec4_bn2 [label="BN + ReLU", fillcolor="#2ECC71"];
}
// 输出层
output_conv [label="Conv 1×1\n388×388\n64→n_classes", fillcolor="#E8E8E8"];
output [label="Output\n388×388×n_classes", fillcolor="#E8E8E8"];
// 连接 - 编码器
input -> enc1_conv1;
enc1_conv1 -> enc1_bn1 -> enc1_conv2 -> enc1_bn2;
enc1_bn2 -> pool1;
pool1 -> enc2_conv1 -> enc2_bn1 -> enc2_conv2 -> enc2_bn2;
enc2_bn2 -> pool2;
pool2 -> enc3_conv1 -> enc3_bn1 -> enc3_conv2 -> enc3_bn2;
enc3_bn2 -> pool3;
pool3 -> enc4_conv1 -> enc4_bn1 -> enc4_conv2 -> enc4_bn2;
enc4_bn2 -> pool4;
// 连接 - 瓶颈
pool4 -> bn_conv1 -> bn_bn1 -> bn_conv2 -> bn_bn2;
// 连接 - 解码器
bn_bn2 -> up1 -> crop1;
crop1 -> dec1_conv1 -> dec1_bn1 -> dec1_conv2 -> dec1_bn2;
dec1_bn2 -> up2 -> crop2;
crop2 -> dec2_conv1 -> dec2_bn1 -> dec2_conv2 -> dec2_bn2;
dec2_bn2 -> up3 -> crop3;
crop3 -> dec3_conv1 -> dec3_bn1 -> dec3_conv2 -> dec3_bn2;
dec3_bn2 -> up4 -> crop4;
crop4 -> dec4_conv1 -> dec4_bn1 -> dec4_conv2 -> dec4_bn2;
dec4_bn2 -> output_conv -> output;
// 跳跃连接(橙色虚线)
enc4_bn2 -> crop1 [style=dashed, color="#E67E22", label="skip"];
enc3_bn2 -> crop2 [style=dashed, color="#E67E22", label="skip"];
enc2_bn2 -> crop3 [style=dashed, color="#E67E22", label="skip"];
enc1_bn2 -> crop4 [style=dashed, color="#E67E22", label="skip"];
}
3. 特征图尺寸变化详解
3.1 原始 U-Net(Valid 卷积)
原始论文使用无填充卷积,每次 3×3 卷积会使尺寸减 2:
| 阶段 | 层 | 输入尺寸 | 输出尺寸 | 通道数 |
|---|---|---|---|---|
| 输入 | - | - | 572×572 | 1 |
| Enc1 | Conv×2 | 572×572 | 568×568 | 64 |
| Enc2 | Pool + Conv×2 | 568×568 | 280×280 | 128 |
| Enc3 | Pool + Conv×2 | 280×280 | 136×136 | 256 |
| Enc4 | Pool + Conv×2 | 136×136 | 64×64 | 512 |
| Bottleneck | Pool + Conv×2 | 64×64 | 28×28 | 1024 |
| Dec1 | UpConv + Conv×2 | 28×28 | 52×52 | 512 |
| Dec2 | UpConv + Conv×2 | 52×52 | 100×100 | 256 |
| Dec3 | UpConv + Conv×2 | 100×100 | 196×196 | 128 |
| Dec4 | UpConv + Conv×2 | 196×196 | 388×388 | 64 |
| 输出 | Conv 1×1 | 388×388 | 388×388 | n_classes |
3.2 现代 U-Net(Same 卷积)
现代实现使用 padding=1 保持尺寸:
| 阶段 | 输入尺寸 | 输出尺寸 | 通道数 |
|---|---|---|---|
| 输入 | - | 256×256 | 3 |
| Enc1 | 256×256 | 256×256 | 64 |
| Enc2 | 128×128 | 128×128 | 128 |
| Enc3 | 64×64 | 64×64 | 256 |
| Enc4 | 32×32 | 32×32 | 512 |
| Bottleneck | 16×16 | 16×16 | 1024 |
| Dec1 | 32×32 | 32×32 | 512 |
| Dec2 | 64×64 | 64×64 | 256 |
| Dec3 | 128×128 | 128×128 | 128 |
| Dec4 | 256×256 | 256×256 | 64 |
| 输出 | 256×256 | 256×256 | n_classes |
4. 卷积块结构详解
4.1 双卷积块(DoubleConv)
每个编码器/解码器阶段都包含两次卷积:
4.2 参数量计算
单个 3×3 卷积层参数:
Params = C i n × C o u t × k 2 + C o u t \text{Params} = C_{in} \times C_{out} \times k^2 + C_{out} Params=Cin×Cout×k2+Cout
其中 k = 3 k=3 k=3 为卷积核大小, C o u t C_{out} Cout 为偏置项(若使用 BN 则无偏置)。
DoubleConv 参数量(无偏置):
Params d o u b l e = C i n × C m i d × 9 + C m i d × C o u t × 9 \text{Params}_{double} = C_{in} \times C_{mid} \times 9 + C_{mid} \times C_{out} \times 9 Paramsdouble=Cin×Cmid×9+Cmid×Cout×9
4.3 各层参数量统计
| 模块 | 输入通道 | 输出通道 | 参数量 |
|---|---|---|---|
| Enc1 | 1 | 64 | 38,720 |
| Enc2 | 64 | 128 | 221,440 |
| Enc3 | 128 | 256 | 885,248 |
| Enc4 | 256 | 512 | 3,539,968 |
| Bottleneck | 512 | 1024 | 14,158,848 |
| Dec1 | 1024 | 512 | 7,079,424 |
| Dec2 | 512 | 256 | 1,769,984 |
| Dec3 | 256 | 128 | 442,624 |
| Dec4 | 128 | 64 | 110,720 |
| Output | 64 | 2 | 130 |
| 总计 | - | - | ~31M |
5. 跳跃连接机制详解
5.1 为什么需要跳跃连接
5.2 跳跃连接工作原理
5.3 裁剪操作(原始 U-Net)
由于 valid 卷积导致编码器特征图更大,需要中心裁剪对齐:
offset = H e n c − H d e c 2 \text{offset} = \frac{H_{enc} - H_{dec}}{2} offset=2Henc−Hdec
F c r o p p e d = F e n c [ offset : offset + H d e c , offset : offset + W d e c ] F_{cropped} = F_{enc}[\text{offset}:\text{offset}+H_{dec}, \text{offset}:\text{offset}+W_{dec}] Fcropped=Fenc[offset:offset+Hdec,offset:offset+Wdec]
5.4 跳跃连接对比
| 方法 | 操作 | 特点 |
|---|---|---|
| U-Net | Concatenate | 保留完整信息,通道数翻倍 |
| ResNet | Add | 参数少,需通道数相同 |
| DenseNet | 密集Concat | 特征复用最充分 |
6. 上采样层详解
6.1 转置卷积(原始 U-Net)
转置卷积参数:
Params = C i n × C o u t × k 2 = 1024 × 512 × 4 = 2 , 097 , 152 \text{Params} = C_{in} \times C_{out} \times k^2 = 1024 \times 512 \times 4 = 2,097,152 Params=Cin×Cout×k2=1024×512×4=2,097,152
6.2 双线性插值 + 卷积(现代实现)
6.3 两种方法对比
| 方法 | 参数量 | 计算量 | 效果 |
|---|---|---|---|
| 转置卷积 | 多 | 大 | 可学习,可能棋盘效应 |
| 双线性+卷积 | 少 | 小 | 平滑,无棋盘效应 |
7. 数据流完整追踪
7.1 前向传播流程
以输入 256×256×1 的灰度图像为例(same padding):
7.2 显存占用估算
| 阶段 | 特征图大小 | 显存占用(float32, batch=1) |
|---|---|---|
| Enc1 | 256×256×64 | 16 MB |
| Enc2 | 128×128×128 | 8 MB |
| Enc3 | 64×64×256 | 4 MB |
| Enc4 | 32×32×512 | 2 MB |
| Bottleneck | 16×16×1024 | 1 MB |
| 前向总计 | - | ~50 MB |
| 训练(含梯度) | - | ~150 MB |
8. 感受野分析
8.1 什么是感受野
感受野(Receptive Field):输出特征图上一个像素对应输入图像的区域大小。
8.2 U-Net 感受野计算
每层感受野递推公式:
R F l = R F l − 1 + ( k l − 1 ) × ∏ i = 1 l − 1 s i RF_l = RF_{l-1} + (k_l - 1) \times \prod_{i=1}^{l-1} s_i RFl=RFl−1+(kl−1)×i=1∏l−1si
| 层 | 感受野大小 |
|---|---|
| Enc1 Conv2 | 5×5 |
| Enc2 Conv2 | 14×14 |
| Enc3 Conv2 | 32×32 |
| Enc4 Conv2 | 68×68 |
| Bottleneck | 140×140 |
8.3 感受野的意义
9. 计算复杂度分析
9.1 FLOPs 计算
单个卷积层 FLOPs:
FLOPs = 2 × H o u t × W o u t × C i n × C o u t × k 2 \text{FLOPs} = 2 \times H_{out} \times W_{out} \times C_{in} \times C_{out} \times k^2 FLOPs=2×Hout×Wout×Cin×Cout×k2
9.2 各层 FLOPs 统计(256×256 输入)
| 模块 | FLOPs |
|---|---|
| Enc1 | 1.2G |
| Enc2 | 1.5G |
| Enc3 | 1.5G |
| Enc4 | 1.5G |
| Bottleneck | 1.5G |
| Dec1 | 1.5G |
| Dec2 | 1.5G |
| Dec3 | 1.5G |
| Dec4 | 1.2G |
| 总计 | ~13G |
10. 总结
10.1 U-Net 结构要点
| 组件 | 结构 | 作用 |
|---|---|---|
| 编码器 | 4个下采样阶段 | 提取多尺度特征 |
| 瓶颈层 | 最深层1024通道 | 高级语义特征 |
| 解码器 | 4个上采样阶段 | 恢复空间分辨率 |
| 跳跃连接 | 4条 Concat | 融合细节信息 |
10.2 关键数值
| 指标 | 数值 |
|---|---|
| 参数量 | ~31M |
| FLOPs (256×256) | ~13G |
| 最大感受野 | 140×140 |
| 下采样倍数 | 16× |
更多推荐


所有评论(0)