本篇是pytorch学习笔记系列第六篇,这一系列博客应该大多会来自于开源教程书:pytorch中文手册(pytorch handbook),其github网址为:https://github.com/zergtant/pytorch-handbook 本篇博客内容来自于这一手册以及pytorch官方的教程 ,感兴趣的观众门可以去自行下载
一般情况下处理图像,文本,音频和视频数据时,可以使用标准的Python包来加载数据到一个numpy数组中. 然后把这个数组转换成 torch.*Tensor。其中:
Dataset是一个抽象类, 为了能够方便的读取,需要将要使用的数据包装为Dataset类。 自定义的Dataset需要继承它并且实现两个成员方法:
getitem() :该方法定义每次怎么获取数据
len() :该方法返回数据集的总长度
下面我们使用kaggle上的一个竞赛bluebook for bulldozers自定义一个数据集,为了方便介绍,我们使用里面的数据字典来做说明(因为条数少)
下面我们使用kaggle上的一个竞赛bluebook for bulldozers自定义一个数据集,为了方便介绍,我们使用里面的数据字典来做说明(因为条数少)
from torch.utils.data import Dataset
import pandas as pd
#定义一个数据集
class BulldozerDataset(Dataset):
""" 数据集演示 """
def __init__(self, csv_file):
"""实现初始化方法,在初始化的时候将数据读载入"""
self.df=pd.read_csv(csv_file)
def __len__(self):
'''
返回df的长度
'''
return len(self.df)
def __getitem__(self, idx):
'''
根据IDX返回一列数据
'''
return self.df.iloc[idx].SalePrice
至此,我们的数据集已经定义完成了,我们可以实例话一个对象访问他
ds_demo= BulldozerDataset('median_benchmark.csv')
len(ds_demo)#实现了__len__ 方法所以可以直接使用len获取数据总数
ds_demo[0]#用索引可以直接访问对应的数据
得到以下输出:
11573
24000.0
DataLoader为我们提供了对Dataset的读取操作,常用参数有:batch_size(每个batch的大小), shuffle(是否进行shuffle操作), num_workers(加载数据的时候使用几个子进程),下面做一个简单的操作
dl = torch.utils.data.DataLoader(ds_demo, batch_size=10, shuffle=True, num_workers=0)DataLoader #返回的是一个迭代器,我们可以使用迭代器分次获取数据
idata=iter(dl)
print(next(idata))
for i, data in enumerate(dl):
print(i,data)
#这里只循环一遍
break
输出结果:
tensor([24000., 24000., 24000., 24000., 24000., 24000., 24000., 24000., 24000.,
24000.], dtype=torch.float64)
0 tensor([24000., 24000., 24000., 24000., 24000., 24000., 24000., 24000., 24000.,
24000.], dtype=torch.float64)
可以看到:我们已经可以通过dataset定义数据集,并使用Datalorder载入和遍历数据集,
特别的,对于图像任务, pytorch创建了一个包 torchvision,它包含了处理一些基本图像数据集的方法,主要包括几个模块:图像转换器torchvision.transforms、数据集torchvision.datasets 和 数据集加载torch.utils.data.DataLoader.
transforms 模块提供了一般的图像转换操作类,用作数据的处理和增广,如下例定义了一个图像转换器:
from torchvision import transforms as transforms
transform = transforms.Compose([
transforms.RandomCrop(32, padding=4), #先四周填充0,在吧图像随机裁剪成32*32
transforms.RandomHorizontalFlip(), #图像一半的概率翻转,一半的概率不翻转
transforms.RandomRotation((-45,45)), #随机旋转
transforms.ToTensor(),
transforms.Normalize((0.4914, 0.4822, 0.4465), (0.229, 0.224, 0.225)), #R,G,B每层的归一化用到的均值和方差
])
肯定有人会问:(0.485, 0.456, 0.406), (0.2023, 0.1994, 0.2010) 这几个数字是什么意思?
官方的这个帖子有详细的说明: https://discuss.pytorch.org/t/normalization-in-the-mnist-example/457/21 这些都是根据ImageNet训练的归一化参数,可以直接使用,pytorch认为这个是固定值就可以
torchvision.datasets包包含很多数据集,可以理解为PyTorch团队自定义的dataset,这些dataset帮我们提前处理好了很多的图片数据集,我们拿来就可以直接使用:
如下例所示导入了MNIST数据集
import torchvision.datasets as datasets
trainset = datasets.MNIST(root='./data', # 表示 MNIST 数据的加载的目录
train=True, # 表示是否加载数据库的训练集,false的时候加载测试集
download=True, # 表示是否自动下载 MNIST 数据集
transform=None) # 表示是否需要对数据进行预处理,none为不进行预处理
torchvision不仅提供了常用图片数据集,还提供了训练好的模型,可以加载之后,直接使用,或者在进行迁移学习 torchvision.models模块的 子模块中包含以下模型结构。
我们直接可以使用训练好的模型,当然这个与datasets相同,都是需要从服务器下载的
如下例所示将导入resnet18这个网络模型
import torchvision.models as models
resnet18 = models.resnet18(pretrained=True)
所以说,torchvision包不仅提供了巨大的便利,也避免了代码的重复.
在这个实例中,我们使用CIFAR10数据集,它有如下10个类别:‘airplane’, ‘automobile’, ‘bird’, ‘cat’, ‘deer’, ‘dog’, ‘frog’, ‘horse’, ‘ship’, ‘truck’. CIFAR-10的图像都是 3x32x32大小的,即3颜色通道,32*32像素.
训练一个图像分类器,将依次按照下列顺序进行:
import torch
import torchvision
import torchvision.transforms as transforms
import matplotlib.pyplot as plt
import numpy as np
import torch.nn as nn
import torch.optim as optim
import time
start =time.process_time()
# 展示图像的函数
def imshow(img):
img = img / 2 + 0.5 # unnormalize
npimg = img.cpu().numpy()
plt.imshow(np.transpose(npimg, (1, 2, 0)))
plt.show()
'''1.读取数据集'''
#torchvision的输出是[0,1]的PILImage图像,我们把它转换为归一化范围为[-1, 1]的张量
transform = transforms.Compose(
[transforms.ToTensor(),
transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])
#print(transform)
#读取数据集
trainset = torchvision.datasets.CIFAR10(root='./data', train=True,download=False, 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=False, num_workers=0)
classes = ('plane', 'car', 'bird', 'cat','deer', 'dog', 'frog', 'horse', 'ship', 'truck')
'''测试数据集是否可用'''
# 获取随机数据
dataiter = iter(trainloader)
images, labels = dataiter.next()
# 展示图像
imshow(torchvision.utils.make_grid(images))
# 显示图像标签
print(' '.join('%5s' % classes[labels[j]] for j in range(4)))
# 确认我们的电脑支持 CUDA ,然后显示CUDA信息:
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
print(device)
'''2.定义卷积神经网络'''
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
self.conv1 = nn.Conv2d(3, 6, 5)
self.pool = nn.MaxPool2d(2, 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):
x = self.pool(F.relu(self.conv1(x)))
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
#获得Net变量
net = Net()
#将递归遍历所有模块并将模块的参数和缓冲区 转换成CUDA张量的:
net.to(device)
'''3.定义损失函数和优化器
使用交叉熵作为损失函数,使用带动量的随机梯度下降'''
criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9)
'''4.训练网路'''
for epoch in range(2): # 多批次循环
running_loss = 0.0
for i, data in enumerate(trainloader, 0):
# 获取输入
inputs, labels = data
# inputs 和targets 也要转换
inputs, labels = inputs.to(device), labels.to(device)
# 梯度置0
optimizer.zero_grad()
# 正向传播,反向传播,优化
outputs = net(inputs)
loss = criterion(outputs, labels)
loss.backward()
optimizer.step()
# 打印状态信息
running_loss += loss.item()
if i % 2000 == 1999: # 每2000批次打印一次
print('[%d, %5d] loss: %.3f' %
(epoch + 1, i + 1, running_loss / 2000))
running_loss = 0.0
print('Finished Training')
'''5.测试集测试网络'''
dataiter = iter(testloader)
images, labels = dataiter.next()
images, labels = images.to(device), labels.to(device)
#5.1 用几张图片来测试
# 显示图片和标签
imshow(torchvision.utils.make_grid(images))
print('GroundTruth: ', ' '.join('%5s' % classes[labels[j]] for j in range(4)))
#神经网络的预测结果
outputs = net(images)
_, predicted = torch.max(outputs, 1)
print('Predicted: ', ' '.join('%5s' % classes[predicted[j]] for j in range(4)))
#5.2 查看网络在测试集上的准确率
correct = 0
total = 0
with torch.no_grad():
for data in testloader:
images, labels = data
# inputs 和targets 也要转换
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))
#5.3 查看网络在各个类别测试的准确率
class_correct = list(0. for i in range(10))
class_total = list(0. for i in range(10))
with torch.no_grad():
for data in testloader:
images, labels = data
# inputs 和targets 也要转换
images, labels = images.to(device), labels.to(device)
outputs = net(images)
_, predicted = torch.max(outputs, 1)
c = (predicted == labels).squeeze()
for i in range(4):
label = labels[i]
class_correct[label] += c[i].item()
class_total[label] += 1
for i in range(10):
print('Accuracy of %5s : %2d %%' % (classes[i], 100 * class_correct[i] / class_total[i]))
#输出程序运行时间
end = time.process_time()
print('Running time: %s Seconds'%(end-start))
上例的代码是在GPU中运行的,若要改成CPU运行,需将其中的.to方法
去掉
如果有多个GPU,需要数据并行,则在定义网络对象后,可以使用DataParallel方法让模型并行运行在多个GPU上。
net= nn.DataParallel(net)
至此,我们已经使用pytorch完成了一个深度学习的编程全过程,进行了初步的入门:使用torchvision加载和归一化CIFAR10训练集和测试集、定义一个卷积神经网络、定义损失函数、在训练集上训练网络、在测试集上测试网络。
更加深入的的案例,比如其他神经网络、其他损失函数和优化器,将在之后的学习笔记中记录。
本篇完…
本系列已更新的学习笔记:
PyTorch学习笔记1—PyTorch简介
PyTorch学习笔记2—win10下pytorch-gpu安装以及CUDA安装记录
PyTorch学习笔记3—PyTorch深度学习入门(一)—基本方法
PyTorch学习笔记4—PyTorch深度学习入门(二)—自动求导
PyTorch学习笔记5—PyTorch深度学习入门(三)—神经网络
PyTorch学习笔记6—PyTorch深度学习入门(四)—入门实例