TensorFlow 和 PyTorch:一场已经打完的战争

2026-08-06

← AI 与大模型

一句话:TensorFlow 和 PyTorch 都不是 AI,它们是做 AI 的工具——本质上是两台「自动求导机」。它们当年吵得最凶的那个分歧(先画图纸还是边搭边跑)今天已经不存在了,因为两边都偷偷学了对方。所以「该选哪个」的答案,和你在网上看到的多半不是同一回事。

反幻觉声明:本文所有代码输出、运行时间、下载量和收藏数,都是 2026 年 8 月 6 日在我自己的机器上(RTX 5090 显卡)和官方接口上真实跑出来的,你照着做能得到同样结果。历史日期取自 Wikipedia 与 GitHub 官方接口,两者有出入的地方我在文中直接写明了分歧,没有替你选一个。本文刻意不引用「PyTorch 占论文 85%」这类流传很广的数字——我追到源头发现它们出自二手博客,方法论不明、无法独立核实,具体见文末第七节。

一、先解决一个最常见的误会

很多人以为 TensorFlow 和 PyTorch 是两种 AI,就像安卓和苹果是两种手机系统。

它们不是 AI。它们是造 AI 的车间设备。

ChatGPT、Midjourney、你手机里的人脸解锁——这些是成品。TensorFlow 和 PyTorch 是造这些成品时用的机床。同一个模型,用哪台机床造出来都可以,造出来是同一个东西。

所以问「TensorFlow 和 PyTorch 哪个更聪明」,就像问「德国机床和日本机床哪个做出来的菜更好吃」——问错了对象。

它们的正式名字叫深度学习框架(deep learning framework,可以直接理解成「搭神经网络的工具箱」)。

二、那这个工具箱,到底替你做了什么?

要理解它们,得先知道训练一个 AI 模型是在干嘛。

你可以把一个模型想成一台有几十亿个旋钮的机器。给它一张猫的照片,它输出「这是狗」——错了。现在的问题是:

这几十亿个旋钮,每一个该往左拧还是往右拧?拧多少?

数学上这个「该往哪拧多少」叫做梯度(gradient)。算梯度的方法一百多年前就有了,是高中就学过的求导,只是要一层层往回套用(这个套用规则叫链式法则)。

问题不在于难,在于多

一个稍微复杂点的模型,手工把这些求导公式推一遍能写满几十页纸;而且你只要改动一层结构,前面推的全部作废,得重来。上世纪九十年代的研究者真的就是这么干的——这也是为什么那时候一年发不了几篇论文。

框架解决的就是这件事,而且解决得非常彻底:

深度学习框架的三件工作:偷偷记账、反向倒推、派给显卡。你只需要正着写公式,倒着算账的部分它全包了

关键是第①步「记账」:你每做一次乘法、一次加法,框架都在背后偷偷记下来,织成一张「谁依赖谁」的网。等你说一声「开始倒推」,它就顺着这张网反着走一遍,把几十亿个旋钮的调整方向全部算出来。

这个能力叫 自动微分(automatic differentiation,就是「自动帮你求导」)。它才是这两个框架的核心,不是别的。

顺便说,这也是为什么 AI 突然火起来的时间点,恰好在这两个框架出现之后。不是因为想法变了——很多想法七十年代就有了——而是因为试错的成本塌了。原来改一个结构要重推两周公式,后来改一行代码重跑一晚上就行。

三、当年真正的分歧:先画图纸,还是边搭边跑

既然两边做的是同一件事,那当年到底在吵什么?

吵的是:这张「谁依赖谁」的网,什么时候织出来。

TensorFlow 2015 年 11 月 9 日由 Google 开源1,它的选择是:你先把整张图纸画完,交给我,我再开工。

这个写法今天还能跑。我在最新的 TensorFlow 2.21.0 上用兼容模式实际跑了一遍——注意看最关键的那一行:

a = tf.placeholder(tf.float32)   # 声明「将来有个数从这进来」,现在没有值
b = tf.placeholder(tf.float32)
c = a * b

print(c)                          # 想看看 a 乘 b 是多少

按常理,print(c) 应该打印出一个数。实际输出是:

Tensor("mul:0", dtype=float32)

它不是数字,是图纸上一个叫「乘法」的节点。 因为这时候压根还没有数据,你只是画了张图。要真的算出来,必须另外开一个「施工现场」(Session),把数据喂进去:

sess.run(c, {a:3, b:4}) = 12.0
sess.run(c, {a:5, b:6}) = 30.0

这就是静态图(static graph):先定义,后执行。

PyTorch 2016 年出现,选了相反的路:写一行就跑一行,跟平时写 Python 一模一样。3 * 4 打印出来就是 12。这叫动态图(dynamic graph),也叫 eager execution(急切执行——「急」在于它不等你画完图,马上就算)。

一个关于生日的小考据。 PyTorch 到底哪天发布的,各家说法不一:Wikipedia 写 2016 年 9 月2,不少文章写 2016 年 10 月,也有说 2017 年 1 月的。我去 GitHub 官方接口查了原始记录:代码仓库创建于 2016 年 8 月 13 日,最早的版本标签 v0.1.1 的提交时间是 2016 年 8 月 24 日,提交信息写着 “bumping to alpha-1”(升到内测第一版);而 v0.1.6 已经是 2017 年 1 月 21 日了3

所以这些说法其实都没错,只是各自在说不同的事:仓库建立、内测版、公开宣传是三个不同的时间点。这类「哪个日期才算数」的模糊,在开源项目里非常常见——遇到打架的日期,去查原始记录比选一个信更靠谱。

这个分歧有多大?我用一句 print 量给你看

我在两边写了完全一样的函数:函数体里放一句 print,然后调用 3 次。

静态图里调用 3 次只打印 1 次,而且打印的是符号不是数值;动态图 3 次都打印,每次都是真实数字

结果差别大到有点吓人:

静态图(TensorFlow 加 @tf.function 动态图(PyTorch)
调用 3 次,print 响几次 1 次 3 次
打印出来的 x 是什么 Tensor("x:0", shape=(), ...) 一个符号 0.01.02.0 真实数字

静态图只响 1 次,是因为它只在第一次调用时读了一遍你的代码,把它翻译成图纸,之后就一直照图纸跑,再也不看你的 Python 代码了。你写的 print 属于「读代码」阶段的东西,所以只执行了一次。

这就是为什么研究者当年集体倒向 PyTorch。 做研究意味着你的模型天天在改、经常出错、需要中途停下来看看某个数到底变成了多少。在动态图里,你随时能加一句 print、打个断点;在静态图里,你面对的是一张已经封好的图纸。

反过来,工程部署需要的恰恰是静态图:一张定死的图纸可以被整体优化、可以塞进手机、可以脱离 Python 运行。这也是 TensorFlow 早年在工业界扎得比较深的原因。

四、然后,两边都认输了

故事的关键转折是:这场仗打完了,因为双方都承认对方是对的。

TensorFlow 在 2019 年 9 月发布的 2.0 版本里,把默认行为改成了动态图4。我装了最新版验证,tf.executing_eagerly() 返回 True——默认就是动态的,Session 那套写法退居兼容模式。

而且它还带了一个很聪明的东西叫 AutoGraph:你在函数上加一句 @tf.function,它会自动把你的 Python 代码改写成图纸。我原本以为在图模式下写普通的 if 会出问题——这个预期错了,它跑得好好的。于是我去看它到底把我的代码改成了什么。我写的是这三行:

if x > 0:
    return tf.constant(100.0)
else:
    return tf.constant(-100.0)

AutoGraph 转换后的真实代码有 40 行左右,我的 if 被拆成了两个函数再交给一个调度器:

def if_body():  ...
def else_body(): ...
ag__.if_stmt(ag__.ld(x) > 0, if_body, else_body, get_state, set_state, ...)

它做的事是:把「Python 在运行时判断一次」改写成「图纸里画一个岔路口,运行时再选」。流传很广的「静态图里不能用 if」这个说法,在今天的 TensorFlow 上已经不成立了——虽然它 2016 年是对的。

PyTorch 则在 2023 年 3 月的 2.0 版本里,学会了编译5。加一句 torch.compile,它会把你动态跑的代码抓下来、编译成优化过的整块——本质上就是「事后补一张图纸」。

也就是说:TensorFlow 学会了先跑后编,PyTorch 学会了先跑再编。两边从两个方向,走到了同一个地方。

「一行代码提速」有多真?我测了,答案是「看情况」

torch.compile 号称一行代码就能提速。我在 RTX 5090 上测了两个场景,结果差得离谱:

同一个 torch.compile:大矩阵乘法场景 0.94 倍反而变慢,一长串小算子场景快了 49.34 倍
场景 普通模式 编译后 变化
A:大矩阵乘法为主 1.15 ms 1.23 ms 0.94×(反而慢了)
B:一长串逐元素小运算 11.34 ms 0.23 ms 49.34×

为什么差这么多?因为编译器省的不是计算时间,是搬运时间

显卡算得极快,但每做一次运算,数据都要从显存搬到计算核心、算完再搬回去。连着做 20 个小运算,就要来回搬 20 趟,时间全花在路上了。编译器把这 20 个动作缝成一个,搬一趟就够——所以场景 B 快了近 50 倍。

而场景 A 是一个巨大的矩阵乘法,本身就是一整块活儿,没有零碎搬运可省;显卡厂商还专门为它写过极致优化的库。编译器插不上手,反而多了点自己的开销,所以慢了 6%。

这解释了一件很实际的事:为什么厂商宣传的加速比,你在自己模型上常常测不出来。它不取决于框架,取决于你的模型是由什么形状的运算堆起来的

五、三个流传很广的说法,我拿数据挨个查了

说法一:「TensorFlow 更受欢迎,你看它 star 多得多」

GitHub 收藏数(star)确实是 TensorFlow 赢,而且赢得不少。但换个指标就完全反过来:

GitHub 收藏数 TensorFlow 是 PyTorch 的 1.93 倍;PyPI 月下载量 PyTorch 是 TensorFlow 的 4.87 倍
指标 TensorFlow PyTorch 谁赢
GitHub 收藏数 196,892 102,233 TF 赢 1.93 倍
PyPI 近 30 天下载量 19,875,837 96,840,777 PyTorch 赢 4.87 倍

(PyPI 是 Python 官方的软件仓库,下载量约等于「这个月有多少台机器装了它」。两个数字都是 2026-08-06 用官方接口取的。)

为什么会打架?因为一个是存量,一个是流量。

收藏数是「历史上有多少人曾经点过收藏」——只增不减。TensorFlow 早发一年,又正好赶上 2016–2019 年深度学习最火的那一波,那些收藏至今还挂在账上,哪怕点收藏的人早就改用别的了。

下载量是「这个月真的有多少台机器装了它」——不用就会掉下去

这条规矩不只适用于这两个框架:判断一个东西现在还火不火,要看流量指标,不要看存量指标。 收藏数、总下载量、注册用户数都是存量,很容易造成「它还很强」的错觉。

说法二:「TensorFlow 报错难懂,PyTorch 报错清楚」

这是我原本最有把握的一条,结果实测把它推翻了。

我在两边制造了完全一样的错误:模型第一层输出 20 维,第二层却声明只接受 30 维,故意让它对不上。然后比较报错。

报错长度 有没有指到我写错的那一行 说清哪两个数对不上了吗
PyTorch 22 行 ✅ 指到了 mat1 and mat2 shapes cannot be multiplied (4x20 and 30x5)
TensorFlow / Keras 14 行 ✅ 指到了 expected axis -1 of input shape to have value 30, but received input with shape (4, 20)

TensorFlow 的报错更短,而且话说得更像人话。 它明确写了「本来期望 30,实际收到 20」,PyTorch 则要你自己从 (4x20 and 30x5) 里看出 20 和 30 对不上。

TensorFlow 更短是因为 Keras 专门做了一层过滤,把框架内部的调用栈藏起来了,只留你自己的代码;PyTorch 把内部调用全都摊开给你看。

「TensorFlow 报错难懂」是 1.x 时代的记忆。 在那个年代,错误发生在「施工」阶段,报错指向的是图纸内部的某个节点,跟你写的哪一行对不上号——那确实是噩梦。但那个 TensorFlow 已经不存在了。

说法三:「TensorFlow 已经死了」

也不对。我数了两个项目最近 90 天的提交次数:

近 90 天提交数 2026 年发布的正式版本
TensorFlow 3,496 次 2.21.0(3 月)——1 个
PyTorch 4,700 次 2.10 / 2.11 / 2.12 / 2.12.1 / 2.13——5 个

TensorFlow 三个月三千多次提交,这显然不是一个死掉的项目

发布节奏的差距是实打实的:PyTorch 今年已经发了 5 个正式版本,几乎每两个月一个;TensorFlow 上一个正式版是 2026 年 3 月,再上一个是 2025 年 8 月——七个月才一个

准确的说法不是「TF 死了」,而是:它从冲刺状态转成了维护状态。 对已经在用它的公司来说,这甚至是好事——版本不折腾。但如果你现在从零开始学,你会发现新论文、新模型、新教程几乎都在另一边。

六、那 2026 年该学哪个?

先说清楚一件容易被忽略的事:你要学的其实是「怎么搭模型」,不是「某个框架的写法」。 这两个框架今天的核心概念几乎一一对应,学会一个再换另一个,通常是几天的事,不是几个月。

在这个前提下:

从零开始学 AI,学 PyTorch。 不是因为它技术上更优越,而是因为生态在那边:你能找到的新教程、新论文的开源代码、Hugging Face(一个公开分享 AI 模型的网站,相当于模型界的应用商店)上的现成模型,绝大多数是 PyTorch 写的。学一个东西,跟着资源最多的那条路走,能省掉大量卡壳的时间。

已经有 TensorFlow 代码在跑的公司,不用改。 它没死,还在更新,而且改框架的成本远大于收益。

如果你的模型要跑在手机、摄像头这类小设备上——这里有个变化值得单独说。很多老文章会告诉你「要上手机就用 TensorFlow,因为有 TensorFlow Lite」。这条建议在 2024 年 9 月失效了:Google 把 TensorFlow Lite 改名成了 LiteRT,并且明确让它同时接受 TensorFlow、Keras、JAX 和 PyTorch 训练出来的模型6

也就是说,Google 自己把「部署到小设备」这件事从 TensorFlow 里拆了出来,做成了谁都能用的工具。所以「要上手机所以得学 TensorFlow」这个理由,今天已经不成立了。(我没有实测过 LiteRT 的实际转换流程,这里只转述官方公告。)

还有第三个选项:Keras 3。 它现在是一个「翻译层」——同一份代码,你可以选择让它在 TensorFlow、PyTorch 或 JAX 上跑7。我装 TensorFlow 时它自带的就是 Keras 3.15.1。如果你只是想快速搭个标准模型,不想被框架绑死,这是个务实的选择。

顺便提一下 JAX。 这是 Google 的另一个框架,2018 年开始做。它在研究圈尤其是大模型训练那一块有存在感——PyPI 月下载量 1,961 万,和 TensorFlow 的 1,988 万几乎持平。不过它的定位和这两个不太一样,属于另一个话题。

七、我的方法、我的错误、和我没查到的东西

这一节是这篇文章里我认为最该读的部分。

我做错的三个地方。 写之前我心里有几个「一定是这样」的判断,结果全被推翻了:

  1. 以为 TensorFlow 图模式下写 if 会出问题——,AutoGraph 早就自动处理了。
  2. 以为 PyTorch 报错一定比 TensorFlow 清楚——,实测 TensorFlow 更短、话更像人话。
  3. 初稿里我写了「要部署到手机就用 TensorFlow Lite」——,它 2024 年 9 月就改名 LiteRT 并且开始支持 PyTorch 模型了。这条是我在发布前最后一轮核实产品名时才发现的,差一点就把一条过期两年的建议写进文章

这三个印象有个共同点:都是 TensorFlow 1.x 那个年代的,在我脑子里存了太久,没跟着版本更新。 如果我不动手跑、不查一遍,这篇文章就会把三个过时的说法又传播一遍——而它们听起来都很有道理。

我刻意没写的数字。 搜索「PyTorch vs TensorFlow」,你会看到一堆很确定的比例:「PyTorch 占顶级学术会议论文的 85%」「TensorFlow 市场份额 37%、PyTorch 25%」。我逐个追了源头,发现它们指向的是二手博客和商业数据库,没有公开的统计口径——不知道统计了哪些会议、哪一年、怎么算的。而且这些数字彼此矛盾:同一批文章里,「85%」和「55%」并存,「TensorFlow 份额更高」和「PyTorch 全面碾压」并存。所以我一个都没用,只用了我能自己跑接口验证的数字。

我没能核实的。 有文章称 Hugging Face 上 PyTorch 模型有 22 万个、TensorFlow 只有 1.5 万个。这个对比很有说服力,但我尝试用 Hugging Face 的接口自己数,它不返回总数,所以我无法独立验证,就没有写进正文

我的实验有哪些局限。 三条:①速度测试只在一台 RTX 5090 上跑,换显卡数字会变;②TensorFlow 我装的是 CPU 版,所以没有做两个框架的正面速度对比——本文任何地方都没有说谁跑得更快;③报错对比只测了一种错误类型(维度不匹配),不能推广成「所有情况下 TensorFlow 报错都更好」。

为什么这些要写出来。 因为这篇文章想说的其实不只是两个框架。在技术领域,你脑子里那些「我很确定」的判断,往往是三五年前的快照。 框架在变,而印象不会自动更新。分辨的办法很朴素:能自己跑一遍的,就别听人说。