帶你了解AKG正反向算子注冊+關聯流程


摘要:簡要介紹一下akg正反向算子的注冊和關聯流程。

本文分享自華為雲社區《AKG正反向算子注冊+關聯》,作者:木子_007 。

一、環境

硬件:eulerosv2r8.aarch64

mindspore:1.1

算子注冊需要編譯安裝框架才能生效,所以默認環境中已經有了mindspore的源碼,並且已經可以編譯安裝

二、正向算子制作及測試

這里制作一個計算向量平方的算子

正向:y = x**2

反向:y = 2*x

先介紹正向

2.1 定義正向算子

路徑:mindspore/akg/python/akg/ms/cce/,創建cus_square.py

參照同級目錄下計算邏輯的定義,定義向量平方的計算邏輯

"""cus_square"""
from akg.tvm.hybrid import script
from akg.ops.math import mul
import akg
def CusSquare(x):
    output_shape = x.shape
    k = output_shape[0]
    n = output_shape[1]

    @script
    def cus_square_compute(x):
        y = output_tensor(output_shape, dtype=x.dtype)
        for i in range(k):
            for j in range(n):
                y[i, j] = x[i, j] * x[i, j]
        return y

    output = cus_square_compute(x)

    attrs = {
        'enable_post_poly_loop_partition': False,
        'enable_double_buffer': False,
        'enable_feature_library': True,
        'RewriteVarTensorIdx': True
    }

    return output, attrs

然后在同級目錄下的__init__.py文件中添加內容

from .cus_square import CusSquare

2.2 注冊算子

到路徑:mindspore/ops/_op_impl/akg/ascend,創建cus_square.py,添加如下代碼

"""CusSquare op"""
from mindspore.ops.op_info_register import op_info_register, AkgAscendRegOp, DataType as DT

op_info = AkgAscendRegOp("CusSquare") \
    .fusion_type("ELEMWISE") \
    .input(0, "x") \
    .output(0, "output") \
    .dtype_format(DT.F32_Default, DT.F32_Default) \
    .get_op_info()
@op_info_register(op_info)
def _cus_square_akg():
    """CusSquare Akg register"""
    return

然后在同級目錄的__init__.py添加如下代碼

from .cus_square import _cus_square_akg

2.3 定義算子原語

到:mindspore/ops/operations,新創建一個_cus_ops.py,添加如下代碼

描述算子的輸入:x,輸出output

infer_shape:描述輸出數據的shape

infer_dtype:說明輸出數據的類型

x1_shape:指的是第一個輸入的shape

x1_dtype:指的是第一個輸入參數的dtype

import math

from ..primitive import prim_attr_register, PrimitiveWithInfer
from ...common import dtype as mstype
from ..._checkparam import Validator as validator
from ..._checkparam import Rel

class CusSquare(PrimitiveWithInfer):
    """CusSquare"""

    @prim_attr_register
    def __init__(self):
        self.init_prim_io_names(inputs=['x'], outputs=['output'])

    def infer_shape(self, x1_shape):
        return x1_shape

    def infer_dtype(self, x1_dtype):
        return x1_dtype

然后在同目錄下的__init__.py文件中添加原語信息

from ._cus_ops import CusSquare

2.4 在ccsrc中添加算子的查詢信息

在mindspore/ccsrc/backend/kernel_compiler/的KernelQuery函數中添加如下信息

// cus_square
const PrimitivePtr kPrimCusSquare = std::make_shared<Primitive>("CusSquare");
if (IsPrimitiveCNode(kernel_node, kPrimCusSquare)) {
    kernel_type = KernelType::AKG_KERNEL;
}

2.5 編譯安裝框架

回到mindspore根目錄

bash build.sh -e ascend -j4  
cd ./build/package
pip install mindspore_ascend-1.1.2-cp37-cp37m-linux_aarch64.whl --force-reinstall

2.6 測試

import numpy as np
import mindspore.nn as nn
import mindspore.context as context
from mindspore import Tensor
from mindspore.ops import operations as P

context.set_context(mode=context.GRAPH_MODE, device_target="Ascend")

class Net(nn.Cell):
    def __init__(self):
        super(Net, self).__init__()
        self.square = P.CusSquare()

    def construct(self, data):
        return self.square(data)

def test_net():
    x = np.array([[1.0, 4.0, 9.0]]).astype(np.float32)
    net = Net()
    output = net(Tensor(x))
    print("x: ", x)
    print("output: ", output)
if __name__ == "__main__":
    test_net()

輸出

三、反向算子的制作和測試

3.1 制作流程

反向算子的計算邏輯:對向量元素進行求導,如 y = x^2,則求導之后 y` = 2x

實際例子就是輸入向量[1, 4, 9] 輸出就是 [2, 8, 18]

反向算子明明為CusSquareGrad,與前邊的計算平方的算子流程相同,這里只貼一下關鍵代碼,流程不再贅述

計算邏輯代碼cus_square_grad.py

"""cus_square_grad"""
from akg.tvm.hybrid import script
import akg

def CusSquareGrad(x):
    output_shape = x.shape
    k = output_shape[0]
    n = output_shape[1]

    @script
    def cus_square_compute_grad(x):
        y = output_tensor(output_shape, dtype=x.dtype)
        for i in range(k):
            for j in range(n):
                y[i, j] = x[i, j] * 2
        return y

    output = cus_square_compute_grad(x)

    attrs = {
        'enable_post_poly_loop_partition': False,
        'enable_double_buffer': False,
        'enable_feature_library': True,
        'RewriteVarTensorIdx': True
    }

    return output, attrs

注冊原語

class CusSquareGrad(PrimitiveWithInfer):
    """
    CusSquareGrad
    """

    @prim_attr_register
    def __init__(self):
        self.init_prim_io_names(inputs=['x'], outputs=['output'])

    def infer_shape(self, x1_shape):
        return x1_shape

    def infer_dtype(self, x1_dtype):
        return x1_dtype  

3.2 測試

import numpy as np
import mindspore.nn as nn
import mindspore.context as context
from mindspore import Tensor
from mindspore.ops import operations as P

context.set_context(mode=context.GRAPH_MODE, device_target="Ascend")

class Net(nn.Cell):
    def __init__(self):
        super(Net, self).__init__()
        self.square = P.CusSquareGrad()   # 替換為grad算子

    def construct(self, data):
        return self.square(data)

def test_net():
    x = np.array([[1.0, 4.0, 9.0]]).astype(np.float32)
    net = Net()
    output = net(Tensor(x))
    print("x: ", x)
    print("output: ", output)
if __name__ == "__main__":
    test_net()

輸出

四、正反向算子關聯及測試

在源碼 mindspore/mindspore/ops/_grad/grad_array_ops.py中添加如下代碼

@bprop_getters.register(P.CusSquare)
def get_bprop_cussquare(self):
    """Generate bprop of CusSquare"""
    cus_square_grad = P.CusSquareGrad()
    matmul = ops.Mul()
    def bprop(x, out, dout):
        gradient = cus_square_grad(x)
        dx = matmul(gradient, dout)
        return (dx,)
    return bprop

bprop函數的輸入是,正向的輸入x,正向的輸出out,反向的梯度輸入dout

上面代碼的意思是指定算子CusSquare的反向梯度的計算方法,CusSquareGrad作為其中的一個函數使用

gradient = cus_square_grad(x)計算的是本平方算子的梯度,但並不能直接返回這個梯度

反向網絡到該算子,最后返回的是dx,注意算子的反向梯度計算一定要放在整個網絡的反向鏈式梯度計算中

測試

import numpy as np
import mindspore.nn as nn
import mindspore.context as context
from mindspore import Tensor
from mindspore.ops import operations as P
from mindspore.ops import composite as C

context.set_context(mode=context.GRAPH_MODE, device_target="Ascend")

class Net(nn.Cell):
    def __init__(self):
        super(Net, self).__init__()
        self.square = P.CusSquare()  

    def construct(self, data):
        return self.square(data)

def test_net():
    x = Tensor(np.array([[1.0, 4.0, 9.0]]).astype(np.float32))
    grad = C.GradOperation(get_all=True)    # 計算網絡梯度
    net = Net()
    output = grad(net)(x)
    print("x: ", x)
    print("output: ", output)
if __name__ == "__main__":
    test_net()

輸出

 

點擊關注,第一時間了解華為雲新鮮技術~


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM