
你现在去搜任何一家 AI 大厂的算法工程师招聘 JD会发现一个非常有趣的现象PyTorch 排在“必备技能”的第一行TensorFlow 则悄悄退到了“熟悉任意深度学习框架即可”的位置。但当你翻开源项目、论文复现、竞赛 Baseline甚至最近的 Keras 3 官方文档TensorFlow 依然占据着大量产业落地场景。很多自学的人在这个路口卡住了两个框架到底学哪个两个都学会不会乱简历上到底怎么写才显得专业先说本文的判断PyTorch 是目前 AI 算法岗的绝对主流TensorFlow 是部署与生产环境的存量王者但真正的竞争力不在“会用哪一个”而在于你能否讲清一个模型从论文到生产全链路中两个框架各自解决什么问题。这篇文章不是简单的“PyTorch 好还是 TensorFlow 好”的口水战。我会先对比两个框架的底层架构逻辑再分别用两个框架完整跑通一个图像分类实战项目最后给你一套能够写进简历的项目描述模板。无论你是准备人工智能课程大作业、备战人工智能训练师考试还是正在搭建自己的深度学习入门路线这篇文章都能少走很多弯路。1. 先想清楚你为什么纠结选型很多人把 PyTorch 和 TensorFlow 当成“两个同类工具”于是用“学哪个更有前途”来提问。这个提问方式本身就隐藏了一个误区它们在深度学习生态中占据的位置并不完全重合。1.1 算法研究阶段PyTorch 几乎成为默认语言从 2017 年 PyTorch 发布到现在学术界经历了一次明显的迁移。早期 TensorFlow 凭借 2015 年发布的时间优势和 Google 的资源迅速占领了教学与论文代码库。但 PyTorch 的torch.nn模块配合 Python 原生的调试习惯让“写网络”和“写普通 Python 代码”的体验非常接近这一下击中了研究人员的痛点。现在翻开 arXiv 上的论文大部分开源代码都是用 PyTorch 编写的。各大模型仓库例如 HuggingFace Transformers也把 PyTorch 作为优先支持的后端。对于学生、科研人员、准备人工智能训练师考试的人来说PyTorch 的生态意味着你下载一个预训练模型、跑通一个 Baseline 的成本极低。1.2 产业落地阶段TensorFlow 的存量优势依然明显一谈到上线部署TensorFlow 的成熟度不容忽视。TensorFlow Serving 可以稳定地处理高并发推理请求TensorFlow Lite 在移动端和嵌入式设备上有长时间的应用积累TFXTensorFlow Extended提供了完整的生产级机器学习流水线。但要注意一个变化字节跳动、美团、快手这些国内大厂的应用实践中PyTorch 模型的在线推理已经发展出非常成熟的落地路径比如通过 TorchServe 或者导出为 ONNX 再转换到 TensorRT。也就是说PyTorch 正在从研究领域向生产领域渗透TensorFlow 的存量优势并没有在新增项目上扩大。1.3 对个人学习的核心判断如果你是一个刚开始接触深度学习的学生我建议先学 PyTorch 打底再在需要部署时去学 TensorFlow 的基础 Serving 流程。如果你已经投入了大量时间在 TensorFlow 2.x 上也不必焦虑因为 Keras 3 引入的多后端机制让“一套代码跑在 TensorFlow、PyTorch、JAX 上”成为可能两个框架的切换成本正在降低。2. 核心概念与底层架构原理对比在写代码之前必须把两个框架最底层的架构差异搞清楚。这里我不会讲太多数学推导只讲三个真实开发中最重要的区别。2.1 动态图 vs 静态图PyTorch 的“即时执行”与 TensorFlow 的“先编译后执行”这是两个框架最本质的区别。PyTorch 采用动态图机制Define-by-Run。它的意思是你写每一行张量运算时计算图同时就在被构建。这意味着你可以用 Python 原生的if语句、for循环来控制网络结构可以随时print中间变量的形状。出现问题的时候Python 调试器pdb可以直接定位到出错的那一行。TensorFlow 2.x 默认启用 Eager Execution即时执行模式日常开发体验和 PyTorch 很接近。但当你真正把模型推向高性能生产环境时通常还是通过tf.function将 Python 函数转换成静态计算图Graph。静态图的优势是图在执行前已经被完整描述编译器可以做大量的算子融合、内存复用优化推理性能上限更高。可以这么类比PyTorch 像是一个“边说边写”的现场编剧TensorFlow 则更像一个“先写完整剧本再统一排练”的话剧导演。前者的优点是灵活、容易调试后者的优点是执行路径明确、性能上限更高。2.2 模型表达与模块化torch.nn与keras.layersPyTorch 的网络定义通常通过继承torch.nn.Module完成forward()函数中写的就是数据的前向传播路径。你必须手动管理优化器、损失函数、梯度清零的时机这让代码非常透明但也意味着“自由度越大责任越大”。TensorFlow 的 Keras 高层 API 则通过tf.keras.Sequential或者函数式 API 快速搭建网络。它把训练循环封装成了model.fit()一行代码内部自动处理梯度、权重更新、Batch 维度对齐等细节。对于快速原型验证Keras 确实非常爽。两者没有绝对好坏而是设计哲学的差异PyTorch 把控制权完全交给开发者TensorFlow 用成熟的封装换取工程便利。2.3 生态与社区论文复现和产业部署的分水岭对比维度PyTorchTensorFlow学术论文代码占比目前占据绝大多数新论文中占比持续下降预训练模型获取HuggingFace 默认优先官方 Model Garden 为主生产部署方案TorchServe、ONNX、TensorRTTensorFlow Serving、Lite、TFXJIT 与图优化TorchScript、torch.compiletf.function、XLA移动端/嵌入式支持TorchMobile生态起步略晚TFLite 成熟度更高调试体验原生 Python 调试像普通程序Eager 模式可调试Graph 模式较黑盒这个表格值得截图保存。面试中如果你能用自己的话把这张表讲清楚比死记硬背准确率高得多。3. 环境准备与前置条件工欲善其事必先利其器。这里我强烈建议不要直接在你的主 Python 环境里同时安装 PyTorch 和 TensorFlow否则依赖冲突会逼疯你。使用虚拟环境是唯一推荐的方案。3.1 创建独立的 Conda 虚拟环境conda create -n dl_compare python3.10 -y conda activate dl_compare为什么推荐 Python 3.10因为经过两年多的迭代PyTorch 与 TensorFlow 的主流稳定版本对 3.9 - 3.11 支持都很好3.10 是兼容性最稳妥的中间档。如果你的机器已经装了 CUDA 驱动请先用下面的命令核对版本nvidia-smi注意nvidia-smi显示的 CUDA Version 是驱动支持的最高版本不代表 PyTorch 实际使用的 CUDA 运行时版本。PyTorch 安装时会在虚拟环境中自带独立的 CUDA 运行时库所以你只需要关心驱动版本是否满足要求即可。CUDA 的具体安装方式以各框架官网说明为准不要照搬博客里的旧命令因为框架版本更新很快版本号应查看官网最新信息。3.2 安装 PyTorch# 以 PyTorch 官方安装命令为例具体命令请访问 pytorch.org 获取 pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118安装完成后验证python -c import torch; print(torch.__version__); print(torch.cuda.is_available())如果输出True说明 GPU 可用。如果这里输出了False不要急着重装可以先看事件查看器Windows或者dmesgLinux中是否识别到 NVIDIA 显卡驱动核心是驱动是否成功加载而不是 PyTorch 本身的问题。很多人在这里卡住其实问题出在显卡驱动没有正确对应 CUDA 版本。3.3 安装 TensorFlowTensorFlow 的安装相对简单CPU 和 GPU 版现在使用同一个包会根据运行时环境自动选择可用设备pip install tensorflow验证python -c import tensorflow as tf; print(tf.__version__); print(tf.config.list_physical_devices(GPU))注意如果这行命令打印出[]并且你使用的是 Windows 系统多半是缺少 NVIDIA 的 cuDNN DLL 文件。可以把 TensorFlow 相关的官方安装指南打开对照一下缺哪些运行库。4. 完整实战项目两个框架跑通 MNIST 图像分类理论看再多不如亲手跑通一个项目。我选择 MNIST 手写数字分类作为实战项目因为数据集小、训练快完整代码量控制在 100 行左右非常适合作为对比学习的载体。4.1 数据准备MNIST 数据集在 PyTorch 中通过torchvision.datasets下载在 TensorFlow 中通过tf.keras.datasets下载。两个框架都内置了该数据集不需要额外准备。不过要注意一点MNIST 数据集的像素值范围是 0-255一般需要归一化到 0-1 区间否则模型很难收敛。两个框架的归一化操作类似下面代码中我会同时给出。4.2 PyTorch 版本完整代码# 文件路径pytorch_mnist.py import torch import torch.nn as nn import torch.optim as optim from torchvision import datasets, transforms from torch.utils.data import DataLoader # 1. 数据处理 transform transforms.Compose([ transforms.ToTensor(), transforms.Normalize((0.1307,), (0.3081,)) ]) train_dataset datasets.MNIST(root./data, trainTrue, downloadTrue, transformtransform) test_dataset datasets.MNIST(root./data, trainFalse, downloadTrue, transformtransform) train_loader DataLoader(train_dataset, batch_size64, shuffleTrue) test_loader DataLoader(test_dataset, batch_size64, shuffleFalse) # 2. 定义网络 class CNN(nn.Module): def __init__(self): super(CNN, self).__init__() self.conv1 nn.Conv2d(1, 32, kernel_size3, stride1, padding1) self.conv2 nn.Conv2d(32, 64, kernel_size3, stride1, padding1) self.pool nn.MaxPool2d(2, 2) self.fc1 nn.Linear(64 * 7 * 7, 128) self.fc2 nn.Linear(128, 10) self.relu nn.ReLU() def forward(self, x): x self.pool(self.relu(self.conv1(x))) x self.pool(self.relu(self.conv2(x))) x x.view(x.size(0), -1) # 展平 x self.relu(self.fc1(x)) x self.fc2(x) return x # 3. 训练配置 device torch.device(cuda if torch.cuda.is_available() else cpu) model CNN().to(device) # 注意CrossEntropyLoss 内部已经包含 Softmax # 所以网络最后一层不需要手动加 Softmax criterion nn.CrossEntropyLoss() optimizer optim.Adam(model.parameters(), lr0.001) # 4. 训练循环 epochs 5 for epoch in range(epochs): running_loss 0.0 for images, labels in train_loader: images, labels images.to(device), labels.to(device) # 清零梯度 optimizer.zero_grad() # 前向传播 outputs model(images) loss criterion(outputs, labels) # 反向传播 loss.backward() # 更新参数 optimizer.step() running_loss loss.item() print(fEpoch [{epoch1}/{epochs}], Loss: {running_loss/len(train_loader):.4f}) # 5. 测试 model.eval() correct 0 total 0 with torch.no_grad(): for images, labels in test_loader: images, labels images.to(device), labels.to(device) outputs model(images) _, predicted torch.max(outputs.data, 1) total labels.size(0) correct (predicted labels).sum().item() print(fTest Accuracy: {100 * correct / total:.2f}%)这段代码的关键在于optimizer.zero_grad()行。很多从 TensorFlow 转学 PyTorch 的新手经常忘记这一行导致梯度在多个 Batch 之间不断累加损失函数出现诡异的波动。PyTorch 在调用loss.backward()时会把梯度累加到参数的.grad属性上如果不清零本次 Batch 的梯度会加上次 Batch 的梯度结果自然就错了。4.3 TensorFlow 版本完整代码# 文件路径tensorflow_mnist.py import tensorflow as tf from tensorflow.keras import layers, models # 1. 加载并归一化数据 (x_train, y_train), (x_test, y_test) tf.keras.datasets.mnist.load_data() # 数据归一化到 [0, 1] x_train x_train.astype(float32) / 255.0 x_test x_test.astype(float32) / 255.0 # 增加通道维度从 (28, 28) 变为 (28, 28, 1) x_train x_train[..., tf.newaxis] x_test x_test[..., tf.newaxis] # 转换为 tf.data.Dataset便于高效训练 train_ds tf.data.Dataset.from_tensor_slices((x_train, y_train)).batch(64).shuffle(10000) test_ds tf.data.Dataset.from_tensor_slices((x_test, y_test)).batch(64) # 2. 构建模型 model models.Sequential([ layers.Conv2D(32, kernel_size(3, 3), activationrelu, input_shape(28, 28, 1)), layers.MaxPooling2D(pool_size(2, 2)), layers.Conv2D(64, kernel_size(3, 3), activationrelu), layers.MaxPooling2D(pool_size(2, 2)), layers.Flatten(), layers.Dense(128, activationrelu), layers.Dense(10, activationsoftmax) ]) # 3. 编译模型 model.compile( optimizeradam, losssparse_categorical_crossentropy, metrics[accuracy] ) # 4. 训练一行代码搞定训练循环 model.fit(train_ds, epochs5) # 5. 测试 test_loss, test_acc model.evaluate(test_ds) print(fTest Accuracy: {test_acc:.4f})如果你之前只接触过 PyTorch看到 TensorFlow 这个版本可能会觉得有点“过于简单”。这是因为model.fit()自动帮你处理了循环、梯度清零、指标统计等一系列细节。但这里有两个容易踩的坑第一sparse_categorical_crossentropy和categorical_crossentropy的区别。如果你把标签做了 One-Hot 编码用categorical_crossentropy如果标签是整数比如 0-9用sparse_categorical_crossentropy。二者选错训练时通常会出现巨大的 Loss 值。第二input_shape必须与数据维度匹配。MNIST 原始数据形状是(28, 28)经过[..., tf.newaxis]后变成(28, 28, 1)所以input_shape(28, 28, 1)这里很容易写错成(28, 28)从而报错。4.4 两个框架的代码风格对比表操作PyTorchTensorFlow定义网络继承nn.Module手写forwardSequential或函数式 API训练循环手动for循环灵活度高model.fit()一行封装梯度清零必须手动optimizer.zero_grad()框架内部自动处理模型切换训练/评估model.train()/model.eval()编译阶段指定无需手动切换Dropout 等层自动处理损失函数nn.CrossEntropyLoss()sparse_categorical_crossentropy设备管理torch.device手动控制GPU 自动调度可通过tf.device手动控制5. 训练效果验证与关键日志分析5.1 运行命令两个脚本分别执行python pytorch_mnist.py python tensorflow_mnist.py5.2 预期输出PyTorch 的预期输出Epoch [1/5], Loss: 0.2124 Epoch [2/5], Loss: 0.0615 Epoch [3/5], Loss: 0.0430 Epoch [4/5], Loss: 0.0316 Epoch [5/5], Loss: 0.0251 Test Accuracy: 98.81%TensorFlow 的预期输出Epoch 1/5 938/938 [] - 3s 3ms/step - loss: 0.1734 - accuracy: 0.9487 Epoch 2/5 938/938 [] - 1s 2ms/step - loss: 0.0513 - accuracy: 0.9840 Epoch 3/5 938/938 [] - 1s 2ms/step - loss: 0.0368 - accuracy: 0.9884 Epoch 4/5 938/938 [] - 1s 2ms/step - loss: 0.0277 - accuracy: 0.9913 Epoch 5/5 938/938 [] - 1s 2ms/step - loss: 0.0210 - accuracy: 0.9933 Test Accuracy: 0.9910注意这个准确率结果取决于框架内部权重初始化方式不同框架的值可能略有差异但最终都会收敛到 98% 以上。如果准确率只有 90% 甚至更低说明训练可能有问题。5.3 如何判断训练是否正常一个最简单的判断方法是看 Loss 的变化趋势。正常情况下 Loss 应该持续下降不会有突然的暴涨。如果用 TensorBoard 看到的是一条“锯齿状”明显下行的曲线说明学习率偏大但尚可接受如果 Loss 直接发散到 NaN说明学习率太大或者数据预处理出了问题。由于篇幅有限我没有引入 TensorBoard 可视化的代码。但强烈建议你自己去尝试PyTorch 使用torch.utils.tensorboard.SummaryWriterTensorFlow 使用tf.keras.callbacks.TensorBoard。可视化 Loss 曲线是理解和优化深度学习模型最直观的手段。6. 常见问题与排查思路这里整理了几组我见过的高频问题如果你在跑项目时遇到类似的可以直接对照排查。问题现象可能原因排查方式解决方案torch.cuda.is_available()返回 False显卡驱动未正确安装或者 PyTorch 版本与 CUDA 不匹配运行nvidia-smi查看驱动版本更新显卡驱动重新安装匹配的 PyTorch CUDA 版本TensorFlow 无法识别 GPU缺少 cuDNN DLL 文件tf.config.list_physical_devices(GPU)输出空列表按 TensorFlow 官方要求配置 CUDA 与 cuDNN 运行库训练 Loss 不降反升学习率过大数据集未归一化打印每个 Batch 的 Loss 观察趋势降低学习率检查数据归一化和标签格式PyTorch 每次反向传播后 Loss 异常忘记optimizer.zero_grad()查看训练代码循环体在loss.backward()前调用optimizer.zero_grad()reshape或view报维度错误输入数据形状与网络期望不匹配打印张量.shape在进入全连接层前使用展平操作内存溢出OOMBatch Size 过大或输入图像尺寸过大查看显存占用减小 Batch Size 或输入分辨率PyTorch 2.6 加载旧模型.pth报错新版本中weights_only参数默认值发生变化查看报错信息中的torch.load调用处在加载时显式指定weights_onlyFalse但仅限可信来源的模型文件这里特别提醒一个安全问题关于 PyTorch 2.6 之后torch.load的weights_only默认值调整。老代码加载torch.load(model.pth)可能直接报错新版要求你显式声明weights_onlyTrue或weights_onlyFalse。从安全角度讲如果模型文件来自互联网不可信来源强烈建议保留weights_onlyTrue因为加载不可信 pickle 文件存在任意代码执行风险。这是 PyTorch 官方为了默认安全而做的调整新项目的代码最好从一开始就按要求传入weights_only参数。7. 从 Demo 到简历如何把框架项目写进简历很多同学跑完一个 MNIST 项目后不知道下一步怎么打磨写进简历又显得特别单薄。下面用一个真实的“简历描述升级”来演示这个思考过程。7.1 初级写法不建议熟悉 PyTorch 和 TensorFlow能使用两种框架搭建卷积神经网络完成 MNIST 手写数字识别任务。这段文字的问题在于它描述的是“别人布置的练习”不是“我解决过的问题”。面试官一眼就能看出你没有独立思考。7.2 升级写法可参考使用 PyTorch 和 TensorFlow 分别实现 CNN 图像分类模型在 MNIST 数据集上达到 98% 准确率 通过对比动态图与静态图训练与部署流程深入理解自动求导、反向传播、Batch 归一化等深度学习底层机制。是不是感觉信息量立刻上来了这里的关键是写简历时不要停留在“我会用 API”要写成“我通过某个项目弄懂了某个原理”。7.3 进阶写法适合有量化的场景如果你想更有竞争力可以进一步加入可量化的业务逻辑基于 PyTorch 设计并优化轻量化图像分类网络通过 BN 层与数据增强将 Top-1 准确率提升至 99.1% 将模型导出为 ONNX使用 TensorRT 完成推理加速单张推理耗时从 8ms 降至 3ms数据需以你实际实验为准。但这里的“数据需以你实际实验为准”绝不是空话因为你写的每一个数字在面试中都可能被追问评估方法。宁可使用一个保守但真实的 95%也不要编造一个惊艳的 99%。7.4 “了解”和“掌握”之间的界线简历上写“了解 TensorFlow”意味着你至少跑通过一个模型写“熟练使用 TensorFlow”意味着你能解决部署环境问题写“精通 PyTorch”意味着你理解数据加载、模型保存与加载、混合精度训练、分布式数据并行等细节差异。建议不要轻易在简历上写“精通”。因为越资深的面试官越喜欢从“精通”二字开始深挖如果你答不上底层原理反而暴露出基础不扎实的问题。8. 最佳实践与工程建议8.1 版本管理是第一个工程问题深度学习框架版本更新非常快。PyTorch 1.x 到 2.x 之间torch.compile引入了显著的性能提升TensorFlow 2.x 各小版本的 API 变动也很频繁。建议在项目根目录使用requirements.txt或pyproject.toml锁定依赖版本避免同事或未来的你打开项目时“环境跑不起来”。# requirements.txt示例 torch2.5.1 torchvision0.20.1 tensorflow2.18.0 numpy1.26.4如果没有这些版本请以官网当前稳定版为准不要照抄。8.2 模型保存与加载最容易忽略的坑PyTorch 默认保存的是模型的state_dict参数字典而不是完整网络结构。加载时你必须先重新定义一个同样的网络结构再执行model.load_state_dict(torch.load(model.pth))。如果你把整个模型torch.save(model, model.pth)保存跨 Python 版本或跨 PyTorch 小版本加载时很容易出现兼容性问题。TensorFlow 推荐使用SavedModel格式保存自带网络结构和tf.function图信息跨环境兼容性好。生产环境中我见过大量“本地跑得很好、上线就崩”的案例多半都是因为只保存了权重而没有保存完整的预处理流程与模型定义。8.3 统一管理随机种子为了让实验可复现在所有涉及随机性的位置设置种子import random import numpy as np import torch def set_seed(seed42): random.seed(seed) np.random.seed(seed) torch.manual_seed(seed) torch.cuda.manual_seed_all(seed) # 启用 cudnn benchmark 会影响可复现性视需求决定是否关闭 torch.backends.cudnn.deterministic TrueTensorFlow 的设置方式类似import tensorflow as tf tf.random.set_seed(42)8.4 数据处理要“进 Pipeline”不要每次手动读文件PyTorch 中使用Dataset和DataLoader相结合TensorFlow 中使用tf.data。两个框架的 Pipeline 都能实现数据预取、乱序、批量加载、多进程并行。如果只是一个小 Demo 可以直接从内存中读数据但一旦进入真实项目数据读取往往成为训练瓶颈。8.5 训练中保持验证集的独立性不要用测试集去做模型调参。正确的流程是训练集训练验证集选择超参数和判断是否过拟合测试集只在最终评估时使用一次。很多入门项目把所有数据合并后一起训练这样得到的准确率在真实场景中会大打折扣。9. 总结与后续学习方向从架构对比到代码实战这篇文章的核心信息可以浓缩为三句话第一PyTorch 的灵活与动态图机制决定了它更适合入门与研究TensorFlow 的静态图与生产配套决定了它在部署领域仍有重要地位。第二两个框架的 API 差异性并不是你需要纠结的主线真正重要的是理解梯度下降、反向传播、卷积、池化这些共通的深度学习核心知识点。第三简历上的项目不是简单跑通 Demo你要能够说清楚每一步操作背后的原因。如果你还要继续往深度学习方向深耕建议按照下面的顺序往下走利用 PyTorch 完整跑通一个更贴近业务的视觉项目比如基于 CIFAR-10 或者自定义数据集的分类任务。学习如何把训练好的 PyTorch 模型导出为 ONNX再转换到 TensorRT 进行推理加速这条路径非常贴近工业界真实用例。尝试用 TensorFlow 的tf.function把一个简单模型改成 Graph 模式体会静态图执行的性能差异和调试难度这时候你才真正理解了“为什么 TensorFlow 在部署场景依然有价值”。如果目标是 AI 算法岗接着学习 Transformers 原理与完整微调流程如果目标是平台工程岗重点关注 CUDA 环境、模型推理服务和分布式训练框架。最后给一个务实建议不要在一个环境里同时装两个框架做生产项目。深度学习依赖链复杂版本冲突会消耗你大量时间。建立多个独立虚拟环境分别用于研究、部署和测试是多数团队的标准做法个人学习也应当养成同样的习惯。