@Team
2018-09-17T15:56:59.000000Z
字数 6131
阅读 1655
石文华
AlexNet,是以论文的第一作者 Alex Krizhevsky 的名
字命名的,另外两位合著者是 ilya Sutskever 和 Geoffery Hinton。在2012年的ILSVRC比赛中,作者通过这个模型获得了冠军。如下图是这个卷积神经网络模型的网络架构
可以看到网络从输入到输出分别是输入层、Conv1层,MaxPool1层、Conv2层,MaxPool2层、Conv3层,Conv4层,Conv5层,MaxPool3层、FC6层、FC7层、FC8层和OutPut层。
(1)输入层:AlexNet 首先用一张 227×227×3 的图片作为输入,实际上原文中使用的图像是 224×224×3,但是如果你尝试去推导一下,你会发现 227×227 这个尺寸更好一些,这里使用输入数据是224*224*3的图像进行推导,即长宽都是224的图像,色彩通道是R、G、B三个。
(2)Conv1层:第一个卷积层、使用的卷积核大小为11*11*3,步长为4、Padding为2,最后输出的特征图为55*55*96,其中55=(224-11+4)/2+1,输出深度为96,是因为使用了96个卷积核进行卷积。最后得到特征图维度是55*55*96
(3)MaxPool1层:卷积之后使用最大池化,池化的滑动窗口是3*3*96,步长为2,池化之后输出的特征图的长宽是27,27=(55-3)/2+1,所以池化之后的特征图维度是27*27*96。
(4)Conv2层:使用卷积核大小为5*5*96,步长为1,Padding设置为same,这里为2,因为要确保输出长度为27,所以27=(27-5+2*a)/1+1,所以也可知Padding为2。这里使用了256个卷积核进行卷积,所以最后输出的特征图是27*27*256。
(5)MaxPool2层:滑动窗口大小为3*3*256,步长为2,得到的长宽为13,13=(27-3)/2+1,最后得到的特征图大小为13*13*256。
(6)Conv3层:卷积核大小为3*3*256,步长为1,Padding为1,得到特征图的长宽为13,13=(13-3+2)/1+1=13,使用384个卷积核进行卷积。得到输出为13*13*384。
(7)Conv4层:卷积核大小为3*3*384,步长为1,Padding为1,得到特征图的长宽为13,13=(13-3+2)/1+1=13,使用384个卷积核进行卷积。得到输出为13*13*384。
(8)Conv5层:卷积核大小为3*3*384,步长为1,Padding为1,得到特征图的长宽为13,13=(13-3+2)/1+1=13,使用256个卷积核进行卷积。得到输出为13*13*256。
(9)MaxPool3:滑动窗口大小为3*3*256,步长为2,得到的长宽为6,6=(13-3)/2+1,最后得到的特征图大小为6*6*256。
(10)FC6层:第一个全连接层,输入特征维度是6*6*256,首先先对输入的特征图进行扁平化处理,将维度变为1*9216的输入特征图,因为要求输出的数据维度为1*4096,所以需要一个9216*4096的矩阵完成输入数据和输出数据的全连接,最后得到输出数据的维度为1*4096。
(11)FC7层:输入数据维度是1*4096,输出特征要求为1*4096,所以需要4096*4096的矩阵完成输入数据和输出数据的全连接,随后得到输出数据的维度是1*4096。
(12)FC8层:输入数据维度是1*4096,输出特征要求为1*1000,所以需要4096*1000的矩阵完成输入数据和输出数据的全连接,随后得到输出数据的维度是1*1000。
(13)OutPut层:输出图像对应的1000个类别的可能性值,将FC7层的输出数据传递到Softma激活函数中,能够得到1000个全新的输出值,这1000个输出值就是模型预测的输入图像对应1000个类别的可能性。
AlexNet 网络结构看起来相对复杂,包含约 6000 万个参数, 这些数字(55×55×96、 27×27×96、
27×27×256……) 都是 Alex Krizhevsky 及其合著者不得不给出的,。
注:AlexNet网络卷积之后的激活函数是relu,每个全连接层后面加上Dropout层减少模型的过拟合问题。
import torch
import torch.nn as nn
class AlexNet(nn.Module):
def __init__(self, num_classes=10):
super(AlexNet, self).__init__()
self.features = nn.Sequential(
nn.Conv2d(3, 96, kernel_size=11, stride=4, padding=0),
nn.ReLU(inplace=True),
nn.MaxPool2d(kernel_size=3, stride=2),
nn.Conv2d(96, 256, kernel_size=5, padding=2, groups=2),
nn.ReLU(inplace=True),
nn.MaxPool2d(kernel_size=3, stride=2),
nn.Conv2d(256, 384, kernel_size=3, padding=1),
nn.ReLU(inplace=True),
nn.Conv2d(384, 384, kernel_size=3, padding=1, groups=2),
nn.ReLU(inplace=True),
nn.Conv2d(384, 256, kernel_size=3, padding=1, groups=2),
nn.ReLU(inplace=True),
nn.MaxPool2d(kernel_size=3, stride=2),
)
self.classifier = nn.Sequential(
nn.Linear(256 * 6 * 6, 4096),
nn.ReLU(inplace=True),
nn.Dropout(),
nn.Linear(4096, 4096),
nn.ReLU(inplace=True),
nn.Dropout(),
nn.Linear(4096, num_classes),
)
def forward(self, x):
x = self.features(x)
x = x.view(x.size(0), 256 * 6 * 6)
x = self.classifier(x)
return x
alexnet=AlexNet()
print(alexnet)
输出结果:
AlexNet(
(features): Sequential(
(0): Conv2d(3, 96, kernel_size=(11, 11), stride=(4, 4))
(1): ReLU(inplace)
(2): MaxPool2d(kernel_size=3, stride=2, padding=0, dilation=1, ceil_mode=False)
(3): Conv2d(96, 256, kernel_size=(5, 5), stride=(1, 1), padding=(2, 2), groups=2)
(4): ReLU(inplace)
(5): MaxPool2d(kernel_size=3, stride=2, padding=0, dilation=1, ceil_mode=False)
(6): Conv2d(256, 384, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(7): ReLU(inplace)
(8): Conv2d(384, 384, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=2)
(9): ReLU(inplace)
(10): Conv2d(384, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=2)
(11): ReLU(inplace)
(12): MaxPool2d(kernel_size=3, stride=2, padding=0, dilation=1, ceil_mode=False)
)
(classifier): Sequential(
(0): Linear(in_features=9216, out_features=4096, bias=True)
(1): ReLU(inplace)
(2): Dropout(p=0.5)
(3): Linear(in_features=4096, out_features=4096, bias=True)
(4): ReLU(inplace)
(5): Dropout(p=0.5)
(6): Linear(in_features=4096, out_features=10, bias=True)
)
)
加载数据:
import torch
from torchvision import datasets,transforms
import os
import matplotlib.pyplot as plt
import time
#transform = transforms.Compose是把一系列图片操作组合起来,比如减去像素均值等。
#DataLoader读入的数据类型是PIL.Image
#这里对图片不做任何处理,仅仅是把PIL.Image转换为torch.FloatTensor,从而可以被pytorch计算
transform = transforms.Compose(
[
transforms.Scale([224,224]),
transforms.ToTensor(),
#transforms.Normalize(mean=[0.5,0.5,0.5],std=[0.5,0.5,0.5])
]
)
#训练集
train_set = datasets.CIFAR10(root='drive/pytorch/Alexnet/', train=True, transform=transform, target_transform=None, download=True)
#测试集
test_set=datasets.CIFAR10(root='drive/pytorch/Alexnet/',train=False,download=True,transform=transform)
trainloader=torch.utils.data.DataLoader(train_set,batch_size=32,shuffle=True,num_workers=0)
testloader=torch.utils.data.DataLoader(test_set,batch_size=32,shuffle=True,num_workers=0)
classes=('plane','car','bird','cat','deer','dog','frog','horse','ship','truck')
(data,label)=train_set[64]
print(classes[label])
查看数据:
X_example,y_example=next(iter(trainloader))
print(X_example.shape)
img=X_example.permute(0, 2, 3, 1)
print(img.shape)
import torchvision
img=torchvision.utils.make_grid(X_example)
img=img.numpy().transpose([1,2,0])
import matplotlib.pyplot as plt
plt.imshow(img)
plt.show()
训练模型:
import torch.optim as optim #导入torch.potim模块
import time
from torch.autograd import Variable # 这一步还没有显式用到variable,但是现在写在这里也没问题,后面会用到
import torch.nn as nn
import torch.nn.functional as F
criterion = nn.CrossEntropyLoss() #同样是用到了神经网络工具箱 nn 中的交叉熵损失函数
optimizer = optim.Adam(alexnet.classifier.parameters(), lr=0.0001) #optim模块中的SGD梯度优化方式---随机梯度下降
epoch_n=5
for epoch in range(epoch_n):
print("Epoch{}/{}".format(epoch,epoch_n-1))
print("-"*10)
running_loss = 0.0 #定义一个变量方便我们对loss进行输出
running_corrects=0
for i, data in enumerate(trainloader, 1): # 这里我们遇到了第一步中出现的trailoader,代码传入
inputs, labels = data # data是从enumerate返回的data,包含数据和标签信息,分别赋值给inputs和labels
#inputs=inputs.permute(0, 2, 3, 1)
#print("hahah",len(labels))
y_pred = alexnet(inputs) # 把数据输进网络net,这个net()在第二步的代码最后一行我们已经定义了
_,pred=torch.max(y_pred.data,1)
optimizer.zero_grad() # 要把梯度重新归零,因为反向传播过程中梯度会累加上一次循环的梯度
loss = criterion(y_pred, labels) # 计算损失值,criterion我们在第三步里面定义了
loss.backward() # loss进行反向传播,下文详解
optimizer.step() # 当执行反向传播之后,把优化器的参数进行更新,以便进行下一轮
# print statistics # 这几行代码不是必须的,为了打印出loss方便我们看而已,不影响训练过程
running_loss += loss.item() # 从下面一行代码可以看出它是每循环0-1999共两千次才打印一次
running_corrects+=torch.sum(pred==labels.data)
if(i % 2 == 0): # print every 2000 mini-batches 所以每个2000次之类先用running_loss进行累加
print("Batch{},Train Loss:{:.4f},Train ACC:{:.4f}".format(i,running_loss/i,100*running_corrects/(32*i)))
参考文献:
Andrew Ng 《Deep Learning》
唐进民 《深度学习之PyTorch实战计算机视觉》