本文给大家介绍几种常用的性能调优方法。
MindSpore Data中的某些算子接收num_parallel_workers参数,该参数设置了算子使用的线程数。根据物理内核的数量和目标系统的负载,增加管道中num_parallel_workers的值,可以提高性能。对于任何包含计算密集型Tensor运算(比如Decode)的Map算子而言,尤其如此。
使用默认数据处理进程数(为1):
if do_train:
cifar_ds = ds.Cifar10Dataset(dataset_dir=data_home, shuffle=True, usage='train')
else:
cifar_ds = ds.Cifar10Dataset(dataset_dir=data_home, shuffle=False, usage='test')
cifar_ds = cifar_ds.map(operations=transform_label, input_columns="label")
cifar_ds = cifar_ds.map(operations=transform_data, input_columns="image")
cifar_ds = cifar_ds.batch(batch_size, drop_remainder=True)
调整数据处理进程数为2:
if do_train:
cifar_ds = ds.Cifar10Dataset(dataset_dir=data_home, num_parallel_workers=2, shuffle=True, usage='train')
else:
cifar_ds = ds.Cifar10Dataset(dataset_dir=data_home, num_parallel_workers=2, shuffle=False, usage='test')
cifar_ds = cifar_ds.map(operations=transform_label, num_parallel_workers=2, python_multiprocessing=True, input_columns="label")
cifar_ds = cifar_ds.map(operations=transform_data, num_parallel_workers=2, python_multiprocessing=True, python_multiprocessing=True, input_columns="image")
cifar_ds = cifar_ds.batch(batch_size, num_parallel_workers=2, drop_remainder=True)
运行后性能对比:使用默认数据处理进程数:2200 imgs/sec ;调整数据处理进程数为2:2300 imgs/sec
目前MindSpore Data支持的特定格式数据集为:MindRecord。使用MindRecord格式的加载数据性能更优。
MindRecord数据加载流程:
import mindspore.dataset as ds
CV_FILE_NAME = "./cifar10.mindrecord"
cifar_ds = ds.MindDataset(dataset_file=CV_FILE_NAME,columns_list=["data","label"], shuffle=True)
运行后性能对比: 自定义数据集加载cifar10:850 imgs/sec;MindRecord格式加载cifar10:2200 imgs/sec
数据集加载时shuffle,算子可以在生成输出Tensor之前对数据进行内部shuffle。与在管道中使用显式shuffle算子相比,这种内部shuffle通常会带来更好的性能。
管道中使用显式shuffle:
import mindspore.dataset as ds
DATA_DIR = "./cifar-10-batches-bin/"
cifar_ds = ds.Cifar10Dataset(DATA_DIR, usage='train')
cifar_ds = cifar_ds.shuffle(buffer_size=10000)
加载数据集时shuffle:
import mindspore.dataset as ds
DATA_DIR = "./cifar-10-batches-bin/"
cifar_ds = ds.Cifar10Dataset(DATA_DIR, shuffle=True, usage='train')
运行后性能对比:本样例未见明显性能收益
混合精度训练方法是通过混合使用单精度和半精度数据格式来加速深度神经网络训练的过程,同时保持了单精度训练所能达到的网络精度。混合精度训练能够加速计算过程,同时减少内存使用和存取,并使得在特定的硬件上可以训练更大的模型或batch size。
运行后性能对比:高阶API: 2200 imgs/sec ;高阶API混合精度: 3300 imgs/sec
低阶API: 2000 imgs/sec ;低阶API混合精度: 3200 imgs/sec
采用数据集的下沉模式,即训练计算下沉到硬件平台中执行,数据下沉可以优化训练性能。
将Model.train接口中dataset_sink_mode 值设为True,即可采用数据下沉模式。
数据未下沉:
model.train(..., dataset_sink_mode=False, ...)
数据下沉:
model.train(..., dataset_sink_mode=True, sink_size=steps_per_epoch_train)
注:低阶API不支持数据下沉,若想实现数据下沉请使用Model.train接口进行训练。
运行后性能对比:数据未下沉:2000 imgs/sec;数据下沉:2200 imgs/sec
c_transform是在C++内维护buffer管理,py_transform是在python内维护buffer管理。因为python和C++切换的性能成本,建议不要混用算子,否则会降低训练性能。
c_transform和py_transform混用:
if do_train:
# Transformation on train data
transform_data = py_trans.Compose([
CV.RandomCrop((32, 32), (4, 4, 4, 4)),
py_vision.ToPIL(),
py_vision.RandomHorizontalFlip(),
CV.Rescale(rescale, shift),
CV.Normalize((0.4914, 0.4822, 0.4465), (0.2023, 0.1994, 0.2010)),
CV.HWC2CHW()
])
else:
# Transformation on validation data
transform_data = py_trans.Compose([
CV.Rescale(rescale, shift),
CV.Normalize((0.4914, 0.4822, 0.4465), (0.2023, 0.1994, 0.2010)),
CV.HWC2CHW()
])
cifar_ds = cifar_ds.map(operations=transform_data, input_columns="image")
c_transform和py_transform未混用:
if do_train:
# Transformation on train data
transform_data = C.Compose([
CV.RandomCrop((32, 32), (4, 4, 4, 4)),
CV.RandomHorizontalFlip(),
CV.Rescale(rescale, shift),
CV.Normalize((0.4914, 0.4822, 0.4465), (0.2023, 0.1994, 0.2010)),
CV.HWC2CHW()])
else:
# Transformation on validation data
transform_data = C.Compose([
CV.Rescale(rescale, shift),
CV.Normalize((0.4914, 0.4822, 0.4465), (0.2023, 0.1994, 0.2010)),
CV.HWC2CHW()])
cifar_ds = cifar_ds.map(operations=transform_data, input_columns="image")
运行后性能对比:c_transform和py_transform混用:1100 imgs/sec;
c_transform和py_transform未混用:2200 imgs/sec
数据预处理时,Map算子可以接收Tensor算子列表,并将按顺序应用所有的这些算子。与为每个Tensor算子使用单独的Map算子相比,可以获得更好的性能。
多map数据预处理:
randomcrop_op = CV.RandomCrop((32, 32), (4, 4, 4, 4))
randomhorizontalflip_op = CV.RandomHorizontalFlip()
rescale_op = CV.Rescale(rescale, shift)
normalize_op = CV.Normalize((0.4914, 0.4822, 0.4465), (0.2023, 0.1994, 0.2010))
hwc2chw_op = CV.HWC2CHW()
if do_train:
cifar_ds = cifar_ds.map(operations=[randomcrop_op], input_columns="image")
cifar_ds = cifar_ds.map(operations=[randomhorizontalflip_op], input_columns="image")
cifar_ds = cifar_ds.map(operations=[rescale_op], input_columns="image")
cifar_ds = cifar_ds.map(operations=[normalize_op], input_columns="image")
cifar_ds = cifar_ds.map(operations=[hwc2chw_op], input_columns="image")else:
cifar_ds = cifar_ds.map(operations=[rescale_op], input_columns="image")
cifar_ds = cifar_ds.map(operations=[normalize_op], input_columns="image")
cifar_ds = cifar_ds.map(operations=[hwc2chw_op], input_columns="image")
合并成单map数据预处理:
if do_train:
# Transformation on train data
transform_data = C.Compose([
CV.RandomCrop((32, 32), (4, 4, 4, 4)),
CV.RandomHorizontalFlip(),
CV.Rescale(rescale, shift),
CV.Normalize((0.4914, 0.4822, 0.4465), (0.2023, 0.1994, 0.2010)),
CV.HWC2CHW()])
else:
# Transformation on validation data
transform_data = C.Compose([
CV.Rescale(rescale, shift),
CV.Normalize((0.4914, 0.4822, 0.4465), (0.2023, 0.1994, 0.2010)),
CV.HWC2CHW()])
cifar_ds = cifar_ds.map(operations=transform_data, input_columns="image")
运行后性能对比:本样例未见明显性能收益
MindSpore Data提供某些融合算子,这些算子将两个或多个算子的功能聚合到一个算子中。 与它们各自组件的流水线相比,这种融合算子提供了更好的性能。一个很好的融合算子是RandomCropDecodeResizeOp,它执行解码,然后对任意给定的Tensor进行随机裁剪和大小调整。用户可以查看算子API文档查看是否有相应融合算子替代现有算子,以获得更好性能。
MindSpore提供了一种自动数据调优的工具——Dataset AutoTune,用于在训练过程中根据环境资源的情况自动调整数据处理管道的并行度,最大化利用系统资源加速数据处理管道的处理速度。在整个训练的过程中,Dataset AutoTune模块会持续检测当前训练性能瓶颈处于数据侧还是网络侧。如果检测到瓶颈在数据侧,则将进一步对数据处理管道中的各个算子(如GeneratorDataset、map、batch此类数据算子)进行参数调整。