Alexnet简介
例如:由于数据和硬件的发展,2012年Alexnet深度卷积神经网络横空出世,其相比于之前LeNet的特点有网络模型更宽更深,也就是网络的参数量增加,此外将sigmoid激活函数改成ReLU激活函数,并且使用dropout方法,防止模型过拟合的问题。
一、查看相关库和硬件资源
使用的是python3.8
import time
import torch
from torch import nn, optim
import torchvision
import sys
sys.path.append("..")
#import d2lzh_pytorch as d2l
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
print(torch.__version__)
print(torchvision.__version__)
print(device)
结果:
1.13.1+cu117
0.14.1+cu117
cuda
二、定义AlexNet网络
1.定义
这里定义模型使用的方法是继承nn.Module,以类的形式定义模型,好处是模型结构一目了然,并且这种方法能自动初始化模型参数。
有一些注释是我自己添加的
class AlexNet(nn.Module):
def __init__(self):
super(AlexNet, self).__init__()
self.conv = nn.Sequential(
nn.Conv2d(1, 96, 11, 4), # in_channels, out_channels, kernel_size, stride, padding
nn.ReLU(),
# 最大池化计算类似于卷积,多了一个dilation扩大参数
nn.MaxPool2d(3, 2), # kernel_size, stride
# 减小卷积窗口,使用填充为2来使得输入与输出的高和宽一致,且增大输出通道数
nn.Conv2d(96, 256, 5, 1, 2),
nn.ReLU(),
nn.MaxPool2d(3, 2),
# 连续3个卷积层,且使用更小的卷积窗口。除了最后的卷积层外,进一步增大了输出通道数。
# 前两个卷积层后不使用池化层来减小输入的高和宽
nn.Conv2d(256, 384, 3, 1, 1),
nn.ReLU(),
nn.Conv2d(384, 384, 3, 1, 1),
nn.ReLU(),
nn.Conv2d(384, 256, 3, 1, 1),
nn.ReLU(),
nn.MaxPool2d(3, 2)
)
# 这里全连接层的输出个数比LeNet中的大数倍。使用丢弃层来缓解过拟合
self.fc = nn.Sequential(
nn.Linear(256*5*5, 4096),# 模型结构新方法
nn.ReLU(), # 新方法
nn.Dropout(0.5), # 新方法
nn.Linear(4096, 4096),
nn.ReLU(),
nn.Dropout(0.5),
# 输出层。由于这里使用Fashion-MNIST,所以用类别数为10,而非论文中的1000
nn.Linear(4096, 10),
)
def forward(self, img):
feature = self.conv(img)
output = self.fc(feature.view(img.shape[0], -1))
return output
2.查看
查看模型结构
net = AlexNet()
print(net)
结果展示:
这里训练的数据集是FashionMNIST,下面是我自己算的一批大小为128数据在网络中的变化,如下图所示
三、读取数据
这里使用的数据集为FashionMNIST,该数据集大小为70000,其中训练集为60000,测试集为10000,一共包括10个类别,并且是灰度图像
这里的操作包括读取训练集和测试集,再将读取到的数据转成大小为(1,224,224)的张量,目的是方便训练
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))
# 将图像转换为张量的操作添加到列表中
trans.append(torchvision.transforms.ToTensor())
# 将多个图像变换操作按顺序组合成一个管道,便于批量处理图像
transform = torchvision.transforms.Compose(trans)
# 读取FashionMNIST数据,并对读到的数据进行transform转换
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
批量大小为128
batch_size = 128
# 如出现“out of memory”的报错信息,可减小batch_size或resize
train_iter, test_iter = load_data_fashion_mnist(batch_size, resize=224)
四、模型训练
1、定义评估准确率函数
def evaluate_accuracy(data_iter, net, device=None):
if device is None and isinstance(net, torch.nn.Module):
# 如果没指定device就使用net的device
device = list(net.parameters())[0].device
acc_sum, n = 0.0, 0
with torch.no_grad():
for X, y in data_iter:
if isinstance(net, torch.nn.Module):
net.eval() # 评估模式, 这会关闭dropout
acc_sum += (net(X.to(device)).argmax(dim=1) == y.to(device)).float().sum().cpu().item()
net.train() # 改回训练模式
else: # 自定义的模型, 3.13节之后不会用到, 不考虑GPU
if('is_training' in net.__code__.co_varnames): # 如果有is_training这个参数
# 将is_training设置成False
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
2、定义训练过程
def train_ch5(net, train_iter, test_iter, batch_size, optimizer, device, num_epochs):
# 将网络模型net移动到指定的设备device上,加速计算
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()
# y_hat.argmax(dim=1)会得到元素下标,表示图片所属的类别
train_acc_sum += (y_hat.argmax(dim=1) == y).sum().cpu().item()
# n是数据量
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))
3、训练
lr, num_epochs = 0.001, 5
optimizer = torch.optim.Adam(net.parameters(), lr=lr)
train_ch5(net, train_iter, test_iter, batch_size, optimizer, device, num_epochs)
4、训练结果
总结
在对AlexNet网络学习中,学到了增加网络的深度和宽度能提升网络模型的性能,因为宽度能学习到更丰富的特征,深度能通过抽象特征不断提取知识,激活函数也能形象模型的效果。此外还学习到每个网络主要包括三部分,网络模型定义,读取数据,最后训练模型。