深度学习-一篇入门

【模型】一篇入门之-AlexNet卷积神经网络

作者 : 老饼 发表日期 : 2023-07-28 10:52:45 更新日期 : 2024-11-01 18:47:21
本站原创文章,转载请说明来自《老饼讲解-深度学习》www.bbbdata.com



AlexNet是第一个在ImageNet挑战赛上取得显著成功的深度卷积神经网络,它是学习CNN的入门模型

本文介绍AlexNet卷积神经网络在原论文中的模型结构,包括每一层的具体运算细节,并展示AlexNet的代码实现

通过本文,可以快速了解AlexNet是什么,它的详细结构是怎么样,以及如何实现一个AlexNet用于图片类别识别






    01. 什么是AlexNet神经网络    




本节简单介绍AlexNet和AlexNet的结构





     什么是AlexNet神经网络     


AlexNet是一个用于图像类别识别的CNN深度学习模型,它在2012年ImageNet竞赛中由于效果卓越,一战成名
  AlexNet原文为《ImageNet Classification with Deep Convolutional Neural Networks》  
由于AlexNet是2012年的作品,当时计算力低下,所以AlexNet采用的是双核计算的结构
 AlexNet论文中AlexNet的结构图如下:
  AlexNet论文中AlexNet的结构图 
 将上述结构图简化后,示图如下:
 简化的AlexNet结构图
AlexNet的输入是224×224×3的图片,输出是1×1000的概率向量P, 第i个输出代表图片属于类别i的概率  
可以看到,在原文中AlexNet由双核构成,每个核都有8层,前五层是卷积层,后三层是全连接层
双核之间除了个别层会交换数据,大部分时候是独立的
最后,输出层汇聚双核的数据,训练一个全连接层并进行softmax后作为输出








   02. AlexNet神经网络结构的详述   




本节详细解释AlexNet每一层的详细结构与运算过程



       


          AlexNet结构详述         


AlexNet输入:224×224×3的图片   
AlexNet输出:1000×1的概率向量P
一、AlexNet-C1卷积层                                                                                                                                  
 AlexNet-C1卷积层的运算过程
  C1卷积层的输入:224×224×3的图像                                                                                                  
         C1卷积层的运算:1. 卷积:使用2组48个11×11×3,步幅为4,填充为2,的卷积核进行卷积运算                         
                                               卷积后的结果为2组[(224+4-11)/4+1]×[(224+4-11)/4+1]×48=55×55×48的输出               
                       2. ReLu:将卷积结果进行ReLu                                                                         
          3. 归一化:将ReLu后的结果进行局部归一化                                         
  4. 池化:使用3×3,步幅为2的MaxPool进行池化                            
                           池化后的结果为2组[(55-3)/2+1]×[(55-3)/2+1]×48=27×27×48的输出                
       C1卷积层的输出:2组27×27×48的特征图                                                                                                 

二、AlexNet-C2卷积层                                                                                                                                  
 AlexNet-C2卷积层的运算过程
C2卷积层的输入:
C2卷积层的输入为C1卷积层的输出,即2组27×27×48的特征图                              
                 C2卷积层的运算:1. 卷积:对每组各自使用128个5×5×48的卷积核,进行步幅为1,填充为2的卷积                       
                                                  卷积后的结果为2组[(27+4-5)/1+1]×[(27+4-5)/1+1]×128=27×27×128的输出               
         
 2. ReLu:将卷积结果进行ReLu                                                                   
        3. 归一化:将ReLu后的结果进行局部归一化                                       
   4. 池化:使用3×3,步幅为2的MaxPool进行池化                             
                池化后得到的结果为2组[(27-3)/2+1]×[(27-3)/2+1]×128=13×13×128的输出
 C2卷积层的输出:2组13×13×128的特征图                                                                                        

三、AlexNet-C3卷积层                                                                                                                                  
 AlexNet-C3卷积层的运算过程
             C3卷积层的输入:
C3卷积层的输入为C2卷积层两组输出的合并结果,即13×13×128*2=13×13×256的特征图 
    
C3卷积层的运算:1. 卷积:对每组各自使用192个3×3×256的卷积核,进行步幅为1,填充为1的卷积             
                                                  卷积后的结果为2组[(13+2-3)/1+1]×[(13+2-3)/1+1]×192=13×13×192的输出              
       2. ReLu:将卷积结果进行ReLu                                                         
  C3卷积层的输出:2组13×13×192的特征图                                                                                        

四、AlexNet-C4卷积层                                                                                                                                  
 
AlexNet-C4卷积层的运算过程
C4卷积层的输入:
C4卷积层的输入为C3卷积层的两组输出, 即2组13×13×192的特征图                   
  C4卷积层的运算:1. 卷积:对每组各自使用192个3×3×192的卷积核,进行步幅为1,填充为1的卷积        
                         卷积后的结果为2组[(13+2-3)/1+1]×[(13+2-3)/1+1]×192=13×13×192的输出
        2. ReLu:将卷积结果进行ReLu                                                         
 C4卷积层的输出:2组13×13×192的特征图                                                                                       
           C4卷积层的总输出个数为13×13×192=64896                            

五、AlexNet-C5卷积层                                                                                                                                  
 
AlexNet-C5卷积层的运算过程
C5卷积层的输入:
C5卷积层的输入为C4卷积层的两组输出,即2组13×13×192的特征图                     
 
C5卷积层的运算:1. 卷积:对每组各自使用128个3×3×192的卷积核,进行步幅为1,填充为1的卷积计算  
                          卷积后的结果为2组[(13+2-3)/1+1]×[(13+2-3)/1+1]×128=13×13×128的输出 
 2. ReLu:将卷积结果进行ReLu                                                  
3. 池化:使用3×3,步幅为2的MaxPool进行池化                        
     池化后的Size为[(13-3)/2+1]×[(13-3)/2+1]×128=6×6×128 
 C5卷积层的输出:2组6×6×128的输出                                                                                               
 C5池化层的总输出个数为6×6×128×2=9216                   

六、AlexNet-F6全连接层                                                                                                                               
 
AlexNet-F6全连接层的运算过程
       F6全连接层的输入:
F6的输入是C5池化层的两组输出合并再展平的结果,即6*6*128*2×1=9216×1的向量
 F6全连接层的运算:使用两组输出Size为2048×1的全连接运算,激活函数为ReLu                                
 具体运算如下:
 
 这里的上标代表层数,下表代表组号
其中,W:2048×9216的权重矩阵             
B:2048×1的阈值列向量    
y:2048×1的列向量           
 F6全连接层的输出:F6层输出2组2048×1的向量                                                                                 
  F6层的总输出个数为2048*1*2=4096                        

七、AlexNet-F7全连接层                                                                                                                               
 AlexNet-F7全连接层的运算过程
 F7全连接层的输入:
F7的输入是F6的2组输出合并的结果,即2048*1*2×1=4096×1的向量                  
 
F7全连接层的运算:使用两组输出Size为2048×1的全连接运算,激活函数为ReLu                                 
  具体运算如下:
 
 这里的上标代表层数,下表代表组数
其中,W:2048×4096的权重矩阵             
B:2048×1的阈值列向量    
y:2048×1的列向量           
 F7全连接层的输出:F7层输出2组2048×1的向量                                                                                 
   F7层的总输出个数为2048*1*2=4096                         

八、AlexNet-F8全连接层                                                                                                                               
 AlexNet-F8全连接层的运算过程
F8全连接层的输入:
F8的输入是F7的2组输出合并的结果,即2048*1*2×1=4096×1的向量                 
              F8全连接层的运算:1. 全连接运算:使用两组输出Size为1000×1的全连接运算,激活函数为ReLu                       
  具体运算如下
  
 
其中,W:1000×4096的权重矩阵          
B :1000×1的阈值列向量 
 y :1000×1的列向量        
                         2. softmax运算:对ReLu后的结果进行SoftMax,得到属于每个类别的概率 
 F8全连接层的输出:得到1000×1的输出向量                                                                                       
  F8层的总输出个数为1000                                 
备注:局部响应归一化是AlexNet自己提出的一种归一化方法,它应用于AlexNet的C1、C2层
局部响应归一化后来被他人使用时效果并不好,一般被其它归一化方法所替代,局部响应归一化可参考文章《》






      AlexNet的配置列表     


AlexNet原文中的双核模式的配置列表如下:
 AlexNet的配置列表 
从配置列表可以较简洁地看到AlexNet的整个运算过程








   03. AlexNe卷积神经网络-单核结构   





本节介绍AlexNet神经网络的单核结构





      关于AlexNe卷积神经网络的单核结构    


由于AlexNet是2012年的作品,当时计算力低下,所以AlexNet采用的是双核计算的结构
在学习时我们可以原汁原汁的学习AlexNet这种最初的设计,但实际使用中,就需要略加改善,
因为现在的计算机算力已经不再需要分核计算,一般会去掉双核结构,以方便实现
 如此一来,单核的AlexNet整体计算流程如下:
 单核的AlexNet整体计算流程   
 将AlexNet的原配置列表改为单核,则得到AlexNet的单核配置如下:
单核的AlexNet配置列表







   04. AlexNet卷积神经网络-代码实现   




本节展示如何实现一个AlexNet用于图片类别识别





      pytorch中的AlexNet模型      


pytorch中提供了AlexNet模型,我们可以将pytorch中的AlexNet模型结构打印出来
 具体如下:
  import torchvision                                                                                               
  model = torchvision.models.AlexNet()                                  # 初始化模型          
  print('\n AlexNet的模块:\n',dict(model.named_children()) )    # 打印模型的模块  

运行结果如下:
 
pytorch中的AlexNet模型结构 
可看到pytorch中AlexNet的模型结构基本与原文基本是一致的








      AlexNet卷积神经网络-代码实现    


下面展示如何训练一个AlexNet卷积神经网络用于图片类别识别
 具体代码如下:
import torchvision
import torch
from   torch.utils.data   import DataLoader
import numpy as np
model = torchvision.models.AlexNet(num_classes=102)                          # 初始化模型
print('\n AlexNet的模块:\n',dict(model.named_children()) ) 
# 训练函数                                                                   
def train(dataloader,valLoader,model,epochs,goal,device):                    
    for epoch in range(epochs):                                              
        err_num  = 0                                                         # 本次epoch评估错误的样本
        eval_num = 0                                                         # 本次epoch已评估的样本
        print('-----------当前epoch:',str(epoch),'----------------')         
        for batch, (imgs, labels) in enumerate(dataloader):                  
		    # -----训练模型-----                                             
            x, y = imgs.to(device), labels.to(device)                        # 将数据发送到设备
            optimizer.zero_grad()                                            # 将优化器里的参数梯度清空
            py   = model(x)                                                  # 计算模型的预测值   
            loss = lossFun(py, y)                                            # 计算损失函数值
            loss.backward()                                                  # 更新参数的梯度
            optimizer.step()                                                 # 更新参数
			# ----计算错误率----                                             
            idx      = torch.argmax(py,axis=1)                               # 模型的预测类别
            eval_num = eval_num + len(idx)                                   # 更新本次epoch已评估的样本
            err_num  = err_num +sum(y != idx)                                # 更新本次epoch评估错误的样本
            if(batch%10==0):                                                 # 每10批打印一次结果
                print('err_rate:',err_num/eval_num)                          # 打印错误率
        # -----------验证数据误差---------------------------                 
        model.eval()                                                         # 将模型调整为评估状态
        val_acc_rate = calAcc(model,valLoader,device)                        # 计算验证数据集的准确率
        model.train()                                                        # 将模型调整回训练状态
        print("验证数据的准确率:",val_acc_rate)                              # 打印准确率    
        if((err_num/eval_num)<=goal):                                        # 检查退出条件
            break                                                            
    print('训练步数',str(epoch),',最终训练误差',str(err_num/eval_num))       

# 计算数据集的准确率                                                         
def calAcc(model,dataLoader,device):                                         
    py = np.empty(0)                                                         # 初始化预测结果
    y  = np.empty(0)                                                         # 初始化真实结果
    for batch, (imgs, labels) in enumerate(dataLoader):                      # 逐批预测
        cur_py =  model(imgs.to(device))                                     # 计算网络的输出
        cur_py = torch.argmax(cur_py,axis=1)                                 # 将最大者作为预测结果
        py     = np.hstack((py,cur_py.detach().cpu().numpy()))               # 记录本批预测的y
        y      = np.hstack((y,labels))                                       # 记录本批真实的y
    acc_rate = sum(y==py)/len(y)                                             # 计算测试样本的准确率
    return acc_rate                                                             

# -------模型参数初始化----------------------
def init_param(model):
    param_dict = dict(model.named_parameters())                              # 获取模型的参数字典
    for key in  param_dict:                                                  # 历遍每个参数,对其初始化
        param_name = key.split(".")[-1]                                      # 获取参数的尾缀作为名称
        if (param_name=='weight'):                                           # 如果是权重
            torch.nn.init.normal_(param_dict[key])                           # 则正态分布初始化
        elif (param_name=='bias'):                                           # 如果是阈值
            torch.nn.init.zeros_(param_dict[key])                            # 则初始化为0
            
            
#-------------主流程脚本----------------------------------
#-------------------加载数据-----------------------
trainsform =torchvision.transforms.Compose([
    torchvision.transforms.Resize([224, 224]),
    torchvision.transforms.ToTensor(),
    ]
    )
train_data = torchvision.datasets.Flowers102(                               
    root       = 'D:\pytorch\data'                                           # 路径,如果路径有,就直接从路径中加载,如果没有,就联网获取
    ,split     ='train'                                                      # 训练数据
    ,transform = trainsform                                                  # 转换数据
    ,download  = True                                                        # 是否下载,选为True,就下载到root下面
    ,target_transform= None)                                                                         
val_data = torchvision.datasets.Flowers102(                                 
    root       = 'D:\pytorch\data'                                           # 路径,如果路径有,就直接从路径中加载,如果没有,就联网获取
    ,split     ='test'                                                       # 测试数据
    ,transform = trainsform                                                  # 转换数据
    ,download  = True                                                        # 是否下载,选为True,就下载到root下面
    ,target_transform= None)                                                 
                                                                             
#-------------------模型训练--------------------------------                 
trainLoader = DataLoader(train_data, batch_size=30, shuffle=True)            # 将数据装载到DataLoader
valLoader   = DataLoader(val_data  , batch_size=30)                          # 将验证数据装载到DataLoader 
device      = torch.device('cuda' if torch.cuda.is_available() else 'cpu')   # 设置训练设备  
init_param(model)                                                            # 初始化模型参数
model       = model.to(device)                                               # 发送到设备  
lossFun     = torch.nn.CrossEntropyLoss()                                    # 定义损失函数为交叉熵损失函数
optimizer   = torch.optim.SGD(model.parameters(), lr=0.01,momentum =0.9)     # 初始化优化器
train(trainLoader,valLoader,model,1000,0.01,device)                          # 训练模型,训练100步,错误低于1%时停止训练

# -----------模型效果评估--------------------------- 
model.eval()                                                                 # 将模型切换到评估状态(屏蔽Dropout)
train_acc_rate = calAcc(model,trainLoader,device)                            # 计算训练数据集的准确率
print("训练数据的准确率:",train_acc_rate)                                     # 打印准确率
val_acc_rate = calAcc(model,valLoader,device)                                # 计算验证数据集的准确率
print("验证数据的准确率:",val_acc_rate)       
备注:代码未亲测,仅供参考







好了,以上就是AlexNet卷积神经网络的模型结构与代码实现了~










 End 






联系老饼