在使用pytorch训练模型,经常需要加载大量图片数据,因此pytorch提供了好用的数据加载工具Dataloader。
为了实现小批量循环读取大型数据集,在Dataloader类具体实现中,使用了迭代器和生成器。
这一应用场景正是python中迭代器模式的意义所在,因此本文对Dataloader中代码进行解读,可以更好的理解python中迭代器和生成器的概念。

本文的内容主要有:

  1. 解释python中的迭代器和生成器概念
  2. 解读pytorch中Dataloader代码,如何使用迭代器和生成器实现数据加载

python迭代基础

python中围绕着迭代有以下概念:

  1. 可迭代对象 iterables
  2. 迭代器 iterator
  3. 生成器 generator

这三个概念互相关联,并不是孤立的。在可迭代对象的基础上发展了迭代器,在迭代器的基础上又发展了生成器。
学习这些概念的名词解释没有多大意义。编程中很多的抽象概念都是为了更好的实现某些功能,才去人为创造的协议和模式。
因此,要理解它们,需要探究概念背后的逻辑,为什么这样设计?要解决的真正问题是什么?在哪些场景下应用是最好的?

迭代模式首先要解决的基础问题是,需要按一定顺序获取集合内部数据,比如循环某个list。
当数据很小时,不会有问题。但当读取大量数据时,一次性读取会超出内存限制,因此想出以下方法:

  • 把大的数据分成几个小块,分批处理
  • 惰性的取值方式,按需取值

循环读数据可分为下面三种应用场景,对应着容器(可迭代对象),迭代器和生成器:

  1. for x in container: 为了遍历python内部序列容器(如list), 这些类型内部实现了__getitem__() 方法,可以从0开始按顺序遍历序列容器中的元素。
  2. for x in iterator: 为了循环用户自定义的迭代器,需要实现__iter__和__next__方法,__iter__是迭代协议,具体每次迭代的执行逻辑在 __next__或next方法里
  3. for x in generator: 为了节省循环的内存和加速,使用生成器来实现惰性加载,在迭代器的基础上加入了yield语句,最简单的例子是 range(5)

代码示例:

# 普通循环 for x in list
numbers = [1, 2, 3,]
for n in numbers:
    print(n) # 1,2,3

# for循环实际干的事情
# iter输入一个可迭代对象list,返回迭代器
# next方法取数据
my_iterator = iter(numbers)
next(my_iterator) # 1
next(my_iterator) # 2
next(my_iterator) # 3
next(my_iterator) # StopIteration exception

# 迭代器循环 for x in iterator
for i,n in enumerate(numbers):
    print(i,n) # 0,1 / 1,3 / 2,3

# 生成器循环 for x in generator
for i in range(3):
    print(i) # 0,1,2

for x in container方法:

  • list, deque, …
  • set, frozensets, …
  • dict, defaultdict, OrderedDict, Counter, …
  • tuple, namedtuple, …
  • str

for x in iterator方法:

  • enumerate() # 加上list的index
  • sorted() # 排序list
  • reversed() # 倒序list
  • zip() # 合并list

for x in generator方法:

  • range()
  • map()
  • filter()
  • reduce()
  • [x for x in list(...)]

上面示例代码中python内置函数iter和next的用法:

  • iter函数,调用__iter__,返回一个迭代器
  • next函数,输入迭代器,调用__next__,取出数据

比较容易混淆的是__iter__和__next__两个方法。它们的区别是:

  1. __iter__是为了可以迭代,真正执行取数据的逻辑是__next__方法实现的,实际调用是通过next(iterator)完成
  2. __iter__可以返回自身(return self),实际读取数据的实现放在__next__方法
  3. __iter__可以和yield搭配,返回生成器对象

__iter__返回自身的做法有点类似 python中的类型系统。为了保持一致性,python中一切皆对象。
每个对象创建后,都有类型指针,而类型对象的指针指向元对象,元对象的指针指向自身。

生成器,是在__iter__方法中加入yield语句,好处有:

  1. 减少循环判断逻辑的复杂度
  2. 惰性取值,节省内存和时间

yield作用:

  1. 代替函数中的return语句
  2. 记住上一次循环迭代器内部元素的位置

Dataloder源码分析

pytorch采用for x in iterator模式,从Dataloader类中读取数据。

  1. 为了实现该迭代模式,在Dataloader内部实现__iter__方法,实际返回的是_DataLoaderIter类。
  2. _DataLoaderIter类里面,实现了 __iter__方法,返回自身,具体执行读数据的逻辑,在__next__方法中。

以下代码只截取了单线程下的数据读取。

class DataLoader(object):
    r"""
    Data loader. Combines a dataset and a sampler, and provides
    single- or multi-process iterators over the dataset.
    """
    def __init__(self, dataset, batch_size=1, shuffle=False, ...):
        self.dataset = dataset
        self.batch_sampler = batch_sampler
        ...

    def __iter__(self):
        return _DataLoaderIter(self)

    def __len__(self):
        return len(self.batch_sampler)

class _DataLoaderIter(object):
    r"""Iterates once over the DataLoader's dataset, as specified by the sampler"""
    def __init__(self, loader):
        self.sample_iter = iter(self.batch_sampler)
        ...

    def __next__(self):
        if self.num_workers == 0:  # same-process loading
            indices = next(self.sample_iter)  # may raise StopIteration
            batch = self.collate_fn([self.dataset[i] for i in indices])
            if self.pin_memory:
                batch = pin_memory_batch(batch)
            return batch
        ...

    def __iter__(self):
        return self

Dataloader类中读取数据Index的方法,采用了 for x in generator方式,但是调用采用iter和next函数

  1. 构建随机采样类RandomSampler,内部实现了 __iter__方法
  2. __iter__方法内部使用了 yield,循环遍历数据集,当数量达到batch_size大小时,就返回
  3. 实例化随机采样类,传入iter函数,返回一个迭代器
  4. next会调用随机采样类中生成器,返回相应的index数据
class RandomSampler(object):
    """random sampler to yield a mini-batch of indices."""
    def __init__(self, batch_size, dataset, drop_last=False):
        self.dataset = dataset
        self.batch_size = batch_size
        self.num_imgs = len(dataset)
        self.drop_last = drop_last

    def __iter__(self):
        indices = np.random.permutation(self.num_imgs)
        batch = []
        for i in indices:
            batch.append(i)
            if len(batch) == self.batch_size:
                yield batch
                batch = []
        ## if images not to yield a batch
        if len(batch)>0 and not self.drop_last:
            yield batch

    def __len__(self):
        if self.drop_last:
            return self.num_imgs // self.batch_size
        else:
            return (self.num_imgs + self.batch_size - 1) // self.batch_size

batch_sampler = RandomSampler(batch_size. dataset)
sample_iter = iter(batch_sampler)
indices = next(sample_iter)

总结

本文总结了python中循环的三种模式:

  1. for x in container 可迭代对象
  2. for x in iterator 迭代器
  3. for x in generator 生成器

pytorch中的数据加载模块 Dataloader,使用生成器来返回数据的索引,使用迭代器来返回需要的张量数据,可以在大量数据情况下,实现小批量循环迭代式的读取,避免了内存不足问题。

参考文章

pytorch :: Dataloader中的迭代器和生成器应用的更多相关文章

  1. python is、==区别;with;gil;python中tuple和list的区别;Python 中的迭代器、生成器、装饰器

    1. is 比较的是两个实例对象是不是完全相同,它们是不是同一个对象,占用的内存地址是否相同 == 比较的是两个对象的内容是否相等 2. with语句时用于对try except finally 的优 ...

  2. 掌握JavaScript中的迭代器和生成器,顺便了解一下async、await的原理

    掌握JavaScript中的迭代器和生成器,顺便了解一下async.await的原理 前言 相信很多人对迭代器和生成器都不陌生,当提到async和await的原理时,大部分人可能都知道async.aw ...

  3. Python中的迭代器和生成器

    本文以实例详解了python的迭代器与生成器,具体如下所示: 1. 迭代器概述: 迭代器是访问集合元素的一种方式.迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束.迭代器只能往前不会后 ...

  4. 终于理解Python中的迭代器和生成器了!

    迭代器和生成器 目录 迭代器和生成器 可迭代对象和迭代器 基础概念 判断 for循环本质 不想用for循环迭代了,如何使用迭代器? 列表推导式 生成器Generator 概念 如何实现和使用? 生成器 ...

  5. JavaScript中的迭代器和生成器[未排版]

    JavaScript中的迭代器 在软件开发领域,"迭代"的意思是按照顺序反复多次执行一段程序,通常会有明确的终止条件. ECMAScript 6规范新增了两个高级特性:迭代器和生成 ...

  6. Python中的迭代器、生成器

    from collections import Iterable, Iterator 1. 可迭代(iterable)对象 参考官网链接 class I: def __init__(self, v): ...

  7. python中的迭代器和生成器学习笔记总结

    生成器就是一个在行为上和迭代器非常类似的对象.   是个对象! 迭代,顾名思意就是不停的代换的意思,迭代是重复反馈过程的活动,其目的通常是为了逼近所需目标或结果.每一次对过程的重复称为一次“迭代”,而 ...

  8. python中的迭代器与生成器

    迭代器 迭代器的引入 假如我现在有一个列表l=['a','b','c','d','e'],我想取列表中的内容,那么有几种方式? 1.通过索引取值 ,如了l[0],l[1] 2.通过for循环取值 fo ...

  9. JS中的迭代器和生成器

    利用迭代器生成一个遍历方法: let arr1 = [1, 2, 3, 11, 22, 13, 24]; function forOf(arr, callback) { // 找到迭代器函数 let ...

随机推荐

  1. ros:出现:error: ros/ros.h: No such file or directory

    出现这个问题是因为在cmakelist.txt中没有声明对roscpp的依赖.在find_package中添加roscpp,重新执行就ok了.

  2. 1 项目里面如何打印log日志

    1  首先写一个logging.py文件 import logging from conf import setting #配置文件,里面有日志存放路径 def mylog(): logger = l ...

  3. Jmeter xpath处理器

  4. 待性能改善的一个SQL

    select t.*, t.rowid from tb_tk_datasakusei_ctrl t; alter table ATOMBB.TB_TK_JISSEKI_INFO_DETAIL add ...

  5. 2018-8-10-添加右键使用-SublimeText-打开

    title author date CreateTime categories 添加右键使用 SublimeText 打开 lindexi 2018-08-10 19:16:52 +0800 2018 ...

  6. 关于6410板文件的dm9000的平台设备地址

    转自csdn #define CONFIG_DM9000_BASE 0x20000300#define DM9000_IO                      0x20000000#define ...

  7. 12 Top Open Source Data Analytics Apps

    1. Hadoop It would be impossible to talk about open source data analytics without mentioning Hadoop. ...

  8. 2015,2016 Open Source Yearbook

    https://opensource.com/yearbook/2015 The 2015 Open Source Yearbook is a community-contributed collec ...

  9. Python 的经典入门书籍

    实python非常适合初学者入门,上手很容易.我就是完全通过网上资源学了python的.最大的是3点经验:1.找一本浅显易懂,例程比较好的教程,从头到尾看下去.不要看很多本,专注于一本.把里面的例程都 ...

  10. Yarn install 报错 Resolving packages... [2/4] Fetching packages... info There appears to be trouble with your network connection. Retrying

    1.设置淘宝代理 yarn config set registry 'https://registry.npm.taobao.org' 2.如果网址本地可以打开,说明你本地有代理设置 所以需要按本地的 ...