张量、矩阵乘法、自动微分与计算图底层图谱
从 Tensor、Shape、Broadcast、MatMul、Operator、Computation Graph、Autograd、Backward Graph 到 Kernel:理解深度学习框架如何把数学变成可训练系统
阅读定位: 这一页放在
神经网络 / 深度学习 / 训练循环 之前,补最底层的“张量计算与自动求导”。
它不替代线性代数教材,也不展开 PyTorch / JAX 源码;目标是让你知道数据怎样变成张量,forward 怎样变成计算图,loss 怎样沿图反传,框架为什么能自动算梯度,以及这些机制怎样连接 GPU、optimizer、checkpoint 和训练稳定性。
Raw Sample
文本 / 图像 / 表格
→
Tensor
shape / dtype / device
→
Operators
matmul / add / norm
→
Forward Graph
输出与 loss
→
Backward Graph
梯度责任链
→
Optimizer Step
参数更新
| 层级 | 它回答什么 | 常见对象 | 出错信号 |
| 张量 | 数据以什么形状、精度和设备进入计算 | shape、dtype、device、stride、contiguous | shape mismatch、dtype 错误、CPU/GPU device 不一致 |
| 算子 | 张量之间如何被变换 | matmul、add、softmax、layer norm、gather、scatter | 数值溢出、kernel 慢、broadcast 误用 |
| 计算图 | 一次 forward 的依赖关系怎样被记录 | node、edge、activation、requires_grad | 梯度断开、显存暴涨、in-place 操作破坏图 |
| 自动微分 | 框架如何按链式法则自动算梯度 | autograd、vjp、jvp、backward、grad_fn | grad 为 None、梯度全零、梯度爆炸或 NaN |
| 优化器 | 梯度如何变成参数更新 | SGD、AdamW、weight decay、optimizer state | 参数不变、更新过大、checkpoint 恢复不一致 |
一句话:
深度学习框架最核心的魔法不是“会 AI”,而是把张量算子组织成可微计算图,并在 backward 时自动沿图计算每个参数该承担的梯度。
| 概念 | 直觉 | 在模型里的例子 | 工程风险 |
| Shape | 张量的维度结构 | [batch, seq, hidden]、[vocab, hidden] | 维度顺序错了,结果可能能跑但语义错 |
| Dtype | 数值精度和存储格式 | FP32、BF16、FP16、FP8、INT8 | 低精度可能溢出、下溢或损失关键差异 |
| Device | 张量放在哪个计算设备上 | CPU、CUDA GPU、TPU、NPU | 跨设备拷贝会慢,设备不一致会直接报错 |
| Stride / Layout | 内存中如何排列 | contiguous、channels-last、view / transpose | 看似无害的转置可能触发隐式拷贝或慢 kernel |
| Broadcast | 小张量按规则扩展参与运算 | bias 加到整批样本、mask 加到 attention logits | 误 broadcast 会悄悄把错误复制到整批数据 |
文本模型里的典型 shape
input_ids: [batch, seq]
embedding: [batch, seq, hidden]
logits: [batch, seq, vocab]
loss mask: [batch, seq]
图像模型里的典型 shape
image: [batch, channel, height, width]
patches: [batch, patch_count, hidden]
latent: [batch, channel, h, w]
mask: [batch, patch_count]
训练系统里的典型张量
- 参数张量:权重和 bias
- 激活张量:forward 中间结果
- 梯度张量:loss 对参数的偏导
- 优化器状态:Adam 的一阶 / 二阶动量
| 算子 | 做什么 | 为什么是核心 | 继续关联 |
| MatMul / GEMM | 矩阵乘矩阵或批量矩阵乘 | Linear 层、QKV 投影、MLP、输出头都依赖它 | AI Infra / GPU、MLP / FFN |
| Elementwise | 逐元素加减乘除、激活函数 | 看起来简单,但大量小算子会带来 kernel launch 开销 | kernel fusion、编译优化;SwiGLU 见 MLP / FFN |
| Reduction | 求和、均值、最大值、归一化统计 | loss、softmax、LayerNorm 都离不开它 | 数值稳定、并行规约;LLM 里的归一化见 Norm / Residual |
| Gather / Scatter | 按索引读取或写回 | Embedding lookup、token 选择、稀疏更新 | 内存访问不连续,性能可能受限 |
| Softmax | 把 logits 变成概率分布 | attention 权重和 next-token 输出都依赖它 | Logits / Softmax、概率 / 熵 / 校准 |
为什么很多优化都围绕 MatMul
大模型的大部分 FLOPs 花在矩阵乘法上。GPU、Tensor Core、低精度、FlashAttention、量化、kernel fusion 和编译器优化,本质上都在让张量算子更贴近硬件高效路径。
| 图里的东西 | 含义 | 为什么必须记录 | 常见坑 |
| Node | 一次算子调用或中间张量 | backward 要知道梯度从哪里来、往哪里传 | 动态图里每次 forward 图都可能不同 |
| Edge | 张量之间的依赖关系 | 决定链式法则的传递路径 | detach 会切断边,梯度就过不去了 |
| Activation | forward 中间结果 | 很多 backward 公式需要用 forward 值 | 保存太多会吃显存,重计算会吃时间 |
| Parameter | 需要被优化器更新的叶子张量 | 只有它们通常会累积梯度并被 step 更新 | 误冻结或没注册到 optimizer 会导致训不动 |
| Loss | 标量目标 | backward 通常从这个标量开始 | loss reduction、mask 和 label 对齐错误会污染梯度 |
动态图
PyTorch 的直觉更接近动态图:Python 代码跑到哪里,图就按实际执行路径搭到哪里。调试友好,控制流自然,但优化器和编译器要额外工作。
静态图 / 编译图
TensorFlow 早期、JAX、XLA、Torch Compile 更强调先捕获或编译图,再整体优化。性能更可控,但对 shape、控制流和调试有更强约束。
动态图和编译不是对立
现代框架常常先保留动态图开发体验,再通过 tracing、graph capture、AOT 编译或 kernel fusion 把热点路径优化掉。
| 概念 | 它是什么 | 在训练里的作用 | 工程观察 |
| Chain Rule | 复合函数求导的链式法则 | 把输出误差逐层传回输入和参数 | 深层模型就是很长的链式法则账本 |
| Reverse-mode AD | 从输出往输入反向累积梯度 | 深度学习训练最常用,因为一个标量 loss 对大量参数求导更高效 | 需要保存 forward 中间值,所以吃显存 |
| Forward-mode AD | 从输入方向传播导数 | 适合输入维度小、输出维度大或某些高级微分任务 | JVP、灵敏度分析、部分二阶方法会用到 |
| VJP | Vector-Jacobian Product | 反向模式常见形式,不显式构造巨大 Jacobian | 高效是因为只算需要的乘积 |
| Gradient Accumulation | 多次 backward 累积梯度再 step | 模拟更大 batch,缓解显存不足 | 忘记清梯度会让更新混入旧 batch |
为什么不直接算完整 Jacobian
大模型参数可能有几十亿甚至更多,完整 Jacobian 极其巨大。自动微分通常不会显式构造它,而是沿计算图做局部梯度规则和向量乘积,把需要的梯度高效传回来。
| 步骤 | 发生了什么 | 要看什么信号 | 常见问题 |
| 1. 从 loss 开始 | 标量 loss 作为反向传播起点 | loss 是否包含正确 mask、label、reduction | padding 参与 loss,模型学错目标 |
| 2. 沿图反传 | 每个算子调用自己的 backward 规则 | grad_fn、梯度路径是否存在 | detach、in-place、no_grad 切断图 |
| 3. 累积到参数 | 叶子参数的 .grad 被填充或累加 | grad norm、None / zero / NaN | 参数没有 requires_grad 或没进 optimizer |
| 4. 优化器读取梯度 | AdamW 等按梯度和状态计算更新 | update norm、学习率、weight decay | 学习率过大、状态恢复错误 |
| 5. 清理旧梯度 | 下一轮前清空或置 None | zero_grad 是否在正确位置 | 旧梯度累积导致训练异常 |
调试原则:
训练 loss 不动时,不要第一时间换模型。先确认数据样本、shape、loss mask、requires_grad、grad norm、参数是否更新、optimizer 是否包含目标参数。
| 显存对象 | 为什么存在 | 如何降低 | 代价 |
| 参数 | 模型需要常驻权重 | 量化、分片、LoRA、小模型 | 可能牺牲质量或增加通信 |
| 激活 | backward 需要 forward 中间值 | activation checkpointing、重计算、短序列 | 省显存但增加计算时间 |
| 梯度 | 每个可训练参数都要有梯度 | 冻结层、LoRA、ZeRO/FSDP | 可训练能力或通信模式变化 |
| 优化器状态 | AdamW 等需要动量和二阶统计 | 低精度 optimizer、分片、CPU offload | 实现复杂、吞吐下降 |
| 临时张量 | 算子执行中间缓存和 workspace | kernel fusion、编译优化、避免隐式拷贝 | 调试和兼容成本 |
训练和推理的图不一样
训练必须保留大量中间信息用于 backward;推理通常不需要梯度,可以用 no_grad / inference mode 省掉反向图。很多“为什么训练这么吃显存”的答案,就藏在计算图和激活保存里。
| 层级 | 做什么 | 常见组件 | 你该关心什么 |
| 前端框架 | 提供张量 API、模块、自动微分和训练循环 | PyTorch、JAX、TensorFlow | 开发体验、生态、调试和分布式支持 |
| 图捕获 / 编译 | 把 Python 执行路径转成可优化图 | Torch Compile、JAX jit、XLA、ONNX | shape 稳定性、控制流、编译耗时 |
| Kernel | 真正跑在 GPU / 加速器上的低层程序 | CUDA kernel、Triton、cuDNN、FlashAttention | 吞吐、显存访问、融合和硬件兼容 |
| Runtime | 调度 kernel、内存、stream 和通信 | CUDA runtime、NCCL、ROCm、XLA runtime | 同步点、通信等待、内存碎片 |
| Profiler | 观察时间和显存花在哪里 | PyTorch Profiler、Nsight、TensorBoard | 先定位瓶颈,再做优化 |
实用判断:
模型代码慢,不一定是算法差;可能是张量布局、隐式拷贝、小算子太碎、shape 动态、kernel 没融合、CPU 数据加载或 GPU 通信在拖后腿。
训练计算图 Trace 证据包
训练问题要能回放到一次 forward / backward 的张量、图、梯度、kernel 和显存账本,否则 NaN、OOM、慢训练和错 loss 都会变成猜谜。
| 证据节点 | 必须记录字段 | 用来排查什么 |
| graph_run | graph_run_id、framework_version、compile_mode、model_config_hash、input_batch_id | 同一训练图是否可复现,编译和动态图路径是否一致 |
| tensor_trace | tensor_name、shape、dtype、device、layout、requires_grad | shape mismatch、隐式拷贝、精度路径和参数冻结问题 |
| backward_trace | loss_value、grad_norm、loss_scale、activation_checkpointing、anomaly_op | NaN / Inf、梯度爆炸、detach 和 checkpointing 副作用 |
| runtime_snapshot | kernel_trace_id、compiler_version、peak_memory、memory_fragmentation、sync_point | 训练慢、显存暴涨和 GPU 利用率低的真实瓶颈 |
| 症状 | 优先怀疑 | 先做什么 | 继续下钻 |
| shape mismatch | batch / seq / hidden / vocab 维度顺序错 | 打印每一步 shape,给关键张量命名 | Tokenizer / 词表 |
| loss 能算但任务全错 | label shift、mask、broadcast 或 reduction 错 | 拿一个样本手动对齐输入、标签和 loss | 训练数据到模型能力 |
| grad 为 None | 图被 detach、参数未注册、requires_grad 关闭 | 检查 grad_fn、requires_grad、optimizer param groups | 优化器 / Loss / 稳定性 |
| 显存突然暴涨 | 保留了多轮图、未 detach、序列长度异常 | 检查列表缓存、日志保存 tensor、activation 数量 | AI Infra / GPU |
| NaN / Inf | 低精度溢出、softmax 不稳定、梯度爆炸 | 开 anomaly detection、看异常 batch 和 gradient norm | 训练稳定性 |
| GPU 利用率低 | 数据加载、CPU/GPU 拷贝、小 kernel、同步点 | 用 profiler 看时间花在哪里 | AI Infra / GPU |
这张图在主干里的位置
神经网络页解释“为什么模型能训练”,优化器页解释“怎样稳定训练”,训练生命周期页解释“样本如何变成能力”。这一页解释这些东西下面的共同机器:张量、算子、计算图和自动微分。理解它之后,再看 shape、loss、gradient、GPU 显存和训练崩坏,会少很多玄学。