CNN——VGG

1.VGG简介

        论文下载地址:https://arxiv.org/pdf/1409.1556.pdf 

        VGGNet 是由牛津大学视觉几何小组(Visual Geometry Group, VGG)提出的一种深层卷积网络结构,他们以 7.32% 的错误率赢得了 2014 年 ILSVRC 分类任务的亚军(冠军由 GoogLeNet 以 6.65% 的错误率夺得)和 25.32% 的错误率夺得定位任务(Localization)的第一名(GoogLeNet 错误率为 26.44%)。 

        VGG通过vgg块的堆积,VGG19最高让网络达到了16 个卷积层和 3 个全连接层,共计 19 层网络(池化层不带参数,一般不算一层)。这也导致参数量非常的多,模型比较臃肿(第一个全连接层占了非常大一部分)

        包括VGG11,VGG13,VGG16和VGG19,性能依次提升,最常用的是VGG16。

核心点:

  1. 全部使用3×3步长为1的小卷积核。3×3卷积核是最小的能够表示上下左右中心的尺寸。

                        

         假设输入为5×5,使用2次3×3卷积后最终得到1×1的特征图,那么这个1×1的特征图的感受野为5×5。这和直接使用一个5×5卷积核得到1×1的特征图是一样的。也就是说2次3×3卷积可以代替一次5×5卷积同时,2次3×3卷积的参数更少(2×3×3=18<5×5=25)而且会经过两次激活函数进行非线性变换,学习能力会更好同样的3次3×3卷积可以替代一次7×7的卷积

        此外步长为1可以不会丢失信息

        2.相同的vgg块堆叠

        3.深度深,且成功证明深度增加可以提高网络性能

2.VGG网络结构详解

        这里以最常用的VGG16为例子。VGG11,VGG13,VGG19都是根据上面的表使用不同的卷积个数。

vgg_block包括若干个3×3卷积(padding=1,stride=1)+激活+2×2池化(padding=0,stride=2),第一个卷积将通道数翻倍

  • 3×3卷积,padding=1,stride=1,output=(input-3+2×1)/1+1=input,特征图尺寸不变
  • 2×2池化,padding=0,stride=2,output=(input-2)/2+1=1/2input,特征图尺寸减半

1.输入层。224×224×3,RGB图

2.vgg_block1

操作填充步长输入通道数输出通道数输出特征图尺寸
3×3卷积11364224×224
Relu激活//6464224×224
3×3卷积116464224×224
Relu激活//6464224×224
2×2最大池化026464112×112

2.vgg_block2

操作填充步长输入通道数输出通道数输出特征图尺寸
3×3卷积1164128112×112
Relu激活//128128112×112
3×3卷积11128128112×112
Relu激活//128128112×112
2×2最大池化0212812856×56

3.vgg_block3

        从这个块开始卷积变成3次

操作填充步长输入通道数输出通道数输出特征图尺寸
3×3卷积1112825656×56
Relu激活//25625656×56
3×3卷积1125625656×56
Relu激活//25625656×56
3×3卷积1125625656×56
Relu激活//25625656×56
2×2最大池化0225625628×28

4.vgg_block4

操作填充步长输入通道数输出通道数输出特征图尺寸
3×3卷积1125651228×28
Relu激活//51251228×28
3×3卷积1151251228×28
Relu激活//51251228×28
3×3卷积1151251228×28
Relu激活//51251228×28
2×2最大池化0251251214×14

 4.vgg_block5

操作填充步长输入通道数输出通道数输出特征图尺寸
3×3卷积1151251214×14
Relu激活//51251214×14
3×3卷积1151251214×14
Relu激活//51251214×14
3×3卷积1151251214×14
Relu激活//51251214×14
2×2最大池化025125127×7

6.向量化

        flatten,7×7×512(25,088) ->> 1×1×25,088

7.全连接FC1

        1×1×25,088 ->>1×1×4096

8.全连接FC2

        1×1×4096 ->> 1×1×4096

9.全连接FC3(Softmax)

        1×1×4096 ->> 1×1×1000

3.VGGPytorch实现

3.1 手动实现VGG

# 定义VGG块
def vgg_block(num_convs, in_channels, out_channels):layers = []  # 初始化一个空列表用于存放层(卷积层和ReLU激活函数)for _ in range(num_convs):  # 循环创建指定数量的卷积层和ReLU激活函数layers.append(nn.Conv2d(in_channels, out_channels, kernel_size=3, padding=1))  # 添加一个卷积层layers.append(nn.ReLU(inplace=True))  # 添加一个ReLU激活函数,并在原地执行节省内存in_channels = out_channels  # 更新输入通道数为输出通道数,以便下一层使用layers.append(nn.MaxPool2d(kernel_size=2, stride=2))  # 添加一个最大池化层return nn.Sequential(*layers)  # 返回一个由这些层组成的Sequential模型# 定义VGG网络
class VGG(nn.Module):def __init__(self, cfg, num_classes=1000):super(VGG, self).__init__()self.conv_layers = self._make_layers(cfg)  # 创建VGG的卷积层部分self.fc_layers = nn.Sequential(  # 创建VGG的全连接层部分nn.Linear(512 * 7 * 7, 4096),  # 全连接层1nn.ReLU(inplace=True),  # ReLU激活函数nn.Dropout(),  # Dropout层用于防止过拟合nn.Linear(4096, 4096),  # 全连接层2nn.ReLU(inplace=True),  # ReLU激活函数nn.Dropout(),  # Dropout层用于防止过拟合nn.Linear(4096, num_classes)  # 全连接层3,输出类别数)self.flatten = nn.Flatten()def forward(self, x):x = self.conv_layers(x)  # 卷积层部分x = self.flatten(x)  # 将特征张量展平以输入全连接层x = self.fc_layers(x)  # 全连接层部分return xdef _make_layers(self, cfg):layers = []  # 初始化一个空列表用于存放VGG的层in_channels = 3  # 输入通道数为RGB图像的3通道for num_convs, out_channels in cfg:layers.append(vgg_block(num_convs, in_channels, out_channels))  # 添加VGG块in_channels = out_channels  # 更新输入通道数为输出通道数,以便下一层使用return nn.Sequential(*layers)  # 返回一个由VGG块组成的Sequential模型# 不同版本的VGG配置
cfgs = {'VGG11': [(1, 64), (1, 128), (2, 256), (2, 512), (2, 512)],  # VGG11的卷积层配置'VGG13': [(2, 64), (2, 128), (2, 256), (2, 512), (2, 512)],  # VGG13的卷积层配置'VGG16': [(2, 64), (2, 128), (3, 256), (3, 512), (3, 512)],  # VGG16的卷积层配置'VGG19': [(2, 64), (2, 128), (4, 256), (4, 512), (4, 512)]   # VGG19的卷积层配置
}# 实例化不同版本的VGG
def get_vgg(model_name, num_classes=1000):cfg = cfgs[model_name]  # 获取指定版本的VGG配置model = VGG(cfg, num_classes)  # 根据配置创建相应版本的VGG模型return model  # 返回指定版本的VGG模型# 实例化不同版本的VGG示例
# vgg11 = get_vgg('VGG11')
# vgg13 = get_vgg('VGG13')
vgg16 = get_vgg('VGG16') # 修改分类数目,vgg16 = get_vgg('VGG16',num_classes=10)
# vgg19 = get_vgg('VGG19')summary(vgg16.to(device), (3, 224, 224))

 3.2 手动实现VGG16简易版

        还有一个简单易懂的实现方式,如VGG16实现如下。但这种方式如果网络比较深代码就比较冗长了,而且一次只能实现一种模型

# 定义VGG16模型结构
class VGG16(nn.Module):def __init__(self, num_classes=1000):super(VGG16, self).__init__()# 特征层self.features = nn.Sequential(nn.Conv2d(3, 64, kernel_size=3, padding=1),nn.ReLU(inplace=True),nn.Conv2d(64, 64, kernel_size=3, padding=1),nn.ReLU(inplace=True),nn.MaxPool2d(kernel_size=2, stride=2),nn.Conv2d(64, 128, kernel_size=3, padding=1),nn.ReLU(inplace=True),nn.Conv2d(128, 128, kernel_size=3, padding=1),nn.ReLU(inplace=True),nn.MaxPool2d(kernel_size=2, stride=2),nn.Conv2d(128, 256, kernel_size=3, padding=1),nn.ReLU(inplace=True),nn.Conv2d(256, 256, kernel_size=3, padding=1),nn.ReLU(inplace=True),nn.Conv2d(256, 256, kernel_size=3, padding=1),nn.ReLU(inplace=True),nn.MaxPool2d(kernel_size=2, stride=2),nn.Conv2d(256, 512, kernel_size=3, padding=1),nn.ReLU(inplace=True),nn.Conv2d(512, 512, kernel_size=3, padding=1),nn.ReLU(inplace=True),nn.Conv2d(512, 512, kernel_size=3, padding=1),nn.ReLU(inplace=True),nn.MaxPool2d(kernel_size=2, stride=2),nn.Conv2d(512, 512, kernel_size=3, padding=1),nn.ReLU(inplace=True),nn.Conv2d(512, 512, kernel_size=3, padding=1),nn.ReLU(inplace=True),nn.Conv2d(512, 512, kernel_size=3, padding=1),nn.ReLU(inplace=True),nn.MaxPool2d(kernel_size=2, stride=2),)self.flatten = nn.Flatten()# 分类层 self.classifier = nn.Sequential(nn.Linear(512 * 7 * 7, 4096),nn.ReLU(inplace=True),nn.Dropout(),nn.Linear(4096, 4096),nn.ReLU(inplace=True),nn.Dropout(),nn.Linear(4096, num_classes),)def forward(self, x):x = self.features(x)x = self.flatten(x)x = self.classifier(x)return x

  3.2 使用Pytorch自带的VGG

        官方文档:VGG — Torchvision 0.16 documentation (pytorch.org)。Pytorch官方实现了VGG,并且还附带有在ImageNet上预训练权重

        可以看到还有bn版本可选,即在卷积后增加使用了batch-normalization批量归一化。下面是使用的示例:

# 初始化预训练的vgg16模型
modelPre = models.vgg16(weights='DEFAULT')
summary(modelPre.to(device), (3, 224, 224))

        weights='DEFAULT'会默认使用最新最好的权重,或者直接指明weights='IMAGENET1K_V1',什么模型有什么权重可以直接去官方文档中查看就好。

        除了加入了全局平均池化层之外,其他和我们自己实现的是一样的。全局平均池化层可以支持任意输入尺寸,无论31输出什么尺寸,全部变成7×7

4.VGG在CIFAR-10简单实践 

        所需库

import torch
import torch.nn as nn
from torchsummary import summary
import torch.optim as optim
import torchvision.datasets as datasets
import torchvision.transforms as transforms
from torch.utils.data import DataLoader
from tqdm import tqdm
from torchvision import models
import matplotlib.pyplot as plt

1.修改网络结构

        CIFAR-10输入尺寸为32×32,为了适应该数据集,需要简单修改一下第一层全连接层的输入参数。根据网络结构,尺寸会减半5次,对于224×224来说会降到7,则会降低到1。

        将nn.Linear(512 * 7 * 7, 4096)修改为nn.Linear(512 * 1 * 1, 4096)

# 定义VGG16模型结构
class VGG16(nn.Module):def __init__(self, num_classes=1000):super(VGG16, self).__init__()# 特征层self.features = nn.Sequential(nn.Conv2d(3, 64, kernel_size=3, padding=1),nn.ReLU(inplace=True),nn.Conv2d(64, 64, kernel_size=3, padding=1),nn.ReLU(inplace=True),nn.MaxPool2d(kernel_size=2, stride=2),nn.Conv2d(64, 128, kernel_size=3, padding=1),nn.ReLU(inplace=True),nn.Conv2d(128, 128, kernel_size=3, padding=1),nn.ReLU(inplace=True),nn.MaxPool2d(kernel_size=2, stride=2),nn.Conv2d(128, 256, kernel_size=3, padding=1),nn.ReLU(inplace=True),nn.Conv2d(256, 256, kernel_size=3, padding=1),nn.ReLU(inplace=True),nn.Conv2d(256, 256, kernel_size=3, padding=1),nn.ReLU(inplace=True),nn.MaxPool2d(kernel_size=2, stride=2),nn.Conv2d(256, 512, kernel_size=3, padding=1),nn.ReLU(inplace=True),nn.Conv2d(512, 512, kernel_size=3, padding=1),nn.ReLU(inplace=True),nn.Conv2d(512, 512, kernel_size=3, padding=1),nn.ReLU(inplace=True),nn.MaxPool2d(kernel_size=2, stride=2),nn.Conv2d(512, 512, kernel_size=3, padding=1),nn.ReLU(inplace=True),nn.Conv2d(512, 512, kernel_size=3, padding=1),nn.ReLU(inplace=True),nn.Conv2d(512, 512, kernel_size=3, padding=1),nn.ReLU(inplace=True),nn.MaxPool2d(kernel_size=2, stride=2),)self.flatten = nn.Flatten()# 分类层 self.classifier = nn.Sequential(#nn.Linear(512 * 7 * 7, 4096),nn.Linear(512 * 1 * 1, 4096),nn.ReLU(inplace=True),nn.Dropout(),nn.Linear(4096, 4096),nn.ReLU(inplace=True),nn.Dropout(),nn.Linear(4096, num_classes),)def forward(self, x):x = self.features(x)x = self.flatten(x)x = self.classifier(x)return x# 打印模型结构
model = VGG16(num_classes=10).to(device)
summary(model, (3, 32, 32))

2.读取数据集

# 数据预处理
transform = transforms.Compose([transforms.ToTensor(),transforms.Normalize((0.485, 0.456, 0.406), (0.229, 0.224, 0.225)),
])# 加载CIFAR-10数据集
train_dataset = datasets.CIFAR10(root='./dataset', train=True, download=True, transform=transform)
test_dataset = datasets.CIFAR10(root='./dataset', train=False, download=True, transform=transform)# 数据加载器
train_dataloader = DataLoader(train_dataset, batch_size=128, shuffle=True)
test_dataloader = DataLoader(test_dataset, batch_size=128, shuffle=False)

3.使用GPU

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

4.模型训练

def train(model, lr, epochs, train_dataloader, device, save_path):# 将模型放入GPUmodel = model.to(device)# 使用交叉熵损失函数loss_fn = nn.CrossEntropyLoss().to(device)# SGDoptimizer = torch.optim.SGD(model.parameters(), lr=lr, weight_decay=5e-4, momentum=0.9)# 记录训练与验证数据train_losses = []train_accuracies = []# 开始迭代   for epoch in range(epochs):   # 切换训练模式model.train()  # 记录变量train_loss = 0.0correct_train = 0total_train = 0# 读取训练数据并使用 tqdm 显示进度条for i, (inputs, targets) in tqdm(enumerate(train_dataloader), total=len(train_dataloader), desc=f"Epoch {epoch+1}/{epochs}", unit='batch'):# 训练数据移入GPUinputs = inputs.to(device)targets = targets.to(device)# 模型预测outputs = model(inputs)# 计算损失loss = loss_fn(outputs, targets)# 梯度清零optimizer.zero_grad()# 反向传播loss.backward()# 使用优化器优化参数optimizer.step()# 记录损失train_loss += loss.item()# 计算训练正确个数_, predicted = torch.max(outputs, 1)total_train += targets.size(0)correct_train += (predicted == targets).sum().item()# 计算训练正确率并记录train_loss /= len(train_dataloader)train_accuracy = correct_train / total_traintrain_losses.append(train_loss)train_accuracies.append(train_accuracy)# 输出训练信息print(f"Epoch [{epoch + 1}/{epochs}] - Train Loss: {train_loss:.4f}, Train Acc: {train_accuracy:.4f}")# 绘制损失和正确率曲线plt.figure(figsize=(10, 5))plt.subplot(1, 2, 1)plt.plot(range(epochs), train_losses, label='Training Loss')plt.xlabel('Epoch')plt.ylabel('Loss')plt.legend()plt.subplot(1, 2, 2)plt.plot(range(epochs), train_accuracies, label='Accuracy')plt.xlabel('Epoch')plt.ylabel('Accuracy')plt.legend()plt.tight_layout()plt.show()torch.save(model.state_dict(), save_path)
model = VGG16(num_classes=10)
lr = 0.01 
epochs = 10
save_path = './modelWeight/VGG16_CIFAR10'
train(model,lr,epochs,train_dataloader,device,save_path)

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

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

相关文章

k8s yaml文件pod的生命周期

Pod是k8s中最小限额资源管理组件&#xff0c;也是最小化运行容器化的应用的资源管理对象。 Pod是一个抽象的概念&#xff0c;可以理解为一个或者多个容器化应用的集合。 在一个pod当中运行一个容器是最常用的方式。 在一个pod当中同时运行多个容器&#xff0c;在一个pod当中…

jupyter更改默认路径到其它的目录或者到其它的盘 比如D盘

1.打开终端 输入jupyter notebook --generate-config 如下 2.在C:\Users\mb5958\.jupyter路径下 3.用记事本打开它&#xff0c;搜索directory 4.在你想要的路径下新建一个文件夹&#xff0c;如‘D:\jupyterFile’&#xff0c;然后将路径名放在c.NotebookApp.notebook_dir"…

Spring见解 1.2

2.3.Spring的IOC解决程序耦合 2.3.1.创建工程 2.3.1.1.pom.xml <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:sc…

Python 快速合并PDF表格转换输出CSV文件

单位的刷脸考勤机后台系统做得比较差&#xff0c;只能导出每个部门的出勤统计表pdf&#xff0c;格式如下&#xff1a; 近期领导要看所有部门的考勤数据&#xff0c;于是动手快速写了个合并pdf并输出csv文件的脚本。 安装模块 pypdf2&#xff0c;pdfplumber&#xff0c;前者用…

华清远见作业第二十一天——IO(第四天)

思维导图&#xff1a; 创建出三个进程完成两个文件之间拷贝工作&#xff0c;子进程1拷贝前一半内容&#xff0c;子进程2拷贝后一半内容&#xff0c;父进程回收子进程的资源。 代码&#xff1a; #include<myhead.h> int main(int argc, const char *argv[]) {if(argc!3)…

使用results.csv文件数据绘制mAP对比图

yolov5每次train完成&#xff08;如果没有中途退出&#xff09;都会在run目录下生成expX目录&#xff08;X代表生成结果次数 第一次训练完成生成exp0 第二次生成exp1…以此类推&#xff09;。expX目录下会保存训练生成的weights以及result.txt文件&#xff0c;其中weights是训练…

freeRTOS——队列集知识总结及实战

1队列集概念 可以在任务间传递不同数据类型的消息 作用&#xff1a;用于对多个队列或信号量进行“监听”&#xff0c;其中不管哪一个消息到来&#xff0c;都可让任务退出阻塞状态 2队列集API函数 1&#xff09;创建队列集 QueueSetHandle_t xQueueCreateSet( const UBaseType…

调用百度地图 API 的步骤详解

百度地图 Web 服务 API 为开发者提供 http/https 接口&#xff0c;即开发者通过 http/https 形式发起检索请求&#xff0c;获取返回 json 或 xml 格式的检索数据。用户可以基于此开发 JavaScript、C#、C、Java 等语言的地图应用。百度地图 API 在线地址为&#xff1a;baidumap.…

C#使用switch多路选择语句判断何为季节

目录 一、 switch语句 二、示例 三、生成 一、 switch语句 switch语句是多路选择语句&#xff0c;它通过一个表达式的值来使程序从多个分支中选取一个用于执行的分支。 switch表达式的值只可以是整型、字符串、枚举和布尔类型。 switch语句中多个case可以使用一个break。 在…

氢燃料电池——产品标准规范汇总和梳理

文章目录 氢燃料电池模块 氢燃料电池发动机 氢燃料电池汽车 加氢系统 总结 氢燃料电池模块 GB/T 33978-2017 道路车辆用质子交换膜燃料电池模块 GB/T 43361-2023 气体分析 道路车辆用质子交换膜燃料电池氢燃料分析方法的确认 GB/T 29729-2022 氢系统安全的基本要求 GB/T 4…

python报错:TypeError: Descriptors cannot be created directly.

问题 报错提示&#xff1a; TypeError&#xff1a;不能直接创建描述符。 如果此调用来自 _pb2.py 文件&#xff0c;则您生成的代码已过期&#xff0c;必须使用 protoc > 3.19.0 重新生成。 如果您不能立即重新生成原型&#xff0c;其他一些可能的解决方法是&#xff1a; 1.…

(JAVA)-(网络编程)-初始网络编程

网络编程就是在通信协议下&#xff0c;不同的计算机上运行的程序&#xff0c;进行的数据传输。 讲的通俗一点&#xff0c;就是以前我们写的代码是单机版的&#xff0c;网络编程就是联机版的。 应用场景&#xff1a;即时通信&#xff0c;网游对战&#xff0c;金融证券&#xf…