【TVM 教程】线性和递归核
Apache TVM 是一个端到端的深度学习编译框架,适用于 CPU、GPU 和各种机器学习加速芯片。更多 TVM 中文文档可访问 → https://tvm.hyper.ai/
作者:Tianqi Chen
下面介绍如何在 TVM 中进行递归计算(神经网络中的典型模式)。
from __future__ import absolute_import, print_functionimport tvm
import tvm.testing
from tvm import te
import numpy as np
TVM 用线性算子来描述符号循环。以下线性算子计算 X 列上的累积和。
线性在张量的最高维度上进行。s_state 是描述线性转换状态的占位符。s_init 描述如何初始化前 k 个时间步长,其第一个维度为 1,描述了如何初始化第一个时间步长的状态。
s_update 描述了如何更新时间步长 t 处的值,更新的值可通过状态占位符引用上一个时间步长的值。注意在当前或之后的时间步长引用 s_state 是无效的。
线性包含状态占位符、初始值和更新描述。推荐列出线性单元的输入,线性的结果是一个张量—— s_state 在时域更新后的结果。
m = te.var("m")
n = te.var("n")
X = te.placeholder((m, n), name="X")
s_state = te.placeholder((m, n))
s_init = te.compute((1, n), lambda _, i: X[0, i])
s_update = te.compute((m, n), lambda t, i: s_state[t - 1, i] + X[t, i])
s_scan = tvm.te.scan(s_init, s_update, s_state, inputs=[X])
调度线性单元
通过分别调度 update 和 init 部分来调度线性体。注意,调度更新部分的第一个迭代维度是无效的。要在时间迭代上拆分,用户可以在 scan_op.scan_axis 上进行调度。
s = te.create_schedule(s_scan.op)
num_thread = 256
block_x = te.thread_axis("blockIdx.x")
thread_x = te.thread_axis("threadIdx.x")
xo, xi = s[s_init].split(s_init.op.axis[1], factor=num_thread)
s[s_init].bind(xo, block_x)
s[s_init].bind(xi, thread_x)
xo, xi = s[s_update].split(s_update.op.axis[1], factor=num_thread)
s[s_update].bind(xo, block_x)
s[s_update].bind(xi, thread_x)
print(tvm.lower(s, [X, s_scan], simple_mode=True))
输出结果:
@main = primfn(X_1: handle, scan_1: handle) -> ()attr = {"from_legacy_te_schedule": True, "global_symbol": "main", "tir.noalias": True}buffers = {X: Buffer(X_2: Pointer(float32), float32, [(stride: int32*m: int32)], [], type="auto"),scan: Buffer(scan_2: Pointer(float32), float32, [(stride_1: int32*m)], [], type="auto")}buffer_map = {X_1: X, scan_1: scan}preflattened_buffer_map = {X_1: X_3: Buffer(X_2, float32, [m, n: int32], [stride, stride_2: int32], type="auto"), scan_1: scan_3: Buffer(scan_2, float32, [m, n], [stride_1, stride_3: int32], type="auto")} {attr [IterVar(blockIdx.x: int32, (nullptr), "ThreadIndex", "blockIdx.x")] "thread_extent" = floordiv((n + 255), 256);attr [IterVar(threadIdx.x: int32, (nullptr), "ThreadIndex", "threadIdx.x")] "thread_extent" = 256;if @tir.likely((((blockIdx.x*256) + threadIdx.x) < n), dtype=bool) {scan[(((blockIdx.x*256) + threadIdx.x)*stride_3)] = X[(((blockIdx.x*256) + threadIdx.x)*stride_2)]}for (scan.idx: int32, 0, (m - 1)) {attr [IterVar(blockIdx.x, (nullptr), "ThreadIndex", "blockIdx.x")] "thread_extent" = floordiv((n + 255), 256);attr [IterVar(threadIdx.x, (nullptr), "ThreadIndex", "threadIdx.x")] "thread_extent" = 256;if @tir.likely((((blockIdx.x*256) + threadIdx.x) < n), dtype=bool) {let cse_var_1: int32 = (scan.idx + 1)scan[((cse_var_1*stride_1) + (((blockIdx.x*256) + threadIdx.x)*stride_3))] = (scan[((scan.idx*stride_1) + (((blockIdx.x*256) + threadIdx.x)*stride_3))] + X[((cse_var_1*stride) + (((blockIdx.x*256) + threadIdx.x)*stride_2))])}}
}
构建和验证
可以像其他 TVM 内核一样构建线性内核,这里用 numpy 来验证结果的正确性。
fscan = tvm.build(s, [X, s_scan], "cuda", name="myscan")
dev = tvm.cuda(0)
n = 1024
m = 10
a_np = np.random.uniform(size=(m, n)).astype(s_scan.dtype)
a = tvm.nd.array(a_np, dev)
b = tvm.nd.array(np.zeros((m, n), dtype=s_scan.dtype), dev)
fscan(a, b)
tvm.testing.assert_allclose(b.numpy(), np.cumsum(a_np, axis=0))
多阶段线性单元
以上示例用 s_update 中的一个张量计算阶段描述了线性单元,可以在线性单元中使用多个张量级。
以下代码演示了有两个阶段操作的线性单元中的线性过程:
m = te.var("m")
n = te.var("n")
X = te.placeholder((m, n), name="X")
s_state = te.placeholder((m, n))
s_init = te.compute((1, n), lambda _, i: X[0, i])
s_update_s1 = te.compute((m, n), lambda t, i: s_state[t - 1, i] * 2, name="s1")
s_update_s2 = te.compute((m, n), lambda t, i: s_update_s1[t, i] + X[t, i], name="s2")
s_scan = tvm.te.scan(s_init, s_update_s2, s_state, inputs=[X])
这些中间张量可以正常调度。为了确保正确性,TVM 创建了一个组约束——禁用线性循环之外的 compute_at 位置的线性体。
s = te.create_schedule(s_scan.op)
xo, xi = s[s_update_s2].split(s_update_s2.op.axis[1], factor=32)
s[s_update_s1].compute_at(s[s_update_s2], xo)
输出结果:
print(tvm.lower(s, [X, s_scan], simple_mode=True))
@main = primfn(X_1: handle, scan_1: handle) -> ()attr = {"from_legacy_te_schedule": True, "global_symbol": "main", "tir.noalias": True}buffers = {X: Buffer(X_2: Pointer(float32), float32, [(stride: int32*m: int32)], [], type="auto"),scan: Buffer(scan_2: Pointer(float32), float32, [(stride_1: int32*m)], [], type="auto")}buffer_map = {X_1: X, scan_1: scan}preflattened_buffer_map = {X_1: X_3: Buffer(X_2, float32, [m, n: int32], [stride, stride_2: int32], type="auto"), scan_1: scan_3: Buffer(scan_2, float32, [m, n], [stride_1, stride_3: int32], type="auto")} {allocate(s1: Pointer(global float32), float32, [32]), storage_scope = global {for (i: int32, 0, n) {scan[(i*stride_3)] = X[(i*stride_2)]}for (scan.idx: int32, 0, (m - 1)) {for (i.outer: int32, 0, floordiv((n + 31), 32)) {for (i_1: int32, 0, 32) {if @tir.likely((((i.outer*32) + i_1) < n), dtype=bool) {s1_1: Buffer(s1, float32, [32], [])[i_1] = (scan[((scan.idx*stride_1) + (((i.outer*32) + i_1)*stride_3))]*2f32)}}for (i.inner: int32, 0, 32) {if @tir.likely((((i.outer*32) + i.inner) < n), dtype=bool) {let cse_var_2: int32 = (scan.idx + 1)let cse_var_1: int32 = ((i.outer*32) + i.inner)scan[((cse_var_2*stride_1) + (cse_var_1*stride_3))] = (s1_1[i.inner] + X[((cse_var_2*stride) + (cse_var_1*stride_2))])}}}}}
}
多状态
对于像 RNN 这样的复杂应用,需要多个递归状态。线性支持多个递归状态,以下示例演示如何构建具有两种状态的递归。
m = te.var("m")
n = te.var("n")
l = te.var("l")
X = te.placeholder((m, n), name="X")
s_state1 = te.placeholder((m, n))
s_state2 = te.placeholder((m, l))
s_init1 = te.compute((1, n), lambda _, i: X[0, i])
s_init2 = te.compute((1, l), lambda _, i: 0.0)
s_update1 = te.compute((m, n), lambda t, i: s_state1[t - 1, i] + X[t, i])
s_update2 = te.compute((m, l), lambda t, i: s_state2[t - 1, i] + s_state1[t - 1, 0])
s_scan1, s_scan2 = tvm.te.scan([s_init1, s_init2], [s_update1, s_update2], [s_state1, s_state2], inputs=[X]
)
s = te.create_schedule(s_scan1.op)
print(tvm.lower(s, [X, s_scan1, s_scan2], simple_mode=True))
输出结果:
@main = primfn(X_1: handle, scan_2: handle, scan_3: handle) -> ()attr = {"from_legacy_te_schedule": True, "global_symbol": "main", "tir.noalias": True}buffers = {X: Buffer(X_2: Pointer(float32), float32, [(stride: int32*m: int32)], [], type="auto"),scan: Buffer(scan_4: Pointer(float32), float32, [(stride_1: int32*m)], [], type="auto"),scan_1: Buffer(scan_5: Pointer(float32), float32, [(stride_2: int32*m)], [], type="auto")}buffer_map = {X_1: X, scan_2: scan, scan_3: scan_1}preflattened_buffer_map = {X_1: X_3: Buffer(X_2, float32, [m, n: int32], [stride, stride_3: int32], type="auto"), scan_2: scan_6: Buffer(scan_4, float32, [m, n], [stride_1, stride_4: int32], type="auto"), scan_3: scan_7: Buffer(scan_5, float32, [m, l: int32], [stride_2, stride_5: int32], type="auto")} {for (i: int32, 0, n) {scan[(i*stride_4)] = X[(i*stride_3)]}for (i_1: int32, 0, l) {scan_1[(i_1*stride_5)] = 0f32}for (scan.idx: int32, 0, (m - 1)) {for (i_2: int32, 0, n) {let cse_var_1: int32 = (scan.idx + 1)scan[((cse_var_1*stride_1) + (i_2*stride_4))] = (scan[((scan.idx*stride_1) + (i_2*stride_4))] + X[((cse_var_1*stride) + (i_2*stride_3))])}for (i_3: int32, 0, l) {scan_1[(((scan.idx + 1)*stride_2) + (i_3*stride_5))] = (scan_1[((scan.idx*stride_2) + (i_3*stride_5))] + scan[(scan.idx*stride_1)])}}
}
总结
本教程演示了如何使用线性原语。
- 用 init 和 update 描述线性。
- 将线性单元当作正常 schedule 进行调度。
- 对于复杂的工作负载,在线性单元中使用多个状态和步骤。
下载 Python 源代码:scan.py
下载 Jupyter Notebook:scan.ipynb
相关文章:
【TVM 教程】线性和递归核
Apache TVM 是一个端到端的深度学习编译框架,适用于 CPU、GPU 和各种机器学习加速芯片。更多 TVM 中文文档可访问 → https://tvm.hyper.ai/ 作者:Tianqi Chen 下面介绍如何在 TVM 中进行递归计算(神经网络中的典型模式)。 from…...
猫主福利大放送,双11猫奴们的购物狂欢节 养猫必备清单
双十一购物狂欢节终于来啦!铲屎官们是不是已经迫不及待想为心爱的猫咪挑选新玩具和必需品了呢?作为一名资深铲屎官,我专门为大家整理了一份双十一养猫必备清单。抓住这个难得的机会,让我们为猫咪挑选最舒适、最实用的好物吧&#…...
Linux中gcc的使用
GCC的基本概念和用途 GCC(GNU Compiler Collection)是GNU项目提供的一套编程语言编译器集合,包括了C、C、Objective-C、Fortran、Java、Ada和Go等语言的编译器。GCC广泛用于Linux和其他类Unix系统中,用于将源代码编译成可执行文件…...
React 组件 API
React 组件 API React 组件 API 是 React 应用程序开发中的核心部分,它提供了一系列的接口和方法,使得开发者能够创建和管理组件的状态、属性以及生命周期。在本篇文章中,我们将深入探讨 React 组件 API 的各个方面,包括组件的定…...
一个使用接口模式、工厂模式、模板方法模式的日志文件系统
引言: 编写一个与具体业务无关的示例代码。这个示例代码主要体现以下几个设计思想和模式: 接口模式(Interface Pattern):定义接口类,并让具体实现类去实现该接口的功能。 工厂模式(Factory Pa…...
openjdk17 C++源码是怎么给java字段赋值的
##java源码 public class OtherClass {public static int CONSTANT_O9876;public int o1234;public void dddd(){String dddd "dddd";//System.out.println(dddd);System.out.println(ddddCONSTANT_O);}} public int o1234; 在openjdk17中 C源码怎么执行这段代码…...
C++初阶(八)--内存管理
目录 引入: 一、C中的内存布局 1.内存区域 2.示例变量存储位置说明 二、C语言中动态内存管理 三、C内存管理方式 1.new/delete操作内置类型 2.new和delete操作自定义类型 四、operator new与operator delete函数(重要点进行讲解) …...
C# 企业微信机器人推送消息 windows服务应用程序的使用
C# 企业微信机器人推送消息 先添加一个机器人! 然后查看机器人就可以得到一个 webhook 特别特别要注意:一定要保护好机器人的webhook地址,避免泄漏! 然后开始写代码 ,只需要httpPost 调用一下这个地址就可以发送消息了。 首先我…...
社区交流系统设计与实现
社区交流系统设计与实现 1. 系统概述 社区交流系统是一个基于PHP和SQL的Web应用程序,旨在为用户提供一个互动交流的平台。该系统允许用户注册、发布帖子、回复帖子、查看其他用户的帖子和回复,以及管理个人资料,提高用户之间的互动和信息共享…...
【模型学习之路】手写+分析bert
手写分析bert 目录 前言 架构 embeddings Bertmodel 预训练任务 MLM NSP Bert 后话 netron可视化 code2flow可视化 fine tuning 前言 Attention is all you need! 读本文前,建议至少看懂【模型学习之路】手写分析Transformer-CSDN博客。 毕竟Bert是tr…...
Redis学习文档(常见面试题)
目录 Redis回收使用的是什么算法? Redis如何做大量数据插入? 为什么要做Redis分区? 你知道有哪些Redis分区实现方案? Redis分区有什么缺点? Redis持久化数据和缓存怎么做扩容? 分布式Redis是前期做还…...
【C++刷题】力扣-#594-最长和谐子序列
题目描述 和谐数组是指一个数组里元素的最大值和最小值之间的差别 正好是 1 。 给你一个整数数组 nums ,请你在所有可能的子序列中找到最长的和谐子序列的长度。 数组的 子序列是一个由数组派生出来的序列,它可以通过删除一些元素或不删除元素、且不改变…...
MoveIt 控制自己的真实机械臂【2】——编写 action server 端代码
完成了 MoveIt 这边 action client 的基本配置,MoveIt 理论上可以将规划好的 trajectory 以 action 的形式发布出来了,浅浅尝试一下,在 terminal 中运行 roslaunch xmate7_moveit_config_new demo.launch 报错提示他在等待 xmate_arm_control…...
C#制作学生管理系统
定义学生类 定义一个简单的类来表示学生,包括学号、姓名、性别、年龄、电话、地址。再给其添加一个方法利于后续添加方法查看学生信息。 //定义学生类 public class student {public int ID { get; set; }//开放读写权限public string Name { get; set; }public i…...
python Pandas合并(单元格、sheet、excel )
安装 Pandas 和 openpyxl 首先,确保已经安装了 Pandas 和 openpyxl。可以通过 pip 安装: pip install pandas openpyxl 创建 DataFrame import pandas as pd # 创建 DataFrame df1 pd.DataFrame({ 姓名: [张三, 李四, 王五], 年龄: [25, 30, 35]…...
OJ在线编程常见输入输出练习【JavaScript】
(注:本文是对【JavaScript Node 】 ACM模式,常见输入输出练习相关内容的介绍!!!) 牛客竞赛_ACM/NOI/CSP/CCPC/ICPC算法编程高难度练习赛_牛客竞赛OJ 一、ACM模式下的编辑页面 二、ACM模式下&a…...
新能源汽车空调系统:绿色出行的舒适保障
在新能源汽车迅速发展的今天,空调系统作为提升驾乘舒适度的重要组成部分,发挥着不可或缺的作用。新能源汽车空调系统主要由压缩机、冷凝器、节流装置和蒸发器四大件组成,它们协同工作,为车内提供适宜的温度和湿度环境。 一、压缩…...
Date工具类详细汇总-Date日期相关方法
# 1024程序员节 | 征文 # 目录 简介 Date工具类单元测试 Date工具类 简介 本文章是个人总结实际工作中常用到的Date工具类,主要包含Java-jdk8以下版本的Date相关使用方法,可以方便的在工作中灵活的应用,在个人工作期间频繁使用这些时间的格…...
TMUX1308PWR规格书 数据手册 具有注入电流控制功能的 5V 双向 8:1单通道和 4:1 双通道多路复用器芯片
TMUX1308 和 TMUX1309 为通用互补金属氧化物半导体 (CMOS) 多路复用器 (MUX)。TMUX1308 是 8:1单通道(单端)多路复用器,而 TMUX1309 是 4:1 双通道(差分)多路复用器。这些器件可在源极 (Sx) 和漏极 (Dx) 引脚上支持从 …...
证件照怎么换底色?简单又快速!不看后悔
一、引言 证件照在我们的生活中有着广泛的应用,无论是求职、考试还是办理各种证件,都需要用到不同底色的证件照。传统的换底色方法往往比较复杂,需要一定的专业技能和软件操作经验。但是现在,有了更简单快捷的方法,让你…...
Rust 基础语法与常用特性
Rust 跨界:全面掌握跨平台应用开发 第一章:快速上手 Rust 1.2 基础语法与常用特性 1.2.1 数据类型与控制流 数据类型 Rust 提供了丰富的内置数据类型,主要分为标量类型和复合类型。 标量类型 标量类型表示单一的值,Rust 中…...
一、开发环境的搭建
环境搭建步骤: 下载软件安装软件运行软件 其他: Visual studio 安装包文件:https://www.alipan.com/s/nd5RgzD4e3b 下载软件 在浏览器中搜索Visual studio,选择如图的选项 点击该区域,进入该页面,【或…...
Docker:存储原理
Docker:存储原理 镜像联合文件系统overlay镜像存储结构容器存储结构 存储卷绑定挂载存储卷结构 镜像 联合文件系统 联合文件系统Union File System是一种分层,轻量且高效的文件系统。其将整个文件系统分为多个层,层与层之间进行覆盖&#x…...
ts:数组的常用方法(push、pop、shift、unshift、splice、slice)
前端css中filter的使用 一、主要内容说明二、例子(一)、push方法(尾添加)1.源码1 (push方法)2.源码1运行效果 (二)、pop方法(尾删除)1.源码2(pop方…...
物联网网关确保设备安全
物联网(IoT)网关在确保设备安全方面扮演着至关重要的角色。 作为连接物联网设备和云端或企业系统的中介,物联网网关可以实施多种安全措施来保护设备和数据。 是物联网网关确保设备安全的关键方法: 1. 设备认证和授权 认证&…...
Vue学习笔记(五)
Class绑定 数据绑定的一个常见需求场景式操纵元素的CSS class列表,因为class是attribute,我们可以和其他attribute一样使用v-bind将它们和动态的字符串绑定。但是,在处理比较复杂的绑定时,通过拼接生成字符串是麻烦且易出错的。因此…...
Nestjs返回格式小结
在 NestJS 中,除了 text/event-stream(用于 Server-Sent Events)之外,还有多种格式的返回方式,具体取决于你的应用需求。以下是一些常见的返回格式及其示例: 1. JSON 格式 Get(json) getJsonResponse(Res…...
【力扣刷题实战】相同的树
大家好,我是小卡皮巴拉 文章目录 目录 力扣题目: 相同的树 题目描述 示例 1: 示例 2: 示例 3: 解题思路 题目理解 算法选择 具体思路 解题要点 完整代码(C语言) 兄弟们共勉 &#…...
Golang | Leetcode Golang题解之第515题在每个树行中找最大值
题目: 题解: func largestValues(root *TreeNode) (ans []int) {if root nil {return}q : []*TreeNode{root}for len(q) > 0 {maxVal : math.MinInt32tmp : qq nilfor _, node : range tmp {maxVal max(maxVal, node.Val)if node.Left ! nil {q …...
Zookeeper 对于 Kafka 的作用是什么?
大家好,我是锋哥。今天分享关于【Zookeeper 对于 Kafka 的作用是什么?】面试题?希望对大家有帮助; Zookeeper 对于 Kafka 的作用是什么? 1000道 互联网大厂Java工程师 精选面试题-Java资源分享网 ZooKeeper 在 Kafka…...
怎么把电脑字体导入wordpress/做百度线上推广
在开始解读AQS的共享功能前,我们再重温一下CountDownLatch,CountDownLatch为java.util.concurrent包下的计数器工具类,常被用在多线程环境下,它在初始时需要指定一个计数器的大小,然后可被多个线程并发的实现减1操作&a…...
石家庄做网站裕华区/外贸网站推广平台
在对AOT\Tables下的数据表做BP检查的时候,如果出现“Code to handle the InventDimId field must be added to the Multisite Activation Wizard.” 这样的BP错误,则到InventSiteActivateDimFieldsCheck类中,找到updateableFields或者nonUpda…...
什么叫网站后台/seo快速排名上首页
首先看看官网API的说明 Oracle官网API : Optional是一个容器对象,它可能包含,也可能不包含一个非空的值,如果这个值存在,isPresent方法将返回true,get方法将会返回它本身。Optional提供一些额外的方法,这些方法依赖于它…...
温岭网站开发/朋友圈信息流广告投放价格
Exchange Server 2016 RTM 可以在 Windows Server 2012、Windows Server 2012 R2 和 Windows Server 2016 的标准版和企业版中进行部署安装,但 Exchange Server 2016 只支持完整 GUI 的 Windows Server 而不支持核心安装的系统,本文我们将对快速部署方式…...
网上代做论文的网站好/优化系统的软件
在当今的智能手机市场,iPhone和安卓形成了两大操作系统阵营,但是由于两者的操作系统不同、用户群体不同,导致使用手机的时候形成了不同的使用习惯,今天小胖就来总结一下两者在使用手机时的一些区别。清后台因为早年的安卓手机性能…...
油气集输毕业设计代做网站/大数据分析营销平台
《专利法》第二条第三款规定,实用新型是指对商品形状、结构或者组合提出的实用新技术方案。从定义上来说,我们可以知道只保护形状和结构的实用新产品的技术方案。那么,你知道申请实用新型专利的要点吗?这里有一个详细的答案。 下面…...