当前位置: 首页 > news >正文

【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 是一个端到端的深度学习编译框架&#xff0c;适用于 CPU、GPU 和各种机器学习加速芯片。更多 TVM 中文文档可访问 → https://tvm.hyper.ai/ 作者&#xff1a;Tianqi Chen 下面介绍如何在 TVM 中进行递归计算&#xff08;神经网络中的典型模式&#xff09;。 from…...

猫主福利大放送,双11猫奴们的购物狂欢节 养猫必备清单

双十一购物狂欢节终于来啦&#xff01;铲屎官们是不是已经迫不及待想为心爱的猫咪挑选新玩具和必需品了呢&#xff1f;作为一名资深铲屎官&#xff0c;我专门为大家整理了一份双十一养猫必备清单。抓住这个难得的机会&#xff0c;让我们为猫咪挑选最舒适、最实用的好物吧&#…...

Linux中gcc的使用

GCC的基本概念和用途 GCC&#xff08;GNU Compiler Collection&#xff09;是GNU项目提供的一套编程语言编译器集合&#xff0c;包括了C、C、Objective-C、Fortran、Java、Ada和Go等语言的编译器。GCC广泛用于Linux和其他类Unix系统中&#xff0c;用于将源代码编译成可执行文件…...

React 组件 API

React 组件 API React 组件 API 是 React 应用程序开发中的核心部分&#xff0c;它提供了一系列的接口和方法&#xff0c;使得开发者能够创建和管理组件的状态、属性以及生命周期。在本篇文章中&#xff0c;我们将深入探讨 React 组件 API 的各个方面&#xff0c;包括组件的定…...

一个使用接口模式、工厂模式、模板方法模式的日志文件系统

引言&#xff1a; 编写一个与具体业务无关的示例代码。这个示例代码主要体现以下几个设计思想和模式&#xff1a; 接口模式&#xff08;Interface Pattern&#xff09;&#xff1a;定义接口类&#xff0c;并让具体实现类去实现该接口的功能。 工厂模式&#xff08;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++初阶(八)--内存管理

目录 引入&#xff1a; 一、C中的内存布局 1.内存区域 2.示例变量存储位置说明 二、C语言中动态内存管理 三、C内存管理方式 1.new/delete操作内置类型 2.new和delete操作自定义类型 四、operator new与operator delete函数&#xff08;重要点进行讲解&#xff09; …...

C# 企业微信机器人推送消息 windows服务应用程序的使用

C# 企业微信机器人推送消息 先添加一个机器人! 然后查看机器人就可以得到一个 webhook 特别特别要注意&#xff1a;一定要保护好机器人的webhook地址&#xff0c;避免泄漏&#xff01; 然后开始写代码 &#xff0c;只需要httpPost 调用一下这个地址就可以发送消息了。 首先我…...

社区交流系统设计与实现

社区交流系统设计与实现 1. 系统概述 社区交流系统是一个基于PHP和SQL的Web应用程序&#xff0c;旨在为用户提供一个互动交流的平台。该系统允许用户注册、发布帖子、回复帖子、查看其他用户的帖子和回复&#xff0c;以及管理个人资料&#xff0c;提高用户之间的互动和信息共享…...

【模型学习之路】手写+分析bert

手写分析bert 目录 前言 架构 embeddings Bertmodel 预训练任务 MLM NSP Bert 后话 netron可视化 code2flow可视化 fine tuning 前言 Attention is all you need! 读本文前&#xff0c;建议至少看懂【模型学习之路】手写分析Transformer-CSDN博客。 毕竟Bert是tr…...

Redis学习文档(常见面试题)

目录 Redis回收使用的是什么算法&#xff1f; Redis如何做大量数据插入&#xff1f; 为什么要做Redis分区&#xff1f; 你知道有哪些Redis分区实现方案&#xff1f; Redis分区有什么缺点&#xff1f; Redis持久化数据和缓存怎么做扩容&#xff1f; 分布式Redis是前期做还…...

【C++刷题】力扣-#594-最长和谐子序列

题目描述 和谐数组是指一个数组里元素的最大值和最小值之间的差别 正好是 1 。 给你一个整数数组 nums &#xff0c;请你在所有可能的子序列中找到最长的和谐子序列的长度。 数组的 子序列是一个由数组派生出来的序列&#xff0c;它可以通过删除一些元素或不删除元素、且不改变…...

MoveIt 控制自己的真实机械臂【2】——编写 action server 端代码

完成了 MoveIt 这边 action client 的基本配置&#xff0c;MoveIt 理论上可以将规划好的 trajectory 以 action 的形式发布出来了&#xff0c;浅浅尝试一下&#xff0c;在 terminal 中运行 roslaunch xmate7_moveit_config_new demo.launch 报错提示他在等待 xmate_arm_control…...

C#制作学生管理系统

定义学生类 定义一个简单的类来表示学生&#xff0c;包括学号、姓名、性别、年龄、电话、地址。再给其添加一个方法利于后续添加方法查看学生信息。 //定义学生类 public class student {public int ID { get; set; }//开放读写权限public string Name { get; set; }public i…...

python Pandas合并(单元格、sheet、excel )

安装 Pandas 和 openpyxl 首先&#xff0c;确保已经安装了 Pandas 和 openpyxl。可以通过 pip 安装&#xff1a; pip install pandas openpyxl 创建 DataFrame import pandas as pd # 创建 DataFrame df1 pd.DataFrame({ 姓名: [张三, 李四, 王五], 年龄: [25, 30, 35]…...

OJ在线编程常见输入输出练习【JavaScript】

&#xff08;注&#xff1a;本文是对【JavaScript Node 】 ACM模式&#xff0c;常见输入输出练习相关内容的介绍&#xff01;&#xff01;&#xff01;&#xff09; 牛客竞赛_ACM/NOI/CSP/CCPC/ICPC算法编程高难度练习赛_牛客竞赛OJ 一、ACM模式下的编辑页面 二、ACM模式下&a…...

新能源汽车空调系统:绿色出行的舒适保障

在新能源汽车迅速发展的今天&#xff0c;空调系统作为提升驾乘舒适度的重要组成部分&#xff0c;发挥着不可或缺的作用。新能源汽车空调系统主要由压缩机、冷凝器、节流装置和蒸发器四大件组成&#xff0c;它们协同工作&#xff0c;为车内提供适宜的温度和湿度环境。 一、压缩…...

Date工具类详细汇总-Date日期相关方法

# 1024程序员节 | 征文 # 目录 简介 Date工具类单元测试 Date工具类 简介 本文章是个人总结实际工作中常用到的Date工具类&#xff0c;主要包含Java-jdk8以下版本的Date相关使用方法&#xff0c;可以方便的在工作中灵活的应用&#xff0c;在个人工作期间频繁使用这些时间的格…...

TMUX1308PWR规格书 数据手册 具有注入电流控制功能的 5V 双向 8:1单通道和 4:1 双通道多路复用器芯片

TMUX1308 和 TMUX1309 为通用互补金属氧化物半导体 (CMOS) 多路复用器 (MUX)。TMUX1308 是 8:1单通道&#xff08;单端&#xff09;多路复用器&#xff0c;而 TMUX1309 是 4:1 双通道&#xff08;差分&#xff09;多路复用器。这些器件可在源极 (Sx) 和漏极 (Dx) 引脚上支持从 …...

证件照怎么换底色?简单又快速!不看后悔

一、引言 证件照在我们的生活中有着广泛的应用&#xff0c;无论是求职、考试还是办理各种证件&#xff0c;都需要用到不同底色的证件照。传统的换底色方法往往比较复杂&#xff0c;需要一定的专业技能和软件操作经验。但是现在&#xff0c;有了更简单快捷的方法&#xff0c;让你…...

OpenClaw定时任务管理:千问3.5-27B驱动日报自动生成

OpenClaw定时任务管理&#xff1a;千问3.5-27B驱动日报自动生成 1. 为什么需要自动化日报 每周五下午&#xff0c;我都会陷入一种"汇报焦虑"——要手动整理GitHub提交记录、汇总JIRA任务进度、编写本周技术总结。这个过程通常要花费1-2小时&#xff0c;而且内容模板…...

OpenClaw技能扩展实战:安装Phi-3-vision-128k-instruct专用图文处理模块

OpenClaw技能扩展实战&#xff1a;安装Phi-3-vision-128k-instruct专用图文处理模块 1. 为什么需要专用技能模块&#xff1f; 上周我在整理技术文档时遇到一个典型场景&#xff1a;需要将十几份混杂着截图和文字说明的会议纪要&#xff0c;自动转换成结构化的Markdown文件。当…...

OpenClaw+SecGPT-14B联动方案:3类网络安全自动化场景实测

OpenClawSecGPT-14B联动方案&#xff1a;3类网络安全自动化场景实测 1. 为什么选择这个技术组合&#xff1f; 去年我在做安全研究时&#xff0c;经常需要重复处理三类任务&#xff1a;分析漏洞报告、检查日志异常、收集威胁情报。这些工作既需要专业判断&#xff0c;又包含大…...

Matterport3D数据集:从全景构建到三维理解的实践指南

1. Matterport3D数据集全景解析 第一次接触Matterport3D数据集时&#xff0c;我被它庞大的数据规模震撼到了。这个数据集包含了90个完整的建筑场景&#xff0c;由194,400张RGB-D图像组成&#xff0c;覆盖了10,800个全景视角。简单来说&#xff0c;它就像是用专业相机把整栋房子…...

Qwen3.5-9B多场景应用:心理咨询对话记录分析+情绪倾向识别案例

Qwen3.5-9B多场景应用&#xff1a;心理咨询对话记录分析情绪倾向识别案例 1. 项目概述 Qwen3.5-9B是一款拥有90亿参数的开源大语言模型&#xff0c;具备强大的逻辑推理、代码生成和多轮对话能力。该模型特别适合处理心理咨询对话记录分析任务&#xff0c;能够准确识别对话中的…...

SEO_本地中小企业快速见效的SEO操作指南(345 )

SEO:本地中小企业快速见效的SEO操作指南 在当今数字化时代&#xff0c;本地中小企业如何在竞争激烈的市场中脱颖而出&#xff0c;是每一个企业主都需要面对的问题。本文将从多个角度为你详细解析如何通过SEO&#xff08;搜索引擎优化&#xff09;让本地中小企业迅速见效。 问…...

PasteMD用户调研报告:2024年文档格式转换需求分析

PasteMD用户调研报告&#xff1a;2024年文档格式转换需求分析 1. 调研背景与核心发现 最近整理了500份来自不同行业用户的实际反馈&#xff0c;这些反馈不是问卷里的标准答案&#xff0c;而是真实工作场景中留下的痕迹——有深夜赶论文时的抱怨截图&#xff0c;有项目汇报前的…...

从Simulink模型到神经网络:一个完整的数据驱动建模与验证实践

1. 为什么需要从Simulink模型转向神经网络&#xff1f; 在控制系统工程领域&#xff0c;Simulink模型一直是建模和仿真的黄金标准。但最近几年&#xff0c;越来越多的工程师开始尝试用神经网络来替代传统模型。这背后有几个关键原因&#xff1a; 首先&#xff0c;传统物理模型在…...

如何用Mi-Create实现小米穿戴设备表盘个性化设计?

如何用Mi-Create实现小米穿戴设备表盘个性化设计&#xff1f; 【免费下载链接】Mi-Create Unofficial watchface creator for Xiaomi wearables ~2021 and above 项目地址: https://gitcode.com/gh_mirrors/mi/Mi-Create Mi-Create是一款专为2021年及以后发布的小米穿戴…...

从理论到实践:基于状态观测器的闭环系统设计与MATLAB仿真

1. 当状态看不见时&#xff0c;我们如何控制一个系统&#xff1f; 想象一下你在驾驶一辆汽车&#xff0c;但仪表盘全部失灵——看不到车速、转速、油量&#xff0c;甚至连方向盘转角都不知道。这时候如果要保持车道&#xff0c;你会怎么做&#xff1f;这就是控制工程中经典的状…...