步骤
官方教程:https://pytorch.org/tutorials/beginner/blitz/cifar10_tutorial.html#loading-and-normalizing-cifar10
步骤:
A、Load and normalizing the CIFAR10 training and test datasets using torchvision
B、Define a Convolution Neural Network
C、Define a loss function
D、Train the network on the training data
E、Test the network on the test data
数据集介绍
CIFAR-10包含10个类别,50,000个训练图像,彩色图像大小:32x32,10,000个测试图像。CIFAR-100与CIFAR-10类似,包含100个类,每类有600张图片,其中500张用于训练,100张用于测试;这100个类分组成20个超类。图像类别均有明确标注。
详细见官方文档说明:http://www.cs.toronto.edu/~kriz/cifar.html
代码
总共有三个py文件,文件名分别为:dataset(数据预处理和加载)、model(网络模型搭建)、train(训练模型并测试)
数据预处理和加载
PyTorch框架中有一个非常重要且好用的包:torchvision
,该包可以用来对数据进行预处理,也可以用来进行数据增强的操作。其主要由3个子包组成,分别是:torchvision.datasets
、torchvision.models
、torchvision.transforms
。
torchvision.datasets
模块中保存了常用的深度学习数据集,例如cifar10、Imagenet、Mnist等。可以直接使用该模块下载指定的数据集。torchvision.transforms
模块用于对datasets模快下载的数据进行预处理操作。torchvision.models
模块保存了一些经典的网络模型。
root:表示存放下载的数据集存放的位置
train:如果为True,则从training.pt创建数据集,否则从test.pt创建数据集。
transform:接受PIL图像并对数据进行预处理
download:如果为True,就从网上下载,如果已经有下载好的数据就不会重复下载了
target_transform:接受目标并对其进行转换的函数/转换。
from torchvision import datasets, transforms
from torch.utils.data import DataLoader
import numpy as np
# transforms模块中的Compose( )可以把多个变换组合在一起,需要写成列表的形式
# 将PIL图片或者numpy.ndarray(HxWxC) (范围在0-255) 转成torch.FloatTensor (CxHxW) (范围为0.0-1.0)
# 归一化,RGB三个通道上的均值(0.5,0.5,0.5),三个通道的标准差(0.5, 0.5, 0.5)
transform = transforms.Compose(
[transforms.ToTensor(),
transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])
# 下载数据集,并将其保存在当前文件夹下的CIFAR文件夹中,并对其进行transform变换
trainset = datasets.CIFAR10(root='./CIFAR', train=True, download=True, transform=transform)
testset = datasets.CIFAR10(root='./CIFAR', train=False, download=True, transform=transform)
# 打印出数据和标签的维度, 标签是list,需要将其转换成numpy数组才能查看其维度
print("train:", trainset.data.shape)
print("train_label:", np.array(trainset.targets).shape)
print("test:", testset.data.shape)
print("test_label:", np.array(testset.targets).shape)
# 数据加载,
trainloader = DataLoader(trainset, batch_size=32,shuffle=True, num_workers=2)
testloader = DataLoader(testset, batch_size=32, shuffle=False, num_workers=2)
# 类别信息也是需要我们给定的
classes = ('plane', 'car', 'bird', 'cat','deer', 'dog', 'frog', 'horse', 'ship', 'truck')
if __name__ == '__main__':
print("训练集长度", len(trainset))
print("测试集集长度", len(testset))
# 每训练完一个epoch,需要训练多少个批次
print("训练集的总批次数", len(trainloader))
print("测试集的总批次数", len(testloader))
from torchvision import datasets, transforms
from torch.utils.data import DataLoader
import numpy as np
# transforms模块中的Compose( )可以把多个变换组合在一起,需要写成列表的形式
# 将PIL图片或者numpy.ndarray(HxWxC) (范围在0-255) 转成torch.FloatTensor (CxHxW) (范围为0.0-1.0)
# 归一化,RGB三个通道上的均值(0.5,0.5,0.5),三个通道的标准差(0.5, 0.5, 0.5)
transform = transforms.Compose(
[transforms.ToTensor(),
transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])
# 下载数据集,并将其保存在当前文件夹下的CIFAR文件夹中,并对其进行transform变换
trainset = datasets.CIFAR10(root='./CIFAR', train=True, download=True, transform=transform)
testset = datasets.CIFAR10(root='./CIFAR', train=False, download=True, transform=transform)
# 打印出数据和标签的维度, 标签是list,需要将其转换成numpy数组才能查看其维度
print("train:", trainset.data.shape)
print("train_label:", np.array(trainset.targets).shape)
print("test:", testset.data.shape)
print("test_label:", np.array(testset.targets).shape)
# 数据加载,
trainloader = DataLoader(trainset, batch_size=32,shuffle=True, num_workers=2)
testloader = DataLoader(testset, batch_size=32, shuffle=False, num_workers=2)
# 类别信息也是需要我们给定的
classes = ('plane', 'car', 'bird', 'cat','deer', 'dog', 'frog', 'horse', 'ship', 'truck')
if __name__ == '__main__':
print("训练集长度", len(trainset))
print("测试集集长度", len(testset))
# 每训练完一个epoch,需要训练多少个批次
print("训练集的总批次数", len(trainloader))
print("测试集的总批次数", len(testloader))
# 结果
Files already downloaded and verified
Files already downloaded and verified
train: (50000, 32, 32, 3)
train_label: (50000,)
test: (10000, 32, 32, 3)
test_label: (10000,)
训练集长度 50000
测试集集长度 10000
训练集的总批次数 1563
测试集的总批次数 313
网络模型搭建
model.py
import torch
import torch.nn as nn
import torch.nn.functional as F
class Net(nn.Module): # 我们定义网络时一般是继承的torch.nn.Module创建新的子类
def __init__(self):
super(Net, self).__init__()
self.conv1 = nn.Conv2d(in_channels=3, out_channels=6, kernel_size=5) # 卷积层,默认padding=0,stride=1
self.pool = nn.MaxPool2d(kernel_size=2, stride=2) # 最大池化层
self.conv2 = nn.Conv2d(6, 16, 5) # 卷积层
self.fc1 = nn.Linear(16 * 5 * 5, 120) # 全连接层
self.fc2 = nn.Linear(120, 84)
self.fc3 = nn.Linear(84, 10)
def forward(self, x): # 前向传播,反向传播涉及到torch.autograd模块
x = self.pool(F.relu(self.conv1(x))) # F是torch.nn.functional的别名,这里调用了relu函数 F.relu()
x = self.pool(F.relu(self.conv2(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
if __name__ == '__main__':
model = Net()
print(model)
input = torch.randn(1,3,32,32)
output = model(input)
print("input", input.shape)
print("output", output.shape)
训练并测试
import torch
from torch.autograd import Variable
# 导入
from dataset import trainloader,testloader,trainset,testset
from model import Net
# 定义超参数
learning_rate = 1e-3
momentum = 0.9
num_epoches = 20
batch_size = 32
model = Net()
#判断是否有GPU, 如果使用GPU就将模型放到GPU中进行训练
use_gpu = torch.cuda.is_available()
if use_gpu:
model.cuda()
print(model)
# 交叉熵损失函数
criterion = torch.nn.CrossEntropyLoss()
# SGD梯度下降方法
optimizer = torch.optim.SGD(model.parameters(), lr=learning_rate, momentum=momentum)
# 训练
for epoch in range(num_epoches):
print("*"*10)
print("epoch{}".format(epoch+1))
train_loss = 0.0
train_acc =0.0
for i,data in enumerate(trainloader,0): # enumerate是python的内置函数,既获得索引也获得数据
#for image, label in trainloader:
image, label = data # data包含数据和标签信息
# 将数据转换成Variable
if use_gpu:
image = Variable(image).cuda()
label = Variable(label).cuda()
else:
image = Variable(image)
label = Variable(label)
# 要把梯度重新归零,因为反向传播过程中梯度会累加上一次循环的梯度
optimizer.zero_grad()
output = model(image)
loss = criterion(output, label)
train_loss += loss.item()*label.size(0) # 此处是什么意思?
# train_loss += loss.item() 也可以
_, pred = torch.max(output,1) # 返回每行的最大值的索引值,也就是预测出的可能性最大的类别的相应的标签
train_correct = (pred == label).sum()
train_acc += train_correct.item()
loss.backward() # 反向传播
optimizer.step() # 梯度更新
# 每训练完一个周期打印一次平均损失和平均精度
print('Finish {} epoch, Loss: {:.6f}, Acc: {:.6f}'.format(
epoch + 1, train_loss / (len(trainset)), train_acc / (len(trainset))
))
# 测试
model.eval()
eval_loss = 0.0
eval_acc = 0.0
for image, label in testloader:
if use_gpu:
image = Variable(image).cuda()
label = Variable(label).cuda()
else:
image = Variable(image)
label = Variable(label)
output = model(image)
loss = criterion(output, label)
eval_loss += loss.item()*label.size(0) # 此处是什么意思?
_, predicted = torch.max(output, 1)
eval_correct = (predicted == label).sum()
eval_acc += eval_correct.item()
print("Test Loss: {:.6f}, Acc: {:.6f}".format(
eval_loss/len(testset), eval_acc/len(testset)
))
参考文献
Pytorch打怪路(一)pytorch进行CIFAR-10分类(1)CIFAR-10数据加载和处理
Pytorch打怪路(一)pytorch进行CIFAR-10分类(2)定义卷积神经网络
Pytorch打怪路(一)pytorch进行CIFAR-10分类(3)定义损失函数和优化器
Pytorch打怪路(一)pytorch进行CIFAR-10分类(4)训练
Pytorch打怪路(一)pytorch进行CIFAR-10分类(5)测试