第2章:TensorFlow架构初探:计算图、Session与Kernel,软件抽象层如何定义硬件接口
各位同学好,今天我们来聊聊TensorFlow的底层架构。说实话,我刚接触TensorFlow时,也被它那一堆概念搞得有点晕——计算图、Session、Kernel……这些到底是个啥?后来在给某家芯片厂商做适配时,我才真正搞明白这些抽象层设计的精妙之处。
你想想看,一个深度学习框架要支持那么多硬件——CPU、GPU、NPU、TPU……如果每换一种芯片就得重写一遍框架,那工程师不得累死?所以TensorFlow搞了一套软件抽象层,说白了就是给硬件定了个“接口标准”。
2.1 计算图:静态的“施工蓝图”
先说说计算图。我个人习惯把计算图理解成一张“施工蓝图”。你写代码时定义的每个操作——比如矩阵乘法、卷积、ReLU激活——都会被框架记录下来,形成一个有向无环图。
举个例子:
import tensorflow as tf
# 定义两个占位符
a = tf.placeholder(tf.float32, shape=[3, 3])
b = tf.placeholder(tf.float32, shape=[3, 3])
# 定义操作
c = tf.matmul(a, b)
d = tf.nn.relu(c)
这段代码执行后,并不会真的做计算。它只是在内存里构建了一张图:a和b是输入节点,matmul是中间节点,relu是输出节点。嗯,这里要注意——计算图是静态的,一旦定义好就不能改了。
为什么是静态的?
静态图的好处在于:框架可以在执行前对整个计算流程做全局优化。比如合并某些操作、消除冗余计算、甚至为特定硬件生成最优的执行计划。我在做芯片适配时,就经常利用这个特性——提前分析计算图,把适合硬件加速的算子标记出来。
2.2 Session:真正的“施工队”
图建好了,谁来干活?Session就是那个“施工队”。它负责把计算图交给后端执行引擎,分配资源,调度计算。
with tf.Session() as sess:
result = sess.run(d, feed_dict={a: A_data, b: B_data})
print(result)
看到没?sess.run()才是真正触发计算的地方。这时候Session会做几件事:
- 检查计算图是否完整
- 分配设备(CPU/GPU等)
- 按拓扑顺序执行节点
- 返回结果
我记得有一次,团队里有人抱怨模型跑得慢。我一看代码,发现他在循环里反复创建Session……这就像每次砌一块砖就重新招一批施工队,效率能高才怪。正确的做法是复用Session,或者用tf.train.MonitoredSession这类高级封装。
2.3 Kernel:硬件上的“真功夫”
计算图里的每个节点(比如matmul),最终都要落到硬件上执行。这个“落地”的过程,就是Kernel的职责。
Kernel是什么? 说白了,它是某个操作在特定硬件上的具体实现。比如矩阵乘法:
- 在CPU上,可能调用Eigen库的
gemm函数 - 在GPU上,可能调用cuBLAS的
cublasSgemm - 在NPU上,可能调用厂商自研的矩阵计算单元
TensorFlow通过Kernel这个抽象层,把操作和硬件解耦了。框架只需要说“我要做矩阵乘法”,至于具体怎么算——那是Kernel的事。
给芯片厂商的提示:
如果你想在TensorFlow里支持自己的硬件,核心工作就是注册Kernel。你需要实现OpKernel接口,把框架发来的计算请求翻译成硬件指令。我曾经帮一家初创公司做过这事——他们花了两周时间,把几个关键算子的Kernel写好了,模型就跑起来了。
2.4 软件抽象层:硬件接口的“翻译官”
现在我们把三个概念串起来,看看TensorFlow的软件抽象层是怎么定义硬件接口的。
整个流程是这样的:
- 用户定义计算图(操作层面)
- Session调度执行(运行时层面)
- Kernel映射到硬件(设备层面)
每一层都定义了清晰的接口。比如Kernel层,TensorFlow规定了Compute()方法——你只要实现这个方法,框架就能调用你的硬件。
下面这张图展示了这个分层架构:
从这张图可以看得很清楚:Kernel是连接框架和硬件的桥梁。芯片厂商只需要关注最下面这一层,上面的三层框架已经帮你搞定了。
2.5 芯片厂商的视角:如何接入TensorFlow
如果你是一家芯片厂商,想让自己的硬件跑TensorFlow模型,通常有两条路:
| 方案 | 工作量 | 性能 | 维护成本 |
|---|---|---|---|
| 实现自定义Kernel | 中等(每个算子约1-2周) | 高(可深度优化) | 高(需跟进框架更新) |
| 使用XLA编译器 | 低(只需实现HLO后端) | 中(依赖编译器优化) | 低(自动适配新算子) |
我曾经踩过的坑:
刚开始做芯片适配时,我图省事选了XLA方案。结果发现XLA对某些动态shape的支持不太好,模型跑起来老是报错。后来老老实实写了几个关键算子的Kernel,反而更稳定。所以我的建议是:核心算子用Kernel,边缘算子用XLA回退。
2.6 小结
这一章我们聊了TensorFlow的三大核心概念:
- 计算图:静态的“施工蓝图”,定义计算流程
- Session:真正的“施工队”,负责调度执行
- Kernel:硬件上的“真功夫”,把操作映射到具体设备
这三层抽象,让TensorFlow能够灵活支持各种硬件。芯片厂商只要把Kernel层实现好,就能让模型在自己的芯片上跑起来。说白了,这就是软件定义硬件的典型例子——框架定好接口,硬件去适配接口。
下一章我们会深入Kernel的实现细节,看看如何为自定义硬件编写高效的算子。到时候我会拿一个真实的案例来拆解,保证让你有收获。