PyTorch图像分类实例

2020-10-5 23:47:13 来源: https://zhuanlan.zhihu.com/p/39667289 发布人:

在使用PyTorch的时候会用到两个包,一个是torch,一个是torchvision。其中torch是关于运算的包,torchvision则是打包了一些数据集,另外用torch实现了一些常见的神经网络模型,比如ResNet。

使用CIFAR-10作为数据集,包含了10个类别60000张图片,每张图片的大小为32x32,其中训练图片50000张,测试图片10000张。

torchvision中已经打包好了这个数据集,不用自己下载,直接如下调用就可以了。

import torch
import torch.nn as nn
import torch.nn.functional as F
import torchvision
import torchvision.transforms as transforms
# cifar-10官方提供的数据集是用numpy array存储的
# 下面这个transform会把numpy array变成torch tensor,然后把rgb值归一到[0, 1]这个区间
transform = transforms.Compose(
[transforms.ToTensor(),
transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])

# 在构建数据集的时候指定transform,就会应用我们定义好的transform
# root是存储数据的文件夹,download=True指定如果数据不存在先下载数据
cifar_train = torchvision.datasets.CIFAR10(root='./data', train=True,
download=True, transform=transform)
cifar_test = torchvision.datasets.CIFAR10(root='./data', train=False,
transform=transform)

load完了之后我们看看这两个数据集的信息

print(cifar_train)
print(cifar_test)

在训练的时候可以自己写代码手动遍历数据集,指定batch和遍历方法,

不过PyTorch提供了一个DataLoader类来方便我们完成这些操作。

trainloader = torch.utils.data.DataLoader(cifar_train, batch_size=32, shuffle=True)
testloader = torch.utils.data.DataLoader(cifar_test, batch_size=32, shuffle=True)


现在我们来定义卷积神经网络,为了简单起见,我们使用经典的LeNet,它包含两个卷积层和三个全连接层

在PyTorch中定义神经网络非常简单,第一步先是继承nn.module这个类,然后定义如下两个函数,一般的网络这样操作就足够了

class LeNet(nn.Module):
# 一般在__init__中定义网络需要的操作算子,比如卷积、全连接算子等等
def __init__(self):
super(LeNet, self).__init__()
# Conv2d的第一个参数是输入的channel数量,第二个是输出的channel数量,第三个是kernel size
self.conv1 = nn.Conv2d(3, 6, 5)
self.conv2 = nn.Conv2d(6, 16, 5)
# 由于上一层有16个channel输出,每个feature map大小为5*5,所以全连接层的输入是16*5*5
self.fc1 = nn.Linear(16*5*5, 120)
self.fc2 = nn.Linear(120, 84)
# 最终有10类,所以最后一个全连接层输出数量是10
self.fc3 = nn.Linear(84, 10)
self.pool = nn.MaxPool2d(2, 2)
# forward这个函数定义了前向传播的运算,只需要像写普通的python算数运算那样就可以了
def forward(self, x):
x = F.relu(self.conv1(x))
x = self.pool(x)
x = F.relu(self.conv2(x))
x = self.pool(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

PyTorch具有自动求导功能,我们不需要自己写backward函数,所以很直观方便,写神经网络的结构就像写普通的数学运算公式一样。定义好网络之后我们就可以训练了,训练的代码也非常简单。

首先,我们先构建一个网络实例。由于需要用到GPU,所以先获取device,然后再把网络的参数复制到GPU上

# 如果你没有GPU,那么可以忽略device相关的代码
device = torch.device("cuda:0")
net = LeNet().to(device)

然后我们需要定义Loss函数和优化方法,最简单的就是使用SGD了。PyTorch都预先定义好了这些东西

# optim中定义了各种各样的优化方法,包括SGD
import torch.optim as optim

# CrossEntropyLoss就是我们需要的损失函数
criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9)

下面我们正式开始训练

print("Start Training...")
for epoch in range(30):
# 我们用一个变量来记录每100个batch的平均loss
loss100 = 0.0
# 我们的dataloader派上了用场
for i, data in enumerate(trainloader):
inputs, labels = data
inputs, labels = inputs.to(device), labels.to(device) # 注意需要复制到GPU
optimizer.zero_grad()
outputs = net(inputs)
loss = criterion(outputs, labels)
loss.backward()
optimizer.step()
loss100 += loss.item()
if i % 100 == 99:
print('[Epoch %d, Batch %5d] loss: %.3f' %
(epoch + 1, i + 1, loss100 / 100))
loss100 = 0.0

print("Done Training!")


以上代码中,核心的代码是下面五行,用注释解释了每一行的作用

# 首先要把梯度清零,不然PyTorch每次计算梯度会累加,不清零的话第二次算的梯度等于第一次加第二次的       
optimizer.zero_grad()
# 计算前向传播的输出
outputs = net(inputs)
# 根据输出计算loss
loss = criterion(outputs, labels)
# 算完loss之后进行反向梯度传播,这个过程之后梯度会记录在变量中
loss.backward()
# 用计算的梯度去做优化
optimizer.step()

ok,训练完了之后我们来检测一下准确率,我们用训练好的模型来预测test数据集

# 构造测试的dataloader
dataiter = iter(testloader)
# 预测正确的数量和总数量
correct = 0
total = 0
# 使用torch.no_grad的话在前向传播中不记录梯度,节省内存
with torch.no_grad():
for data in testloader:
images, labels = data
images, labels = images.to(device), labels.to(device)
# 预测
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))

在我这里最终训练了30个epoch之后准确率为63%。

以上就是用PyTroch做图像分类的基本步骤。



完整可运行代码:

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

#我们使用CIFAR-10作为数据集,包含了10个类别60000张图片,每张图片的大小为32x32,其中训练图片50000张,测试图片10000张。
# cifar-10官方提供的数据集是用numpy array存储的
# 下面这个transform会把numpy array变成torch tensor,然后把rgb值归一到[0, 1]这个区间
transform = transforms.Compose(
[transforms.ToTensor(),
transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])

# 在构建数据集的时候指定transform,就会应用我们定义好的transform
# root是存储数据的文件夹,download=True指定如果数据不存在先下载数据
cifar_train = torchvision.datasets.CIFAR10(root='./data', train=True,
download=True, transform=transform)
cifar_test = torchvision.datasets.CIFAR10(root='./data', train=False,
transform=transform)

# 数据其实是用numpy array存储的,label是个list
print(cifar_train)
print(cifar_test)


trainloader = torch.utils.data.DataLoader(cifar_train, batch_size=32, shuffle=True)
testloader = torch.utils.data.DataLoader(cifar_test, batch_size=32, shuffle=True)


class LeNet(nn.Module):
# 一般在__init__中定义网络需要的操作算子,比如卷积、全连接算子等等
def __init__(self):
super(LeNet, self).__init__()
# Conv2d的第一个参数是输入的channel数量,第二个是输出的channel数量,第三个是kernel size
self.conv1 = nn.Conv2d(3, 6, 5)
self.conv2 = nn.Conv2d(6, 16, 5)
# 由于上一层有16个channel输出,每个feature map大小为5*5,所以全连接层的输入是16*5*5
self.fc1 = nn.Linear(16*5*5, 120)
self.fc2 = nn.Linear(120, 84)
# 最终有10类,所以最后一个全连接层输出数量是10
self.fc3 = nn.Linear(84, 10)
self.pool = nn.MaxPool2d(2, 2)
# forward这个函数定义了前向传播的运算,只需要像写普通的python算数运算那样就可以了
def forward(self, x):
x = F.relu(self.conv1(x))
x = self.pool(x)
x = F.relu(self.conv2(x))
x = self.pool(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

# 如果你没有GPU,那么可以忽略device相关的代码
device = torch.device("cuda:0")
net = LeNet().to(device)

# optim中定义了各种各样的优化方法,包括SGD
import torch.optim as optim

# CrossEntropyLoss就是我们需要的损失函数
criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9)


print("Start Training...")
for epoch in range(10): #训练历元数量 epoch 10
# 我们用一个变量来记录每100个batch的平均loss
loss100 = 0.0
# 我们的dataloader派上了用场
for i, data in enumerate(trainloader):
inputs, labels = data
inputs, labels = inputs.to(device), labels.to(device) # 注意需要复制到GPU

# 首先要把梯度清零,不然PyTorch每次计算梯度会累加,不清零的话第二次算的梯度等于第一次加第二次的
optimizer.zero_grad()
# 计算前向传播的输出
outputs = net(inputs)
# 根据输出计算loss
loss = criterion(outputs, labels)
# 算完loss之后进行反向梯度传播,这个过程之后梯度会记录在变量中
loss.backward()
# 用计算的梯度去做优化
optimizer.step()

'''
optimizer.zero_grad()
outputs = net(inputs)
loss = criterion(outputs, labels)
loss.backward()
optimizer.step()
'''
loss100 += loss.item()
if i % 100 == 99:
print('[Epoch %d, Batch %5d] loss: %.3f' %
(epoch + 1, i + 1, loss100 / 100))
loss100 = 0.0

print("Done Training!")


#----------------ok,训练完了之后我们来检测一下准确率,我们用训练好的模型来预测test数据集
# 构造测试的dataloader
dataiter = iter(testloader)
# 预测正确的数量和总数量
correct = 0
total = 0
# 使用torch.no_grad的话在前向传播中不记录梯度,节省内存
with torch.no_grad():
for data in testloader:
images, labels = data
images, labels = images.to(device), labels.to(device)
# 预测
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))







阅读次数: 477

这已是最新的一篇。

上一篇: SimpleNet.py -PyTorch从零搭建图像分类模型

尚无评论!

返回上一页面