一、前情说明:

写在前面的话
本系列博客在于汇总CSDN的精华帖,类似自用笔记,方便以后的复习回顾,博文中的引用都注明出处,并点赞收藏原博主。
博客大致分为两部分,第一部是转载于《雪饼》大侠的博客https://blog.csdn.net/rogerchen1983/article/details/79549085(值得推荐,深入浅出,但少部分涉及较深或者错误的没贴出),第二部分是对网络的复现,代码主要参考bilibili大神《霹雳吧啦Wz》https://github.com/WZMIAOMIAO/deep-learning-for-image-processing(代码做了部分改动,不完全一样,也十分推荐他的视频)

二、论文解读:

2014年,牛津大学计算机视觉组(Visual Geometry Group)和Google DeepMind公司的研究员一起研发出了新的深度卷积神经网络:VGGNet,并取得了ILSVRC2014比赛分类项目的第二名(第一名是GoogLeNet,也是同年提出的)和定位项目的第一名。
VGGNet探索了卷积神经网络的深度与其性能之间的关系,成功地构筑了16~19层深的卷积神经网络,证明了增加网络的深度能够在一定程度上影响网络最终的性能,使错误率大幅下降,同时拓展性又很强,迁移到其它图片数据上的泛化性也非常好。到目前为止,VGG仍然被用来提取图像特征。

一、VGG的特点
先看一下VGG的结构图

VGG由5层卷积层、3层全连接层、softmax输出层构成,层与层之间使用max-pooling(最大化池)分开,所有隐层的激活单元都采用ReLU函数。
2、小卷积核和多卷积子层
VGG使用多个较小卷积核(3x3)的卷积层代替一个卷积核较大的卷积层,一方面可以减少参数,另一方面相当于进行了更多的非线性映射,可以增加网络的拟合/表达能力。
小卷积核是VGG的一个重要特点,虽然VGG是在模仿AlexNet的网络结构,但没有采用AlexNet中比较大的卷积核尺寸(如7x7),而是通过降低卷积核的大小(3x3),增加卷积子层数来达到同样的性能(VGG:从1到4卷积子层,AlexNet:1子层)。
VGG的作者认为两个3x3的卷积堆叠获得的感受野大小,相当一个5x5的卷积;而3个3x3卷积的堆叠(卷积核级联)获取到的感受野相当于一个7x7的卷积。这样可以增加非线性映射,也能很好地减少参数(例如7x7的参数为49个,而3个3x3的参数为27),如下图所示:


3、小池化核
相比AlexNet的3x3的池化核,VGG全部采用2x2的池化核。
4、通道数多
VGG网络第一层的通道数为64,后面每层都进行了翻倍,最多到512个通道,通道数的增加,使得更多的信息可以被提取出来。
5、层数更深、特征图更宽
由于卷积核专注于扩大通道数、池化专注于缩小宽和高,使得模型架构上更深更宽的同时,控制了计算量的增加规模。

二、VGG的网络结构
下图是来自论文《Very Deep Convolutional Networks for Large-Scale Image Recognition》(基于甚深层卷积网络的大规模图像识别)的VGG网络结构,正是在这篇论文中提出了VGG,如下图:

上图中,池化层都不算入层数中,例vgg-16为13个conv+3个FC组成。
在这篇论文中分别使用了A、A-LRN、B、C、D、E这6种网络结构进行测试,这6种网络结构相似,都是由5层卷积层、3层全连接层组成,其中区别在于每个卷积层的子层数量不同,从A至E依次增加(子层数量从1到4),总的网络深度从11层到19层(添加的层以粗体显示),表格中的卷积层参数表示为“conv⟨感受野大小⟩-通道数⟩”,例如con3-128,表示使用3x3的卷积核,通道数为128。为了简洁起见,在表格中不显示ReLU激活功能。
其中,网络结构D就是著名的VGG16,网络结构E就是著名的VGG19。

以网络结构D(VGG16)为例,介绍其处理过程如下,请对比上面的表格和下方这张图,留意图中的数字变化,有助于理解VGG16的处理过程:

整个input→output过程如下:
1、输入224x224x3的图片,经2个3x3的卷积核(out_channel=64)作两次卷积+ReLU,卷积后的尺寸变为224x224x64
2、作max pooling(最大化池化),池化单元尺寸为2x2(stride=2,根据特征图大小计算公式进行计算:https://blog.csdn.net/weixin_45829462/article/details/106581152(如下图),效果为图像尺寸减半),池化后的尺寸变为112x112x64

3、经2个3x3的卷积核(out_channel=128)作两次卷积+ReLU,尺寸变为112x112x128
4、作2x2的max pooling池化,尺寸变为56x56x128
5、经3个3x3的卷积核(out_channel=256)作三次卷积+ReLU,尺寸变为56x56x256
6、作2x2的max pooling池化,尺寸变为28x28x256
7、经3个3x3的卷积核(out_channel=512)作三次卷积+ReLU,尺寸变为28x28x512
8、作2x2的max pooling池化,尺寸变为14x14x512
9、经3个3x3的卷积核作三次卷积+ReLU,尺寸变为14x14x512
10、作2x2的max pooling池化,尺寸变为7x7x512
11、与两层1x1x4096,一层1x1x1000进行全连接+ReLU(共三层)
12、通过softmax输出1000个预测结果(softmax函数的作用是输出概率最大,也就是最有可能的那个类)

从上面的过程可以看出VGG网络结构还是挺简洁的,都是由小卷积核、小池化核、ReLU组合而成。其简化图如下(以VGG16为例):

但是,唯一不能忍受的是vgg产生的网络参数实在是太多了,且大部分都在全连接层,特别是第一个全连接层(标红部分),你难以能想象一个16层的模型产生的预训练权重就有500m之大(darknet53的53层神经也才200m左右)。

A、A-LRN、B、C、D、E这6种网络结构的深度虽然从11层增加至19层,但参数量变化不大,这是由于基本上都是采用了小卷积核(3x3,只有9个参数),这6种结构的参数数量(百万级)并未发生太大变化,这是因为在网络中,参数主要集中在全连接层。

经作者对A、A-LRN、B、C、D、E这6种网络结构进行单尺度的评估,错误率结果如下:

从上表可以看出:
1、LRN层无性能增益(A-LRN)
VGG作者通过网络A-LRN发现,AlexNet曾经用到的LRN层(local response normalization,局部响应归一化)并没有带来性能的提升,因此在其它组的网络中均没再出现LRN层。
2、随着深度增加,分类性能逐渐提高(A、B、C、D、E)
从11层的A到19层的E,网络深度增加对top1和top5的错误率下降很明显。
3、多个小卷积核比单个大卷积核性能好(B)
VGG作者做了实验用B和自己一个不在实验组里的较浅网络比较,较浅网络用conv5x5来代替B的两个conv3x3,结果显示多个小卷积核比单个大卷积核效果要好。

最后进行个小结:
1、通过增加深度能有效地提升性能;
2、最佳模型:VGG16,从头到尾只有3x3卷积与2x2池化,简洁优美;
3、卷积可代替全连接,可适应各种尺寸的图片

三、模型复现

3.1 下载数据集集:’http://download.tensorflow.org/example_images/flower_photos.tgz

3.2 数据集处理:
数据集下载后,解压到data_set文件夹,另外,新建sample,output,flower_data文件加夹,使用split_data将数据集分成训练集和验证集,比例在9:1之间,分好后的样本自动存入flower_data文件夹中,总的文件如下:

3.3模型构建
建立model.py,构建神经网络:

import torch
import torch.nn as nnclass VGG(nn.Module):def __init__(self, features, num_classes=1000, init_weights=True):super(VGG, self).__init__()self.features = featuresself.avgpool = nn.AdaptiveAvgPool2d((7, 7))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 = self.avgpool(x)x = torch.flatten(x, 1)x = self.classifier(x)return xdef _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.BatchNorm2d):nn.init.constant_(m.weight, 1)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)def make_layers(cfg, batch_norm=False):layers = []in_channels = 3for v in cfg:if v == 'M':layers += [nn.MaxPool2d(kernel_size=2, stride=2)]else:conv2d = nn.Conv2d(in_channels, v, kernel_size=3, padding=1)if batch_norm:layers += [conv2d, nn.BatchNorm2d(v), nn.ReLU(inplace=True)]else:layers += [conv2d, nn.ReLU(inplace=True)]in_channels = vreturn nn.Sequential(*layers)cfgs = {'vgg11': [64, 'M', 128, 'M', 256, 256, 'M', 512, 512, 'M', 512, 512, 'M'],'vgg13': [64, 64, 'M', 128, 128, 'M', 256, 256, 'M', 512, 512, 'M', 512, 512, 'M'],'vgg16': [64, 64, 'M', 128, 128, 'M', 256, 256, 256, 'M', 512, 512, 512, 'M', 512, 512, 512, 'M'],'vgg19': [64, 64, 'M', 128, 128, 'M', 256, 256, 256, 256, 'M', 512, 512, 512, 512, 'M', 512, 512, 512, 512, 'M'],
}def vgg(model_name="vgg16", **kwargs):try:cfg = cfgs[model_name]except:print("Warning: model number {} not in cfgs dict!".format(model_name))exit(-1)model = VGG(make_layers(cfg), **kwargs)return model

建立train.py,用来训练模型,记住,由于博主电脑性能较老旧,所以博主采用的是迁移学习的训练方式,硬件设施允许的情况下,还是可以试试从零开始训练的:

import torch
import torch.nn as nn
from torchvision import transforms, datasets
import torch.optim as optim
from model import vgg
import os
import json
import time
import torchvision.models.vggdevice = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
print(device)data_transform = {"train": transforms.Compose([transforms.RandomResizedCrop(224),transforms.RandomHorizontalFlip(),transforms.ToTensor(),transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))]),"val": transforms.Compose([transforms.Resize((224, 224)),  # cannot 224, must (224, 224)transforms.ToTensor(),transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])}data_root = os.path.abspath(os.path.join(os.getcwd(), "/content/gdrive/My Drive"))  # get data root path
image_path = data_root + "/flower_data/"  # flower data set path
train_dataset = datasets.ImageFolder(root=image_path + "/train",transform=data_transform["train"])
train_num = len(train_dataset)# {'daisy':0, 'dandelion':1, 'roses':2, 'sunflower':3, 'tulips':4}
flower_list = train_dataset.class_to_idx
cla_dict = dict((val, key) for key, val in flower_list.items())
# write dict into json file
json_str = json.dumps(cla_dict, indent=4)
with open('class_indices.json', 'w') as json_file:json_file.write(json_str)batch_size = 32
train_loader = torch.utils.data.DataLoader(train_dataset,batch_size=batch_size, shuffle=True,num_workers=0)validate_dataset = datasets.ImageFolder(root=image_path + "/val",transform=data_transform["val"])
val_num = len(validate_dataset)
validate_loader = torch.utils.data.DataLoader(validate_dataset,batch_size=32, shuffle=True,num_workers=0)
model_name = "vgg16"
net = vgg(model_name=model_name, init_weights=True)# 加载预训练模型
model_weights_path = './vgg16.pth'
missing_keys, unexpected_keys = net.load_state_dict(torch.load(model_weights_path), strict=False)net.to(device)loss_function = nn.CrossEntropyLoss()
# pata = list(net.parameters())
optimizer = optim.Adam(net.parameters(), lr=0.0002)if __name__ == '__main__':save_path = './vgg.pth'best_acc = 0.0for epoch in range(20):# trainnet.train()running_loss = 0.0t1 = time.perf_counter()for step, data in enumerate(train_loader, start=0):images, labels = dataimages = images.to(device)labels = labels.to(device)optimizer.zero_grad()outputs = net(images)loss = loss_function(outputs, labels)loss.backward()optimizer.step()# print statisticsrunning_loss += loss.item()# print train processrate = (step + 1) / len(train_loader)a = "*" * int(rate * 50)b = "." * int((1 - rate) * 50)print("\rtrain loss: {:^3.0f}%[{}->{}]{:.3f}".format(int(rate * 100), a, b, loss), end="")print()print(time.perf_counter() - t1)# validatenet.eval()acc = 0.0  # accumulate accurate number / epochwith torch.no_grad():for val_data in validate_loader:val_images, val_labels = val_dataoutputs = net(val_images.to(device))predict_y = torch.max(outputs, dim=1)[1]acc += (predict_y == val_labels.to(device)).sum().item()val_accurate = acc / val_numif val_accurate > best_acc:best_acc = val_accuratetorch.save(net.state_dict(), save_path)print('[epoch %d] train_loss: %.3f  test_accuracy: %.3f' %(epoch + 1, running_loss / step, val_accurate))print('Finished Training')

加载预训练模型关键代码:

model_weights_path = ‘./vgg16.pth’ missing_keys, unexpected_keys = net.load_state_dict(torch.load(model_weights_path),

3.4 结果如下:

可以看到,使用了预训练模型,网络收敛非常的快,,随着迭代次数的增加,精度也在不断的上升,检测后的结果保存在了output文件夹,检测效果如下:

CNN经典之VGG网络+PyTorch复现相关推荐

  1. 【医学图像分割网络】之Res U-Net网络PyTorch复现

    [医学图像分割网络]之Res U-Net网络PyTorch复现 1.内容 U-Net网络算是医学图像分割领域的开山之作,我接触深度学习到现在大概将近大半年时间,看到了很多基于U-Net网络的变体,后续 ...

  2. DenseNet——CNN经典网络模型详解(pytorch实现)

    一.概述 论文:Densely Connected Convolutional Networks 论文链接:https://arxiv.org/pdf/1608.06993.pdf 代码的github ...

  3. 【深度学习】ResNet——CNN经典网络模型详解(pytorch实现)

    建议大家可以实践下,代码都很详细,有不清楚的地方评论区见~ 1.前言 ResNet(Residual Neural Network)由微软研究院的Kaiming He等四名华人提出,通过使用ResNe ...

  4. 进阶必备:CNN经典论文代码复现 | 附下载链接

    经常会看到类似的广告<面试算法岗,你被要求复现论文了吗?>不好意思,我真的被问过这个问题.当然也不是所有面试官都会问,究其原因,其实也很好理解.企业肯定是希望自己的产品是有竞争力,有卖点的 ...

  5. 手撕 CNN 经典网络之 VGGNet(PyTorch实战篇)

    大家好,我是红色石头! 在上一篇文章: 手撕 CNN 经典网络之 VGGNet(理论篇) 详细介绍了 VGGNet 的网络结构,今天我们将使用 PyTorch 来复现VGGNet网络,并用VGGNet ...

  6. Pytorch之CNN:基于Pytorch框架实现经典卷积神经网络的算法(LeNet、AlexNet、VGG、NIN、GoogleNet、ResNet)——从代码认知CNN经典架构

    Pytorch之CNN:基于Pytorch框架实现经典卷积神经网络的算法(LeNet.AlexNet.VGG.NIN.GoogleNet.ResNet)--从代码认知CNN经典架构 目录 CNN经典算 ...

  7. 图像分类CNN经典网络(一)AlexNet与VGG

    本篇文章总结了CNN领域用于图像分类的两个网络AlexNet与VGGNet. 在图像分类领域,CNN有了很好的应用,下图展示了2012年之后的一些经典的网络架构. AlexNet AlexNet是打开 ...

  8. VGG——CNN经典网络模型(pytorch实现)

    三.VGG 论文下载地址 VGG 是Oxford的Visual Geometry Group的组提出的(大家应该能看出VGG名字的由来了).该网络是在ILSVRC 2014上的相关工作,主要工作是证明 ...

  9. 手撕 CNN 经典网络之 VGGNet(理论篇)

    2014年,牛津大学计算机视觉组(Visual Geometry Group)和Google DeepMind公司一起研发了新的卷积神经网络,并命名为VGGNet.VGGNet是比AlexNet更深的 ...

最新文章

  1. 继承QTreeWidgetItem发生error: 'staticMetaObject' is not a member of 'QTreeWidgetItem' 错误
  2. 编程入门python语言是多大孩子学的-包头孩子学编程python好吗
  3. Apache Hadoop版本详解
  4. Docker新手入门:基本用法
  5. 你可能对电灯泡一无所知
  6. POJ NOI MATH-7829 神奇序列求和
  7. UE4官方文档学习笔记材质篇——分层材质
  8. Hprose轻松实现远程过程调用(RPC)
  9. 2022-2028年中国工业级无人机行业市场全景调查及投资潜力研究报告
  10. 新sniffer pro 4.75 sp5下载
  11. 16种常用的数据分析方法-相关分析
  12. Arduino IDE for ESP8266 项目(1) 点亮灯+按键LED+pwm
  13. 优秀开源项目之二:流媒体直播系统Open Broadcaster Software
  14. 关于Windows系统脚本(jscript)
  15. easyexcel复杂模板导出(合并行列,列统计汇总)
  16. ​​​LabVIEW视觉软件使用什么算法
  17. 例题 8-4 传送中的车(Fabled Rook, UVa 11134)
  18. 将png图片转化为icon
  19. rabbitMQ实例(java)
  20. MYSQL 如何得到两个日期间的工作日(不含周六日,节假日)

热门文章

  1. MySQL高级篇——索引简介
  2. 【arxiv】网站打不开
  3. java图片框_java实现图片写入高清字体及带边框的方法
  4. kali linux中文乱码解决,解决Kali2020.1中文乱码的问题
  5. 世界是你们的,也是我们的,但终究是程序员的
  6. Blockchain Assisted Decentralized Federated Learning 阅读笔记 TPDS’2022
  7. 机器学习-37-GAN-04-Unsupervised Condition GAN(无监督有条件GAN:Direct Transformation,Projection to Comon Space)
  8. iOS 12-12.1.2 完整越狱教程
  9. .NET 6 之 ABP vNext 初体验
  10. (1)RIL简析(高通)——RIL如何启动及初始化