资讯详情

AlexNet论文解读以Pytorch实现

AlexNet论文解读以Pytorch实现

  • 一、AlexNet背景
    • 1、ILSVRC
    • 2、GPU
  • 二、AlexNet研究成果及意义
    • 1、研究成果
    • 2、研究意义
  • 三、AlexNet网络结构
    • 1.网络结构层的具体操作
    • 2、具体操作
      • (1)激活函数
      • (2)LRN(目前几乎不用)
      • (3)pooling(池化)
  • 四、AlexNet训练技巧
    • 1、Data Augmentation
    • 2、Dropout
  • 五、分析实验结果
    • 1、实验结果
    • 2.卷积核可视化
    • 3.特征的相似性
  • 六、结论
  • 七、代码实现(含具体解释)
    • 1.采用预训练模型
      • (1)数据集及处理
      • (2)dataset
      • (3)dataset封装
      • (4)建立预训练模型
      • (5)损失函数,优化器,学习率
      • (6)训练和验证
      • (7)预训练模型识别
      • (8)pyqt5视觉界面识别(预训练模型)
  • 八、卷积核、特征图可视化
日志: 2022.6.19:补充代码部分 作者是:加拿大多伦多大学 的 Alex Krizhevsky(第一作者) 以下是论文 《ImageNet Classification with Deep ConvolutionalNeural Networks》

一、AlexNet背景

1、ILSVRC

在讨论AlexNet在此之前,首先要知道一场著名的比赛,即ILSVRC(ImageNet Large Scale Visual Recognition Challenge)大规模图像识别挑战赛是李飞飞等人于2010年创办的图像识别挑战赛,自2010年以来已举办8届。它也是近年来机器视觉领域最受欢迎、最权威的学术竞赛之一。 比赛项目包括:图像分类:(lassification)、目标定位(Object localization)、目标检测(Object detection)、视频目标检测(Object detection from video)、场景分类(Scene classification)、场景解析(Scene parsing)。 大量经典模型在比赛中脱颖而出: alexnet, vgg, googlenet, resnet, densenet等 实际上ILSVRC数据集不等于ImageNet数据集是从中挑选出来的。大规模数据集是AlexNet成功奠定了基础。 在这里插入图片描述 下面是ILSVRC的网址链接 ILSVRC网址

2、GPU

Alexnet除了大规模数据集的成功外,还得益于强大的计算资源GPU

二、AlexNet研究成果及意义

1、研究成果

AlexNet在ILSVRC-2012年超过第二名.9个百分点夺冠。下表是错误率。

Model Top-1(val) Top-5(val) Top-5(test)
SIFT FVs[7] - - 26.2% ILSVRC 2012年分类任务第二名的结果
1 CNN 40.7% 18.2% - 训练一个AlexNet的结果
5 CNNs 38.1% 16.4% 16.4% 训练五个AlexNet取平均结果
1 CNN* 39.0% 16.6% - 在最后一个池化层后,增加第六个卷积层ImageNet 2011 (秋) 数据集预训练
7 CNNs* 36.7% 15.4% 15.3% 两个预训练微调,5CNNs取平均值

2、研究意义

2012年前的分类任务大多采用机器学习方法,即特征提取->特征筛选->输入分类机。 2012年以后采用深度学习方法,将特色工程与分类相结合。 因此,卷积神经网络开启了统治计算机视觉的序幕,加速了计算机视觉应用的实施。

三、AlexNet网络结构

1.网络结构层的具体操作

下面是论文中作者的网络结构图 首先要知道AlexNet网络的一些基础: 由5层卷积层和3层全连接层网络组成。 2.由于计算能力的限制,作者分为两部分GPU训练(图中上下两个代表)GPU,第一层和第三层与前面的所有信息相连,但现在GPU算力用一块GPU也可以) 3、LRN(后介绍)出现在第一卷和第二卷积层之后,但后来有论文提出LRN效果不明显,pytorch中将其去掉。 4、pooling:1、2、5卷积层出现后 5、ReLU:采用所有层 6、Dropout:使用前两层全连接层 : conv1->ReLU->Pool->LRN 作者在论文中输入图像的大小是224*根据公式,224声明中存在一些问题padding计算(224-11)/4 1=54与论文原图中的55不一致,如果不加入padding输入应为227 * 227,pytorch中导模型AlexNet网络将padding设置为了2. : conv2->ReLU->Pool->LRN : conv3->ReLU 这里实现了特征层的交互 : conv4->ReLU : conv5->ReLU->Pool : Dropout->Linear->ReLu : Dropout->Linear->ReLu : Linear 下图是每层的尺寸计算和参数的大小。在计算前详细解释了一些基本文章,这里不再过分强调。

2、具体操作

(1)激活函数

本文提到了激活函数 对卷积神经网络的深入理解——基础篇(卷积、激活、池化、差反传) 论文中作者提到了两种饱和的激活函数sigmoid激活函数和tanh激活函数

认为饱和的激活函数梯度下降会比较慢,故采用了非饱和的激活函数,以加快梯度下降的速度。 上图是作者用四层网络做的一个实验,在错误率下降到0.25时采用非饱和激活函数ReLU比饱和激活函数tanh快了六倍。

(2)LRN(目前几乎不采用)

全称Local Response Normalization,局部响应标准化,其受启发于侧抑制(lateral inhibition):细胞分化变为不同时,它会对周围细胞产生抑制信号,阻止他们向相同方向分化,最终表现为细胞命运的不同 论文中采用LRN在top-1和top-5的错误率上分别下降了1.4%和1.2%。 公式如下: b x , y i = a x , y i / ( k + α ∑ j = m a x ( 0 , i − n / 2 ) m i n ( N − 1 , i + n / 2 ) ( a x , y i ) 2 ) β b_{x,y}^{i}=a_{x,y}^{i}/(k+\alpha \sum_{j=max(0,i-n/2)}^{min(N-1,i+n/2)}(a_{x,y}^{i})^2)^{\beta } bx,yi​=ax,yi​/(k+αj=max(0,i−n/2)∑min(N−1,i+n/2)​(ax,yi​)2)β 看着比较复杂,不过原理相对简单, 先来看下图, 下图中假设当前位置的值为 a x , y i a_{x,y}^{i} ax,yi​,那么根据LRN的原理,它必然受 i i i这个通道周围通道值得影响,假设 a x , y i a_{x,y}^{i} ax,yi​受周围 n / 2 n/2 n/2个通道的值的影响,那么显然 a x , y i a_{x,y}^{i} ax,yi​周围的值越大经过LRN后得到的 b x , y i b_{x,y}^{i} bx,yi​的值就应该越小 ( k + α ∑ j = m a x ( 0 , i − n / 2 ) m i n ( N − 1 , i + n / 2 ) ( a x , y i ) 2 ) β 就 代 表 了 周 围 的 值 , (k+\alpha \sum_{j=max(0,i-n/2)}^{min(N-1,i+n/2)}(a_{x,y}^{i})^2)^{\beta }就代表了周围的值, (k+αj=max(0,i−n/2)∑min(N−1,i+n/2)​(ax,yi​)2)β就代表了周围的值, a x , y i 表 示 原 先 的 值 , a_{x,y}^{i}表示原先的值, ax,yi​表示原先的值, b x , y i 表 示 经 过 抑 制 的 值 b_{x,y}^{i}表示经过抑制的值 bx,yi​表示经过抑制的值 论文中采用LRN在top-1和top-5的错误率上分别下降了1.4%和1.2%。

(3)pooling(池化)

Overlapping Pooling(带重叠的池化),其实就是设置了步长的一个池化,之前的文章中我也具体介绍过,这里不再细讲。作者所采用的是核大小为3, 步长为2的一个池化,在top-1和 top-5的错误率上分别下降了0.4%和0.3%。

四、AlexNet训练技巧

1、Data Augmentation

论文中采用如下技巧 (1)针对位置 训练阶段: 1、图片统一缩放至256256 2、随机位置裁剪出224224区域(这里改为227*227) 3、随机进行水平翻转

测试阶段: 1、图片统一缩放至256256 2、裁剪出5个224224区域(这里改为227227) 3、均进行水平翻转,共得到10张224224图片(这里改为227*227) (2)针对颜色 通过PCA方法修改RGB通道的像素值,实现颜色扰动,效果有限,仅在top-1提升1个点(top-1 acc约62.5%),PCA方法比较麻烦,现在使用也比较少。

2、Dropout

这里我在神经网络基础部分也详细介绍过,见下面链接卷积神经网络的深入理解-正则化方法篇 不再细说。随机的停止某些神经元的计算,训练的时候采用,测试的时候停止。

五、实验结果即分析

1、实验结果

Model Top-1(val) Top-5(val) Top-5(test)
SIFT+FVs[7] - - 26.2% ILSVRC 2012分类任务第二名的结果
1 CNN 40.7% 18.2% - 训练一个AlexNet的结果
5 CNNs 38.1% 16.4% 16.4% 训练五个AlexNet取平均值结果
1 CNN* 39.0% 16.6% - 最后一个池化层后额外添加第六个卷积层并使用ImageNet 2011 (秋) 数据集预训练
7 CNNs* 36.7% 15.4% 15.3% 两个预训练微调,与5CNNs取平均值

集成思想

2、卷积核可视化

如图所示这个是在输入图像224 * 224(这里改为227 * 227)也就是第一层进行11*11的一个卷积核的可视化,作者发现,训练过程中会出现这种现象,GPU1核学习到了包含频率和方向的特征,而GPU2核学习到了包含颜色的特征。

3、特征的相似性

相似图片第二个全连接层输出的特征向量的欧式距离相近。由此得到启发启发:可以用AlexNet提取高级特征进行图像检索、图像聚类、图像编码,这里其实已经有很多网络采用了这种思想,比如行人重识别。

六、结论

网络层之间是有一定相关性的,移除或是加入一层都会影响网络的性能。作者在论文中提到移除任何中间层都会引起网络损失大约2%的top-1性能。

七、代码实现(含具体讲解)

代码部分主要采用两种实现方式,第一种是调用pytorch中的alexnet网络并加载预训练模型进行实现(不要小看这种方式)。第二种自构建alexnet网络训练模型。

1、采用预训练模型进行

这部分花了我很长时间,有点繁琐,之后应该还会更改完善,那么我们开始吧。

(1)数据集以及处理

给出网盘地址: 链接:https://pan.baidu.com/s/1Pe5QNW-nltrf8qVboi1Yow?pwd=ld61 提取码:ld61

数据集采用猫狗数据集,直接从一个train文件中分割出训练集和验证集,这是train文件中的图片以及图片命名, 如下代码将数据集进行分割并存放于txt文件中,用于之后进行读取训练

import os
import random

# 用于产生txt文件,相当于dataset操作

def Data_division(data_path,save_txt_train_path,save_txt_eval_path):
    lists = os.listdir(data_path)
    for list in lists:
        if list in ['train']:
            datalist = []
            datadir = os.path.join(os.path.abspath(data_path),list)
            data = os.listdir(datadir)
            for n in data:
                if n.startswith('cat'):
                    datalist.append(os.path.join(datadir,n) + "\t" + str(0) + "\n")
                elif n.startswith('dog'):
                    datalist.append(os.path.join(datadir,n) + "\t" + str(1) + "\n")
            random.shuffle(datalist)
            print("总数据集的个数:", len(datalist))
            # 划分训练集验证集
            train_list = []
            eval_list = []
            for i in range(int(len(datalist) * 0.9)):
                train_list.append(datalist[i])
            for i in range(int(len(datalist) * 0.9), len(datalist)):
                eval_list.append(datalist[i])
            print("训练数据个数:", len(train_list))
            print("验证数据个数:", len(eval_list))

            # 将数据路径存放于txt文件里
            with open(save_txt_train_path, 'w', encoding='UTF-8') as f:
                for train_img in train_list:
                    f.write(str(train_img))
                f.close()
            with open(save_txt_eval_path, 'w', encoding='UTF-8') as f:
                for eval_img in eval_list:
                    f.write(eval_img)
                f.close()

# 说明:这里将train中的猫狗图片分为训练集和测试集
if __name__ == '__main__':
    Data_division('../data','../data/train.txt','../data/eval.txt')

txt文件保存格式如下:前者路径,后者类别

(2)dataset

这一步我们要将txt文件中的数据读取出来并进行数据增强,返回为分离的数据和标签。这里详细说明一下transform这一部分。将上面训练,测试的数据增广操作再写一遍

训练阶段 测试阶段
图片统一缩放至256*256 图片统一缩放至256*256
随机位置裁剪出224x224区域 裁剪出5个224x224区域,进行水平翻转得到10张224*224图片
随机进行水平翻转

这里我为什么改为224x224,而非上面的227x227呢,这是由于pytorch内置的网络更改了部分网络层中的padding,使得网络输入与论文中的输入一致。

训练阶段 测试阶段
transforms.Resize((256,256)) transforms.Resize((256, 256))
transforms.CenterCrop(256)
transforms.RandomCrop(224) transforms.TenCrop(224, vertical_flip=False)
transforms.RandomHorizontalFlip(p=0.5)
transforms.ToTensor() transforms.ToTensor()
transforms.Normalize() transforms.Normalize()
torch.stack()

我们主要来看一下测试阶段的TenCrop操作,它的作用见我的这篇博客神经网络数据增强transforms的相关操作(持续更新),主要作用是裁剪图片中心和四个角并做翻转得到10张图片,操作后会出现这样一个问题,我们希望对图片处理后得到的是一张(b,c,h,w),但是经过TenCrop处理后得到的却是这样一个list[(b,c,h,w),(b,c,h,w)…(b,c,h,w)],因此需要用到torch.stack()这个操作扩展一维变成(b,10,c,h,w),

如下为代码部分:

# -*- coding: utf-8 -*-

import torch
from PIL import Image
from torch.utils.data import Dataset
from torchvision import transforms

class CatDogDataset(Dataset):
    def __init__(self,txt_path,img_size,is_train=True):
        self.imgs_info = self.Separate_data(txt_path)
        self.img_size = img_size
        self.is_train = is_train
        self.train_transform = transforms.Compose([
        transforms.Resize((256)),
        transforms.CenterCrop(256),
        transforms.RandomCrop(img_size),
        transforms.RandomHorizontalFlip(p=0.5),
        transforms.ToTensor(),
        transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]),
    ])
        self.val_transform = transforms.Compose([
        transforms.Resize((256, 256)),
        transforms.TenCrop(224, vertical_flip=False),
        # transforms.Lambda()函数自行定义transform操作
        transforms.Lambda(lambda crops: torch.stack([transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])(transforms.ToTensor()(crop)) for crop in crops])),
    ])

    # 分离数据和标签[路径,标签]
    def Separate_data(self,txt_path):
        with open(txt_path,'r',encoding='utf-8') as f:
            imgs_info = f.readlines()
            imgs_info = list(map(lambda x: x.strip().split('\t'), imgs_info))
        return imgs_info


    def __getitem__(self, index):
        img_path, label = self.imgs_info[index]
        img = Image.open(img_path).convert('RGB')
        if self.is_train:
            image_datasets = self.train_transform(img)
        else:
            image_datasets = self.val_transform(img)
        label = int(label)
        return image_datasets,label

    def __len__(self):
        return len(self.imgs_info)

类中函数不太理解的看我这篇博客 卷积神经网络构建的python基础-详细理解(Pytorch) 最终的返回值如下:

train val
(图片(b,c,h,w),标签) (图片(b,10,c,h,w),标签)

图片维数不一样?先接着往下看,后面告知怎么处理。

(3)dataset封装

train_loader = DataLoader(dataset=train_datasets, batch_size=batch_size, shuffle=True, num_workers=0)
val_loader = DataLoader(dataset=val_datasets, batch_size = batch_size, num_workers=0)

(4)预训练模型搭建

数据准备好后我们就要开始搭建网络了,这一部分由于是调用pytorch内置的网络,因此比较简单,分为以下几步 (1)下载好AlexNet预训练模型, (2)搭建网络,更改网络层(pytoch中alexnet网络所分类别是1000类,我们这里只分2类)

如下搭建网络代码:

import torchvision.models as models
alexnet_model = models.alexnet()  # 网络
alexnet_model.load_state_dict(torch.load(path_state_dict)) #导入参数

下面是更改网络层代码

num_ftrs = alexnet_model.classifier._modules["6"].in_features
alexnet_model.classifier._modules["6"] = nn.Linear(num_ftrs, n_class)

实际上是将最后一层的全连接层输出只能1000改为2,即:

Linear(in_features=4096, out_features=1000, bias=True)
变为:
Linear(in_features=4096, out_features=2, bias=True)

判断GPU是否可用

    if torch.cuda.is_available():
        device = 'cuda'
    else:
        device = 'cpu'
    alexnet_model.to(device)

(5)损失函数、优化器、学习率

这一部分重点主要是优化器,我们需要阻碍一下除全连接层外的所有参数更新。见如下代码,注意代码中的参数并没有完全冻结,想要完全冻结的可以将学习率置为0.

# 损失函数、优化器、学习率
    criterion = nn.CrossEntropyLoss(
        标签: lrn系列热继电器

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

锐单商城 - 一站式电子元器件采购平台