数据下载和预处理是机器学习、深度学习实际项目中耗时又重要的任务,尤其是数据预处理,关系到数据质量和模型性能,往往要占据项目的大部分时间。PyTorch提供了专门的数据下载,数据处理包,可以极大提高开发效率及数据质量。
数据处理工具箱概述
torch.utils.data工具包:
- Dataset:一个抽象类,其他数据集需要继承这个类,并且覆盖其中的两个方法(__getitem__、__len__)。
- DataLoader:定义一个新的迭代器,实现批量(batch)读取,打乱数据(shuffle)并提供并行加速等功能。
- random_split:把数据集随机拆分为给定长度的非重叠新数据集。
- *Sampler:多种采样函数。
PyTorch可视化处理工具(torchvision),它是一个视觉处理工具包,独立于PyTorch,需要另外安装(pip install torchvision 或 conda install torchvision)。
torchvision工具包:
datasets:提供常用的数据集加载,设计上都是继承torch.utils.data.Dataset,主要包括 MNIST、CIFAR10/100、ImageNet、COCO等。
models:提供深度学习中各种经典的网络结构以及训练好的模型(如果要选择训练好的模型,则设置pretrained=True),包括AlexNet、VGG系列、ResNet系列、Inception系列等。
transforms:常用的数据预处理操作,主要包括对Tensor及PIL Image对象的操作。
utils:包含两个函数,一个是make_grid(将多张图像拼接在一个网格中),另一个是save_img(将Tensor保存为图像)。
utils.data
utils.data包括Dataset和DataLoader。torch.utils.data.Dataset为抽象类,自定义数据集需要继承这个类,并实现两个函数,即__len__和__getitem__。前者提供数据的大小(size),后者通过给定索引获取数据、标签或一个样本。__getitem__一次只能获取一个样本,所以通过torch.utils.data.DataLoader来定义一个新的迭代器,实现批量获取数据。
# 1) 导入需要的模块
import torch
from torch.utils import data
import numpy as np
# 定义获取数据集的类
# 该类继承基类Dataset,自定义一个数据集及对应标签
class TestDataset(data.Dataset):
def __init__(self):
# 一些由二维向量表示的数据集
# np.asarray():把输入数据转换为数组
self.Data = np.asarray([[1, 2], [3, 4], [2, 1], [3, 4], [4, 5]])
# 这是数据集对应的标签
self.Label = np.asarray([0, 1, 0, 1, 2])
def __getitem__(self, index):
# 把numpy转换为Tensor
txt = torch.from_numpy(self.Data[index])
label = torch.tensor(self.Label[index])
return txt, label
def __len__(self):
return len(self.Data)
# 获取数据集中的数据
Test = TestDataset()
# 调用 def __getitem__(self. index)
# 获取下标为2的数据值
print("*" * 10 + " Test[2] " + "*" * 10)
print(Test[2])
# 调用 __len__(self)
print("*" * 10 + " Test.__len__() " + "*" * 10)
print(Test.__len__())
运行结果:
Dataset只负责数据的抽取,一次调用__getitem__只返回一个样本。如果希望批量处理,同时进行数据打乱和并行加速等操作,可选择DataLoader。
torch.utils.data.DataLoader(
dataset, # 加载的数据集
batch_size=1, # 批大小
shuffle=False, # 是否将数据打乱
sampler=None, # 样本抽样
batch_sampler=None, # 样本抽样的批大小
num_workers=0, # 使用多进程加载的进程数,0代表不使用多进程。
collate_fn=<function default_collate at 0x7f108ee01620>, #如何将多个样本数据拼接成一个批量,一般使用默认的拼接方式即可。
pin_memory=False, # 是否将数据保存在锁页内存(pin_memory)区,pin_memory中的数据转到GPU会快一些。
drop_last=False, # dataset中的数据个数可能不是batch_size的整数倍,将drop_last设置为True时会将多出来且不足一个批量的数据丢弃。
timeout=0, # 设置数据读取的超时时间
worker_init_fn=None
)
# 1) 导入需要的模块
import torch
from torch.utils import data
import numpy as np
# 定义获取数据集的类
# 该类继承基类Dataset,自定义一个数据集及对应标签
class TestDataset(data.Dataset):
def __init__(self):
# 一些由二维向量表示的数据集
# np.asarray():把输入数据转换为数组
self.Data = np.asarray([[1, 2], [3, 4], [2, 1], [3, 4], [4, 5]])
# 这是数据集对应的标签
self.Label = np.asarray([0, 1, 0, 1, 2])
def __getitem__(self, index):
# 把numpy转换为Tensor
txt = torch.from_numpy(self.Data[index])
label = torch.tensor(self.Label[index])
return txt, label
def __len__(self):
return len(self.Data)
# 获取数据集中的数据
Test = TestDataset()
# 批量读取数据
# 打包数据(数据集:Test,批大小:2个数据,不打乱顺序)
test_loader = torch.utils.data.DataLoader(Test, batch_size=2, shuffle=False)
# 转换为迭代器,方便使用
dataiter = iter(test_loader)
# enumerate():将一个可遍历的数据对象组合为一个索引序列,同时列出数据和数据下标。
for i, traindata in enumerate(test_loader):
# 打印打包后的索引值
print('i:', i)
# 输出打包后的 数据集 和 其所对应的标签
Data, Label = traindata
print('data:', Data)
print('Label:', Label)
imgs, labels = next(dataiter)
print(imgs)
print(labels)
运行结果:
torchvision
torchvision有4个功能模块,model、datasets、transforms和utils。可以利用datasets下载一些经典数据集。
transforms
transforms提供了对PIL Image对象和Tensor对象的常用操作:
1)对PIL Image的常见操作如下:
Scale/Resize:调整尺寸,长宽比保持不变。
CenterCrop、RandomCrop、RandomResizedCrop:裁剪图像。 CenterCrop和RandomCrop表示裁剪为固定大小。RandomResizedCrop表示裁剪为随机大小。
Pad:填充
ToTensor:把一个取值范围是[0, 255]的PIL.Image转换成Tensor。如把形状为(H, W, C)的numpy.ndarray转换成形状为[C, H, W], 取值范围是[0, 1.0]的torch.FloatTensor。
RandomHorizontalFlip:图像随机水平翻转,翻转概率为0.5。
RandomVerticalFlip:图像随机垂直翻转。
ColorJitter:修改亮度、对比度和饱和度。
2)对Tensor的常见操作如下:
Normalize:标准化,即减均值,除以标准差。
ToPILImage:将Tensor转换PIL Image。
如果要对数据集进行多个操作,可通过Compose将这些操作像管道一样拼接起来,类似于nn.Sequential。
transforms.Compose([
# 将给定的 PIL.Image 进行中心切割,得到给定的size,可以是元组,如(target_height, target_width)。也可以是一个整型,在这种情况下,切出来的图像形状是正方形。
transforms.RandomCrop(10),
# 随机选取切割中心点的位置
transforms.RandomCrop(20, padding=0),
# 把一个取值范围是[0, 255]的PIL.Image 或者 形状为(H, W, C)的numpy.ndarray,转换成形状为(C, H, W),取值范围是[0, 1]的torch.FloatTensor
transforms.ToTensor(),
# 规范化到[-1, 1]
transforms.Normalize(mean=(0.5, 0.5, 0.5), std=(0.5, 0.5, 0.5))
])
ImageFolder
文件依据标签处于不同文件下时:
使用torchvision.datasets.ImageFolder直接构造出dataset
dataset = datasets.ImageFolder(path)
loader = data.DataLoader(dataset)
ImageFolder会将目录中的文件夹名自动转化成序列,那么载入DataLoader时,标签自动就是整数序列了。
# 导入需要的模块
from torchvision import transforms, utils
from torchvision import datasets
import torch
import matplotlib.pyplot as plt
# 使用transforms进行图像预处理,使用Compose把预处理的多个操作拼接在一起。
my_trans = transforms.Compose([
# RandomResizedCrop:裁剪为随机大小
transforms.RandomResizedCrop(224),
# 图像随机水平翻转,翻转概率为0.5
transforms.RandomHorizontalFlip(),
# 把一个取值范围是[0, 255]的PIL.Image转换成Tensor
transforms.ToTensor()
])
# 使用 torchvision.datasets.ImageFolder来直接构造出dataset
# 需要修改为存放图片的文件路径
train_data = datasets.ImageFolder('./data/torchvision_data/torchvision_data', transform=my_trans)
# 使用DataLoader()加载数据:数据集:train_data,批量大小:batch_size,打乱:是
train_loader = torch.utils.data.DataLoader(train_data, batch_size=8, shuffle=True)
# enumerate():将一个可遍历的数据对象组合为一个索引序列,同时列出数据和数据所在的文件件的下标
for i_batch, img in enumerate(train_loader):
# 输出打包后的图像(enumerate后就会有图像的具体信息和图像所在的文件夹的下标)
print("*" * 10 + "img" + "*" * 10)
print(img)
# 输出打包后的图像的具体信息(三个二维数组 R G B)
print("*" * 10 + "img[0]" + "*" * 10)
print(img[0])
# 输出打包后的图像具体来自哪个文件夹(从0开始编号)
print("*" * 10 + "img[1]" + "*" * 10)
print(img[1])
# 只列出第1批打包的数据
if i_batch == 0:
# 输出打包后的图像具体来自哪个文件夹(从0开始编号)
print(img[1])
# make-grid:Make a grid of images,把图像按照网格排列的方式绘制出来。
# img[0]:图像的具体信息(三个二维数组 R G B )
grid = utils.make_grid(img[0])
print("*" * 10 + "type(grid)" + "*" * 10)
print(type(grid))
# grid.numpy()转化为numpy数据类型。
# transpose:将x、y、z轴移位。(0, 1, 2) --(x, y, z) ==> (1, 2, 0) -- (y, z, x)
plt.imshow(grid.numpy().transpose((1, 2, 0)))
# 显示图像
plt.show()
# save_image():直接把tensor保存为图片(一般来说,需要将tensor转变为numpy类型的数组,再保存图片)
utils.save_image(grid, 'test01.png')
break
from PIL import Image
# Image.open():保持了图像被读取的状态,但是图像的真实数据并未被读取
img1 = Image.open("test01.png")
# 显示图像
img1.show()
运行结果:
可视化工具
TensorBoard是Google TensorFlow的可视化工具,可以记录训练数据、评估数据、网络结构、图像等,并且可以在Web上展示,对于观察神经网络训练过程非常有帮助。PyTorch支持tensorboard_logger、visdom等可视化工具。
TensorBoard简介
TensorBoard功能很强大,支持scalar、image、figure、histogram、audio、text、graph、onnx_graph、embedding、pr_curve、videosummaries等可视化方式。
使用TensorBoard的一般步骤如下:
1) 导入TensorBoard,实例化SummaryWriter类,指明记录日志路径等信息。
from torch.utils.tensorboard import SummaryWriter
# 实例化SummaryWriter,并指明日志存放路径。如果当前目录没有logs目录,则自动创建。
writer = SummaryWriter(log_dir='logs')
# 调用实例
writer.add_xxx()
# 关闭writer
writer.close()
【说明】
1、其中logs是指生成日志文件路径,如果是在Windows环境下,需要注意其logs路径格式与Linux环境不同,需要使用 转义字符 或者 在字符串前加r,如
writer = SummaryWriter(log_dir=r'D:\myboard\test\logs')
2、SummaryWriter的格式为:
SummaryWriter(log_dir=None, comment='', **kwargs)
其中comment表示在文件命名后加上comment后缀。
3、如果不写log_dir,系统将在当前目录创建一个runs目录。
2)调用相应的API,接口的一般格式为:
add_xxx(tag-name, object, iteration-number)
# add_xxx(标签, 记录的对象, 迭代次数)
3) 启动TensorBoard服务。
使用cd命令到logs目录所在的同级目录,在命令行输入如下命令,logdir等式右边可以是相对路径或绝对路径。
tensorboard --logdir=logs --port 6006
# 如果是Windows环境,要注意路径解析,如下:
# tensorboard --logdir=r'D:\myboard\test\logs' --port 6006
4) Web展示。
在浏览器输入:
http://服务器IP或名称:6006
# 如果是本机,服务器名称可以使用localhost
查看logs目录保存的各种图形。
使用TensorBoard可视化神经网络
# 可视化工具:TensorBoard
# 1) 导入需要的模块
import torch
import torch.nn as nn
import torch.nn.functional as F
import torchvision
from torch.utils.tensorboard import SummaryWriter
# 2) 构建神经网络
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
# 5 * 5 卷积层
self.conv1 = nn.Conv2d(1, 10, kernel_size=5)
# 5 * 5 卷积层
self.conv2 = nn.Conv2d(10, 20, kernel_size=5)
# Dropout2d(p=0.5):第二个维度(从左向右数第二个)的数据有p=0.5的概率被置为0,而且第二个维度(从左向右数第二个)的数据有p=0.5的概率被缩放,缩放比例:1/(1-p)
self.conv2_drop = nn.Dropout2d()
# 全连接层
self.fc1 = nn.Linear(320, 50)
# 全连接层
self.fc2 = nn.Linear(50, 10)
# 批量归一化层
self.bn = nn.BatchNorm2d(20)
def forward(self, x):
# 输入:self.conv1(x), kernel_size=2
x = F.max_pool2d(self.conv1(x), 2)
# 激活函数ReLU()
x = F.relu(x) + F.relu(-x)
# 激活函数ReLU(最大池化层(卷积层,kernel_size=2))
x = F.relu(F.max_pool2d(self.conv2_drop(self.conv2(x)), 2))
# 批量归一化层
x = self.bn(x)
# view():修改Tensor,列值固定,行值自动变化。
x = x.view(-1, 320)
# 激活函数ReLU(全连接层)
x = F.relu(self.fc1(x))
# 在调用 F.dropout() 时,将self.training传入函数,就可以在训练时应用dropout,测试时关闭dropout
x = F.dropout(x, training=self.training)
# 全连接层
x = self.fc2(x)
# 激活函数 F.softmax()
x = F.softmax(x, dim=1)
return x
3) 把模型保存为图像
# 定义输入
# 生成[0, 1),形状为 32*1*28*28 的均匀分布
input = torch.rand(32, 1, 28, 28)
# 实例化网络
model = Net()
# 将模型保存为图像
# 实例化SummaryWriter,并指明日志存放路径。如果当前目录没有logs目录,则自动创建。
with SummaryWriter(log_dir='./logs/log', comment='Net') as w:
# 调用实例
w.add_graph(model, (input, ))
# 关闭writer
w.close()
运行结果:
需要先进入test虚拟环境:
使用TensorBoard可视化损失值
可视化损失值需要用到add_scalar()函数,这里利用一层全连接神经网络,训练一元二次函数的参数。
# 使用TensorBoard可视化损失值
# 导入需要的模块
import numpy as np
import torch
from torch import nn
from torch.utils.tensorboard import SummaryWriter
# 学习率
learning_rate = 0.001
# 训练轮数
num_epoches = 60
# 数据类型
dtype = torch.FloatTensor
# 实例化SummaryWriter,并指明日志存放路径。如果当前目录没有logs目录,则自动创建。
writer = SummaryWriter(log_dir='logs', comment='Linear')
# 随机数种子
np.random.seed(100)
# 生成x的训练数据,[-1, 1]分为100分,形状为100×1
x_train = np.linspace(-1, 1, 100).reshape(100, 1)
# 生成目标数据y_train = 3 * x_train^2 + 2 + 噪声数据
y_train = 3*np.power(x_train, 2) + 2 + 0.2*np.random.rand(x_train.size).reshape(100, 1)
# 输入大小
input_size = 1
output_size = 1
# 构建网络
model = nn.Linear(input_size, output_size)
# 定义损失函数
criterion = nn.MSELoss()
# 定义优化器
optimizer = torch.optim.SGD(model.parameters(), lr=learning_rate)
# 循环训练数据
for epoch in range(num_epoches):
# 转换数据类型
inputs = torch.from_numpy(x_train).type(dtype)
# 转换数据类型
targets = torch.from_numpy(y_train).type(dtype)
# 实例化网络
output = model(inputs)
# 计算loss
loss = criterion(output, targets)
# 梯度清零
optimizer.zero_grad()
# 反向传播:求偏导
loss.backward()
# 更新梯度
optimizer.step()
# 保存 epoch 和 loss 数值
writer.add_scalar('训练损失值', loss, epoch)
# 关闭writer
writer.close()
运行结果
需要先进入test虚拟环境:
使用TensorBoard可视化特征图
不同卷积层的特征图的抽取程度是不一样的。
# 导入需要的模块
import torch
import torchvision.utils as vutils
from torch import nn
from torch.utils.tensorboard import SummaryWriter
import torch.nn.functional as F
import torchvision
import torchvision.transforms as transforms
from torch.utils.data import DataLoader
# 定义预处理函数
transform = transforms.Compose([transforms.ToTensor(), transforms.Grayscale(num_output_channels=1)])
# 下载数据,并对数据进行预处理
train_data = torchvision.datasets.CIFAR10("./mydata", train=True, transform=transform, download=True)
test_data = torchvision.datasets.CIFAR10("./mydata", train=False, transform=transform, download=True)
# 得到一个生成器
train_loader = DataLoader(train_data, batch_size=64, shuffle=False)
test_loader = DataLoader(test_data, batch_size=128, shuffle=False)
# print("*" * 10 + " train_loader " + "*" * 10)
# print(torch.is_tensor(train_loader))
# 检测是否有可用的GPU,有则使用GPU,否则使用CPU
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
# 构建神经网络
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
# 5 * 5 卷积层
self.conv1 = nn.Conv2d(1, 10, kernel_size=5)
# 5 * 5 卷积层
self.conv2 = nn.Conv2d(10, 20, kernel_size=5)
# Dropout2d(p=0.5):第二个维度(从左向右数第二个)的数据有p=0.5的概率被置为0,而且第二个维度(从左向右数第二个)的数据有p=0.5的概率被缩放,缩放比例:1/(1-p)
self.conv2_drop = nn.Dropout2d()
# 全连接层
self.fc1 = nn.Linear(11520, 50)
# 全连接层
self.fc2 = nn.Linear(50, 10)
# 批量归一化层
self.bn = nn.BatchNorm1d(10)
def forward(self, x):
# 输入:self.conv1(x), kernel_size=2
x = F.max_pool2d(self.conv1(x), 2)
# 激活函数ReLU()
x = F.relu(x) + F.relu(-x)
# 激活函数ReLU(最大池化层(卷积层,kernel_size=2))
x = F.relu(F.max_pool2d(self.conv2_drop(self.conv2(x)), 2))
# 批量归一化层
x = self.bn(x)
# view():修改Tensor,列值固定,行值自动变化。
x = x.view(-1, 320)
# 激活函数ReLU(全连接层)
x = F.relu(self.fc1(x))
# 在调用 F.dropout() 时,将self.training传入函数,就可以在训练时应用dropout,测试时关闭dropout
x = F.dropout(x, training=self.training)
# 全连接层
x = self.fc2(x)
# 激活函数 F.softmax()
x = F.softmax(x, dim=1)
return x
# x = torch.tensor(train_loader)
#
# print(torch.is_tensor(x))
# 实例化网络
net = Net()
# 桨模型改为预测模式
net.eval()
# 实例化SummaryWriter,并指明日志存放路径。如果当前目录没有logs目录,则自动创建。
writer = SummaryWriter(log_dir='logs', comment='feature map')
# enumerate()函数用于将一个可遍历的数据对象(如列表、元组或字符串)组合为一个索引序列,同时列出数据和数据下标,一般用在for循环中。
# enumerate(sequence, [start = 0]):数据下标默认是从0开始。
for i, data in enumerate(train_loader, 0):
# 获取训练数据
inputs, labels = data
inputs, labels = inputs.to(device), labels.to(device)
# unsqueeze(0):将数据下标为0的数据增加一个维度
x = inputs[0].unsqueeze(0)
# torchvision.utils.make_grid(tensor, nrow=8, padding=2, normalize=False, range=None, scale_each=False, pad_value=8):将显示的图像网格化处理
# tensor:输入张量 nrow:每行显示的图像数量,默认为8 padding:每个图像之间的像素填充,默认为2
# normalize:是否将图像像素值归一化到[0,1]的范围。 range:将图像像素值缩放到指定范围(min, max)
# scale_each:是否将每个图像的像素值独立缩放。 pad_value:填充像素的值
img_grid = vutils.make_grid(x, normalize=True, scale_each=True, nrow=2)
print("*" * 10 + " x " + "*" * 10)
print(x)
break
# 神经网络实例化对象名._modules.items():包含模型所有子模块的迭代器,用于访问模型中的每个子模块。
# _modules:是一个有序字典,键是子模块的名称,值是子模块的对象。
# name:子模块名称 layer:子模块的实例对象
for name, layer in net._modules.items():
# x.size()[0]:0代表输出第一个维度的纬度值,1代表输出第二个维度的纬度值,2代表输出第三个维度的纬度值
# x.size(0):代表输出第一个维度的纬度值,1代表输出第二个维度的纬度值,2代表输出第三个维度的纬度值
#
# x.view(x.size(0), -1):将多维度的tensor展平成一维。
# 这里的x(神经网络的第一个层)是4维的(batchsize, channels, x, y),x.size(0)代表batchsize的值,-1表示自适应调整剩余的维度。
# x.view(x.size(0), -1) 将tensor的结构转换为(batchsize, channels*x*y),即将(channels, x, y)拉直
x = x.view(x.size(0), -1) if "fc" in name else x
print(x.size())
# 正向传播
x = layer(x)
# f'{替换变量} 与str.format() 相比,不许要提供格式化信息
print(f'{name}')
# 查看卷积层的特征图:layer 与 conv 在 name 中
if 'layer' in name or 'conv' in name:
# torch.transpose():一次只能在两个维度间进行转置(可以理解为维度转换)
# torch.t():类似于求矩阵转置的函数,要求输入的tensor的结构维度<=2维,如果是0维或者2维,则输出本身,2维输出维度的转置。
x1 = x.transpose(0, 1)
# 将显示的图像网格化处理,每行显示的图像数量为4
img_grid = vutils.make_grid(x1, normalize=True, scale_each=True, nrow=4)
# 调用实例,添加图片
writer.add_image(f'{name}_feature_maps', img_grid, global_step=0)
# 关闭writer
writer.close()
运行结果
需要先进入test虚拟环境:
小结
介绍了PyTorch有关数据下载、预处理方面的一些常用包,以及可视化工具——TensorBoard,并通过一些实例详细说明如何使用这些包或工具。
torch.utils.data.DataLoader(
dataset, # 加载的数据集
batch_size=1, # 批大小
shuffle=False, # 是否将数据打乱
sampler=None, # 样本抽样
batch_sampler=None, # 样本抽样的批大小
num_workers=0, # 使用多进程加载的进程数,0代表不使用多进程。
collate_fn=<function default_collate at 0x7f108ee01620>, #如何将多个样本数据拼接成一个批量,一般使用默认的拼接方式即可。
pin_memory=False, # 是否将数据保存在锁页内存(pin_memory)区,pin_memory中的数据转到GPU会快一些。
drop_last=False, # dataset中的数据个数可能不是batch_size的整数倍,将drop_last设置为True时会将多出来且不足一个批量的数据丢弃。
timeout=0, # 设置数据读取的超时时间
worker_init_fn=None
)
标签:__,self,torch,PyTorch,import,新手,工具箱,数据,size From: https://blog.51cto.com/u_16093693/7192923