【MindSpore易点通】性能调试经验总结下篇

背景信息

MindSpore使用model.train接口训练时,默认同步保存模型,可能影响训练性能。

经验总结内容

在模型定义中配置async_save参数为True,即可实现异步保存模型,提升训练性能。

config_ck = CheckpointConfig(save_checkpoint_steps=steps_per_epoch_train, keep_checkpoint_max=epoch_max, async_save=True)
ckpoint_cb = ModelCheckpoint(prefix="train_resnet_cifar10", directory="./", config=config_ck)
model.train(..., callbacks=[ckpoint_cb, ...], ...)

性能对比:异步保存模型:2450 imgs/sec;同步保存模型:2200 imgs/sec

自动数据加速

背景信息

MindSpore提供了一种自动数据调优的工具——Dataset AutoTune,用于在训练过程中根据环境资源的情况自动调整数据处理管道的并行度, 最大化利用系统资源加速数据处理管道的处理速度。

在整个训练的过程中,Dataset AutoTune模块会持续检测当前训练性能瓶颈处于数据侧还是网络侧。如果检测到瓶颈在数据侧,则将 进一步对数据处理管道中的各个算子(如GeneratorDataset、map、batch此类数据算子)进行参数调整。

1、示例代码

示例代码如下:

import mindspore.dataset as ds

def create_dataset(...)
    """
    create dataset for train or test
    """
    # 使能自动数据加速
    ds.config.set_enable_autotune(True)

    # 其他数据集代码无需变更
    data_set = ds.Cifar10Dataset(data_path)
    ...

自动数据加速关键代码:

import mindspore.dataset as ds
ds.config.set_enable_autotune(True)

注:

  • 自动数据加速目前仅可用于下沉模式(dataset_sink_mode=True),在非下沉模式(dataset_sink_mode=False)下,Dataset AutoTune将不会生效,但不会影响网络正常训练;
  • Profiling性能分析和自动数据加速无法同时开启,否则会导致Profiling或Dataset AutoTune不生效。如果这样同时开启此两个功能,则会有一条警告信息提示用户检查是否为误操作。因此在使用Dataset AutoTune时,用户需要确保关闭Profiling功能。

2、运行结果分析

以示例代码为例使能数据自动加速功能,随后自动数据加速模块会通过打屏log的形式展示其对于性能瓶颈的分析情况:

[WARNING] ME(15918,fffde27fc1e0,python):2022-01-13-09:33:16.061.926 [mindspore/ccsrc/minddata/dataset/engine/perf/auto_tune.cc:425] Analyse] Op (BatchOp(ID:2)) getting low average worker cpu utilization 1.25694% < 35% threshold.



[WARNING] ME(15918,fffde27fc1e0,python):2022-01-13-09:33:16.061.941

[mindspore/ccsrc/minddata/dataset/engine/perf/auto_tune.cc:359]

RequestConnectorCapacityChange] Added request to change "prefetch_size" of Operator: BatchOp(ID:2)From old value: [32] to new value: [36].

Epoch:8, 2703.92imgs/sec

epoch time: 22154.670 ms, per step time: 47.339 ms

数据管道的性能分析和调整过程可以通过上述的log体现:

当检测到数据侧为瓶颈且需要发生参数变更时,则会通过WARNING级别的LOG信息提醒用户正在调整的参数。

BatchOp(ID:2)From old value: [32] to new value: [36]

在数据处理管道的初始配置下,Device Connector队列(数据管道与计算网络之间的缓冲队列)的利用率较低。

[mindspore/ccsrc/minddata/dataset/engine/perf/auto_tune.cc:425] Analyse] Op (BatchOp(ID:2)) getting low average worker cpu utilization 1.25694% < 35% threshold.

在使用示例代码运行20epoch加速前与加速后运行时间对比:

自动数据加速后效果:

未使用自动数据加速效果:

MatMul、Tile、Div等算子最好把输入做16倍数对齐

背景信息

因为D芯片底层数据搬运按照16字节对齐,所以部分算子的输入在非16倍数的情况下无法使能多核,导致性能变差。

经验总结内容

MatMul、Tile以及Div算子的输入最好先向上进行16字节的对齐,以MatMul举例如下:

import mindspore.nn as nn

class CustomMatMul(nn.Cell):
    def __init__(self, transpose_a=False, transpose_b=False):
        super(CustomMatMul, self).__init__()
        self.fc = P.MatMul(transpose_a=transpose_a, transpose_b=transpose_b)

    def construct(self, x1, x2):
        out = self.fc(x1, x2)
        return out


class MatMul_assignto16(nn.cell):
    def __init__(self, args):
        super(MatMul_assignto16, self).__init__()
        self.num_classes = (args.num_classes // 16 + 1) * 16    #输入的类别数做向上16字节对齐
        self.weight = Parameter(Tensor(np.ones((self.num_classes, args.emb_size)), mstype.float32), name="w", requires_grad=True)
        self.fc = CustomMatMul(transpose_b=True).add_flags_recursive(fp16=True)

    def construct(self, feature):
        out = self.fc(feature, self.weight)
        return out

MatMul算子性能差

背景信息

D芯片中矩阵运算是用cube做的,只能支持fp16的计算类型

经验总结内容

MatMul算子尽量使用fp16的计算类型,fp32的MatMul是在aicpu上计算的,性能会比fp16差很多。 可以在me脚本层面手动指定算子的计算类型,如下:

class CustomMatMul(nn.Cell):
    def __init__(self, transpose_a=False, transpose_b=False):
        super(CustomMatMul, self).__init__()
        self.fc = P.MatMul(transpose_a=transpose_a, transpose_b=transpose_b)

    def construct(self, x1, x2):
        out = self.fc(x1, x2)
        return out

self.fc = CustomMatMul(transpose_b=True).add_flags_recursive(fp16=True)

ResNet50跑ImageNet类别数设定

背景信息

ResNet50跑ImageNet在PyTorch上面的类别数设定是1000,而TensorFlow的类别数设定是1001。

经验总结内容

Ascend环境下针对TensorFlow做了更深度的优化,因此在跑MindSpore+Ascend的时候,也建议把类别数设置为1001以获取更加极致的性能。 可以在me脚本层面手动指定类别数,如下:

class CommonHead(nn.Cell):
    def __init__(self, num_classes=1001, out_channels):  #将类别数默认置为1001
        super(CommonHead, self).__init__()
        self.avgpool = GlobalAvgPooling()
        self.fc = nn.Dense(out_channels, num_classes, has_bias=True)

    def construct(self, x):
        x = self.avgpool(x)
        x = self.fc(x)
        return x
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值