CNN的各种知识点(三):有关于VGG16 的结构展开的问题(1)
有关于VGG16 的结构展开的问题(1)
- 1. VGG16 的原生结构
- 2. `model.avgpool` 的作用
- 原生 VGG16 中没有 `avgpool` 层?
- **代码中的 `model.avgpool` 是什么?**
- 3. `model.classifier` 的作用
- 原生 VGG16 的 `classifier`
- 用户代码中的 `classifier`
- 4. 为什么模型能分化为 `avgpool` 和 `classifier`?
- 模块化设计
- 代码中的分化
- 5. 数据流动示例
- 总结
1. VGG16 的原生结构
在标准的 VGG16 模型中,网络分为两大模块:
features
:包含所有卷积层和池化层,用于提取图像特征。classifier
:包含所有全连接层,用于分类。
原版 VGG16 的结构如下(简化版):
VGG16(
features: Sequential( # 特征提取部分
Conv2d(3, 64, kernel_size=3, padding=1),
ReLU(),
Conv2d(64, 64, kernel_size=3, padding=1),
ReLU(),
MaxPool2d(kernel_size=2, stride=2),
# ... 后续卷积层和池化层
Conv2d(512, 512, kernel_size=3, padding=1),
ReLU(),
Conv2d(512, 512, kernel_size=3, padding=1),
ReLU(),
MaxPool2d(kernel_size=2, stride=2) # 最后一层池化
),
avgpool: AdaptiveAvgPool2d(output_size=(7, 7)), # 全局平均池化(非原生,可能是自定义修改)
classifier: Sequential( # 分类部分
Linear(25088, 4096), # 输入维度 512*7*7=25088
ReLU(),
Dropout(0.5),
Linear(4096, 4096),
ReLU(),
Dropout(0.5),
Linear(4096, 1000) # 输出 1000 类(ImageNet)
)
)
2. model.avgpool
的作用
原生 VGG16 中没有 avgpool
层?
- 原版 VGG16 的真相:
实际上,原版 VGG16 没有显式的avgpool
层。
它的features
模块最后一层是MaxPool2d
,输出形状为(512, 7, 7)
,然后直接展平输入classifier
。
AdaptiveAvgPool2d
是 PyTorch 对原版结构的调整,用于统一不同输入尺寸的输出。
代码中的 model.avgpool
是什么?
在用户提供的代码中:
model.avgpool = nn.Sequential(
nn.Conv2d(512, 512, 3),
nn.MaxPool2d(2),
nn.Flatten()
)
-
目的:替换原版 VGG16 的全局池化层,添加自定义操作:
Conv2d(512, 512, 3)
:在特征图上进行 3x3 卷积(保持通道数不变)。MaxPool2d(2)
:通过最大池化进一步压缩空间维度。Flatten()
:将多维特征图展平为一维向量,输入全连接层。
-
输入输出流程:
- 输入形状:来自
features
的最后一层,假设为(batch, 512, 7, 7)
。 - 操作分解:
- 卷积层:
输入(512, 7, 7)
→ 输出(512, 5, 5)
(7-3+1=5
)。 - 最大池化:
输入(512, 5, 5)
→ 输出(512, 2, 2)
(5//2=2
)。 - 展平:
输出(512*2*2) = 2048
→ 形状(batch, 2048)
。
- 卷积层:
- 输入形状:来自
-
意义:
通过添加卷积和池化,进一步提取高阶特征并压缩维度,最终输出2048
维向量。
3. model.classifier
的作用
原生 VGG16 的 classifier
原版 classifier
是全连接层,用于将 features
提取的特征映射到类别概率:
classifier: Sequential(
Linear(25088, 4096), # 25088 = 512*7*7
ReLU(),
Dropout(0.5),
Linear(4096, 4096),
ReLU(),
Dropout(0.5),
Linear(4096, 1000) # 输出 ImageNet 的 1000 类
)
用户代码中的 classifier
用户代码修改了 classifier
,适配关键点回归任务:
model.classifier = nn.Sequential(
nn.Linear(2048, 512), # 输入来自 avgpool 的 2048 维
nn.ReLU(),
nn.Dropout(0.5),
nn.Linear(512, 136), # 输出 136 个坐标值(68 个关键点)
nn.Sigmoid() # 将输出限制到 [0,1](归一化坐标)
)
- 输入输出流程:
- 输入:来自
avgpool
的2048
维向量。 - 全连接层:降维到
512
→ 激活函数 → Dropout 防过拟合。 - 输出层:映射到
136
维(68 个关键点的 x 和 y 坐标)。 - Sigmoid:将坐标归一化到图像比例(如 x=0.5 表示图像宽度中点)。
- 输入:来自
4. 为什么模型能分化为 avgpool
和 classifier
?
模块化设计
PyTorch 的模型是模块化的,允许用户按需替换子模块。
在 VGG16 的实现中,开发者可能将网络分为多个逻辑部分:
features
:所有卷积层(特征提取)。avgpool
:全局池化层(自定义添加,非原生)。classifier
:全连接层(分类或回归)。
代码中的分化
用户代码中:
model.avgpool
:处理从features
输出的特征图(卷积 + 池化 + 展平)。model.classifier
:将展平后的特征向量映射到目标输出(如关键点坐标)。
5. 数据流动示例
假设输入图像尺寸为 224x224
:
features
部分:
输入(3, 224, 224)
→ 输出(512, 7, 7)
。avgpool
部分:
输入(512, 7, 7)
→ 卷积 →(512, 5, 5)
→ 池化 →(512, 2, 2)
→ 展平 →(2048)
。classifier
部分:
输入2048
→ 全连接 →512
→ ReLU → Dropout → 全连接 →136
→ Sigmoid → 输出归一化坐标。
总结
模块 | 输入 | 操作 | 输出 | 目的 |
---|---|---|---|---|
features | (3, 224, 224) | 卷积 + 池化 | (512, 7, 7) | 提取图像特征 |
avgpool | (512, 7, 7) | 卷积 + 池化 + 展平 | (2048) | 进一步压缩特征维度 |
classifier | (2048) | 全连接 + 激活 + Dropout | (136) | 回归关键点坐标 |
通过替换 avgpool
和 classifier
,用户将 VGG16 从图像分类任务改造为关键点回归任务,同时利用预训练的卷积层提取通用特征。