0%

回顾-VGG

自动摘要: title:回顾VGG date:2018121918:46:12 tags: VGG categories: 框架 pytorch ……..

title: 回顾-VGG
date: 2018-12-19 18:46:12
tags:
- VGG
categories:
- 框架
- pytorch
- VGG

1. VGGNet 简介:

1.VGG是由Simonyan 和Zisserman在文献《Very Deep Convolutional Networks for Large Scale Image Recognition》中提出卷积神经网络模型,其名称来源于作者所在的牛津大学视觉几何组(Visual Geometry Group)的缩写。

2.该模型参加2014年的 ImageNet图像分类与定位挑战赛,取得了优异成绩:在分类任务上排名第二,在定位任务上排名第一。


2. VGGNet 网络结构:

根据VGG中根据卷积核大小和卷积层数目的不同,分为A,A-LRN,B,C,D,E共6个网络结构配置!


D,E就是常用配置,也就是常说的VGG16和VGG19

  • 如图:

** 1.为什么叫vgg16? **
如图绿色(D配置),即VGG16配置,

  • 1.用conv3表示3*3卷积,conv3-xx中的xx表示通道数(也就是有多少个这样的卷积核)
  • 2.一共数下来 有13个conv3,3个FC(最下面那里),5个池化层(每个模块中间)
  • 3.13(卷积)+3(全连接)=16,不加池化层,是因为池化层不涉及权重.所以d配置叫VGG16,其他以此类推!

  • 官方图:
    • 如图:

  • 这个图又看出什么?
    • 1.每个卷积层(上面的conv3-xx)里面是表示conv+relu(图中黑色方块注释)
    • 2.看到红色方块(池化层)没,是不是经过它,似乎方块都变小1/2了!
      • 即池化层采用的参数均为2*2,步幅stride=2,max的池化方式,这样就能够使得每一个池化层的宽和高是前一层的1/2。

  • 输入图片大小变化图:

  • 这个图又看出什么?
    • 1.图片从224*244大小→112*112→56*56→28*28→ 14*14→7*7大小(图最左边Size),大小一直减半
    • 2.卷积通道数(卷积核数量)从64–>128–>256–>512,然后固定在512! 都是2倍数(因为计算机以2进制计算)—————-2是个好东西

  • 计算量图:

    ** 注:memory=内存计算(红色),params=参数量计算(蓝色)**

3.VGG优缺点:

** 优点:**

  • 1.VGGNet的结构非常简洁,整个网络都使用了同样大小的卷积核尺寸(3x3)和最大池化尺寸(2x2)
  • 2.几个小滤波器(3x3)卷积层的组合比一个大滤波器(5x5或7x7)卷积层好
  • 3.通过不断加深网络结构可以提升性能.具有很高的拟合能力(怎么加深?(这个网络告诉我们就像汉堡一样一层层堆叠上去))

** 缺点:**

  • 1.训练时间过长(3个全连接啊(听说:发现这些全连接层即使被去除,对于性能也没有什么影响,这样就显著降低了参数数量)),调参难度大
  • 2.需要的存储容量大,不利于部署。(单片机就那几MB可怜的空间)

4.PyTorch实现:

  • 网络架构:VGG.py文件:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    import torch


    '''
    今天这个有点难得堆
    一个个写,重复性太高,而且不利于观看,不优雅
    重复性事我们交给你喊函数完成
    '''
    #遵从原来步骤
    class VGG(torch.nn.Module):
    #初始化
    def __init__(self):
    super(VGG, self).__init__()
    self.Sumlayers=self.make_layers()#这里我们交由make_layers函数创建相似的模块
    #假设我们上面已经创好五个模块(文章第一幅图block1->block5)

    #现在创建最后的Fc
    self.fc=torch.nn.Sequential(
    torch.nn.Linear (7*7*512, 4096), # 第一个全连接
    torch.nn.ReLU(),

    torch.nn.Linear (4096, 4096), # 第二个全连接
    torch.nn.ReLU(),

    torch.nn.Linear (4096, 2), # 第三个全连接
    # 原版1000类 最后分成2类 因为只有猫狗两个类
    torch.nn.ReLU(),

    torch.nn.Softmax (dim=1), # 最后一个 softmax 不填dim=1会报警 1.0以前好像可以直接写Softmax ()


    )

    #前向传播
    def forward (self, x):
    conv = self.Sumlayers (x)
    res = conv.view (conv.size (0), -1) # 展平多维的卷积图成 一维
    out = self.fc(res)
    return out


    #构建刚才的构建模块函数make_layers
    def make_layers(self):
    #创建一个列表,用来快速构造模块,你也可以测试vgg19等等
    vgg16=[64, 64, 'Maxpool', 128, 128, 'Maxpool', 256, 256, 256, 'Maxpool',
    512, 512, 512, 'Maxpool', 512, 512, 512, 'Maxpool']
    #创建一个列表,用来放后面层 ,后面我们直接往里面添加就可以了
    Sumlayers=[]

    #创建一个变量,来控制 卷积参数输入大小(in_channels)和输出大小(out_channels)
    in_c = 3 #第一次输入大小
    #遍历列表
    for x in vgg16: #获取到每个配置,我这只有vgg16这一行
    if x =='Maxpool':#如果遇到Maxpool ,我们就创建maxpool层
    Sumlayers+=[torch.nn.MaxPool2d(kernel_size=2, stride=2)]#参数看上文
    else: #否则我们创建conv(卷积模块)
    Sumlayers+= [torch.nn.Conv2d (in_channels=in_c,out_channels=x , kernel_size=3, padding=1), #x是列表中的参数
    torch.nn.BatchNorm2d (x),#标准化一下
    torch.nn.ReLU ()]
    in_c=x #输出大小成为下个输入大小

    return torch.nn.Sequential (*Sumlayers) #然后把构建好模型传出


  • 网络架构:train.py文件:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    import torch
    import torch.nn as nn
    import torchvision.datasets as datasets
    import torchvision.transforms as transforms
    from torch.autograd import Variable

    # 工具包
    import argparse
    # 载入网络
    from VGG import VGG

    #############参数设置#############
    ####命令行设置########
    parser = argparse.ArgumentParser (description='CNN') # 导入命令行模块
    # 对于函数add_argumen()第一个是选项,第二个是数据类型,第三个默认值,第四个是help命令时的说明
    parser.add_argument ('--batch-size', type=int, default=2, metavar='N',
    help='训练batch-size大小 (default: 2)')
    parser.add_argument ('--epochs', type=int, default=10, metavar='N',
    help='训练epochs大小 (default: 10)')
    parser.add_argument ('--lr', type=float, default=0.001, metavar='LR',
    help='学习率 (default: 0.001)')
    parser.add_argument ('--no-cuda', action='store_true', default=False,
    help='不开启cuda训练')
    parser.add_argument ('--seed', type=int, default=1, metavar='S',
    help='随机种子 (default: 1)')
    parser.add_argument ('--log-interval', type=int, default=1, metavar='N',
    help='记录等待n批次 (default: 1)')
    args = parser.parse_args () # 相当于激活命令

    args.cuda = not args.no_cuda and torch.cuda.is_available () # 判断gpu

    torch.manual_seed (args.seed)
    if args.cuda:
    torch.cuda.manual_seed (args.seed) # 为CPU设置种子用于生成随机数,以使得结果是确定的

    ##########数据转换#####################
    data_transforms = transforms.Compose ([transforms.Scale (224), # 通过调整比例调整大小,会报警
    transforms.CenterCrop (224), # 在中心裁剪给指定大小方形PIL图像
    transforms.ToTensor ()]) # 转换成pytorch 变量tensor
    ###############数据载入################
    train_dataset = datasets.ImageFolder (root="./data/train/", # 保存目录
    transform=data_transforms) # 把数据转换成上面约束样子

    test_dataset = datasets.ImageFolder (root='./data/test/',
    transform=data_transforms)

    ##########数据如下####
    # # root/dog/xxx.png
    # # root/dog/xxy.png
    # # root/dog/xxz.png
    # #
    # # root/cat/123.png
    # # root/cat/nsdf3.png
    # # root/cat/asd932_.png
    ######################

    ##############数据装载###############
    train_loader = torch.utils.data.DataLoader (dataset=train_dataset, # 装载数据
    batch_size=args.batch_size, # 设置批大小
    shuffle=True) # 是否随机打乱
    test_loader = torch.utils.data.DataLoader (dataset=test_dataset,
    batch_size=args.batch_size,
    shuffle=True)

    #############模型载入#################
    VGG=VGG ()
    if not args.no_cuda:
    print ('正在使用gpu')
    VGG.cuda ()
    print (VGG)

    ###############损失函数##################
    criterion = nn.CrossEntropyLoss () # 内置标准损失
    optimizer = torch.optim.Adam (VGG.parameters (), lr=args.lr) # Adam优化器
    #############训练过程#####################
    total_loss = 0 #内存循环使用
    for epoch in range (args.epochs):
    for i, (images, labels) in enumerate (train_loader): # 枚举出来
    if not args.no_cuda: # 数据处理是否用gpu
    images = images.cuda ()
    labels = labels.cuda ()

    images = Variable (images) # 装箱
    labels = Variable (labels)

    ##前向传播
    optimizer.zero_grad ()
    outputs = VGG (images)
    # 损失
    loss = criterion (outputs, labels)
    # 反向传播
    loss.backward ()
    optimizer.step ()#更新参数
    total_loss += loss#内存循环使用 防止cuda超出内存
    ##打印记录

    if (i + 1) % args.log_interval == 0:
    print ('Epoch [%d/%d], Iter [%d/%d] Loss: %.4f'
    % (epoch + 1, args.epochs, i + 1, len (train_dataset) // args.batch_size, loss.item ()))

    # 保存模型
    torch.save (VGG.state_dict (), 'VGG.pkl')




结果如图:

注:如果提示内存不够,可以在命令行用下面命令训练:

1
python train.py --no-cuda --batch-size=2

** 因为我自带内存8g,显卡内存2g,所以我不用gpu跑**

  • 本文作者: SindreYang
  • 本文链接: http://blog.mviai.com/2018/回顾-VGG/
  • 版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!

欢迎关注我的其它发布渠道