图是“拓扑 + 类型 + 形状“的世界

概述

图是"拓扑 + 类型 + 形状"的世界

这句话是理解 AI 编译器"图视角"的核心纲领。

它并不是在说图里没有"数值",而是在强调:

  • 在图编译器(Graph Compiler)这个层面,它完全无视具体的数据值(比如像素是 0.5 还是 0.8)
  • 只关心描述数据的"元数据"和描述计算的"骨架"。

通俗比喻

假设图视角是一家 中央厨房的调度 中心,它负责指挥整个烹饪流程。

  • 拓扑 = 菜谱步骤的依赖关系(必须先洗菜,才能切菜;必须先切菜,才能炒菜)。调度中心不看菜切得漂不漂亮,只看"谁依赖谁"。

  • 类型 = 食材的品种分类(这是肉、这是蔬菜、这是调料)。调度中心不关心这块肉是猪肉还是牛肉(具体值),但必须知道是肉(要用肉刀)还是菜(要用菜刀)。

  • 形状 = 食材的分量和容器大小(这是 1 斤肉装在大盆里,那是 2 两葱花装在小碟里)。调度中心根据这个决定用什么锅炒、炒多久。

调度中心绝对不关心 "这块肉上的花纹长什么样"、"这棵白菜重多少克"(具体数值),它只关注数据(食材)的流向、类型和尺寸,以此来决定如何最高效地安排流水线。


例子:图编译器如何"看"代码

假设我们有一个简单的 PyTorch 模型片段:Conv2d -> BatchNorm -> ReLU。

1. 图编译器"看到"的抽象表示

图编译器不关心输入图片是猫还是狗,它只构建如下 DAG:

text 复制代码
输入 (placeholder)
  │  形状: [1, 3, 224, 224]  (类型: fp32)
  ▼
Conv2d 节点
  │  权重形状: [64, 3, 7, 7] (类型: fp32)
  │  输出形状: [1, 64, 112, 112] (类型: fp32)  ← 中间张量 A
  ▼
BatchNorm 节点
  │  参数: γ, β (类型: fp32)
  │  输出形状: [1, 64, 112, 112] (类型: fp32)  ← 中间张量 B
  ▼
ReLU 节点
  │  输出形状: [1, 64, 112, 112] (类型: fp32)  ← 最终输出
  ▼

2. 拆解"拓扑 + 类型 + 形状"如何驱动优化

① 拓扑(依赖关系)告诉编译器"能不能融"

图编译器看拓扑:ReLU 只依赖 BatchNorm,BatchNorm 只依赖 Conv,且它们是一对一的线性关系(没有别的节点使用 中间张量 A)。

这三个节点可以融合(Fusion)!因为中间结果只有这一个"消费者",不需要存回内存给别人用。

② 形状(大小维度)告诉编译器"值不值得融"

图编译器看形状:中间张量 A 的大小是 1, 64, 112, 112,即 1 * 64 * 112 * 112 = 802,816 个元素(约 3.2 MB)。

如果不融合,这 3.2 MB 要写回 DRAM(主存),再读出来给 BN,成本极高(访存墙)。由于形状巨大,融合收益极高,必须融。

③ 类型(数据精度)告诉编译器"用什么指令融"

图编译器看类型:三个节点的数据类型都是 fp32(单精度浮点)。

既然类型一致,融合后的 Kernel 内部统一使用 SIMD 浮点向量指令(如 AVX2 的 vmulps)。如果类型是 int8,则会触发生成专用的低精度矩阵乘指令(如 VPMADDUBSW)。

对比:如果"类型"或"形状"变了,图编译器的决策会截然不同

场景 拓扑(依赖) 类型 形状 图编译器(调度中心)的决策变化
原始场景 Conv → BN → ReLU(链式) fp32 1,64,112,112(大) 融合:节省大量 DRAM 流量。
场景 A Conv → BN → ReLU(链式) fp32 1,64,7,7(小) 不融合或不强求:中间张量只有 12 KB,访存成本低,融合带来的指令调度开销可能大于收益。
场景 B Conv → BN → 输出给 ReLU 和 另一个分支) fp32 1,64,112,112(大) 不融合:拓扑显示中间张量有两个消费者,如果融合进 ReLU,另一个分支就拿不到数据了,必须存回内存。
场景 C Conv → BN → ReLU(链式) 中间转为 int8 1,64,112,112(大) 强行融合但改变内核:为了省访存依然融合,但内部循环会插入量化(Quantization)指令,将 fp32 中间结果截断为 int8。

总结

一批具有固定类型(Type)和固定尺寸(Shape)的数据包(Tensor),按照固定的流向(Topology)流经一堆黑盒操作(Ops)

至于数据包里面的具体数值(比如像素 RGB 是 128 还是 255)------那是 Kernel 层(运行时循环)要处理的具体数值,图编译器完全不看,也不需要看。

在编译期完成 80% 的全局优化决策(内存分配、算子融合、布局转换)。

这,就是"图是拓扑 + 类型 + 形状的世界"的真正含义。

相关推荐
小静AI工程实验室4 小时前
RSA 算法详解:从模幂原理到 OAEP 加密、PSS 签名与 Python 实验
python·算法
xzal125 小时前
GESP Python笔记
笔记·python·算法
wzdark5 小时前
算法中的记忆化思想与重复子问题优化4
算法
Discipline10295 小时前
流水线运行是否正随时间变得更加集中?
算法
Leo.yuan5 小时前
企业可信Data Agent怎么建:可信分析智能体(Traceable Analytic Agent)的分析链路与验证机制
大数据·人工智能·机器学习
lzhdim5 小时前
C#不为人知的10个魔法特性:资深开发者也会震惊的底层奥秘
java·前端·javascript·算法·c#
weixin_307779135 小时前
C++代码实现MATLAB中的pade函数功能
开发语言·c++·算法·matlab
smj2302_796826526 小时前
解决leetcode第4064题至多一次取反能被k整除的最长子数组II
python·算法·leetcode
AIGC小尼6 小时前
ACE-Step-1.5 本地 AI 音乐模型实战|text2music 歌词转人声,部署脚本、参数调优与排坑全记录
人工智能·算法·comfyui·ai漫剧
YOLO数据集集合6 小时前
树木检测数据集 | 树木检测 树种分类 航拍林业 森林监测9131期
人工智能·算法·机器学习·分类·数据挖掘·林业·树种分类