本文源自: http://studyai.com/pytorch-1.4/beginner/data_loading_tutorial.html

在解决任何机器学习问题时我们通常付出了很大的努力来准备数据。PyTorch提供了许多工具来简化数据加载, 并希望能够使您的代码更具可读性。在本教程中,我们将了解如何从非平凡的数据集中加载和预处理/增强数据。

要运行本教程, 请确保安装了这些 packages :

scikit-image: 用于图像的输入输出(IO)和变换(transforms)
pandas: 用于更加简单的解析 csv 文件
from __future__ import print_function, division
import os
import torch
import pandas as pd
from skimage import io, transform
import numpy as np
import matplotlib.pyplot as plt
from torch.utils.data import Dataset, DataLoader
from torchvision import transforms, utils# Ignore warnings
import warnings
warnings.filterwarnings("ignore")plt.ion()   # 交互式模式

我们要处理的数据集是面部姿态(facial pose). 这意味着一张人脸将被如下标注:
…/_images/landmarked_face2.png

总体上, 每张脸上标注了 68 个不同的landmark points。

Note

从 这里 下载数据集, 以便 图像数据的存放目录结构是这样的:’data/faces/’ 。 这个数据集事实上是使用 dlib 的姿态估计 来产生的,所用的图像来自于 imagenet 中标记为 ‘face’ 的若干张图像。

数据集自带一个 csv 文件,里面是存放着 标注(annotations),就像这样哒:

image_name,part_0_x,part_0_y,part_1_x,part_1_y,part_2_x, ... ,part_67_x,part_67_y
0805personali01.jpg,27,83,27,98, ... 84,134
1084239450_e76e00b7e7.jpg,70,236,71,257, ... ,128,312

让我们快速读取 CSV 文件 然后获得标注信息,并保存到一个 (N, 2) 的数组中去吧,其中 N 是 landmarks 的数量。

landmarks_frame = pd.read_csv('data/faces/face_landmarks.csv')n = 65
img_name = landmarks_frame.iloc[n, 0]
landmarks = landmarks_frame.iloc[n, 1:].as_matrix()
landmarks = landmarks.astype('float').reshape(-1, 2)print('Image name: {}'.format(img_name))
print('Landmarks shape: {}'.format(landmarks.shape))
print('First 4 Landmarks: {}'.format(landmarks[:4]))

让我们编写一个简单的辅助函数来显示一个图像及其标注,并使用它来显示一个示例。

def show_landmarks(image, landmarks):"""Show image with landmarks"""plt.imshow(image)plt.scatter(landmarks[:, 0], landmarks[:, 1], s=10, marker='.', c='r')plt.pause(0.001)  # pause a bit so that plots are updatedplt.figure()
show_landmarks(io.imread(os.path.join('data/faces/', img_name)),landmarks)
plt.show()

Dataset 类

torch.utils.data.Dataset 是表示数据集的抽象类。 您的自定义数据集类应该继承 Dataset 并覆盖以下方法:

__len__ 以便 len(dataset) 可以返回数据集的size。
__getitem__ 用于支持类似 dataset[i] 这样的索引,用来取得第 i个样本。

让我们为我们的脸地标数据集创建一个DataSet类。我们将在 init 中读取CSV, 但将图像的读取留给 getitem 。 这是内存有效的,因为所有的图像不是一次存储在内存中,而是根据需要读取。

我们数据集的样本将会是一个字典,像这样 {‘image’: image, ‘landmarks’: landmarks}。 我们的数据集将会接受一个可选参数 transform 以便任何需要的数据预处理可以施加到样本上。 我们将会在下一个小节看到 transform 的用处。

class FaceLandmarksDataset(Dataset):"""Face Landmarks dataset."""def __init__(self, csv_file, root_dir, transform=None):"""Args:csv_file (string): Path to the csv file with annotations.root_dir (string): Directory with all the images.transform (callable, optional): Optional transform to be appliedon a sample."""self.landmarks_frame = pd.read_csv(csv_file)self.root_dir = root_dirself.transform = transformdef __len__(self):return len(self.landmarks_frame)def __getitem__(self, idx):img_name = os.path.join(self.root_dir,self.landmarks_frame.iloc[idx, 0])image = io.imread(img_name)landmarks = self.landmarks_frame.iloc[idx, 1:].as_matrix()landmarks = landmarks.astype('float').reshape(-1, 2)sample = {'image': image, 'landmarks': landmarks}if self.transform:sample = self.transform(sample)return sample

让我们实例化这个类并迭代数据样本。我们将打印前4个样本的大小并显示它们的landmarks。

face_dataset = FaceLandmarksDataset(csv_file='data/faces/face_landmarks.csv',root_dir='data/faces/')fig = plt.figure()for i in range(len(face_dataset)):sample = face_dataset[i]print(i, sample['image'].shape, sample['landmarks'].shape)ax = plt.subplot(1, 4, i + 1)plt.tight_layout()ax.set_title('Sample #{}'.format(i))ax.axis('off')show_landmarks(**sample)if i == 3:plt.show()break

变换(Transforms)

从上面我们可以看到一个问题,就是样本的尺寸不一样。大多数神经网络都期望得到固定 大小的图像。因此,我们需要编写一些预处理代码。让我们创建三个转换:

Rescale: 缩放图像
RandomCrop: 随机裁剪图像. 用于数据增广(data augmentation).
ToTensor: 把 numpy images 转换为 torch images (我们需要交换坐标轴).

我们将把它们写成可调用的类(callable classes),而不是简单的函数, 这样每次调用transform时都不需要传递转换的参数。 为此,我们只需实现 call 方法,如果需要,则实现 init 方法。 然后我们可以使用这样的转换:

tsfm = Transform(params)
transformed_sample = tsfm(sample)

下面观察如何将这些转换应用于图像和landmarks。

class Rescale(object):"""把图像缩放到一个给定的尺寸。Args:output_size (tuple or int): 想要的输出尺寸. If tuple, output ismatched to output_size. If int, smaller of image edges is matchedto output_size keeping aspect ratio the same."""def __init__(self, output_size):assert isinstance(output_size, (int, tuple))self.output_size = output_sizedef __call__(self, sample):image, landmarks = sample['image'], sample['landmarks']h, w = image.shape[:2]if isinstance(self.output_size, int):if h > w:new_h, new_w = self.output_size * h / w, self.output_sizeelse:new_h, new_w = self.output_size, self.output_size * w / helse:new_h, new_w = self.output_sizenew_h, new_w = int(new_h), int(new_w)img = transform.resize(image, (new_h, new_w))# h and w are swapped for landmarks because for images,# x and y axes are axis 1 and 0 respectivelylandmarks = landmarks * [new_w / w, new_h / h]return {'image': img, 'landmarks': landmarks}class RandomCrop(object):"""在一个图像样本上随机裁切图像.Args:output_size (tuple or int): 想要的输出尺寸. If int, square crop is made."""def __init__(self, output_size):assert isinstance(output_size, (int, tuple))if isinstance(output_size, int):self.output_size = (output_size, output_size)else:assert len(output_size) == 2self.output_size = output_sizedef __call__(self, sample):image, landmarks = sample['image'], sample['landmarks']h, w = image.shape[:2]new_h, new_w = self.output_sizetop = np.random.randint(0, h - new_h)left = np.random.randint(0, w - new_w)image = image[top: top + new_h,left: left + new_w]landmarks = landmarks - [left, top]return {'image': image, 'landmarks': landmarks}class ToTensor(object):"""把样本的 ndarrays 转换为 Tensors."""def __call__(self, sample):image, landmarks = sample['image'], sample['landmarks']# swap color axis because# numpy image: H x W x C# torch image: C X H X Wimage = image.transpose((2, 0, 1))return {'image': torch.from_numpy(image),'landmarks': torch.from_numpy(landmarks)}

复合式变换器

现在,我们将转换应用于一个示例。

比方说,我们想要将图像的较短部分恢复到256, 然后从图像中随机裁剪出大小为224的方形图像。 也就是说,我们想要合成 Rescale 和 RandomCrop 变换。 torchvision.transforms.Compose 是一个简单的可调用类,它允许我们这样做。

scale = Rescale(256)
crop = RandomCrop(128)
composed = transforms.Compose([Rescale(256),RandomCrop(224)])# 将上述每个转换应用于示例
fig = plt.figure()
sample = face_dataset[65]
for i, tsfrm in enumerate([scale, crop, composed]):transformed_sample = tsfrm(sample)ax = plt.subplot(1, 3, i + 1)plt.tight_layout()ax.set_title(type(tsfrm).__name__)show_landmarks(**transformed_sample)plt.show()

在数据集上迭代遍历

让我们将所有这些放在一起创建一个具有组合转换的数据集。总之,每次采样该数据集时

从文件中动态读取一张图像。
Transforms 被应用于读取出的图像。
由于其中一个变换是随机的,所以在采样时会增加数据。

我们可以像以前一样使用 for i in range 循环迭代创建的数据集。

transformed_dataset = FaceLandmarksDataset(csv_file='data/faces/face_landmarks.csv',root_dir='data/faces/',transform=transforms.Compose([Rescale(256),RandomCrop(224),ToTensor()]))for i in range(len(transformed_dataset)):sample = transformed_dataset[i]print(i, sample['image'].size(), sample['landmarks'].size())if i == 3:break

但是,通过使用一个简单的for循环来迭代数据,我们失去了很多特性。特别是,我们错过了:

批量化数据
随机打乱数据
使用 multiprocessing workers 并行加载数据.

torch.utils.data.DataLoader 是一个提供了上述特性的迭代器。 我们应该对下面所用的参数有所明了。 其中一个有意思的参数是 collate_fn 。 你可以通过 collate_fn 来明确的指定样本如何被批量化。 然而, 默认的设置已经足以应付大多数情况啦。

dataloader = DataLoader(transformed_dataset, batch_size=4,shuffle=True, num_workers=4)# 显示一个批次的辅助函数
def show_landmarks_batch(sample_batched):"""Show image with landmarks for a batch of samples."""images_batch, landmarks_batch = \sample_batched['image'], sample_batched['landmarks']batch_size = len(images_batch)im_size = images_batch.size(2)grid = utils.make_grid(images_batch)plt.imshow(grid.numpy().transpose((1, 2, 0)))for i in range(batch_size):plt.scatter(landmarks_batch[i, :, 0].numpy() + i * im_size,landmarks_batch[i, :, 1].numpy(),s=10, marker='.', c='r')plt.title('Batch from dataloader')for i_batch, sample_batched in enumerate(dataloader):print(i_batch, sample_batched['image'].size(),sample_batched['landmarks'].size())# observe 4th batch and stop.if i_batch == 3:plt.figure()show_landmarks_batch(sample_batched)plt.axis('off')plt.ioff()plt.show()break

后记: torchvision

在本教程中,我们已经了解了如何编写和使用数据集类、变换器类和数据加载器类。 torchvision 包提供了一些常见的数据集类和变换器类。 您甚至可能不必编写自定义类。 torchvision 中可用的更通用的数据集之一是 ImageFolder 。 它假定图像的组织方式如下:

root/ants/xxx.png
root/ants/xxy.jpeg
root/ants/xxz.png
.
.
.
root/bees/123.jpg
root/bees/nsdf3.png
root/bees/asd932_.png

其中 ‘ants’, ‘bees’ etc. 是类标签。 类似的,操作 PIL.Image 类型的图像的通用变换,比如 RandomHorizontalFlip, Scale 等也是可用的。 你可以使用这些来写一个 dataloader,就像这样:

import torch
from torchvision import transforms, datasetsdata_transform = transforms.Compose([transforms.RandomSizedCrop(224),transforms.RandomHorizontalFlip(),transforms.ToTensor(),transforms.Normalize(mean=[0.485, 0.456, 0.406],std=[0.229, 0.224, 0.225])])
hymenoptera_dataset = datasets.ImageFolder(root='hymenoptera_data/train',transform=data_transform)
dataset_loader = torch.utils.data.DataLoader(hymenoptera_dataset,batch_size=4, shuffle=True,num_workers=4)

PyTorch-5 自定义 Datasets, DataLoaders 和 Transforms相关推荐

  1. [pytorch学习笔记] 3.Datasets Dataloaders

    目录 介绍 加载数据集 迭代和可视化数据集 自定义数据集 __init__ __len__ __getitem__ 使用 DataLoaders 准备训练数据 遍历 DataLoader 参考 介绍 ...

  2. PyTorch基础-自定义数据集和数据加载器(2)

    处理数据样本的代码可能会变得混乱且难以维护: 理想情况下,我们想要数据集代码与模型训练代码解耦,以获得更好的可读性和模块化.PyTorch 域库提供了许多预加载的数据(例如 FashionMNIST) ...

  3. 在pytorch中自定义dataset读取数据2021-1-8学习笔记

    在pytorch中自定义dataset读取数据 utils import os import json import pickle import randomimport matplotlib.pyp ...

  4. PyTorch框架学习五——图像预处理transforms(一)

    PyTorch框架学习五--图像预处理transforms(一) 一.transforms运行机制 二.transforms的具体方法 1.裁剪 (1)随机裁剪:transforms.RandomCr ...

  5. DeepChem | PyTorch中用自定义层实现DeepChem的GraphConvLayer

    PyTorch中用自定义层实现DeepChem的GraphConvLayer 环境 DeepChem 2.4 PyTorch 1.7.0 Python3.7.9 PyTorch中用自定义层实现Deep ...

  6. PyTorch手把手自定义Dataloader读取数据

    PyTorch手把手自定义Dataloader读取数据 https://zhuanlan.zhihu.com/p/35698470 pytorch之dataloader深入剖析 https://www ...

  7. PyTorch框架学习六——图像预处理transforms(二)

    PyTorch框架学习六--图像预处理transforms(二) (续)二.transforms的具体方法 4.图像变换 (1)尺寸变换:transforms.Resize() (2)标准化:tran ...

  8. (pytorch-深度学习系列)pytorch实现自定义网络层,并自设定前向传播路径-学习笔记

    pytorch实现自定义网络层,并自设定前向传播路径-学习笔记 1. 不包含模型参数的自定义网络层 首先我们自定义一个网络层, 定义一个网络层,使其不包含模型参数,并在forward()函数中进行运算 ...

  9. Pytorch的自定义拓展:torch.nn.Module和torch.autograd.Function

    参考链接:pytorch的自定义拓展之(一)--torch.nn.Module和torch.autograd.Function_LoveMIss-Y的博客-CSDN博客_pytorch自定义backw ...

最新文章

  1. python小学_小学生学python(二)
  2. 3句话概括 PUT/POST 的区别
  3. 【转】编程思想之消息机制
  4. 用python处理excel 数据分析_像Excel一样使用python进行数据分析(1)
  5. html和css占前端的多少比例,【CSS】前端怎么实现像chrome浏览器的百分比缩放同样的效果?...
  6. 编译php5遇到iconv错误的解决方法
  7. Scrapy将爬取的段落整合为字符串
  8. Listview条目删除完,显示默认的图片的数据
  9. 2019.7.24循环结构以及昨天的预习题。
  10. 方维团购系统二次开发,项目经验
  11. 拓端tecdat|在python 深度学习Keras中计算神经网络集成模型
  12. python缩进格式错误修改_Python,意外的缩进错误解析,Pythonunexpectedindent,解决,方法...
  13. WebService入门
  14. UML建模与软件开发设计(三)——UML常用开发工具
  15. 随机预言机模型与标准模型
  16. 数电发票(全电发票)时代如何查验发票?
  17. 红警职教智能硬件电子电路基础版教材与配套视频资源即将开发完毕
  18. No module named ‘quantopian‘
  19. 基于java的药店管理系统
  20. Arch linux 安装 docker

热门文章

  1. 浮点数之间的等值判断
  2. 做DSP应该懂的56个问题,反正我已经收藏了!
  3. 十万部冷知识:“沙特”为什么能赢“阿根廷”
  4. 锵锵三人行:AWS,下个路口见
  5. 关于我国计算机软件著作权保护的调研报告,我国计算机软件著作权保护问题研究...
  6. python怎么实现模糊找色_Python下尝试实现图片的高斯模糊化
  7. 如何让计算机显示器满屏,电脑显示器满屏条纹的解决方法
  8. unity技美27——优化项目内美术3D,2D等资源,详解unity打包体的潜规则与案例
  9. 【腾讯TMQ】iOS逻辑自动化测试实践
  10. uniapp 使用图表