
数据加载与处理数据是深度学习的基础高质量的数据输入将在整个深度神经网络中起到积极作用。MindSpore提供基于Pipeline的数据引擎通过数据集Dataset、数据变换Transforms和数据批处理Batch可以实现高效的数据预处理。其中数据集Dataset是Pipeline的起始用于从存储中加载原始数据至内存。mindspore.dataset提供了内置的图像、文本、音频等数据集加载接口同时支持自定义数据集加载接口数据变换Transforms对内存中的数据做进一步的变换操作mindspore.dataset.transforms提供通用的数据变换操作、mindspore.dataset.transforms.vision提供图像数据变换操作、mindspore.dataset.transforms.text提供文本数据变换操作、mindspore.dataset.transforms.audio提供音频数据变换操作数据批处理Batch完成对变换后的数据组批处理Batch用于最终的神经网络训练Batch操作是针对一个数据集对象其接口可参考batch操作数据集迭代器是将最后的数据通过迭代的方式输出迭代器也是针对一个数据集对象其接口可参考迭代器。此外MindSpore的领域开发库也提供了大量的预加载数据集可以使用API一键下载使用。本教程将分别对不同的数据集Dataset加载方式自定义数据集、标准格式数据集和常见数据集数据变换Transforms和数据batch方法进行详细阐述。importosimportnumpyasnpfrommindsporeimportdtypeasmstypefrommindspore.datasetimporttransformsfrommindspore.datasetimportvisionfrommindspore.datasetimportMindDataset,GeneratorDataset,MnistDataset,NumpySlicesDatasetfrommindspore.mindrecordimportFileWriterimportmatplotlib.pyplotasplt数据集加载mindspore.dataset模块提供了自定义数据集、标准格式数据集和一些常用的公开数据集的加载API。自定义数据集对于MindSpore暂不支持直接加载的数据集可以构造自定义数据加载类或自定义数据集生成函数的方式来生成数据集然后通过GeneratorDataset接口实现自定义方式的数据集加载。GeneratorDataset支持通过可随机访问数据集对象、可迭代数据集对象和生成器(generator)构造自定义数据集下面分别对其进行介绍。可随机访问数据集可随机访问数据集是实现了__getitem__和__len__方法的数据集表示可以通过索引/键直接访问对应位置的数据样本。例如当使用dataset[idx]访问这样的数据集时可以读取dataset内容中第idx个样本或标签。# Random-accessible object as input sourceclassRandomAccessDataset:def__init__(self):self._datanp.ones((5,2))self._labelnp.zeros((5,1))def__getitem__(self,index):returnself._data[index],self._label[index]def__len__(self):returnlen(self._data)loaderRandomAccessDataset()datasetGeneratorDataset(sourceloader,column_names[data,label])fordataindataset:print(data)[Tensor(shape[2], dtypeFloat64, value [1., 1.]), Tensor(shape[1], dtypeFloat64, value [0.])] [Tensor(shape[2], dtypeFloat64, value [1., 1.]), Tensor(shape[1], dtypeFloat64, value [0.])] [Tensor(shape[2], dtypeFloat64, value [1., 1.]), Tensor(shape[1], dtypeFloat64, value [0.])] [Tensor(shape[2], dtypeFloat64, value [1., 1.]), Tensor(shape[1], dtypeFloat64, value [0.])] [Tensor(shape[2], dtypeFloat64, value [1., 1.]), Tensor(shape[1], dtypeFloat64, value [0.])]# list, tuple are also supported.loader[np.array(0),np.array(1),np.array(2)]datasetGeneratorDataset(sourceloader,column_names[data])fordataindataset:print(data)[Tensor(shape[], dtypeInt32, value 2)] [Tensor(shape[], dtypeInt32, value 0)] [Tensor(shape[], dtypeInt32, value 1)]可迭代数据集可迭代的数据集是实现了__iter__和__next__方法的数据集表示可以通过迭代的方式逐步获取数据样本。这种类型的数据集特别适用于随机访问成本太高或者不可行的情况。例如当使用iter(dataset)的形式访问数据集时可以读取从数据库、远程服务器返回的数据流。下面构造一个简单迭代器并将其加载至GeneratorDataset。# Iterator as input sourceclassIterableDataset():def__init__(self,start,end):init the class object to hold the dataself.startstart self.endenddef__next__(self):iter one data and returnreturnnext(self.data)def__iter__(self):reset the iterself.dataiter(range(self.start,self.end))returnself loaderIterableDataset(1,5)datasetGeneratorDataset(sourceloader,column_names[data])fordindataset:print(d)[Tensor(shape[], dtypeInt32, value 1)] [Tensor(shape[], dtypeInt32, value 2)] [Tensor(shape[], dtypeInt32, value 3)] [Tensor(shape[], dtypeInt32, value 4)]生成器生成器也属于可迭代的数据集类型其直接依赖Python的生成器类型generator返回数据直至生成器抛出StopIteration异常。下面构造一个生成器并将其加载至GeneratorDataset。# Generatordefmy_generator(start,end):foriinrange(start,end):yieldi# since a generator instance can be only iterated once, we need to wrap it by lambda to generate multiple instancesdatasetGeneratorDataset(sourcelambda:my_generator(3,6),column_names[data])fordindataset:print(d)[Tensor(shape[], dtypeInt32, value 3)] [Tensor(shape[], dtypeInt32, value 4)] [Tensor(shape[], dtypeInt32, value 5)]标准格式数据集对于MindSpore暂不支持直接加载的数据集可以将数据集转换成MindRecord格式数据集然后通过mindspore.dataset.MindDataset接口实现数据集加载。首先通过MindRecord格式接口FileWriter创建一个新的MindRecord格式数据集其中每个样本包含file_name、label和data三个字段。ifos.path.exists(./test.mindrecord):os.remove(./test.mindrecord)ifos.path.exists(./test.mindrecord.db):os.remove(./test.mindrecord.db)writerFileWriter(file_nametest.mindrecord,shard_num1,overwriteTrue)schema_json{file_name:{type:string},label:{type:int32},data:{type:int32,shape:[-1]}}writer.add_schema(schema_json,test_schema)foriinrange(4):data[{file_name:str(i).jpg,label:i,data:np.array([i]*(i1),dtypenp.int32)}]writer.write_raw_data(data)writer.commit()然后通过MindDataset接口读取MindRecord格式数据集。datasetMindDataset(test.mindrecord,shuffleFalse)fordataindataset:print(data)[Tensor(shape[1], dtypeInt32, value [0]), array(0.jpg, dtypeU5), Tensor(shape[], dtypeInt32, value 0)] [Tensor(shape[2], dtypeInt32, value [1, 1]), array(1.jpg, dtypeU5), Tensor(shape[], dtypeInt32, value 1)] [Tensor(shape[3], dtypeInt32, value [2, 2, 2]), array(2.jpg, dtypeU5), Tensor(shape[], dtypeInt32, value 2)] [Tensor(shape[4], dtypeInt32, value [3, 3, 3, 3]), array(3.jpg, dtypeU5), Tensor(shape[], dtypeInt32, value 3)]常用数据集我们使用Mnist数据集作为样例介绍使用常用数据集的加载方法。mindspore.dataset提供的接口仅支持解压后的数据文件因此我们使用download库下载数据集并解压。# Download data from open datasetsfromdownloadimportdownload urlhttps://mindspore-website.obs.cn-north-4.myhuaweicloud.com/\notebook/datasets/MNIST_Data.zippathdownload(url,./,kindzip,replaceTrue)Downloading data from https://mindspore-website.obs.cn-north-4.myhuaweicloud.com/notebook/datasets/MNIST_Data.zip (10.3 MB) file_sizes: 100%|██████████████████████████| 10.8M/10.8M [00:0100:00, 5.78MB/s] Extracting zip file... Successfully downloaded / unzipped to ./压缩文件删除后直接加载可以看到其数据类型为MnistDataset。train_datasetMnistDataset(MNIST_Data/train,shuffleFalse)print(type(train_dataset))class mindspore.dataset.engine.datasets_vision.MnistDataset使用迭代器循环输出数据下面定义一个可视化函数迭代Mnist数据集中9张图片进行展示。defvisualize(dataset):figureplt.figure(figsize(4,4))cols,rows3,3plt.subplots_adjust(wspace0.5,hspace0.5)foridx,(image,label)inenumerate(dataset.create_tuple_iterator()):figure.add_subplot(rows,cols,idx1)plt.title(int(label))plt.axis(off)plt.imshow(image.asnumpy().squeeze(),cmapgray)ifidxcols*rows-1:breakplt.show()visualize(train_dataset)数据变换通常情况下直接加载的原始数据并不能直接送入神经网络进行训练此时我们需要对其进行数据预处理。MindSpore提供不同种类的数据变换Transforms配合数据处理Pipeline来实现数据预处理所有的Transforms均可通过.map(...)方法传入。.map(...)操作可以针对数据集指定列column添加数据变换Transforms将数据变换应用于该列数据的每个元素并返回包含变换后元素的新数据集。.map(...)操作可以执行Dataset模块提供的内置数据变换操作也可以执行用户自定义的变换操作。mindspore.dataset提供了面向图像、文本、音频等不同数据类型的内置数据变换操作同时也支持使用自定义数据变换操作。下面分别对其进行介绍。内置数据变换操作mindspore.dataset提供的内置数据变换vision数据变换 nlp数据变换 audio数据变换。下面举例对Mnist数据集中data使用Rescale、Normalize和HWC2CHW操作对label使用TypeCast操作。Rescale用于调整图像像素值的大小包括两个参数rescale缩放因子和shift平移因子图像的每个像素将根据这两个参数进行调整输出的像素值为o u t p u t i i n p u t i ∗ r e s c a l e s h i f t output_{i} input_{i} * rescale shiftoutputiinputi∗rescaleshift。Normalize用于对输入图像的归一化包括三个参数mean图像每个通道的均值、std图像每个通道的标准差和is_hwcbool值输入图像的格式。True为(height, width, channel)False为(channel, height, width)。图像的每个通道将根据mean和std进行调整计算公式为o u t p u t c i n p u t c − m e a n c s t d c output_{c} \frac{input_{c} - mean_{c}}{std_{c}}outputcstdcinputc−meanc其中c cc代表通道索引。HWC2CHW用于转换图像格式将图像从HWC转换成CHW。train_datasetMnistDataset(MNIST_Data/train)train_datasettrain_dataset.map(operations[vision.Rescale(1.0/255.0,0),vision.Normalize(mean(0.1307,),std(0.3081,)),vision.HWC2CHW()],input_columns[image])train_datasettrain_dataset.map(operations[transforms.TypeCast(mstype.int32)],input_columns[label])fordataintrain_dataset:print(data[0].shape,data[0].dtype)print(data[1].shape,data[1].dtype)break(1, 28, 28) Float32 () Int32自定义数据变换操作下面举例对Mnist数据集中data使用 自定义的Rescale、自定义的Normalize和 自定义的HWC2CHW操作对label使用自定义的TypeCast操作。train_datasetMnistDataset(MNIST_Data/train)defrescale_normalize_hwc2chw(input_col):trans_resultinput_col/255.0trans_result(trans_result-0.1307)/0.3081trans_resulttrans_result.transpose(2,0,1)returntrans_result.astype(np.float32)train_datasettrain_dataset.map(operationsrescale_normalize_hwc2chw,input_columns[image])deftypecast(input_col):trans_resultinput_col.astype(np.int32)returntrans_result train_datasettrain_dataset.map(operationstypecast,input_columns[label])fordataintrain_dataset:print(data[0].shape,data[0].dtype)print(data[1].shape,data[1].dtype)break(1, 28, 28) Float32 () Int32数据batchBatch意义在于将多个样本打包为固定大小的batch批次。这是在有限硬件资源下使用梯度下降进行模型优化的折中方法既保证了梯度下降的随机性又优化了计算量。一般我们会设置一个固定的batch size将连续的数据分为若干批batch。batch后的数据增加一维大小为batch_size。data([[1,2],[3,4],[5,6],[7,8],[9,10],[11,12]],[0,1,0,1,0,1])datasetNumpySlicesDataset(datadata,column_names[data,label],shuffleFalse)datasetdataset.batch(2)fordataindataset.create_tuple_iterator():print(data[0].shape,data[1].shape)(2, 2) (2,) (2, 2) (2,) (2, 2) (2,)数据集迭代器数据集Pipeline定义完成后一般以迭代方式获取数据然后送入神经网络中进行训练。我们可以用create_tuple_iterator或create_dict_iterator接口创建数据迭代器并迭代访问数据。访问的数据类型默认为Tensor若设置output_numpyTrue访问的数据类型为Numpy。下面展示create_tuple_iterator迭代器输出的结果。data([1,2,3,4,5,6,7,8],[0,1,0,1,0,1,0,1])datasetNumpySlicesDataset(datadata,column_names[data,label],shuffleFalse)datasetdataset.map(lambdax:x*2,input_columns[data])datasetdataset.batch(2)fordataindataset.create_tuple_iterator():print(data)[Tensor(shape[2], dtypeInt32, value [2, 4]), Tensor(shape[2], dtypeInt32, value [0, 1])] [Tensor(shape[2], dtypeInt32, value [6, 8]), Tensor(shape[2], dtypeInt32, value [0, 1])] [Tensor(shape[2], dtypeInt32, value [10, 12]), Tensor(shape[2], dtypeInt32, value [0, 1])] [Tensor(shape[2], dtypeInt32, value [14, 16]), Tensor(shape[2], dtypeInt32, value [0, 1])]下面展示create_dict_iterator迭代器输出的结果。data([1,2,3,4,5,6,7,8],[0,1,0,1,0,1,0,1])datasetNumpySlicesDataset(datadata,column_names[data,label],shuffleFalse)datasetdataset.map(lambdax:x*2,input_columns[data])datasetdataset.batch(2)fordataindataset.create_dict_iterator():print(data){data: Tensor(shape[2], dtypeInt32, value [2, 4]), label: Tensor(shape[2], dtypeInt32, value [0, 1])} {data: Tensor(shape[2], dtypeInt32, value [6, 8]), label: Tensor(shape[2], dtypeInt32, value [0, 1])} {data: Tensor(shape[2], dtypeInt32, value [10, 12]), label: Tensor(shape[2], dtypeInt32, value [0, 1])} {data: Tensor(shape[2], dtypeInt32, value [14, 16]), label: Tensor(shape[2], dtypeInt32, value [0, 1])}