当前位置: 首页 > 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;让你…...

Makefile -GNU和MakeFile关系(二)

跟我一起写Makefile 一、 GNU 到底是什么&#xff1f;&#xff08;极简版&#xff09; GNU 一套开源、免费、自由的软件生态系统 全称&#xff1a;GNU’s Not Unix&#xff08;递归梗&#xff0c;意思“不是Unix&#xff0c;但像Unix”&#xff09; 你可以把它理解成&#x…...

LDPC码实战:用Python对比比特翻转(BF)与和积(SPA)算法,谁更强?

LDPC码算法对决&#xff1a;Python实战比特翻转与和积译码性能全解析 在通信系统设计与优化过程中&#xff0c;LDPC码作为接近香农极限的高性能编码方案&#xff0c;其译码算法的选择直接影响系统性能与实现成本。本文将带您深入两种经典译码算法——比特翻转(BF)与和积(SPA)的…...

从ChatGLM到Qwen,不同架构大模型的监控差异图谱:8大维度对比分析(含GPU/TPU/NPU全栈指标映射表)

第一章&#xff1a;大模型工程化运维监控体系建设的范式演进 2026奇点智能技术大会(https://ml-summit.org) 大模型工程化运维监控已从传统AI服务的“可观测性补丁”阶段&#xff0c;演进为覆盖训练、推理、评估、反馈全生命周期的“闭环治理范式”。这一转变由模型规模跃升、…...

从数据到模型:最小二乘法在曲线拟合中的实战解析

1. 最小二乘法&#xff1a;数据与模型的桥梁 当你手头有一堆实验数据&#xff0c;想要找出其中的规律时&#xff0c;最小二乘法就是你的好帮手。想象一下&#xff0c;你是一位化学研究员&#xff0c;记录了一组反应速率随温度变化的数据点。这些点看起来大致呈直线分布&#xf…...

终极Windows驱动清理指南:用Driver Store Explorer释放20GB磁盘空间

终极Windows驱动清理指南&#xff1a;用Driver Store Explorer释放20GB磁盘空间 【免费下载链接】DriverStoreExplorer Driver Store Explorer 项目地址: https://gitcode.com/gh_mirrors/dr/DriverStoreExplorer Driver Store Explorer&#xff08;简称RAPR&#xff09…...

免费开源电路板查看器OpenBoardView终极指南:轻松查看和分析.brd文件

免费开源电路板查看器OpenBoardView终极指南&#xff1a;轻松查看和分析.brd文件 【免费下载链接】OpenBoardView View .brd files 项目地址: https://gitcode.com/gh_mirrors/op/OpenBoardView OpenBoardView是一款功能强大的免费开源电路板文件查看工具&#xff0c;专…...

LabVIEW声音采集避坑指南:从麦克风选型到.lvm文件存储,新手必看的5个实战细节

LabVIEW声音采集避坑指南&#xff1a;从麦克风选型到.lvm文件存储的5个实战细节 第一次用LabVIEW做声音采集时&#xff0c;我对着波形图上跳动的噪声信号发呆了整整两小时——采样率设对了&#xff0c;接线也没问题&#xff0c;但采集到的音频就像老式收音机调频不准时的杂音。…...

保姆级教程:用WPS JS API给你的WPS Office装个“外挂”(从环境配置到第一个加载项)

零基础玩转WPS加载项开发&#xff1a;从效率工具到个性化定制 你是否曾在处理大量WPS文档时&#xff0c;幻想过能有个"一键搞定"的神器&#xff1f;就像游戏玩家安装Mod扩展玩法一样&#xff0c;WPS其实也隐藏着强大的扩展能力。本文将带你走进WPS加载项开发的世界&a…...

Ozon运营5大核心场景,Captain AI全功能精准赋能

做Ozon运营,不少卖家会遇到这样的场景:选品时纠结不定,不清楚哪类产品适配市场、合规且有盈利空间;新品上架后缺乏有效推广思路,流量难以提升;财税申报流程复杂,担心操作失误引发违规;物流方案选择困难,难以平衡成本与时效;对账时面对俄语账单无从下手,无法清晰掌握…...

iOS开发中.p12证书密码丢失的应急处理与安全导出指南

1. 当.p12证书密码丢失时&#xff0c;开发者该怎么办&#xff1f; 遇到.p12证书密码丢失的情况&#xff0c;很多iOS开发者会感到手足无措。这种情况在实际开发中并不少见&#xff0c;特别是当项目交接或长时间未使用证书时。我曾经接手过一个老项目&#xff0c;就遇到过前任开发…...