这里以MNIST为例
这里有讲
我们需要再下一个库,方便我们下载数据集:
pip install download -i https://pypi.douban.com/simple/
以后用到pip安装时就不多说pypi源了,方便大家直接下载。
# 从开源数据集下载
from download import download
# 定义url地址
url = "https://mindspore-website.obs.cn-north-4.myhuaweicloud.com/notebook/datasets/MNIST_Data.zip"
# 下载并解压
path = download(url, "./", kind="zip", replace=True)
我们还需要一个解析该文件的库,mindspore提供了这个功能:
from mindspore.dataset import MnistDataset
然后:
train_dataset = MnistDataset('MNIST_Data/train')
test_dataset = MnistDataset('MNIST_Data/test')
就可以获得数据集对象了。
压缩文件删除后,直接加载,可以看到其数据类型为MnistDataset。
type(train_dataset)
mindspore.dataset.engine.datasets_vision.MnistDataset
这里有讲,看看数据集中:
使用 create_tuple_iterator
或 create_dict_iterator
对数据集进行迭代。这里我说明 create_dict_iterator
:创建字典迭代:
for data in test_dataset.create_dict_iterator():
print(f"Shape of image [N, C, H, W]: {data['image'].shape} {data['image'].dtype}")
print(f"Shape of label: {data['label'].shape} {data['label'].dtype}")
break
Shape of image [N, C, H, W]: (64, 1, 28, 28) Float32
Shape of label: (64,) Int32
当然我们也可以使用next获取:
dit = next(test_dataset.create_dict_iterator())
print(f"Shape of image [N, C, H, W]: {dit['image'].shape} {dit['image'].dtype}")
print(f"Shape of label: {dit['label'].shape} {dit['label'].dtype}")
我比较喜欢这样,因为就不需要再刻意打一个无意义的for循环。
同时,我们也输出一下我们的图片。
我们需要安装一个库:
pip3 install -i https://pypi.doubanio.com/simple/ matplotlib
先把数据集转换为numpy类型。
import numpy
image = dit['image'].asnumpy()
转换后再用plt库显示图片:
import matplotlib.pyplot as plt
plt.imshow(image) # 显示图片第0张第0维
数据集加载后,一般以迭代方式获取数据,然后送入神经网络中进行训练。我们可以用create_tuple_iterator或create_dict_iterator接口创建数据迭代器,迭代访问数据。
访问的数据类型默认为Tensor;若设置output_numpy=True,访问的数据类型为Numpy。
下面定义一个可视化函数,迭代9张图片进行展示。
def visualize(dataset):
figure = plt.figure(figsize=(4, 4))
cols, rows = 3, 3
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(train_dataset)
有时候我们是需要对数据集进行操作,如:
故事:在一个国度里,国王AK的夫人%%%生下一个孩子Orz,Orz2岁了,应该学习数数了。%%%便教孩子数:
1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 1,2,3,4,5,6,7,8,9,10 1,2,3,4,5,6,7,8,9,10
Orz:
1 , 2... 1,2... 1,2...
%%%:
1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 1,2,3,4,5,6,7,8,9,10 1,2,3,4,5,6,7,8,9,10
Orz:
1 , 2 , 3 , 4 , 5 , 6... 1,2,3,4,5,6... 1,2,3,4,5,6...
几天后,Orz终于会数了:
1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 1,2,3,4,5,6,7,8,9,10 1,2,3,4,5,6,7,8,9,10
AK国王听后很开心,便打乱来考:
1 , 9 , 2 , 6 , 3 , 5 , 7 , 4 , 10 , 8 1,9,2,6,3,5,7,4,10,8 1,9,2,6,3,5,7,4,10,8
Orz:
1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 1,2,3,4,5,6,7,8,9,10 1,2,3,4,5,6,7,8,9,10
AK很生气,于是。。。
好了,故事结束了,其实%%%公主应该打乱来教的,不然Orz以为一定要
1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 1,2,3,4,5,6,7,8,9,10 1,2,3,4,5,6,7,8,9,10
来读。
这如果在深度学习中也是不好,所以我们需要用到 shuffle 来打乱。
shuffle_dataset = train_dataset.shuffle(buffer_size=64)
visualize(shuffle_dataset)
就可以发现和原来不一样了。
这里有一个很费解的东西。什么是 buffer_size ?
首先呢,我们有一些数据 a。
我们从 a 中取出前 buffer_size 条数据到 buffer 中。
从 buffer 中随机取出一条
放入输出,这时 buffer 就空了一个是吧?
从 a 中再取出第一个填空
重复2条,直至 a 中没有数据
那么buffer_size的取值就十分重要,如果取了一些错误值,shuffle可能会没有任何效果。
一个人背一包重10kg的东西上山,怎样最省力?
打车
嗯嗯,我们不说这个,答案是 一包一包拿。
同样,我们不可能一下把10kg搬上山,我们可以往返10次,每次拿10kg。这样可以减轻你的负担。
在训练时,我们也不能一下把上万的数据一次训练完(超能力除外),这时候就需要batch打包了。
我们通过 batch(batch_size) 把数据打包为batch_size个包。
train_dataset = train_dataset.batch(batch_size=32)
batch后的数据增加一维,大小为 batch_size 。
image, label = next(train_dataset.create_tuple_iterator())
print(image.shape, image.dtype)
(32, 28, 28, 1) Float32
map 操作是数据预处理的关键操作,可以针对数据集指定列(column)添加数据变换(Transforms),将数据变换应用于该列数据的每个元素,并返回包含变换后元素的新数据集。
from mindspore.dataset import vision
image, label = next(train_dataset.create_tuple_iterator())
print(image.shape, image.dtype)
(28, 28, 1) UInt8
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
更多变换详见下一篇
当我们拥有一些数据时,我们可以自定义一个数据集。可以通过构造自定义数据集类或自定义数据集生成函数的方式来生成数据集,然后通过 GeneratorDataset 接口实现自定义方式的数据集加载。
GeneratorDataset 支持通过可迭代对象、迭代器和生成函数构造自定义数据集,下面分别对其进行详解。
dataset = GeneratorDataset(source=np.array([[1, 0, 2, 4]]), column_names=["col"])
Python中可以使用 for
循环遍历出所有元素的,都可以称为可迭代对象(Iterable),我们可以通过实现 __getitem__
方法来构造可迭代对象,并将其加载至 GeneratorDataset
。
# 可迭代对象
class Iterable:
def __init__(self):
self._data = np.random.sample((5, 2))
self._label = np.random.sample((5, 1))
def __getitem__(self, index):
return self._data[index], self._label[index]
def __len__(self):
return len(self._data)
data = Iterable()
dataset = GeneratorDataset(source=data, column_names=["data", "label"])
Python中内置有 __iter__
和 __next__
方法的对象,称为迭代器(Iterator)。下面构造一个简单迭代器,并将其加载至 GeneratorDataset
。
# 迭代器
class Iterator:
def __init__(self):
self._index = 0
self._data = np.random.sample((5, 2))
self._label = np.random.sample((5, 1))
def __next__(self):
if self._index >= len(self._data):
raise StopIteration
else:
item = (self._data[self._index], self._label[self._index])
self._index += 1
return item
def __iter__(self):
self._index = 0
return self
def __len__(self):
return len(self._data)
data = Iterator()
dataset = GeneratorDataset(source=data, column_names=["data", "label"])
请看 np.array([[1, 0, 2, 4]]
与 np.random.sample((5, 2))
的结果。他们都是:
[[num1, num2, ...]]
我们发现外面加上了[],这是为什么呢?回想一下我们怎么迭代数据集的,是吧?取第一项。我们可以作此验证:
tmp = np.array([[1,2,3,4,5]])
for i in tmp:
print(i)
[1 2 3 4 5]
我们可以发现这正合我意,那如果不加[]呢?
tmp = np.array([1,2,3,4,5])
for i in tmp:
print(i)
1
2
3
4
5
我们发现它变成了一项一项单独的数据,这不是我们想要的,所以我们还要加一层[]防止把数据分开了。
运行上面代码可能会出现警告,别担心,这只是Windows端的多线程的兼容问题了。Mindspore是并行的。