今天主要学习数据变换Transforms的用法。
一般情况下,直接加载的原始数据不能直接送入神经网络进行训练,需要进行数据预处理。MindSpore提供不同种类的数据变换(Transforms),配合数据处理Pipeline来实现数据预处理。所有的Transforms均可通过map方法传入,实现对指定数据列的处理。
mindspore.dataset提供了面向图像、文本、音频等不同数据类型的Transforms,同时也支持使用Lambda函数。Transforms包含四种使用方式,Common Transforms,Vision Transforms,Text Transforms,Lambda Transforms,下面来一一介绍各种使用方法。
首先,导入必要的依赖。
import numpy as np
from PIL import Image
from download import download
from mindspore.dataset import transforms, vision, text
from mindspore.dataset import GeneratorDataset, MnistDataset
目录
一、 Common Transforms
mindspore.dataset.transforms模块支持一系列通用Transforms。我们以Compose为例,介绍其使用方式。
Compose:接收一个数据增强操作序列,然后将其组合成单个数据增强操作。我们仍基于Mnist数据集呈现Transforms的应用效果。
# 下载数据集
url = "https://mindspore-website.obs.cn-north-4.myhuaweicloud.com/" \
"notebook/datasets/MNIST_Data.zip"
path = download(url, "./", kind="zip", replace=True)
train_dataset = MnistDataset('MNIST_Data/train')
# 获取第一个样本,包括图像和标签,并打印图像的形状
image, label = next(train_dataset.create_tuple_iterator())
print(image.shape)
运行结果:
(28, 28, 1)
# 定义一个图像处理流程
composed = transforms.Compose(
[
# 缩放图像像素
vision.Rescale(1.0 / 255.0, 0),
# 标准化处理
vision.Normalize(mean=(0.1307,), std=(0.3081,)),
# 转换图像格式,从HWC(高度x宽度x通道)格式转换为CHW(通道x高度x宽度)格式
vision.HWC2CHW()
]
)
# 对训练集进行处理,然后打印图像的形状
train_dataset = train_dataset.map(composed, 'image')
image, label = next(train_dataset.create_tuple_iterator())
print(image.shape)
运行结果:
(1, 28, 28)
二、 Vision Transforms
mindspore.dataset.vision模块提供一系列针对图像数据的Transforms。在Mnist数据集的处理过程中,使用了Rescale、Normalize和HWC2CHW,下面对其进行详述。
1. Rescale
Rescale变换用于调整图像像素值的大小,包括两个参数:
· rescale:缩放因子。
· shift:平移因子。
图像的每个像素将根据这两个参数进行调整,输出的像素值为
# numpy随机生成一个像素值在[0, 255]的48*48的图像
random_np = np.random.randint(0, 255, (48, 48), np.uint8)
random_image = Image.fromarray(random_np)
print(random_np)
运行结果:
[[237 239 5 ... 203 180 36] [ 57 79 239 ... 10 238 208] [197 136 224 ... 193 49 151] ... [ 46 223 108 ... 104 156 51] [104 32 0 ... 53 168 213] [174 155 101 ... 116 141 126]]
# 对图像的像素值进行缩放,将图像数据从[0, 255]缩放到[0, 1]区间
rescale = vision.Rescale(1.0 / 255.0, 0)
rescaled_image = rescale(random_image)
print(rescaled_image)
运行结果:
[[0.9294118 0.93725497 0.01960784 ... 0.7960785 0.7058824 0.14117648] [0.22352943 0.30980393 0.93725497 ... 0.03921569 0.9333334 0.81568635] [0.7725491 0.53333336 0.87843144 ... 0.7568628 0.19215688 0.5921569 ] ... [0.18039216 0.8745099 0.42352945 ... 0.40784317 0.6117647 0.20000002] [0.40784317 0.1254902 0. ... 0.20784315 0.65882355 0.8352942 ] [0.68235296 0.60784316 0.39607847 ... 0.454902 0.5529412 0.49411768]]
2. Normalize
Normalize变换用于对输入图像的归一化,包括三个参数:
· mean:图像每个通道的均值。
· std:图像每个通道的标准差。
· is_hwc:bool值,输入图像的格式。True为(height, width, channel),False为(channel, height, width)。
图像的每个通道将根据mean和std进行调整,计算公式为:其中c代表通道索引。
normalize = vision.Normalize(mean=(0.1307,), std=(0.3081,))
normalized_image = normalize(rescaled_image)
print(normalized_image)
运行结果:
[[ 2.5923786 2.617835 -0.3605718 ... 2.1596189 1.8668692 0.03400347] [ 0.3012964 0.58131754 2.617835 ... -0.29693064 2.6051068 2.22326 ] [ 2.0832493 1.306827 2.4269116 ... 2.0323365 0.19947055 1.4977505 ] ... [ 0.1612858 2.4141834 0.9504364 ... 0.8995235 1.5613916 0.22492702] [ 0.8995235 -0.01690946 -0.42421296 ... 0.2503835 1.7141304 2.286901 ] [ 1.7904998 1.5486634 0.8613388 ... 1.0522623 1.3704681 1.1795447 ]]
3. HWC2CHW
HWC2CHW变换用于转换图像格式。在不同的硬件设备中可能会对(height, width, channel)或(channel, height, width)两种不同格式有针对性优化。MindSpore设置HWC为默认图像格式,在有CHW格式需求时,可使用该变换进行处理。
# 将前文中normalized_image处理为HWC格式,然后进行转换。可以看到转换前后的shape发生了变化。
hwc_image = np.expand_dims(normalized_image, -1)
hwc2chw = vision.HWC2CHW()
chw_image = hwc2chw(hwc_image)
print(hwc_image.shape, chw_image.shape)
运行结果:
(48, 48, 1) (1, 48, 48)
三、 Text Transforms
mindspore.dataset.text模块提供一系列针对文本数据的Transforms。与图像数据不同,文本数据需要有分词(Tokenize)、构建词表、Token转Index等操作。
首先先定义三段文本,作为待处理的数据,并使用GeneratorDataset进行加载。
# 定义三段文本,作为待处理的数据,并使用GeneratorDataset进行加载。
texts = ['Welcome to Beijing']
test_dataset = GeneratorDataset(texts, 'text')
1. PythonTokenizer
分词(Tokenize)操作是文本数据的基础处理方法,MindSpore提供多种不同的Tokenizer。这里我们选择基础的PythonTokenizer举例,此Tokenizer允许用户自由实现分词策略。随后我们利用map操作将此分词器应用到输入的文本中,对其进行分词。
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)
# 查看词表
print(vocab.vocab())
运行结果:
{'to': 2, 'Welcome': 1, 'Beijing': 0}
# 使用map方法进行词表映射变换,将Token转为Index。
test_dataset = test_dataset.map(text.Lookup(vocab))
print(next(test_dataset.create_tuple_iterator()))
运行结果:
[Tensor(shape=[3], dtype=Int32, value= [1, 2, 0])]
四、 Lambda Transforms
Lambda函数是一种不需要名字、由一个单独表达式组成的匿名函数,表达式会在调用时被求值。Lambda Transforms可以加载任意定义的Lambda函数,提供足够的灵活度。
1. 简单Lambda函数
在这里,我们首先使用一个简单的Lambda函数,对输入数据乘2:
# map传入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)]]
2. 函数中配合使用Lambda函数
另外,还可以定义函数,在函数中,配合Lambda函数实现复杂数据处理。
# 在函数中,配合Lambda函数实现复杂数据处理
def func(x):
return x * x + 2
test_dataset = test_dataset.map(lambda x: func(x))
print(list(test_dataset.create_tuple_iterator()))
运行结果:(注:test_dataset的值经过第一次处理之后,已经由原来的[1, 2, 3]变成 [2,4,6],因此再次处理之后是[6, 18, 38]。)
[[Tensor(shape=[], dtype=Int64, value= 6)], [Tensor(shape=[], dtype=Int64, value= 18)], [Tensor(shape=[], dtype=Int64, value= 38)]]