VGG

AlexNet在Lenet的基础上增加了几个卷积层,改变了卷积核大小,每一层输出通道数目等,并且取得了很好的效果.但是并没有提出一个简单有效的思路.

VGG做到了这一点,提出了可以通过重复使⽤简单的基础块来构建深度学习模型的思路.

论文地址:https://arxiv.org/abs/1409.1556

vgg的结构如下所示:



上图给出了不同层数的vgg的结构.也就是常说的vgg16,vgg19等等.

VGG BLOCK

vgg的设计思路是,通过不断堆叠3x3的卷积核,不断加深模型深度.vgg net证明了加深模型深度对提高模型的学习能力是一个很有效的手段.



看上图就能发现,连续的2个3x3卷积,感受野和一个5x5卷积是一样的,但是前者有两次非线性变换,后者只有一次!,这就是连续堆叠小卷积核能提高

模型特征学习的关键.此外,2个3x3的参数数量也比一个5x5少.(2x3x3 < 5x5)

vgg的基础组成模块,每一个卷积层都由n个3x3卷积后面接2x2的最大池化.池化层的步幅为2.从而卷积层卷积后,宽高不变,池化后,宽高减半.

我们可以有以下代码:

  1. def make_layers(in_channels,cfg):
  2. layers = []
  3. previous_channel = in_channels #上一层的输出的channel数量
  4. for v in cfg:
  5. if v == 'M':
  6. layers.append(nn.MaxPool2d(kernel_size=2,stride=2))
  7. else:
  8. layers.append(nn.Conv2d(previous_channel,v,kernel_size=3,padding=1))
  9. layers.append(nn.ReLU())
  10. previous_channel = v
  11. conv = nn.Sequential(*layers)
  12. return conv
  13. cfgs = {
  14. 'A': [64, 'M', 128, 'M', 256, 256, 'M', 512, 512, 'M', 512, 512, 'M'],
  15. 'B': [64, 64, 'M', 128, 128, 'M', 256, 256, 'M', 512, 512, 'M', 512, 512, 'M'],
  16. 'D': [64, 64, 'M', 128, 128, 'M', 256, 256, 256, 'M', 512, 512, 512, 'M', 512, 512, 512, 'M'],
  17. 'E': [64, 64, 'M', 128, 128, 'M', 256, 256, 256, 256, 'M', 512, 512, 512, 512, 'M', 512, 512, 512, 512, 'M'],
  18. }

cfgs定义了不同的vgg模型的结构,比如'A'代表vgg11. 数字代表卷积后的channel数. 'M'代表Maxpool

我们可以给出模型定义

  1. class VGG(nn.Module):
  2. def __init__(self,input_channels,cfg,num_classes=10, init_weights=True):
  3. super(VGG, self).__init__()
  4. self.conv = make_layers(input_channels,cfg) # torch.Size([1, 512, 7, 7])
  5. self.fc = nn.Sequential(
  6. nn.Linear(512*7*7,4096),
  7. nn.ReLU(),
  8. nn.Linear(4096,4096),
  9. nn.ReLU(),
  10. nn.Linear(4096,num_classes)
  11. )
  12. def forward(self, img):
  13. feature = self.conv(img)
  14. output = self.fc(feature.view(img.shape[0], -1))
  15. return output

卷积层的输出可由以下测试代码得出

  1. # conv = make_layers(1,cfgs['A'])
  2. # X = torch.randn((1,1,224,224))
  3. # out = conv(X)
  4. # #print(out.shape)

加载数据

  1. batch_size,num_workers=4,4
  2. train_iter,test_iter = learntorch_utils.load_data(batch_size,num_workers,resize=224)

这里batch_size调到8我的显存就不够了...

定义模型

  1. net = VGG(1,cfgs['A']).cuda()

定义损失函数

  1. loss = nn.CrossEntropyLoss()

定义优化器

  1. opt = torch.optim.Adam(net.parameters(),lr=0.001)

定义评估函数

  1. def test():
  2. acc_sum = 0
  3. batch = 0
  4. for X,y in test_iter:
  5. X,y = X.cuda(),y.cuda()
  6. y_hat = net(X)
  7. acc_sum += (y_hat.argmax(dim=1) == y).float().sum().item()
  8. batch += 1
  9. #print('acc_sum %d,batch %d' % (acc_sum,batch))
  10. return 1.0*acc_sum/(batch*batch_size)

训练

  1. num_epochs = 3
  2. def train():
  3. for epoch in range(num_epochs):
  4. train_l_sum,batch,acc_sum = 0,0,0
  5. start = time.time()
  6. for X,y in train_iter:
  7. # start_batch_begin = time.time()
  8. X,y = X.cuda(),y.cuda()
  9. y_hat = net(X)
  10. acc_sum += (y_hat.argmax(dim=1) == y).float().sum().item()
  11. l = loss(y_hat,y)
  12. opt.zero_grad()
  13. l.backward()
  14. opt.step()
  15. train_l_sum += l.item()
  16. batch += 1
  17. mean_loss = train_l_sum/(batch*batch_size) #计算平均到每张图片的loss
  18. start_batch_end = time.time()
  19. time_batch = start_batch_end - start
  20. print('epoch %d,batch %d,train_loss %.3f,time %.3f' %
  21. (epoch,batch,mean_loss,time_batch))
  22. print('***************************************')
  23. mean_loss = train_l_sum/(batch*batch_size) #计算平均到每张图片的loss
  24. train_acc = acc_sum/(batch*batch_size) #计算训练准确率
  25. test_acc = test() #计算测试准确率
  26. end = time.time()
  27. time_per_epoch = end - start
  28. print('epoch %d,train_loss %f,train_acc %f,test_acc %f,time %f' %
  29. (epoch + 1,mean_loss,train_acc,test_acc,time_per_epoch))
  30. train()

全连接层4096个神经元,参数太多,训练缓慢.4G的GTX 1050显卡,训练一个epoch大概一个多小时.

完整代码:https://github.com/sdu2011/learn_pytorch


batch=4,收敛极慢,迭代次数不够的话,欠拟合严重.在训练集上的train accuracy也很低.

由于全连接层的存在,参数极多,造成训练慢,显存占用多,导致batch_size调不大.模型修改为

  1. class VGG(nn.Module):
  2. def __init__(self,input_channels,cfg,num_classes=10, init_weights=True):
  3. super(VGG, self).__init__()
  4. self.conv = make_layers(input_channels,cfg) # torch.Size([1, 512, 7, 7])
  5. self.fc = nn.Sequential(
  6. nn.Linear(512*7*7,512),
  7. nn.ReLU(inplace=True), #inplace作用:节省显存 https://www.cnblogs.com/wanghui-garcia/p/10642665.html
  8. nn.Dropout(p=0.5),
  9. nn.Linear(512,512),
  10. nn.ReLU(inplace=True),
  11. nn.Dropout(p=0.5),
  12. nn.Linear(512,num_classes)
  13. )
  14. def forward(self, img):
  15. feature = self.conv(img)
  16. output = self.fc(feature.view(img.shape[0], -1))
  17. return output

全连接层调整为512个神经元.batch_size调到16.训练快多了.

从头学pytorch(十六):VGG NET的更多相关文章

  1. 从头学pytorch(十二):模型保存和加载

    模型读取和存储 总结下来,就是几个函数 torch.load()/torch.save() 通过python的pickle完成序列化与反序列化.完成内存<-->磁盘转换. Module.s ...

  2. 从头学pytorch(十五):AlexNet

    AlexNet AlexNet是2012年提出的一个模型,并且赢得了ImageNet图像识别挑战赛的冠军.首次证明了由计算机自动学习到的特征可以超越手工设计的特征,对计算机视觉的研究有着极其重要的意义 ...

  3. 从头学pytorch(十八):GoogLeNet

    GoogLeNet GoogLeNet和vgg分别是2014的ImageNet挑战赛的冠亚军.GoogLeNet则做了更加大胆的网络结构尝试,虽然深度只有22层,但大小却比AlexNet和VGG小很多 ...

  4. 从头学pytorch(十九):批量归一化batch normalization

    批量归一化 论文地址:https://arxiv.org/abs/1502.03167 批量归一化基本上是现在模型的标配了. 说实在的,到今天我也没搞明白batch normalize能够使得模型训练 ...

  5. 从头学pytorch(十四):lenet

    卷积神经网络 在之前的文章里,对28 X 28的图像,我们是通过把它展开为长度为784的一维向量,然后送进全连接层,训练出一个分类模型.这样做主要有两个问题 图像在同一列邻近的像素在这个向量中可能相距 ...

  6. 从头学pytorch(一):数据操作

    跟着Dive-into-DL-PyTorch.pdf从头开始学pytorch,夯实基础. Tensor创建 创建未初始化的tensor import torch x = torch.empty(5,3 ...

  7. 从头学pytorch(六):权重衰减

    深度学习中常常会存在过拟合现象,比如当训练数据过少时,训练得到的模型很可能在训练集上表现非常好,但是在测试集上表现不好. 应对过拟合,可以通过数据增强,增大训练集数量.我们这里先不介绍数据增强,先从模 ...

  8. 从头学pytorch(二十):残差网络resnet

    残差网络ResNet resnet是何凯明大神在2015年提出的.并且获得了当年的ImageNet比赛的冠军. 残差网络具有里程碑的意义,为以后的网络设计提出了一个新的思路. googlenet的思路 ...

  9. 从头学pytorch(二十二):全连接网络dense net

    DenseNet 论文传送门,这篇论文是CVPR 2017的最佳论文. resnet一文里说了,resnet是具有里程碑意义的.densenet就是受resnet的启发提出的模型. resnet中是把 ...

随机推荐

  1. 查看php-fpm的进程和端口号

    ps -ef | grep php-fpm   查看php-fpm所有的进程 ps -ef | grep php-fpn.conf 查看配置所在路径 netstat -lntp 查看监听端口  lis ...

  2. P1014 高精度减法

    题目描述 给你两个很大的正整数A和B,你需要计算他们的差. 输入格式 输入一行包含两个正整数A和B,以一个空格分隔(A和B的位数都不超过 \(10^5\) ,但是B有可能比A大) 输出格式 输出一行包 ...

  3. printk函数 打印设备编号

    偶尔地, 当从一个驱动打印消息, 你会想打印与感兴趣的硬件相关联的设备号. 打印主次 编号不是特别难, 但是, 为一致性考虑, 内核提供了一些实用的宏定义( 在 <linux/kdev_t.h& ...

  4. tf.squeeze()

    转载自:https://www.cnblogs.com/mdumpling/p/8053376.html 原型 tf.squeeze(input, squeeze_dims=None, name=No ...

  5. 2019-9-24-dotnet-remoting-抛出异常

    title author date CreateTime categories dotnet remoting 抛出异常 lindexi 2019-09-24 15:39:50 +0800 2018- ...

  6. Linux 内核中的数据类型

    在我们进入更高级主题之前, 我们需要停下来快速关注一下可移植性问题. 现代版本的 Linux 内核是 高度可移植的, 它正运行在很多不同体系上. 由于 Linux 内核的多平台特性, 打算做认真使用的 ...

  7. Linux 内核总线属性

    几乎 Linux 驱动模型中的每一层都提供一个添加属性的接口, 并且总线层不例外. bus_attribute 类型定义在 <linux/device.h> 如下: struct bus_ ...

  8. <Codeforce>1082A. Vasya and Book

    题目描述: Vasya is reading a e-book. The file of the book consists of nn pages, numbered from 11 to nn. ...

  9. MFC 获取本机IP、网络ip和物理地址

    获取本机Ip CString CNet::GetLocalIP() { WSADATA wsaData; , ), &wsaData); ) { return ""; } ...

  10. codeforces 572(Div2)A、B、C、D1、D2、E

    Cdoeforces 572(Div2)A.B.C.D1.D2.E 传送门:https://codeforces.com/contest/1189 A.题意: 给你一串长为n的字符串,要求你将其切割为 ...