当前位置:   article > 正文

从VGG看CNN_vggnet相对于传统cnn的优点

vggnet相对于传统cnn的优点

从VGG看CNN

VGG的成功之处

VGG与AlexNet相比,VGG采用小的卷积核和池化层,层数更深,通道数更多,其中每个通道代表着一个FeatureMap,更多的通道数表示更丰富的图像特征。VGG网络第一层的通道数为64,后面每层都进行了翻倍,最多到512个通道,通道数的增加,使得更多的信息可以被提取出来。

对于给定的感受野,VGG可以使用小卷积核代替大卷积核,比如2个3x3的卷积核可以代替一个5x5的卷积核、3个3x3的卷积核可以代替一个7x7的卷积核。

采用堆积的小卷积核优于采用大的卷积核,因为可以增加网络深度来保证学习更复杂的模式,而且代价还比较小(参数更少)。

从上表可以看出,大卷积核带来的特征图和卷积核得参数量并不大,无论是单独去看卷积核参数或者特征图参数,不同kernel大小下这二者加和的结构都是30万的参数量,也就是说,无论大的卷积核还是小的,对参数量来说影响不大甚至持平。

卷积层的参数减少。相比5x5、7x7和11x11的大卷积核,3x3明显地减少了参数量,这点可以回过头去看上面的表格。比方input channel数和output channel数均为C,那么3层conv3x3卷积所需要的卷积层参数是:3x(Cx3x3xC)=27C2,而一层conv7x7卷积所需要的卷积层参数是:Cx7x7xC=49C2。conv7x7的卷积核参数比conv3x3多了(49-27)/27x100% ≈ 81%;

增大的反而是卷积的计算量,在表格中列出了计算量的公式,最后要乘以2,代表乘加操作。为了尽可能证一致,这里所有卷积核使用的stride均为4,可以看到,conv3x3、conv5x5、conv7x7、conv9x9、conv11x11的计算规模依次为:1600万,4500万,1.4亿、2亿,这种规模下的卷积,虽然参数量增长不大,但是计算量是惊人的。

总结一下,我们可以得出两个结论:

  • 同样stride下,不同卷积核大小的特征图和卷积参数差别不大;
  • 越大的卷积核计算量越大。

其实一个关键的点——多个小卷积核的堆叠比单一大卷积核带来了精度提升,这也是最重要的一点。

采用小卷积核的优点

  • 采用小卷积核,使层数增加,可以使用更多的激活函数,获取更多的特征,更强的辨识能力
  • 减少参数

VGG的结构特点

在上图中加粗的文字是相比前一个网络增加的结构。从上图易知层数越多参数越多,神经网络越复杂。

由上图可得:

A与A-LRN相比,局部响应归一化LRN对上述结构没多大的影响

B与A相比,通过增加卷积数来增加准确率

C与B相比,使用1x1的conv在不影响感受野的情况下增加非线性决策函数,同时C中的第三个与前两个相比增加图像的尺寸抖动(S[min],S[max],min<=max-min)可以增加准确率。

E与D相比,通过增加卷积数来增加top-1准确率但是却增加了top-5错误率

VGG-11代码

#!/usr/bin/env python
# coding: utf-8

# In[ ]:


import time
import torch
import torchvision
from torch import nn,optim


# In[ ]:


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


# In[17]:


print(device)


# In[18]:


get_ipython().system('nvidia-smi')


# In[19]:


torch.cuda.get_device_name(0) #


# In[ ]:


def load_data_fashion_mnist(batch_size, resize=None, root='~/Datasets/FashionMNIST'):
    """Download the fashion mnist dataset and then load into memory."""
    trans = []
    if resize:
        trans.append(torchvision.transforms.Resize(size=resize))  # 更改PIL图像的大小
    trans.append(torchvision.transforms.ToTensor())  # 将形状为(HxWxC)PIL的图像转为形状为(CxHxW)的FloatTensor

    transform = torchvision.transforms.Compose(trans)  # 一起组成一个变换
    mnist_train = torchvision.datasets.FashionMNIST(root=root, train=True, download=True, transform=transform)
    mnist_test = torchvision.datasets.FashionMNIST(root=root, train=False, download=True, transform=transform)

    train_iter = torch.utils.data.DataLoader(mnist_train, batch_size=batch_size, shuffle=True, num_workers=4)
    test_iter = torch.utils.data.DataLoader(mnist_test, batch_size=batch_size, shuffle=False, num_workers=4)

    return train_iter, test_iter


# In[ ]:


def evaluate_accuracy(data_iter, net, device=None):
    if device is None and isinstance(net, nn.Module):
        device = list(net.parameters())[0].device  # 运行设备为net所运行的设备
    acc_sum, n = 0.0, 0
    with torch.no_grad():  # 禁用梯度计算
        for X, y in data_iter:
            if isinstance(net, nn.Module):
                net.eval()  # 不启用 BatchNormalization 和 Dropout
                acc_sum += (net(X.to(device)).argmax(dim=1) == y.to(device)).float().sum().cpu().item()
                net.train()  # 启用 BatchNormalization 和 Dropout
            else:
                if ('is training' in net.__code__.co_varnames):
                    acc_sum += (net(X, is_training=False).argmax(dim=1) == y).float().sum().item()
                else:
                    acc_sum += (net(X).argmax(dim=1) == y).float().sum().item()
            n += y.shape[0]
    return acc_sum / n


# In[ ]:


def train(net, train_iter, test_iter, batch_size, optimizer, device, num_epochs):
    net = net.to(device)
    print("training on ", device)
    loss = torch.nn.CrossEntropyLoss()
    for epoch in range(num_epochs):
        train_l_sum, train_acc_sum, n, batch_count, start = 0.0, 0.0, 0, 0, time.time()
        for X, y in train_iter:
            X = X.to(device)
            y = y.to(device)
            y_hat = net(X)
            l = loss(y_hat, y)
            optimizer.zero_grad()
            l.backward()  # 把参数通过反向传播来优化
            optimizer.step()  # 更新参数
            train_l_sum += l.cpu().item()
            train_acc_sum += (y_hat.argmax(dim=1) == y).sum().cpu().item()  # softMax:y_hat.argmax(dim=1)
            n += y.shape[0]
            batch_count += 1
        test_acc = evaluate_accuracy(test_iter, net)
        print('epoch %d, loss %.4f, train acc %.3f, test acc %.3f, time %.1f sec'
              % (epoch + 1, train_l_sum / batch_count, train_acc_sum / n, test_acc, time.time() - start))


# In[ ]:


def vgg_block(num_convs,in_channels,out_channels):
    blk=[]
    for i in range(num_convs):
        if i==0:
            blk.append(nn.Conv2d(in_channels,out_channels,kernel_size=3,padding=1))
        else:
            blk.append(nn.Conv2d(out_channels,out_channels,kernel_size=3,padding=1))
        blk.append(nn.ReLU())
    blk.append(nn.MaxPool2d(kernel_size=2,stride=2))
    return nn.Sequential(*blk) # conv+relu+maxpool


# In[ ]:


conv_arch=((1,1,64),(1,64,128),(2,128,256),(2,256,512),(2,512,512))
# 经过5个vgg_block, 宽高会减半5次, 变成 224/32 = 7
fc_features=512*7*7 # c * w * h
fc_hidden_units=4096


# In[ ]:


class FlattenLayer(nn.Module):
    def __init__(self):
        super(FlattenLayer,self).__init__()
    def forward(self, x):
        return x.view(x.shape[0],-1)


# In[ ]:


def vgg(conv_arch,fc_features,fc_hidden_units=4096):
    net=nn.Sequential()
    for i , (num_convs,in_channels, out_channels) in enumerate(conv_arch):
        net.add_module('vgg_block_'+str(i+1),vgg_block(num_convs,in_channels,out_channels))
    net.add_module('fc',nn.Sequential(FlattenLayer(),
                                      nn.Linear(fc_features,fc_hidden_units),
                                      nn.ReLU(),
                                      nn.Dropout(0.5),
                                      nn.Linear(fc_hidden_units,fc_hidden_units),
                                      nn.ReLU(),
                                      nn.Dropout(0.5),
                                      nn.Linear(fc_hidden_units,10)))
    return net


# In[27]:


net=vgg(conv_arch,fc_features,fc_hidden_units)
X=torch.rand(1,1,224,224)
# print('X:\n',X)
# print('X.shape:\n',X.shape)
print(net)

for name,blk in net.named_children():
    X=blk(X)
    print(name,'output shape:',X.shape)


# In[28]:


batch_size=64
train_iter,test_iter=load_data_fashion_mnist(batch_size,resize=224)

lr,num_epoch=0.001,5
optimizer=torch.optim.Adam(net.parameters(),lr=lr)
train(net,train_iter,test_iter,batch_size,optimizer,device,num_epoch)


# In[29]:


ratio=8
small_conv_arch=[(1,1,64//ratio),(1,64//ratio,128//ratio),(2,128//ratio,256//ratio),
                (2,256//ratio,512//ratio),(2,512//ratio,512//ratio)] # 减少通道数
net=vgg(small_conv_arch,fc_features//ratio,fc_hidden_units//ratio)
print(net)


# In[30]:


batch_size=64
train_iter,test_iter=load_data_fashion_mnist(batch_size,resize=224)

lr,num_epoch=0.001,5
optimizer=torch.optim.Adam(net.parameters(),lr=lr)
train(net,train_iter,test_iter,batch_size,optimizer,device,num_epoch)





  • 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
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家小花儿/article/detail/496878
推荐阅读
相关标签
  

闽ICP备14008679号