深度学习篇---VGGNet网络结构

在 PyTorch 中实现 VGGNet 非常直观,因为它的结构极其规整 ------ 全是 3×3 卷积层和 2×2 池化层的组合,就像搭积木一样按固定模式堆叠。我们以最经典的 VGG-16 为例,一步步实现,保证零基础你能看懂每一行代码的作用。

一、先明确 VGG-16 的核心结构(复习)

VGG-16 的结构可以用一句话概括:5 个卷积块(每个块含多个 3×3 卷积层)+3 个全连接层,每个卷积块后都接 1 个 2×2 的最大池化层。具体来说:

复制代码
输入(224×224彩色图) → 
卷积块1(2个3×3卷积) → 池化 → 
卷积块2(2个3×3卷积) → 池化 → 
卷积块3(3个3×3卷积) → 池化 → 
卷积块4(3个3×3卷积) → 池化 → 
卷积块5(3个3×3卷积) → 池化 → 
全连接层1(4096) → 全连接层2(4096) → 输出层(1000类)

二、PyTorch 实现 VGG-16 的步骤

步骤 1:导入必要的库

和之前实现 LeNet、AlexNet 一样,先准备好工具:

python 复制代码
import torch  # 核心库
import torch.nn as nn  # 神经网络层
import torch.optim as optim  # 优化器
from torch.utils.data import DataLoader  # 数据加载器
from torchvision import datasets, transforms  # 图像数据处理

步骤 2:定义 VGG-16 网络结构

VGG 的精髓是 "用配置列表定义卷积块",避免重复写代码。我们先定义每个卷积块的 "卷积层数量" 和 "输出通道数",再用循环构建网络。

python 复制代码
class VGG16(nn.Module):
    def __init__(self, num_classes=1000):
        super(VGG16, self).__init__()
        # 定义卷积块的配置:(卷积层数量, 输出通道数)
        # 5个卷积块对应VGG-16的结构
        config = [
            (2, 64),   # 卷积块1:2个卷积层,输出64通道
            (2, 128),  # 卷积块2:2个卷积层,输出128通道
            (3, 256),  # 卷积块3:3个卷积层,输出256通道
            (3, 512),  # 卷积块4:3个卷积层,输出512通道
            (3, 512)   # 卷积块5:3个卷积层,输出512通道
        ]
        
        # 构建卷积部分
        layers = []
        in_channels = 3  # 输入是3通道彩色图
        for (num_convs, out_channels) in config:
            # 每个卷积块内堆叠num_convs个3×3卷积层
            for _ in range(num_convs):
                layers.append(nn.Conv2d(
                    in_channels=in_channels,
                    out_channels=out_channels,
                    kernel_size=3,  # 固定3×3卷积核
                    padding=1       # 边缘填充1像素,保证卷积后尺寸不变
                ))
                layers.append(nn.ReLU(inplace=True))  # ReLU激活
                in_channels = out_channels  # 更新输入通道数为当前输出通道数
            # 每个卷积块后接1个2×2最大池化层(步长2,尺寸减半)
            layers.append(nn.MaxPool2d(kernel_size=2, stride=2))
        
        # 把卷积层组合成Sequential
        self.features = nn.Sequential(*layers)
        
        # 构建全连接部分
        self.classifier = nn.Sequential(
            # 全连接层1:输入是7×7×512(池化后特征图),输出4096
            nn.Linear(512 * 7 * 7, 4096),
            nn.ReLU(inplace=True),
            nn.Dropout(p=0.5),  # Dropout防止过拟合
            
            # 全连接层2:输入4096,输出4096
            nn.Linear(4096, 4096),
            nn.ReLU(inplace=True),
            nn.Dropout(p=0.5),
            
            # 输出层:输入4096,输出类别数
            nn.Linear(4096, num_classes)
        )
    
    # 定义数据流向
    def forward(self, x):
        x = self.features(x)  # 经过卷积部分
        x = x.view(x.size(0), 512 * 7 * 7)  # 拉平特征图(batch_size, 512×7×7)
        x = self.classifier(x)  # 经过全连接部分
        return x

关键解释

  • 配置列表 config :用元组(卷积层数量, 输出通道数)定义每个卷积块,让代码更简洁(改数字就能调整网络结构)。
  • 3×3 卷积 + padding=1:保证卷积后特征图尺寸不变(比如 224×224 的图经过 3×3 卷积 + padding=1 后还是 224×224)。
  • 池化层作用:每个卷积块后用 2×2 池化(步长 2),让特征图尺寸减半(224→112→56→28→14→7),最终得到 7×7×512 的特征图。

步骤 3:准备数据(用 CIFAR-10 演示)

VGG 原本用于 ImageNet(1000 类),我们用小型的 CIFAR-10(10 类)演示,预处理流程类似:

python 复制代码
# 数据预处理:缩放+裁剪+翻转+标准化
transform = transforms.Compose([
    transforms.Resize(256),  # 缩放为256×256
    transforms.RandomCrop(224),  # 随机裁剪成224×224(VGG输入尺寸)
    transforms.RandomHorizontalFlip(),  # 随机水平翻转(数据增强)
    transforms.ToTensor(),  # 转为Tensor
    # 标准化(用ImageNet的均值和标准差,通用做法)
    transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
])

# 加载CIFAR-10数据集
train_dataset = datasets.CIFAR10(
    root='./data', train=True, download=True, transform=transform
)
test_dataset = datasets.CIFAR10(
    root='./data', train=False, download=True, transform=transform
)

# 批量加载数据(VGG参数量大,batch_size设小一点,这里用64)
train_loader = DataLoader(train_dataset, batch_size=64, shuffle=True, num_workers=4)
test_loader = DataLoader(test_dataset, batch_size=64, shuffle=False, num_workers=4)

步骤 4:初始化模型、损失函数和优化器

  • 模型:我们定义的 VGG16(CIFAR-10 是 10 类,输出层设为 10)。
  • 损失函数:交叉熵损失(分类任务标配)。
  • 优化器:SGD + 动量(VGG 原文推荐的优化器)。
python 复制代码
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')  # 用GPU加速(必须!否则太慢)
model = VGG16(num_classes=10).to(device)  # 初始化模型,输出10类

criterion = nn.CrossEntropyLoss()  # 交叉熵损失
# 优化器:学习率0.001,动量0.9,权重衰减5e-4(防止过拟合)
optimizer = optim.SGD(model.parameters(), lr=0.001, momentum=0.9, weight_decay=5e-4)

注意:VGG-16 参数量达 1.38 亿,必须用 GPU 训练,否则单轮训练可能要几小时甚至几天。

步骤 5:训练模型

训练逻辑和 AlexNet 类似,但 VGG 更深,需要更多轮次才能收敛(这里示例训练 20 轮):

python 复制代码
def train(model, train_loader, criterion, optimizer, epoch):
    model.train()  # 训练模式
    for batch_idx, (data, target) in enumerate(train_loader):
        data, target = data.to(device), target.to(device)  # 数据放GPU
        
        optimizer.zero_grad()  # 清空梯度
        output = model(data)   # 模型预测
        loss = criterion(output, target)  # 计算损失
        loss.backward()        # 反向传播
        optimizer.step()       # 更新参数
        
        # 打印进度(每200个batch打印一次)
        if batch_idx % 200 == 0:
            print(f'Epoch {epoch}, Batch {batch_idx}, Loss: {loss.item():.6f}')

步骤 6:测试模型效果

用测试集评估准确率:

python 复制代码
def test(model, test_loader):
    model.eval()  # 评估模式(关闭Dropout)
    correct = 0
    total = 0
    with torch.no_grad():  # 不计算梯度,节省内存
        for data, target in test_loader:
            data, target = data.to(device), target.to(device)
            output = model(data)
            _, predicted = torch.max(output.data, 1)  # 取概率最大的类别
            total += target.size(0)
            correct += (predicted == target).sum().item()
    
    print(f'Test Accuracy: {100 * correct / total:.2f}%')

步骤 7:开始训练和测试

python 复制代码
# 训练20轮(实际需要更多轮次,比如50-100轮才能达到较高准确率)
for epoch in range(1, 21):
    train(model, train_loader, criterion, optimizer, epoch)
    test(model, test_loader)

训练过程中,你会看到损失逐渐下降,准确率逐渐上升。在 CIFAR-10 上,VGG-16 训练充分后准确率通常能达到 90% 左右。

三、完整代码总结

python 复制代码
import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import DataLoader
from torchvision import datasets, transforms

# 1. 定义VGG-16网络结构
class VGG16(nn.Module):
    def __init__(self, num_classes=1000):
        super(VGG16, self).__init__()
        # 卷积块配置:(卷积层数量, 输出通道数)
        config = [
            (2, 64),   # 卷积块1
            (2, 128),  # 卷积块2
            (3, 256),  # 卷积块3
            (3, 512),  # 卷积块4
            (3, 512)   # 卷积块5
        ]
        
        # 构建卷积部分
        layers = []
        in_channels = 3  # 输入为3通道彩色图
        for (num_convs, out_channels) in config:
            # 堆叠num_convs个3×3卷积层
            for _ in range(num_convs):
                layers.append(nn.Conv2d(
                    in_channels=in_channels,
                    out_channels=out_channels,
                    kernel_size=3,
                    padding=1
                ))
                layers.append(nn.ReLU(inplace=True))
                in_channels = out_channels
            # 每个卷积块后接池化层
            layers.append(nn.MaxPool2d(kernel_size=2, stride=2))
        
        self.features = nn.Sequential(*layers)
        
        # 构建全连接部分
        self.classifier = nn.Sequential(
            nn.Linear(512 * 7 * 7, 4096),
            nn.ReLU(inplace=True),
            nn.Dropout(p=0.5),
            nn.Linear(4096, 4096),
            nn.ReLU(inplace=True),
            nn.Dropout(p=0.5),
            nn.Linear(4096, num_classes)
        )
    
    def forward(self, x):
        x = self.features(x)
        x = x.view(x.size(0), 512 * 7 * 7)  # 拉平特征图
        x = self.classifier(x)
        return x

# 2. 准备CIFAR-10数据
transform = transforms.Compose([
    transforms.Resize(256),
    transforms.RandomCrop(224),
    transforms.RandomHorizontalFlip(),
    transforms.ToTensor(),
    transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
])

train_dataset = datasets.CIFAR10(
    root='./data', train=True, download=True, transform=transform
)
test_dataset = datasets.CIFAR10(
    root='./data', train=False, download=True, transform=transform
)

train_loader = DataLoader(train_dataset, batch_size=64, shuffle=True, num_workers=4)
test_loader = DataLoader(test_dataset, batch_size=64, shuffle=False, num_workers=4)

# 3. 初始化模型、损失函数和优化器
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
model = VGG16(num_classes=10).to(device)  # CIFAR-10是10类
criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(model.parameters(), lr=0.001, momentum=0.9, weight_decay=5e-4)

# 4. 训练函数
def train(model, train_loader, criterion, optimizer, epoch):
    model.train()
    for batch_idx, (data, target) in enumerate(train_loader):
        data, target = data.to(device), target.to(device)
        
        optimizer.zero_grad()
        output = model(data)
        loss = criterion(output, target)
        loss.backward()
        optimizer.step()
        
        if batch_idx % 200 == 0:
            print(f'Epoch {epoch}, Batch {batch_idx}, Loss: {loss.item():.6f}')

# 5. 测试函数
def test(model, test_loader):
    model.eval()
    correct = 0
    total = 0
    with torch.no_grad():
        for data, target in test_loader:
            data, target = data.to(device), target.to(device)
            output = model(data)
            _, predicted = torch.max(output.data, 1)
            total += target.size(0)
            correct += (predicted == target).sum().item()
    print(f'Test Accuracy: {100 * correct / total:.2f}%')

# 6. 开始训练和测试
for epoch in range(1, 21):
    train(model, train_loader, criterion, optimizer, epoch)
    test(model, test_loader)
    

四、关键知识点回顾

  1. 结构设计 :用配置列表config定义卷积块,通过循环堆叠 3×3 卷积层,结构极其规整,修改config即可得到 VGG-11、VGG-19 等其他版本。
  2. 核心技巧:3×3 卷积 + padding=1 保证尺寸不变,2×2 池化实现尺寸减半,Dropout 防止过拟合。
  3. 实现注意 :VGG 参数量极大(1.38 亿),必须用 GPU 训练;全连接层输入维度是512×7×7(最后一次池化后的特征图尺寸),别算错。

运行这段代码,你就能亲手实现这个 "结构标准化典范" 模型,感受 "小卷积核 + 深网络" 的强大特征提取能力!

相关推荐
Elastic 中国社区官方博客26 分钟前
将 agents 连接到 Elasticsearch 使用模型上下文协议 - docker
大数据·数据库·人工智能·elasticsearch·搜索引擎·docker·ai
一水鉴天36 分钟前
整体设计 修订 之1 三“先”之“基” 与范畴重构:康德先验哲学的批判性程序化实现
java·人工智能·算法
我叫侯小科1 小时前
机器学习-支持向量机
人工智能·机器学习·支持向量机
Java中文社群1 小时前
哇塞!AI编程神器Kiro,免排队畅用真Claude模型!
人工智能
IT_陈寒1 小时前
Python性能优化必知必会:7个让代码快3倍的底层技巧与实战案例
前端·人工智能·后端
即兴小索奇1 小时前
AI智能物流仓储新变革:从自动分拣到动态路径规划,破解仓储运营效率难题
人工智能·ai·商业·ai商业洞察·即兴小索奇
中草药z2 小时前
【SpringAI】快速上手,详解项目快速集成主流大模型DeepSeek,ChatGPT
人工智能·flux·sse·springai·deepseek·硅基流动·流式编程
BioRunYiXue2 小时前
FRET、PLA、Co-IP和GST pull-down有何区别? 应该如何选择?
java·服务器·网络·人工智能·网络协议·tcp/ip·eclipse
界面开发小八哥2 小时前
界面控件Telerik UI for Blazor 2025 Q2新版亮点 - AI集成全面增强
人工智能·ui·blazor·用户界面·telerik
皮皮学姐分享-ppx2 小时前
机器人行业工商注册企业基本信息数据(1958-2023年)
大数据·人工智能·python·物联网·机器人·区块链