Pytorch 第八回:卷积神经网络——GoogleNet模型
本次开启深度学习第八回,基于Pytorch的GoogleNet卷积神经网络模型。对于卷积神经网络,咱们讲过了AlexNe模型和VGG模型。本回再分享一个新的模型,叫做GoogleNet模型。这个模型的突出点是采用了不同大小的卷积核进行组合训练。接下来给大家分享具体思路。
本次学习,借助的平台是PyCharm 2024.1.3,python版本3.11 numpy版本是1.26.4,pytorch版本2.0.0+cu118
文章目录
前言
讲解模型前,先分享几个概念,统一下思想:
1、GoogleNet模型
2014年的ImageNet大规模视觉竞赛中,出彩的不止有VGG模型,还有GoogleNet模型。GoogleNet模型在当年是夺得了比赛的冠军,VGG模型夺得了亚军。GoogLeNet模型,在不大量增加硬件计算力的前提下,采用提高网络模型深度和宽度的方法,提高了模型的准确率。其中,GooogleNet模型的核心结构是Inception块。Inception块架构如下图所示:
2、inception块
如上图所示,inception块有四条路径并联组成。左一路径有一层卷积;左二路径有两层不同卷积核的卷积层;右二路径有两层不同卷积核的卷积;右一路径有一层卷积和一层池化。具体代码可以继续看下文。
该结构的成功,表明不同尺寸的滤波器可以有效识别不同范围的图像细节。那这个块有可改变的的变量吗?肯定有,它的超参数是每层输入和输出的通道数。
注:
在机器学习中,超参数是模型训练过程的配置变量。
3、批量规范化
通过批量规范化,可以帮助深层神经网络在短时间进行收敛。
其公式如下:
其中,x为小批量的输入,u’为小批量样本的均值,p’为小批量样本的标准差。应用标准化后,生成的小批量的均值为0,单位方差为1。由于单位方差是人为的影响,因此需要拉伸参数a和偏移参数b进行矫正。同时,参数a和b是需要在模型训练过程中进行学习优化的。
注:
在训练模式下,数据的均值和方差只能通过小批量的方式来获取,而测试模式下,可以根据整个测试集进行精确计算均值和方差。
4、影响神经网络的训练因素有哪些,可以先总结一下:
1)数据预处理的效果;
2)随着神经网络的加深,中间层的变量具有更大的变化范围;
3)网络的层数增多时,其过拟合的几率也在加大。
闲言少叙,直接展示逻辑,先上引用:
import numpy as np
import torch
from torch import nn
from torchvision.datasets import CIFAR10
import time
from torch.utils.data import DataLoader
一、数据准备
如前几回一样,本次仍然采用CIFAR10数据集,因此不做重点解释(有兴趣的可以查看前两回的内容),本回只展示代码:
def data_tf(x):x = x.resize((96, 96), 2) x = np.array(x, dtype='float32') / 255x = (x - 0.5) / 0.5 x = x.transpose((2, 0, 1)) x = torch.from_numpy(x)return x
train_set = CIFAR10('./data', train=True, transform=data_tf)
train_data = torch.utils.data.DataLoader(train_set, batch_size=64, shuffle=True)
二、模型准备
1.搭建inception块
(展示的代码有点长,大家不要一上来就感到心烦,耐心看一下,还是比较简单。)
如上文所述,inception块是将四个线路的网络合成了一个网络。需要注意的是,这里为了加快数据的收敛,引用了批量标准化。这点可能和原始的inception块结构不一样。
inception块的输入参数由各个路线的通道数量组成,输出为搭建好的inception网络,具体如下:
class inception(nn.Module):def __init__(self, channel_in, channel_out1, channel_out2, channel_out3, channel_out4):super(inception, self).__init__()channel_out2_1 = channel_out2[0]channel_out2_2 = channel_out2[1]channel_out3_1 = channel_out3[0]channel_out3_2 = channel_out3[1]# 第一条线路 1*1卷积self.path1 = nn.Sequential(nn.Conv2d(channel_in, channel_out1, kernel_size=1, stride=1),nn.BatchNorm2d(channel_out1, eps=1e-3),nn.ReLU(True))# 第二条线路 1*1卷积-> 3*3卷积+填充1self.path2 = nn.Sequential(nn.Conv2d(channel_in, channel_out2_1, kernel_size=1, stride=1),nn.BatchNorm2d(channel_out2_1, eps=1e-3),nn.ReLU(True),nn.Conv2d(channel_out2_1, channel_out2_2, kernel_size=3, stride=1, padding=1),nn.BatchNorm2d(channel_out2_2, eps=1e-3),nn.ReLU(True))# 第三条线路 1*1卷积-> 5*5卷积+填充2self.path3 = nn.Sequential(nn.Conv2d(channel_in, channel_out3_1, kernel_size=1, stride=1),nn.BatchNorm2d(channel_out3_1, eps=1e-3),nn.ReLU(True),nn.Conv2d(channel_out3_1, channel_out3_2, kernel_size=5, stride=1, padding=2),nn.BatchNorm2d(channel_out3_2, eps=1e-3),nn.ReLU(True))# 第四条线路 3*3池化+填充1-> 3*3卷积self.path4 = nn.Sequential(nn.MaxPool2d(3, stride=1, padding=1),nn.Conv2d(channel_in, channel_out4, kernel_size=1, stride=1),nn.BatchNorm2d(channel_out4, eps=1e-3),nn.ReLU(True))def forward(self, x):y1 = self.path1(x)y2 = self.path2(x)y3 = self.path3(x)y4 = self.path4(x)output = torch.cat((y1, y2, y3, y4), dim=1)return output
2.GoogleNet网络搭建
GoogleNet模型下有五个网络块,通过使用9个Inception块和池化层的堆叠来完成其数值预测。从代码中可以看出,相比前两回分享的模型,GoogleNet模型的网络是又宽又深。当然,GoogleNet模型主要由卷积层和池化层组成,减少了全连接层,因此其神经元的个数在一定程度上也是优化过的。
GoogleNet模型输入为数据的通道数,输出为分类的类别数目。
class googlenet(nn.Module):def __init__(self, channel_in, class_out):super(googlenet, self).__init__()# 网络块1self.block1 = nn.Sequential(nn.Conv2d(channel_in, 64, kernel_size=7, stride=2,padding=3),nn.BatchNorm2d(64, eps=1e-3),nn.ReLU(True),nn.MaxPool2d(3, 2,padding=1))# 网络块2self.block2 = nn.Sequential(nn.Conv2d(64, 64, kernel_size=1),nn.BatchNorm2d(64, eps=1e-3),nn.ReLU(True),nn.Conv2d(64, 192, kernel_size=3, padding=1),nn.BatchNorm2d(192, eps=1e-3),nn.ReLU(True),nn.MaxPool2d(3, 2,padding=1))# 网络块3self.block3 = nn.Sequential(inception(192, 64, (96, 128), (16, 32), 32),inception(256, 128, (128, 192), (32, 96), 64),nn.MaxPool2d(3, 2))# 网络块4self.block4 = nn.Sequential(inception(480, 192, (96, 208), (16, 48), 64),inception(512, 160, (112, 224), (24, 64), 64),inception(512, 128, (128, 256), (24, 64), 64),inception(512, 112, (144, 288), (32, 64), 64),inception(528, 256, (160, 320), (32, 128), 128),nn.MaxPool2d(3, 2,padding=1))# 网络块5self.block5 = nn.Sequential(inception(832, 256, (160, 320), (32, 128), 128),inception(832, 384, (182, 384), (48, 128), 128),nn.AvgPool2d(2))# 分类self.classifier = nn.Linear(1024, class_out)def forward(self, x):x = self.block1(x)x = self.block2(x)x = self.block3(x)x = self.block4(x)x = self.block5(x)# 将矩阵拉平x = x.view(x.shape[0], -1)x = self.classifier(x)return x
注:由于篇幅问题,通道数的计算,在这里就不展示了,有兴趣的可以在小记当中观看。
3.定义损失函数和优化器
定义交叉熵损失函数和梯度下降函数优化器(学习率设定为0.01)。
Loss_f = nn.CrossEntropyLoss()
optimizer = torch.optim.SGD(classify_GoogleNet.parameters(), lr=0.01)
二、模型训练
1、实例化模型
在实例化时,设定输入通道为3,输出类别数为10。
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
print("device=", device)
classify_GoogleNet = googlenet(3, 10).to(device)
2、迭代训练
进行20次迭代训练
time1 = time.time()
for k in range(20):train_loss = 0train_acc = 0classify_GoogleNet.train() # 将模型设为测试模式for x_data, y_data in train_data:x_data = x_data.to(device)y_data = y_data.to(device)# 前向传播y_predict = classify_GoogleNet(x_data)loss_train = Loss_f(y_predict, y_data).to(device)# 反向传播optimizer.zero_grad() # 梯度清零loss_train.backward() # 计算梯度optimizer.step() # 使用优化器更新参数# 记录误差train_loss += loss_train# 计算分类的准确率_, pred = y_predict.max(1)train_correct = (pred == y_data).sum().item() / x_data.shape[0]train_acc += train_correcttime_consume = time.time() - time1print('epoch:{},TrainLoss:{:.6f},TrainAcc:{:.6f},consume time:{:.3f}s'.format(k,train_loss / len(train_data),train_acc / len(train_data),time_consume))
torch.save(classify_GoogleNet.state_dict(), 'classify_GoogleNet_params.pth')
3、输出展示
与前两回相比,此次训练集训练的精度更高,损失值更低,但时间也是花费的更多。
epoch:0,TrainLoss:1.480232,TrainAcc:0.458440,consume time:302.479s
epoch:4,TrainLoss:0.527561,TrainAcc:0.814978,consume time:1512.602s
epoch:8,TrainLoss:0.212731,TrainAcc:0.926031,consume time:2728.588s
epoch:12,TrainLoss:0.093475,TrainAcc:0.967032,consume time:3948.487s
epoch:16,TrainLoss:0.055090,TrainAcc:0.980818,consume time:5171.283s
epoch:19,TrainLoss:0.030331,TrainAcc:0.989950,consume time:6088.418s
总结
1、数据准备:准备CIFAR10数据集
2、模型准备:准备inception块,GoogleNet模型,交叉熵损失函数和优化器
3、数据训练:实例化训练模型,并进行20次数据训练。