人工智能-python-深度学习-神经网络VGG(详解)

LeNet 系列之后 ------ VGG(详解):从原理到 PyTorch 实现

文章目录

  • [LeNet 系列之后 ------ **VGG(详解)**:从原理到 PyTorch 实现](#LeNet 系列之后 —— VGG(详解):从原理到 PyTorch 实现)
    • [1. VGG 的发展历史与意义(一句话+背景)](#1. VGG 的发展历史与意义(一句话+背景))
    • [2. VGG 的核心思想(要点)](#2. VGG 的核心思想(要点))
    • [3. VGG 的主要版本(配置)](#3. VGG 的主要版本(配置))
    • [4. **逐层结构详解 + 逐层计算举例(以 VGG-16 为例)**](#4. 逐层结构详解 + 逐层计算举例(以 VGG-16 为例))
      • [4.1 形状变化公式(卷积/池化常用公式)](#4.1 形状变化公式(卷积/池化常用公式))
      • [4.2 参数数量计算公式](#4.2 参数数量计算公式)
      • [4.3 逐层数值举例(VGG-16,输入:`3 × 224 × 224`)](#4.3 逐层数值举例(VGG-16,输入:3 × 224 × 224))
      • [4.4 感受野(receptive field)直观说明](#4.4 感受野(receptive field)直观说明)
    • [5. 关键设计点解析(深入)](#5. 关键设计点解析(深入))
    • [6. PyTorch 实现](#6. PyTorch 实现)
      • [A. 直接用 torchvision(推荐做迁移学习)](#A. 直接用 torchvision(推荐做迁移学习))
      • [B. 自定义 VGG-16(配置 D)的实现](#B. 自定义 VGG-16(配置 D)的实现)
      • 训练/微调的示例骨架(CIFAR-10)
    • [7. 训练与评估(超参、训练曲线、结果呈现建议)](#7. 训练与评估(超参、训练曲线、结果呈现建议))
    • [8. 实验扩展(对比/消融实验建议)](#8. 实验扩展(对比/消融实验建议))
    • [9. 总结与实践建议](#9. 总结与实践建议)

标题 :VGG 神经网络(详解)------ 原理、逐层计算、PyTorch 实现与实验指南
简介:VGG(Visual Geometry Group)由 Simonyan & Zisserman 在 2014 年提出,主张用统一的小卷积核(3×3)堆叠并加深网络,取得了 ImageNet 上的优秀效果。本文目标是把 VGG 的思想、结构、逐层维度/参数计算以及一个可跑通的 PyTorch 示例讲清楚



1. VGG 的发展历史与意义(一句话+背景)

  • 一句话:VGG 提出"把网络加深并统一用小卷积核(3×3)"的设计思想,证明了深度(depth)在卷积网络表征能力上的重要性。
  • 背景/成绩:VGG 的模型(其两个最优模型)在 ILSVRC2014 的分类/定位任务中取得了非常靠前的结果(classification 与 localization 分别取得优异名次)。在 ILSVRC2014 上,GoogLeNet 为第一名,VGG 的提交在分类赛道上名列前茅(ensemble/top5 ~7.3% 左右),在定位上也表现很好。([image-net.org][1])

2. VGG 的核心思想(要点)

  • 非常小的卷积核(3×3)并多层堆叠:用许多 3×3 卷积替代单个大核(例如 7×7),既能增加网络深度,也能用更少的参数获得更复杂的非线性。论文中阐明:两层 3×3 的堆叠拥有等效的 5×5 感受野,三层 3×3 等效 7×7。
  • 统一的设计范式:每个 block 中重复 "(conv3×3 → ReLU)" 若干次,再做 2×2 max-pool 下采样;每个 block 的通道数在下采样后翻倍(64→128→256→512)。
  • 深度优先:通过把"宽度"控制在中等,重点增加"深度",提升表达能力。
  • 简单实用:统一且模块化,便于移植、迁移学习与微调。现成的预训练权重也广泛可得(PyTorch/torchvision 等)。([PyTorch Docs][2])

3. VGG 的主要版本(配置)

论文中给出几种配置(A--E);常见编号对应为:

  • VGG-11 (A)
  • VGG-13 (B)
  • VGG-16 (D,最常见,常说的 VGG16)
  • VGG-19 (E)

各配置差异主要在每个 block 内 conv 层的个数(例如 VGG16 每个 block 的 conv 层数为 2/2/3/3/3)。更多细节见论文配置表。


4. 逐层结构详解 + 逐层计算举例(以 VGG-16 为例)

4.1 形状变化公式(卷积/池化常用公式)

  • 卷积输出宽/高计算(单层):

    H o u t = ⌊ H i n − K + 2 P S ⌋ + 1 H_{out} = \left\lfloor\frac{H_{in} - K + 2P}{S}\right\rfloor + 1 Hout=⌊SHin−K+2P⌋+1

    其中 K K K 为核大小(例如 3), P P P 为 padding, S S S 为 stride。VGG 全部 conv 使用 K=3, P=1, S=1,因此空间尺度 保持不变

  • 池化(2×2, stride=2)会将宽高各除以 2: H o u t = H i n / 2 H_{out} = H_{in}/2 Hout=Hin/2。

4.2 参数数量计算公式

  • 卷积层参数(含偏置):

    params = C i n × C o u t × K × K    ( +    C o u t biases ) \text{params} = C_{in} \times C_{out} \times K \times K \; (+\; C_{out}\ \text{biases}) params=Cin×Cout×K×K(+Cout biases)

  • 全连接层参数(含偏置):

    params = N i n × N o u t    ( +    N o u t biases ) \text{params} = N_{in} \times N_{out} \; (+\; N_{out}\ \text{biases}) params=Nin×Nout(+Nout biases)

4.3 逐层数值举例(VGG-16,输入:3 × 224 × 224

下面表格列出每层的输入/输出尺寸以及该层参数(含 bias)。(注:padding=1, stride=1 的 3×3 conv;池化 2×2 stride=2)

层名 类型 输入 输出 参数量(含 bias)
conv1_1 conv 3→64 (3×3) 3×224×224 64×224×224 1,792 (1728 + 64)
conv1_2 conv 64→64 64×224×224 64×224×224 36,928
pool1 maxpool 2×2 64×224×224 64×112×112 0
conv2_1 conv 64→128 64×112×112 128×112×112 73,856
conv2_2 conv 128→128 128×112×112 128×112×112 147,584
pool2 maxpool 128×112×112 128×56×56 0
conv3_1 conv 128→256 128×56×56 256×56×56 295,168
conv3_2 conv 256→256 256×56×56 256×56×56 590,080
conv3_3 conv 256→256 256×56×56 256×56×56 590,080
pool3 maxpool 256×56×56 256×28×28 0
conv4_1 conv 256→512 256×28×28 512×28×28 1,180,160
conv4_2 conv 512→512 512×28×28 512×28×28 2,359,808
conv4_3 conv 512→512 512×28×28 512×28×28 2,359,808
pool4 maxpool 512×28×28 512×14×14 0
conv5_1 conv 512→512 512×14×14 512×14×14 2,359,808
conv5_2 conv 512→512 512×14×14 512×14×14 2,359,808
conv5_3 conv 512→512 512×14×14 512×14×14 2,359,808
pool5 maxpool 512×14×14 512×7×7 0
flatten --- 512×7×7 25088 0
fc1 FC 25088→4096 25088 4096 102,764,544
fc2 FC 4096→4096 4096 4096 16,781,312
fc3 FC 4096→1000 4096 1000 4,097,000
  • 总参数量(VGG-16)138,357,544 (约 138M)。可以看到 绝大部分参数来源于前两层 FC(尤其是第一个 FC)。这就是为什么全连接层往往是参数与存储瓶颈。数据来源与论文表格一致。

4.4 感受野(receptive field)直观说明

  • 单个 3×3 卷积的感受野是 3×3;两个 3×3 连着(无下采样)对原图的等效感受野是 5×5;三个 3×3 等效为 7×7。直观上,第二层卷积核"看"到的是前一层 3×3 的特征,而这些特征本身对应原始图像上的 3×3 区域,合并后等价于更大的窗口,但通过两次非线性(ReLU)增强了表达能力。

5. 关键设计点解析(深入)

  • 为什么用 3×3 而不是 7×7/11×11?

    • 参数效率:用三个 3×3 的层实现 7×7 的等效感受野,但参数更少;例如单个 7×7(输入 c, 输出 d)参数为 c × d × 7 × 7 c \times d \times 7 \times 7 c×d×7×7,而三个 3×3 的组合参数为 c × m × 3 × 3 + m × n × 3 × 3 + n × d × 3 × 3 c\times m\times3\times3 + m\times n\times3\times3 + n\times d\times3\times3 c×m×3×3+m×n×3×3+n×d×3×3(若中间通道数相同可更便宜),并且中间层带非线性,表达更丰富。论文对这一点做了论证。
  • ReLU:使用 ReLU 作为非线性,训练收敛快且减少了梯度消失问题(当时为常用选择)。

  • FC 层大且昂贵:VGG 的 FC 层占参数大头(约 90%+),是模型存储的瓶颈。后来很多工作(例如全局平均池化、去掉大 FC)用来做模型轻量化。

  • BN(批归一化) :在后续实践中,VGG-BN(在 conv 后加 BN)能显著加速训练并提升稳定性;PyTorch/torchvision 提供 vgg16_bn。([PyTorch Docs][3])


6. PyTorch 实现

下面给出两个版本:(A)直接使用 torchvision 的预训练模型(最简单)(B)自定义实现 VGG-16 的代码(用于教学/修改)。二者都附训练/微调的骨架。

A. 直接用 torchvision(推荐做迁移学习)

python 复制代码
# 直接加载 torchvision 预训练 VGG16,并替换最后的分类头(示例:用于 CIFAR-10)
import torch
import torch.nn as nn
from torchvision import models

device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

# 加载预训练权重(PyTorch >= 0.13 的新 API 以 weights 参数为准)
vgg = models.vgg16(weights=models.VGG16_Weights.IMAGENET1K_V1)  # 或 weights='IMAGENET1K_V1'
# 替换 classifier 的最后一层为 10 类(CIFAR-10)
vgg.classifier[-1] = nn.Linear(in_features=4096, out_features=10)
vgg = vgg.to(device)

如果你的 PyTorch 版本没有 weights 参数,用 pretrained=True(老版本)。

B. 自定义 VGG-16(配置 D)的实现

python 复制代码
# 教学用:手写 VGG-16 的构建函数(简化版,不含 BN)
import torch.nn.functional as F
import torch

def make_vgg_layers(cfg):
    layers = []
    in_channels = 3
    for v in cfg:
        if v == 'M':
            layers += [nn.MaxPool2d(kernel_size=2, stride=2)]
        else:
            layers += [
                nn.Conv2d(in_channels, v, kernel_size=3, padding=1),
                nn.ReLU(inplace=True)
            ]
            in_channels = v
    return nn.Sequential(*layers)

# cfg for VGG-16 (D)
cfg_D = [64, 64, 'M',
         128, 128, 'M',
         256, 256, 256, 'M',
         512, 512, 512, 'M',
         512, 512, 512, 'M']

class VGG16Custom(nn.Module):
    def __init__(self, num_classes=1000, init_weights=True):
        super().__init__()
        self.features = make_vgg_layers(cfg_D)
        self.classifier = nn.Sequential(
            nn.Linear(512*7*7, 4096),
            nn.ReLU(True),
            nn.Dropout(),
            nn.Linear(4096, 4096),
            nn.ReLU(True),
            nn.Dropout(),
            nn.Linear(4096, num_classes)
        )
        if init_weights:
            self._initialize_weights()
    def forward(self, x):
        x = self.features(x)
        x = torch.flatten(x, 1)
        x = self.classifier(x)
        return x
    def _initialize_weights(self):
        for m in self.modules():
            if isinstance(m, nn.Conv2d):
                nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu')
                if m.bias is not None:
                    nn.init.constant_(m.bias, 0)
            elif isinstance(m, nn.Linear):
                nn.init.normal_(m.weight, 0, 0.01)
                nn.init.constant_(m.bias, 0)

# 使用举例
model = VGG16Custom(num_classes=10).to(device)

训练/微调的示例骨架(CIFAR-10)

python 复制代码
# 训练骨架(略去数据加载细节)
import torch.optim as optim
from torch.optim import lr_scheduler

criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0.9, weight_decay=5e-4)
scheduler = lr_scheduler.StepLR(optimizer, step_size=30, gamma=0.1)  # 每 30 epoch lr*0.1

num_epochs = 50
for epoch in range(num_epochs):
    model.train()
    for imgs, labels in train_loader:
        imgs, labels = imgs.to(device), labels.to(device)
        optimizer.zero_grad()
        outputs = model(imgs)
        loss = criterion(outputs, labels)
        loss.backward()
        optimizer.step()
    scheduler.step()

    # 验证部分省略:计算 val_loss / val_acc 并记录用于作图

数据增强(CIFAR-10 推荐):RandomCrop(32, padding=4), RandomHorizontalFlip(), Normalize(mean,std)

PyTorch 官方 vgg16 / vgg16_bn 的细节参见 torchvision 文档。([PyTorch Docs][2])


7. 训练与评估(超参、训练曲线、结果呈现建议)

建议超参数(可作为 baseline)

  • dataset:CIFAR-10(演示)或 ImageNet(实际训练需大量资源)
  • batch_size:128(显存允许越大越稳)
  • optimizer:SGD(momentum=0.9)或 Adam(调参)
  • lr:0.01(SGD)或 1e-3(Adam),配合 lr scheduler(StepLR / CosineAnnealing)
  • weight_decay:5e-4(控制过拟合)
  • epochs:50--200(视数据集大小而定)
  • 数据增强:随机裁剪、水平翻转、颜色抖动(视情况)

训练曲线绘制(示例)

  • 每 epoch 记录 train_loss, val_loss, train_acc, val_acc,使用 matplotlib 绘图:
python 复制代码
import matplotlib.pyplot as plt
plt.figure()
plt.plot(epochs, train_losses, label='train_loss')
plt.plot(epochs, val_losses, label='val_loss')
plt.legend(); plt.xlabel('epoch'); plt.ylabel('loss')
  • 典型现象:train_loss 下降,val_loss 在开始下降后趋于平稳或上升(过拟合)→ 可加入更多增强/减小 FC/加 BN/用 dropout。

示例结果(说明)

  • 说明 :我无法在这里替你训练模型拿到具体曲线,但用上面的超参和 CIFAR-10 数据增强,VGG-16(做迁移学习或从头训练)通常能达到 约 90%+ 的 top-1 准确率(取决于训练时长与增强策略)。如果需要,我可以给出一个更精确的训练脚本和绘图代码(但当前回答包含了足够骨架,让你直接运行)。(注:不同实现/数据预处理会影响最终数值)

8. 实验扩展(对比/消融实验建议)

若想在文章里做小实验来展示设计选择的影响,这里给出常见对比项与预期结论:

  1. 3×3 堆叠 vs 大核(5×5 / 7×7)

    • 实验:用 2×3×3 替代 1×5×5,或用单 7×7 替代三 3×3;比较参数、准确率与训练收敛速度。
    • 预期:堆叠小核在参数相近或更少的情况下表现更好(且非线性更多)。
  2. ReLU vs LeakyReLU / ELU

    • 实验:把所有 ReLU 替换为 LeakyReLU,比较收敛和最后精度。
    • 预期:对大多数任务 ReLU 是稳健选择,其他激活在特定任务上可能有微小提升。
  3. MaxPool vs AvgPool

    • 实验:把每个 max-pool 换成 avg-pool,或用 stride=2 卷积实现下采样。
    • 预期:max-pool 通常保留更锐利的特征(分类任务常更优);avg-pool 可以在某些特征统计任务上更稳定。
  4. BatchNorm(VGG_BN)

    • 实验:VGG-16 与 VGG-16-BN 对比(相同 lr/optimizer),观察收敛速度与最终精度。
    • 预期:BN 能显著加速训练并提高稳定性(更容易用较大学习率训练)。PyTorch 的 vgg16_bn 可直接使用。([PyTorch Docs][3])
  5. 数据增强(与否)

    • 实验:无增强 / 基础增强 / 强增强(Cutout, Mixup)对比。
    • 预期:合理增强能显著提高泛化与 val 精度,尤其在小数据集上效果明显。

9. 总结与实践建议

  • VGG 的价值不仅在于当时的比赛成绩,更在于它提出并验证了"深层 + 小卷积核"的设计范式(简单、统一、易迁移),对后续网络设计影响深远(ResNet/Inception 等都是在这类设计基础上进一步改进)。
  • 如果你资源有限:优先使用预训练的 VGG 并做微调(替换最后一层 / 冻结前几层),而不是从头训练 ImageNet。PyTorch torchvision 提供了便捷的接口与权重。([PyTorch Docs][2])
  • 若关注模型轻量化或部署效率:考虑移除大 FC 层(换成 GAP)或改用更现代且轻量的 backbone(ResNet / MobileNet / EfficientNet 等)。

相关推荐
martinzh2 小时前
检索器江湖:那些让RAG神功大成的武林绝学
人工智能
Dersun2 小时前
python学习进阶之异常和文件操作(三)
开发语言·python·学习·json
Juchecar2 小时前
通过“单词补全”演示 Transformer 原理(Python代码可运行)
人工智能·python
c8i2 小时前
关于python中的钩子方法和内置函数的举例
python
禁默2 小时前
第六届机器学习与计算机应用国际学术会议
运维·人工智能·机器学习·自动化
念念01072 小时前
基于机器学习的P2P网贷平台信用违约预测模型
人工智能·机器学习
悟乙己3 小时前
机器学习超参数调优全方法介绍指南
人工智能·机器学习·超参数
阿里云大数据AI技术3 小时前
Mem0 + Milvus:为人工智能构建持久化长时记忆
人工智能