昇思25天学习打卡营第2天 | “孢子”的数据处理

张量(tensor)

张量(Tensor)是一种特殊的数据结构,与数组和矩阵非常相似,是网络运算中的基本数据结构。
在 MindSpore 中 张量属于 mindspore.common.tensor.Tensor 这个类。

何为张量

  1. 张量的属性:

    x = Tensor(np.array([[1, 2], [3, 4]]), mindspore.int32)
    
    print("x_shape:", x.shape)         # Tensor的shape,是一个tuple
    print("x_dtype:", x.dtype)         # Tensor的dtype,是MindSpore的一个数据类型
    print("x_itemsize:", x.itemsize)   # Tensor中每一个元素占用字节数,是一个整数
    print("x_nbytes:", x.nbytes)       # Tensor占用的总字节数,是一个整数
    print("x_ndim:", x.ndim)           # Tensor的秩,也就是len(tensor.shape),是一个整数
    print("x_size:", x.size)           # Tensor中所有元素的个数,是一个整数
    print("x_strides:", x.strides)     # Tensor每一维所需要的字节数,是一个tuple
    

    输出的结果:
    x_shape: (2, 2)
    x_dtype: Int32
    x_itemsize: 4
    x_nbytes: 16
    x_ndim: 2
    x_size: 4
    x_strides: (8, 4)

  2. 张量索引
    Tensor索引与Numpy索引类似,索引从0开始编制,负索引表示按倒序编制,冒号:和 …用于对数据进行切片操作。
    演示代码:

    tensor = Tensor(np.array([[0, 1], [2, 3]]).astype(np.float32))
    
    print("First row: {}".format(tensor[0]))
    print("value of bottom right corner: {}".format(tensor[1, 1]))
    print("Last column: {}".format(tensor[:, -1]))
    print("First column: {}".format(tensor[..., 0]))
    
  3. 稀疏张量
    稀疏张量是一种特殊张量,其中绝大部分元素的值为零。

    在某些应用场景中(比如推荐系统、分子动力学、图神经网络等),数据的特征是稀疏的,若使用普通张量表征这些数据会引入大量不必要的计算、存储和通讯开销。这时就可以使用稀疏张量来表征这些数据。

    MindSpore现在已经支持最常用的CSR和COO两种稀疏数据格式。

    • CSR(Compressed Sparse Row)稀疏张量格式有着高效的存储与计算的优势。其中,非零元素的值存储在values中,非零元素的位置存储在indptr(行)和indices(列)中
    • COO(Coordinate Format)稀疏张量格式用来表示某一张量在给定索引上非零元素的集合,若非零元素的个数为N,被压缩的张量的维数为ndims。

操作张量

  1. 四种方法创建张量

    • 根据数据直接生成
    data = [1, 0, 1, 0]
    x_data = Tensor(data)
    
    • 从NumPy数组生成
    np_array = np.array(data)
    x_np = Tensor(np_array)
    
    • 使用init初始化器构造张量
    from mindspore.common.initializer import One, Normal
    # 用 1 初始化生成一个张量
    tensor1 = mindspore.Tensor(shape=(2, 2), dtype=mindspore.float32, init=One())
    # 从正态分布初始化生成一个张量
    tensor2 = mindspore.Tensor(shape=(2, 2), dtype=mindspore.float32, init=Normal())
    
    • 继承另一个张量的属性,形成新的张量
    from mindspore import ops
    # 生成全 1 的 tensor
    x_ones = ops.ones_like(x_data)
    # 生成全 0 的 tensor
    x_zeros = ops.zeros_like(x_data)
    
  2. 六种普通的张量运算

    x = Tensor(np.array([1, 2, 3]), mindspore.float32)
    y = Tensor(np.array([4, 5, 6]), mindspore.float32)
    
    output_add = x + y            # 加(+)
    output_sub = x - y            # 减(-)
    output_mul = x * y            # 乘(*)
    output_div = y / x            # 除(/)
    output_mod = y % x            # 取模(%)
    output_floordiv = y // x      # 整除(//)
    
  3. 张量的连接和合并

    data1 = Tensor(np.array([[0, 1], [2, 3]]).astype(np.float32))
    data2 = Tensor(np.array([[4, 5], [6, 7]]).astype(np.float32))
    # concat将给定维度上的一系列张量连接起来
    output = ops.concat((data1, data2), axis=0)
    # stack则是从另一个维度上将两个张量合并起来
    output = ops.stack([data1, data2])
    
    
  4. Tensor 与 Numpy相互转换

    # 与张量创建相同,使用 Tensor.asnumpy() 将Tensor变量转换为NumPy变量。
    t = Tensor([1., 1., 1., 1., 1.])
    n = t.asnumpy()
    # 使用Tensor()将NumPy变量转换为Tensor变量。
    n = np.ones(5)
    t = Tensor.from_numpy(n)
    

数据集(dataset)

MindSpore提供基于Pipeline的数据引擎,通过数据集(Dataset)和数据变换(Transforms)实现高效的数据预处理。其中Dataset是Pipeline的起始,用于加载原始数据。mindspore.dataset提供了内置的文本、图像、音频等数据集加载接口,并提供了自定义数据集加载接口。

数据集加载

mindspore.dataset模块支持的数据集

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

输出:
<class ‘mindspore.dataset.engine.datasets_vision.MnistDataset’>

mindspore.dataset模块不支持的数据集

对于MindSpore暂不支持直接加载的数据集,可以构造自定义数据加载类或自定义数据集生成函数的方式来生成数据集,然后通过GeneratorDataset接口实现自定义方式的数据集加载。

GeneratorDataset支持通过可随机访问数据集对象、可迭代数据集对象和生成器(generator)构造自定义数据集。

可随机访问数据集
可随机访问数据集是实现了__getitem__和__len__方法的数据集,表示可以通过索引/键直接访问对应位置的数据样本。

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

# list, tuple are also supported.
loader = [np.array(0), np.array(1), np.array(2)]
dataset = GeneratorDataset(source=loader, column_names=["data"])

数据集迭代

mindspore.dataset模块支持的数据集
数据集加载后,一般以迭代方式获取数据,然后送入神经网络中进行训练。我们可以用create_tuple_iterator或create_dict_iterator接口创建数据迭代器,迭代访问数据。

访问的数据类型默认为Tensor;若设置output_numpy=True,访问的数据类型为Numpy。

代码实例:

    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

mindspore.dataset模块不支持的数据集

可迭代数据集
可迭代的数据集是实现了__iter__和__next__方法的数据集,表示可以通过迭代的方式逐步获取数据样本。这种类型的数据集特别适用于随机访问成本太高或者不可行的情况。

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

生成器
生成器也属于可迭代的数据集类型,其直接依赖Python的生成器类型generator返回数据,直至生成器抛出StopIteration异常。

# Generator
def my_generator(start, end):
    for i in range(start, end):
        yield i

# 由于生成器实例只能迭代一次,我们需要用 lambda 对其进行包装,以生成多个实例
dataset = GeneratorDataset(source=lambda: my_generator(3, 6), column_names=["data"])

数据集常用操作

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

以下是几种常见的数据集操作:

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

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

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

数据变换(transforms)

MindSpore提供不同种类的数据变换(Transforms),配合数据处理Pipeline来实现数据预处理。所有的Transforms均可通过map方法传入,实现对指定数据列的处理。

Common Transforms

mindspore.dataset.transforms模块支持一系列通用Transforms。这里以Compose为例

composed = transforms.Compose(
    [
        vision.Rescale(1.0 / 255.0, 0),
        vision.Normalize(mean=(0.1307,), std=(0.3081,)),
        vision.HWC2CHW()
    ]
)

train_dataset = train_dataset.map(composed, 'image')

Vision Transforms

mindspore.dataset.vision模块提供一系列针对图像数据的Transforms。在Mnist数据处理过程中,使用了Rescale、Normalize和HWC2CHW变换。

  1. Rescale
    Rescale变换用于调整图像像素值的大小,包括两个参数:

    • rescale:缩放因子。
    • shift:平移因子。
    random_np = np.random.randint(0, 255, (48, 48), np.uint8)
    random_image = Image.fromarray(random_np)
    
    rescale = vision.Rescale(1.0 / 255.0, 0)
    rescaled_image = rescale(random_image)
    
  2. Normalize
    Normalize变换用于对输入图像的归一化,包括三个参数:

    • mean:图像每个通道的均值。
    • std:图像每个通道的标准差。
    • is_hwc:bool值,输入图像的格式。True为(height, width, channel),False为(channel, height, width)。
    normalize = vision.Normalize(mean=(0.1307,), std=(0.3081,))
    normalized_image = normalize(rescaled_image)
    
  3. HWC2CHW
    HWC2CHW变换用于转换图像格式。在不同的硬件设备中可能会对(height, width, channel)或(channel, height, width)两种不同格式有针对性优化。MindSpore设置HWC为默认图像格式,在有CHW格式需求时,可使用该变换进行处理。

    # 先将前文中normalized_image处理为HWC格式,然后进行转换
    hwc_image = np.expand_dims(normalized_image, -1)
    hwc2chw = vision.HWC2CHW()
    chw_image = hwc2chw(hwc_image)
    

Text Transforms

mindspore.dataset.text模块提供一系列针对文本数据的Transforms。与图像数据不同,文本数据需要有分词(Tokenize)、构建词表、Token转Index等操作。

# 定义三段文本,作为待处理的数据,并使用GeneratorDataset进行加载
texts = ['Welcome to Beijing']
test_dataset = GeneratorDataset(texts, 'text')
  1. PythonTokenizer
    分词(Tokenize)操作是文本数据的基础处理方法,MindSpore提供多种不同的Tokenizer。

    def my_tokenizer(content):
        return content.split()
    
    test_dataset = test_dataset.map(text.PythonTokenizer(my_tokenizer))
    print(next(test_dataset.create_tuple_iterator()))    # 输出:[Tensor(shape=[3], dtype=String, value= ['Welcome', 'to', 'Beijing'])]
    
  2. Lookup
    Lookup为词表映射变换,用来将Token转换为Index。在使用Lookup前,需要构造词表,一般可以加载已有的词表,或使用Vocab生成词表。

    # 使用Vocab.from_dataset方法从数据集中生成词表
    vocab = text.Vocab.from_dataset(test_dataset)
    # 使用vocab方法查看词表
    print(vocab.vocab())   # 输出:{'to': 2, 'Welcome': 1, 'Beijing': 0}
    # 生成词表后,可以配合map方法进行词表映射变换,将Token转为Index
    test_dataset = test_dataset.map(text.Lookup(vocab))
    

Lambda Transforms

Lambda函数是一种不需要名字、由一个单独表达式组成的匿名函数,表达式会在调用时被求值。Lambda Transforms可以加载任意定义的Lambda函数,提供足够的灵活度。

# 使用一个简单的Lambda函数,对输入数据乘2
test_dataset = GeneratorDataset([1, 2, 3], 'data', shuffle=False)
test_dataset = test_dataset.map(lambda x: x * 2)
print(list(test_dataset.create_tuple_iterator()))   # 输出:[[Tensor(shape=[], dtype=Int64, value= 2)], [Tensor(shape=[], dtype=Int64, value= 4)], [Tensor(shape=[], dtype=Int64, value= 6)]]


# 定义较复杂的函数,配合Lambda函数实现复杂数据处理
def func(x):
    return x * x + 2
test_dataset = test_dataset.map(lambda x: func(x))
  • 14
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值