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 headdecode head 是指满足如下要求的网络结构:
backbone / neck 提取的 feature map 或 feature map listsegmentation 结果backbone 输出的 feature map(例如 PSPNet 输出)backbone 不同阶段 / neck (例如 FPN) 输出的不同尺度的 feature map listfeature 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 listfeature 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 AttentionDANet: 2018年,出自京东,两路 Non-local,一路 attention to postion 一路 attention to channelEncNet: 2018年,出自商汤 + Amazon,优化了 SENet 中的暴力编码方式,在分割任务中额外加入了分类辅助监督EMANet: 2019年,出自北大,attention to channel 和 attention to postion 可分离的 attentionANN: 2019年,出自华中科技大学,简化 Non-local 同时引入 PPM,极大的降低了 matmul 和 softmax 两类算子的耗时GCNet: 2019年,出自 MSRA,简化版 Non-local + SENet 的缝合怪OCRNet: 2019年,出自 MSRA,级联结构,在其他 decode head 的输出结果上做了 Self-Attention,并在论文中从 Transformer 角度解释了 Self-AttentionAPCNet: 2019年,出自商汤,复杂网络结构 + 简化矩阵乘实现的 AttentionDMNet: 2019年,出自商汤,根据输入特征的全局信息动态生成卷积核,本质也是 AttentionLRASPP: 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 命名…TransformerSETR: 2020年,出自腾讯,Vit 做 backbone + FCN / FPN decode headDPT: 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 Networksresize 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 中的普通 ConvFast-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_logitsrefined_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]lossTrain 过程与 Inference 过程基本相同,区别在于:
topk 运算对梯度反向传播不友好,所以在 Train 的过程中使用随机采样点的策略,没有挖掘 hard caseTrain 不会引入多尺度,只会在同一尺度学习 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-localAttention,通道维度 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 实现的 AttentionMobileNet 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 headdecode head就是基础的FCN
BiSeNet 的改进版
对
BiSeNet主要改进有:
context branch上增加了更多更复杂的模块,可更好收集上下文信息context branch上增加了更多监督,每个尺度上都有监督损失- 分支融合模块设计的更加复杂
SDTC 的全称是 Short-Term Dense Concatenate network,在 BiSeNet 系列的基础上将 context branch 变成训练时的监督(或者说融合两路信息到一路上)很新颖的 Loss 设计,效果和计算量都优于
BiSeNet系列
这就是SDTC模块
SETR 的全称是 Segmentation TransformerSETR_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 FCNSETR_MLA decode head==sequence FPN(Attention不改变输入宽高,所以不存在严格意义上的 多尺度,只是不同网络深度的特征)
DPTNet 的全称是 Dense Prediction Transformer Network,本质和 SETR 一样都是使用 Vit 做 backbone
- 本质和
SETR一样都是使用Vit做backbone- 和
SETR不同的地方在于:
- 不同
backbone深度特征融合方式更复杂,更接近FPNdecode 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之间存在overlapMit使用了coarse-to-fine的特征表示,随着Transformer Encoder变深feature map宽高变小Mit使用了更简单的Self-Attention公式Mit去掉了position embeding,使用了Mix-FFNdecode 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 有定义。
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 索引,在实际应用中,可以有多维线程索引
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 memorythread 从 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.functionblock: 计算块。张量的基本计算单位,通常包含多个 计算轴 上的循环。通常用 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 fetching1 | 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 memorythread 从 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): |
IRModule1 | @tvm.script.ir_module |
1 | from tvm import meta_schedule as ms |
Builder 创建 IRModuleTensorIR(主张量函数)1 | from tvm import te |
1 | # from tvm.script import tir as T |
BlockBuilder 构造 IRModule1 | A = relax.Var("A", (128, 128), relax.DynTensorType(2, "float32")) |
1 | tvm.script.ir_module |
BlockBuilder 创建 IRModule 与直接创建 IRMoudle 的对比
bb.emit_te 做了以下事情:
A 和 B 创建一个输入 te.placeholderte_matmul 函数运行它们te.create_prim_func 来创建一个 TensorIR 函数call_tir 生成对函数的调用Pytorch 映射到 IRModulePytorch 模型1 | class MyModel(nn.Module): |
1 | # pytorch module parameter to IRModule parameter |
Pytorch Module 到 TensorIR1 | # TensorIR 映射变换 |
IRModule1 | @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 和其他函数创建 IRModuleIRModule,实现与现有的机器学习框架的整合MLC 的本质是张量函数之间的转换,但我们不知道哪种转换是让模型运行更快的,所以需要使用自动程序优化,去自动搜索最有转换。1 | # IR_Module 使用自定义主张量函数和库张量函数 |
1 | # 调优 API 只接受一个带有一个 main 函数的 IRModule,所以需要将原始 IRModule 中的 linear0 转成新 IRModule 的 main 函数 |
MLC 的本质:张量函数之间的转换MLC 的关注点:
IR_Module1 | @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 blockTensorIR 函数。x 轴正方向,纵轴(高度方向)向下为 y 轴正方向,单位是像素z 轴正方向,垂直于 z 轴且平行于相机平面,水平向右为 x 轴正方向,竖直向下为 y 轴正方向,单位是米x, y, z 轴两两垂直的坐标系,单位是米相机内参可以实现像素坐标系与相机坐标系之间相互转换,通常使用一个 3 * 3 矩阵表示


根据小孔成像和相似三角形原理,可以得出相机坐标系与成像坐标系点的对应关系:fZ=xX=yY
其中:(X,Y,Z) 为相机坐标系下的点的坐标, (x,y) 为投影到成像平面上的点的坐标, f 表示焦距。
再根据成像坐标系到像素坐标系的对应关系:
u=α⋅x+cxv=β⋅y+cy
其中:
所以,相机坐标系 (X,Y,Z) 与像素坐标系 (u,v) 可通过相机内参相互转换:
Z⎣⎢⎡uv1⎦⎥⎤=⎣⎢⎡fx000fy0cxcy1⎦⎥⎤⎣⎢⎡XYZ⎦⎥⎤
其中: fx=α⋅f, fy=β⋅f, Z 表示相机坐标系下的深度
⎣⎢⎡fx000fy0cxcy1⎦⎥⎤ 被称为相机内参 K
相机外参可以实现相机坐标系与世界坐标系之间相互转换(刚体变换),通常用一个 3 * 3 的旋转矩阵 R 和一个 3 * 1 的平移矩阵 T 表示:
⎣⎢⎡XcYcZc⎦⎥⎤=⎣⎢⎡R11R21R31R12R22R32R13R23R33⎦⎥⎤⎣⎢⎡XwYwZw⎦⎥⎤+⎣⎢⎡T1T2T3⎦⎥⎤
其中: (Xc,Yc,Zc) 表示相机坐标系下的点, (Xw,Yw,Zw) 表示世界坐标系下的点
齐次化之后,得到一个 4 * 4 的矩阵:
⎣⎢⎢⎢⎡XcYcZc1⎦⎥⎥⎥⎤=⎣⎢⎢⎢⎡R11R21R310R12R22R320R13R23R330T1T2T31⎦⎥⎥⎥⎤⎣⎢⎢⎢⎡XwYwZw1⎦⎥⎥⎥⎤