【AlexNet论文精读以及代码复现以及训练结果】

2023-11-04

AlexNet论文精读以及代码复现

三遍读一篇论文的方法——李沐

  1.标题 ——>摘要——>结论——>关键的图表

  2.从标题开始读到最后(太过于细节部分可以先放一下),搞清楚重要的图标的细节

  3.第三遍要弄清楚每句话在干嘛,搞清楚细节,并且在读论文的过程中在脑中脑补整个实验过程,看看自己能不能复现

一、AlexNet论文精读

1.abstract

1)在ImageNet中取得了很好的成绩

2)用了五个卷积层,一些池化层,三个全连接层,还使用了1000路softmax激活函数

3)使用了GPU和不饱和神经元,并且发现这样使得训练更快

4)使用了Dropout来降低过拟合

摘要主要讲这篇论文做了什么(what)

2.Introduction

1)相比过去,使用了更大的数据集

2)CNN拥有更少的连接和参数,使得更容易训练

3)使用GPU训练CNN,并且ImageNet的数据集足够大,使得过拟合不会太严重

4)作者认为:<1>我们训练了目前为止最大的神经网络并且以足够大的优势赢得了比赛

  <2>编写了高度优化的2D卷积神经网络及其内部固有的其他操作的GPU实现

  <3>我们的网络包含许多不寻常的新特性,使得能够降低训练时间,并且使用了新技术来防止过拟合

  <3>我们使用了五个卷积层,三个全连接层,并且深度是非常重要的,移走一层卷积层,那么性能表现就会下降。

  5)我们(GPU)硬件受限

  Introduction大概讲的是做了什么(what)

3.The dataset

1)除了裁剪图像,没有做任何的预处理,使用原始的RGB值来训练网络

怎么做,对数据集做了什么处理(how)

4.The Architecture

1)ReLu Nonlinearity

<1>使用非饱和非线性的ReLU,比标准方式tanh,sigmoid更快

讲为什么使用ReLU,ReLU好在哪里(why,how),并且用Figure 1给出了使用ReLU达到25%的训练错误率的速度是tanh的六倍

2)Training on Multiple GPUs

<1>将模型放在两块GPU上进行训练,比使用单GPU上训练使得top-1,top-5的错误降低了1.7%,1.2%

<2>由于可以跨GPU并行,无需通过主存,因此在每个GPU上放一半的神经元

<3>在某些层的输入只能来自相同的GPU

讲了做了什么(what),好在哪里,怎么做的(how),并且给出了使用两块GPU更好的结果验证

3)Local Response Normalization(局部响应标准化)

<1>局部响应标准化有助于泛化

讲了怎样归一化并且归一化有什么好处(how)

4)Overlapping pooling(重叠池化)

<1>采用重叠池化,使得更不容易过拟合(步长要小于池化单元的大小)

讲了采用重叠池化的好处,并且如何取值(how)

5)Overall Architecture

<1>第2,4,5层的卷积层内核仅连接到位于同一GPU的上一层内核映射

<2>第三层的卷积层连接到第二层的所有内核映射

<3>全连接层连接到上一层的所有神经元

<4>响应归一化(LRN)跟在第一和第二卷积层之后

<5>ReLU应用于所有的卷积层和全连接层之后


<1>第一层卷积层使用了11 x3 x3,步长为4的96个核(过滤器)来过滤224 x224x 3的输入图像

<2>第二层使用5x 5x 48的256个内核过滤第一层的输出

<3>3,4,5层直接相连,没有任何池化层和归一层

<4>第三层有3x3x256的384个内核连接到被归一化,池化的第二层的输出

<5>第四层有3x3x192,384个卷积核,第五层3x3x192的256个卷积核,全连接层共4096个神经元

介绍了神经网络的结构

5.Reduce overfitting(两种对抗过拟合的方式)

1)Data Augmentation

<1>第一种数据增强的方式是生成图像转换和水平翻转

从256x256的图片中随机提取224x224的图片(包含翻转了的),使得训练规模增大了2048倍,测试时从四个角和中间提取5张224X224的图片以及翻转后的图片共10张

改变训练图像中RGB通道的强度,对ImageNet训练集的RGN像素值集合使用PCA主成分分析法,将添加多个找到的主成分到每张训练图片

<2>dropout

在每轮前馈,反馈传播中,每层神经炎有一定比例失活,50%几率降低过拟合

讲做了什么,怎么做,有什么效果,为什么这么做来对抗过拟合

6.Detials of learning

1)优化器为SGD,batch_size为128,momentun为0.9,weight decay为0.0005,使用均值为0,标准差为0.01的高斯分布来初始化多层权重,用常量1来初始化2,4,5层和全连接层的偏置,用常量0来初始化其它层的偏置

2)每层的学习率相同并在训练中手动调整,学习率lr初始化为0.01,验证集错误率不随当前学习率而变化时,将学习率除以10,并且在训练停止前减少三次

3)120万张图像训练了90轮

7.Result

用两张图表表明在比赛中的成果,总的来说就是AlexNet这个模型比别的模型好,并且体现了各种改变模型结构后对最后结果的影响。

1)Qualitative Evaluation

<1>GPU1上学到的是与颜色无关的特征,GPU2上学到特征与颜色相关,且这种情况与任何特定的随机权重无关

(最后一个隐藏层中生成的特征向量是具有最小的欧氏距离的特征向量,若产生最小的欧氏距离分离的特征激活向量,则高层神经网络认为它们相似,训练编码器将向量压缩为二进制编码来提高计算欧式距离的效率。)*

8.Discussion

模型的深度很重要,希望能在video上用很大,很深的CNN,因为时间序列中有很重要的信息

二、代码实现

目标:尽量将整个流程清晰简明的表达出来,并且对细节处进行标注

整个代码实现分为四个板块,分别是:

(1)构建网络模型(net.py)

(2)将数据集分类为训练集验证集和测试集(data_split.py)

(3)训练(train.py) (4)测试(test.py)

在这之前要在网上下载了猫狗数据集,每一个类别大概一万四千张图片

一、构建神经网络(net.py)

import torch
import torchvision.transforms as transforms
import torch.utils.data
import matplotlib.pyplot as plt
import os
from PIL import Image
import torch.nn.functional as f 
from torchvision.datasets import ImageFolder
import torch.optim as optim
import torch.nn as nn

DEVICE = torch.device("cuda" if torch.cuda.is_available() else "cpu")
EPOCH = 30
BATCH_SIZE = 256

class MyAlexNet(nn.Module):
    #原双GPU 此处构建模型按照单GPU构造
    def __init__(self): #num_classes指的只有两个类别
        super(MyAlexNet,self).__init__()
        self.features = nn.Sequential(
            #第一层
            nn.Conv2d(3,48,kernel_size=11,stride=4,padding=2),        #input[3,65,65],output[48,55,55]
            nn.ReLU(),
            nn.MaxPool2d(2),    
            
            #第二层
            nn.Conv2d(48,128,kernel_size=5,padding=2,stride=1),  #output[128,27,27]
            nn.ReLU(),
            nn.MaxPool2d(2), # output[128,13,13]
            
            #第三层
            nn.Conv2d(128,192,kernel_size=3,padding=1,stride=1), #output[192,13,13]
            nn.ReLU(),
            #第四层
            nn.Conv2d(192,192,kernel_size=3,padding=1,stride=1), #output[192,13,13]
            nn.ReLU(),
            #第五层
            nn.Conv2d(192,128,kernel_size=3,padding=1,stride=1), #output[128,13,13]
            nn.ReLU(),
            nn.MaxPool2d(kernel_size=3,stride=2),   #output[128,6,6]
        )
        #三层全连接层
        self.classifer = nn.Sequential(
            #在第一二层全连接层使用Dropout 
            nn.Linear(4608, 2048), #[128 * 6 * 6]
            nn.Dropout(p=0.5),
            nn.ReLU(),
            nn.Linear(2048,2048),
            nn.Dropout(p=0.5),
            nn.ReLU(),
            nn.Linear(2048,1000),
            nn.Linear(1000,2)
        )
    def forward(self,x):
        x = self.features(x)
        x = torch.flatten(x,start_dim=1)
        x = self.classifer(x)
        return x

总结: 构建神经网络按照论文中的图像对照构造就可以,在最开始令我疑惑的是最后一层卷积层到第一层全连接层的4608是如何来的,后面发现最快的方法是运行后看报错:RuntimeError: mat1 and mat2 shapes cannot be multiplied (32x4608 and 460x2048),说明我们应该把460改为4608。

二、将数据集分开为验证集训练集测试集(split_data.py)

import os
import random
from shutil import copy
def mkfile(file):
    if not os.path.exists(file):
        os.makedirs(file)
#获取指定目录下的所有文件夹名
file_path = '/Users/ropha/code学习/AlexNet/data_name'
list_class = [cla for cla in os.listdir(file_path)] #listdir返回指定文件夹的‘文件名’

#list_class中根据listdir存储了文件名(Cat,Dog),如果不存在则添加AlexNet/train/Cat or Dog
mkfile('AlexNet/train')
for cla in list_class:
    mkfile('AlexNet/train/'+ cla)

#同上,创建验证集val的文件夹及其子目录
mkfile('AlexNet/val')

for cla in list_class:
    mkfile('AlexNet/val/'+cla)

#划分数据集验证集
split_rate = 0.2

for cla in list_class:
    cla_path = file_path + '/' + cla + '/' #访问某一类别的子目录
    images = os.listdir(cla_path) #images中存储每一张图片的名称
    num = len(images)
    eval_index = random.sample(images,k=int(num * split_rate)) #images列表中随机抽取k个图像名称
    #eval_index中保存验证集val的图像名称
    for index,image in enumerate(images):
        # mac在访问图片时会访问到.DS_Store的隐藏文件导致报错,加如下语句
        if image == ".DS_Store":
            pass
        elif image in eval_index:
            image_path = cla_path + image
            new_path = 'AlexNet/val/' + cla
            copy(image_path,new_path)
        else:
            image_path = cla_path + image
            new_path = 'AlexNet/train/' + cla
            copy(image_path,new_path)
        print("\r[{}] processing [{}/{}]".format(cla,index+1,num),end=" ")
print("\tProcessing Done!")

总结:这里我遇到的最大问题就是mac在访问图片时为访问到一个名叫 .DS_Store的隐藏文件夹导致报错,只需要在在开始访问图像的for循环后加上 if image == ".DS_Store": pass,则不会报错。

三、训练(train.py)

 

import torch
from torch import nn
from net import MyAlexNet
import numpy as np
from torch.optim import lr_scheduler
import os
from torchvision import transforms
from torchvision.datasets import ImageFolder
from torch.utils.data import DataLoader
import matplotlib.pyplot as plt


ROOT_val = '/Users/ropha/code学习/AlexNet/train'
ROOT_test ='/Users/ropha/code学习/AlexNet/val'

#将图像的像素值归一化到[-1,1]之间
normalize = transforms.Normalize([0.5,0.5,0.5],[0.5,0.5,0.5])

train_transform = transforms.Compose([
    transforms.Resize((224,224)),
    transforms.RandomVerticalFlip(), #数据随机垂直旋转
    transforms.ToTensor(),
    normalize
])
val_transform = transforms.Compose([
    transforms.Resize((224,224)),
    transforms.ToTensor(),
    normalize
])
#从链接中加载数据集并接受转换后的图像
train_dataset = ImageFolder(ROOT_val,transform=train_transform)
val_dataset = ImageFolder(ROOT_test,transform=val_transform)

#对图像分组并打乱
train_dataloader = DataLoader(train_dataset,batch_size=32,shuffle=True)
val_dataloader = DataLoader(val_dataset,batch_size=32,shuffle=True)

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

model = MyAlexNet().to(device)

#定义一个损失函数,优化器
loss_fn = nn.CrossEntropyLoss()
optimizer = torch.optim.SGD(model.parameters(),lr=0.01,momentum=0.9)
#学习率每10轮变为原来的0.5
lr_scheduler = lr_scheduler.StepLR(optimizer,step_size=10,gamma=0.5)



#定义训练函数
def train(dataloader,model,loss_fn,optimizer):
    model.train()
    loss, current,n = 0.0,0.0,0
    for batch,(x,y) in enumerate(dataloader): #x(图片数据),y(真实标签)
        image, y = x.to(device), y.to(device)
        output = model(image) #output为预测值
        cur_loss = loss_fn(output,y)    
        _,pred = torch.max(output,1)
        cur_acc = torch.sum(y == pred) / output.shape[0]
        #反向传播
        optimizer.zero_grad()
        cur_loss.backward()
        optimizer.step()
        #计算每一批次的总损失值和准确率
        loss = loss + cur_loss.item()
        current = current +  cur_acc.item()
        n = n + 1
#每一批次的平均loss
    train_loss =  loss / n
    train_acc = current/ n
    print('train_loss: '+ str(train_loss))
    print('train_acc : '+ str(train_acc))
    return train_loss,train_acc




#定义验证函数
def val(dataloader,model,loss_fn):
    model.eval()
    loss, current,n = 0.0,0.0,0
    with torch.no_grad():
        for batch,(x,y) in enumerate(dataloader): #x(图片数据),y(真实标签)
            image, y = x.to(device), y.to(device)
            output = model(image) #output为预测值
            cur_loss = loss_fn(output,y)    
            _,pred = torch.max(output,1)
            cur_acc = torch.sum(y == pred) / output.shape[0]
            loss = loss + cur_loss.item()
            current = current +  cur_acc.item()
            n = n + 1
#每一批次的平均loss
    val_loss =  loss / n
    val_acc = current/ n
    print('val_loss: '+ str(val_loss))
    print('val_acc : '+ str(val_acc))
    return val_loss,val_acc

#定义画图函数
def matplot_loss(train_loss, val_loss): #判断训练集和验证集哪个效果好
    plt.plot(train_loss, label='train_loss')
    plt.plot(val_loss, label='val_loss')
    plt.legend(loc='best')
    plt.ylabel('loss')
    plt.xlabel('epoch')
    plt.title('Compare loss of train dataset and validation dataset')
    plt.show()


def matplot_acc(train_acc, val_acc): #判断训练集和验证集哪个效果好
    plt.plot(train_acc, label='train_acc')
    plt.plot(val_acc, label='val_acc')
    plt.legend(loc='best')
    plt.ylabel('Accuracy')
    plt.xlabel('epoch')
    plt.title('Compare accuracy of train dataset and validation dataset')
    plt.show()
#开始训练

loss_train = []
acc_train = []
loss_val = []
acc_val = []

epoch = 3
min_acc = 0
for t in range(epoch):
    print(f"epoch{t+1}\n ------------------")
    train_loss, train_acc = train(train_dataloader,model,loss_fn,optimizer)
    val_loss,val_acc = val(val_dataloader,model,loss_fn)
    lr_scheduler.step()

    loss_train.append(train_loss)
    acc_train.append(train_acc)
    loss_val.append(val_loss)
    acc_val.append(val_acc) #画图准备

    #保存最好的模型权重
    if val_acc > min_acc:
        folder = 'save_model'
        if not os.path.exists(folder):
            os.mkdir('save_model')
        min_acc = val_acc
        print(f"save best model, 第{t+1}轮")
        torch.save(model.state_dict(),'save_model/best_model.pth') #state_dict()中保存了权重和偏置

    #保存最后一轮的权重文件
    if t == epoch-1:
        torch.save(model.state_dict(),'save_model/last_model.pth')

matplot_loss(loss_train,loss_val)
matplot_acc(acc_train,acc_val)
print("---------Done!---------")

总结:这一部分是核心,包含归一化,分组,优化,训练,验证,对图片进行数据增强并且加载数据集,权重衰减,画图,保存最好模型等。需要注意的细节很多,需要逐行推敲。

四、测试(test.py)

import torch
from net import MyAlexNet
from torch.autograd import Variable
from torchvision import datasets,transforms
from torchvision.transforms import ToTensor
from torchvision.datasets import ImageFolder
from torch.utils.data import DataLoader
from torchvision.transforms import ToPILImage


ROOT_val = '/Users/ropha/code学习/AlexNet/train'
ROOT_test ='/Users/ropha/code学习/AlexNet/val'

#将图像的像素值归一化到[-1,1]之间
normalize = transforms.Normalize([0.5,0.5,0.5],[0.5,0.5,0.5])

# train_transform = transforms.Compose([
#     transforms.Resize((224,224)),
#     transforms.RandomVerticalFlip(), #数据随机垂直旋转
#     transforms.ToTensor(),
#     normalize
# ])
val_transform = transforms.Compose([
    transforms.Resize((224,224)),
    transforms.ToTensor(),
    normalize
])
#从链接中加载数据集并接受转换后的图像
# train_dataset = ImageFolder(ROOT_val,transform=train_transform)
val_dataset = ImageFolder(ROOT_test,transform=val_transform)

#对图像分组并打乱
# train_dataloader = DataLoader(train_dataset,batch_size=32,shuffle=True)
val_dataloader = DataLoader(val_dataset,batch_size=32,shuffle=True)

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

model = MyAlexNet().to(device)

#加载模型
model.load_state_dict(torch.load("/Users/ropha/code学习/save_model/best_model.pth"))

#获取预测结果
classes = [
    "cat",
    "dog",
]

#把张量转化为照片格式,方便可视化
show = ToPILImage()

#进入验证阶段
model.eval()
for i in range(5):
    x, y = val_dataset[i][0],val_dataset[i][1] #第i张的照片以及标签
    show(x).show()
    x = Variable(torch.unsqueeze(x,dim=0).float(),requires_grad=True).to(device)
    # x = torch.tensor(x).to(device)
    x = x.clone().detach().to(device)
    with torch.no_grad():
        pred = model(x)
        predicted,actual = classes[torch.argmax(pred[0])], classes[y]
        print(f'prediction:"{predicted}", Actual:"{actual}"')

总结:测试阶段和验证阶段差不多,但是不再需要更新梯度权重等,测试阶段只需要拿出测试集真实图片标签与预测标签进行对比并输出。

由于设备限制,只训练了三轮(光这三轮就用了一个多小时。。。)

附上结果 

 

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【AlexNet论文精读以及代码复现以及训练结果】 的相关文章

随机推荐

  • LeetCode646.最长数对链

    题目描述 646 最长数对链 力扣 LeetCode 这是一道典型的贪心算法题 我们先对原数对进行排序 排序规则是按照数对的右边界值的大小进行升序排列 初始化变量end为升序后第一个数对的右边界值 这个数无疑是最小的右边界 之后依次遍历整个
  • Qt程序的打包和发布(使用windeployqt)

    将编写完成的Qt程序进行打包 使得没有安装Qt环境的电脑也可以运行编写完成的应用 1 Release文件生成 在Qt Creator中 将构建方式改为Release 点击运行 或ctrl r快捷键运行 2 找到exe文件 复制到一个空目录中
  • java单元测试之Mock静态方法

    用例子说明 例如有下面静态方法 public final class AmountUtil public static String CustomFormatWith2Digits int amount return 1 单元测试代码 需要
  • 服务器端虚拟化安卓,安卓服务器端实例

    安卓服务器端实例 内容精选 换一换 本章节以Linux操作系统为例 指导您通过内网IP的方式连接GaussDB for Redis 实例 目标实例必须与弹性云服务器在同一个虚拟私有云和子网内才能访问 弹性云服务器必须处于目标实例所属安全组允
  • vscode和vs有什么区别?

    vscode是微软新推出的一款代码编辑器 内置了一些编译器 免费开源跨平台的工具 VS是微软的商业企业级开发环境IDE 在这之前 我们写代码的工具可以分为三个层次 最轻量级的叫做代码编辑器 例如notepad notepad subline
  • OD查看字符串

    在反汇编窗口中右击 出来一个菜单 我们在 查找 gt 所有参考文本字串 上左键点击 在text string窗口后 再右击这个窗口里面随便一处 选 search for text 输入要查找的内容 把Case sensitive 区分大小写
  • 【RoCE】拥塞控制机制(ECN, DC-QCN)

    1 网络拥塞问题 在网络交换机中 当入口流量大于出口流量的带宽时会发生网络拥塞 典型的例子是多个发送方同时向同一个目的地发送网络数据 交换机的缓存可以处理暂时的拥塞 但是当拥塞太久时 交换机的缓存就会过载 当交换机缓存过载时 下一个收到的新
  • mysql ipk 编译_OpenWrt的ipk包安装

    在 make menuconfig 进行裁减 OpenWrt 时 为了让系统更精小一点 我们会把部分功能以 模块 的方式编译 即不编入内核 只是在后期用户可以进行安装与卸载 包安装示例 如下关于Lua的配置项 其中 json4lua lua
  • 头文件库文件

    问题 不知道 Visual Studio 的头文件库文件如何配置 方法 C C gt 常规 gt 附加包含目录 添加头文件 链接器 gt 常规 gt 附加库目录 添加库目录 链接器 gt 输入 gt 附加依赖项 添加库文件
  • zip文件解压详解

    文章目录 1 起因 2 详解 3 实践 4 参考 1 起因 自己之前在linux系统解压zip文件 一直用 unzip zip 我们期望解压后的文件都是统一放到以 命名的文件夹下 但是自己有一次解压后发现所有的文件并没有放到上述文件夹下 而
  • 使用 @RequestMapping 注解,需要导入的包:spring-webmvc

    在Controller 层使用 RequestMapping注解 需要导入的包 spring webmvc 在类中需要添加 import org springframework web bind annotation RequestMapp
  • Pycharm激活方法使用的是(license server)Activate new license with: License server

    pycharm所有版本 http www jetbrains com pycharm download previous html 打开激活窗口 选择 Activate new license with License server 用li
  • 【JVM调优】JDK11-JVM基本参数调优以及日志打印

    Parm Xmx2g Xms1g Xss256k XX MaxDirectMemorySize 256m XX UseG1GC XX UseCompressedOops XX UseCompressedClassPointers XX Se
  • Python Interview Questions: A Review

    本文转载至 https www udemy com blog python interview questions Python is an elegant and versatile language used for a wide va
  • java获取当前时间戳的方法

    https www cnblogs com zhujiabin p 6168671 html utm source itdadao utm medium referral 获取当前时间戳 方法 一System currentTimeMill
  • Qt中的位置和尺寸

    在QT中我们常见的 点 线 尺寸 矩形 都被进行了封装 下边依次为大家介绍相关的类 目录 QPoint QLine QSize QRect QPoint QPoint是C 编程语言中Qt框架中的一个类 它表示2D坐标系中的一个点 它用于定义
  • python如何判断一个数是整数,浮点数,复数还是字符?

    遇到判断字符类型 上网搜索了一下 整理下来 1 判断字符串 python字符串常用的判断函数很多 有如下8种 1 str isalnum 所有字符都是数字或者字母 2 str isdecimal 所有字符都是十进制数字 3 str isdi
  • 字符串格式化:% 运算符

    1 课题导入 任务 用字符串拼接法原样输出 圆周率是3 1415926 声明变量pi 用于存储圆周率 pi 3 1415926 type函数查看变量pi 的数据类型 print type pi 用字符串拼接的方法输出 str函数将浮点数类型
  • 一个简单音乐播放器的java实现(一)

    写在前面 这几天正在读head first系列的书籍 现在正好读的是java 这本书讲的深入浅出 环环相扣 非常精彩 不妨安利给大家 顺便把我学习过程中的一些心得体会已经实例分享出来 1 一个最简单音乐播放器的需求 我们需要四样东西 1 播
  • 【AlexNet论文精读以及代码复现以及训练结果】

    AlexNet论文精读以及代码复现 三遍读一篇论文的方法 李沐 1 标题 gt 摘要 gt 结论 gt 关键的图表 2 从标题开始读到最后 太过于细节部分可以先放一下 搞清楚重要的图标的细节 3 第三遍要弄清楚每句话在干嘛 搞清楚细节 并且