世界杯红牌 / 2026-01-18 16:33:59

引言如果你去问十个做深度学习的工程师:“算子是什么?”

大多数人可能会回答:“就是 MatMul、ReLU 这些嘛。”

听起来好像简单,但要是真的用一句话解释清楚 —— 很难。因为算子不是某个函数,也不是某段 Python 代码,它更像是深度学习世界里“最小执行单元”的一种抽象。

为了避免抽象,我们换一种最接地气的方式来理解:

算子就像做菜时的动作。

切、炒、翻、焖、撒盐——这些都是厨房里的基本动作。

不管你想做宫保鸡丁还是红烧肉,最终都要由这些动作组合来实现。

深度学习模型也是一样的。不管你写的是 CNN、Transformer、LLM 还是 OCR 模型,本质上都是一个个算子的组合,比如:

判断是否大于 0 的 ReLU做矩阵乘法的 MatMul扫描特征图的 Conv2D做平滑归一化的 Softmax / LayerNorm模型结构再高大上,最终都要回到这些“动作”来执行。

但这里有一个更关键的误区需要拆开: 算子不是你写在 Python 里的那段代码。

就像“翻炒”不是厨师口头描述的步骤,而是真正落在锅里的那套动作。

你写 torch.relu(x),只是告诉 PyTorch: “我要用 ReLU 这个动作。”

但 PyTorch 会做的事情是:

先查 GPU 里有没有 ReLU 对应的 kernel? 如果你用的是昇腾 NPU,它会查 CANN 提供的 ReLU 实现? 如果你用的是 CPU,它会找 MKL、OpenMP 的优化版本?

也就是说: 算子的本体不是 Python 实现,而是最终被硬件执行的计算核(kernel)。

换个更工程化的说法你就明白了:

模型结构是“动作的剧本”框架是“动作导演”算子是“动作演员和动作设计”硬件(GPU / NPU / CPU)是“拍摄现场”

你写指令,框架理解剧情,但真正执行的人,是算子。

这也是为什么大模型部署到不同硬件上,就像同一部电影找不同动作演员演会出现“动作不一样”“动作做不出来”的情况。比如你把 HuggingFace 的模型丢到昇腾上,有时会遇到:

“xxx 算子不支持”

这不是什么 bug,而是因为: 昇腾上没有这个算子的实现。

一、要弄懂算子,得抓住两个本质为了避免概念漂浮,我把算子的本质压到两个最关键的观念:

① 算子是深度学习中最小的“可执行计算单元”。它不是某个函数,而是更底层的:

做一次矩阵乘法做一次卷积做一次点积做一次 Softmax模型执行的时候,都是这些原子操作。

如果一个模型有 200 层,那么可能需要执行几千到几万个算子。

而在大模型训练中,这种算子会在一次 forward/backward 中被重复使用数万亿次。

② 算子是“抽象概念”和“硬件指令”之间的桥梁。你写的是抽象的数学公式:

Q·K^TSoftmaxLinear但 GPU/昇腾真正执行的是:

一段 CUDA kernel一段 CANN kernel一段能利用向量化、访存优化、并发调度的底层算子代码。算子把“你想要的结果”和“硬件能够执行的动作”打通。

如果没有算子,那深度学习框架就像一个只会写小说的人,面对 GPU 这个“只说汇编语言的工人”完全没法沟通。

假设你跑一个简单的 Transformer Attention,代码可能只有几行:

代码语言:javascript复制attn = softmax(Q @ K.T) @ V看着简单对吧? 但你知道底层跑了多少个算子吗?

Q @ K.T → 一个 BatchMatMul 算子Softmax → 一个 Softmax 算子再 @ V → 另一个 BatchMatMul 算子

每个算子背后,可能还分成几十个 kernel 执行:

矩阵分块(tile)调度线程卷积/乘法循环展开向量化Cache 调用指令流水等而当你说:

“FlashAttention 让推理快 2~4 倍”

它本质上就是: 把原本多个算子 + 多个访存步骤 → 合并成一个更高效的算子。

这就是算子的力量 —— 既决定模型能不能跑,又影响模型跑得快不快。

二、算子在深度学习框架中的位置如果说前面告诉你算子是什么、为什么重要,那么接下来这章要回答一个更关键的问题:

算子到底在深度学习框架里处于什么地位?

很多工程师学 PyTorch、TensorFlow 或 JAX 时,会自然觉得:

“我写了代码,框架帮我算,GPU 自动跑。”

但这个感觉容易误导你以为框架是“直接”在 GPU 上执行你写的 Python 代码。

实际上,Python 只是“给框架写了个故事”,而真正把故事变成动作的,是算子。

我们可以把整个工作流拆解成一个非常现实的场景:

1.你写的是“剧本”

你写的 PyTorch 模型、TensorFlow 计算、JAX 程序,就像写了一段剧本:

代码语言:javascript复制y = torch.relu(x @ W + b)这段代码只告诉框架:

我需要一次矩阵乘法然后加一次偏置再跑一个 ReLU它并不会告诉 GPU 如何执行。像 PyTorch、TensorFlow、MindSpore 等框架会把你的代码解析成一个所谓的 计算图(Computation Graph)。

框架会把剧本翻成“计算图”

你可以把它理解为: 把你的剧情拆成“动作节点”和“数据流向”。

我们的这段代码会变成:

代码语言:javascript复制x @ W ——> Add ——> ReLU ——> y这就是计算图。 也就是说,你写的所有模型结构,本质就是一张巨大的、有向的动作图。

2. 框架会把剧本翻成“计算图”

像 PyTorch、TensorFlow、MindSpore 等框架会把你的代码解析成一个所谓的 计算图(Computation Graph)。

你可以把它理解为: 把你的剧情拆成“动作节点”和“数据流向”。

我们的这段代码会变成:

代码语言:javascript复制x @ W ——> Add ——> ReLU ——> y这就是计算图。 也就是说,你写的所有模型结构,本质就是一张巨大的、有向的动作图。

3. 计算图会被“映射”(Mapping)成对应的算子

框架接下来的工作不是直接让 GPU 执行,而是:

在它支持的算子库里,寻找对应的算子。

比如 PyTorch 会去查:

MatMul → 调用 cuBLAS 或自家 kernel?Add → 调用 fused add kernel?ReLU → 调用 CUDA kernel?如果你换到昇腾 NPU:

MatMul → 调用 CANN 里的 MatmulV2?Add → 调用 vector add?ReLU → 调用 TBE 实现的 ReLU?这一步非常关键,因为:

框架不负责做计算 它只负责找到“能做这个计算的人”。

4.算子翻译成 Kernel

算子看起来是抽象的动作: “做一次矩阵乘法”“做一次卷积”“执行一次 Softmax”

但它最终会落成一段非常底层的 kernel(内核指令),这段代码:

在 GPU 上是 CUDA C++ 写的在昇腾上是 CANN TBE/Cube/CANN Dialect在 CPU 上是矢量化 SIMD 指令在 Apple M 芯片上则是 Metal kernelKernel 才是硬件真正执行的对象。

如果算子是“动作指令”, 那么 kernel 是“具体动作流程”。

5. 硬件执行 Kernel,数据在算子间流动最后一步才轮到 GPU/NPU 出场。

它们不会去看你写的 Python,不看模型结构,不看抽象公式,只负责一件事:

“你给我什么 Kernel,我照着执行。”

执行过程中,训练/推理的数据会在算子之间流动,就像流水线上的物料一样被一步步加工,最终得出结果。

把整个链路放在一起看,你会发现一个非常朴素的事实

模型看起来神秘,但本质上就是:

而在这一整条链路中,只有算子是“真正执行的人”。

框架负责组织和调度,算子负责实际计算,硬件负责跑算子。

假设你做一个简单的 MLP 推理,Python 只写了两行:

代码语言:javascript复制x = torch.matmul(x, W)

y = torch.relu(x)你以为执行了两行代码,但实际情况是:

PyTorch 把两行变成 2 个算子每个算子背后有可能拆成几十个 GPU kernel: load tile访存matmul kerneladd kernelthread block 调度warp 分配向量化执行GPU 只负责执行这些 kernel整套流程可能发生上千次看似简单的 ReLU,其实底层也要调度 kernel、控制线程、做访存优化。

这也是为什么你越深入推理优化,就越容易听到:

“这个算子太慢了,得换一个。” “这个算子在 GPU 上有优化版本。” “昇腾上这个算子要用 TBE 自定义。”

因为算子是这个世界里最接近硬件的那一层, 也是整个性能、兼容性、跨硬件迁移、训练效率的核心。

有更多感悟以及有关大模型的相关想法可随时联系博主深层讨论,我是Fanstuck,致力于将复杂的技术知识以易懂的方式传递给读者,热衷于分享最新的人工智能行业动向和技术趋势。如果你对大模型的创新应用、AI技术发展以及实际落地实践感兴趣,那么请关注Fanstuck,下期内容我们再见!

公司註冊處
微信视频压缩怎么操作?分享些视频压缩小妙招