深度学习模型部署-番外-TVM机器学习编译

什么是机器学习编译器/AI编译?

在这里插入图片描述

图片来自知乎大佬的文章

机器学习编译是指:将模型从训练形式转变为部署模式

  • 训练模式:使用训练框架定义的模型
  • 部署模式:部署所需要的模式,包括模型每个步骤的实现代码,管理资源的控制器,与应用程序开发环境的接口。

这个行为和传统的编译很像,所以称为机器学习编译,但是这里的编译和传统编译不完全一样,这里的输入是网络模型,输出可能是网络模型,也可能是生成的对于推理库函数的调用代码。
机器学习的三个目标:

  • 集成与最小化依赖:将必要的元素组合成一个程序,尽量减小应用大小,并且减小对外部环境依赖,使得模型可以部署到更多环境。
  • 利用硬件加速:
  • 通用优化

为什么学AI编译

在这里插入图片描述
AI发展越来越快,各种模型不断更新,像以前那种手写plugin去实现算子的时代估计很快就会过去,这就像以往人们手工提取数据特征一样,过去的特征工程,在大模型时代也逐渐落寞,而prompt变得更加重要。我们现在处在AI时代的黎明,目前AI还没有和各行各业相结合,有效推动社会生产力的发展,我们还可以容忍低效部署,等到将来,AI编译,端到端部署一定会取代人手工写算子,所以学AI编译非常有必要。

AI编译的要素

  • 数据类型/张量:输入,输出,中间变量的存储
  • 算子/张量函数:网络模型中的计算称为张量函数,张量函数不一定非要和模型中的算子定义完全一致,可以进行融合或者优化。
    请添加图片描述
    AI编译要做的事情也围绕这两个要素展开,优化张量函数,把张量函数的一种实现替换为另一种实现【张量函数不一定非要对应模型中的一个算子,多个算子也可以一起实现,总之,模型是一个计算图,在不影响结果的情况下,内部实现可以与模型定义不完全一致。】
    请添加图片描述

模型是计算的抽象,我们使用抽象 (Abstraction)来表示我们用来表示相同张量函数的方式。不同的抽象可能会指定一些细节,而忽略其他实现(Implementations)细节,对于一个模型,我们可以抽象为左边这种,将relu和线性层分隔开,也可以定义成中间这样,将两个结合到一起,这些都是抽象,与实现无关,只要能确保实现和抽象的结果一致即可,抽象是指定做什么,实现是具体怎么做,二者没有具体的界限,因为怎么做也可以理解为一种抽象,然后再向下细分怎么做,就比如循环本身是一个实现,也可以是一种抽象,底层用并行实现。

MLC 实际上是在相同或不同抽象下转换和组装张量函数的过程。我们将研究张量函数的不同抽象类型,以及它们如何协同工作以解决机器学习部署中的挑战。

TensorIR抽象

安装TVM:

python3 -m  pip install mlc-ai-nightly -f https://mlc.ai/wheels

上面提的抽象啦,实现啦,都是为了这个做铺垫,IR,中间层表示。学过编译原理的应该非常熟悉,对的,刚才讲的就是这个意思,把网络模型翻译成一种统一的程序抽象:TensorIR,然后具体实现由backend来完成,刚才提到的实现也是一种抽象也是在为下面做铺垫,因为IR也可以分为high IR,middle IR,hardware IR,依次向下,对于下一层的IR来说,上一层就是抽象,这种IR分层的好处是可以把专用优化和通用优化分离开,根据硬件平台进行最大程度实现模型优化。
关于抽象于实现的关系,以及抽象之间的转化,实战代码:

import numpy as np
import tvm
from tvm.ir.module import IRModule
from tvm.script import tir as T

dtype = "float32"
a_np = np.random.rand(128, 128).astype(dtype)
b_np = np.random.rand(128, 128).astype(dtype)
# a @ b is equivalent to np.matmul(a, b)
c_mm_relu = np.maximum(a_np @ b_np, 0)
# Numpy底层调用OpenBLAS等底层库以及自己的一些C语言实现来执行计算

# 等效实现
def lnumpy_m_relu(A:np.ndarray,B:np.ndarray,C:np.ndarray):
    Y = np.empty(A.shape,dtype='float32')
    for i in range(128):
        for j in range(128):
            for k in range(128):
                if k == 0:
                    Y[i,j] = 0
                Y[i,j] += A[i,k] * B[k,j]
            C[i,j] = max(Y[i,j],0)

c_np = np.empty((128, 128), dtype=dtype)
lnumpy_m_relu(a_np, b_np, c_np)
np.testing.assert_allclose(c_np, c_mm_relu, rtol=1e-5)

# 使用TVM的TensorIR来实现
# TVM的TensorIR是一种用于表示计算的中间表示,它是一种低级的表示,用于表示计算的数据流和计算的依赖关系
# 它是由TVMScript语言来实现的,这是一种嵌入在Python AST中的DSL,用于表示TVM的中间表示
@tvm.script.ir_module # 修饰器,用于声明MyModule是一个TVM的IRModule,IRModule是在机器学习编译中保存张量函数集合的容器。
class MyModule:
    @T.prim_func # 修饰器,用于声明一个TVM的primitive function
    def mm_relu(
        A:T.Buffer((128, 128), dtype="float32"),
        B:T.Buffer((128, 128), dtype="float32"),
        C:T.Buffer((128, 128), dtype="float32")):
        T.func_attr({"global_symbol": "mm_relu","tir.noalias": True})
        # T.func_attr是一个语法糖,用来声明函数的属性,global_symbol对应函数名,tir.noalias表示所有的缓冲存储器都是不重叠的。
        Y = T.alloc_buffer((128, 128), "float32")
        for i,j,k in T.grid(128,128,128): # T.grid()返回的是一个生成器,可以用for循环来遍历,是TensorIR的一个语法糖
            with T.block("Y"): # T.block()是一个语法糖,用来定义一个block,这个block可以包含多个axis以及围绕这些axis的计算
                vi = T.axis.spatial(128, i) # 定义了一个空间轴,表示i的范围是[0,128),声明了轴的属性spatial,表示这是一个空间轴
                vj = T.axis.spatial(128, j) 
                vk = T.axis.reduce(128, k) # 还有一种属性是reduce,表示这是一个规约轴
                with T.init():
                    Y[vi, vj] = T.float32(0)
                Y[vi, vj] = Y[vi, vj] + A[vi, vk] * B[vk, vj]
        for i,j in T.grid(128,128):
            with T.block("C"):
                vi = T.axis.spatial(128, i)
                vj = T.axis.spatial(128, j)
                C[vi, vj] = T.max(Y[vi, vj], T.float32(0))

# 块设计有助于我们进行机器学习编译分析,我们总是在空间轴上做并行化,但是在规约轴上做并行化需要考虑数据依赖关系

# 每个块轴直接映射到外部循环迭代器的情况下,我们可以使用 T.axis.remap 在一行中声明所有块轴。
@tvm.script.ir_module
class MyModuleWithAxisRemapSugar:
    @T.prim_func
    def mm_relu(
        A:T.Buffer((128, 128), dtype="float32"),
        B:T.Buffer((128, 128), dtype="float32"),
        C:T.Buffer((128, 128), dtype="float32")):
        T.func_attr({"global_symbol": "mm_relu","tir.noalias": True})
        Y = T.alloc_buffer((128, 128), "float32")
        for i,j,k in T.grid(128,128,128):
            with T.block("Y"):
                vi, vj, vk = T.axis.remap("SSR", [i, j, k])
                with T.init():
                    Y[vi, vj] = T.float32(0)
                Y[vi, vj] = Y[vi, vj] + A[vi, vk] * B[vk, vj]

        for i,j in T.grid(128,128):
            with T.block("C"):
                vi, vj = T.axis.remap("SS", [i, j])
                C[vi, vj] = T.max(Y[vi, vj], T.float32(0))


# IRModule可以包含多个张量函数
@tvm.script.ir_module
class MyModuleWithTwoFunctions:
    @T.prim_func
    def mm(A:T.Buffer((128, 128), dtype="float32"),
            B:T.Buffer((128, 128), dtype="float32"),
            Y:T.Buffer((128, 128), dtype="float32")):
        T.func_attr({"global_symbol": "mm","tir.noalias": True})
        for i,j,k in T.grid(128,128,128):
            with T.block("Y"):
                vi,vj,vk = T.axis.remap("SSR",[i,j,k])
                with T.init():
                    Y[vi,vj] = T.float32(0)
                Y[vi,vj] = Y[vi,vj] + A[vi,vk] * B[vk,vj]
        
        
    @T.prim_func
    def relu(A: T.Buffer((128, 128), "float32"),
             B: T.Buffer((128, 128), "float32")):
        T.func_attr({"global_symbol": "relu", "tir.noalias": True})
        for i, j in T.grid(128, 128):
            with T.block("B"):
                vi, vj = T.axis.remap("SS", [i, j])
                B[vi, vj] = T.max(A[vi, vj], T.float32(0))

# 张量函数的变换
# 张量函数的变换是指对张量函数的变换,包括对块轴的变换,对块的变换,对循环迭代器的变换等

def lnumpy_mm_relu_v2(A:np.ndarray,B:np.ndarray,C:np.ndarray):
    Y = np.empty(A.shape,dtype='float32')
    for i in range(128):
        for j0 in range(32):
            for k in range(128):
                for j1 in range(4):
                    j = j0*4+j1
                    if k == 0:
                        Y[i,j] = 0
                    Y[i,j] += A[i,k] * B[k,j]
    for i in range(128):
        for j in range(128):
            C[i,j] = max(Y[i,j],0)

c_np_v2 = np.empty((128, 128), dtype=dtype)
lnumpy_mm_relu_v2(a_np, b_np, c_np_v2)
np.testing.assert_allclose(c_np_v2, c_mm_relu, rtol=1e-5)

# 进行张量函数变换
sch = tvm.tir.Schedule(MyModule)
# 创建一个辅助的Schedule对象,用于对张量函数进行变换

block_Y = sch.get_block("Y", func_name="mm_relu")
i,j,k = sch.get_loops(block_Y)
# 获得块和循环的引用

j0,j1 = sch.split(j, factors=[None,4])
# 将j轴分裂为j0和j1两个轴,j0的范围是[0,32),j1的范围是[0,4)

sch.reorder(i,j0,k,j1)
# 重新排列块轴的顺序

block_C = sch.get_block("C", func_name="mm_relu")
sch.reverse_compute_at(block_C, j0)
# 将C块的计算位置移动到j0轴

sch.decompose_reduction(block_Y, k)
# 将Y的元素初始化和计算分解开

#最后变换后的实现等价于lnumpy_mm_relu_v3
def lnumpy_mm_relu_v3(A: np.ndarray, B: np.ndarray, C: np.ndarray):
    Y = np.empty((128, 128), dtype="float32")
    for i in range(128):
        for j0 in range(32):
            # Y_init
            for j1 in range(4):
                j = j0 * 4 + j1
                Y[i, j] = 0
            # Y_update
            for k in range(128):
                for j1 in range(4):
                    j = j0 * 4 + j1
                    Y[i, j] = Y[i, j] + A[i, k] * B[k, j]
            # C
            for j1 in range(4):
                j = j0 * 4 + j1
                C[i, j] = max(Y[i, j], 0)

c_np = np.empty((128, 128), dtype=dtype)
lnumpy_mm_relu_v3(a_np, b_np, c_np)
np.testing.assert_allclose(c_mm_relu, c_np, rtol=1e-5)

本文是 机器编译课程的学习笔记。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/457691.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

html--宠物

文章目录 htmljscss html <!DOCTYPE html> <html lang"en" > <head><meta charset"UTF-8"><title>CodePen - Spaceworm</title><script> window.requestAnimFrame (function() {return (window.requestAnimat…

简单的思考(一):MATLAB实现心形线

今天刷B站的时候看见了&#xff1a; 于是想着自己能不能也做出来 clc;clear; % 定义x的范围 x -2:0.01:2;% 初始化图形 figure; set(gcf,position,[0,0,800,600],color,w); h1 plot(x,abs(x).^(2/3) (0.9*sqrt((3.3-x.^2))).*sin(0.1*pi*x),r,LineWidth,3); hold on xlim(…

HAProxy——高性能负载均衡器

目录 一.常见的Web集群调度器 二.HAProxy基本介绍 1.HAProxy是什么&#xff1f; 2.HAProxy的特性 3.HAProxy常用的8种负载均衡调度算法 3.1 轮询&#xff1a;RR&#xff08;Round Robin&#xff09; 3.2 最小连接数&#xff1a;LC&#xff08;Least Connections&#xff…

基于JAVA的教务系统小程序的设计与实现【附项目源码】分享

基于JAVA的教务系统小程序的设计与实现: 源码地址&#xff1a;https://download.csdn.net/download/qq_41810183/88842782 一、引言 随着信息技术的不断发展&#xff0c;教务管理工作逐渐走向数字化、智能化。为了提高教务管理效率&#xff0c;方便师生查询教务信息&#xff…

ChatGPT 插件Plugin集合

ChatGPT的插件功能推出一段时间了&#xff0c;陆陆续续的上架了得有200了。 但是其中大部分都不是很好用&#xff0c;并且找起来也复杂。 推荐一个不知名热心人做的导航页。 ChatGPT Plugins Overview 基本上集合了所有的插件&#xff0c;并且还在实时更新中。 需要升级4.0&a…

机器学习 Python库 乱记录

MLFlow—模型实验和跟踪 MLflow是一个平台&#xff0c;帮助你从头到尾管理你的机器学习实验&#xff0c;确保可追溯性和可重复性。它提供了一个集中的存储库&#xff0c;用于存储你的代码、数据和模型工件&#xff0c;以及一个跟踪系统&#xff0c;记录你所有的实验&#xff0c…

前端实现文件预览(pdf、excel、word、图片)

需求&#xff1a;实现一个在线预览pdf、excel、word、图片等文件的功能。 介绍&#xff1a;支持pdf、xlsx、docx、jpg、png、jpeg。 以下使用Vue3代码实现所有功能&#xff0c;建议以下的预览文件标签可以在外层包裹一层弹窗。 图片预览 iframe标签能够将另一个HTML页面嵌入到…

openGauss学习笔记-242 openGauss性能调优-SQL调优-典型SQL调优点-SQL自诊断

文章目录 openGauss学习笔记-242 openGauss性能调优-SQL调优-典型SQL调优点-SQL自诊断242.1 SQL自诊断242.1.1 告警场景242.1.2 规格约束 openGauss学习笔记-242 openGauss性能调优-SQL调优-典型SQL调优点-SQL自诊断 SQL调优是一个不断分析与尝试的过程&#xff1a;试跑Query&…

操作系统(AndroidIOS)图像绘图的基本原理

屏幕显示图像的过程 我们知道&#xff0c;屏幕是由一个个物理显示单元组成&#xff0c;每一个单元我们可以称之为一个物理像素点&#xff0c;而每一个像素点可以发出多种颜色。 而图像&#xff0c;就是在不同的物理像素点上显示不同的颜色构成的。 像素点的颜色 像素的颜色是…

HTML5、CSS3面试题(二)

上一章:HTML5、CSS3面试题&#xff08;一&#xff09; 哪些是块级元素那些是行内元素&#xff0c;各有什么特点 &#xff1f;&#xff08;必会&#xff09; 行内元素: a、span、b、img、strong、input、select、lable、em、button、textarea 、selecting 块级元素&#xff1…

一文解决Word中公式插入问题(全免费/latex公式输入/texsWord)

分文不花&#xff0c;搞定你的word公式输入/texsWord完全使用指南 背景 碎碎念&#xff1a;折折腾腾至少装了几个小时&#xff0c;遇到了若干大坑。遇到的问题网上都搜索不到答案&#xff01;&#xff01;&#xff01;就让我来当指路的小火柴吧。 本篇适用于在word中输入la…

使用Python进行自然语言处理(NLP):NLTK与Spacy的比较【第133篇—NLTK与Spacy】

使用Python进行自然语言处理&#xff08;NLP&#xff09;&#xff1a;NLTK与Spacy的比较 自然语言处理&#xff08;NLP&#xff09;是人工智能领域的一个重要分支&#xff0c;它涉及到计算机如何理解、解释和生成人类语言。在Python中&#xff0c;有许多库可以用于NLP任务&…

【设计模式】二、UML 类图与面向对象设计原则 之 UML概述

二、UML 类图与面向对象设计原则 &#xff08;一&#xff09;UML 类图 UML 概述类与类的UML图示类之间的关系 &#xff08;二&#xff09;面向对象设计原则 单一职责原则&#xff08;Single Responsibility Principle, SRP&#xff09;开闭原则&#xff08;Open-Closed Princip…

x86_64架构栈帧以及帧指针FP

文章目录 一、x86_64架构寄存器简介二、x86_64架构帧指针FP三、示例四、保存帧指针参考资料 一、x86_64架构寄存器简介 在x86架构中&#xff0c;有8个通用寄存器可用&#xff1a;eax、ebx、ecx、edx、ebp、esp、esi和edi。在x86_64&#xff08;x64&#xff09;扩展中&#xff…

基于检索增强的 GPT-3.5 的文本到 SQL 框架,具有样本感知提示和动态修订链。

文章目录 一、论文关键信息二、基础概念三、主要内容1. Motivations2. Insights3. 解决方案的关键4. 实验 四、总结与讨论 &#x1f349; CSDN 叶庭云&#xff1a;https://yetingyun.blog.csdn.net/ 一、论文关键信息 论文标题&#xff1a;Retrieval-augmented GPT-3.5-based T…

港大提出图结构大语言模型:GraphGPT

1. 引言 图神经网络&#xff08;Graph Neural Networks&#xff09;已经成为分析和学习图结构数据的强大框架&#xff0c;推动了社交网络分析、推荐系统和生物网络分析等多个领域的进步。图神经网络的主要优势在于它们能够捕获图数据中固有的结构信息和依赖关系。利用消息传递…

Selenium 自动化 —— 入门和 Hello World 实例

Selenium 是什么 Selenium 是一个用于自动化网页浏览器操作的工具&#xff0c;它支持多种浏览器和多种操作系统。主要用于测试 web 应用程序的功能&#xff0c;也可用于执行一些基本的浏览器操作任务&#xff0c;例如自动化表单填写、网页导航等。 Selenium 是一个开源项目&a…

大米自动化生产线的运作原理与科技创新

在当今科技飞速发展的时代&#xff0c;自动化生产线已经成为各个行业提高效率、降低成本的重要工具。而在粮食产业中&#xff0c;大米的自动化生产线更是以其独特的魅力&#xff0c;引领着粮食加工业的转型升级。星派将带您深入了解大米自动化生产线的运作原理&#xff0c;以及…

Docker 安装部署MySQL教程

前言 Docker安装MySQL镜像以及启动容器&#xff0c;大致都是三步&#xff1a;查询镜像–>拉取镜像–>启动容器 1、查询镜像 docker search mysql2、拉取镜像 拉取镜像时选择stars值较高的 docker pull mysql:5.7 #这里指定拉取对应的版本Mysql5.7&#xff0c;没有指…

【新手】在idea中配置tomcat服务器,并部署一个项目

目录 第一步&#xff1a;新建java项目第二步&#xff1a;新建完成后&#xff0c;按照步骤选择添加框架支持第三步&#xff1a;点击添加配置第四步&#xff1a;在web文件包下创建一个新的jsp第五步&#xff1a;在新建的jsp里编写java或者html 前言&#xff1a;本章学习的是在ide…