VGG-16网络模型pytorch实现
今天把VGG-16的网络模型过了一遍,来记录一下,我是根据别人的博客改的代码,并没有完全的自己敲一遍。
下面是吴恩达老师第四课——第二周——2.2经典网络里的VGG-16网络结构图
这张图里很清晰的讲解了各个层图像的尺度和深度,以及卷积层和池化层的位置。
AlexNet只有八层网络,使用11 * 11的卷积核(滤波器),而VGG-16有16层网络,使用的是3 * 3的卷积核(滤波器)。VGG的优势在于使用3 * 3的卷积核代替AlexNet中大的卷积核,在具有相同的感受野的同时,减少了参数,有着更深的网络结构,因为每一个卷积层都引入了激励函数,所以更具非线性。
我的代码是参考的这一篇博文
在这一篇博文的基础上做了一些修改,添加了一些注释。细心的小伙伴会发现我参考的这一篇博文并没有在每一个卷积层之后引入激励函数。而卷积层进行的是线性操作,也就是说不管有多少个卷积层,最后得到的结果都是输入层的线性变换,而引入激励函数后,会增加卷积结果的非线性。因此我在每一个卷积层之后都引入了激励函数。然后就是在每一个大的层的第一个卷积层增加了padding,这样可以保证图像的尺寸不会减少,保存更多的边缘位置信息(虽然我师兄说对于224 * 224 * 3的图像而言,多保留的边缘位置信息的效果并不明显)
下面是代码实现
import torch
import torch.nn as nn
import torch.nn.functional as F
class VGG16(nn.Module):
def __init__(self):
super(VGG16, self).__init__()
'''卷积层输出图像的尺寸
N=(W−F+2P)/S+1
W 表示输入图片尺寸(W×W)
F 表示卷积核的大小(F×F)
S 表示步长
P 表示填充的像素数(Padding)
池化层输出图像的尺寸:
N = (W-F)/S+1
'''
# 通道数为3,尺寸为224*244*3,卷积核个数为64,卷积核尺寸为3*3*3,默认步长为1,sample填充(用0填充)
# 增加padding后可以保证图像的尺寸不会减小
self.conv1_1 = nn.Conv2d(3, 64, kernel_size=3,padding=1) #输出 64 * 224 * 224
#若如此引入激励函数,则会将激励函数作为激励层
self.relu1_1 = nn.ReLU()
#输入是一个tuple的话,则第一个参数表示高度上面的padding,第2个参数表示宽度上面的padding
#padding=(1, 1)的效果和padding=1的效果是一样的
self.conv1_2 = nn.Conv2d(64, 64, kernel_size=3, padding=(1, 1)) # 64 * 224 * 224
#若如此引入激励函数,则在输出网络结构的时候不会将激励函数作为一层输出
nn.ReLU()
#池化操作是非线性操作
self.maxpool1 = nn.MaxPool2d((2, 2), padding=(1, 1)) # 输出 64 * 112 * 112
#
self.conv2_1 = nn.Conv2d(64, 128, kernel_size=3, padding=1) # 输出128 * 112* 112
nn.ReLU()
self.conv2_2 = nn.Conv2d(128, 128, kernel_size=3, padding=(1, 1)) # 输出128 * 112* 112
nn.ReLU()
self.maxpool2 = nn.MaxPool2d((2, 2), padding=(1, 1)) # 输出 128 * 56 * 56
self.conv3_1 = nn.Conv2d(128, 256, kernel_size=3, padding=1) # 256 * 56 * 56
nn.ReLU()
self.conv3_2 = nn.Conv2d(256, 256, kernel_size=3, padding=(1, 1)) # 256 * 56 * 56
nn.ReLU()
self.conv3_3 = nn.Conv2d(256, 256, kernel_size=3, padding=(1, 1)) # 256 * 56 * 56
nn.ReLU()
self.maxpool3 = nn.MaxPool2d((2, 2), padding=(1, 1)) # pooling 256 * 28 * 28
self.conv4_1 = nn.Conv2d(256, 512, kernel_size=3, padding=1) # 512 * 28 * 28
nn.ReLU()
self.conv4_2 = nn.Conv2d(512, 512, kernel_size=3, padding=(1, 1)) # 512 * 28 * 28
nn.ReLU()
self.conv4_3 = nn.Conv2d(512, 512, kernel_size=3, padding=(1, 1)) # 512 * 28 * 28
nn.ReLU()
self.maxpool4 = nn.MaxPool2d((2, 2), padding=(1, 1)) # pooling 512 * 14 * 14
self.conv5_1 = nn.Conv2d(512, 512, kernel_size=3, padding=1) # 512 * 14 * 14
nn.ReLU()
self.conv5_2 = nn.Conv2d(512, 512, kernel_size=3, padding=(1, 1)) # 512 * 14 * 14
nn.ReLU()
self.conv5_3 = nn.Conv2d(512, 512, kernel_size=3, padding=(1, 1)) # 512 * 14 * 14
nn.ReLU()
self.maxpool5 = nn.MaxPool2d((2, 2), padding=(1, 1)) # pooling 512 * 7 * 7
#全连接层
self.fc1 = nn.Linear(512 * 7 * 7, 4096)
nn.ReLU()
#调用Dropout函数防止过拟合
nn.Dropout()
self.fc2 = nn.Linear(4096, 4096)
nn.ReLU()
nn.Dropout()
self.fc3 = nn.Linear(4096, 1000)
# softmax 1 * 1 * 1000
#前向传播
def forward(self, x):
# x.size(0)即为batch_size
in_size = x.size(0)
out = self.conv1_1(x) # 224
out = F.relu(out)
out = self.conv1_2(out) # 224
out = F.relu(out)
out = self.maxpool1(out) # 112
out = self.conv2_1(out) # 112
out = F.relu(out)
out = self.conv2_2(out) # 112
out = F.relu(out)
out = self.maxpool2(out) # 56
out = self.conv3_1(out) # 56
out = F.relu(out)
out = self.conv3_2(out) # 56
out = F.relu(out)
out = self.conv3_3(out) # 56
out = F.relu(out)
out = self.maxpool3(out) # 28
out = self.conv4_1(out) # 28
out = F.relu(out)
out = self.conv4_2(out) # 28
out = F.relu(out)
out = self.conv4_3(out) # 28
out = F.relu(out)
out = self.maxpool4(out) # 14
out = self.conv5_1(out) # 14
out = F.relu(out)
out = self.conv5_2(out) # 14
out = F.relu(out)
out = self.conv5_3(out) # 14
out = F.relu(out)
out = self.maxpool5(out) # 7
# 展平
out = out.view(in_size, -1)
out = self.fc1(out)
out = F.relu(out)
out = self.fc2(out)
out = F.relu(out)
out = self.fc3(out)
out = F.log_softmax(out, dim=1)
return out
if __name__ == '__main__':
net = VGG16()
print(net)