pytorch :: Dataloader中的迭代器和生成器应用
在使用pytorch训练模型,经常需要加载大量图片数据,因此pytorch提供了好用的数据加载工具Dataloader。
为了实现小批量循环读取大型数据集,在Dataloader类具体实现中,使用了迭代器和生成器。
这一应用场景正是python中迭代器模式的意义所在,因此本文对Dataloader中代码进行解读,可以更好的理解python中迭代器和生成器的概念。
本文的内容主要有:
- 解释python中的迭代器和生成器概念
- 解读pytorch中Dataloader代码,如何使用迭代器和生成器实现数据加载
python迭代基础
python中围绕着迭代有以下概念:
- 可迭代对象 iterables
- 迭代器 iterator
- 生成器 generator
这三个概念互相关联,并不是孤立的。在可迭代对象的基础上发展了迭代器,在迭代器的基础上又发展了生成器。
学习这些概念的名词解释没有多大意义。编程中很多的抽象概念都是为了更好的实现某些功能,才去人为创造的协议和模式。
因此,要理解它们,需要探究概念背后的逻辑,为什么这样设计?要解决的真正问题是什么?在哪些场景下应用是最好的?
迭代模式首先要解决的基础问题是,需要按一定顺序获取集合内部数据,比如循环某个list。
当数据很小时,不会有问题。但当读取大量数据时,一次性读取会超出内存限制,因此想出以下方法:
- 把大的数据分成几个小块,分批处理
- 惰性的取值方式,按需取值
循环读数据可分为下面三种应用场景,对应着容器(可迭代对象),迭代器和生成器:
for x in container: 为了遍历python内部序列容器(如list), 这些类型内部实现了__getitem__() 方法,可以从0开始按顺序遍历序列容器中的元素。for x in iterator: 为了循环用户自定义的迭代器,需要实现__iter__和__next__方法,__iter__是迭代协议,具体每次迭代的执行逻辑在 __next__或next方法里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的indexsorted()# 排序listreversed()# 倒序listzip()# 合并list
for x in generator方法:
range()map()filter()reduce()[x for x in list(...)]
上面示例代码中python内置函数iter和next的用法:
- iter函数,调用__iter__,返回一个迭代器
- next函数,输入迭代器,调用__next__,取出数据
比较容易混淆的是__iter__和__next__两个方法。它们的区别是:
- __iter__是为了可以迭代,真正执行取数据的逻辑是__next__方法实现的,实际调用是通过next(iterator)完成
- __iter__可以返回自身(return self),实际读取数据的实现放在__next__方法
- __iter__可以和yield搭配,返回生成器对象
__iter__返回自身的做法有点类似 python中的类型系统。为了保持一致性,python中一切皆对象。
每个对象创建后,都有类型指针,而类型对象的指针指向元对象,元对象的指针指向自身。
生成器,是在__iter__方法中加入yield语句,好处有:
- 减少循环判断逻辑的复杂度
- 惰性取值,节省内存和时间
yield作用:
- 代替函数中的return语句
- 记住上一次循环迭代器内部元素的位置
Dataloder源码分析
pytorch采用for x in iterator模式,从Dataloader类中读取数据。
- 为了实现该迭代模式,在Dataloader内部实现__iter__方法,实际返回的是_DataLoaderIter类。
- _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函数
- 构建随机采样类RandomSampler,内部实现了 __iter__方法
- __iter__方法内部使用了 yield,循环遍历数据集,当数量达到batch_size大小时,就返回
- 实例化随机采样类,传入iter函数,返回一个迭代器
- 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中循环的三种模式:
for x in container可迭代对象for x in iterator迭代器for x in generator生成器
pytorch中的数据加载模块 Dataloader,使用生成器来返回数据的索引,使用迭代器来返回需要的张量数据,可以在大量数据情况下,实现小批量循环迭代式的读取,避免了内存不足问题。
参考文章
pytorch :: Dataloader中的迭代器和生成器应用的更多相关文章
- python is、==区别;with;gil;python中tuple和list的区别;Python 中的迭代器、生成器、装饰器
1. is 比较的是两个实例对象是不是完全相同,它们是不是同一个对象,占用的内存地址是否相同 == 比较的是两个对象的内容是否相等 2. with语句时用于对try except finally 的优 ...
- 掌握JavaScript中的迭代器和生成器,顺便了解一下async、await的原理
掌握JavaScript中的迭代器和生成器,顺便了解一下async.await的原理 前言 相信很多人对迭代器和生成器都不陌生,当提到async和await的原理时,大部分人可能都知道async.aw ...
- Python中的迭代器和生成器
本文以实例详解了python的迭代器与生成器,具体如下所示: 1. 迭代器概述: 迭代器是访问集合元素的一种方式.迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束.迭代器只能往前不会后 ...
- 终于理解Python中的迭代器和生成器了!
迭代器和生成器 目录 迭代器和生成器 可迭代对象和迭代器 基础概念 判断 for循环本质 不想用for循环迭代了,如何使用迭代器? 列表推导式 生成器Generator 概念 如何实现和使用? 生成器 ...
- JavaScript中的迭代器和生成器[未排版]
JavaScript中的迭代器 在软件开发领域,"迭代"的意思是按照顺序反复多次执行一段程序,通常会有明确的终止条件. ECMAScript 6规范新增了两个高级特性:迭代器和生成 ...
- Python中的迭代器、生成器
from collections import Iterable, Iterator 1. 可迭代(iterable)对象 参考官网链接 class I: def __init__(self, v): ...
- python中的迭代器和生成器学习笔记总结
生成器就是一个在行为上和迭代器非常类似的对象. 是个对象! 迭代,顾名思意就是不停的代换的意思,迭代是重复反馈过程的活动,其目的通常是为了逼近所需目标或结果.每一次对过程的重复称为一次“迭代”,而 ...
- python中的迭代器与生成器
迭代器 迭代器的引入 假如我现在有一个列表l=['a','b','c','d','e'],我想取列表中的内容,那么有几种方式? 1.通过索引取值 ,如了l[0],l[1] 2.通过for循环取值 fo ...
- JS中的迭代器和生成器
利用迭代器生成一个遍历方法: let arr1 = [1, 2, 3, 11, 22, 13, 24]; function forOf(arr, callback) { // 找到迭代器函数 let ...
随机推荐
- BackgroundWorker 的输入、输出参数、进度条与文字刷新、取消机制、返回事件
1. 定义全局变量 BackgroundWorker backgroundwoker; 2. 点击开始按钮或其它时机初始化 backgroundwoker = new BackgroundWorker ...
- 2019-1-9-WPF-最小的代码使用-DynamicRenderer-书写
title author date CreateTime categories WPF 最小的代码使用 DynamicRenderer 书写 lindexi 2019-1-9 14:7:26 +080 ...
- qt 自定义窗口显示鼠标划过的轨迹
鼠标事件分为四种: 1.按下 2.抬起 3.移动 4.双击 鼠标事件继承与QWidget void mouseDoubleClickEvent(QMouseEvent *event) void mou ...
- 重磅开源|AOP for Flutter开发利器——AspectD
https://github.com/alibaba-flutter/aspectd 问题背景 随着Flutter这一框架的快速发展,有越来越多的业务开始使用Flutter来重构或新建其产品.但在我们 ...
- mysql的root密码忘记解决方
mysql的root密码忘记解决方 没关注第一步,直接从第二步开始,(可以参看上一篇,先停止mysql服务).然后从第二步开始. 我启动mysql用的命令是/etc/init.d/mysql sta ...
- oracle函数 current_timestamp
[功能]:以timestamp with time zone数据类型返回当前会话时区中的当前日期 [参数]:没有参数,没有括号 [返回]:日期 [示例]select current_timestamp ...
- Libev源码分析08:Libev中的信号监视器
Libev中的信号监视器,用于监控信号的发生,因信号是异步的,所以Libev的处理方式是尽量的将异步信号同步化.异步信号的同步化方法主要有:signalfd.eventfd.pipe.sigwaiti ...
- 异常解决:non-compatible bean definition of same name and class【com.xxx.xxx.XXX】
昨天同事遇到这样一个问题,意思是spring找到 有相同的实现类名在不同的package目录下. 跟踪他的项目代码并未发现问题. 重新给他的maven项目进行maven install一下. 查看 ...
- HDU3746 Teacher YYF 题解 KMP算法
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3746 题目大意:给你一个串 \(s\) ,要求 \(s\) 的开头或结尾添加最少的字符,使得添加后的串 ...
- redis 写入数据 越来越慢 是什么原因
redis并不会因为key的增加而导致写入明显变慢,肯定是其他因素.如果redis开启了持久化,在进行持久化时,性能必然下降,可以使用config命令查看持久化设置了没有.另外考虑是否是内存不足,一般 ...