本站原创文章,转载请说明来自《老饼讲解-深度学习》www.bbbdata.com
AlexNet是第一个在ImageNet挑战赛上取得显著成功的深度卷积神经网络,它是学习CNN的入门模型
本文介绍AlexNet卷积神经网络在原论文中的模型结构,包括每一层的具体运算细节,并展示AlexNet的代码实现
通过本文,可以快速了解AlexNet是什么,它的详细结构是怎么样,以及如何实现一个AlexNet用于图片类别识别
本节简单介绍AlexNet和AlexNet的结构
什么是AlexNet神经网络
AlexNet是一个用于图像类别识别的CNN深度学习模型,它在2012年ImageNet竞赛中由于效果卓越,一战成名
AlexNet原文为《ImageNet Classification with Deep Convolutional Neural Networks》
由于AlexNet是2012年的作品,当时计算力低下,所以AlexNet采用的是双核计算的结构
AlexNet论文中AlexNet的结构图如下:
将上述结构图简化后,示图如下:
AlexNet的输入是224×224×3的图片,输出是1×1000的概率向量P, 第i个输出代表图片属于类别i的概率
可以看到,在原文中AlexNet由双核构成,每个核都有8层,前五层是卷积层,后三层是全连接层
双核之间除了个别层会交换数据,大部分时候是独立的
最后,输出层汇聚双核的数据,训练一个全连接层并进行softmax后作为输出
本节详细解释AlexNet每一层的详细结构与运算过程
AlexNet结构详述
AlexNet输入:224×224×3的图片
AlexNet输出:1000×1的概率向量P
一、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卷积层
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卷积层
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卷积层
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卷积层
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全连接层
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全连接层
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全连接层
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神经网络的单核结构
关于AlexNe卷积神经网络的单核结构
由于AlexNet是2012年的作品,当时计算力低下,所以AlexNet采用的是双核计算的结构
在学习时我们可以原汁原汁的学习AlexNet这种最初的设计,但实际使用中,就需要略加改善,
因为现在的计算机算力已经不再需要分核计算,一般会去掉双核结构,以方便实现
如此一来,单核的AlexNet整体计算流程如下:
将AlexNet的原配置列表改为单核,则得到AlexNet的单核配置如下:
本节展示如何实现一个AlexNet用于图片类别识别
pytorch中的AlexNet模型
pytorch中提供了AlexNet模型,我们可以将pytorch中的AlexNet模型结构打印出来
具体如下:
import torchvision
model = torchvision.models.AlexNet() # 初始化模型
print('\n AlexNet的模块:\n',dict(model.named_children()) ) # 打印模型的模块运行结果如下:
可看到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