经典卷积神经网络-VGGNet

经典卷积神经网络-VGGNet

一、背景介绍

VGG是Oxford的Visual Geometry Group的组提出的。该网络是在ILSVRC 2014上的相关工作,主要工作是证明了增加网络的深度能够在一定程度上影响网络最终的性能。VGG有两种结构,分别是VGG16和VGG19,两者并没有本质上的区别,只是网络深度不一样。

在这里插入图片描述

二、VGG-16网络结构

在这里插入图片描述

其中VGG系列具体的网络结构如下表所示:

在这里插入图片描述

如图所示,这是论文中所有VGG网络的详细信息,D列对应的为VGG-16网络。16指的是在这个网络中包含16个卷积层和全连接层(不算池化层和Softmax)。

  • VGG-16的卷积层没有那么多的超参数,在整个网络模型中,所有卷积核的大小都是 3 × 3的,并且padding为same,stride为1。所有池化层的池化核大小都是 2 × 2 的,并且步长为2。在几次卷积之后紧跟着池化,整个网络结构很规整。

  • 总共包含约1.38亿个参数,但其结构并不复杂,结构很规整,都是几个卷积层后面跟着可以压缩图像大小的池化层,同时,卷积层的卷积核数量的变化也存在一定的规律,都是池化之后图像高度宽度减半,但在下一个卷积层中通道数翻倍,这正是这种简单网络结构的一个规则。

  • VGG16相比AlexNet的一个改进是采用连续的几个3x3的卷积核代替AlexNet中的较大卷积核(11x11,7x7,5x5)。对于给定的感受野(与输出有关的输入图片的局部大小),采用堆积的小卷积核是优于采用大的卷积核,因为多层非线性层可以增加网络深度来保证学习更复杂的模式,而且代价还比较小(参数更少)。在VGG中,使用了3个3x3卷积核来代替7x7卷积核,使用了2个3x3卷积核来代替5×5卷积核,这样做的主要目的是在保证具有相同感受野的条件下,提升了网络的深度,在一定程度上提升了神经网络的效果。

  • 它的主要缺点就是需要训练的特征数量非常大。有些文章介绍了VGG-19,但通过研究发现VGG-19和VGG-16的性能表现几乎不分高下,所以很多人还是使用VGG-16,这也说明了单纯的增加网络深度,其性能不会有太大的提升。

  • 论文中还介绍了权重初始化方法,即预训练低层模型参数为深层模型参数初始化赋值。原文:网络权重初始化是非常重要的,坏的初始化会使得深度网络的梯度的不稳定导致无法学习。为了解决这个问题,我们首先在网络A中使用随机初始化进行训练。然后到训练更深的结构时,我们将第一层卷积层和最后三层全连接层的参数用网络A中的参数初始化(中间层的参数随机初始化)。

  • 论文中揭示了,随着网络深度的增加,图像的高度和宽度都以一定规律不断缩小,每次池化之后刚好缩小一半,而通道数量在不断增加,而且刚好也是在每组卷积操作后增加一倍。也就是说,图像缩小和通道增加的比例是有规律的,从这个角度看,这篇论文很吸引人。

三、VGG-16的Pytorch实现

我们可以根据:https://dgschwend.github.io/netscope/#/preset/vgg-16,来搭建VGG-16。

在这里插入图片描述

后面要将VGG-16Net应用到CIFAR10数据集上,所以对网络做了一些修改,具体代码如下:

from torch import nnclass Vgg16_Net(nn.Module):def __init__(self):super(Vgg16_Net, self).__init__()self.layer1 = nn.Sequential(# input_size = (3, 32, 32)nn.Conv2d(in_channels=3, out_channels=64, kernel_size=3, stride=1, padding=1),nn.BatchNorm2d(64),nn.ReLU(inplace=True),# input_size = (64, 32, 32)nn.Conv2d(in_channels=64, out_channels=64, kernel_size=3, stride=1, padding=1),nn.BatchNorm2d(64),nn.ReLU(inplace=True),# input_size = (64, 32, 32)nn.MaxPool2d(kernel_size=2, stride=2))self.layer2 = nn.Sequential(# input_size = (64, 16, 16)nn.Conv2d(in_channels=64, out_channels=128, kernel_size=3, stride=1, padding=1),nn.BatchNorm2d(128),nn.ReLU(inplace=True),# input_size = (128, 16, 16)nn.Conv2d(in_channels=128, out_channels=128, kernel_size=3, stride=1, padding=1),nn.BatchNorm2d(128),nn.ReLU(inplace=True),# input_size = (128, 16, 16)nn.MaxPool2d(2, 2))self.layer3 = nn.Sequential(# input_size = (128, 8, 8)nn.Conv2d(in_channels=128, out_channels=256, kernel_size=3, stride=1, padding=1),nn.BatchNorm2d(256),nn.ReLU(inplace=True),# input_size = (256, 8, 8)nn.Conv2d(in_channels=256, out_channels=256, kernel_size=3, stride=1, padding=1),nn.BatchNorm2d(256),nn.ReLU(inplace=True),# input_size = (256, 8, 8)nn.Conv2d(in_channels=256, out_channels=256, kernel_size=3, stride=1, padding=1),nn.BatchNorm2d(256),nn.ReLU(inplace=True),# input_size = (256, 8, 8)nn.MaxPool2d(2, 2))self.layer4 = nn.Sequential(# input_size = (256, 4, 4)nn.Conv2d(in_channels=256, out_channels=512, kernel_size=3, stride=1, padding=1),nn.BatchNorm2d(512),nn.ReLU(inplace=True),# input_size = (512, 4, 4)nn.Conv2d(in_channels=512, out_channels=512, kernel_size=3, stride=1, padding=1),nn.BatchNorm2d(512),nn.ReLU(inplace=True),# input_size = (512, 4, 4)nn.Conv2d(in_channels=512, out_channels=512, kernel_size=3, stride=1, padding=1),nn.BatchNorm2d(512),nn.ReLU(inplace=True),# input_size = (512, 4, 4)nn.MaxPool2d(2, 2))self.layer5 = nn.Sequential(# input_size = (512, 2, 2)nn.Conv2d(in_channels=512, out_channels=512, kernel_size=3, stride=1, padding=1),nn.BatchNorm2d(512),nn.ReLU(inplace=True),# input_size = (512, 2, 2)nn.Conv2d(in_channels=512, out_channels=512, kernel_size=3, stride=1, padding=1),nn.BatchNorm2d(512),nn.ReLU(inplace=True),# input_size = (512, 2, 2)nn.Conv2d(in_channels=512, out_channels=512, kernel_size=3, stride=1, padding=1),nn.BatchNorm2d(512),nn.ReLU(inplace=True),# input_size = (512, 2, 2)nn.MaxPool2d(2, 2)# output_size = (512, 1, 1))self.conv = nn.Sequential(self.layer1,self.layer2,self.layer3,self.layer4,self.layer5)self.fc = nn.Sequential(# input_size = 512nn.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)# -1表示自动计算行数# -1也可以改成x.size(0) 表示batch_size的大小x = x.view(-1, 512 * 1 * 1)x = self.fc(x)return x

四、案例:CIFAR-10分类问题

import time
import torch
import torchvision
from model import *
from torch.utils.data import DataLoader
import torchvision.transforms as transforms
from matplotlib import pyplot as plt# 加载数据集 拿到dataloader
def load_dataset(batch_size):train_data = torchvision.datasets.CIFAR10("../dataset/CIFAR10", train=True, download=True, transform=transforms.ToTensor())test_data = torchvision.datasets.CIFAR10("../dataset/CIFAR10", train=False, download=True, transform=transforms.ToTensor())train_dataloader = DataLoader(train_data, batch_size=batch_size, shuffle=True, num_workers=2)test_dataloader = DataLoader(test_data, batch_size=batch_size, shuffle=False, num_workers=2)return train_dataloader, test_dataloader# 模型训练
def train(model, train_dataloader, criterion, optimizer, epochs, device, num_print, lr_scheduler=None, test_dataloader=None):# 记录train和test的acc方便绘制学习曲线record_train = list()record_test = list()# 开始训练model.train()for epoch in range(epochs):print("========== epoch: [{}/{}] ==========".format(epoch + 1, epochs))# total记录样本数 correct记录正确预测样本数total, correct, train_loss = 0, 0, 0start = time.time()# 结合enumerate函数和迭代器的unpacking 可以在获取数据的同时获取该批次数据对应的索引for i, (image, target) in enumerate(train_dataloader):image, target = image.to(device), target.to(device)output = model(image)loss = criterion(output, target)optimizer.zero_grad()loss.backward()optimizer.step()train_loss += loss.item()total += target.size(0)correct += (output.argmax(dim=1) == target).sum().item()train_acc = 100.0 * correct / totalif (i + 1) % num_print == 0:print("step: [{}/{}], train_loss: {:.3f} | train_acc: {:6.3f}% | lr: {:.6f}".format(i + 1,len(train_dataloader), train_loss / (i + 1), train_acc, get_cur_lr(optimizer)))# 更新当前优化器的学习率if lr_scheduler is not None:lr_scheduler.step()print("--- cost time: {:.4f}s ---".format(time.time() - start))if test_dataloader is not None:record_test.append(test(model, test_dataloader, criterion, device))record_train.append(train_acc)# 保存当前模型torch.save(model.state_dict(), "train_model/VGG-16Net_{}.pth".format(epoch + 1))return record_train, record_test# 模型测试
def test(model, test_dataloader, criterion, device):# total记录样本数 correct记录正确预测样本数total, correct = 0, 0# 开始测试model.eval()with torch.no_grad():print("*************** test ***************")for X, y in test_dataloader:X, y = X.to(device), y.to(device)output = model(X)loss = criterion(output, y)total += y.size(0)correct += (output.argmax(dim=1) == y).sum().item()test_acc = 100.0 * correct / totalprint("test_loss: {:.3f} | test_acc: {:6.3f}%".format(loss.item(), test_acc))print("************************************\n")# 记得重新调用model.train()model.train()return test_acc# 获取当前的学习率 这里直接返回了第一个参数分组的学习率
def get_cur_lr(optimizer):for param_group in optimizer.param_groups:return param_group['lr']# 绘制学习曲线
def learning_curve(record_train, record_test=None):# 设置 Matplotlib 图形样式# ggplot2 是一个用于数据可视化的流行 R 语言包,以其优雅和灵活的语法而闻名plt.style.use("ggplot")plt.plot(range(1, len(record_train) + 1), record_train, label="train acc")if record_test is not None:plt.plot(range(1, len(record_test) + 1), record_test, label="test acc")plt.legend(loc=4)plt.title("learning curve")plt.xticks(range(0, len(record_train) + 1, 5))plt.yticks(range(0, 101, 5))plt.xlabel("epoch")plt.ylabel("accuracy")plt.show()# 定义超参数
BATCH_SIZE = 128
NUM_EPOCHS = 20
NUM_CLASSES = 10
LEARNING_RATE = 0.02
MOMENTUM = 0.9
WEIGHT_DECAY = 0.0005
NUM_PRINT = 100
DEVICE = "cuda" if torch.cuda.is_available() else "cpu"def main():model = Vgg16_Net()model = model.to(DEVICE)# 加载数据train_dataloader, test_dataloader = load_dataset(BATCH_SIZE)# 定义损失函数criterion = nn.CrossEntropyLoss()# 定义优化器optimizer = torch.optim.SGD(model.parameters(),lr=LEARNING_RATE,momentum=MOMENTUM,weight_decay=WEIGHT_DECAY,nesterov=True)# 定义学习率调度器lr_scheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size=7, gamma=0.1)# 进行训练 返回训练集正确率和测试集正确率record_train, record_test = train(model, train_dataloader, criterion, optimizer, NUM_EPOCHS, DEVICE, NUM_PRINT, lr_scheduler, test_dataloader)# 绘制学习曲线learning_curve(record_train, record_test)if __name__ == '__main__':main()

查看训练结果可以发现,测试集正确率基本保持在87.3%左右,训练集正确率接近100%:

在这里插入图片描述

学习曲线如下:

在这里插入图片描述

参考链接:

  • https://cloud.tencent.com/developer/article/1638597

  • https://blog.csdn.net/m0_50127633/article/details/117047057?spm=1001.2014.3001.5502

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

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

相关文章

01-SpringCloud微服务入门

1.认识微服务 随着互联网行业的发展,对服务的要求也越来越高,服务架构也从单体架构逐渐演变为现在流行的微服务架构。这些架构之间有怎样的差别呢? 1.1.单体架构 单体架构:将业务的所有功能集中在一个项目中开发,打…

如何利用Oracle官方网站不登录账号下载和安装非最新版本的JDK(版本自由选择)

一、JDK概述 JDK(Java Development Kit)是Java开发工具集,是针对Java编程语言的软件开发环境。它包含了Java编译器、JRE(Java运行时环境)以及其他一些用于开发、调试和测试Java应用程序的工具,是Java开发人员的必备工具。 二、JDK下载 进入Oracle官方网站,我们很容易发…

桌面天气预报软件 Weather Widget free mac特点介绍

Weather Widget free for Mac多种吸引人的小部件设计可供选择,可以随时了解天气!还可以在Dock和菜单栏中为您提供简短的天气预报或当前状况的概述。 Weather Widget free for Mac软件介绍 始终在桌面上使用时尚的天气小部件来随时了解天气!多…

逻辑回归(LR)----机器学习

基本原理 逻辑回归(Logistic Regression,LR)也称为"对数几率回归",又称为"逻辑斯谛"回归。 logistic回归又称logistic 回归分析 ,是一种广义的线性回归分析模型,常用于数据挖掘&#…

FA发放云桌面并与FC对接

(7)分配桌面(该组为刚刚创建的域名用户和组),确认无误,直接发放 (8)可在任务中心查看发放的进度 3、FA的登录流程 (1)登录WI:客户端访问VLB&…

springcloud alibaba整合sentinel并结合dashboard控制面板设置规则

目录 一、springcloud alibaba整合sentinel二、采用代码方式设置流控规则三、结合dashboard控制面板设置规则3.1、准备工作3.2、设置全局异常处理3.3、编写测试接口3.4、结合dashboard控制面板设置规则3.4.1、流控规则设置并测试——QPS3.4.2、流控规则设置并测试——线程数3.4…

【Unity嵌入Android原生工程】

Unity嵌入Android原生工程 本章学习,Unity模块嵌入Android## 标题Unity导出Android工程创建Android Studio工程Unity嵌入到Andorid StudioAndroid原生代码跳转到Unity场景工作需要嵌入原生工程,并实现热更,记录一下 工具,Unity2023.3.14,Android Studio 2022.3.1 patch3 Un…

《JVM由浅入深学习【四】 2023-12-24》JVM由简入深学习提升分享

JVM由简入深学习提升分享四 1.JVM中java堆的特点及作用2. JVM中对象如何在堆内存中分配3. JVM堆内存中的对象布局 1.JVM中java堆的特点及作用 是线程共享的一块区域虚拟机启动时就创建了是虚拟机中内存占用很大的一块存放所有的实例对象和数组GC主要的作用区域可分为新生代&am…

初学者快速入门学习日语,PDF文档音频教学资料合集

一、资料描述 本套学习资料是很全面的,共有734份文件,包括PDF,PPT,表格,图片,音频等多种格式,可以作为初级日语的学习教材,也是非常适合初学者入门的,可以帮助大家快速的…

Nginx(十三) 配置文件详解 - 反向代理(超详细)

本篇文章主要讲ngx_http_proxy_module和ngx_stream_proxy_module模块下各指令的使用方法。 1. 代理请求 proxy_pass 1.1 proxy_pass 代理请求 Syntax: proxy_pass URL; Default: — Context: location, if in location, limit_except 设置代理服务器的协议和地址以…

使用Redis进行搜索

文章目录 构建反向索引 构建反向索引 在Begin-End区域编写 tokenize(content) 函数,实现文本标记化的功能,具体参数与要求如下: 方法参数 content 为待标记化的文本; 文本标记的实现:使用正则表达式提取全小写化后的…

初识Java并发,一问读懂Java并发知识文集(1)

🏆作者简介,普修罗双战士,一直追求不断学习和成长,在技术的道路上持续探索和实践。 🏆多年互联网行业从业经验,历任核心研发工程师,项目技术负责人。 🎉欢迎 👍点赞✍评论…

数据库索引、三范式、事务

索引 索引(Index)是帮助 MySQL 高效获取数据的数据结构。常见的查询算法,顺序查找,二分查找,二叉排序树查找,哈希散列法,分块查找,平衡多路搜索树 B 树(B-tree)。 常见索引原则有 选择唯一性索引:唯一性索引的值是唯…

树与二叉树笔记整理

摘自小红书 ## 树与二叉树 ## 排序总结

eclipse中更改jdk版本

文章目录 步骤1:installed JREs步骤2:选择已安装的jdk步骤3:项目配置 步骤1:installed JREs 在eclipse上方工具栏找到Window -->Preferences,如下图所示: 选择Installed JREs 点击 Add 按钮, 选择Stand…

【字典树Trie】LeetCode-139. 单词拆分

139. 单词拆分。 给你一个字符串 s 和一个字符串列表 wordDict 作为字典。请你判断是否可以利用字典中出现的单词拼接出 s 。 注意:不要求字典中出现的单词全部都使用,并且字典中的单词可以重复使用。 示例 1: 输入: s "leetcode&q…

MySQL是如何保证数据一致性的?

文章目录 前言MySQL保证的一致性MySQL发生不一致环节并发冲突redolog不完整binlog&redolog不一致 MySQL解决不一致方案加锁解决并发冲突undolog解决redolog不完整XA两阶段提交解决binlog和redolog的不一致 总结 前言 通过上文《MySQL是如何保证数据不丢失的?》…

Ubuntu安装CUDA出在三个cuda相关文件夹?

按照网上的教程,在/usr/local中操作cuda文件夹,但是发现这里会出现不止一个cuda文件夹: 可以看大这里有cuda、cuda-11、cuda-11.8三个文件夹,实际上我安装的是11.8的cuda,那么第三个文件是好理解的,就是我…

Django Web框架

1、创建PyCharm项目 2、安装框架 pip install django4.2.0 3、查看安装的包列表 4、使用命令创建django项目 django-admin startproject web 5、目录结构 6、运行 cd web python manage.py runserver7、初始化后台登录的用户名密码 执行数据库迁移生成数据表 python man…

【Redis交响乐】Redis中的数据类型/内部编码/单线程模型

文章目录 一. Redis中的数据类型和内部编码二. Redis的单线程模型面试题: redis是单线程模型,为什么效率之高,速度之快呢? 在上一篇博客中我们讲述了Redis中的通用命令,本篇博客中我们将围绕每个数据结构来介绍相关命令. 一. Redis中的数据类型和内部编码 type命令实际返回的…