TORCH.UTILS.DATA

pytorch数据处理的一个核心部分。主要支持:

  • map-style和iterable-style的数据集。
  • 定制数据载入模式
  • 自动批量化
  • 单或多线程数据载入
  • 自动memory pinning

用法:

DataLoader(dataset, batch_size=1, shuffle=False, sampler=None,
           batch_sampler=None, num_workers=0, collate_fn=None,
           pin_memory=False, drop_last=False, timeout=0,
           worker_init_fn=None)

可以看到dataloader中的dataset是关键参数,就是数据的来源。pytorch主要支持两种不同风格的dataset:

map-style datasetsiterable-style datasets

 

Map-style datasets

map-style风格的数据是实现__getitem__()和__len__()方法,实现一种映射:从索引/keys(可以是非这个整型)到data samples的过程。

例如:dataset[idx]可以实现从硬盘里读取第idx个图像和其对应的label。

 

Iterable-style datasets

iterable-style风格的数据是类IterableDataset的子类实例,需要实现__iter__()方法。作为一种在数据样本上的迭代器。这种风格适用于随机读取数据的代价较大或者是不合适的、或适用于批量大小取决于获取的数据。例如,通过iter(dataset)语句实现从数据库、远程服务器或实时生成的logs中返回一个数据流。

 

Data Loading Order and Sampler

对于iterable-style datasets,数据载入顺序完全由用户定义的迭代器所控制。这种风格对于chunk-reading和动态批量是很容易实现的。由于迭代风格的数据没有key和indx(只是每次输出图像),所以没有sampler或batch_sampler参数。

所以对于载入顺序sampler主要考虑第一种风格,就是map-style。torch.utils.data.Sampler 类常被指定用来数据载入中的indices/keys(其实就是数据的索引或键,可以和图像一一对应起来)的顺序。该类sampler就是实现了一种在索引上的迭代器类。所以通过影响索引/键就可以来确定图像载入的顺序以及形式。sampler(迭代器)就可以随机组合indices/keys列表的顺序,从而实现每次yield一个样本或yield一组样本。

dataloader中的参数shuffle是一个自动构建的sampler。当指定shuffle=true时就是随机的sampler,序列随机产生。当指定shuffle=false时就是顺序sampler,序列顺序产生。当然,除了这个默认的操作,用户可以自己指定sampler,来定制序列的产生方法。当定制sampler产生一组序列时,就可以作为参数传给batch_sampler中。自动batching可以通过batch_size和drop_last参数来指定。

 

Loading Batched and Non-Batched Data

默认和常见的方法就是批量载入数据。当batch_size和drop_last作为参数从sampler来构建bath_sampler。对于map-style的数据,sampler要不是用户制定,要不来源于shuffle参数。所以sampler和shuffle参数是互斥的。当从sampler中提供的序列index获取到一批样本后,参数collate_fn就可以用来整理这个批量数据了。有人会问:获取到批量样本不就好了吗,用这个collate_fn干嘛?实际上多数情况下这个参数本来也是默认为None的,但是对于特殊情况:批量中的图像有的是坏的,需要筛选、批量中的序列不等长,需要处理到等长等等情况。

map-style的数据大约等价于:

for indices in batch_sampler:
    yield collate_fn([dataset[i] for i in indices])

iterable-style的数据大致等价于:

dataset_iter = iter(dataset)
for indices in batch_sampler:    # 这里的sampler没啥用的。
    yield collate_fn([next(dataset_iter) for _ in indices])

 

Disable automatic batching

就是禁止默认的自动批量化。在某些情况下,用户可能希望在数据集代码中手动处理批处理,或只是加载单个样本。例如,直接加载批处理数据(例如,从数据库批量读取或读取连续内存块)可能更容易,或者批处理大小依赖于数据,或者程序设计用于处理单个样本。在这些场景下,最好不要使用自动批处理(collate_fn用于整理样本),而是让数据加载器直接返回dataset对象的每个成员。

当batch_size和batch_sampler是None时,自动batching就被禁止了。这时候每个样本就直接进入collate_fn函数的处理中了。当自动批量被禁止,默认的collate_fn就只是将numpy数组转为pytorch tensor,然后保持其他不变。

此时的map-style大致等价于:

for index in sampler:   # 没有了batch
    yield collate_fn(dataset[index])

iterable-style的代码大致为:

for data in iter(dataset):
    yield collate_fn(data)

所以自动批量被禁后,就变成了单个样本。

 

Single- and Multi-process Data Loading

在Python进程中,全局解释器锁(GIL)防止在线程之间完全并行化Python代码。为了避免在数据加载时阻塞计算代码,PyTorch提供了一个简单的开关,通过将参数num_workers设置为正整数来执行多进程数据加载。

 

单进程数据加载(默认)
在此模式下,数据获取在初始化数据加载器的同一进程中完成。因此,数据加载可能会阻塞计算。但是,当用于在进程(例如,共享内存、文件描述符)之间共享数据的资源有限时,或者当整个数据集很小并且可以完全加载到内存中时,可以优选此模式。此外,单进程加载通常显示更可读的错误跟踪,因此对调试很有用。

 

多进程数据加载
将参数num_workers设置为正整数将启用具有指定数量的加载程序工作进程的多进程数据加载。
在这种模式下,每次创建数据加载器的迭代器(例如,当调用enumerate(DataLoader))时,都会创建num_workers工作进程。此时,dataset、collate_fn和worker_init_fn将传递给每个worker,在那里它们用于初始化和获取数据。这意味着数据集访问及其内部IO、转换(包括collate fn)在工作进程中运行。
torch.utile.data.get_worker_info()返回工作进程中的各种有用信息(包括工作进程id、数据集副本、初始种子等),在主进程中不返回任何信息。用户可以在数据集代码和/或worker_init_fn中使用此函数来分别配置每个数据集副本,并确定代码是否在工作进程中运行。这对于切分数据集特别有用。
对于map-style数据集,主进程使用sampler生成索引并将它们发送workers。因此,任何随机shuffle都是在主进程中完成的,该进程通过将索引分配给加载来指导加载。
对于iterable-style数据集,由于每个工作进程都得到一个dataset对象的副本,因此简单的多进程加载通常会导致重复的数据。使用torch.utile.data.get_worker_info()和/或worker_init_fn,用户可以独立配置每个副本。(有关如何实现这一点,请参阅IterableDataset文档。)出于类似的原因,在多进程加载中,drop_last参数将删除每个worker的iterable样式数据集副本的最后一批非完整数据。
一旦到达迭代的末尾,或者当迭代器成为垃圾回收器时,将关闭工作进程。

posted @ 2020-05-24 13:05  三年一梦  阅读(1142)  评论(0编辑  收藏  举报