【PyTorch】2.2 使用Pytorch构建一个分类器(使用真实数据集CIFAR10)

2.2 使用Pytorch构建一个分类器

学习目标

  • 了解分类器的任务和数据样式
  • 掌握如何使用Pytorch实现一个分类器

分类器任务和数据介绍

  • 构建一个将不同图像进行分类的神经网络分类器,对输入的图片进行判断并完成分类
  • 本案例采用CIFAR0数据集作为原始图片数据
CIFAR10数据集介绍
  • 数据集中每张图片的尺寸是33232,代表彩色3通道
  • CIFAR10数据集共有10种不同的分类,分别是:airplane automobile bird cat deer dog frog horse ship truck
训练分类器步骤
  • 1:使用torchvision下载CIFAR10数据集
  • 2:定义卷积神经网络
  • 3:定义损失函数
  • 4:在训练集上训练模型
  • 5:在测试集上测试模型
1:使用torchvision下载CIFAR10数据集
  • 导入torchvision包来辅助下载数据集
import torch
import torchvision
import torchvision.transforms as transforms
import ssl
ssl._create_default_https_context = ssl._create_unverified_context
#下载数据集并对图片进行调整,因为torchvision数据集的输出是PILImage格式,数据域在[0,1]
#我们将其转换成标准数据域[-1,1]的张量格式
#transform 数据转换器
transform=transforms.Compose([transforms.ToTensor(),transforms.Normalize((0.5,0.5,0.5),(0.5,0.5,0.5))])
trainset=torchvision.datasets.CIFAR10(root='./data',train=True,download=True,transform=transform)
# 下载下来的数据放在trainset里面
trainloader=torch.utils.data.DataLoader(trainset,batch_size=4,shuffle=True,num_workers=2)
# DataLoader数据迭代器 将数据封装成DataLoader
# num_workers:两个线程读取数据
# batch_size=4 批处理

testset=torchvision.datasets.CIFAR10(root='./data',train=False,download=True,transform=transform)
testloader=torch.utils.data.DataLoader(testset,batch_size=4,shuffle=False,num_workers=2)
classes=('airplane', 'automobile', 'bird', 'cat', 'deer', 'dog', 'frog', 'horse', 'ship', 'truck')
#输出
Downloading https://www.cs.toronto.edu/~kriz/cifar-10-python.tar.gz to ./data\cifar-10-python.tar.gz
100.0%
Extracting ./data\cifar-10-python.tar.gz to ./data
Files already downloaded and verified

在这里插入图片描述注意:

可能存在的两个问题:

  • windows系统下运行上述代码,并且出现报错信息“BrokenPipeError”时,可以尝试将torch.utils.DataLoader()中的num_workers设置为0
  • 下载CIFAR10数据集报错:urllib.error.URLError: <urlopen error [SSL: CERTIFICATE_VERIFY_FAILED] certi
  • 尽量使用linux系统学习深度学习
展示若干训练集的图片

在此处遇到问题可以访问我之前写的一篇文章

import torch
import torchvision
import torchvision.transforms as transforms
import ssl
import torch.utils.data as Data
ssl._create_default_https_context = ssl._create_unverified_context
#下载数据集并对图片进行调整,因为torchvision数据集的输出是PILImage格式,数据域在[0,1]
#我们将其转换成标准数据域[-1,1]的张量格式
#transform 数据转换器
transform=transforms.Compose([transforms.ToTensor(),transforms.Normalize((0.5,0.5,0.5),(0.5,0.5,0.5))])
trainset=torchvision.datasets.CIFAR10(root='./data',train=True,download=True,transform=transform)
# 下载下来的数据放在trainset里面
trainloader=torch.utils.data.DataLoader(dataset=trainset,batch_size=4,shuffle=True,num_workers=2)
# DataLoader数据迭代器 将数据封装成DataLoader
# num_workers:两个线程读取数据
# batch_size=4 批处理

testset=torchvision.datasets.CIFAR10(root='./data',train=False,download=True,transform=transform)
testloader=torch.utils.data.DataLoader(dataset=testset,batch_size=4,shuffle=False,num_workers=2)
classes=('airplane', 'automobile', 'bird', 'cat', 'deer', 'dog', 'frog', 'horse', 'ship', 'truck')

#展示若干训练集的图片
#导入画图包和numpy
import matplotlib.pyplot as plt
import numpy as np

#构建展示图片的函数
def imShow(img):
    img=img/2+0.5
    #img是tensor类型的数据,tensor类型转换成numpy类型的数据
    npimg=img.numpy()
    plt.imshow(np.transpose(npimg,(1,2,0)))#维度转换成 1 2 0 这三个维度
    plt.show()

#从数据迭代器中读取一张图片
if __name__ == '__main__':
    try:
        dataiter = iter(trainloader)
        images, labels = dataiter.next()

        # 展示图片
        imShow(torchvision.utils.make_grid(images))
        # 使用网格的形式展示图片
        # 打印标签label
        print(' '.join('%5s' % classes[labels[j]] for j in range(4)))
    except Exception as e:
        print(e)
#输出
dog horse   cat   cat

在这里插入图片描述

定义卷积神经网络

仿照2.1节中的类来构造此处的类,唯一的区别在于次数采用的三通道 3-channel

import torch.nn as nn
import torch.nn.functional as F

class Net(nn.Module):
    def __init__(self):
        super(Net,self).__init__()
        #定义两个卷积层
        self.conv1=nn.Conv2d(3,6,5)
        self.conv2=nn.Conv2d(6,16,5)
        #定义池化层
        self.pool = nn.MaxPool2d(2, 2)
        #定义全连接层
        self.fc1=nn.Linear(16*5*5,120)
        self.fc2=nn.Linear(120,84)
        self.fc3=nn.Linear(84,10)

    def forward(self,x):
        x=self.pool(F.relu(self.conv1(x)))
        x=self.pool(F.relu(self.conv2(x)))
        #变换x的形状 以适配全连接层的输入
        x=x.view(-1,16*5*5)
        x=F.relu(self.fc1(x))
        x=F.relu(self.fc2(x))
        x=self.fc3(x)
        return x
net=Net()
print(net)
3: 定义损失函数

采用交叉熵损失函数和随机梯度下降优化器

#交叉熵损失函数和随机梯度下降优化器
import torch.optim as optim
criterion=nn.CrossEntropyLoss()
optimizer=optim.SGD(net.parameters(),lr=0.001,momentum=0.9)
4: 在训练集上训练模型

采用基于梯度下降的优化算法,都需要很多个轮次的迭代训练

import torch.nn as nn
import torch.nn.functional as F
import torchvision
import torch
import torchvision.transforms as transforms

transform=transforms.Compose([transforms.ToTensor(),transforms.Normalize((0.5,0.5,0.5),(0.5,0.5,0.5))])
trainset=torchvision.datasets.CIFAR10(root='./data',train=True,download=True,transform=transform)
trainloader=torch.utils.data.DataLoader(trainset,batch_size=4,shuffle=True,num_workers=0)
class Net(nn.Module):
    def __init__(self):
        super(Net,self).__init__()
        #定义两个卷积层
        self.conv1=nn.Conv2d(3,6,5)
        self.conv2=nn.Conv2d(6,16,5)
        #定义池化层
        self.pool = nn.MaxPool2d(2, 2)
        #定义全连接层
        self.fc1=nn.Linear(16*5*5,120)
        self.fc2=nn.Linear(120,84)
        self.fc3=nn.Linear(84,10)

    def forward(self,x):
        x=self.pool(F.relu(self.conv1(x)))
        x=self.pool(F.relu(self.conv2(x)))
        #变换x的形状 以适配全连接层的输入
        x=x.view(-1,16*5*5)
        x=F.relu(self.fc1(x))
        x=F.relu(self.fc2(x))
        x=self.fc3(x)
        return x
net=Net()
print(net)

#交叉熵损失函数和随机梯度下降优化器
import torch.optim as optim
criterion=nn.CrossEntropyLoss()
optimizer=optim.SGD(net.parameters(),lr=0.001,momentum=0.9)

for epoch in range(2):
    running_loss=0.0
    for i ,data in enumerate(trainloader,0):
        #data 中包含输入图像张量inputs,标签张量labels
        inputs,labels=data

        #首先将优化器梯度归零
        optimizer.zero_grad()

        #输入图像张量进网络,得到输出张量outputs
        outputs=net(inputs)

        #利用网络的输出outputs和标签labels计算损失值
        loss=criterion(outputs,labels)

        #反向传播+参数更新,是标准的代码流程
        loss.backward()
        optimizer.step()

        #打印轮次和损失值
        running_loss+=loss.item()
        if (i+1)%2000==0:
            print('[%d , %5d] loss: %.3f'%(epoch+1,i+1,running_loss/2000))
            running_loss=0.0
print("Finished Training")
  • 输出结果
[1 ,  2000] loss: 2.220
[1 ,  4000] loss: 1.859
[1 ,  6000] loss: 1.658
[1 ,  8000] loss: 1.570
[1 , 10000] loss: 1.512
[1 , 12000] loss: 1.436
[2 ,  2000] loss: 1.373
[2 ,  4000] loss: 1.367
[2 ,  6000] loss: 1.340
[2 ,  8000] loss: 1.303
[2 , 10000] loss: 1.312
[2 , 12000] loss: 1.277
Finished Training
  • 保存模型
#首先设定模型的保存路径
PATH='./cifar_net.pth'
torch.save(net.state_dict(),PATH)
5:在测试集上测试模型
  • 第一步,展示测试集中的若干图片
dataiter=iter(testloader)
images,labels=dataiter.next()

#打印原始图片
imShow(torchvision.utils.make_grid(images))
#打印真实的标签
print('GroundTruth:',' '.join('%5s' %classes[labels[j]] for j in range(4)))

【完整版本代码】

import torch.nn as nn
import torch.nn.functional as F
import torchvision
import torch
import numpy as np
import matplotlib.pyplot as plt
import torchvision.transforms as transforms

transform=transforms.Compose([transforms.ToTensor(),transforms.Normalize((0.5,0.5,0.5),(0.5,0.5,0.5))])
trainset=torchvision.datasets.CIFAR10(root='./data',train=True,download=True,transform=transform)
trainloader=torch.utils.data.DataLoader(trainset,batch_size=4,shuffle=True,num_workers=0)

testset=torchvision.datasets.CIFAR10(root='./data',train=False,download=True,transform=transform)
testloader=torch.utils.data.DataLoader(testset,batch_size=4,shuffle=True,num_workers=0)

classes=('airplane', 'automobile', 'bird', 'cat', 'deer', 'dog', 'frog', 'horse', 'ship', 'truck')

def imShow(img):
    img=img/2+0.5
    #img是tensor类型的数据,tensor类型转换成numpy类型的数据
    npimg=img.numpy()
    plt.imshow(np.transpose(npimg,(1,2,0)))#维度转换成 1 2 0 这三个维度
    plt.show()

class Net(nn.Module):
    def __init__(self):
        super(Net,self).__init__()
        #定义两个卷积层
        self.conv1=nn.Conv2d(3,6,5)
        self.conv2=nn.Conv2d(6,16,5)
        #定义池化层
        self.pool = nn.MaxPool2d(2, 2)
        #定义全连接层
        self.fc1=nn.Linear(16*5*5,120)
        self.fc2=nn.Linear(120,84)
        self.fc3=nn.Linear(84,10)

    def forward(self,x):
        x=self.pool(F.relu(self.conv1(x)))
        x=self.pool(F.relu(self.conv2(x)))
        #变换x的形状 以适配全连接层的输入
        x=x.view(-1,16*5*5)
        x=F.relu(self.fc1(x))
        x=F.relu(self.fc2(x))
        x=self.fc3(x)
        return x
net=Net()
print(net)

#交叉熵损失函数和随机梯度下降优化器
import torch.optim as optim
criterion=nn.CrossEntropyLoss()
optimizer=optim.SGD(net.parameters(),lr=0.001,momentum=0.9)

# for epoch in range(2):
#     running_loss=0.0
#     for i ,data in enumerate(trainloader,0):
#         #data 中包含输入图像张量inputs,标签张量labels
#         inputs,labels=data
#
#         #首先将优化器梯度归零
#         optimizer.zero_grad()
#
#         #输入图像张量进网络,得到输出张量outputs
#         outputs=net(inputs)
#
#         #利用网络的输出outputs和标签labels计算损失值
#         loss=criterion(outputs,labels)
#
#         #反向传播+参数更新,是标准的代码流程
#         loss.backward()
#         optimizer.step()
#
#         #打印轮次和损失值
#         running_loss+=loss.item()
#         if (i+1)%2000==0:
#             print('[%d , %5d] loss: %.3f'%(epoch+1,i+1,running_loss/2000))
#             running_loss=0.0
# print("Finished Training")
#
# #保存模型
# #首先设定模型的保存路径
# PATH='./cifar_net.pth'
# torch.save(net.state_dict(),PATH)

dataiter=iter(testloader)
images,labels=dataiter.next()

#打印原始图片
imShow(torchvision.utils.make_grid(images))
#打印真实的标签
print('GroundTruth:',' '.join('%5s' %classes[labels[j]] for j in range(4)))
  • 第二步,加载模型并对测试图片进行预测
import torch.nn as nn
import torch.nn.functional as F
import torchvision
import torch
import numpy as np
import matplotlib.pyplot as plt
import torchvision.transforms as transforms

transform=transforms.Compose([transforms.ToTensor(),transforms.Normalize((0.5,0.5,0.5),(0.5,0.5,0.5))])
trainset=torchvision.datasets.CIFAR10(root='./data',train=True,download=True,transform=transform)
trainloader=torch.utils.data.DataLoader(trainset,batch_size=4,shuffle=True,num_workers=0)

testset=torchvision.datasets.CIFAR10(root='./data',train=False,download=True,transform=transform)
testloader=torch.utils.data.DataLoader(testset,batch_size=4,shuffle=True,num_workers=0)

classes=('airplane', 'automobile', 'bird', 'cat', 'deer', 'dog', 'frog', 'horse', 'ship', 'truck')

def imShow(img):
    img=img/2+0.5
    #img是tensor类型的数据,tensor类型转换成numpy类型的数据
    npimg=img.numpy()
    plt.imshow(np.transpose(npimg,(1,2,0)))#维度转换成 1 2 0 这三个维度
    plt.show()

class Net(nn.Module):
    def __init__(self):
        super(Net,self).__init__()
        #定义两个卷积层
        self.conv1=nn.Conv2d(3,6,5)
        self.conv2=nn.Conv2d(6,16,5)
        #定义池化层
        self.pool = nn.MaxPool2d(2, 2)
        #定义全连接层
        self.fc1=nn.Linear(16*5*5,120)
        self.fc2=nn.Linear(120,84)
        self.fc3=nn.Linear(84,10)

    def forward(self,x):
        x=self.pool(F.relu(self.conv1(x)))
        x=self.pool(F.relu(self.conv2(x)))
        #变换x的形状 以适配全连接层的输入
        x=x.view(-1,16*5*5)
        x=F.relu(self.fc1(x))
        x=F.relu(self.fc2(x))
        x=self.fc3(x)
        return x
# net=Net()
# print(net)

#交叉熵损失函数和随机梯度下降优化器
import torch.optim as optim
criterion=nn.CrossEntropyLoss()
# optimizer=optim.SGD(net.parameters(),lr=0.001,momentum=0.9)

# for epoch in range(2):
#     running_loss=0.0
#     for i ,data in enumerate(trainloader,0):
#         #data 中包含输入图像张量inputs,标签张量labels
#         inputs,labels=data
#
#         #首先将优化器梯度归零
#         optimizer.zero_grad()
#
#         #输入图像张量进网络,得到输出张量outputs
#         outputs=net(inputs)
#
#         #利用网络的输出outputs和标签labels计算损失值
#         loss=criterion(outputs,labels)
#
#         #反向传播+参数更新,是标准的代码流程
#         loss.backward()
#         optimizer.step()
#
#         #打印轮次和损失值
#         running_loss+=loss.item()
#         if (i+1)%2000==0:
#             print('[%d , %5d] loss: %.3f'%(epoch+1,i+1,running_loss/2000))
#             running_loss=0.0
# print("Finished Training")
#
# #保存模型
# #首先设定模型的保存路径
PATH='./cifar_net.pth'
# torch.save(net.state_dict(),PATH)

dataiter=iter(testloader)
images,labels=dataiter.next()

#打印原始图片
imShow(torchvision.utils.make_grid(images))
#打印真实的标签
print('GroundTruth:',' '.join('%5s' %classes[labels[j]] for j in range(4)))

#第二步,加载模型并对测试图片进行预测
#首先实例化模型的类对象
net=Net()
#加载训练阶段保存好的模型的状态字典
net.load_state_dict(torch.load(PATH))

#利用模型对图片进行预测
outputs=net(images)

#共有10个类别,采用模型计算出的概率最大的作为预测的类别
_,predicted=torch.max(outputs,1)
print("predicted:",predicted)

#打印预测标签的结果
print("Predicted:",' '.join('%5s' % classes[predicted[j]] for j in range(4)))
  • 输出
GroundTruth:  ship  frog  bird automobile
predicted: tensor([0, 2, 8, 1])
Predicted: airplane  bird  ship automobile
  • 训练好的模型在全部测试集上的表现
#在全部测试集上的表现
correct=0
total=0
with torch.no_grad():
    for data in testloader:
        images,labels=data
        outputs=net(images)
        _,predicted=torch.max(outputs.data,1)
        total+=labels.size(0)
        correct+=(predicted==labels).sum().item()
print("Accuracy of the network on the 10000 test images:%d %%"%(100*correct/total))
#输出
 Accuracy of the network on the 10000 test images:56 %
  • 分析:对于拥有10个类被的数据集,随机猜测的准确率是10%,模型达到了53%,说明模型学到了真实的东西。
    上面计算的是模型在总体测试集上的准确率,为了更加细致多看一些模型在哪些类别上表现更好,在哪些类别上表现更差,可以对每个类别的准确率进行计算。
#在全部测试集上的表现
correct=0
total=0
class_correct=[0. for i in range(10)]
class_total=[0 for i in range(10)]
max_index=-1
min_index=1000
with torch.no_grad():
    for data in testloader:
        images,labels=data
        outputs=net(images)
        _,predicted=torch.max(outputs.data,1)
        total+=labels.size(0)
        correct+=(predicted==labels).sum().item()
        for i in range(labels.size(0)):
            class_correct[labels[i]]+=(predicted[i]==labels[i])
            class_total[labels[i]]+=1
print("Accuracy of the network on the 10000 test images:%d %%"%(100*correct/total))
# print(class_total)
# print(class_correct)
#打印不同类别的准确度
for i in range(10):
    print("Accuracy of ",classes[i], ":",100*class_correct[i].item()/class_total[i],"%")
#输出
Accuracy of the network on the 10000 test images:56 %
Accuracy of  airplane : 59.4 %
Accuracy of  automobile : 70.2 %
Accuracy of  bird : 30.1 %
Accuracy of  cat : 38.6 %
Accuracy of  deer : 59.1 %
Accuracy of  dog : 35.4 %
Accuracy of  frog : 70.7 %
Accuracy of  horse : 68.3 %
Accuracy of  ship : 69.2 %
Accuracy of  truck : 67.6 %

在GPU上训练模型

为了真正利用Pytorch中Tensor的优秀属性,加速模型的训练,可以将训练过程转移到GPU上进行。
首先,定义设备,如果CUDA是可用的则被定义成GPU,否则被定义成CPU
将模型放在GPU上训练分两步走:
1:将模型转移到GPU上
2:将数据转移到GPU上

device=torch.device('cuda:0' if torch.cuda.is_available() else "cpu")
print(device)

#当训练模型的时候,只需要将模型转移到GPU上满天星将输入的图片和标签页转移到GPU上即可
net.to(device)

#将输入的图片张量和标签张量转移到GPU上
inputs,labels=data[0].to(device),data[1].to(device)

【小节总结】

学习了分类器的任务和数据样式
  • 将不同图像进行分类的神经网络分类器,对输入的图片进行判断并完成分类
  • 采用CIFAR10数据集作为原始图片数据,CIFAR10数据集拥有10个类别的33232彩色图片
学习了训练分类器的步骤
  • 使用torchvision下载CIFAR10数据集
  • 定义卷积神经网络
  • 定义损失函数
  • 在训练集上训练模型
  • 在测试集上测试模型
学习了在GPU上训练模型
  • 首先需要定义设备,CPU和GPU二选一
    device=torch.device('cuda:0' if torch.cuda.is_available() else "cpu")
  • 然后将模型转移到GPU上去
    net.to(device)
  • 最后在迭代训练的过程中,每一步都将图片和标签张量转移到GPU上去
    inputs,labels=data[0].to(device),data[1].to(device)
  • 0
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值