昇思MindSpore进阶教程--使能图算融合

大家好,我是刘明,明志科技创始人,华为昇思MindSpore布道师。
技术上主攻前端开发、鸿蒙开发和AI算法研究。
努力为大家带来持续的技术分享,如果你也喜欢我的文章,就点个关注吧

正文开始

图算融合是MindSpore特有的网络性能优化技术。它可以通过自动分析和优化现有网络计算图逻辑,并结合目标硬件能力,对计算图进行计算化简和替代、算子拆分和融合、算子特例化编译等优化,以提升设备计算资源利用率,实现对网络性能的整体优化。相比传统优化技术,图算融合具有多算子跨边界联合优化、与MindSpore AKG(基于Polyhedral的算子编译器)跨层协同、即时编译等独特优势。另外,图算融合只需要用户打开对应配置后,整个优化过程即可自动完成,不需要网络开发人员进行其它额外感知,使得用户可以聚焦网络算法实现。
图算融合的适用场景包括:

  • 对网络执行时间具有较高性能要求的场景;
  • 通过拼接基本算子实现自定义组合算子,并希望对这些基本算子进行自动融合,以提升自定义组合算子性能的场景。

使用方法

当前图算融合优化默认关闭状态,我们只需在训练脚本中为context指定参数enable_graph_kernel=True即可启用图算融合:

import mindspore as ms
ms.set_context(enable_graph_kernel=True)

样例脚本

为了说明图算融合优化场景,我们构造了一个简单网络MyNet, 包含一个乘法和加法计算。在打开图算融合进行优化之后,这两个计算便会自动合成一个融合算子:

import numpy as np
import mindspore as ms
from mindspore.nn import Cell
import mindspore.ops as ops

ms.set_context(mode=ms.GRAPH_MODE, device_target="GPU")
# save graph ir to view fusion detail.
ms.set_context(save_graphs=2)
# enable graph kernel optimization.
ms.set_context(enable_graph_kernel=True)

class MyNet(Cell):
    def construct(self, x):
        a = ops.mul(x, 2.0)
        res = ops.add(a, 1.0)
        return res

x = np.ones((4, 4)).astype(np.float32) * 0.5
net = MyNet()
result = net(ms.Tensor(x))
print("result: {}".format(result))

该计算图的融合结果如图1所示,其中左图为未使能图算融合时的对应计算图,右图为使能图算融合后的对应计算图。可以看到该网络中的加法和乘法被融合成一个算子。该融合过程可以通过查看中间IR,或者通过Profiling等工具跟踪算子执行过程进行验证。

在这里插入图片描述

自定义组合算子

基于图算融合技术,用户可以很方便地实现高性能的自定义组合算子。其主要流程为:

  1. 在脚本中用基本算子组合的方式实现自定义算子定义和使用;

  2. 打开图算融合配置;

  3. 图算融合对自定义组合算子中的基本算子自动进行算子融合,并生成高性能融合算子。

相比其它自定义算子方式,这种方式具有对框架无侵入、简单易用等优点。

样例脚本

我们构造一个简单网络MyNet,并在其中使用了自定义算子MyOp。代码样例如下:

import numpy as np
import mindspore as ms
from mindspore.nn import Cell
import mindspore.ops as ops

ms.set_context(mode=ms.GRAPH_MODE, device_target="GPU")
# enable graph kernel optimization.
ms.set_context(enable_graph_kernel=True)

class MyOp(Cell):
    """ my first custom OP composited by basic OPs """
    def construct(self, x, y):
        a = ops.sub(x, y)
        return ops.mul(a, x)

class MyNet(Cell):
    def __init__(self):
        super(MyNet, self).__init__()
        self.my_op = MyOp()

    def construct(self, x, y):
        a = ops.mul(x, 2.0)
        b = ops.pow(a, 3.0)
        res = self.my_op(b, y)
        return res

x = np.ones((4, 4)).astype(np.float32) * 0.2
y = np.ones((4, 4)).astype(np.float32) * 0.3
net = MyNet()
result = net(ms.Tensor(x), ms.Tensor(y))
print("result: {}".format(result))

该计算图的融合结果如图2所示,其中左图为未使能图算融合时的对应计算图,右图为使能图算融合后的对应计算图。可以看到不仅自定义算子MyOp中的基本算子进行了融合,并且与主图中的其他算子也进行了更大范围融合。该融合过程可以通过查看中间IR,或者通过Profiling等工具跟踪算子执行过程进行验证。
在这里插入图片描述

好的,下面是使用MindSpore实现ResNet-34的步骤: 1. 导入必要的库 ```python import mindspore.nn as nn from mindspore import Tensor from mindspore.ops import operations as P ``` 2. 定义基本的卷积块 ```python class ConvBlock(nn.Cell): def __init__(self, in_channels, out_channels, kernel_size, stride=1, padding=0): super(ConvBlock, self).__init__() self.conv = nn.Conv2d(in_channels, out_channels, kernel_size, stride, padding, has_bias=False) self.bn = nn.BatchNorm2d(out_channels) self.relu = nn.ReLU() def construct(self, x): x = self.conv(x) x = self.bn(x) x = self.relu(x) return x ``` 3. 定义ResNet-34的基本块 ```python class BasicBlock(nn.Cell): expansion = 1 def __init__(self, in_channels, out_channels, stride=1, downsample=None): super(BasicBlock, self).__init__() self.conv1 = ConvBlock(in_channels, out_channels, kernel_size=3, stride=stride, padding=1) self.conv2 = ConvBlock(out_channels, out_channels, kernel_size=3, stride=1, padding=1) self.downsample = downsample self.stride = stride def construct(self, x): identity = x out = self.conv1(x) out = self.conv2(out) if self.downsample is not None: identity = self.downsample(x) out += identity out = nn.ReLU()(out) return out ``` 4. 定义ResNet-34的主体部分 ```python class ResNet34(nn.Cell): def __init__(self, num_classes=1000): super(ResNet34, self).__init__() self.in_channels = 64 self.conv1 = ConvBlock(3, 64, kernel_size=7, stride=2, padding=3) self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1) self.layer1 = self._make_layer(BasicBlock, 64, 3, stride=1) self.layer2 = self._make_layer(BasicBlock, 128, 4, stride=2) self.layer3 = self._make_layer(BasicBlock, 256, 6, stride=2) self.layer4 = self._make_layer(BasicBlock, 512, 3, stride=2) self.avgpool = nn.AvgPool2d(kernel_size=7, stride=1) self.fc = nn.Dense(512 * BasicBlock.expansion, num_classes) def _make_layer(self, block, out_channels, num_blocks, stride): downsample = None if stride != 1 or self.in_channels != out_channels * block.expansion: downsample = nn.SequentialCell([ nn.Conv2d(self.in_channels, out_channels * block.expansion, kernel_size=1, stride=stride, has_bias=False), nn.BatchNorm2d(out_channels * block.expansion) ]) layers = [] layers.append(block(self.in_channels, out_channels, stride, downsample)) self.in_channels = out_channels * block.expansion for i in range(1, num_blocks): layers.append(block(self.in_channels, out_channels)) return nn.SequentialCell(layers) def construct(self, x): x = self.conv1(x) x = self.maxpool(x) x = self.layer1(x) x = self.layer2(x) x = self.layer3(x) x = self.layer4(x) x = self.avgpool(x) x = P.Reshape()(x, (x.shape[0], -1)) x = self.fc(x) return x ``` 5. 加载数据和训练模型 这里的数据加载和训练模型的部分可以根据具体的数据集和训练需求进行编写。 以上就是使用MindSpore实现ResNet-34的基本步骤,你可以根据自己的需要进行修改和调整。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

明志刘明

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值