AlexNet卷积神经网络介绍及Pytorch实现
AlexNet简介
2012年,AlexNet横空出世。它首次证明了学习到的特征可以超越手工设计的特征。它一举打破了计算机视觉研究的现状。 AlexNet使用了8层卷积神经网络,并以很大的优势赢得了2012年ImageNet图像识别挑战赛。
AlexNet和LeNet的架构非常相似,如下图所示。 本文在这里提供的是一个稍微精简版本的AlexNet,去除了当年需要两个小型GPU同时运算的设计特点。
AlexNet和LeNet的设计理念非常相似,但也存在显著差异。
- AlexNet比相对较小的LeNet5要深得多。AlexNet由八层组成:五个卷积层、两个全连接隐藏层和一个全连接输出层。
- AlexNet使用ReLU而不是sigmoid作为其激活函数。
ReLU激活函数
一方面,ReLU激活函数的计算更简单,它不需要如sigmoid激活函数那般复杂的求幂运算。 另一方面,当使用不同的参数初始化方法时,ReLU激活函数使训练模型更加容易。 当sigmoid激活函数的输出非常接近于0或1时,这些区域的梯度几乎为0,因此反向传播无法继续更新一些模型参数。 相反,ReLU激活函数在正区间的梯度总是1。 因此,如果模型参数没有正确初始化,sigmoid函数可能在正区间内得到几乎为0的梯度,从而使模型无法得到有效的训练。
Pytorch实现
net = nn.Sequential(
nn.Conv2d(1, 96, kernel_size=11, stride=4, padding=1), nn.ReLU(),
nn.MaxPool2d(kernel_size=3, stride=2),
nn.Conv2d(96, 256, kernel_size=5, padding=2), nn.ReLU(),
nn.MaxPool2d(kernel_size=3, stride=2),
nn.Conv2d(256, 384, kernel_size=3, padding=1), nn.ReLU(),
nn.Conv2d(384, 384, kernel_size=3, padding=1), nn.ReLU(),
nn.Conv2d(384, 256, kernel_size=3, padding=1), nn.ReLU(),
nn.MaxPool2d(kernel_size=3, stride=2),
nn.Flatten(),
nn.Linear(6400, 4096), nn.ReLU(),
nn.Dropout(p=0.5),
nn.Linear(4096, 4096), nn.ReLU(),
nn.Dropout(p=0.5),
nn.Linear(4096, 10))
AlexNet识别FasionMNIST
尽管原文中AlexNet是在ImageNet上进行训练的,但本文在这里使用的是Fashion-MNIST数据集。因为即使在现代GPU上,训练ImageNet模型,同时使其收敛可能需要数小时或数天的时间。
将AlexNet直接应用于Fashion-MNIST的一个问题是,Fashion-MNIST图像的分辨率((28 \times 28)像素)低于ImageNet图像。 为了解决这个问题,本文将它们增加到(224 \times 224)(通常来讲这不是一个明智的做法,但在这里这样做是为了有效使用AlexNet架构)。
from torch.utils.data import DataLoader
import torch
import torchvision
from torch import nn
import torch.nn.functional as F
import matplotlib.pyplot as plt
n_epochs = 5
batch_size_train = 128
batch_size_test = 1000
learning_rate = 0.001
log_interval = 10
train_loader = torch.utils.data.DataLoader(
torchvision.datasets.FashionMNIST('./data/', train=True, download=True,
transform=torchvision.transforms.Compose([
torchvision.transforms.ToTensor(),
torchvision.transforms.Resize([224, 224])
])), batch_size=batch_size_train, shuffle=True)
test_loader = torch.utils.data.DataLoader(
torchvision.datasets.FashionMNIST('./data/', train=False, download=True,
transform=torchvision.transforms.Compose([
torchvision.transforms.ToTensor(),
torchvision.transforms.Resize([224, 224])
])), batch_size=batch_size_test, shuffle=True)
# AxNet
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
self.conv1 = nn.Conv2d(1, 96, kernel_size=11, stride=4, padding=1) # 输入通道待修改
self.conv2 = nn.Conv2d(96, 256, kernel_size=5, padding=2)
self.conv3 = nn.Conv2d(256, 384, kernel_size=3, padding=1)
self.conv4 = nn.Conv2d(384, 384, kernel_size=3, padding=1)
self.conv5 = nn.Conv2d(384, 256, kernel_size=3, padding=1)
self.fc1 = nn.Linear(6400, 4096) # 输入通道待修改
self.fc2 = nn.Linear(4096, 4096)
self.fc3 = nn.Linear(4096, 10)
def forward(self, x):
x = F.relu(self.conv1(x))
x = F.max_pool2d(x, 3, stride=2)
x = F.relu(self.conv2(x))
x = F.max_pool2d(x, 3, stride=2)
x = F.relu(self.conv3(x))
x = F.relu(self.conv4(x))
x = F.relu(self.conv5(x))
x = F.max_pool2d(x, 3, stride=2)
x = x.view(-1, 6400) # 待修改
x = F.relu(self.fc1(x))
x = F.dropout(x, 0.5)
x = F.relu(self.fc2(x))
x = F.dropout(x, 0.5)
x = self.fc3(x)
return F.log_softmax(x, dim=1)
network = Net().cuda() # gpu加速
optimizer = torch.optim.Adam(network.parameters(), lr=learning_rate)
train_losses = []
def train(epoch):
network.train() # 训练模式
for batch_idx, (data, target) in enumerate(train_loader):
optimizer.zero_grad() # 梯度清零
output = network(data.cuda())
loss = F.nll_loss(output, target.cuda())
loss.backward() # 反向传播
optimizer.step() # 参数更新
if batch_idx % log_interval == 0:
print('Train Epoch: {} [{}/{} ({:.0f}%)]\tLoss: {:.6f}'.format(epoch, batch_idx * len(data),
len(train_loader.dataset),
100. * batch_idx / len(train_loader),
loss.item()))
train_losses.append(loss.item())
torch.save(network.state_dict(), './model/model_FashionMnist.pth')
torch.save(optimizer.state_dict(), './model/optimizer_FashionMnist.pth')
def test():
network.eval() # 测试模式
test_loss = 0
correct = 0
with torch.no_grad():
for data, target in test_loader:
data_cuda = data.cuda()
target_cuda = target.cuda()
output = network(data_cuda)
test_loss += F.nll_loss(output, target_cuda, reduction='sum').item()
pred = output.data.max(1, keepdim=True)[1]
correct += pred.eq(target_cuda.data.view_as(pred)).sum()
test_loss /= len(test_loader.dataset)
print('\nTest set: Avg. loss: {:.4f}, Accuracy: {}/{} ({:.0f}%)\n'.format(
test_loss, correct, len(test_loader.dataset),
100. * correct / len(test_loader.dataset)))
for epoch in range(1, n_epochs + 1):
train(epoch)
test()
plt.plot(train_losses)
plt.show()
运行结果如下:
Test set: Avg. loss: 0.3991, Accuracy: 8553/10000 (86%)
Test set: Avg. loss: 0.3407, Accuracy: 8743/10000 (87%)
Test set: Avg. loss: 0.3119, Accuracy: 8853/10000 (89%)
Test set: Avg. loss: 0.2954, Accuracy: 8908/10000 (89%)
Test set: Avg. loss: 0.2878, Accuracy: 8999/10000 (90%)
损失函数图像: