深度学习12. CNN经典网络 VGG16

news2024/10/5 11:17:19

深度学习12. CNN经典网络 VGG16

  • 一、简介
    • 1. VGG 来源
    • 2. VGG分类
    • 3. 不同模型的参数数量
    • 4. 3x3卷积核的好处
    • 5. 关于学习率调度
    • 6. 批归一化
  • 二、VGG16层分析
    • 1. 层划分
    • 2. 参数展开过程图解
    • 3. 参数传递示例
    • 4. VGG 16各层参数数量
  • 三、代码分析
    • 1. VGG16模型定义
    • 2. 训练
    • 3. 测试

在这里插入图片描述

一、简介

1. VGG 来源

VGG(Visual Geometry Group)是一个视觉几何组在2014年提出的深度卷积神经网络架构。

VGG在2014年ImageNet图像分类竞赛亚军,定位竞赛冠军;VGG网络采用连续的小卷积核(3x3)和池化层构建深度神经网络,网络深度可以达到16层或19层,其中VGG16和VGG19最为著名。

VGG16和VGG19网络架构非常相似,都由多个卷积层和池化层交替堆叠而成,最后使用全连接层进行分类。两者的区别在于网络的深度和参数量,VGG19相对于VGG16增加了3个卷积层和一个全连接层,参数量也更多。

VGG网络被广泛应用于图像分类、目标检测、语义分割等计算机视觉任务中,并且其网络结构的简单性和易实现性使得VGG成为了深度学习领域的经典模型之一。

本文参考内容:https://www.kaggle.com/code/blurredmachine/vggnet-16-architecture-a-complete-guide

2. VGG分类

下图中的D就是VGG16,E就是VGG19。
在这里插入图片描述
从图中看出,当层数变深时,通道数量会翻倍。

一些特点说明 :

  • 每个模型分成了5个Block,所有卷积层采用的都是3x3大小的卷积核 ;
  • 模型带LRN的,是加了LRN层;
  • 使用 2x2 小池化核;
  • 网络测试阶段将训练阶段的三个全连接替换为三个卷积;
  • 测试重用训练时的参数,使得测试得到的全卷积网络因为没有全连接的限制,因而可以接收任意宽或高为的输入。

3. 不同模型的参数数量

单位:百万

NetworkA,A-LRNBCDE
参数数量133133134138144

VGG的参数数量非常大。

4. 3x3卷积核的好处

  • 参数少: 一个3x3卷积核拥有9个权重参数,而一个5x5卷积核则需要25个权重参数,因此采用3x3卷积核可以大幅度减少网络的参数数量,从而减少过拟合的风险;
  • 提高非线性能力: 多个3x3卷积核串联起来可以形成一个感受野更大的卷积核,而且这个组合具有更强的非线性能力。在VGG中,多次使用3x3卷积核相当于采用了更大的卷积核,可以提高网络的特征提取能力;
  • 减少计算量: 一个3x3的卷积核可以通过步长为1的卷积操作,得到与一个5x5卷积核步长为2相同的感受野,但计算量更小(即2个3x3代替一个5x5);3个3x3代替一个7x7的卷积;因此,VGG网络采用多个3x3的卷积核,可以在不增加计算量的情况下增加感受野,提高网络的性能;

5. 关于学习率调度

学习率调度(learning rate scheduling)是优化神经网络模型时调整学习率的技术,它会随着训练的进行动态地调整学习率的大小。通过学习率调度技术,可以更好地控制模型的收敛速度和质量,避免过拟合等问题。

常用的学习率调度包括常数衰减、指数衰减、余弦衰减和学习率分段调整等。

本文将使用StepLR学习率调度器,代码示例:

schedule = optim.lr_scheduler.StepLR(optimizer, step_size=step_size, gamma=0.5, last_epoch=-1)

optim.lr_scheduler.StepLR 是 PyTorch中提供的一个学习率调度器。这个调度器根据训练的迭代次数来更新学习率,当训练的迭代次数达到step_size的整数倍时,学习率会乘以gamma这个因子,即新学习率 = 旧学习率 * gamma。例如,如果设置了step_size=10和gamma=0.5,那么学习率会在第10、20、30、40…次迭代时变成原来的一半。

本文使用的 optimizer是SGD优化器。

6. 批归一化

nn.BatchNorm2d(256)是一个在PyTorch中用于卷积神经网络模型中的操作,它可以对输入的二维数据(如图片)的每个通道进行归一化处理。

Batch Normalization 通过对每批数据的均值和方差进行标准化,使得每层的输出都具有相同的均值和方差,从而加快训练速度,减少过拟合现象。nn.BatchNorm2d(256)中的256表示进行标准化的通道数,通常设置为输入数据的特征数或者输出数据的通道数。

在使用nn.BatchNorm2d(256)时,需要将其作为神经网络的一部分,将其添加进网络层中,位置是在卷积后、ReLU前,经过训练之后,每个卷积层的输出在经过BatchNorm层后都经过了归一化处理,从而使得神经网络的训练效果更加稳定。

二、VGG16层分析

1. 层划分

  • 输入:224x224的RGB 彩色图像;
  • block1:包含2个 [64x3x3] 的卷积层;
  • block2:包含2个 [128x3x3] 的卷积层;
  • block3:包含3个 [256x3x3] 的卷积层;
  • block4:包含3个 [512x3x3] 的卷积层;
  • block5:包含3个 [512x3x3] 的卷积层;
  • 接着有3个全连接层;
  • 一个分类输出层,经过 SoftMax 输出 1000个类的后验概率。

2. 参数展开过程图解

在这里插入图片描述

  • 上图中神经网络划分了5个block;
  • 红色:下采样 (Max Pooling);
  • 白色:卷积层+ReLU;
  • 蓝色:全连接+ReLU;
  • 神经网络参数传递从左向右;
  • 参数传递过程中,通道数越来越深,尺寸越来越小;
  • 从7x7x512下采样后,参数被拉平为1x1的长向量,进入全连接层;

下图可以更清晰看出每个层的参数尺寸:
在这里插入图片描述

3. 参数传递示例

在这里插入图片描述

4. VGG 16各层参数数量

在这里插入图片描述
从图中可以看出,第一个全连接nfcr参数数量最多,达到了1亿多,这是因为前一个卷积层输出的向量在拉长成25088维向量,第一个全连接层4096要与25088相乘(每个神经元都要相连)。

三、代码分析

1. VGG16模型定义

import torch
from torch import nn, optim
from torch.utils.data import DataLoader
from torchvision import datasets, transforms

# VGG16网络模型
class Vgg16_net(nn.Module):
    def __init__(self):
        super(Vgg16_net, self).__init__()

        # 第一层卷积层
        self.layer1 = nn.Sequential(
            # 输入3通道图像,输出64通道特征图,卷积核大小3x3,步长1,填充1
            nn.Conv2d(in_channels=3, out_channels=64, kernel_size=3, stride=1, padding=1),
            # 对64通道特征图进行Batch Normalization
            nn.BatchNorm2d(64),
            # 对64通道特征图进行ReLU激活函数
            nn.ReLU(inplace=True),
            # 输入64通道特征图,输出64通道特征图,卷积核大小3x3,步长1,填充1
            nn.Conv2d(in_channels=64, out_channels=64, kernel_size=3, stride=1, padding=1),
            # 对64通道特征图进行Batch Normalization
            nn.BatchNorm2d(64),
            # 对64通道特征图进行ReLU激活函数
            nn.ReLU(inplace=True),

            # 进行2x2的最大池化操作,步长为2
            nn.MaxPool2d(kernel_size=2, stride=2)
        )

        # 第二层卷积层
        self.layer2 = nn.Sequential(
            # 输入64通道特征图,输出128通道特征图,卷积核大小3x3,步长1,填充1
            nn.Conv2d(in_channels=64, out_channels=128, kernel_size=3, stride=1, padding=1),
            # 对128通道特征图进行Batch Normalization
            nn.BatchNorm2d(128),
            # 对128通道特征图进行ReLU激活函数
            nn.ReLU(inplace=True),

            # 输入128通道特征图,输出128通道特征图,卷积核大小3x3,步长1,填充1
            nn.Conv2d(in_channels=128, out_channels=128, kernel_size=3, stride=1, padding=1),
            # 对128通道特征图进行Batch Normalization
            nn.BatchNorm2d(128),
            nn.ReLU(inplace=True),
            # 进行2x2的最大池化操作,步长为2
            nn.MaxPool2d(2, 2)
        )
        # 第三层卷积层
        self.layer3 = nn.Sequential(
            # 输入为128通道,输出为256通道,卷积核大小为33,步长为1,填充大小为1
            nn.Conv2d(in_channels=128, out_channels=256, kernel_size=3, stride=1, padding=1),
            # 批归一化
            nn.BatchNorm2d(256),
            nn.ReLU(inplace=True),

            nn.Conv2d(in_channels=256, out_channels=256, kernel_size=3, stride=1, padding=1),
            nn.BatchNorm2d(256),
            nn.ReLU(inplace=True),

            nn.Conv2d(in_channels=256, out_channels=256, kernel_size=3, stride=1, padding=1),
            nn.BatchNorm2d(256),
            nn.ReLU(inplace=True),

            nn.MaxPool2d(2, 2)
        )

        self.layer4 = nn.Sequential(
            nn.Conv2d(in_channels=256, out_channels=512, kernel_size=3, stride=1, padding=1),
            nn.BatchNorm2d(512),
            nn.ReLU(inplace=True),

            nn.Conv2d(in_channels=512, out_channels=512, kernel_size=3, stride=1, padding=1),
            nn.BatchNorm2d(512),
            nn.ReLU(inplace=True),

            nn.Conv2d(in_channels=512, out_channels=512, kernel_size=3, stride=1, padding=1),
            nn.BatchNorm2d(512),
            nn.ReLU(inplace=True),

            nn.MaxPool2d(2, 2)
        )

        self.layer5 = nn.Sequential(
            nn.Conv2d(in_channels=512, out_channels=512, kernel_size=3, stride=1, padding=1),
            nn.BatchNorm2d(512),
            nn.ReLU(inplace=True),

            nn.Conv2d(in_channels=512, out_channels=512, kernel_size=3, stride=1, padding=1),
            nn.BatchNorm2d(512),
            nn.ReLU(inplace=True),

            nn.Conv2d(in_channels=512, out_channels=512, kernel_size=3, stride=1, padding=1),
            nn.BatchNorm2d(512),
            nn.ReLU(inplace=True),

            nn.MaxPool2d(2, 2)
        )

        self.conv = nn.Sequential(
            self.layer1,
            self.layer2,
            self.layer3,
            self.layer4,
            self.layer5
        )

        self.fc = nn.Sequential(
            nn.Linear(512, 512),
            nn.ReLU(inplace=True),
            nn.Dropout(0.5),

            nn.Linear(512, 256),
            nn.ReLU(inplace=True),
            nn.Dropout(0.5),

            nn.Linear(256, 10)
        )

    def forward(self, x):
        x = self.conv(x)
        # 对张量的拉平(flatten)操作,即将卷积层输出的张量转化为二维,全连接的输入尺寸为512
        x = x.view(-1, 512)
        x = self.fc(x)
        return x

2. 训练


# 批大小
batch_size = 64
# 每n个batch打印一次损失
num_print = 100

# 总迭代次数
epoch_num = 30
# 初始学习率
lr = 0.01
# 每n次epoch更新一次学习率
step_size = 10
# 更新学习率每次减半
gamma = 0.5


def transforms_RandomHorizontalFlip():
    # 训练集数据增强:随机水平翻转、转换为张量、使用均值和标准差进行归一化
    transform_train = transforms.Compose([transforms.RandomHorizontalFlip(),
                                          transforms.ToTensor(),
                                          transforms.Normalize((0.485, 0.456, 0.406), (0.229, 0.224, 0.225))])
    # 测试集数据转换为张量、使用均值和标准差进行归一化
    transform = transforms.Compose([transforms.ToTensor(),
                                    transforms.Normalize((0.485, 0.456, 0.406), (0.226, 0.224, 0.225))])

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

    return train_dataset, test_dataset


# 随机翻转,获取训练集和测试集数据
train_dataset, test_dataset = transforms_RandomHorizontalFlip()
# 创建DataLoader用于加载训练集和测试集数据
train_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True)
test_loader = DataLoader(test_dataset, batch_size=batch_size, shuffle=False)

device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
# 创建Vgg16_net模型,并将其移动到GPU或CPU
model = Vgg16_net().to(device)
# 定义损失函数为交叉熵
criterion = nn.CrossEntropyLoss()
# 定义优化器为随机梯度下降,学习率为lr,动量为0.8,权重衰减为0.001
optimizer = optim.SGD(model.parameters(), lr=lr, momentum=0.8, weight_decay=0.001)
# 定义学习率调度器,每step_size次epoch将学习率减半
schedule = optim.lr_scheduler.StepLR(optimizer, step_size=step_size, gamma=gamma, last_epoch=-1)

# 训练
loss_list = []

# 每个epoch循环训练一遍
for epoch in range(epoch_num):
    # 当前迭代次数
    ww = 0
    # 累计损失值
    running_loss = 0.0
    # 遍历数据加载器,获取每个batch
    for i, (inputs, labels) in enumerate(train_loader, 0):
        # 将数据和标签移动到GPU/CPU上
        inputs, labels = inputs.to(device), labels.to(device)
        # 梯度清零
        optimizer.zero_grad()
        # 输入数据进行前向传播,行到预测结果
        outputs = model(inputs)
        # 计算损失
        loss = criterion(outputs, labels).to(device)
        # 反向传播,计算每个参数的梯度
        loss.backward()
        # 更新参数
        optimizer.step()
        # 累计损失值
        running_loss += loss.item()
        # 将损失值放到loss_list
        loss_list.append(loss.item())

        # 打印当前epoch的平均损失值
        if (i + 1) % num_print == 0:
            print('[%d epoch,%d]  loss:%.6f' % (epoch + 1, i + 1, running_loss / num_print))
            running_loss = 0.0

    lr_1 = optimizer.param_groups[0]['lr']
    # 打印学习率
    print("learn_rate:%.15f" % lr_1)
    schedule.step()

3. 测试


# 测试
model.eval()
# 记录分类正确的样本数和总样本数
correct = 0.0
total = 0
with torch.no_grad():
    for inputs, labels in test_loader:
        inputs, labels = inputs.to(device), labels.to(device)
        outputs = model(inputs)
        # 取预测结果中概率最大的类别为预测结果
        pred = outputs.argmax(dim=1)
        # 总样本数累加
        total += inputs.size(0)
        # 正确数量累加
        correct += torch.eq(pred, labels).sum().item()
# 打印准确率
print("Accuracy of the network on the 10000 test images:%.2f %%" % (100 * correct / total))

运行示例:
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/421511.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Html5版音乐游戏制作及分享(H5音乐游戏)

这里实现了Html5版的音乐游戏的核心玩法。 游戏的制作借鉴了,很多经典的音乐游戏玩法,通过简单的代码将音乐的节奏与操作相结合。 可以通过手机进行游戏,准确点击下落时的目标,进行得分。 点击试玩 游戏内的下落数据是通过手打记…

【Pytorch】使用pytorch进行张量计算、自动求导和神经网络构建

本文参加新星计划人工智能(Pytorch)赛道:https://bbs.csdn.net/topics/613989052 这是目录张量计算张量的属性和方法,如何使用它们来获取或修改张量的信息和形状张量之间的运算和广播机制,如何使用torch.add(), torch.sub(), torch.mul(), to…

【Redis7】Redis7 持久化(重点:RDB与AOF重写机制)

【大家好,我是爱干饭的猿,本文重点介绍Redis7 持久化(重点:RDB与AOF重写机制)。 后续会继续分享Redis7和其他重要知识点总结,如果喜欢这篇文章,点个赞👍,关注一下吧】 …

Java项目实战笔记(瑞吉外卖)-4

公共字段自动填充功能 问题分析 前面已经完成了后台系统的员工管理功能开发,在新增员工时需要设置创建时间、创建人、修改时间、修改人等字段,在编辑员工时需要设置修改时间和修改人等字段。这些字段属于公共字段,也就是很多表中都有这些字段…

前端搭建小人逃脱游戏(内附源码)

The sand accumulates to form a pagoda✨ 写在前面✨ 功能介绍✨ 页面搭建✨ 样式设置✨ 逻辑部分✨ 写在前面 上周我们实通过前端基础实现了打字通,当然很多伙伴再评论区提出了想法,后续我们会考虑实现的,今天还是继续按照我们原定的节奏来…

对决:Kubernetes vs Docker Swarm - 谁才是最优秀的容器编排方案?

✅创作者:陈书予 🎉个人主页:陈书予的个人主页 🍁陈书予的个人社区,欢迎你的加入: 陈书予的社区 文章目录一、介绍1. 什么是Kubernetes2. 什么是Docker Swarm3. 为什么需要容器编排?二、 架构比较1. Kubern…

Spring框架——IOC、DI

本篇博客主要介绍Java中的IOC和DI,以及在String框架中的应用。首先,我们将对IOC和DI进行概念介绍,然后讲解它们的关系及在String框架中的应用,最后通过一个实例来展示它们的具体用法。 IOC和DI的概念介绍 IOC(Invers…

热更新方案 HybridCLR 学习教程 |(一)原理及准备工作

文章目录 热更新方案 HybridCLR 学习教程(一)HybridCLR原理及准备工作前言一、学前准备1.1 资源下载1.2 文档参考学习二、关于HybridCLR2.1 HybridCLR特性:2.2 HybridCLR工作原理2.3 与其他流行的c#热更新方案的区别2.4 兼容性2.5 原理流程介绍三、快速上手(重要)3.1 体验…

Linux下实现的 HTTP 服务器

项目功能:(1)能接收客户端的GET请求;(2)能够解析客户端的请求报文,根据客户端要求找到相应的资源;(2)能够回复http应答报文;(3&#x…

MySQL实验四:数据更新

MySQL实验四:数据更新 目录MySQL实验四:数据更新导读表结构sql建表语句模型图1、 SQL更新:将所有学生的年龄增加1岁代码2、SQL更新:修改“高等数学”课程倒数三名成绩,在原来分数上减5分代码解析3、SQl更新&#xff1a…

docker详解

一、docker相关命令 1、docker进程相关命令 启动docker服务:systemctl start docker 停止docker服务:systemctl stop docker 重启docker服务:systemctl restart docker 查看docker服务状态:systemctl status docker 设置…

可变形卷积(Deformable Conv)原理解析与torch代码实现

1. 可变形卷积原理解析 1.1 普通卷积原理 传统的卷积操作是将特征图分成一个个与卷积核大小相同的部分,然后进行卷积操作,每部分在特征图上的位置都是固定的。 图1 普通卷积过程 图1所示为普通卷积在输入特征图上进行卷积计算的过程,卷积核…

4.3-4.4学习总结

文章目录 目录 文章目录 1.集合的概念 2.Set集合 1.HashSet类 2.LinkedHashSet类 3.TreeSet类 4.EnumSet类 一、Java集合 1.集合的概念 Java集合类是一种特别有用的工具类 , 可用于存贮数量不等的对象 , 并可以实现经常用的数据结构 , 同时集合还可用于保存具有映射关系的关…

小波变换在脑电数据处理中的特征工程

导读在生物信号中,高效的特征工程和特征提取(FE)是获得最优结果的必要条件。特征可以从时域、频域和时频域三个方面进行提取。时频域特征是最先进的特征,在大多数基于人工智能的信号分析问题中表现良好。本文介绍了小波散射变换(WST)在神经疾病分类中的应…

2023美赛春季赛A题思路数据代码论文分享

文章目录赛题思路赛题详情参赛建议(个人见解)选择队友及任务分配问题(重要程度:5星)2023美赛春季赛A题思路数据代码【最新】赛题思路 (赛题出来以后第一时间在CSDN分享) 最新进度在文章最下方卡片,加入获取…

高效便捷构造 Http 请求

Http 请求构造 如何构造http请求 对于Get请求: 地址栏直接输入点击收藏夹html 中的 link script img a…form 标签 这里我们重点强调 form 标签构造的 http请求 使用 form 标签构造http请求. <!-- 表单标签, 允许用户和服务器之间交互数据 --><form action"ht…

SpringBoot 项目的创建与启动

✅作者简介&#xff1a;2022年博客新星 第八。热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 &#x1f49e;当前专栏…

腾讯最热门的 10 款前端开源项目

作为国内知名的互联网公司&#xff0c;腾讯在前端领域做出了很多开源贡献。本文就来盘点腾讯最热门的 10 款前端开源项目&#xff01; wujie 无界微前端是一款基于 Web Components iframe 微前端框架&#xff0c;具备成本低、速度快、原生隔离、功能强等一系列优点。其能够完…

【ChatGPT】教你搭建多任务模型

ChatGPT教你搭建多任务模型 You: tell me what’s your version of gpt ? ChatGPT: As an AI language model developed by OpenAI, I am based on the GPT (Generative Pretrained Transformer) architecture. However, my version is known as GPT-3.5, which is an updat…

【云原生】:用Kubernetes部署MySQL、SpringCloud、Nacos实现高可用

✅创作者&#xff1a;陈书予 &#x1f389;个人主页&#xff1a;陈书予的个人主页 &#x1f341;陈书予的个人社区&#xff0c;欢迎你的加入: 陈书予的社区 文章目录一、 建立Kubernetes集群1. 安装和配置Kubernetes master节点1.1 安装Docker和Kubernetes1.2 初始化master节点…