【PyTorch学习(四)】数据加载的流程,以面部姿态数据集为例。

PyTorch数据加载的流程

深度学习需要处理大量的数据,一般使用pytorch进行数据加载。
pytorch提供了许多工具来让加载数据更简单并尽量减少代码的复杂度。本文将从一个小数据集中学习如何加载和预处理/增强数据。
需要使用的包:

scikit-image: 图形接口以及变换
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()   # interactive mode
<matplotlib.pyplot._IonContext at 0x2061b38fe50>

处理的是一个面部姿态的数据集。也就是按如下方式标注的人脸:面部姿态的数据集
数据集下载地址:https://download.pytorch.org/tutorial/faces.zip
将下载好的数据集放在’data/faces/'下。
csv中数据格式如下:

image_name,part_0_x,part_0_y,part_1_x,part_1_y,part_2_x, ... ,part_67_x,part_67_y

读取csv并将标注点数据写入(N,2)数组中,其中N是特征点的数量。

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:].values
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]))

输出:

Image name: person-7.jpg
Landmarks shape: (68, 2)
First 4 Landmarks: [[32. 65.]
 [33. 76.]
 [34. 86.]
 [34. 97.]]

展示其中一张图片和它对应的标注点:

def show_landmarks(image, landmarks):
    """Show image with landmarks"""
    plt.imshow(image)
    plt.scatter(landmarks[:, 0], landmarks[:, 1], s=5, marker='.', c='y')
    plt.pause(0.01)  # pause a bit so that plots are updated

plt.figure()
show_landmarks(io.imread(os.path.join('data/faces/', img_name)),
               landmarks)

输出:
在这里插入图片描述

数据集类 Dataset class

torch.utils.data.Dataset 是一个代表数据集的抽象类。你自定的数据集类应该继承自 Dataset 类并重新实现以下方法:

  1. __len__ (两个下划线)实现 len(dataset) 返还数据集的尺寸。
  2. __getitem__ 用来获取一些索引数据,例如 使用dataset[i] 获得第i个样本。
    下面为本文使用的数据集创建一个类。在 __init__ 中读取csv的文件内容,在 __getitem__中读取图片。(这么做是为了节省内存空间。只有在需要用到图片的时候才读取它而不是一开始就把图片全部存进内存里。)
    数据样本将按这样一个字典 {‘image’: image, ‘landmarks’: landmarks}组织。
    数据集类将添加一个可选参数 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 applied
 on a sample.
 """
        self.landmarks_frame = pd.read_csv(csv_file)
        self.root_dir = root_dir
        self.transform = transform

    def __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:].values
        landmarks = landmarks.astype('float').reshape(-1, 2)
        sample = {'image': image, 'landmarks': landmarks}
        
        if self.transform:
            sample = self.transform(sample)

        return sample

接着实例化这个类并创建几个数据。下面将会打印出前四个例子的尺寸并展示标注的特征点。

face_dataset = FaceLandmarksDataset(csv_file='data/faces/face_landmarks.csv',
                                    root_dir='data/faces/')
print(face_dataset)
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
<__main__.FaceLandmarksDataset object at 0x00000206280BCAC0>
0 (324, 215, 3) (68, 2)

在这里插入图片描述

1 (500, 333, 3) (68, 2)

在这里插入图片描述

2 (250, 258, 3) (68, 2)

在这里插入图片描述

3 (434, 290, 3) (68, 2)

在这里插入图片描述

转换 Transforms

官网文档介绍:https://pytorch.org/docs/stable/generated/torch.nn.Transformer.html?highlight=transformers

上面例子的图片并不是同样的尺寸,但大多数神经网络的输入要求是相同尺寸的图片。因此还需要对数据集做一些预处理。
创建三个常用的转换:

  1. Rescale: 缩放图片
  2. RandomCrop: 对图片进行随机裁剪。这是一种数据增强操作
  3. ToTensor: 把 numpy 格式图片转为 torch 格式图片 (我们需要交换坐标轴).
    写成可调用的类的形式而不是简单的函数,这样就不需要每次调用时传递一遍参数。只需要实现 __call__ 方法,必要的时候实现 __init__ 方法。我们可以这样调用这些转换:
tsfm = Transform(params)
transformed_sample = tsfm(sample)

观察下面这些转换是如何应用在图像和标签上的:
缩放、裁剪、转换tensor的类型

class Rescale(object):
    """将样本中的图像重新缩放到给定的大小.

 Args:
 output_size (tuple or int): 期望的输出大小。
 如果是tuple,输出是output_size匹配。
 如果是int,则匹配较小的图像边缘output_size保持长宽比相同.
 """

    def __init__(self, output_size):
        assert isinstance(output_size, (int, tuple))
        self.output_size = output_size

    def __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_size
            else:
                new_h, new_w = self.output_size, self.output_size * w / h
        else:
            new_h, new_w = self.output_size

        new_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 respectively
        landmarks = landmarks * [new_w / w, new_h / h]

        return {'image': img, 'landmarks': landmarks}

class RandomCrop(object):
    """Crop randomly the image in a sample.

 Args:
 output_size (tuple or int): Desired output size. 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) == 2
            self.output_size = output_size

    def __call__(self, sample):
        image, landmarks = sample['image'], sample['landmarks']

        h, w = image.shape[:2]
        new_h, new_w = self.output_size

        top = 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):
    """Convert ndarrays in sample to 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 W
        image = image.transpose((2, 0, 1))
        return {'image': torch.from_numpy(image),
                'landmarks': torch.from_numpy(landmarks)}

组合转换 Compose transforms

将上面创建的三个常用图片转换进行组合,并应用本文。
把图像的短边调整为256,然后随机裁剪 (randomcrop) 为224大小的正方形。需组合使用Rescale 和 RandomCrop 的变换。
可以调用一个简单的类 torchvision.transforms.Compose 来实现这一操作。

scale = Rescale(256)
crop = RandomCrop(128)
composed = transforms.Compose([Rescale(256),
                               RandomCrop(224)])

# Apply each of the above transforms on sample.
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()

转换后的结果:
在这里插入图片描述在这里插入图片描述在这里插入图片描述

迭代数据集 Iterating through the dataset

下面创建一个带组合转换的数据集:

  1. 从文件中读取图片
  2. 对读取的图片进行组合转换
  3. 操作是随机的 (randomcrop) , 数据被增强了
    为作为对比,我们首先使用for循环来对所有创建的数据集执行同样的操作。
transformed_dataset = FaceLandmarksDataset(csv_file='data/faces/face_landmarks.csv',
                                           root_dir='data/faces/',
                                           transform=transforms.Compose([
                                               Rescale(256),
                                               RandomCrop(224),
                                               ToTensor()
                                           ]))
print(transformed_dataset)
print(len(transformed_dataset))
for i in range(len(transformed_dataset)):
    sample = transformed_dataset[i]

    print(i, sample['image'].size(), sample['landmarks'].size())

    if i == 3:
        break

输出:

<__main__.FaceLandmarksDataset object at 0x0000020626EF51C0>
69
0 torch.Size([3, 224, 224]) torch.Size([68, 2])
1 torch.Size([3, 224, 224]) torch.Size([68, 2])
2 torch.Size([3, 224, 224]) torch.Size([68, 2])
3 torch.Size([3, 224, 224]) torch.Size([68, 2])

从计算成本和功能考虑,for循环存在一下不足:

  1. 计算成本高
  2. 不能进行批处理(Batching the data)
  3. 不能对数据进行随机处理(shuffling)
  4. 不支持多线程加载数据
    基于以上的不足,人们又创建了一种新的迭代器torch.utils.data.DataLoader以解决这些问题。
    collate_fn是确定如何对数据进行批处理的参数。
dataloader = DataLoader(transformed_dataset, batch_size=4,
                        shuffle=True, num_workers=4)

# Helper function to show a batch
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

输出:
在这里插入图片描述

0 torch.Size([4, 3, 224, 224]) torch.Size([4, 68, 2])
1 torch.Size([4, 3, 224, 224]) torch.Size([4, 68, 2])
2 torch.Size([4, 3, 224, 224]) torch.Size([4, 68, 2])
3 torch.Size([4, 3, 224, 224]) torch.Size([4, 68, 2])

以上我们完成了构造和使用数据集类 (datasets), 转换 (transforms) 和数据加载器 (dataloader)。torchvision 包提供了常用的数据集类 (datasets) 和转换 (transforms)。 使用torchvision包就不用自己再构造这些类了。

torchvision

torchvision 是PyTorch中用于处理图像任务的包。包括如下类:

  • torchvision.datasets
  • torchvision.models
  • torchvision.transforms
  • torchvision.utils
    torchvision.datasets 是用来进行数据加载的。
    torchvision.models 中提供了已经训练好的模型,让我们可以进行数据加载之后,直接使用。
import torchvision.models as models
alexnet = models.alexnet()

此外,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’ 等是分类标签。
PIL.Image 中你也可以使用类似的转换(transforms) 例如 RandomHorizontalFlip, Scale。利用这些你可以按如下的方式创建一个数据加载器 (dataloader) :

import torch
from torchvision import transforms, datasets

data_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)

以上是数据集加载的大致流程

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: PyTorch是一个开源的机器学习库,内置丰富的函数和工具包用于数据集加载数据预处理、模型构建、训练和评估。数据集是机器学习模型的重要组成部分,PyTorch提供了多种方法用于加载数据集,包括内置的函数和可定制的方法,让用户可以根据自己的需求和数据集特性来选择适合的方式。 内置函数 PyTorch提供了内置的函数用于加载常见的数据集,如MNIST、CIFAR-10、ImageNet等。这些函数通常包括下载数据集、转换为Tensor格式、划分为训练集和测试集等步骤,使用简单方便,适合快速上手使用。 可定制方法 如果内置函数不能满足需求,PyTorch也提供了许多可定制的方法。最常用的是Dataset和DataLoader类。Dataset类是抽象类,用户需要继承这个类并实现getitem和len方法来定义自己的数据集。DataLoader类用于生成一个迭代器,用户可以设置批量大小、并行加载、随机采样等参数。 除此之外,PyTorch还提供了其它一些用于数据集处理的工具,如transforms模块、Sampler类、collate_fn函数等,可以用于数据增强、数据集分块和数据集拼接等场景。 总结 PyTorch提供了内置函数和可定制方法用于加载数据集,用户可以根据自己的需求和数据集特性来选择适合的方式。使用内置函数可以快速上手,使用可定制方法可以更加灵活和高效。对于多样化的数据集PyTorch还提供了多个处理工具,可以用于数据增强、数据集分块和数据集拼接等场景。 ### 回答2: PyTorch是一种基于Python的开源机器学习库,它可以用于构建各种机器学习模型。在PyTorch中,数据集加载是一个非常重要的部分,因为机器学习模型需要大量的数据来进行训练。 在PyTorch中,数据集加载可以通过DataLoader类来实现。DataLoader是一个Python迭代器,它可以加载大量的数据集,并将其分成小批量进行训练。这样可以避免一次性将整个数据集加载到内存中,从而节省内存空间。 首先,我们需要将数据集加载到内存或磁盘中,并将其转换为PyTorch数据集类的对象。PyTorch提供了两种数据集类:Dataset和IterableDataset。其中,Dataset类是一种基于索引的数据集类,它可以通过索引来访问数据集中的每个数据样本;而IterableDataset是一种基于迭代器的数据集类,它可以像Python中的迭代器一样使用。 然后,我们可以使用DataLoader类来加载数据集。DataLoader类有很多参数,包括batch_size(表示每个小批量包含的样本数)、shuffle(表示是否随机打乱数据集顺序)、num_workers(表示使用多少个工作线程来加载数据集)等。 在使用DataLoader类加载数据集时,我们可以通过for循环来迭代数据集中的每个小批量,并将其传递给机器学习模型进行训练。 总之,PyTorch数据集加载是非常灵活和易于使用的。通过使用DataLoader类和PyTorch提供的数据集类,我们可以轻松地加载和处理大量的数据集,并将其用于训练各种机器学习模型。 ### 回答3: Pytorch是一个使用Python作为开发语言的深度学习框架,提供了非常强大的数据加载和预处理工具。在Pytorch中,数据集加载主要通过两个类来实现,分别是Dataset和DataLoader。Dataset类负责加载和处理数据集,而DataLoader类则负责将处理后的数据安装指定的batch_size分批加载到内存中,避免了内存不足的问题。 Dataset类是一个抽象类,需要根据具体的数据集来实现其中的方法。一般而言,Dataset类中需要实现__len__()方法和__getitem__()方法,分别用来获取数据集的长度和获取具体的数据样本。 DataLoader类则负责对数据集进行batch处理,这样可以充分利用系统的存储和计算资源,提高了模型的训练速度。在使用DataLoader时,需要指定batch_size、shuffle和num_workers等参数,其中num_workers可以指定使用多少个进程来装载数据,从而进一步提高了数据装载的效率。 在数据集加载过程中,可以使用Pytorch提供的transforms模块来进行数据的预处理,如改变图像尺寸、随机翻转、归一化等操作,从而增加模型的泛化能力和准确性。 总之,Pytorch数据集加载和预处理方面提供了非常强大的工具,只需要实现一些简单的代码,就能轻松地完成数据加载和预处理,从而为模型的训练和测试提供了坚实的基础。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值