基于深度学习的图像分类任务实现(二)卷积神经网络分类器&基于Residual Bolck的卷积神经网络分类器

实现多种对Cifar-10数据集的分类器,并比较其算法精度。要求基于PyTorch设计并实现四种分类器,并利用Cifar-10的测试集评估各分类器的性能:

1.代码编写思路

在利用pytorch进行模型构造与训练时,总体思路分为四个阶段:
数据集准备、用类设计模型、构造损失函数和优化器、训练模型+测试。
如下图所示

2.卷积神经网络分类器

2.1基本原理

卷积神经网络一般特征提取和全连接神经网络训练。
特征提取包括卷积、池化(两个部分可以重复进行),经过卷积、池化数据达到了特征提取和降维的目的。将提取出的特征扁平化处理后输入至全连接神经网络中通过学习训练模型获得预测标签。在这里插入图片描述

2.2 代码实现

#导入包
import torch
import torchvision
#transforms 定义了一系列数据转化形式,并对数据进行预处理。
import torchvision.transforms as transforms
#matplotlib inline
# 下载数据集并对数据进行处理
transform=transforms.Compose([transforms.ToTensor(),transforms.Normalize((0.5,0.5,0.5),(0.05,0.05,0.05))])
batch_size=128
trainset= torchvision.datasets.CIFAR10(root='./data',train=True,download=True,transform=transform)
trainloader=torch.utils.data.DataLoader(trainset, batch_size, shuffle=True, num_workers=1)
testset=torchvision.datasets.CIFAR10(root='./data',train=False,download=True,transform=transform)
testloader=torch.utils.data.DataLoader(testset, batch_size, shuffle=False, num_workers=1)
# 定义神经网络
import torch.nn as nn
import torch.nn.functional as F
class CNNNet(nn.Module):
    def __init__(self):
        super(CNNNet, self).__init__()
        self.conv1 = nn.Conv2d(3,64,5)  #卷积层,分别是输入通道、输出通道和卷积核大小
        self.pool = nn.MaxPool2d(2,2)  #池化层,对应池化大小和步长。
        self.conv2 = nn.Conv2d(6,16,5) 
        self.fc1 = nn.Linear(16*5*5, 384)
        self.fc2 = nn.Linear(384,192)
        self.fc3 = nn.Linear(192,10)
        self.dropout = nn.Dropout(p=0.6)  #为了防止过拟合使用的drought层
    def forward(self, x):
        x = self.pool(F.relu(self.conv1(x)))  #3*32*32 -- 64*28*28 -- 64*14*14
        x = self.pool(F.relu(self.conv2(x)))  #64*14*14 -- 16*10*10 -- 16*5*5
        #print(x)
        #print(x.shape[0])
        x = x.view(-1,16*5*5) #扁平化处理
        x = F.relu(self.fc1(x))
        x = self.dropout(x)
        x = F.relu(self.fc2(x))
        x = self.fc3(x)
        return x
cnnnet=CNNNet()
print('信计181-181294-齐佳静')
print(cnnnet)
device = torch.device("cuda:0"if torch.cuda.is_available() else"cpu")
cnnnet.to(device)
# 假设在一台CUDA机器上运行,那么这里将输出一个CUDA设备号:
print(device)
#定义损失函数和优化器
import torch.optim as optim
loss=nn.CrossEntropyLoss()
optimizer= optim.SGD(cnnnet.parameters(),lr=0.001,momentum=0.99)

#数据准确度评估
def evaluate_accuracy(data_iter, net):
    acc_sum, n = 0.0, 0
    for data in data_iter:
        X, y=data
        X, y=X.to(device),y.to(device)
        acc_sum += (net(X).argmax(dim=1) == y).float().sum().item()
        n += y.shape[0]
    return acc_sum / n
#模型训练及训练过程和预测
epochs=200
def train_model(optimizer):
    for epoch in range(epochs):
        train_loss_sum, train_acc_sum, n = 0.0, 0.0, 0
        for data in trainloader:
            inputs, y=data
            inputs, y=inputs.to(device),y.to(device)
            #print(inputs)
            y_hat = cnnnet(inputs)
            cost = loss(y_hat, y).sum()
 
            optimizer.zero_grad()
            cost.backward()
            optimizer.step()
 
            train_loss_sum += cost.item()
            train_acc_sum += (torch.argmax(y_hat, dim=1) == y).float().sum().item()
            n += y.shape[0]
        test_acc = evaluate_accuracy(testloader, cnnnet)
        print('epoch %d, loss %.4f, train acc %.4f, test acc %.4f' % (epoch + 1, train_loss_sum / n, train_acc_sum / n, test_acc))
#调用函数进行模型训练
train_model(optimizer)

2.3 运行结果

在这里插入图片描述

#结果可视化
#准确度绘制
import matplotlib.pyplot as plt
# 绘制训练%验证的精度
Trainacc=[] #请输入上述训练过程中保存的数据
Testacc=[] ##请输入上述训练过程中保存的数据
plt.plot(Trainacc)
plt.plot(Testacc)
plt.title('qijiajing')
plt.xlabel('Epoch')
plt.ylabel('Accuracy')
plt.legend(['Train acc', 'Test acc'], loc='upper left')
plt.show()
#loss绘制
import matplotlib.pyplot as plt
loss=[]
plt.plot(loss)
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.title('qijiajing-Model accuracy') 

在这里插入图片描述
transform=transforms.Compose([transforms.ToTensor(),transforms.Normalize((0.5,0.5,0.5),(0.05,0.05,0.05))]),使得特征具有0.5的均值和0.05方差。我们使用64通道和16通道的55卷积核,22步长为2的池化层,线性层分别为(1655,384)、(384,192)、(192,10)。batch_size=128,使用交叉熵损失函数和SGD优化器,其中lr=0.001,momentum=0.99。
我们选取epoch=100,我们对比了学习率分别为0.01和0.001时模型的训练效果,显然lr=0.001模型表现更佳,也许是因为学习率较高时模型陷入了局部最优解。最终训练集精度稳定在76%左右,测试集精度稳定在65%左右。卷积神经网络经过特征提取保留了图像的空间特征(结构),故训练效果比线性分类器和全连接神经网络分类器都要好。

3.基于Residual Bolck的卷积神经网络分类器

3.1基本原理

全连接神经网络和卷积神经网络在网络的架构上是串行的结构,每一层输出是下一层的输入,我们用的是反向传播算法,用链式法则把一系列偏导数相乘得到总梯度,若每个偏导数都小于1,相乘后的结果会越来越小,甚至趋近于0。当梯度趋于0时,权重得不到更新,随着网络深度的增加,离输入较近的权重无法得到充分的训练。这种现象叫做梯度消失现象。
引入Residual Net:有很多支线将输入跳过中间层直接连接至后面的层,使得后面的层可以直接学习残差(即加一个跳连接),逐层训练,以解决梯度消失问题。在这里插入图片描述
残差块将输入x经过两个权重层,与x相加,再激活。(使得导数在原来基础上加一,进而梯度>1)

3.2代码实现

#导入包
import torch
import torchvision
#transforms 定义了一系列数据转化形式,并对数据进行预处理。
import torchvision.transforms as transforms
#matplotlib inline

#导入数据
transform=transforms.Compose([transforms.ToTensor(),transforms.Normalize((0.5,0.5,0.5),(0.05,0.05,0.05))])
batch_size=128
trainset= torchvision.datasets.CIFAR10(root='./data',train=True,download=True,transform=transform)
trainloader=torch.utils.data.DataLoader(trainset, batch_size, shuffle=True, num_workers=1)
#测试数据集
testset=torchvision.datasets.CIFAR10(root='./data',train=False,download=True,transform=transform)
testloader=torch.utils.data.DataLoader(testset, batch_size, shuffle=False, num_workers=1)
#定义ResidualBlock模型
import torch.nn as nn
import torch.nn.functional as F
class ResidualBlock(nn.Module):
    def __init__(self,channels):
        super(ResidualBlock, self).__init__()
        self.channels = channels
        self.conv1 = nn.Conv2d(channels, channels, kernel_size=3, padding=1)
        self.conv2 = nn.Conv2d(channels, channels, kernel_size=3, padding=1)
    def forward(self, x):
        y=F.relu(self.conv1(x))
        y=self.conv2(y)
        return F.relu(x+y)
residualblock=ResidualBlock(32)
print(residualblock)
#定义网络模型
import torch.nn as nn
import torch.nn.functional as F
class CNNNet(nn.Module):
    def __init__(self):
        super(CNNNet, self).__init__()
        self.conv1 = nn.Conv2d(3,16,5) 
        self.pool = nn.MaxPool2d(2)
        self.conv2 = nn.Conv2d(16,32,5) 
        self.rblock1=ResidualBlock(16)
        self.rblock2=ResidualBlock(32)
        self.fc = nn.Linear(32*5*5 ,10)
    def forward(self, x):
        x = self.pool(F.relu(self.conv1(x))) #3*32*32 -- 16*14*14
        x = self.rblock1(x)
        x = self.pool(F.relu(self.conv2(x))) #16*14*14 -- 32*5*5
        x = self.rblock2(x)
        x = x.view(-1, 32 * 5 * 5)
        x = self.fc(x)
        return x
cnnnet=CNNNet()
print(cnnnet)
device = torch.device("cuda:0"if torch.cuda.is_available() else"cpu")
cnnnet.to(device)
#定义损失函数和优化器
import torch.optim as optim
loss=nn.CrossEntropyLoss()
optimizer= optim.SGD(cnnnet.parameters(),lr=0.01)#,momentum=0.9)

#数据准确度评估
def evaluate_accuracy(data_iter, net):
    acc_sum, n = 0.0, 0
    for data in data_iter:
        X, y=data
        X, y=X.to(device),y.to(device)
        acc_sum += (net(X).argmax(dim=1) == y).float().sum().item()
        n += y.shape[0]
return acc_sum / n
epochs=200
def train_model(optimizer):
    for epoch in range(epochs):
        train_loss_sum, train_acc_sum, n = 0.0, 0.0, 0
        for data in trainloader:
            inputs, y=data
            inputs, y=inputs.to(device),y.to(device)
            #print(inputs)
            y_hat = cnnnet(inputs)
            cost = loss(y_hat, y).sum()
 
            optimizer.zero_grad()
            cost.backward()
            optimizer.step()
 
            train_loss_sum += cost.item()
            train_acc_sum += (torch.argmax(y_hat, dim=1) == y).float().sum().item()
            n += y.shape[0]
        test_acc = evaluate_accuracy(testloader, cnnnet)
        print('epoch %d, loss %.4f, train acc %.4f, test acc %.4f' % (epoch + 1, train_loss_sum / n, train_acc_sum / n, test_acc))
train_model(optimizer)

3.3模型结构可视化

在这里插入图片描述

3.4运行结果

对数据特征进行归一化处理成均值为0.5,方差为0.05。除了上述模型的参数,我们使用交叉熵损失函数和SGD优化器,其中lr=0.01,batch_size=128。
在这里插入图片描述
经过100个epoch后,训练集的准确度在86%左右,测试集的准确度在71%左右。与卷积神经网络模型的64%准确度相比有了较大的提升。Residual Block属于高级卷积神经网络的一种,使用残差块来解决因为深度增加而导致模型性能下降的问题,对于此数据集进行训练,提高了网络的性能。

Guess you like

Origin blog.csdn.net/weixin_45928096/article/details/122342780