1、数据集Dataset
数据是深度学习的基础,高质量的数据输入将在整个深度神经网络中起到积极作用。MindSpore提供基于Pipeline(流水线)的数据引擎数据引擎数据引擎,通过数据集(Dataset)和数据变换(Transforms)实现高效的数据预处理。其中Dataset是Pipeline的起始,用于加载原始数据。mindspore.dataset
提供了内置的文本、图像、音频等数据集加载接口,并提供了自定义数据集加载接口。
1.1 Pipeline介绍
如上图所示,MindSpore Dataset模块使得用户很简便地定义数据预处理Pipeline,并以最高效(多进程/多线程)的方式处理 数据集中样本,具体的步骤参考如下:
-
加载数据集(Dataset):用户可以方便地使用 *Dataset 类来加载已支持的数据集,或者通过 UDF Loader + GeneratorDataset 实现Python层自定义数据集的加载,同时加载类方法可以使用多种Sampler、数据分片、数据shuffle等功能;
-
数据集操作(filter/ skip):用户通过数据集对象方法 .shuffle / .filter / .skip / .split / .take / … 来实现数据集的进一步混洗、过滤、跳过、最多获取条数等操作;
-
数据集样本增强操作(map):用户可以将数据增强操作 (vision类 , nlp类 , audio类 ) 添加到map操作中执行,数据预处理过程中可以定义多个map操作,用于执行不同增强操作,数据增强操作也可以是 用户自定义增强的 PyFunc ;
-
批(batch):用户在样本完成增强后,使用 .batch 操作将多个样本组织成batch,也可以通过batch的参数 per_batch_map 来自定义batch逻辑;
-
迭代器(create_dict_iterator):最后用户通过数据集对象方法 create_dict_iterator 来创建迭代器, 可以将预处理完成的数据循环输出。
2、数据集加载
2.1 添加库
import numpy as np
from mindspore.dataset import vision
from mindspore.dataset import MnistDataset, GeneratorDataset
import matplotlib.pyplot as plt
2.2 mindspore.dataset 加载
mindspore.dataset 接口仅支持解压后的数据文件,使用download库下载数据集并解压。
# Download data from open datasets
from download import download
url = "https://mindspore-website.obs.cn-north-4.myhuaweicloud.com/" \
"notebook/datasets/MNIST_Data.zip"
path = download(url, "./", kind="zip", replace=True)
这里下载的数据集在构建的.ipynb文件在同一目录下。
2.3 查看数据类型
train_dataset = MnistDataset("MNIST_Data/train", shuffle=False)
print(type(train_dataset))
3、数据集迭代
数据集加载后,一般以迭代方式获取数据,然后送入神经网络中进行训练。我们可以用create_tuple_iterator或create_dict_iterator接口创建数据迭代器,迭代访问数据。访问的数据类型默认为Tensor
;若设置output_numpy=True
,访问的数据类型为Numpy
。
mindspore.dataset.Dataset.create_tuple_iterator(columns=None, num_epochs=-1, output_numpy=False, do_copy=True)创建数据集迭代器,返回列表形式的样本,其中的元素为各列数据。
【参数】
-
columns (list[str], 可选) - 指定输出数据列及其顺序。默认值:
None
,保留所有输出列及其原始顺序。 -
num_epochs (int, 可选) - 数据集迭代次数。默认值:
-1
,数据集可以无限迭代。 -
output_numpy (bool, 可选) - 是否保持输出数据类型为 NumPy 数组,否则转换为 mindspore.Tensor 。默认值:
False
。 -
do_copy (bool, 可选) - 指定转换输出类型为 mindspore.Tensor 时是否拷贝数据,否则直接复用数据缓冲区以获得更好的性能,仅当 output_numpy 为
False
时有效。默认值:True
。
mindspore.dataset.Dataset.create_dict_iterator(num_epochs=-1, output_numpy=False, do_copy=True)创建数据集迭代器,返回字典形式的样本,其中键为列名,值为数据。
【参数】
-
num_epochs (int, 可选) - 数据集迭代次数。默认值:
-1
,数据集可以无限迭代。 -
output_numpy (bool, 可选) - 是否保持输出数据类型为 NumPy 数组,否则转换为 mindspore.Tensor 。默认值:
False
。 -
do_copy (bool, 可选) - 指定转换输出类型为 mindspore.Tensor 时是否拷贝数据,否则直接复用数据缓冲区以获得更好的性能,仅当 output_numpy 为
False
时有效。默认值:True
。
#下面定义一个可视化函数,迭代9张图片进行展示。
def visualize(dataset):
figure = plt.figure(figsize=(4, 4))
cols, rows = 3, 3
plt.subplots_adjust(wspace=0.5, hspace=0.5)
for idx, (image, label) in enumerate(dataset.create_tuple_iterator()):
figure.add_subplot(rows, cols, idx + 1)
plt.title(int(label))
plt.axis("off")
plt.imshow(image.asnumpy().squeeze(), cmap="gray")
if idx == cols * rows - 1:
break
plt.show()
调用visualize()。
visualize(train_dataset)
【运行结果】这里是迭代展示,所以每次展示的效果不同。
4、数据集常用操作
数据集操作(filter/ skip):用户通过数据集对象方法 .shuffle / .filter / .skip / .split / .take / … 来实现数据集的进一步混洗、过滤、跳过、最多获取条数等操作;
4.1 Shuffle 操作
Shuffle操作,就是将一组有规则数据重新转换为一组无规则数据,分布式计算中引申为集群范围内跨节点、跨进程的数据分发。数据集随机shuffle
可以消除数据排列造成的分布不均问题。
mindspore.dataset.Dataset.shuffle(buffer_size)
通过创建 buffer_size 大小的缓存来混洗该数据集。
-
生成一个混洗缓冲区包含 buffer_size 条数据行。
-
从混洗缓冲区中随机选择一个数据行,传递给下一个操作。
-
从上一个操作获取下一个数据行(如果有的话),并将其放入混洗缓冲区中。
-
重复步骤2和3,直到混洗缓冲区中没有数据行为止。
【参数】
-
buffer_size (int) - 用于混洗的缓冲区大小(必须大于1)。将 buffer_size 设置为数据集大小将进行全局混洗。
train_dataset = train_dataset.shuffle(buffer_size=64)
visualize(train_dataset)
【运行结果】展示随机同上。
4.2 map 操作
map操作是数据预处理的关键操作,可以针对数据集指定列(column)添加数据变换(Transforms),将数据变换应用于该列数据的每个元素,并返回包含变换后元素的新数据集。
mindspore.dataset.Dataset.map(operations, input_columns=None, output_columns=None, column_order=None, num_parallel_workers=None, **kwargs)
给定一组数据增强列表,按顺序将数据增强作用在数据集对象上。
每个数据增强操作将数据集对象中的一个或多个数据列作为输入,将数据增强的结果输出为一个或多个数据列。 第一个数据增强操作将 input_columns 中指定的列作为输入。 如果数据增强列表中存在多个数据增强操作,则上一个数据增强的输出列将作为下一个数据增强的输入列。
最后一个数据增强的输出列的列名由 output_columns 指定,如果没有指定 output_columns ,输出列名与 input_columns 一致。
【参数】
-
operations (Union[list[TensorOperation], list[functions]]) - 一组数据增强操作,支持数据集增强操作或者用户自定义的Python Callable对象。map操作将按顺序将一组数据增强作用在数据集对象上。
-
input_columns (Union[str, list[str]], 可选) - 第一个数据增强操作的输入数据列。此列表的长度必须与 operations 列表中第一个数据增强的预期输入列数相匹配。默认值:
None
。表示所有数据列都将传递给第一个数据增强操作。 -
output_columns (Union[str, list[str]], 可选) - 最后一个数据增强操作的输出数据列。如果 input_columns 长度不等于 output_columns 长度,则必须指定此参数。列表的长度必须必须与最后一个数据增强的输出列数相匹配。默认值:
None
,输出列将与输入列具有相同的名称。 -
num_parallel_workers (int, 可选) - 指定map操作的多进程/多线程并发数,加快处理速度。默认值:
None
,将使用 set_num_parallel_workers 设置的并发数。 -
**kwargs - 其他参数。
-
python_multiprocessing (bool, 可选) - 启用Python多进程模式加速map操作。当传入的 operations 计算量很大时,开启此选项可能会有较好效果。默认值:
False
。 -
max_rowsize (Union[int, list[int]], 可选) - 指定在多进程之间复制数据时,共享内存分配的基本单位,总占用的共享内存会随着
num_parallel_workers
和 mindspore.dataset.config.set_prefetch_size() 增加而变大,仅当 python_multiprocessing 为True
时,该选项有效。如果是int值,代表input_columns
和output_columns
均使用该值为单位创建共享内存;如果是列表,第一个元素代表input_columns
使用该值为单位创建共享内存,第二个元素代表output_columns
使用该值为单位创建共享内存。默认值:16
,单位为MB。 -
cache (DatasetCache, 可选) - 单节点数据缓存服务,用于加快数据集处理,详情请阅读 单节点数据缓存 。默认值:
None
,不使用缓存。 -
callbacks (DSCallback, list[DSCallback], 可选) - 要调用的Dataset回调函数列表。默认值:
None
。 -
offload (bool, 可选) - 是否进行异构硬件加速,详情请阅读 数据准备异构加速 。默认值:
None
。
-
image, label = next(train_dataset.create_tuple_iterator())
print(image.shape, image.dtype)
#输出结果---> (28, 28, 1) UInt8
对Mnist数据集做数据缩放处理,将图像统一除以255,数据类型由uint8转为了float32。
train_dataset = train_dataset.map(vision.Rescale(1.0 / 255.0, 0), input_columns='image')
image, label = next(train_dataset.create_tuple_iterator())
print(image.shape, image.dtype)
#输出结果---> (28, 28, 1) Float32
对比map前后的数据,可以看到数据类型变化。
4.3 batch 操作
将数据集打包为固定大小的batch
是在有限硬件资源下使用梯度下降进行模型优化的折中方法,可以保证梯度下降的随机性和优化计算量。
batch后的数据增加一维,大小为batch_size。
train_dataset = train_dataset.batch(batch_size=32)
image, label = next(train_dataset.create_tuple_iterator())
print(image.shape, image.dtype)
5、自定义数据集
mindspore.dataset
模块提供了一些常用的公开数据集和标准格式数据集的加载API。
对于MindSpore暂不支持直接加载的数据集,可以构造自定义数据加载类或自定义数据集生成函数的方式来生成数据集,然后通过GeneratorDataset
接口实现自定义方式的数据集加载。
GeneratorDataset
支持通过可随机访问数据集对象、可迭代数据集对象和生成器(generator)构造自定义数据集,下面分别对其进行介绍。
5.1 可随机访问数据集
可随机访问数据集是实现了__getitem__
和__len__
方法的数据集,表示可以通过索引/键直接访问对应位置的数据样本。
例如,当使用dataset[idx]
访问这样的数据集时,可以读取dataset内容中第idx个样本或标签。
# Random-accessible object as input source
class RandomAccessDataset:
def __init__(self):
self._data = np.ones((5, 2))
self._label = np.zeros((5, 1))
def __getitem__(self, index):
return self._data[index], self._label[index]
def __len__(self):
return len(self._data)
loader = RandomAccessDataset()
dataset = GeneratorDataset(source=loader, column_names=["data", "label"])
for data in dataset:
print(data)
# list, tuple are also supported.
loader = [np.array(0), np.array(1), np.array(2)]
dataset = GeneratorDataset(source=loader, column_names=["data"])
for data in dataset:
print(data)
5.2 可迭代数据集
可迭代的数据集是实现了__iter__
和__next__
方法的数据集,表示可以通过迭代的方式逐步获取数据样本。这种类型的数据集特别适用于随机访问成本太高或者不可行的情况。
例如,当使用iter(dataset)
的形式访问数据集时,可以读取从数据库、远程服务器返回的数据流。
下面构造一个简单迭代器,并将其加载至GeneratorDataset
。
# Iterator as input source
class IterableDataset():
def __init__(self, start, end):
'''init the class object to hold the data'''
self.start = start
self.end = end
def __next__(self):
'''iter one data and return'''
return next(self.data)
def __iter__(self):
'''reset the iter'''
self.data = iter(range(self.start, self.end))
return self
loader = IterableDataset(1, 5)
dataset = GeneratorDataset(source=loader, column_names=["data"])
for d in dataset:
print(d)
5.3 生成器
生成器也属于可迭代的数据集类型,其直接依赖Python的生成器类型generator
返回数据,直至生成器抛出StopIteration
异常。
下面构造一个生成器,并将其加载至GeneratorDataset
。
# Generator
def my_generator(start, end):
for i in range(start, end):
yield i
# since a generator instance can be only iterated once, we need to wrap it by lambda to generate multiple instances
dataset = GeneratorDataset(source=lambda: my_generator(3, 6), column_names=["data"])
for d in dataset:
print(d)