AlexNet 深度卷积神经网络

AlexNet 深度卷积神经网络

一、前言

在LeNet出现后的十几年里,目标识别领域的主流还是传统目标识别算法,当然也与算力限制有关。直到2012年AlexNet赢ImageNet挑战赛,才使得卷积神经网络再次引起人们的关注,也因此一发不可收拾,不断推陈出新。

2012 年,Alex Krizhevsky等人提出的AlexNet模型在ILSVRC挑战赛中取得骄人成绩,这一成果使得CNN成为了当时最先进的图像识别模型。

AlexNet模型很大程度上推动了深度学习的发展,并为今天的卷积神经网络模型奠定了基础。截止2023.07.30,论文引用次数约12万。

二、AlexNet网络的亮点、创新点:

1.卷积神经网络(Deep Convolutional Neural Networks)

2.首次使用了双GPU进行网络加速训练。

GPU进行网络加速训练,双GPU实现。

3.使用了ReLU激活函数,而不是传统的Sigmoid激活函数以及Tanh激活函数。

4.使用了LRN局部响应归一化。

Local Response Normalization,LRN

img

超参数:论文中使用的值是k=2,n=5,α=0.0001,β=0.75。

img

5.在全连接层的前两层中使用了Droupout随机失活神经元操作,以减少过拟合。

6.重叠池化Overlapping Pooling

7.数据增强

8.端到端训练

将每个像素减去训练集的像素均值。

三、AlexNet网络模型

AlexNet整体网络结构:1个输入层(input layer)、5个卷积层(C1、C2、C3、C4、C5)、2个全连接层(FC6、FC7)和1个输出层(output layer)。

AlexNet输入为RGB三通道的224 × 224 × 3大小的图像(也可填充为227 × 227 × 3 )。AlexNet 共包含5 个卷积层(包含3个池化)和 3 个全连接层。其中,每个卷积层都包含卷积核、偏置项、ReLU激活函数和局部响应归一化(LRN)模块。第1、2、5个卷积层后面都跟着一个最大池化层,后三个层为全连接层。最终输出层为softmax,将网络输出转化为概率值,用于预测图像的类别。

1.第一层卷积层(卷积、池化)

Conv1

输入数据:227×227×3,卷积核:11×11×3;步长:4;数量(也就是输出个数):96

由输入数据矩阵的尺寸W1 * H1 * D1 (输入层:227 * 227* 3) ,求输出特征图组尺寸W2 * H2 * D2 ,公式如下:
W2=\dfrac{(W1-F+2pad)}{S}+1
H2=\dfrac{(H1-F+2pad)}{S}+1

得出公式W2=(227-11+2*0)/4+1=55。

  • featuremap为:55 * 55

卷积后数据:55×55×96 (原图N×N,卷积核大小n×n,卷积步长大于1为k,输出维度是(N-n)/k+1) relu1后的数据:55×55×96

Maxpool1

Max pool1的核:3×3,步长:2

W2=(55-3+2*0)/2+1=27

  • featuremap为:27 * 27

Max pool1后的数据:27×27×96 norm1:local_size=5 (LRN(Local Response Normalization) 局部响应归一化) 最后的输出:27×27×96

AlexNet采用了Relu激活函数,取代了之前经常使用的S函数和T函数,Relu函数也很简单:

ReLU(x) = max(x,0)

AlexNet另一个创新是LRN(Local Response Normalization) 局部响应归一化,LRN模拟神经生物学上一个叫做 侧抑制(lateral inhibitio)的功能,侧抑制指的是被激活的神经元会抑制相邻的神经元。LRN局部响应归一化借鉴侧抑制的思想实现局部抑制,使得响应比较大的值相对更大,提高了模型的泛化能力。LRN只对数据相邻区域做归一化处理,不改变数据的大小和维度。LRN概念是在AlexNet模型中首次提出,在GoogLenet中也有应用,但是LRN的实际作用存在争议,如在2015年Very Deep Convolutional Networks for Large-Scale Image Recognition 论文中指出LRN基本没什么用。

AlexNet还应用了Overlapping(重叠池化),重叠池化就是池化操作在部分像素上有重合。池化核大小是n×n,步长是k,如果k=n,则是正常池化,如果 k<n, 则是重叠池化。官方文档中说明,重叠池化的运用减少了top-5和top-1错误率的0.4%和0.3%。重叠池化有避免过拟合的作用。

2.第二层卷积层(卷积、池化)

Conv2

输入数据:27×27×96,卷积核:5×5;步长:1;数量(也就是输出个数):256,

卷积后数据:27×27×256(做了Same padding(相同补白),使得卷积后图像大小不变。)

relu2后的数据:27×27×256

Max pool2

Max pool2的核:3×3,步长:2

Max pool2后的数据:13×13×256 ((27-3)/2+1=13 ) norm2:local_size=5 (LRN(Local Response Normalization) 局部响应归一化)

最后的输出:13×13×256

在AlexNet的conv2中使用了same padding,保持了卷积后图像的宽高不缩小。

3.第三层卷积层(卷积、池化)

Conv3

输入数据:13×13×256

卷积核:3×3;步长:1;数量(也就是输出个数):384 卷积后数据:13×13×384 (做了Same padding(相同补白),使得卷积后图像大小不变。) relu3后的数据:13×13×384 最后的输出:13×13×384

无Max pool3

conv3层没有Max pool层和norm层

4.第四层卷积层(卷积、池化)

Conv4

输入数据:13×13×384

卷积核:3×3;步长:1;数量(也就是输出个数):384 卷积后数据:13×13×384 (做了Same padding(相同补白),使得卷积后图像大小不变。) relu4后的数据:13×13×384 最后的输出:13×13×384

无Max pool4

conv4层也没有Max pool层和norm层

5.第五层卷积层(卷积、池化)

Conv5

输入数据:13×13×384 卷积核:3×3;步长:1;数量(也就是输出个数):256 卷积后数据:13×13×256 (做了Same padding(相同补白),使得卷积后图像大小不变。) relu5后的数据:13×13×256

无Max pool5

Max pool5的核:3×3,步长:2 Max pool2后的数据:6×6×256 ((13-3)/2+1=6 ) 最后的输出:6×6×256

conv5层有Max pool,没有norm层

6.第六层fc6阶段

输入数据:6×6×256,全连接输出:4096×1,relu6后的数据:4096×1

drop out6后数据:4096×1 最后的输出:4096×1

AlexNet在fc6全连接层引入了drop out的功能。dropout是指在深度学习网络的训练过程中,对于神经网络单元,按照一定的概率(一般是50%,这种情况下随机生成的网络结构最多)将其暂时从网络中丢弃(保留其权值),不再对前向和反向传输的数据响应。注意是暂时,对于随机梯度下降来说,由于是随机丢弃,故而相当于每一个mini-batch都在训练不同的网络,drop out可以有效防止模型过拟合,让网络泛化能力更强,同时由于减少了网络复杂度,加快了运算速度。还有一种观点认为drop out有效的原因是对样本增加来噪声,变相增加了训练样本。

7.第七层fc7阶段

输入数据:4096×1

全连接输出:4096×1 relu7后的数据:4096×1 drop out7后数据:4096×1 最后的输出:4096×1

8.第八层fc8阶段

输入数据:4096×1 全连接输出:1000 fc8输出一千种分类的概率。

四、AlexNet代码实现

1.定义AlexNet网络模型

import torch
import torch.nn as nn
​
class AlexNet(nn.Module):
    def __init__(self, config):
        super(AlexNet, self).__init__()
        self._config = config
        # 定义卷积层和池化层
        self.features = nn.Sequential(
            nn.Conv2d(3, 64, kernel_size=11, stride=4, padding=2),
            nn.ReLU(inplace=True),
            nn.MaxPool2d(kernel_size=3, stride=2),
            nn.Conv2d(64, 192, kernel_size=5, stride=1, padding=2),
            nn.ReLU(inplace=True),
            nn.MaxPool2d(kernel_size=3, stride=2),
            nn.Conv2d(192, 384, kernel_size=3, stride=1, padding=1),
            nn.ReLU(inplace=True),
            nn.Conv2d(384, 256, kernel_size=3, stride=1, padding=1),
            nn.ReLU(inplace=True),
            nn.Conv2d(256, 256, kernel_size=3, stride=1, padding=1),
            nn.ReLU(inplace=True),
            nn.MaxPool2d(kernel_size=3, stride=2),
        )
        # 自适应层,将上一层的数据转换成6x6大小
        self.avgpool = nn.AdaptiveAvgPool2d((6, 6))
        # 全连接层
        self.classifier = nn.Sequential(
            nn.Dropout(),
            nn.Linear(256 * 6 * 6, 4096),
            nn.ReLU(inplace=True),
            nn.Dropout(),
            nn.Linear(4096, 1024),
            nn.ReLU(inplace=True),
            nn.Linear(1024, self._config['num_classes']),
        )
​
    def forward(self, x):
        x = self.features(x)
        x = self.avgpool(x)
        x = torch.flatten(x, 1)
        x = self.classifier(x)
        return x

2.定义模型保存与模型加载函数

def saveModel(self):
        torch.save(self.state_dict(), self._config['model_name'])
​
def loadModel(self, map_location):
        state_dict = torch.load(self._config['model_name'], map_location=map_location)
        self.load_state_dict(state_dict, strict=False)

3.数据集预处理

这里选择采用CIFAR-10数据集。

import torchvision
from torch.utils.data import DataLoader
import torchvision.transforms as transforms
# 定义构造数据加载器的函数
def Construct_DataLoader(dataset, batchsize):
    return DataLoader(dataset=dataset, batch_size=batchsize, shuffle=True)
# 图像预处理
transform = transforms.Compose([
    transforms.Resize(96),
    transforms.ToTensor(),
    transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))
])
# 加载CIFAR-10数据集函数
def LoadCIFAR10(download=False):
    # Load CIFAR-10 dataset
    train_dataset = torchvision.datasets.CIFAR10(root='../CIFAR10', train=True, transform=transform, download=download)
    test_dataset = torchvision.datasets.CIFAR10(root='../CIFAR10', train=False, transform=transform)
    return train_dataset, test_dataset

4.模型训练函数封装

from torch.autograd import Variable
from torch.utils.data import DataLoader
​
import torch.nn as nn
​
class Trainer(object):
    # 初始化模型、配置参数、优化器和损失函数
    def __init__(self, model, config):
        self._model = model
        self._config = config
        self._optimizer = torch.optim.Adam(self._model.parameters(),\
                                           lr=config['lr'], weight_decay=config['l2_regularization'])
        self.loss_func = nn.CrossEntropyLoss()
    # 对单个小批量数据进行训练,包括前向传播、计算损失、反向传播和更新模型参数
    def _train_single_batch(self, images, labels):
        y_predict = self._model(images)
​
        loss = self.loss_func(y_predict, labels)
        # 先将梯度清零,如果不清零,那么这个梯度就和上一个mini-batch有关
        self._optimizer.zero_grad()
        # 反向传播计算梯度
        loss.backward()
        # 梯度下降等优化器 更新参数
        self._optimizer.step()
        # 将loss的值提取成python的float类型
        loss = loss.item()
​
        # 计算训练精确度
        # 这里的y_predict是一个多个分类输出,将dim指定为1,即返回每一个分类输出最大的值以及下标
        _, predicted = torch.max(y_predict.data, dim=1)
        return loss, predicted
​
    def _train_an_epoch(self, train_loader, epoch_id):
        """
        训练一个Epoch,即将训练集中的所有样本全部都过一遍
        """
        # 设置模型为训练模式,启用dropout以及batch normalization
        self._model.train()
        total = 0
        correct = 0
​
        # 从DataLoader中获取小批量的id以及数据
        for batch_id, (images, labels) in enumerate(train_loader):
            images = Variable(images)
            labels = Variable(labels)
            if self._config['use_cuda'] is True:
                images, labels = images.cuda(), labels.cuda()
​
            loss, predicted = self._train_single_batch(images, labels)
​
            # 计算训练精确度
            total += labels.size(0)
            correct += (predicted == labels.data).sum()
​
            # print('[Training Epoch: {}] Batch: {}, Loss: {}'.format(epoch_id, batch_id, loss))
        print('Training Epoch: {}, accuracy rate: {}%%'.format(epoch_id, correct / total * 100.0))
​
    def train(self, train_dataset):
        # 是否使用GPU加速
        self.use_cuda()
        for epoch in range(self._config['num_epoch']):
            print('-' * 20 + ' Epoch {} starts '.format(epoch) + '-' * 20)
            # 构造DataLoader
            data_loader = DataLoader(dataset=train_dataset, batch_size=self._config['batch_size'], shuffle=True)
            # 训练一个轮次
            self._train_an_epoch(data_loader, epoch_id=epoch)
​
    # 用于将模型和数据迁移到GPU上进行计算,如果CUDA不可用则会抛出异常
    def use_cuda(self):
        if self._config['use_cuda'] is True:
            assert torch.cuda.is_available(), 'CUDA is not available'
            torch.cuda.set_device(self._config['device_id'])
            self._model.cuda()
​
    # 保存训练好的模型
    def save(self):
        self._model.saveModel()

5.训练+测试过程

对模型进行训练与测试,最终打印输出测试准确率。

from torch.autograd import Variable
# 定义参数配置信息
alexnet_config = \
{
    'num_epoch': 20,              # 训练轮次数
    'batch_size': 500,            # 每个小批量训练的样本数量
    'lr': 1e-3,                   # 学习率
    'l2_regularization':1e-4,     # L2正则化系数
    'num_classes': 10,            # 分类的类别数目
    'device_id': 0,               # 使用的GPU设备的ID号
    'use_cuda': True,             # 是否使用CUDA加速
    'model_name': '../AlexNet.model' # 保存模型的文件名
}
​
if __name__ == "__main__":
    ####################################################################################
    # AlexNet 模型
    ####################################################################################
    train_dataset, test_dataset = LoadCIFAR10(True)
    # define AlexNet model
    alexNet = AlexNet(alexnet_config)
​
    ####################################################################################
    # 模型训练阶段
    ####################################################################################
    # # 实例化模型训练器
    trainer = Trainer(model=alexNet, config=alexnet_config)
    # # 训练
    trainer.train(train_dataset)
    # # 保存模型
    trainer.save()
​
    ####################################################################################
    # 模型测试阶段
    ####################################################################################
    alexNet.eval()
    alexNet.loadModel(map_location=torch.device('cpu'))
    if alexnet_config['use_cuda']:
        alexNet = alexNet.cuda()
​
    correct = 0
    total = 0
   # 对测试集中的每个样本进行预测,并计算出预测的精度
    for images, labels in Construct_DataLoader(test_dataset, alexnet_config['batch_size']):
        images = Variable(images)
        labels = Variable(labels)
        if alexnet_config['use_cuda']:
            images = images.cuda()
            labels = labels.cuda()
​
        y_pred = alexNet(images)
        _, predicted = torch.max(y_pred.data, 1)
        total += labels.size(0)
        temp = (predicted == labels.data).sum()
        correct += temp
    print('Accuracy of the model on the test images: %.2f%%' % (100.0 * correct / total))

6.运行结果

D:\Anaconda\envs\pytorch\python.exe E:/pythoncode/深度学习/AlexNet.py
Downloading https://www.cs.toronto.edu/~kriz/cifar-10-python.tar.gz to ../CIFAR10\cifar-10-python.tar.gz
100%|██████████| 170498071/170498071 [00:42<00:00, 4057573.25it/s]
Extracting ../CIFAR10\cifar-10-python.tar.gz to ../CIFAR10
-------------------- Epoch 0 starts --------------------
Training Epoch: 0, accuracy rate: 20.375999450683594%%
-------------------- Epoch 1 starts --------------------
Training Epoch: 1, accuracy rate: 38.96799850463867%%
-------------------- Epoch 2 starts --------------------
Training Epoch: 2, accuracy rate: 49.88399887084961%%
-------------------- Epoch 3 starts --------------------
Training Epoch: 3, accuracy rate: 55.60199737548828%%
-------------------- Epoch 4 starts --------------------
Training Epoch: 4, accuracy rate: 59.9219970703125%%
-------------------- Epoch 5 starts --------------------
Training Epoch: 5, accuracy rate: 62.67799758911133%%
-------------------- Epoch 6 starts --------------------
Training Epoch: 6, accuracy rate: 66.00999450683594%%
-------------------- Epoch 7 starts --------------------
Training Epoch: 7, accuracy rate: 68.08200073242188%%
-------------------- Epoch 8 starts --------------------
Training Epoch: 8, accuracy rate: 70.26399993896484%%
-------------------- Epoch 9 starts --------------------
Training Epoch: 9, accuracy rate: 71.89399719238281%%
-------------------- Epoch 10 starts --------------------
Training Epoch: 10, accuracy rate: 73.50599670410156%%
-------------------- Epoch 11 starts --------------------
Training Epoch: 11, accuracy rate: 74.86000061035156%%
-------------------- Epoch 12 starts --------------------
Training Epoch: 12, accuracy rate: 76.73400115966797%%
-------------------- Epoch 13 starts --------------------
Training Epoch: 13, accuracy rate: 76.947998046875%%
-------------------- Epoch 14 starts --------------------
Training Epoch: 14, accuracy rate: 78.78999328613281%%
-------------------- Epoch 15 starts --------------------
Training Epoch: 15, accuracy rate: 79.88599395751953%%
-------------------- Epoch 16 starts --------------------
Training Epoch: 16, accuracy rate: 80.88600158691406%%
-------------------- Epoch 17 starts --------------------
Training Epoch: 17, accuracy rate: 81.64599609375%%
-------------------- Epoch 18 starts --------------------
Training Epoch: 18, accuracy rate: 83.03600311279297%%
-------------------- Epoch 19 starts --------------------
Traceback (most recent call last):
  File "E:\pythoncode\深度学习\AlexNet.py", line 176, in <module>
    trainer.save()
  File "E:\pythoncode\深度学习\AlexNet.py", line 145, in save
    self._model.saveModel()
  File "D:\Anaconda\envs\pytorch\lib\site-packages\torch\nn\modules\module.py", line 1265, in __getattr__
    raise AttributeError("'{}' object has no attribute '{}'".format(
AttributeError: 'AlexNet' object has no attribute 'saveModel'
Training Epoch: 19, accuracy rate: 83.73799896240234%%
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

真的卷不栋了

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值