【深度学习论文阅读】四大分类网络之AlexNet

news2024/11/26 16:33:22

ImageNet Classification with Deep Convolution Nerual Networks

论文原文:ImageNet Classification with Deep Convolutional Neural Networks

1 引言

解决的问题:

        提高效率(GPU训练),防止过拟合(dropout)

关键点:

        · 大量带标签数据——ImageNet

        · 高性能计算资源——GPU(GPU搭配了高度优化的2D卷积实现,强大到足够促进有趣大量CNN的训练)

        · 合理算法模型——CNN深度卷积网络(与具有层次大小相似的标准前馈神经网络相比,CNNs有更少的连接和参数,因此它们更容易训练,而它们理论上的最佳性能可能仅比标准前馈神经网络稍微差一点)

创新点:

        · 采用ReLu加快大型神经网络训练

        · 采用LRN

        · 采用Overlapping Pooling提升指标

        · 采用随机裁剪翻转及色彩扰动增加数据多样性

        · 采用Dropout减轻过拟合

2 数据集

ImageNet 包含各种分辨率的图像,而我们的系统要求固定的输入维度。

预处理:将图像进行下采样到固定的256×256分辨率。

1)给定一个矩形图像,首先缩放图像短边长度为256;

2)然后从结果图像中裁剪中心的256×256大小的图像块。

除了在训练集上对像素减去平均活跃度外,我们不对图像做任何其它的预处理。因此我们在原始的RGB像素值(中心化的)上训练我们的网络。

3 The Architecture 网络结构

AlexNet的网络结构如图所示。它总共包含八层可学习的层——五层卷积层和三层全连接层。

3.1 ReLU Nonlinearity ReLU非线性激活函数

对模型神经元输出的标准激活函数: f(x)=tanh(x) 或者 f(x)=(1+e^{-x})^{-1} 。

整流线性单元(ReLUs):在训练阶段使用梯度下降算法,在训练期间,这种饱和非线性函数的速度显著慢于非饱和非线性函数 f(x)=max(0,x) 。

ReLU优点:使网络训练更快;防止梯度消失(弥散),使网络具有稀疏性。

结果:用ReLUs训练的卷积神经网络比用tanh训练的网络几倍。在与传统饱和神经元模型做对比时,我们不需要用大型神经网络来做该实验。

3.2 Training on Multiple GPUs 多张GPU训练

优点:

1)当前的GPUs能够有效的跨GPU并行——精确的控制交互的数量从而达到可接收的总计算量

2)能够互相直接读取彼此的内存,还不用增加即时内存——将神经元在每张GPU上放一半

并行机制的限制:GPUs的通信只限于特定的层。例如,第三层可以得到第二层的所有神经元输出。可是第四层就只能得到位于同一GPU的第三层的输出。

3.3 Local Response Normalization 局部响应归一化

ReLUs优势:不需要输入的归一化来避免饱和。如果一些训练样本给予ReLU正向刺激输入,在该神经元就能进行有效学习

LRN能够避免泛化:定义 a_{x,y}^{i} 为核i 在位置 (x,y) 的神经元,然后使用ReLU非线性激活函数,对应的局部响应归一值 b_{x,y}^{i} 的表达式展示如下:

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 }

其中,k\alpha, n, \beta 都为常数,a为特征图中i对应像素具体值,i为通道channel

侧向抑制:lateral inhibition

3.4 Overlapping Pooling 重叠池化

CNN中的池化层只是将同一内核映射层的相邻单元进行了池化。按传统方式,相邻的池化单元是不重叠的。

重叠池化机制分别降低了错误率top-1 0.4个点以及错误率top-5 0.3个点。我们同样发现用重叠池化训练的模型能够有效缓解过拟合。

 

3.5 Overall Architecture 整体架构

        网络总共包含8层及其权重,刚开始五层是卷积层,后三层是全连接层。最后一个全连接层的输出为1000大小的softmax,包含1000种类的分布。我们的网络最大化多项逻辑回归目标,这相当于最大化基于训练样例的预测分布下正确标签的对数概率的平均值。

        第2,4,5卷积层只与同一GPU的前一层输出相关。第三层卷积层与第二层的所有输出相关。全连接层中的神经元与前一层的所有神经元相连。局部响应归一化层在第一、二层卷积层。第五层卷积层包括3.4节中所讨论的最大池化层以及局部响应归一化层。ReLU非线性激活函数用于每一层卷积层和全连接层。
在这里插入图片描述

4 Reducing Overfitting 降低过拟合

4.1 Data Augmentation 数据增强

        用于图像数据集降低过拟合的最简单以及最常用的方式是用标签保留转换(label-preserving transformations)来增强数据集。

        我们使用两种直接的数据增强方式,只需要很少的计算量就可以从原数据集中产生新的转换图像,因此这种增强后的图像不需要保存在磁盘。

第一种方式:针对位置

训练阶段:

1)图片统一缩放至256x256

2)随机位置裁剪出224x224区域 ——(256-224)的平方

3)随机进行水平翻转 ——(256-224)的平方*2

测试阶段:

1)图片统一缩放至256x256

2)裁剪出5个224x224区域(左上角、右上角、左下角、右下角、中心)

3)均进行水平翻转,共得到10张224*224图片

第二种方式:

通过PCA方法修改RGB通道的像素值,实现颜色扰动,效果有限,将top-1错误率降低了1%。

4.2 Dropout

随机失活——weight = 0,无连接无权值,以这种方式"退出"的神经元不参与前向传播,也不参与反向传播。

训练&测试两个阶段的尺度发生变化,测试时,神经元输出值需*p。

效果:每次呈输入时,神经元都会得到不一样的架构,但所有这些架构都是权值共享的。这种技术减少了神经元复杂的协同适应,因为神经元不能依赖于特定其他神经元的存在。因此,这种方式能够使得神经元从不同随机子集合中学到更鲁棒的特征

前两层全连接层中使用dropout。没有dropout的加持,我们的网络面临着严重的过拟合问题。Droput使得需要两倍的迭代周期实现收敛。

5 启发点

(1)深度与宽度可决定网络能力

(2)更强大的GPU及更多数据可进一步提高模型性能

(3)图片缩放细节,对短边先缩放

(4)ReLU不需要对输入进行标准化来防止饱和现象,即说明Siagmoid/tanh激活函数有必要对输入进行标准化

(5)卷积核学习到频率、方向和颜色特征

(6)相似图像具有“相近”的高级特征

(7)图像检索可基于高级特征,效果应该优于基于原始图像

(8)网络结构具有相关性,不可轻易移除某一层

(9)采用视频数据,可能有突破 

代码

1. 网络结构

import torch.nn as nn
import torch
class AlexNet(nn.Module): # 定义类
    def __init__(self, num_classes=1000, init_weights=False):    
        super(AlexNet, self).__init__()    # 继承
        self.features = nn.Sequential(
    # 特征提取
    # 卷积层:output_width=(input_width-kernel_size+2*padding)/stride+1
    # output_height=(input_height-kernel_size+2*padding)/stride+1  
    # 最大池化层:output_width=(input_width-pool_size)/stride+1
    # output_height=(input_height-pool_size)/stride+1 
            nn.Conv2d(3, 48, kernel_size=11, stride=4, padding=2),  
    # 第一层卷积层input[3, 224, 224], output[48, 55, 55], 输入通道数, 输出通道数
            nn.ReLU(inplace=True),    
    # 激活函数
            nn.MaxPool2d(kernel_size=3, stride=2),                  
    # 第一层最大池化层output[48, 27, 27],输出通道数不变,只改变尺寸
            nn.Conv2d(48, 128, kernel_size=5, padding=2),           
    # 第二层卷积层output[128, 27, 27]
            nn.ReLU(inplace=True),
    # 激活函数
            nn.MaxPool2d(kernel_size=3, stride=2),                  
    # 第二层最大池化层output[48, 27, 27],输出通道数不变,只改变尺寸
            nn.Conv2d(128, 192, kernel_size=3, padding=1),          
    # 第三层卷积层output[192, 13, 13]
            nn.ReLU(inplace=True),
    # 激活函数
            nn.Conv2d(192, 192, kernel_size=3, padding=1),          
    # 第四层卷积层output[192, 13, 13]
            nn.ReLU(inplace=True),
    # 激活函数
            nn.Conv2d(192, 128, kernel_size=3, padding=1),          
    # 第五层卷积层output[128, 13, 13]
            nn.ReLU(inplace=True),
    # 激活函数
            nn.MaxPool2d(kernel_size=3, stride=2),                  
    # 第五层最大池化层output[128, 6, 6],输出通道数不变,只改变尺寸
        )
        self.classifier = nn.Sequential(
    # 分类器       
            nn.Dropout(p=0.5),
    # Dropout概率
            nn.Linear(128 * 6 * 6, 2048),
    # 第六层全连接层128*6*6进行平展
            nn.ReLU(inplace=True),
    # 激活函数  
            nn.Dropout(p=0.5),
    # Dropout概率
            nn.Linear(2048, 2048),
    # 第七层全连接层
            nn.ReLU(inplace=True),
    # 激活函数 
            nn.Linear(2048, num_classes),  
    # 第八层全连接层(num_classes为类的数量)
        )
        if init_weights:
            self._initialize_weights()
    #初始化权重
 
    def forward(self, x):
    # 实现了网络的前向计算过程:输入图像经过特征提取部分后被平展成一维张量,最后通过分类器部分得到最终的分类结果
        x = self.features(x)
    # 特征提取
        x = torch.flatten(x, start_dim=1)
    #平展
        x = self.classifier(x)
    #分类器
        return x
 
    def _initialize_weights(self):
    # 用于初始化模型的权重
        for m in self.modules():
            if isinstance(m, nn.Conv2d):
    # 对卷积层使用kaiming_normal_初始化方法
                nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu')
                if m.bias is not None:
                    nn.init.constant_(m.bias, 0)
    # 如果偏置不为None,则使用nn.init.constant_函数将偏置初始化为常数0
            if isinstance(m, nn.Linear):
    # 对全连接层使用normal_初始化方法
                nn.init.normal_(m.weight, 0, 0.01)
                nn.init.constant_(m.bias, 0)
    # 用nn.init.constant_函数将偏置初始化为常数0

2. 训练过程

# 若当前有可使用的GPU设备,就是用第一块GPU来进行训练,若没有则使用CPU训练
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
print("using {} device.".format(device))


# 对数据进行预处理
data_transform = {
    # 当Key为train时
    "train": transforms.Compose([transforms.RandomResizedCrop(224),
    # 将图片随机裁剪成224×224像素大小
                                 transforms.RandomHorizontalFlip(),
    # 将图片在水平方向上随机翻转
                                 transforms.ToTensor(),
    # 转化成tensor类型
                                 transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))]),
    # 进行标准化处理

    # Key为val时
    "val": transforms.Compose([transforms.Resize((224, 224)),  
    # cannot 224, must (224,224)将图片缩放成224×224的
                               transforms.ToTensor(),
    # 将图片转化成tensor类型
                               transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])}
    # 进行标准化处理


# 读取训练集和测试集并且采用对应的图片处理方式
train_dataset = datasets.ImageFolder(root="flower_data/train",

                                     transform=data_transform["train"])
validate_dataset = datasets.ImageFolder(root="flower_data/val",

                                        transform=data_transform["val"])

# {'daisy':0, 'dandelion':1, 'roses':2, 'sunflower':3, 'tulips':4}
flower_list = train_dataset.class_to_idx
cla_dict = dict((val, key) for key, val in flower_list.items())

# write dict into json file
json_str = json.dumps(cla_dict, indent=4)
with open('class_indices.json', 'w') as json_file:
    json_file.write(json_str)

# 训练相关参数设置
batch_size = 32
train_loader = torch.utils.data.DataLoader(train_dataset,
                                           batch_size=batch_size, shuffle=True,
                                           num_workers=0)
validate_loader = torch.utils.data.DataLoader(validate_dataset,
                                              batch_size=batch_size, shuffle=False,
                                              num_workers=0)
net = AlexNet(num_classes=5, init_weights=True)
net.to(device)
# 定义一个损失函数
loss_function = nn.CrossEntropyLoss()
# 定义一个优化器_Adam
optimizer = optim.Adam(net.parameters(), lr=0.0002)

def train(dataloader,net,loss_function,optimizer):
    # 设定训练次数
    epochs = 10
    # 设置模型存储路径
    save_path = './AlexNet.pth'
    # 用来记录最优的正确率
    best_acc = 0.0
    # 一共有多少个batch(训练集数目 / batch_size),用于后面求平均损失
    train_steps = len(train_loader)
    # 开始迭代
    for epoch in range(epochs):
        # train
        # 启用Dropout方法
        net.train()
        # 记录每一次的损失,所以每次迭代都清0
        running_loss = 0.0
        # 生成一个迭代器
        train_bar = tqdm(train_loader, file=sys.stdout)
        # 通过enumerate获得迭代器的索引和值
        for step, data in enumerate(train_bar):
            # 将值赋给图像和标签
            images, labels = data
            # 每次迭代都清空梯度
            optimizer.zero_grad()
            # 将图像送入到网络训练(注意添加到设备)
            outputs = net(images.to(device))
            # 将输出值和实际值做对比,计算损失(注意labels也要添加到设备)
            loss = loss_function(outputs, labels.to(device))
            # 将损失反向传播
            loss.backward()
            # 通过优化器更新节点参数
            optimizer.step()
     
            # print statistics
            # 将每一次的损失累加,用于求平均损失
            running_loss += loss.item()
            # 输出进度
            train_bar.desc = "train epoch[{}/{}] loss:{:.3f}".format(epoch + 1,
                                                                     epochs,
                                                                     loss)
        # validate
        # 禁用Dropout方法
        net.eval()
        # 用来记录正确预测的个数
        acc = 0.0
        # 不需要计算梯度也不进行反向传播
        with torch.no_grad():
            # 生成一个迭代器
            val_bar = tqdm(validate_loader, file=sys.stdout)
            # 开始验证过程
            for val_data in val_bar:
                # 将值赋给图像和标签
                val_images, val_labels = val_data
                # 将图像送入到网络训练(注意添加到设备)
                outputs = net(val_images.to(device))
                # 在outputs的第1维找最大值;返回的第0维是最大值,第1维是最大值对应的标签;[1]将标签赋给predict_y
                predict_y = torch.max(outputs, dim=1)[1]
                # 将预测值与实际值对比,相同返回1,否则返回0,求和可得正确的个数,通过item()获得其值
                acc += torch.eq(predict_y, val_labels.to(device)).sum().item()
 
        # 求预测正确率
        val_accurate = acc / val_num
        # 打印迭代次数,平均损失,预测正确率
        print('[epoch %d] train_loss: %.3f  val_accuracy: %.3f' %
              (epoch + 1, running_loss / train_steps, val_accurate))
        # 找到正确率最高的模型参数,存到目录
        if val_accurate > best_acc:
            best_acc = val_accurate
            torch.save(net.state_dict(), save_path)

3. 预测过程

# 用GPU训练
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")

# 图片预处理:缩放,转换成tensor,归一化
data_transform = transforms.Compose([transforms.Resize((224, 224)),
                                     transforms.ToTensor(),
                                transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])

# 获取图片路径
img_path = "sunflower1.png"
img = Image.open(img_path)

# 显示图片
plt.imshow(img)

# 对图片进行预处理,载入的图片是[H, W, C],经过预处理后会自动把深度提前[C, H, W]
img = data_transform(img)
# 要求的输入有四个维度,所以给图片加一个维度变为[N, C, H, W]
img = torch.unsqueeze(img, dim=0)
 
# 获取记录类别名称的json文件
json_path = 'class_indices.json'
# 解码成我们需要的字典
with open(json_path, "r") as f:
    class_indict = json.load(f)
 
# 初始化网络
model = AlexNet(num_classes=5).to(device)
 
# 载入权重
weights_path = "AlexNet.pth"
# 利用torch.load加载权重并利用model.load_state_dict()函数把加载的权重复制到模型的权重中去
model.load_state_dict(torch.load(weights_path))
 
# 禁用Dropout方法
model.eval()

# 不需要计算梯度也不进行反向传播
with torch.no_grad():
    # predict class
    # 将图片送入网络,利用squeeze注意要将tensor转换到CPU,因为后面的numpy是CPU-only
    output = torch.squeeze(model(img.to(device))).cpu()
    # 利用softmax函数使输出满足概率分布
    predict = torch.softmax(output, dim=0)
    # 获取概率最大处所对应的索引值
    predict_cla = torch.argmax(predict).numpy()
 
# 在记录类别名称的json文件中利用索引获取类别,并且利用索引获得类别对应的概率
print_res = "class: {}   prob: {:.3}".format(class_indict[str(predict_cla)],
                                             predict[predict_cla].numpy())
plt.title(print_res)
# 打印出该图片归于每一类的概率
for i in range(len(predict)):
    print("class: {:10}   prob: {:.3}".format(class_indict[str(i)],
                                              predict[i].numpy()))
# 画图(包括图片展示和归于的最大可能类及其概率)
plt.show()

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/770125.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

VCSEL器件的常见参数有哪些?如何测试?

概述 垂直腔面发射激光器(VCSEL)是一种激光发射方向垂直于P-N结平面,而谐振腔面平行于P-N结平面的半导体激光器,它属于面发射激光器的一种。而EEL边射型激光器的光则是沿着水平方向,由芯片的边缘射出。与EEL相比, VCSEL的生产过程更具经济效益并且响应快,因此在越来越多的应用中…

cocosCreator笔记 之Android打包

版本: v3.4.0 环境: Mac 简介 cocosCreator打包APK包,需要AndroidStudio 4.1版本以上的支持,不支持Eclipse的ANT构建。大概的环境配置: 下载Java SE Development Kit 8 Downloads , 安装JAVA环境下载 Android Studi…

让 AI 真正读懂人类语言,5分钟搞懂 word embedding 技术

大家好啊,我是董董灿。 在学习自然语言处理(NLP,Natural Language Processing)时,最先遇到的一个概念,可能就是词嵌入(word embedding)了。 词嵌入,是让AI真正理解人类自然语言的技术(看完本文再回过头来看这句话&am…

【分布式系统案例课】计数服务之计数服务设计

计数服务如何实现 回顾需求 对于可扩展:对写入的数据进行分区。 对于高性能:借助缓存技术的处理,为了提高吞吐量,需要做批量batch批处理 对于高可靠:不丢数据,需要对数据进行持久化,还要借助复…

After Effects(AE)如何选择电脑硬件?

处理器(CPU) 处理器(或 CPU)是 After Effects 工作站最重要的部件之一。虽然 GPU 加速越来越受欢迎,但现在您选择的 CPU 通常会对整体系统性能产生更大的影响。然而,After Effects 使用 CPU 的方式意味着仅…

github.com网站提示无法访问此页面——亲测有效(已解决)

最近在看stepin-template的vue3版本,发现这个后台框架还是有很多不足的地方。于是想要提交一个关于菜单折叠展开的issue。 但是一打开github.com网站,就提示下图了: 于是百度搜索解决办法: 附上大神提供的解决办法:g…

Navicat分配子用户及权限管理

一、创建用户,分配权限 新建用户 输入要创建的子用户的信息 主机名 表示访问本服务的方式,%表示即可以本机访问,也可以远程访问 之后,我们给创建的用户分配权限(在该数据库的可操作空间) 为用户分配增删改…

SciencePub学术 | 计算机语音类重点SCIEEI征稿中

SciencePub学术 刊源推荐: 计算机语音类重点SCIE&EI征稿中!信息如下,录满为止: 一、期刊概况: 计算机语音类重点SCIE 【期刊简介】IF:4.0-4.5,JCR2区,中科院3区; 【出版社】世…

结构型模式 - 桥接模式

概述 现在有一个需求,需要创建不同的图形,并且每个图形都有可能会有不同的颜色。我们可以利用继承的方式来设计类的关系: 我们可以发现有很多的类,假如我们再增加一个形状或再增加一种颜色,就需要创建更多的类。 试想…

数据结构(王道)——数据结构之 树

一、数据结构-树的定义 树的概念补充: 树型逻辑结构图 结点之间的关系描述 结点、树的属性描述: 有序树、无序树: 树和森林: 树的总结: 二、树的性质 概念总结:

2023年四川大学生程序设计竞赛-K.倒转乾坤

Cuber QQ 现在手上有两个圆环,其中小圆环的直径是 d,大圆环的直径是 2d 。他将小圆环放在大圆环内, 并让小圆环紧贴大圆环内壁进行无滑动的滚动。 Cuber QQ 总是喜欢动态的美,他在小圆环上等间隔地标记了 n 个点,他想…

存储服务的演化与MySQL分库分表

文章目录 一、存储服务的演化1.单体结构2.单表单库的数据量膨胀 -> 分库分表3.单个MySQL的读写压力过大 -> MySQL索引优化4.进一步缓解MySQL读写压力 -> 读写分离5.冷热数据分离 -> 使用Redis缓存 二、MySQL分库分表1.策略2.需要注意的问题 一、存储服务的演化 1.…

「深度学习之优化算法」(十六)万有引力算法

1. 万有引力算法简介 (以下描述,均不是学术用语,仅供大家快乐的阅读) 万有引力算法(Gravitational Search Algorithm)是受物体之间的万有引力启发而提出的算法。算法提出于2008(2009)年,时间不长,不过相关的文章和应用已经相对较多,也有不少的优化改进方案。   万…

浅谈性能测试策略之银行测试

一、性能测试的四个方面 在一般的性能测试讨论中大家通常只围绕三个方面进行提问和总结:测试脚本如何编写,被测系统如何监控,性能瓶颈如何调优。大部分刚刚接触性能测试的人会纠结于脚本的编写,如何设置参数化、如何设置关联、何时…

宋浩高等数学笔记(五)定积分

本章内容和第四章的不定积分其实大差不差,然而不定积分本身与求和无关,本质为求导的逆运算;而定积分则和求导无关,实质为无限分割再求和。而牛顿莱布尼茨公式的伟大之处在于,可以用不定积分这个求导的逆运算来计算定积…

OpenCv之Canny

目录 一、自适应阈值 二、边缘检测Canny 一、自适应阈值 引入前提:在前面的部分我们使用是全局闻值,整幅图像采用同一个数作为闻值。当时这种方法并不适应与所有情况,尤其是当同一幅图像上的不同部分的具有不同亮度时。这种情况下我们需要采用自适应闻…

第七次CCF计算机软件能力认证

第一题: 折点计数 给定 n 个整数表示一个商店连续 n 天的销售量。 如果某天之前销售量在增长,而后一天销售量减少,则称这一天为折点,反过来如果之前销售量减少而后一天销售量增长,也称这一天为折点。 其他的天都不是折…

白皮书分享|数字孪生应用门槛降低!速来围观易知微新型技术成果

一、数字孪生应用门槛 (一)数字孪生的概念 百度百科中的数字孪生 (Digital Twin)1 定义,是充分利用物理模型、传感器更新、运行历史等数据,集成多学科、多物理量、多尺度、多概率的仿真过程,在虚拟空间中完成映射&…

MySQL数据库,冷备份,热备份,温备份,物理备份,逻辑备份

🧊冷备份 MySQL数据库冷备份的优点包括: 快速备份:冷备份只需要拷贝文件,因此备份速度非常快,不会影响数据库的读写操作。易于归档和恢复:冷备份可以通过简单拷贝文件进行归档和恢复,而且可以…

MP3416 是一款低静态电流、升压变换器

详情 MP3416 是一款低静态电流、升压变换器,利用峰值电流控制和变频架构来调节输出电压。MP3416 的工作输入电压低至 0.86V,提供 1.8V 至 5.5V 输出电压。 MP3416 在峰值电流控制模式下工作,提供良好的瞬态响应能力。它集成的 P 路同步整流…