原文链接:http://fuel.readthedocs.io/en/latest/overview.html
英文比较烂,所以还是喜欢看直观的中文
综述
假设我们有8个2×2的灰度图,分为4类
import numpy
seed = 1234
rng = numpy.random.RandomState(seed)
features = rng.randint(256, size=(8, 2, 2))
targets = rng.randint(4, size=(8, 1))
没有明白randomstate是生成什么的,好像主要就是用种子生成伪随机序列。features是一个8*2*2的矩阵,最大值不超过256
人工分支
FUEL的任务:
1.与数据交互,存到硬盘或内存
2.决定访问什么数据,以什么顺序
3.迭代选择的数据点
4.每次迭代,对被筛选出的数据进行变换
图解
Datasets:数据交互
是一个抽象类,其子类是用来与数据进行交互的
构造参数:
sources:可选,用来限制对数据要求的返回源
axis_labels: 可选,用来设置轴语义.
实例参数:
sources: 一个字符串元祖,反应dataset提供的源和用get_data返回数据顺序
provides_sources: 源名称元祖,反应什么源是可以提供使用的
axis_labels: 字典, source(源)名称到字符元组或者None。用来设置数据集源的轴语义。
num_examples: 当执行时,代表datasets中样本的数量。
请求数据的方法:
open(): 返回一个state格式实体,此实体是Dataset将要用来交互的,或者返回None如果它不需要交互。
get_data(): 给定state实体和一个可选设置请求参数,返回数据。
close(): 给定state实体,正确关闭。
reset(): 对给定实体关闭并重新打开。
IterableDataset
是Dataset子类
允许与迭代实体交互
IterableDataset.open()返回迭代实体.
get_data() 不接收请求
只能迭代实例并且按序操作。
构造参数:
iterables: 字典,源名:对应迭代实体 。用 collections.OrderedDict instances 如果源顺序很重要的话。
IndexableDataset:
允许与可加索引的实体交互
IndexableDataset.open() 返回None.
get_data() 方法接受请求
允许随机路径
构造参数:
indexables: 字典,源名:对应的可加索引实体。用collections.OrderedDict 实例如果顺序对你很重要。
Datasets包含一个或多个数据源,比如图片矩阵、标签序列、字典。每个源用不同的名称定义。
IterableDataset
是Dataset最简单的子类,处理迭代实体。
from collections import OrderedDict
from fuel.datasets import IterableDataset
dataset = IterableDataset(
… iterables=OrderedDict([(‘features’, features), (‘targets’, targets)]),
… axis_labels=OrderedDict([(‘features’, (‘batch’, ‘height’, ‘width’)),
… (‘targets’, (‘batch’, ‘index’))]))
这里OrderedDict把列表转换为字典。迭代器类似自动循环或者说遍历所有输入。这个结果是一个数据结构体,恩…自己瞎起的名。数据调用dataset.iterables[0]就表示我们说的features特征。
print(‘Provided sources are {}.’.format(dataset.provides_sources))
Provided sources are (‘features’, ‘targets’).
print(‘Sources are {}.’.format(dataset.sources))
Sources are (‘features’, ‘targets’).
print(‘Axis labels are {}.’.format(dataset.axis_labels))
Axis labels are OrderedDict([(‘features’, (‘batch’, ‘height’, ‘width’)), (‘targets’, (‘batch’, ‘index’))]).
print(‘Dataset contains {} examples.’.format(dataset.num_examples))
Dataset contains 8 examples.
IterableDataset的源顺序取决于iterables的键,而在一般的字典中是没有顺序的。因此我们说如果顺序必要的话,我们建议使用collection.OrderedDict。
Datasets自己是无状态实体。为了从dataset中请求数据,我们需要请求它来实例化一些状态实体。我们用Dataset.open()方法,如果不使用此方法,那么我们只能得到dataset的4个构造属性。(自己理解,欢迎纠正)
state = dataset.open()
print(state.class.name)
imap
当数据是IterableDataset的情况,我们可以看出state是一个迭代实体imap。我们现在可以用get_data()方法访问dataset的实例。
while True:
… try:
… print(dataset.get_data(state=state))
… except StopIteration:
… print(‘Iteration over’)
… break
(array([[ 47, 211],
[ 38, 53]]), array([0]))
(array([[204, 116],
[152, 249]]), array([3]))
(array([[143, 177],
[ 23, 233]]), array([0]))
(array([[154, 30],
[171, 158]]), array([1]))
(array([[236, 124],
[ 26, 118]]), array([2]))
(array([[186, 120],
[112, 220]]), array([2]))
(array([[ 69, 80],
[201, 127]]), array([2]))
(array([[246, 254],
[175, 50]]), array([3]))
Iteration over
最终,迭代器被耗尽并且引发一个StopIteration错误。如果我们需要重新再迭代一次需要用reset()去请求一个新的迭代器。
state = dataset.reset(state=state)
print(dataset.get_data(state=state))
(array([[ 47, 211],
[ 38, 53]]), array([0]))
当你完成任务,别忘了调用dataset的close()方法于这个state上。
dataset.close(state=state)
IndexableDataset可索引dataset
IterableDataset处理是很小的。举个例子,他只是让你按顺序迭代并且例化你的数据。
如果你的数据恰好是可索引的(例如,list或者一个numpy.ndarry),那么IndexableDataset可以非常有用。
from fuel.datasets import IndexableDataset
dataset = IndexableDataset(
… indexables=OrderedDict([(‘features’, features), (‘targets’, targets)]),
… axis_labels=OrderedDict([(‘features’, (‘batch’, ‘height’, ‘width’)),
… (‘targets’, (‘batch’, ‘index’))]))
IndexableDataset比IterableDataset的主要优点就是,可以从一个任意的入口读取数据,而不像迭代型必须从头开始。所以我们必须提供一个request参数。
state = dataset.open()
print(‘State is {}.’.format(state))
State is None.
print(dataset.get_data(state=state, request=[0, 1]))
(array([[[ 47, 211],
[ 38, 53]],
[[204, 116],
[152, 249]]]), array([[0],
[3]]))
dataset.close(state=state)
这里就读出了前两个特征与分类。如果没有对应结果返回NONE。
限制源
在某些情况(例如.无监督学习),或许你想用提供源的子集。通过传递sources的参数可以实现。
restricted_dataset = IndexableDataset(
… indexables=OrderedDict([(‘features’, features), (‘targets’, targets)]),
… axis_labels=OrderedDict([(‘features’, (‘batch’, ‘height’, ‘width’)),
… (‘targets’, (‘batch’, ‘index’))]),
… sources=(‘features’,))
print(restricted_dataset.provides_sources)
(‘features’, ‘targets’)
print(restricted_dataset.sources)
(‘features’,)
state = restricted_dataset.open()
print(restricted_dataset.get_data(state=state, request=[0, 1]))
(array([[[ 47, 211],
[ 38, 53]],
[[204, 116],
[152, 249]]]),)
restricted_dataset.close(state=state)
可见,这种情况下只有features被返回了。
Iteration schemes: which examples to visit(访问的例子)
In summary
IterationScheme
抽象类.其子类是用来决定实例被访问的顺序
Methods:
get_request_iterator(): 返回一个迭代实体,实体返回请求。这些请求可以被提供给dataset的get_data()方法。
BatchScheme
抽象类,其子类返回包请求。
常用子类:
SequentialScheme: 按顺序请求批(batch)
ShuffledScheme: 随机请求batch。
IndexScheme
抽象类,其子类返回实例请求。
常用子类:
SequentialExampleScheme: 按顺序请求实例
ShuffledExampleScheme: 随机请求实例
封装和访问我们的数据是可以的,但是如果我们要把其整合到一个训练循环中,我们需要能够迭代数据。为此,我们需要决定请求的目录和其顺序。这能被IterationScheme子类实现。
在他的最基础层次,一个用来建立返回请求的迭代器的迭代计划是可行的,通过它的get_request_iterator() 方法
from fuel.schemes import (SequentialScheme, ShuffledScheme,SequentialExampleScheme, ShuffledExampleScheme)
schemes = [SequentialScheme(examples=8, batch size=4),
ShuffledScheme(examples=8, batch_size=4),
… SequentialExampleScheme(examples=8),
… ShuffledExampleScheme(examples=8)]
for scheme in schemes:
[[0, 1, 2, 3], [4, 5, 6, 7]][[7, 2, 1, 6], [0, 4, 3, 5]][0, 1, 2, 3, 4, 5, 6, 7][7, 2, 1, 6, 0, 4, 3, 5
总的来说,要得到数据就必须用get_request_iterator()方法,这里大概得到的只是索引吧。examples表示例子数,并自动生成0-7。
因此我们可以用一个迭代计划去访问一个dataset以我们需要的顺序。
state = dataset.open()
scheme = ShuffledScheme(examples=dataset.num_examples, batch_size=4)
for request in scheme.get_request_iterator():
… data = dataset.get_data(state=state, request=request)
… print(data[0].shape, data[1].shape)
(4, 2, 2) (4, 1)
(4, 2, 2) (4, 1)
dataset.close(state)
Data streams: automating the iteration process
自动化迭代进程
In summary
AbstractDataStream
抽象类。其子类是用来协调一个dataset和迭代计划来迭代数据。
迭代方法:
get_epoch_iterator(): 返回一个,用来返回例子或batch的迭代器.
构造参数:
iteration_scheme: IterationScheme实例, 可选,用来定义迭代顺序.
axis_labels: 可选,用来定义轴语义.
DataStream
最常用的data stream.
Constructor arguments:
dataset: Dataset实例, 用来迭代的dataset.
迭代计划提供了一个更加便捷的方法来访问dataset对比手动访问来说,但是我们还可以做得更好:从dataset中得到一个新的state,从迭代计划中得到请求,用二者来访问数据并且之前的方法关闭state是重复的。为了自动化这个过程,我们使用data streams,是AbstractDataStream的子类。
最常见的AbstractDataStream子类就是DataStream.用一个dataset和iteration scheme来实例化,并且返回一个纪元迭代器通过get epoch iterator()方法,即按照迭代计划在dataset迭代的结果。
from fuel.streams import DataStream
data_stream = DataStream(dataset=dataset, iteration_scheme=scheme)
for data in data_stream.get_epoch_iterator():
… print(data[0].shape, data[1].shape)
(4, 2, 2) (4, 1)
(4, 2, 2) (4, 1)
Transformers: apply some transformation on the fly
处理时应用变换
In summary
Transformer
AbstractDataStream的子类.抽象类. 其子类是用来输入一个data stream输出一个data stream, 即对输入data stream进行变换.
Transformers可以串联生成复杂变换.
Constructor arguments:
data_stream: AbstractDataStream实例,输入流
一些AbstractDataStream子类将数据流当做输入。我们称之为transformer,它们促使我们能够建立复杂的数据预处理通道。
Transformer有一下几种常用的类:
Flatten:将输入平坦化为矩阵(对批输入)或者向量(实例输入)
ScaleAndShift:将输入变为标量,缩放与平移。
Cast:将输入投影到一些数据类型。
举个例子,让我们标准化图片即减均值除以标准差
from fuel.transformers import ScaleAndShift
#Note: ScaleAndShift applies (batch * scale) + shift, as
#opposed to (batch + shift) * scale.
scale = 1.0 / features.std()
shift = - scale * features.mean()
standardized_stream = ScaleAndShift(data_stream=data_stream,
… scale=scale, shift=shift,
… which_sources=(‘features’,))
end
同理由于是AbstractDataStream,我们要使用get_epoch_iterator()
这里标准化是在通信过程(on-the-fly)中
for batch in standardized_stream.get_epoch_iterator():
… print(batch)
(array([[[ 0.18530572, -1.54479571],
[ 0.42249705, 0.24111545]],
[[-1.30760439, 0.98059429],
[-1.43317627, -1.2238898 ]],
[[ 1.46892937, 1.58054882],
[ 0.47830677, -1.2657471 ]],
[[ 0.63178351, -0.28907693],
[-0.40069638, 1.10616617]]]), array([[1],
[0],
[3],
[2]]))
(array([[[ 1.32940506, -0.2332672 ],
[-1.60060544, -0.31698179]],
[[ 0.03182898, 0.50621164],
[-1.64246273, 1.28754777]],
[[ 0.88292727, -0.34488665],
[ 0.15740086, 1.51078666]],
[[-1.00065091, -0.84717417],
[ 0.84106998, -0.19140991]]]), array([[2],
[0],
[3],
[2]]))
现在,我们假设需要float32类型的特征(如:在GPU上使用Theano代码)。我们可以把它们投影到on-the-fly,用Cast
from fuel.transformers import Cast
cast_standardized_stream = Cast(
… data_stream=standardized_stream,
… dtype=’float32’, which_sources=(‘features’,))
串联方法
data stream = Cast(
… ScaleAndShift(
… DataStream(
… dataset=dataset, iteration scheme=scheme),
… scale=scale, shift=shift, which sources=(‘features’,)),
更进一步
如何使用内建dataset:http://fuel.readthedocs.io/en/latest/built_in_datasets.html
如何导入自己的数据:http://fuel.readthedocs.io/en/latest/h5py_dataset.html
如何根据需要扩展Fuel:http://fuel.readthedocs.io/en/latest/extending_fuel.html