第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的软件抽象层是怎么定义硬件接口的。

整个流程是这样的:

  1. 用户定义计算图(操作层面)
  2. Session调度执行(运行时层面)
  3. Kernel映射到硬件(设备层面)

每一层都定义了清晰的接口。比如Kernel层,TensorFlow规定了Compute()方法——你只要实现这个方法,框架就能调用你的硬件。

下面这张图展示了这个分层架构:

用户代码(Python/C++ API) 定义计算图、配置Session 计算图(Graph) 静态结构:节点(Op)+ 边(Tensor) Session(运行时) 调度执行、设备分配、资源管理 Kernel(硬件抽象层) OpKernel::Compute() → 硬件指令 API层 抽象层 硬件层

从这张图可以看得很清楚:Kernel是连接框架和硬件的桥梁。芯片厂商只需要关注最下面这一层,上面的三层框架已经帮你搞定了。

2.5 芯片厂商的视角:如何接入TensorFlow

如果你是一家芯片厂商,想让自己的硬件跑TensorFlow模型,通常有两条路:

方案 工作量 性能 维护成本
实现自定义Kernel 中等(每个算子约1-2周) 高(可深度优化) 高(需跟进框架更新)
使用XLA编译器 低(只需实现HLO后端) 中(依赖编译器优化) 低(自动适配新算子)

我曾经踩过的坑:

刚开始做芯片适配时,我图省事选了XLA方案。结果发现XLA对某些动态shape的支持不太好,模型跑起来老是报错。后来老老实实写了几个关键算子的Kernel,反而更稳定。所以我的建议是:核心算子用Kernel,边缘算子用XLA回退

2.6 小结

这一章我们聊了TensorFlow的三大核心概念:

  • 计算图:静态的“施工蓝图”,定义计算流程
  • Session:真正的“施工队”,负责调度执行
  • Kernel:硬件上的“真功夫”,把操作映射到具体设备

这三层抽象,让TensorFlow能够灵活支持各种硬件。芯片厂商只要把Kernel层实现好,就能让模型在自己的芯片上跑起来。说白了,这就是软件定义硬件的典型例子——框架定好接口,硬件去适配接口。

下一章我们会深入Kernel的实现细节,看看如何为自定义硬件编写高效的算子。到时候我会拿一个真实的案例来拆解,保证让你有收获。


专注资料整理