【论文阅读】ImageNet Classification with Deep Convolutional Neural Networks

avatar
作者
筋斗云
阅读量:0

https://blog.csdn.net/shuweishuwei/article/details/120151499
https://blog.csdn.net/miracleoa/article/details/107632035
https://paperswithcode.com/search?q_meta=&q_type=&q=ImageNet+Classification+with+Deep++Convolutional+Neural+Networks
https://www.bilibili.com/video/BV1ih411J7Kz/

第一遍(看标题、摘要、结论和文中比较重要的图片)

1、摘要
alexNet由5个卷积层和3个全连接层构成,其中还加入了一些max pooling层,最后用softmax回归的到1000个结果。使用了dropout正则化方法来防止过拟合。在ImageNetSVRC-2010的1000个类的120万张高分辨率图像中,获得了37.5%和17.0%的top-1和top-5的错误率。这是近年神经网络兴起的除了LeNet之外的第一个开山之作

(1)TOP-1 准确率:在图像分类任务中,TOP-1 准确率表示模型在给定的测试数据集上正确分类的图像所占的比例。具体而言,对于每张测试图像,模型会给出一个类别的预测结果,而 TOP-1 准确率是指模型的预测结果中的第一个(概率最高)预测是否与实际标签相符的比例。如果模型的第一个预测与实际标签相符,则该图像被认为是正确分类的。TOP-1 准确率通常是最常用的分类性能指标。
(2)TOP-5准确率:与TOP-1准确率考虑不同,TOP-5准确率有更多的选择。在TOP-5准确率中,模型会给出前五个概率最高的预测结果,而图像被认为分类正确的前提是实际标签出现在这五个预测中的任何一个。TOP-5准确率通常用于适应适应的图像分类任务,其中图像包含许多相似的类别,使得正确分类更加困难。

2、discussion
我们的结果表明,使用纯监督学习,大型深度卷积神经网络能够在极具挑战性的数据集上获得破记录的结果。值得注意的是,如果去掉一个卷积层,我们的网络性能会下降。例如,删除任何中间层都会导致网络top-1的性能损失约2%。因此,深度对于实现我们的结果非常重要。(不能一定说明深度一定是最重要的,可能是因为参数不够好,搜参搜的不够。但这个结论是没问题,深度很重要,宽度也很重要
alexnet没有使用任何无监督的预训练(直到近年Bert、GAN的兴起,才把大家的注意又引回到无监督学习),如果我们有足够多的计算资源可以显著增加网络的大小,那么没有标号的数据也没有关系。只要网络更大,训练时间更长,结果就会更好。但跟人类的视觉还是差的很远。
我们希望在视频序列上使用非常大和深的卷积网络,其中的时序信息。

3、重要的图
在这里插入图片描述
左图表示alex对一些图片的预测结果
右边图的结论。第一列中是五个ILSVRC-2010测试图像。其余列示出了在最后隐藏层中产生与测试图像的特征向量具有最小欧几里得距离的特征向量的六个训练图像
把倒数第二层的输出拿出来得到一个长的向量,给定一张图片,看一下跟这张图片向量最接近的那些图片。找出来的图片和给定的图片很接近。深度神经网络的一个图片训练出来的最后那个向量,在语义空间中的表示特别好。相似的图片会放到一起。(深度学习可以用作简单的分离器)

在这里插入图片描述
对比其他模型在Top1和Top5上的错误率

在这里插入图片描述

在这里插入图片描述
网络架构,包含8个学习层——5个卷积层和3个完全连接层

第二遍

为了更好提升机器学习的性能,需要收集更大的数据集,训练更好的模型,使用更好的技术去避免过拟合(但正则目前看来没那么重要,最关键是神经网络的设计,使得很大的神经网络在没有很好的正则的情况下也一样能训练出来)。

用CNN来做,CNN做大不容易,做大很容易过拟合或者训练不动。

Alexnet需要把网络切开放到GPU上

ILSVRC-2010测试集公开了,因为有测试集所以可以报告测试精度。2012年开始,不公布测试集,只能去网上提交,然后看到结果。

ImageNet有很多图像的分辨率不同,但我们的系统需要统一分辨率,把每张图片变成256×256。给定一个矩形图像,我们首先重新缩放图像,使短边的长度为256,然后从得到的图像中裁剪出中心的256×256块。没有做其他任何预处理,不提取特征(当时做计算机视觉,会对图片做特征提取,抽SIFT特征或者其他)。这就是end2end(放在本文的最后了)

饱和的非线性激活函数 会比 非饱和非线性激活函数训练的要慢 (但relu其实并没有比别的激活函数快多少,目前大家用relu主要是因为它简单)

右饱和:
当x趋向于正无穷时,函数的导数趋近于0,此时称为右饱和。
饱和函数和非饱和函数:
当一个函数既满足右饱和,又满足左饱和,则称为饱和函数,否则称为非饱和函数。

在这里插入图片描述
网络切开,注意上下GPU通讯的地方。(这个模型切割(模型并行)不具有通用性,且过于复杂。但最近两三年更大的模型GPT、BERT出现,又训练不动了,得把模型切成几个部分训练。在计算机视觉中用的不多,但在自然语言处理称为一个主流的办法)
通道数可以理解成不同的模式。192个通道可以理解成识别图片中的192种不同模式。语义空间慢慢增加

数据增强

  • 从一张256×256的图片上可以扣出来2048个224×224的图片
  • 对RGB颜色通道做一些变换,使用PCA,使得图片颜色会变得不一样

dropout
以50%的概率将每个隐藏神经元的输出设置为零(每一次都可以得到一个新的模型)。dropout等价于一个L2的正则项(权重衰减)。
在前两个全连接层中使用dropout。如果没有dropout,网络会出现大量过拟合。有Dropout大约使收敛所需的迭代次数加倍。

优化算法使用随机梯度下降算法。
使用均值为0,方差为0.01的高斯分布初始化每层中的权重

对所有层使用相同的学习率,学习率初始化为0.01,并在整个训练过程中手动调整。遵循的启发式方法是,当验证错误率往下降了(不再随着学习率的提高而提高了),就将学习率除以10。(当前是使用cos这种比较平滑的函数来下降学习率)

端到端

https://blog.csdn.net/qq_52358603/article/details/127605489
https://blog.csdn.net/qq_45003504/article/details/140085594

端到端学习是一种通过训练一个单一的模型来直接映射输入到输出的方法,避免了传统方法中的手工特征提取和中间步骤。端到端的学习,就是把特征提取的任务也交给模型去做,直接输入原始数据或者经过些微预处理的数据,让模型自己进行特征提取。
在端到端学习中,整个系统被视为一个整体,通过训练数据来调整系统中的所有参数,使其能够直接将输入映射到期望的输出。这种方法的核心思想是减少人为干预,依赖数据和模型的能力来自动学习输入和输出之间的复杂关系。

端到端学习的优点

  • 减少复杂性:端到端系统不需要手工设计特征和中间步骤,简化了模型设计过程。
  • 更高的性能:由于整个系统被联合训练,它能够更好地捕捉输入和输出之间的相关性,通常能带来更高的性能。
  • 自动化特征学习:端到端模型通过数据自动学习特征,避免了手工特征提取带来的局限性。

一个程序包括很多函数,一个做法是一个个实现这个函数,然后组成一个程序,另一个做法肯定是写成一个函数,在传统编程领域,后者一般是肯定会受到鄙视。但是在深度学习领域,更宽泛的说可微分编程领域,后者有极大的意义,它把所有模块放在一起,可以当成一个整体来训练,在前向推理的时候,可以一次性得到想要的结果。这样做有什么意义?误差理论告诉我们误差传播的途径本身会导致误差的累积,多个阶段一定会导致误差累积,e2e训练能减少误差传播的途径,联合优化。通过缩减人工预处理和后续处理,尽可能使模型从原始输入到最终输出,给模型更多可以根据数据自动调节的空间,增加模型的整体契合度。

代码实现

来自https://blog.csdn.net/weixin_59798969/article/details/140351895

model.py

import torch from torch import nn from torchsummary import summary import torch.nn.functional as F     class AlexNet(nn.Module):     def __init__(self):         super(AlexNet, self).__init__()         self.relu = nn.ReLU()  # 定义激活函数         self.conv1 = nn.Conv2d(in_channels=1, out_channels=96, kernel_size=11, stride=4)  # 第一层卷积         self.pool1 = nn.MaxPool2d(kernel_size=3, stride=2)  # 第一次池化         self.conv2 = nn.Conv2d(in_channels=96, out_channels=256, kernel_size=5, padding=2)  # 第二层卷积         self.pool2 = nn.MaxPool2d(kernel_size=3, stride=2)  # 第二次池化         self.conv3 = nn.Conv2d(in_channels=256, out_channels=384, kernel_size=3, padding=1)  # 第三层卷积         self.conv4 = nn.Conv2d(in_channels=384, out_channels=384, kernel_size=3, padding=1)  # 第四层卷积         self.conv5 = nn.Conv2d(in_channels=384, out_channels=256, kernel_size=3, padding=1)  # 第五层卷积         self.pool3 = nn.MaxPool2d(kernel_size=3, stride=2)  # 第三次池化           self.flatten = nn.Flatten()  # 平展操作         self.fc1 = nn.Linear(6 * 6 * 256, 4096)  # 第一个全连接层         self.fc2 = nn.Linear(4096, 4096)  # 第二个全连接层         self.fc3 = nn.Linear(4096, 10)  # 第三个全连接层       def forward(self, x):         x = self.relu(self.conv1(x))         x = self.pool1(x)         x = self.relu(self.conv2(x))         x = self.pool2(x)         x = self.relu(self.conv3(x))         x = self.relu(self.conv4(x))         x = self.relu(self.conv5(x))         x = self.pool3(x)           x = self.flatten(x)         x = self.relu(self.fc1(x))  # 全连接层通过激活函数         x = F.dropout(x, 0.5)  # Dropout随机(临时)删掉网络中一半的隐藏神经元,防止模型过拟合         x = self.relu(self.fc2(x))         x = F.dropout(x, 0.5)         # 注意此处未使用激活函数(也可以使用),所以一个x的输出为具有10个数字的向量,在model_train内才进行最大值的选择         x = self.fc3(x)         return x     if __name__ == "__main__":     device = torch.device("cuda" if torch.cuda.is_available() else "cpu")     model = AlexNet().to(device)     print(summary(model, (1, 227, 227)))  # 打印模型信息 

model_train.py

import copy import time import torch import torch.nn as nn import pandas as pd import numpy as np import matplotlib.pyplot as plt import torch.utils.data as data from torchvision.datasets import FashionMNIST from torchvision import transforms from model import AlexNet     def train_val_data_process():     train_dataset = FashionMNIST(         root='./data',  # 指定下载文件夹         train=True,  # 只下载训练集         # 归一化处理数据集         transform=transforms.Compose([transforms.Resize(size=227),                                       transforms.ToTensor()]),  # 数据转换成张量形式,方便模型应用         download=True  # 下载数据     )     # 划分训练集和验证集     train_data, val_data = data.random_split(train_dataset,                                              [round(0.8 * len(train_dataset)), round(0.2 * len(train_dataset))])     # 训练集加载     train_dataloader = data.DataLoader(dataset=train_data,                                        batch_size=32,  # 一个批次数据的数量                                        shuffle=True,  # 数据打乱                                        num_workers=2)  # 分配的进程数目     # 验证集加载     val_dataloader = data.DataLoader(dataset=val_data,                                      batch_size=32,                                      shuffle=True,                                      num_workers=2)     return train_dataloader, val_dataloader     def train_model_process(model, train_dataloader, val_dataloader, num_epochs):     # 定义训练使用的设备,有GPU则用,没有则用CPU     device = torch.device("cuda" if torch.cuda.is_available() else "cpu")     # 使用Adam优化器进行模型参数更新,学习率为0.001     optimizer = torch.optim.Adam(model.parameters(), lr=0.001)     # 损失函数为交叉熵损失函数     criterion = nn.CrossEntropyLoss()     # 将模型放入训练设备内     model = model.to(device)     # 复制当前模型参数(w,b等),以便将最好的模型参数权重保存下来     best_model_wts = copy.deepcopy(model.state_dict())       # 初始化参数     # 最高准确度     best_acc = 0.0     # 训练集损失值列表     train_loss_all = []     # 验证集损失值列表     val_loss_all = []     # 训练集准确度列表     train_acc_all = []     # 验证集准确度列表     val_acc_all = []     # 当前时间     since = time.time()       for epoch in range(num_epochs):         print("Epoch {}/{}".format(epoch, num_epochs - 1))         print("-" * 10)           # 初始化参数         # 训练集损失值         train_loss = 0.0         # 训练集精确度         train_corrects = 0         # 验证集损失值         val_loss = 0.0         # 验证集精确度         val_corrects = 0         # 训练集样本数量         train_num = 0         # 验证集样本数量         val_num = 0           # 对每一个mini-batch训练和计算         for step, (b_x, b_y) in enumerate(train_dataloader):             # 将特征放入到训练设备中             b_x = b_x.to(device)  # batch_size*28*28*1的tensor数据             # 将标签放入到训练设备中             b_y = b_y.to(device)  # batch_size大小的向量tensor数据             # 设置模型为训练模式             model.train()               # 前向传播过程,输入为一个batch,输出为一个batch中对应的预测             output = model(b_x)  # 输出为:batch_size大小的行和10列组成的矩阵             # 查找每一行中最大值对应的行标             pre_lab = torch.argmax(output, dim=1)  # batch_size大小的向量表示属于物品的标签             # 计算每一个batch的损失函数,向量形式的交叉熵损失函数             loss = criterion(output, b_y)               # 将梯度初始化为0,防止梯度累积             optimizer.zero_grad()             # 反向传播计算             loss.backward()             # 根据网络反向传播的梯度信息来更新网络的参数,以起到降低loss函数计算值的作用             optimizer.step()             # 对损失函数进行累加,该批次的loss值乘于该批次数量得到批次总体loss值,在将其累加得到轮次总体loss值             train_loss += loss.item() * b_x.size(0)             # 如果预测正确,则准确度train_corrects加1             train_corrects += torch.sum(pre_lab == b_y.data)             # 当前用于训练的样本数量             train_num += b_x.size(0)           for step, (b_x, b_y) in enumerate(val_dataloader):             # 将特征放入到验证设备中             b_x = b_x.to(device)             # 将标签放入到验证设备中             b_y = b_y.to(device)             # 设置模型为评估模式             model.eval()             # 前向传播过程,输入为一个batch,输出为一个batch中对应的预测             output = model(b_x)             # 查找每一行中最大值对应的行标             pre_lab = torch.argmax(output, dim=1)             # 计算每一个batch的损失函数             loss = criterion(output, b_y)               # 对损失函数进行累加             val_loss += loss.item() * b_x.size(0)             # 如果预测正确,则准确度val_corrects加1             val_corrects += torch.sum(pre_lab == b_y.data)             # 当前用于验证的样本数量             val_num += b_x.size(0)           # 计算并保存每一轮次迭代的loss值和准确率         # 计算并保存训练集的loss值         train_loss_all.append(train_loss / train_num)         # 计算并保存训练集的准确率         train_acc_all.append(train_corrects.double().item() / train_num)         # 计算并保存验证集的loss值         val_loss_all.append(val_loss / val_num)         # 计算并保存验证集的准确率         val_acc_all.append(val_corrects.double().item() / val_num)           # 打印每一轮次的loss值和准确度         print("{} train loss:{:.4f} train acc: {:.4f}".format(epoch, train_loss_all[-1], train_acc_all[-1]))         print("{} val loss:{:.4f} val acc: {:.4f}".format(epoch, val_loss_all[-1], val_acc_all[-1]))           if val_acc_all[-1] > best_acc:             # 保存当前最高准确度             best_acc = val_acc_all[-1]             # 保存当前最高准确度的模型参数             best_model_wts = copy.deepcopy(model.state_dict())           # 计算训练和验证的耗时         time_use = time.time() - since         print("训练和验证耗费的时间{:.0f}m{:.0f}s".format(time_use // 60, time_use % 60))       # 选择最优参数,保存最优参数的模型     torch.save(best_model_wts, "./model_save/AlexNet_best_model.pth")       # 将产生的数据保存成表格,方便查看     train_process = pd.DataFrame(data={"epoch": range(num_epochs),                                        "train_loss_all": train_loss_all,                                        "val_loss_all": val_loss_all,                                        "train_acc_all": train_acc_all,                                        "val_acc_all": val_acc_all})       return train_process     def matplot_acc_loss(train_process):     # 显示每一次迭代后的训练集和验证集的损失函数和准确率     plt.figure(figsize=(12, 4))     plt.subplot(1, 2, 1)  # 表示一行两列的第一张图     plt.plot(train_process['epoch'], train_process.train_loss_all, "ro-", label="Train loss")     plt.plot(train_process['epoch'], train_process.val_loss_all, "bs-", label="Val loss")     plt.legend()     plt.xlabel("epoch")     plt.ylabel("Loss")       plt.subplot(1, 2, 2)  # 表示一行两列的第二张图     plt.plot(train_process['epoch'], train_process.train_acc_all, "ro-", label="Train acc")     plt.plot(train_process['epoch'], train_process.val_acc_all, "bs-", label="Val acc")     plt.xlabel("epoch")     plt.ylabel("acc")     plt.legend()     plt.show()     if __name__ == '__main__':     # 加载需要的模型     AlexNet = AlexNet()     # 加载数据集     train_data, val_data = train_val_data_process()     # 利用现有的模型进行模型的训练     train_process = train_model_process(AlexNet, train_data, val_data, num_epochs=1)  # 注:由于硬件限制,只设置训练一轮(可修改)     matplot_acc_loss(train_process) 

model.test.py

import torch import torch.utils.data as data from torchvision import transforms from torchvision.datasets import FashionMNIST from model import AlexNet     def test_data_process():     test_data = FashionMNIST(root='./data',                              train=False,  # 用测试集进行测试                              transform=transforms.Compose([transforms.Resize(size=227), transforms.ToTensor()]),                              download=True)       test_dataloader = data.DataLoader(dataset=test_data,                                       batch_size=1,  # 该批次设为1                                       shuffle=True,                                       num_workers=0)     return test_dataloader     def test_model_process(model, test_dataloader):     # 设定测试所用到的设备,有GPU用GPU没有GPU用CPU     device = "cuda" if torch.cuda.is_available() else 'cpu'       # 讲模型放入到训练设备中     model = model.to(device)       # 初始化参数     test_corrects = 0.0     test_num = 0       # 只进行前向传播计算,不计算梯度,从而节省内存,加快运行速度     with torch.no_grad():         for test_data_x, test_data_y in test_dataloader:             # 将特征放入到测试设备中             test_data_x = test_data_x.to(device)             # 将标签放入到测试设备中             test_data_y = test_data_y.to(device)             # 设置模型为评估模式             model.eval()             # 前向传播过程,输入为测试数据集,输出为对每个样本的预测值             output = model(test_data_x)             # 查找每一行中最大值对应的行标             pre_lab = torch.argmax(output, dim=1)             # 如果预测正确,则准确度test_corrects加1             test_corrects += torch.sum(pre_lab == test_data_y.data)             # 将所有的测试样本进行累加             test_num += test_data_x.size(0)       # 计算测试准确率     test_acc = test_corrects.double().item() / test_num     print("测试的准确率为:", test_acc)     if __name__ == "__main__":     # 加载模型     model = AlexNet()     model.load_state_dict(torch.load('./model_save/AlexNet_best_model.pth'))  # 调用训练好的参数权重     # 加载测试数据     test_dataloader = test_data_process()     # 加载模型测试的函数     test_model_process(model, test_dataloader) 

广告一刻

为您即时展示最新活动产品广告消息,让您随时掌握产品活动新动态!