TL;DR
- 《俄罗斯方块》这部电影里游戏作者用命令行玩俄罗斯方块原型机太酷了,所以决定自己实现一把
1 | import numpy as np |
遇到的问题
- python 监听字符读入(非 input,input 需要回车结束)好困难,所以该程序 必须用 root 用户下命令行运行…
- python 的超时阻塞式监听更难,
func_timeout
在linux
上运行疑似还有bug
:多线程打开文件但没有关闭,超出OS limit
,在玩十分钟可能才会出现…
1 | import numpy as np |
func_timeout
在 linux
上运行疑似还有 bug
:多线程打开文件但没有关闭,超出 OS limit
,在玩十分钟可能才会出现…decode head
结构和 部分分割专用 backbone
,用于理解语义分割算法的演进过程decode head
模型来源: mmsegmentaion decode head
decode head
是指满足如下要求的网络结构:
backbone / neck
提取的 feature map
或 feature map list
segmentation
结果backbone
输出的 feature map
(例如 PSPNet
输出)backbone
不同阶段 / neck
(例如 FPN
) 输出的不同尺度的 feature map list
feature map
,可以 resize
到输出大小再送入 decode head
,也可以直接送入 decode head
,根据具体算法选择feature map list
,一般有两种做法,根据具体算法选择:
resize concat
: 将所有 feature map
全部 resize
到输出大小后再 concat
(例如 FCN-8s
)multiple select
: 根据 index
在 feature map list
中索引并输出对应的 feature map sub list
feature map / feature map list
转化成与输出 宽高一致 的 feature map
,也是本文具体展开讲的内容1
或 2
,N
分类的语义分割输出通道数为 N
)CNN
结构上创新FCN
: 2014年,出自 UC Berkeley
,分割算法起点PSP
: 2016年,出自商汤,FCN
+ 多尺度ASPP
: 2017年,出自 Google
,PSP
的优雅实现版(DeepLab V2
、DeepLab V3
)FPN
: 2018年,出自 FAIR
,UNet
多尺度的升级版UperNet
: 2018年,出自旷视,PSP
+ FPN
更暴力的多尺度DepthwiseSeparableASPP
: 2018年,出自 Google
,DeepLab V3
结构的小改动(DeepLab V3+
)DepthwiseSeparableFCN
: 2019年,出自东芝 + 剑桥,FCN
的轻量化改造(Fast-SCNN
)PointRend
: 2019年,出自 FAIR
,在其他 decode head
基础上级联了一个 subnetwork
实现了图像分割边缘的细化Self-Attention
(Non-local
/ Channel Attention
)Non-Local
: 2017年,出自 FAIR
,Self Attention
经典PSANet
: 2018年,出自商汤,Non-local
的二维 CCNet
: 2018年,出自地平线,Non-local
的低算力版,使用两个低算力的 Attention
替代 Non-local Attention
DANet
: 2018年,出自京东,两路 Non-local
,一路 attention to postion
一路 attention to channel
EncNet
: 2018年,出自商汤 + Amazon
,优化了 SENet
中的暴力编码方式,在分割任务中额外加入了分类辅助监督EMANet
: 2019年,出自北大,attention to channel
和 attention to postion
可分离的 attention
ANN
: 2019年,出自华中科技大学,简化 Non-local
同时引入 PPM
,极大的降低了 matmul
和 softmax
两类算子的耗时GCNet
: 2019年,出自 MSRA
,简化版 Non-local
+ SENet
的缝合怪OCRNet
: 2019年,出自 MSRA
,级联结构,在其他 decode head
的输出结果上做了 Self-Attention
,并在论文中从 Transformer
角度解释了 Self-Attention
APCNet
: 2019年,出自商汤,复杂网络结构 + 简化矩阵乘实现的 Attention
DMNet
: 2019年,出自商汤,根据输入特征的全局信息动态生成卷积核,本质也是 Attention
LRASPP
: 2019年,出自 Google
,全局 scale
实现的 Attention
(MobileNet V3
)ISANet
: 2019年,出自 MSAR
,使用 feature map shuffle
实现长范围和短范围的稀疏注意力机制DNLNet
: 2020年,出自 MSAR
,改进 Non-local
,加入了归一化和一元分支BiSeNet
: 2019年,出自旷视,在 backbone
之外加入了一个 context branch
,将特征提取和 attention
解耦,降低了 attention
恐怖的计算量BiSeNet V2
: 2020年,出自腾讯,BiSeNet
的改进SDTC
: 2021年,出自美团,BiSeNet
系列的改进版,但由于融合了两路分支到一处,不再 Bilateral
,所以用特征提取 SDTC block
命名…Transformer
SETR
: 2020年,出自腾讯,Vit
做 backbone
+ FCN / FPN decode head
DPT
: 2021年,出自 Intel
,SETR
的升级版,backbone
不变,decode head
更 FPN
了一些Segmenter
: 2021年,出自法国 INRIA
研究所,用了纯 Transformer
架构而不是像 SETR / DPT
一样用 Transformer Encoder + CNN Decoder
架构SegFormer
: 2021年,出自 NVIDIA
,SETR
的高效版KNet
: 2021年,出自商汤,decode head
融合了 Channel Attention + Multi-head Attention + RNN
,统一了语义分割、实例分割、全景分割框架FCN
全称是 Fully Convolutional Networks
resize concat
方式,将多个不同尺度(backbone
不同阶段)的 feature map resize concat
到输出尺寸,如下图所示:实验证明越多尺度融合分割效果越好
Conv
+ 可选择的 concat input
(shortcut
)结构PSP
全称是 Pyramid Scene Parsing
(金字塔场景理解)PSPNet
的原始特征是 backbone
最后一层的输出,所以无需原始特征处理PSPNet
将输入特征通过 Pyramid Pooling Module
结构做了 feature map
不同尺度 down sample
+ up sample
,如下图所示:Atrous Spatial Pyramid Pooling
(空洞空间金字塔池化)DeepLab V3
)DeepLabV3
输入为单个单尺度 feature map
,所以此步骤可省略与
PSPNet
很像,PSPNet
是使用普通Conv
去卷积多种尺度的Pooled feature map
;ASPP
是不改变feature map
而是使用 不同空洞系数的Conv
FPN
全称是 Feature Pyramid Network
,出自 kaiming 大神,可以用在所有和 feature map scale
大小相关的领域UperNet
的全称是 Unified Perceptual Parsing Network
(统一感知解析网络),本身是多任务模型:
objects
语义分割parts
语义分割materials
语义分割textures
语义分割objects
语义分割部分decode head
中内嵌使用 FPN
(而不是以网络 neck
方式使用),所以 feature map list
格式的原始特征无需处理,直接透传到特征解码部分本文只讨论图中蓝色框部分
只需要看蓝色框为输出的通路,算法:
- 在最小尺度
feature map
上使用PPM
(全称Pyramid Pooling Module
,来自于PSPNet
)- 使用
FPN
融合多尺度特征
DeepLab V3
引入的 ASPP
基础上增加了两点改进:
DepthwiseSeparable ASPP
替代 ASPP
,减小计算量vanilla FPN
结构,避免了 DeepLab V3
直接上采样 8 倍预测的问题DeepLab V3+
)相较于
DeepLab V3
在 8 倍下采样的feature map
上使用 ASPP,DeepLab V3+
在更小尺度(16 倍下采样)feature map
上使用DepthwiseSeparable ASPP
同时为了解决小尺度预测的问题,加入了一个vanilla FPN
做不同尺度特征融合
FCN
的轻量化实现,使用 DWConv
(Depthwise Conv
) 和 DSConv
(Depthwise Separable Conv
) 替换 FCN
中的普通 Conv
Fast-SCNN
)图中的
DWConv
是指Depthwise Conv
(ic == oc == group
)
图中的DSConv
是指Depthwise Separable Conv
,DSConv
不是一个Conv
而是Depthwise Conv
和Pointwise Conv
(kernel_size == 1 and group == 1
) 以及激活函数 /BN
一起组成的一个block
PointRend
全称是 point-base rendering
(基于点的渲染算法),是一个级联分割算法,实例分割和语义分割都可使用,依赖于一个其他完整的 decode head
(例如 FCN
)的输出,该算法提出了一个 subnetwork
,该结构只关心目标边界点的分割,可预测更准确更 sharp
的目标边界
- 渲染:渲染(
render
)是指在电脑中使用三维制作软件将制作的模型经过纹理、绑定、动画、灯光处理后得到模型和动画的图像。三维渲染是使用计算机从数字三维场景中生成二维影像的过程- 细分表面算法:细分表面算法(
subdivision surface algorithm
)在3D计算机图形中使用,通过递归完善基本级多边形网格来创建弯曲表面
Subdivision render
思想用于分割,使用 coarse-to-fine
思想,逐级细分,提高分割效果Inference
过程(以 FCN
作为 prev_decode_head
为例):
backbone
的输出 x
,shape = [batch, channels, height, width]
FCN
的输出 prev_output
,shape = [batch, num_cls, height, width]
refine
后的输出,shape = [batch, num_cls, 2 * subdivision_steps * height, 2 * subdivision_steps * width]
prev_output copy
一份作为 refined_seg_logits
refined_seg_logits
插值放大两倍,shape = [batch, num_cls, 2 * height, 2 * width]
refined_seg_logits
上挑选最 hard
的 N
个点(hard
的定义是:如果一个像素的 top1_cls_logitis
和 top2_cls_logits
越接近,则该点越 hard
),输出相对坐标,shape = [batch, N, 2]
N
个点的坐标在 x
中找到对应的点(需要插值找出),作为 fine_grained_point_feats
,shape = [batch, channels, N]
N
个点的坐标在 prev_output
中找到对应的点(需要插值找出),作为 coarse_point_feats
,shape = [batch, num_cls, N]
fine_grained_point_feats
和 coarse_point_feats
concat
后经过 Subnetwork
(几层 MLP
)映射到类别空间 point_logits
,shape = [batch, num_cls, N]
3
中的 point index
,将 6
输出的 point_logits
替换到 1
中的 refined_seg_logits
对应位置2 ~ 7 subdivision_steps
次,输出最终的 refined_seg_logits
,shape = [batch, num_cls, 2 * subdivision_steps * height, 2 * subdivision_steps * width]
Train
过程:
backbone
的输出 x
,shape = [batch, channels, height, width]
FCN
的输出 prev_output
,shape = [batch, num_cls, height, width]
gt_semantic_seg
,shape = [batch, num_cls, height, width]
loss
Train
过程与 Inference
过程基本相同,区别在于:
topk
运算对梯度反向传播不友好,所以在 Train
的过程中使用随机采样点的策略,没有挖掘 hard case
Train
不会引入多尺度,只会在同一尺度学习 subnetwork
对 point
的分类用于
2
维图像,所以T == 1
,通过增加(HW, HW)
的特征相关性矩阵给特征带来全局相关性(Attention
)
decode head
前后处理和 FCN
一致PSA
的全称是 Point-wise Spatial Attention
借鉴于
Non-local
,强行给了比较牵强的数学解释,推理过程复杂到需要调用CUDA
而不是使用pure pytorch
CC
的全称是 Criss-Cross Attention
(十字交叉注意力机制)使用两个十字交叉注意力模块的串联替代
Non-local
,降低算力
整体流程平平无奇
decode head
前后处理和 FCN
一致DANet
全称是 Dual Attention Network
(双路 Attention
网络)
Attention
,照搬 Non-local
Attention
,通道维度 Non-local
EncNet
的全称是 Context Encoding Network
(上下文编码网络),做法是对网络中间层 feature map
编码到分类空间,加入了分类 Loss
监督对于
SE-loss
: 监督图中包含哪些类别的像素,使用交叉熵实现
对于Encode
:
- 从本质上看:
- 上图使用的
Encode
和SENet
(Squeeze and Excitation Network
) 对feature map per channel
编码没有区别- 从实现层面看:
Encode
使用了更在数学上更好解释的编码方式(而不是SENet
粗暴的Global Average Pooling
编码方式)Encode
编码空间比SENet
更大(SENet 每个通道使用 R 空间编码,Encode
每个通道使用 Rd 空间编码)
EMA
的全称是 Expectation-Maximization Attention
(最大期望注意力),从数学角度解释了 Attention
,实现上也是通过多个矩阵乘实现的 channel
与 position
分离的 Attention
ANN
全称是 Asymmetric Non-local
(非对称 Non-local
), 简化 Non-local
同时引入 PPM
,极大的降低了 matmul
和 softmax
两类算子的耗时在
key
/value
上对特征进行了降维N -> S
,由下图可知,上图的sample
方法具体是指PPM
(Pyramid Pooling Module
)
AFNB
全称是Asymmetric Fusion Non-local Block
APNB
全称是Asymmetric Pyramid Non-local Block
二者对Non-local
的Self-Attention
进行简化,例如share key value
GCNet
的全称是 Global Context Network
,作者认为 Non-local
对全局信息把握的不够好,本文是简化版 Non-local
+ SENet
的缝合怪,Non-local
的 Spatial Attention
和 SENet
的 Channel Attention
结合
Non-local
结构的化简
作者认为一个全局上下文建模结构如图 (a) 所示
图 (b) 为简化后的Non-local
结构
图 © 是SENet
结构
图 (d) 是本文提出的GC
结构
decode head
前后处理和 FCN
一致OCR
的全称是 Object Contextual Representations
(目标上下文表征)Optical Character Recognition
(光学字符识别)OCRNet
是一种 Cascade Encoder Decoder
结构的 decode head
,该算法依赖于其他算法输出的分割结果,如下图所示(OCRNet
依赖于 FCN
的输出):上图中粉红色的部分即为
OCRNet decode head
论文中给出的算法架构图,给中间结果赋予了可解释的含义
APCNet
的全称是 Adaptive Pyramid Context Network
(自适应金字塔上下文),该算法引入了 Adaptive Context Modules(ACM)
(自适应上下文模块),本质就是通过矩阵乘实现全局 Attention
DMNet
的全称是 Dynamic Multi-scale Filters Network
,本文根据输入特征动态获得多种尺度的卷积核参数,本质也是一种全局 Attention
机制之前的网络结构都是通过空洞卷积或大卷积核实现多尺度
DMNet
通过输入特征的Adaptive Pooling
生成动态卷积核实现多尺度
LRASPP
全称是 Lite Reduced Atrous Spatial Pyramid Pooling
(轻量简化空洞空间金字塔池化),是在 MobileNet V3
论文中提出的结构,是和 MobileNet V2
提出的 RASPP
结构对比,更轻量效果更好;从实现上看 LRASPP
并没有空洞卷积和空间金字塔池化…,而是通过全局 scale
实现的 Attention
MobileNet V3
)ISANet
的全称是 Interlaced Sparse Attention Network
(交错稀疏注意力网络),通过 feature map shuffle
实现长范围和短范围的稀疏注意力。
- 利用
feature map
重排实现长范围或短范围的稀疏注意力。
DNL
的全称是 Disentangled Non-Local
(分离 Non-local
),对原始 Non-local
做了改进,参数量和计算量更高,效果更好
DNL
结构(图 d)在原始Non-local
结构(图 a)上做了如下改动:
- 加入了一元
Non-local
分支Unary Non-local
- 在二元分支矩阵乘之前加入了白化操作(
H*W
维度减均值,相当于instance norm
)
由于减了均值,所以二元分支上 “+” 这一点在Attention map
∈RHW×HW 上的索引heat map
∈RH×W 变干净很多(相当只学习残差)
这张图也从侧面反映了Non-local
还是很强的,Attention
不是在讲故事
BiSeNet
的全称是 Bilateral Segmentation Network
(双边分割网络),是一个分割专用的神经网络(包括专用 backbone
和 decode head
)
backbone
主要分成两个分支spatial path
和context path
,本质就是在基础backbone
的基础上加入了一个计算量(通道数)非常小的attention branch
增加上下文信息,最后融合两通道特征送入decode head
decode head
就是基础的FCN
BiSeNet
的改进版对
BiSeNet
主要改进有:
context branch
上增加了更多更复杂的模块,可更好收集上下文信息context branch
上增加了更多监督,每个尺度上都有监督损失- 分支融合模块设计的更加复杂
SDTC
的全称是 Short-Term Dense Concatenate network
,在 BiSeNet
系列的基础上将 context branch
变成训练时的监督(或者说融合两路信息到一路上)很新颖的 Loss 设计,效果和计算量都优于
BiSeNet
系列
这就是SDTC
模块
SETR
的全称是 Segmentation Transformer
SETR_PUP_decode_head
: https://github.com/open-mmlab/mmsegmentation/blob/master/mmseg/models/decode_heads/setr_up_head.pySETR_MLA_decode_head
: https://github.com/open-mmlab/mmsegmentation/blob/master/mmseg/models/decode_heads/setr_mla_head.py
- 本质是
Vit(vision transformer)
做backbone
,FCN
/ 类似FPN
做decode head
的分割算法- 为了缩减计算量,
Vit
会将原图剪成多个patch
(worth 16x16 words...
),每个patch
单独输入到 24 层Transformer Encoder
中,每个patch
内部独立做全局attention
- 剪
patch
带来的问题是:与其他CNN backbone + decode head
结构不同,Transformer backbone + decode head
结构中decode head
需要顺序inference
每个patch feature
(注意图aDecoder
输入为多个patch feature
),最后拼回到整张图大小SETR_UPU decode head
==sequence FCN
SETR_MLA decode head
==sequence FPN
(Attention
不改变输入宽高,所以不存在严格意义上的 多尺度,只是不同网络深度的特征)
DPTNet
的全称是 Dense Prediction Transformer Network
,本质和 SETR
一样都是使用 Vit
做 backbone
- 本质和
SETR
一样都是使用Vit
做backbone
- 和
SETR
不同的地方在于:
- 不同
backbone
深度特征融合方式更复杂,更接近FPN
decode head
不再是输入sequence patch feature
,而是输入融合后的全图feature
Segmenter
全称是 Segmentation Transformer
,用了纯 Transformer
架构而不是 Transformer Encoder + CNN Decoder
架构
- 用了纯
Transformer
架构(Transformer Encoder + Decoder
),SETR
和DPT
都是Transformer Encoder + CNN Decoder
SegFormer
全称也是 Segmentation Transformer
…,是 NVIDIA
对 SETR
的高效实现版,backbone
和 decoder head
都进行了轻量化升级
backbone
不再是标准Transformer Encoder(Vit)
,而是改成了更轻量化的MixVisionTransformer(Mit)
Mit
使用了更大的patch
且patch
之间存在overlap
Mit
使用了coarse-to-fine
的特征表示,随着Transformer Encoder
变深feature map
宽高变小Mit
使用了更简单的Self-Attention
公式Mit
去掉了position embeding
,使用了Mix-FFN
decode head
使用了纯MLP
,且很自然的融合了多尺度(真.多尺度)
KNet
的全称是 Kernel Network
,是一种跳出语义分割、实例分割、全景分割原有框架的一种新分割范式,用一组 kernel
去预测一个分割 mask
,最多预测 num_proposals
个(类似 DETR
的策略),训练时用最优匹配的方法计算损失函数;优点是在框架上统一了所有分割任务(语义分割、实例分割、全景分割),缺点 decode head
就是实现复杂,融合了 Channel Attention + Multi-head Attention + RNN
从框架上统一了三种分割方式
红字标出的是每个张量的shape
绿字标出的是每个计算过程实际是在做什么
上述过程会像RNN
一样循环多次去更新kernel
,使得结果更好(重复使用backbone
的输出)
Z∈Rd×m ,其中 d 是表征向量的长度,m 是一个 batch
的大小,典型值是 d=128, m=1000 。
det 表示行列式的值, logdet 表示行列式的值的自然对数。
Πj 表示选择函数,选择属于类 j 的特征向量进行计算。
logdet
有定义。
shared memory
速度几乎和 L1 cache 一样,比local memory
和global memory
都快的多(在物理上,local memory
和global memory
是同一块DRAM
)
GPU
进行编程时,需要创建一组进程块 (thread blocks
),每个 thread
映射到单个核心,而 block
映射到流式多处理器 (SM
),如下图所示:threadIdx
和 blockIdx
索引,在实际应用中,可以有多维线程索引block
中的多个 thread
可共享内存,因此可以重排同一个 block
中的 thread
数据,使得尽可能少的数据缓存到 shared memory
中thread
需要计算输出矩阵中 8 * 8
的数据,需要从 local memory
中读取 8 * 8 * 1024 * 2
数据block
中的 thread
之间没有数据共享,所以需要从 local memory
中读取 8∗8∗8∗8∗1024∗2=223 个矩阵元素block
计算输出矩阵的 64 * 64
的数据最少需要 64∗1024∗2=217 的数据,可提前将这部分数据缓存到 shared memory
thread
从 shared memory
读数据计算,需读取 64∗1024∗2=217 个数据block
读取数据对比:
local memory
读取 223 个矩阵元素local memory
读取 217 个矩阵元素到 shared memory
,再从 shared memory
读取 217 个数据计算TVM
是什么:是 Tensor Virtual Machine
的缩写,是一个 Open Deep Learning Compiler Stack(深度学习开源编译栈)TVM
想干什么:将机器学习算法从开发阶段形态,通过变换和优化算法,使其变成部署形态TVM
的原则:
TVM Module
层次结构:
IRModule
:包含一个或多个 元张量函数 和一个 高层神经网络执行的抽象。通常用 @tvm.script.ir_module
装饰器装饰tensorIR
: 元张量函数。通常表示一个算子实例的计算过程,包含多个 计算块。通常用 @T.prim_func
装饰器装饰IRModule
的程序入口。通常用 @R.function
block
: 计算块。张量的基本计算单位,通常包含多个 计算轴 上的循环。通常用 with T.block(block_name)
来标明作用域计算轴
:
spatial axis
):空间轴上循环的每个位置的计算独立于其他位置reduce axis
):规约轴上的位置不会反映到最后的计算输出上TVM Module
变换过程:
TVM Module
执行过程: 1 | ex = relax.vm.build(MyModule, target="llvm") |
GPU
包含一组流处理器 (stream multi-processors
, SM
),每个流处理器都有许多核心,硬件实现上这些核心之间可共享内存(shared memory
)Grid
/ Block
/ Thread
三级概念,逻辑模型与物理的对应关系如下图所示:因此:同一个
Block
中的Thread
可共享shared memory
shared memory
速度几乎和 L1 cache 一样,比local memory
和global memory
都快的多(在物理上,local memory
和global memory
是同一块DRAM
)
GPU
进行编程时,需要创建一组进程块 (thread blocks
),每个 thread
映射到单个核心,而 block
映射到流式多处理器 (SM
),如下图所示:threadIdx
和 blockIdx
索引,在实际应用中,可以有多维线程索引1 | @tvm.script.ir_module |
1 | sch = tvm.tir.Schedule(MyModuleVecAdd) |
threadIdx.x
和 blockIdx.x
1 | sch.bind(i0, "blockIdx.x") |
1 | @tvm.script.ir_module |
Element-wise Add
不存在数据依赖,所以可以直接拆分到多个 block
中的多个 thread
中,一个 cycle
全部算完1 | @tvm.script.ir_module |
block
和 thread
1 | sch = tvm.tir.Schedule(MyModuleWindowSum) |
IRModule
1 | @tvm.script.ir_module |
1 | A_shared = sch.cache_read(block_C, read_buffer_index=0, storage_scope="shared") |
IRModule
1 | @tvm.script.ir_module |
cooperative fetching
1 | ax = sch.get_loops(A_shared)[-1] |
IRModule
1 | @tvm.script.ir_module |
IRModule
基础实现: 1 | @tvm.script.ir_module |
block
和 thread
+ 本地存储分块优化循环拆分,来增加整体内存复用,只需要从
A
和B
加载一次条形数据(上图中的灰色部分),然后使用它们来计算矩阵乘法结果
下面代码中设置V = 8
1 | def blocking(sch, |
IRModule
1 | @tvm.script.ir_module |
与上图不同,图中矩阵 C 中
L * L
灰色区域表示一个block
的计算输出
每个L * L
灰色区域由多个V * V
的小区域组成,表示一个thread
的输出
同一个 block
中的多个 thread
可共享内存,因此可以重排同一个 block
中的 thread
数据,使得尽可能少的数据缓存到 shared memory
中
优化前:
thread
需要计算输出矩阵中 8 * 8
的数据,需要从 local memory
中读取 8 * 8 * 1024 * 2
数据block
中的 thread
之间没有数据共享,所以需要从 local memory
中读取 8∗8∗8∗8∗1024∗2=223 个矩阵元素优化后:
block
计算输出矩阵的 64 * 64
的数据最少需要 64∗1024∗2=217 的数据,可提前将这部分数据缓存到 shared memory
thread
从 shared memory
读数据计算,需读取 64∗1024∗2=217 个数据内存优化前后每个 block
读取数据对比:
local memory
读取 223 个矩阵元素local memory
读取 217 个矩阵元素到 shared memory
,再从 shared memory
读取 217 个数据计算优化过程:
1 | def cache_read_and_coop_fetch(sch, block, nthread, read_idx, read_loc): |
IRModule
1 | @tvm.script.ir_module |
1 | from tvm import meta_schedule as ms |
Builder
创建 IRModule
TensorIR
(主张量函数)1 | from tvm import te |
1 | # from tvm.script import tir as T |
BlockBuilder
构造 IRModule
1 | A = relax.Var("A", (128, 128), relax.DynTensorType(2, "float32")) |
1 | tvm.script.ir_module |
BlockBuilder
创建 IRModule
与直接创建 IRMoudle
的对比bb.emit_te
做了以下事情:
A
和 B
创建一个输入 te.placeholder
te_matmul
函数运行它们te.create_prim_func
来创建一个 TensorIR
函数call_tir
生成对函数的调用Pytorch
映射到 IRModule
Pytorch
模型1 | class MyModel(nn.Module): |
1 | # pytorch module parameter to IRModule parameter |
Pytorch Module
到 TensorIR
1 | # TensorIR 映射变换 |
IRModule
1 | @tvm.script.ir_module |
Pytorch Module
到 IRModule
更高层的算子1 | def map_nn_relu_op(bb, node_map, node, nn_mod): |
1 | @tvm.script.ir_module |
TensorIR
函数BlockBuilder
API 通过 emit_te
和其他函数创建 IRModule
IRModule
,实现与现有的机器学习框架的整合MLC
的本质是张量函数之间的转换,但我们不知道哪种转换是让模型运行更快的,所以需要使用自动程序优化,去自动搜索最有转换。1 | # IR_Module 使用自定义主张量函数和库张量函数 |
1 | # 调优 API 只接受一个带有一个 main 函数的 IRModule,所以需要将原始 IRModule 中的 linear0 转成新 IRModule 的 main 函数 |
MLC
的本质:张量函数之间的转换MLC
的关注点:
IR_Module
1 | @tvm.script.ir_module |
@tvm.script.ir_module
装饰 IR_Module
@T.prim_func
装饰 主张量函数@R.function
装饰 高层神经网络执行的抽象,(将整个 IR_Module
中的主张量函数串起来组成一个计算图)R.dataflow()
用于标记程序计算图区域R.call_tir(prim_func, inputs, output_shape, dtype)
分配输出内存并和输入一起输入主张量函数1 | ex = relax.vm.build(MyModule, target="llvm") |
1 | @tvm.script.ir_module |
env.linear
是库张量函数,同一个 IR_Module
中可使用库张量函数,也可使用自定义张量函数,也可以二者混用。1 | @tvm.register_func("env.linear", override=True) |
@tvm.register_func
,注册库张量函数IR_Module
都是在调用时才传入 数据 和 权重参数,但 权重参数 是不变的,所以可以将 权重参数 提前绑定到 IR_Module
中,调用时只传入输入数据。1 | MyModuleWithParams = relax.transform.BindParams("main", nd_params)(MyModuleMixture) |
relax.transform.BindParams(计算图入口函数,模型参数)(IR_Module)
将模型参数绑定到 IR_Module
的计算图入口函数中,返回一个绑定好模型参数的 IR_Module
。main
函数) 有助于将元张量函数拼接在一起以进行端到端执行。Relax
抽象的关键要素包括
call_tir
构造,将目标传递规范的元函数嵌入到计算图中Dataflow block
TensorIR
函数。x
轴正方向,纵轴(高度方向)向下为 y
轴正方向,单位是像素z
轴正方向,垂直于 z
轴且平行于相机平面,水平向右为 x
轴正方向,竖直向下为 y
轴正方向,单位是米x
, y
, z
轴两两垂直的坐标系,单位是米相机内参可以实现像素坐标系与相机坐标系之间相互转换,通常使用一个 3 * 3 矩阵表示
根据小孔成像和相似三角形原理,可以得出相机坐标系与成像坐标系点的对应关系:fZ=xX=yY
其中:(X,Y,Z) 为相机坐标系下的点的坐标, (x,y) 为投影到成像平面上的点的坐标, f 表示焦距。
再根据成像坐标系到像素坐标系的对应关系:
$
\begin{matrix}
u=\alpha \cdot x + c_x \
v = \beta \cdot y + c_y
\end{matrix}
$
其中:
所以,相机坐标系 (X,Y,Z) 与像素坐标系 (u,v) 可通过相机内参相互转换:
$
Z \begin{bmatrix} u \ v \ 1 \end{bmatrix} = \begin{bmatrix} f_x & 0 & c_x \ 0 & f_y & c_y \ 0 & 0 & 1 \end{bmatrix} \begin{bmatrix}X\ Y\ Z \end{bmatrix}
$
其中: fx=α⋅f, fy=β⋅f, Z 表示相机坐标系下的深度
⎣⎢⎡fx000fy0cxcy1⎦⎥⎤ 被称为相机内参 K
相机外参可以实现相机坐标系与世界坐标系之间相互转换(刚体变换),通常用一个 3 * 3 的旋转矩阵 R 和一个 3 * 1 的平移矩阵 T 表示:
$
\begin{bmatrix} X_c\ Y_c \ Z_c \end{bmatrix}=\begin{bmatrix} R_{11} & R_{12} & R_{13} \ R_{21} & R_{22} & R_{23} \ R_{31} & R_{32} & R_{33} \end{bmatrix} \begin{bmatrix} X_w\ Y_w \ Z_w \end{bmatrix} + \begin{bmatrix} T_{1} \ T_{2} \ T_{3} \end{bmatrix}
$
其中: (Xc,Yc,Zc) 表示相机坐标系下的点, (Xw,Yw,Zw) 表示世界坐标系下的点
齐次化之后,得到一个 4 * 4
的矩阵:
$
\begin{bmatrix} X_c\ Y_c \ Z_c \ 1\end{bmatrix}=\begin{bmatrix} R_{11} & R_{12} & R_{13} & T_1 \ R_{21} & R_{22} & R_{23} & T_2 \ R_{31} & R_{32} & R_{33} & T_3 \ 0 & 0 & 0 & 1\end{bmatrix} \begin{bmatrix} X_w\ Y_w \ Z_w \ 1\end{bmatrix}
$