VGG 最大的特点就是通过比较彻底地采用 3x3 尺寸的卷积核来堆叠神经网络
,这样也加深整个神经网络的深度。这两个重要的改变对于人们重新定义卷积神经网络模型架构也有不小的帮助,至少证明使用更小的卷积核并且增加卷积神经网络的深度,可以更有效地提升模型的性能。
1.VGG16结构图 
  
 
 2.VGG16总参数
VGG16总参数量是138M,具体如下:
 第1层:1792 = 3*3*3*64+64
 第2层:36928 = 3*3*64*64+64
 第3层:73856 = 3*3*64*128+128
 第4层:147584 = 3*3*128*128+128
 第5层:295168 = 3*3*128*256+256
 第6层:590080 = 3*3*256*256+256
 第7层:590080 = 3*3*256*256+256
 第8层:1180160 = 3*3*256*512+512
 第9层:2359808 = 3*3*512*512+512
 第10层:2359808 = 3*3*512*512+512
 第11层:2359808 = 3*3*512*512+512
 第12层:2359808 = 3*3*512*512+512
 第13层:2359808 = 3*3*512*512+512
 第14层:102764544 = 7*7*512*4096+4096
 第15层:16781312 = 4096*4096+4096
 第16层:4097000 = 4096*1000+1000
3.VGG总结
 1、VGG16相比AlexNet的一个改进是采用连续的3x3的卷积核代替AlexNet中的较大卷积核(11x11,7x7,5x5)
2、加深结构都使用ReLU激活函数:提升非线性变化的能力
 3、VGG16 全部采用3*3卷积核,步长统一为1,Padding统一为1,和2*2最大池化核,步长为2,Padding统一为0
 4、VGG19比VGG16的区别在于多了3个卷积层,其它完全一样
5、VGG16基本是AlexNet(AlexNet是8层,包括5个卷积层和3个全连接层)的加强版,深度上是其2倍,参数量大小也是两倍多。
  
4.VGG创新点
1。使用3x3卷积核替代7x7卷积核的好处?
2 个 3x3 的卷积核叠加,它们的感受野等同于 1 个 5x5 的卷积核,3 个叠加后,它们的感受野等同于 1 个 7x7 的效果。
由于感受野相同,3个3x3的卷积,使用了3个非线性激活函数,增加了非线性表达能力,从而可以提供更复杂的模式学习。
使用3x3卷积核可以减少参数,假设现在有 3 层 3x3 卷积核堆叠的卷积层,输出和输出通道数都是C,那么它的参数总数是 
3x(3x3xCxC)=27xCxC 。同样和它感受野大小一样的一个卷积层,卷积核是 7x7 的尺寸,假如输出和输出通道数都是C,那么它的参数总数就是 
7x7xCxC=49xCxC。而且通过上述方法网络层数还加深了。三层3x3的卷积核堆叠参数量比一层7x7的卷积核参数链还要少。总的来说,
使用3x3卷积核堆叠的形式,既增加了网络层数又减少了参数量。
2。多少个3x3的卷积核可以替代原来11x11的卷积核?
(11-1)/2=5,故5个3x3的卷积核可以替代原来11x11的卷积核,即n-11+1=n+(-3+1)*5
3。VGG的C网络结构使用了1x1卷积核,1x1卷积核的主要好处?
使用多个1x1卷积核,在保持feature map 尺寸不变(即不损失分辨率)的前提下,可以大幅增加非线性表达能力,把网络做得很deep。
进行卷积核通道数的降维和升维。
1x1卷积相当于线性变换,非线性激活函数起到非线性作用。
总结就是:1x1 卷积核的好处是不改变感受野的情况下,进行升维和降维,同时也加深了网络的深度
5.代码
import torch import torch.nn as nn class VGG16(nn.Module): def __init__(self, 
num_classes=10): super(VGG16, self).__init__() self.features = nn.Sequential( 
#1 nn.Conv2d(3,64,kernel_size=3,padding=1), nn.BatchNorm2d(64), nn.ReLU(True), 
#2 nn.Conv2d(64,64,kernel_size=3,padding=1), nn.BatchNorm2d(64), nn.ReLU(True), 
nn.MaxPool2d(kernel_size=2,stride=2), #3 
nn.Conv2d(64,128,kernel_size=3,padding=1), nn.BatchNorm2d(128), nn.ReLU(True), 
#4 nn.Conv2d(128,128,kernel_size=3,padding=1), nn.BatchNorm2d(128), 
nn.ReLU(True), nn.MaxPool2d(kernel_size=2,stride=2), #5 
nn.Conv2d(128,256,kernel_size=3,padding=1), nn.BatchNorm2d(256), nn.ReLU(True), 
#6 nn.Conv2d(256,256,kernel_size=3,padding=1), nn.BatchNorm2d(256), 
nn.ReLU(True), #7 nn.Conv2d(256,256,kernel_size=3,padding=1), 
nn.BatchNorm2d(256), nn.ReLU(True), nn.MaxPool2d(kernel_size=2,stride=2), #8 
nn.Conv2d(256,512,kernel_size=3,padding=1), nn.BatchNorm2d(512), nn.ReLU(True), 
#9 nn.Conv2d(512,512,kernel_size=3,padding=1), nn.BatchNorm2d(512), 
nn.ReLU(True), #10 nn.Conv2d(512,512,kernel_size=3,padding=1), 
nn.BatchNorm2d(512), nn.ReLU(True), nn.MaxPool2d(kernel_size=2,stride=2), #11 
nn.Conv2d(512,512,kernel_size=3,padding=1), nn.BatchNorm2d(512), nn.ReLU(True), 
#12 nn.Conv2d(512,512,kernel_size=3,padding=1), nn.BatchNorm2d(512), 
nn.ReLU(True), #13 nn.Conv2d(512,512,kernel_size=3,padding=1), 
nn.BatchNorm2d(512), nn.ReLU(True), nn.MaxPool2d(kernel_size=2,stride=2), 
nn.AvgPool2d(kernel_size=1,stride=1), ) self.classifier = nn.Sequential( #14 
nn.Linear(512,4096), nn.ReLU(True), nn.Dropout(), #15 nn.Linear(4096, 4096), 
nn.ReLU(True), nn.Dropout(), #16 nn.Linear(4096,num_classes), ) 
#self.classifier = nn.Linear(512, 10) def forward(self, x): out = 
self.features(x) # print(out.shape) out = out.view(out.size(0), -1) # 
print(out.shape) out = self.classifier(out) # print(out.shape) return out