向Relay添加算子

为了在Relay IR中使用TVM算子,需要在Relay中注册算子,以确保将其集成到Relay的类型系统中。

注册算子需要三个步骤:

  • 使用RELAY_REGISTER_OPC ++中的宏注册算子的Arity和类型信息
  • 定义一个C ++函数为算子生成一个调用节点,并为该函数注册一个Python API挂钩
  • 将上述Python API挂钩包装在更整洁的界面中

该文件src/relay/op/tensor/binary.cc提供了前两个步骤的python/tvm/relay/op/tensor.py示例,同时提供了后两个步骤的示例。

注册算子

TVM已经具有算子注册表,如果没有其它类型信息,Relay无法正确合并TVM算子。

为了在注册算子时具有更大的灵活性,并在Relay中表达类型时,提高了表达性和粒度,使用输入和输出类型之间的关系来对算子进行类型化。这些关系表示为接受输入类型和输出类型的列表(这些类型中的任何一个都不完整),返回满足该关系的输入和输出类型的列表的函数。本质上,算子的关系除了计算输出类型外,还可以强制执行所有必要的键入规则(即,通过检查输入类型)。

例如,参阅src/relay/op/type_relations.h及其实现。例如,BroadcastRel接受两个输入类型和一个输出类型,都是具有相同基础数据类型的张量类型,最后确保输出类型的形状是输入类型的形状的广播。

type_relations.h 如果现有类型未捕获所需算子的行为,则可能有必要添加另一种类型关系。

使用RELAY_REGISTER_OPC ++中的宏,开发人员可以在Relay中指定有关算子的以下信息:

  • Arity(参数个数)
  • 位置参数的名称和说明
  • 支持级别(1表示内部固有;较高的数字表示积分较少或不受外部支持的算子)
  • 算子的类型关系

下面的示例来自binary.cc张量,并将其用于张量广播:

RELAY_REGISTER_OP("add")

.set_num_inputs(2)

.add_argument("lhs", "Tensor", "The left hand side tensor.")

.add_argument("rhs", "Tensor", "The right hand side tensor.")

.set_support_level(1)

.add_type_rel("Broadcast", BroadcastRel);

创建调用节点

此步骤仅需要简单地编写一个将参数带给算子的函数(如Relay表达式),然后将调用节点返回给算子(即,应将其放置在Relay AST中的节点,该AST是要向算子进行调用的位置)。

目前不支持调用属性和类型参数(最后两个字段),足以用于Op::Get从算子注册表中获取算子信息,并将参数传递给调用节点,如下所示。

TVM_REGISTER_GLOBAL("relay.op._make.add")

.set_body_typed<Expr(Expr, Expr)>([](Expr lhs, Expr rhs) {

static const Op& op = Op::Get("add");

return Call(op, {lhs, rhs}, Attrs(), {});

});

包括Python API挂钩

在Relay中通常是约定,通过导出的函数TVM_REGISTER_GLOBAL应该包装在单独的Python函数中,而不是在Python中直接调用。对于产生对算子的调用的函数,捆绑起来可能很方便,其中python/tvm/relay/op/tensor.py,都提供了张量上的元素算子。例如,以下是上一节中的add函数在Python中的显示方式:

def add(lhs, rhs):

"""Elementwise addition.

    Parameters

    ----------

    lhs : relay.Expr

        The left hand side input data

    rhs : relay.Expr

        The right hand side input data

    Returns

    -------

    result : relay.Expr

        The computed result.

    """

return _make.add(lhs, rhs)

这些Python库也可能是向算子提供更简单接口的好机会。例如,该 concat算子被注册为仅接受一个算子,即一个具有要连接的张量的元组,Python库将这些张量作为参数,组合成一个元组,然后生成调用节点:

def concat(*args):

"""Concatenate the input tensors along the zero axis.

    Parameters

    ----------

    args: list of Tensor

    Returns

    -------

    tensor: The concatenated tensor.

    """

tup = Tuple(list(args))

return _make.concat(tup)

梯度算子

梯度算子对于在Relay中编写可区分的程序很重要。尽管Relay的autodiff算法可以区分一流的语言结构,算子是不透明的。由于Relay无法调查实现,必须提供明确的区分规则。

Python和C ++均可用于编写梯度算子,将示例重点放在Python上,因为更常用。

在Python中添加渐变

可以找到Python梯度算子的集合 python/tvm/relay/op/_tensor_grad.py。将通过两个有代表性的示例:sigmoid和multiply。

@register_gradient("sigmoid")

def sigmoid_grad(orig, grad):

"""Returns [grad * sigmoid(x) * (1 - sigmoid(x))]."""

return [grad * orig * (ones_like(orig) - orig)]

这里的输入是原始算子orig和grad要累加到的渐变。返回的是一个列表,其中第i个索引处的元素是算子相对于算子第i个输入的派生。通常,渐变将返回一个列表,其中包含与基本算子输入相同数量的元素。

在进一步分析这个定义之前,应该回想一下S型函数的导数: ∂σ∂x=σ(x)(1−σ(x))∂σ∂x=σ(x)(1−σ(x))。上面的定义看起来与数学定义相似,有一个重要的补充,将在下面进行描述。

该术语直接与导数匹配,因为这是S型函数,不仅对如何计算此函数的梯度感兴趣。有兴趣将此梯度与其它梯度组成,可以在整个程序中累积该梯度。这是该术语出现的地方。指定到目前为止如何用梯度组成导数。orig * (ones_like(orig) - orig)origgradgrad * orig * (ones_like(orig) - orig)grad

来看multiply一个更有趣的示例:

@register_gradient("multiply")

def multiply_grad(orig, grad):

"""Returns [grad * y, grad * x]"""

x, y = orig.args

return [collapse_sum_like(grad * y, x),

collapse_sum_like(grad * x, y)]

在此示例中,返回列表中有两个元素, multiply是一个二进制算子。如果f(x,y)=xyf(x,y)=xy,偏导数是 ∂f∂x=y∂f∂x=y 和 ∂f∂y=x∂f∂y=x。

有一个必需的步骤,因为广播具有语义,multiply不需要。可能与输入的形状不匹配,习惯于获取术语的内容,并使形状与要区分的输入的形状相匹配。sigmoidmultiplygradcollapse_sum_likegrad * <var>

在C ++中添加渐变

在C ++中添加渐变类似于在Python中添加渐变,注册的界面略有不同。

确保src/relay/pass/pattern_utils.h包含其中。提供了用于在Relay AST中创建节点的辅助功能。然后,以类似于Python示例的方式定义渐变:

tvm::Array<Expr> MultiplyGrad(const Expr& orig_call, const Expr& output_grad) {

const Call& call = orig_call.Downcast<Call>();

return { CollapseSumLike(Multiply(output_grad, call.args[1]), call.args[0]),

CollapseSumLike(Multiply(output_grad, call.args[0]), call.args[1]) };

}

在C ++中,不能使用与Python中相同的算子重载,并且需要向下转换,实现更为冗长。即使这样,仍可以轻松地验证此定义,是否与Python中的先前示例相同。

代替使用Python渲染,需要set_attr在基本算子注册的末尾添加对“ FPrimalGradient”的调用,以注册渐变。

RELAY_REGISTER_OP("multiply")

// ...

// Set other attributes

// ...

.set_attr<FPrimalGradient>("FPrimalGradient", MultiplyGrad);

总结

  • TVM算子可以使用表示适当类型信息的关系在中继中注册。
  • 在中继中使用算子需要一个函数来为算子生成调用节点。
  • 最好有一个简单的Python库来生成调用节点。

向Relay添加算子的更多相关文章

  1. 【推理引擎】如何在 ONNXRuntime 中添加新的算子

    如果模型中有些算子不被ONNX算子库支持,我们就需要利用ONNXRuntime提供的API手动添加新算子.在官方文档中已经对如何添加定制算子进行了介绍(https://onnxruntime.ai/d ...

  2. Sentry 企业级数据安全解决方案 - Relay 入门

    内容整理自官方开发文档 Sentry Relay 通过提供作为应用程序和 sentry.io 之间中间层的独立服务来提供企业级数据安全性. Relay 专门设计用于: 在将个人身份信息 (PII) 发 ...

  3. Tensorflow源码解析1 -- 内核架构和源码结构

    1 主流深度学习框架对比 当今的软件开发基本都是分层化和模块化的,应用层开发会基于框架层.比如开发Linux Driver会基于Linux kernel,开发Android app会基于Android ...

  4. 浅谈 PHP Yaf 开启session之后对响应头的影响

    当使用PHP Yaf框架,如果某个 Action 在返回响应(输出页面或者返回json)之前,启用了session,那么将会在响应头里面加上强制不缓存的响应头,也就是如下的三个响应头. Expires ...

  5. PaddlePaddle实现线性回归

    在本次实验中我们将使用PaddlePaddle来搭建一个简单的线性回归模型,并利用这一模型预测你的储蓄(在某地区)可以购买多大面积的房子.并且在学习模型搭建的过程中,了解到机器学习的若干重要概念,掌握 ...

  6. tensorflow源码分析

    前言: 一般来说,如果安装tensorflow主要目的是为了调试些小程序的话,只要下载相应的包,然后,直接使用pip install tensorflow即可. 但有时我们需要将Tensorflow的 ...

  7. TensorFlow+TVM优化NMT神经机器翻译

    TensorFlow+TVM优化NMT神经机器翻译 背景 神经机器翻译(NMT)是一种自动化的端到端方法,具有克服传统基于短语的翻译系统中的弱点的潜力.本文为全球电子商务部署NMT服务. 目前,将Tr ...

  8. 昇思MindSpore全场景AI框架 1.6版本,更高的开发效率,更好地服务开发者

    摘要:本文带大家快速浏览昇思MindSpore全场景AI框架1.6版本的关键特性. 全新的昇思MindSpore全场景AI框架1.6版本已发布,此版本中昇思MindSpore全场景AI框架易用性不断改 ...

  9. Relay外部库使用

    Relay外部库使用 本文介绍如何将cuDNN或cuBLAS等外部库与Relay一起使用. Relay内部使用TVM生成目标特定的代码.例如,使用cuda后端,TVM为用户提供的网络中的所有层生成cu ...

随机推荐

  1. 01- web测试快速入门

    web测试与APP测试最非常重要的两个测试种类.web端指的就是在浏览器页面的测试. 你测试的软件通常是两种结构的,B/S架构与C/S架构. B/S架构:Browser/Server 浏览器与服务器模 ...

  2. Android adb不是内部或外部命令 问题解决

    就是没有配置环境变量, 这个只需要将android安装:例如C:\Program File\android-sdk-windows\tools加入到系统变量Path中,需要注意的是Path中会配置的有 ...

  3. 【DB宝51】CentOS7修改网卡名称

    目录 1.修改/etc/default/grub文件 2.修改/etc/udev/rules.d/70-persistent-net.rules文件 3.修改网卡配置文件 4.重启服务器 需求:原来的 ...

  4. PHP 读取文件夹(比如某共享文件夹)中的图片并显示

    1.获取文件夹下图片public function albumList(){ $share_url = input('path'); $files = getImgList($share_url); ...

  5. 异常检测算法Robust Random Cut Forest(RRCF)关键定理引理证明

    摘要:RRCF是亚马逊发表的一篇异常检测算法,是对周志华孤立森林的改进.但是相比孤立森林,具有更为扎实的理论基础.文章的理论论证相对较为晦涩,且没给出详细的证明过程.本文不对该算法进行详尽的描述,仅对 ...

  6. Catalan数以及相关性质的证明

    \(Catalan\) 数相关证明 Mushroom 2021-5-14 \(Catalan\)数的定义 给定一个凸\(n + 1\)边形, 通过在内部不相交的对角线,把它划分成为三角形的组合,不同的 ...

  7. Codeforces Round #691 (Div. 2)

    A. Red-Blue Shuffle 题意:有两个长度为n的数组,数组a和数组b,问那个数组中的数字相比之下比另一个数组中相应位置的元素值更大一些,如果数组a大就输出RED,如果数组b大就输出BLU ...

  8. (转)如何优雅的使用rabbit mq

    RabbitMQ无疑是目前最流行的消息队列之一,对各种语言环境的支持也很丰富,作为一个.NET developer有必要学习和了解这一工具.消息队列的使用场景大概有3种: 1.系统集成,分布式系统的设 ...

  9. Convert character array to string in MATLAB

    Matlab提取特征值是经常要读取多个图片文件,把文件名保存在数组中后再读取会出错.从stackoverflow中找到如下解决方法: I've a M by N matrix, each cell c ...

  10. linux如何patch打补丁

    1. 创建2个文件 1.txt 和 2.txt 并在1.txt基础上修改成为2.txt book@100ask:~/patch$ ls 1.txt 2.txt book@100ask:~/patch$ ...