Bootstrap

《昇思25天学习打卡营第03天|qingyun201003》

日期

image.png

心得

从本次课程中学习了有关数据集的加载,数据集迭代,和数据集有关的操作,同时对数据集的来源有了认知,知道如何去获取到高质量的数据集,也充分体验到了高质量数据集对于编钟的重要性。

昇思MindSpore 基础入门学习 数据集 (AI 代码解析)

数据集介绍

数据是深度学习的基础,高质量的数据输入将在整个深度神经网络中起到积极作用。MindSpore提供基于Pipeline的数据引擎,通过数据集(Dataset)数据变换(Transforms)实现高效的数据预处理。其中Dataset是Pipeline的起始,用于加载原始数据。mindspore.dataset提供了内置的文本、图像、音频等数据集加载接口,并提供了自定义数据集加载接口。

数据集引用

import numpy as np
from mindspore.dataset import vision
from mindspore.dataset import MnistDataset, GeneratorDataset
import matplotlib.pyplot as plt

解析

这段代码主要用于导入必要的库和模块,以便进行数据处理和可视化。以下是对每个导入语句的详细解析:

  1. import numpy as np:
    • numpy 是一个强大的科学计算库,提供了高性能的多维数组对象和各种派生对象(如掩码数组和矩阵),以及用于数组快速操作的各种函数。
    • npnumpy 的别名,通常在代码中使用这个别名来调用 numpy 的功能。
  2. from mindspore.dataset import vision:
    • mindspore 是华为开发的一个深度学习框架,类似于 TensorFlow 和 PyTorch。
    • mindspore.dataset 模块提供了数据集处理的相关功能。
    • visionmindspore.dataset 中的一个子模块,提供了与计算机视觉相关的数据处理功能,如图像变换和增强。
  3. from mindspore.dataset import MnistDataset, GeneratorDataset:
    • MnistDatasetmindspore.dataset 模块中的一个类,用于加载和处理 MNIST 数据集,这是一个手写数字识别的经典数据集。
    • GeneratorDatasetmindspore.dataset 模块中的另一个类,用于从自定义数据生成器中创建数据集。
  4. import matplotlib.pyplot as plt:
    • matplotlib 是一个用于绘制二维图表和图形的库。
    • pyplotmatplotlib 的一个子模块,提供了一个类似于 MATLAB 的绘图框架。
    • pltmatplotlib.pyplot 的别名,通常在代码中使用这个别名来调用 matplotlib.pyplot 的功能。

这些导入语句为后续的数据处理、模型训练和结果可视化提供了必要的工具和功能。

数据集加载

# 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)

解析

这段代码用于从指定的 URL 下载数据集,并将其保存到本地目录。以下是对代码的详细解析:

  1. 导入 download 函数:
    • from download import download:这行代码从 download 模块中导入 download 函数。这个函数通常用于下载文件并保存到本地。
  2. 设置 URL:
    • url = "https://mindspore-website.obs.cn-north-4.myhuaweicloud.com/notebook/datasets/MNIST_Data.zip":这行代码定义了要下载的文件的 URL。这个 URL 指向一个包含 MNIST 数据集的 ZIP 文件。
  3. 调用 download 函数:
    • path = download(url, "./", kind="zip", replace=True):这行代码调用 download 函数来执行下载操作。参数解释如下:
      • url:要下载的文件的 URL。
      • "./":指定下载文件的保存路径,这里表示当前目录。
      • kind="zip":指定下载的文件类型为 ZIP 文件。
      • replace=True:如果目标路径中已经存在同名文件,则替换它。

API 解析

  • download** 函数**:
    • 参数:
      • url (str): 要下载的文件的 URL。
      • path (str): 文件保存的本地路径。
      • kind (str): 文件类型,例如 “zip”、“tar” 等。
      • replace (bool): 如果目标路径中已经存在同名文件,是否替换它。
    • 返回值:
      • path (str): 下载文件保存的本地路径。

通过这段代码,可以方便地下载并解压 MNIST 数据集到当前目录,为后续的数据处理和模型训练做准备。

train_dataset = MnistDataset("MNIST_Data/train", shuffle=False)
print(type(train_dataset))

解析

这段代码用于创建一个 MnistDataset 对象,并打印其类型。以下是对代码的详细解析:

  1. 创建 MnistDataset 对象:
    • train_dataset = MnistDataset("MNIST_Data/train", shuffle=False):这行代码创建了一个 MnistDataset 对象,用于加载 MNIST 训练数据集。参数解释如下:
      • "MNIST_Data/train":指定数据集的路径,这里指向 MNIST 训练数据集所在的目录。
      • shuffle=False:指定是否在加载数据时对数据进行随机洗牌。这里设置为 False,表示不进行洗牌。
  2. 打印 train_dataset 的类型:
    • print(type(train_dataset)):这行代码打印 train_dataset 对象的类型。通常情况下,输出应该是 <class 'mindspore.dataset.datasets.MnistDataset'>,表示这是一个 mindspore.dataset 模块中的 MnistDataset 类实例。

API 解析

  • MnistDataset** 类**:
    • 参数:
      • dataset_dir (str): 数据集所在的目录路径。
      • shuffle (bool): 是否在加载数据时对数据进行随机洗牌。
    • 方法:
      • __getitem__: 用于获取数据集中的单个样本。
      • __len__: 用于获取数据集的总样本数。

通过这段代码,可以加载 MNIST 训练数据集,并查看其类型,确保数据集正确加载。

数据集迭代

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(train_dataset)

解析

这段代码定义了一个名为 visualize 的函数,用于可视化数据集中的图像和标签。以下是对代码的详细解析:

  1. 创建图形对象:
    • figure = plt.figure(figsize=(4, 4)):创建一个大小为 4x4 英寸的图形对象。
  2. 设置子图布局:
    • cols, rows = 3, 3:定义子图的列数和行数,这里设置为 3x3 的布局。
    • plt.subplots_adjust(wspace=0.5, hspace=0.5):调整子图之间的水平和垂直间距。
  3. 遍历数据集:
    • for idx, (image, label) in enumerate(dataset.create_tuple_iterator()):使用 create_tuple_iterator 方法遍历数据集中的每个样本,获取图像和标签。
    • figure.add_subplot(rows, cols, idx + 1):在图形对象中添加子图,位置由 idx + 1 决定。
    • plt.title(int(label)):设置子图的标题为标签值。
    • plt.axis("off"):关闭子图的坐标轴。
    • plt.imshow(image.asnumpy().squeeze(), cmap="gray"):显示图像,使用灰度颜色映射。image.asnumpy().squeeze() 将图像转换为 NumPy 数组并去除多余的维度。
    • if idx == cols * rows - 1: break:当遍历到第 9 个样本时,停止遍历。
  4. 显示图形:
    • plt.show():显示创建的图形。

API 解析

  • dataset.create_tuple_iterator** 方法**:
    • 返回: 一个迭代器,每次迭代返回一个包含图像和标签的元组。
  • plt.figure** 函数**:
    • 参数:
      • figsize (tuple): 图形的大小,以英寸为单位。
  • figure.add_subplot** 方法**:
    • 参数:
      • rows (int): 子图的行数。
      • cols (int): 子图的列数。
      • index (int): 子图的位置索引。
  • plt.title** 函数**:
    • 参数:
      • label (str): 子图的标题。
  • plt.axis** 函数**:
    • 参数:
      • off (str): 关闭坐标轴。
  • plt.imshow** 函数**:
    • 参数:
      • image (numpy.ndarray): 要显示的图像。
      • cmap (str): 颜色映射,这里使用灰度颜色映射。

通过这段代码,可以可视化 MNIST 训练数据集中的前 9 个样本,帮助理解数据集的内容。

数据集常用操作

Pipeline的设计理念使得数据集的常用操作采用dataset = dataset.operation()的异步执行方式,执行操作返回新的Dataset,此时不执行具体操作,而是在Pipeline中加入节点,最终进行迭代时,并行执行Pipeline。
下面分别介绍几种常见的数据集操作。

shuffle

数据集随机shuffle可以消除数据排列造成的分布不均问题。

train_dataset = train_dataset.shuffle(buffer_size=64)

visualize(train_dataset)

解析

这段代码对 train_dataset 进行了随机洗牌操作,并再次调用 visualize 函数来可视化洗牌后的数据集。以下是对代码的详细解析:

  1. 随机洗牌数据集:
    • train_dataset = train_dataset.shuffle(buffer_size=64):这行代码对 train_dataset 进行随机洗牌。shuffle 方法会从数据集中随机抽取 buffer_size 个样本放入缓冲区,然后从这个缓冲区中随机选择样本。这里设置 buffer_size 为 64。
  2. 可视化洗牌后的数据集:
    • visualize(train_dataset):这行代码调用之前定义的 visualize 函数,传入洗牌后的 train_dataset,以可视化洗牌后的数据集中的前 9 个样本。

API 解析

  • train_dataset.shuffle** 方法**:
    • 参数:
      • buffer_size (int): 缓冲区的大小,用于随机抽取样本。
    • 返回: 洗牌后的数据集对象。

通过这段代码,可以观察到数据集在洗牌后的样本分布情况,有助于确保模型训练时不会受到数据顺序的影响。

map

map操作是数据预处理的关键操作,可以针对数据集指定列(column)添加数据变换(Transforms),将数据变换应用于该列数据的每个元素,并返回包含变换后元素的新数据集。
Dataset支持的不同变换类型详见数据变换Transforms

image, label = next(train_dataset.create_tuple_iterator())
print(image.shape, image.dtype)
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)

解析

这段代码展示了如何从数据集中获取图像和标签,并对图像进行归一化处理。以下是对代码的详细解析:

  1. 获取图像和标签:
    • image, label = next(train_dataset.create_tuple_iterator()):这行代码从 train_dataset 中获取下一个样本的图像和标签。
    • print(image.shape, image.dtype):这行代码打印图像的形状和数据类型。
  2. 对图像进行归一化处理:
    • train_dataset = train_dataset.map(vision.Rescale(1.0 / 255.0, 0), input_columns='image'):这行代码对 train_dataset 中的图像进行归一化处理。vision.Rescale 是一个图像处理操作,将图像的像素值从 [0, 255] 缩放到 [0, 1] 范围内。input_columns='image' 指定操作应用于图像列。
  3. 再次获取图像和标签并打印信息:
    • image, label = next(train_dataset.create_tuple_iterator()):这行代码再次从 train_dataset 中获取下一个样本的图像和标签。
    • print(image.shape, image.dtype):这行代码再次打印图像的形状和数据类型,以验证归一化处理后的结果。

API 解析

  • train_dataset.create_tuple_iterator** 方法**:
    • 返回: 一个迭代器,每次迭代返回一个包含图像和标签的元组。
  • vision.Rescale** 类**:
    • 参数:
      • rescale (float): 缩放因子。
      • shift (float): 偏移量。
    • 作用: 对图像进行缩放和偏移操作。
  • train_dataset.map** 方法**:
    • 参数:
      • operations (transform): 要应用的图像处理操作。
      • input_columns (str): 指定操作应用于的列名。
    • 返回: 应用了指定操作后的数据集对象。

通过这段代码,可以验证图像在归一化处理前后的形状和数据类型,确保归一化操作正确执行。

batch

将数据集打包为固定大小的batch是在有限硬件资源下使用梯度下降进行模型优化的折中方法,可以保证梯度下降的随机性和优化计算量。

train_dataset = train_dataset.batch(batch_size=32)

image, label = next(train_dataset.create_tuple_iterator())
print(image.shape, image.dtype)

解析

这段代码对 train_dataset 进行了批处理操作,并获取一个批次的数据。以下是对代码的详细解析:

  1. 批处理数据集:
    • train_dataset = train_dataset.batch(batch_size=32):这行代码将 train_dataset 分批处理,每批包含 32 个样本。batch 方法会将数据集中的样本按指定的大小分组,形成批次。
  2. 获取一个批次的数据:
    • image, label = next(train_dataset.create_tuple_iterator()):这行代码从 train_dataset 中获取下一个批次的数据。由于数据集已经分批处理,这里获取的是一个包含 32 个样本的批次。
    • print(image.shape, image.dtype):这行代码打印图像批次的形状和数据类型。由于每个批次包含 32 个样本,图像的形状会变成 (32, height, width, channels),其中 heightwidth 是图像的高度和宽度,channels 是图像的通道数。

API 解析

  • train_dataset.batch** 方法**:
    • 参数:
      • batch_size (int): 每个批次的大小。
    • 返回: 分批处理后的数据集对象。
  • train_dataset.create_tuple_iterator** 方法**:
    • 返回: 一个迭代器,每次迭代返回一个包含图像和标签的元组。

通过这段代码,可以验证数据集在分批处理后的形状和数据类型,确保批次大小正确设置。

自定义数据集

mindspore.dataset模块提供了一些常用的公开数据集和标准格式数据集的加载API。
对于MindSpore暂不支持直接加载的数据集,可以构造自定义数据加载类或自定义数据集生成函数的方式来生成数据集,然后通过GeneratorDataset接口实现自定义方式的数据集加载。
GeneratorDataset支持通过可随机访问数据集对象、可迭代数据集对象和生成器(generator)构造自定义数据集,下面分别对其进行介绍。

可随机访问数据集

可随机访问数据集是实现了__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)

解析

这段代码定义了一个随机访问的数据集类 RandomAccessDataset,并使用 GeneratorDataset 将其转换为可迭代的数据集。以下是对代码的详细解析:

  1. 定义 RandomAccessDataset:
    • __init__ 方法:初始化数据和标签。这里 _data 是一个形状为 (5, 2) 的数组,所有元素都为 1;_label 是一个形状为 (5, 1) 的数组,所有元素都为 0。
    • __getitem__ 方法:根据索引返回对应的数据和标签。
    • __len__ 方法:返回数据的长度。
  2. 创建 RandomAccessDataset 实例:
    • loader = RandomAccessDataset():创建一个 RandomAccessDataset 实例。
  3. 使用 GeneratorDataset 转换数据集:
    • dataset = GeneratorDataset(source=loader, column_names=["data", "label"]):将 RandomAccessDataset 实例转换为 GeneratorDataset,并指定列名为 "data""label"
  4. 迭代数据集并打印数据:
    • for data in dataset: print(data):迭代 dataset 并打印每个样本的数据。

API 解析

  • GeneratorDataset** 类**:
    • 参数:
      • source (Iterable): 数据源,可以是可迭代对象或生成器。
      • column_names (list of str): 列名列表,指定数据集的列名。
    • 作用: 将数据源转换为可迭代的数据集。

通过这段代码,可以实现对自定义数据集的随机访问和迭代,适用于各种数据加载和预处理场景。

# 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)

解析

这段代码展示了如何使用列表作为数据源来创建 GeneratorDataset,并迭代打印数据。以下是对代码的详细解析:

  1. 定义数据源:
    • loader = [np.array(0), np.array(1), np.array(2)]:创建一个包含三个 NumPy 数组的列表,分别是 [0, 1, 2]
  2. 使用 GeneratorDataset 转换数据集:
    • dataset = GeneratorDataset(source=loader, column_names=["data"]):将列表 loader 转换为 GeneratorDataset,并指定列名为 "data"
  3. 迭代数据集并打印数据:
    • for data in dataset: print(data):迭代 dataset 并打印每个样本的数据。

API 解析

  • GeneratorDataset** 类**:
    • 参数:
      • source (Iterable): 数据源,可以是可迭代对象或生成器。
      • column_names (list of str): 列名列表,指定数据集的列名。
    • 作用: 将数据源转换为可迭代的数据集。

通过这段代码,可以实现对列表数据源的迭代,适用于各种数据加载和预处理场景。

可迭代数据集

可迭代的数据集是实现了__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)

解析

这段代码定义了一个可迭代的数据集类 IterableDataset,并使用 GeneratorDataset 将其转换为可迭代的数据集。以下是对代码的详细解析:

  1. 定义 IterableDataset:
    • __init__ 方法:初始化数据集的起始和结束值。
    • __next__ 方法:返回下一个数据项。
    • __iter__ 方法:重置迭代器,并返回自身。
  2. 创建 IterableDataset 实例:
    • loader = IterableDataset(1, 5):创建一个 IterableDataset 实例,范围从 1 到 5。
  3. 使用 GeneratorDataset 转换数据集:
    • dataset = GeneratorDataset(source=loader, column_names=["data"]):将 IterableDataset 实例转换为 GeneratorDataset,并指定列名为 "data"
  4. 迭代数据集并打印数据:
    • for d in dataset: print(d):迭代 dataset 并打印每个样本的数据。

API 解析

  • GeneratorDataset** 类**:
    • 参数:
      • source (Iterable): 数据源,可以是可迭代对象或生成器。
      • column_names (list of str): 列名列表,指定数据集的列名。
    • 作用: 将数据源转换为可迭代的数据集。

通过这段代码,可以实现对自定义可迭代数据集的迭代,适用于各种数据加载和预处理场景。

生成器

生成器也属于可迭代的数据集类型,其直接依赖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)

解析

这段代码定义了一个生成器函数 my_generator,并使用 GeneratorDataset 将其转换为可迭代的数据集。由于生成器实例只能迭代一次,因此通过 lambda 函数来生成多个实例。以下是对代码的详细解析:

  1. **定义生成器函数 **my_generator:
    • my_generator(start, end):生成从 startend 的整数序列。
  2. 使用 lambda 函数包装生成器:
    • lambda: my_generator(3, 6):创建一个 lambda 函数,每次调用时生成一个新的生成器实例。
  3. 使用 GeneratorDataset 转换数据集:
    • dataset = GeneratorDataset(source=lambda: my_generator(3, 6), column_names=["data"]):将 lambda 函数作为数据源,转换为 GeneratorDataset,并指定列名为 "data"
  4. 迭代数据集并打印数据:
    • for d in dataset: print(d):迭代 dataset 并打印每个样本的数据。

API 解析

  • GeneratorDataset** 类**:
    • 参数:
      • source (Iterable): 数据源,可以是可迭代对象或生成器。
      • column_names (list of str): 列名列表,指定数据集的列名。
    • 作用: 将数据源转换为可迭代的数据集。

通过这段代码,可以实现对生成器数据源的迭代,适用于需要多次迭代的数据加载和预处理场景。

;