2026 年了,还有不少刚接触深度学习的朋友在微信、知乎、CSDN 私信里问我同一个问题:“我想入门深度学习,到底学 TensorFlow 还是 PyTorch?” 说实话,这个问题放在五年前确实很纠结,那时候 TensorFlow 背靠 Google,生态完善,PyTorch 虽然好用但总让人觉得“不够工业级”。但放到 2026 年再来看,这个问题的答案已经越来越清晰。
如果你正打算系统入门深度学习,或者已经在环境配置、框架选择的泥潭里挣扎了几天,这篇文章就是为你准备的。我会从框架选型、环境搭建、核心语法,再到一个完整的实战项目,带你用最短的时间跑通 PyTorch 的核心流程。
文章会涉及深度学习框架对比、PyTorch 基础语法、数据加载、模型训练、GPU 配置、常见报错排查,以及工程化的最佳实践。无论你是学生、转行开发者,还是工作中需要落地 AI 能力的工程师,都能从中找到可以直接复用的内容。
1. 先搞清楚:TensorFlow 和 PyTorch 到底在争什么
很多零基础的朋友一开始就有个误区,觉得深度学习框架是所有 AI 的根本,选错了就完了。其实没那么严重,但选择确实会影响你的学习曲线和开发效率。
1.1 两个框架的本质区别
从工程实现来看,TensorFlow 和 PyTorch 都做同一件事:构建计算图,自动求梯度,然后通过反向传播更新模型的参数。但它们的“设计哲学”不一样。
早年的 TensorFlow 1.x 采用“静态图”模式。你写代码的时候,先定义好一张完整的计算流程图,然后把它丢给一个会话去执行。这种设计的好处是性能优化空间大,适合大规模分布式训练和线上部署;坏处就是调试非常反人类,你不能像写普通 Python 那样在中间打印变量、打断点,哪怕只是看一个张量的值也要费不少功夫。
PyTorch 从诞生起就走“动态图”路线,它把计算图的构建和执行过程合二为一。你在写 PyTorch 代码的时候,实际上就是在写普通的 Python 代码,每一行的张量运算都会立即执行,可以随时打印中间结果,可以随心所欲地用 if、for 这些 Python 原生语法控制流程。这让调试体验有了质的飞跃,也是它能在研究社区迅速火起来的根本原因。
后来 TensorFlow 2.x 也引入了 Eager Execution(动态图机制),学习 PyTorch 的交互模式,也默认开启动态图。但从社区习惯、教程质量和周边工具链来看,PyTorch 已经建立了相当稳固的优势。
1.2 当前的真实生态格局
在 2026 年这个时间点,我们来看两个框架的实际处境:
- PyTorch:已经成为学术界、科研机构和 AI 竞赛的主流选择。绝大多数前沿模型的开源实现,比如各种大语言模型(LLM)、视觉 Transformer(ViT)、扩散模型(Diffusion Model),首选都是 PyTorch。Hugging Face Transformers 这类重要的模型库,底层也主要基于 PyTorch。
- TensorFlow:在企业级生产环境、移动端和嵌入式部署,尤其是 Google 系产品和一些老牌工业项目里,仍然有大量存量系统。TensorFlow Serving、TensorFlow Lite 这些部署工具链在特定场景下依然能打。
- JAX:谷歌力推的新一代框架,在科研领域有一定份额,尤其在某些高性能计算场景。但它的学习曲线更陡,生态圈也远不如前两者成熟,新手入门不建议直接踩进去。
如果你是入门新手,当前更务实的建议是:把 PyTorch 作为主学框架。你学的张量操作、自动求导、网络层设计、训练流程,这些底层思维换到 TensorFlow 或 JAX 上依然适用,并不会白学。等到需要做生产部署或特定平台的模型转换时,再针对性补充 TensorFlow 的知识也不迟。
| 对比维度 | PyTorch | TensorFlow |
|---|---|---|
| 计算图模式 | 动态图为主,调试直观 | 动态图为主,兼容静态图部署 |
| 学习曲线 | 平缓,接近原生 Python | 相对陡峭,概念较多 |
| 研究/论文实现 | 绝对主流 | 比例较低 |
| 工业部署工具链 | TorchServe、ONNX | TensorFlow Serving、Lite |
| 社区活跃度 | 极高,教程丰富 | 稳定但热度下降 |
| 适合人群 | 新手入门、科研、快速原型 | 企业存量项目、特定部署场景 |
1.3 选框架时到底在看什么
很多新人容易陷入“性能对比”的焦虑,觉得选一个跑分更高的就赢了。实际上对入门者来说,以下几个因素远比跑分重要:
- 调试体验:能不能直观地看到中间变量的值,能不能用 Python 原生的方式写流程控制。PyTorch 在这点的优势是碾压性的。
- 教程质量和社区解答:你遇到报错时,能不能快速搜到解决方案。PyTorch 的活跃社区保证了你踩过的坑,大概率已经有人填过。
- 生态匹配度:你想做的方向,比如 NLP、CV、语音,主流开源代码是用哪个框架写的。跟主流走,你才能站在前人的肩膀上。
- 学习资料的系统性:有没有一套从入门到实战的完整路径。这个方面,PyTorch 的官方文档和社区教程覆盖得已经很全面。
一句话总结:选框架不是选信仰,而是选效率和生态。对于 2026 年入门的你,PyTorch 是综合成本最低、回报最高的选择。
2. 环境准备:把 PyTorch 跑起来
确定了方向之后,第一步就是把环境搭好。很多新手在这一步就被劝退了,所以我尽量把常见问题讲清楚。
2.1 Python 环境与虚拟环境工具
深度学习开发绕不开 Python,但我不建议你直接在系统全局环境里安装各种包,那样很容易把环境搞乱。推荐使用虚拟环境隔离项目依赖。
常见的选择有:
- venv:Python 3.3+ 内置的虚拟环境工具,轻量简单,适合单项目隔离。
- conda:Anaconda 或 Miniconda 自带的包管理与虚拟环境工具,在处理 CUDA 相关依赖时更省心,Windows 和 Linux 都适用。
- virtualenv:Python 社区经典的虚拟环境工具,功能与 venv 类似。
我的建议是:使用 conda 管理环境,使用 pip 安装 PyTorch 相关依赖。因为 PyTorch 的安装命令在 pip 下更直观,而 conda 能帮你处理好 Python 版本和部分底层库的冲突。
创建并激活虚拟环境:
# 创建 Python 3.10 环境 conda create -n pytorch_env python=3.10 # 激活环境 conda activate pytorch_env如果你更习惯 venv,也可以用:
# 创建虚拟环境 python -m venv pytorch_env # Windows 激活 pytorch_env\Scripts\activate # Linux/macOS 激活 source pytorch_env/bin/activate2.2 CPU 版本与 GPU 版本的安装
PyTorch 的安装分为 CPU 版和 GPU 版两种。如果你只是学习语法、跑一些小型网络,CPU 版本也够用;但如果要训练稍大规模的模型,还是建议用 GPU 加速。
先检查你的显卡:
# Windows 查看显卡 nvidia-smi如果nvidia-smi没有输出,说明你没装 NVIDIA 驱动或者没有 NVIDIA 显卡。这时候只能安装 CPU 版本。
2026 年 PyTorch 的安装方式已经比早年简单很多,直接去 PyTorch 官网选择对应的环境配置,就会生成安装命令。以当前较常见的稳定版本为例:
# CPU 版本安装 pip install torch torchvision torchaudio # CUDA 12.x 版本安装(需要本机已安装对应版本驱动) pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121需要注意的一点是,不要只看 CUDA 的版本号就盲目安装。PyTorch 的 CUDA 版本要求的是驱动兼容性,不是说你系统装了什么 CUDA Toolkit 就一定要选什么版本。通常来说,只要 NVIDIA 驱动足够新,nvidia-smi显示的 CUDA 版本高于 PyTorch 所需的版本,就能正常运行。
安装完成后,一定要验证是否能够正确调用 GPU:
import torch # 打印 PyTorch 版本 print("PyTorch 版本:", torch.__version__) # 检查 CUDA 是否可用 print("CUDA 是否可用:", torch.cuda.is_available()) # 如果可用,打印 GPU 名称 if torch.cuda.is_available(): print("GPU 名称:", torch.cuda.get_device_name(0))如果你看到类似输出:
PyTorch 版本: 2.5.1+cu121 CUDA 是否可用: True GPU 名称: NVIDIA GeForce RTX 4060 Laptop GPU说明你的环境和显卡已经就绪。如果 CUDA 可用性为 False,建议检查驱动版本,或者确认安装的是不是 GPU 版本。
2.3 开发工具推荐
写深度学习代码,IDE 的选择因人而异。我常用的组合是:
- PyCharm Professional:对 PyTorch 项目结构支持很好,远程服务器调试也方便。
- VS Code + Python 插件:轻量、免费,配合 Jupyter Notebook 扩展,非常适合做快速实验。
- Jupyter Notebook / JupyterLab:交互式验证模型和数据可视化很好用,学习阶段推荐。
入门阶段,我更推荐 VS Code + Jupyter Notebook 的组合,既能写脚本,又能分块运行代码,非常舒服。
3. PyTorch 核心概念与基础语法
环境搭好之后,我们正式进入 PyTorch 的语法学习。这里不会展开长篇大论,而是带你把核心概念捋一遍,后面实战项目中会用到。
3.1 Tensor:PyTorch 的基本数据结构
Tensor(张量)是 PyTorch 中最核心的数据结构,可以理解成多维数组。它和 NumPy 的 ndarray 很像,但额外支持自动求导和 GPU 加速。
创建张量的几种方式:
import torch # 从列表创建 a = torch.tensor([1, 2, 3]) print(a) # 创建全零张量 b = torch.zeros(2, 3) print(b) # 创建全一张量 c = torch.ones(2, 3) print(c) # 创建随机张量 d = torch.randn(2, 3) print(d) # 从 NumPy 数组创建 import numpy as np np_array = np.array([[1, 2], [3, 4]]) tensor_from_np = torch.from_numpy(np_array) print(tensor_from_np)张量与 NumPy 数组可以高效转换:
# Tensor 转 NumPy tensor_a = torch.tensor([1.0, 2.0, 3.0]) numpy_b = tensor_a.numpy() print(numpy_b) # NumPy 转 Tensor numpy_c = np.array([4.0, 5.0, 6.0]) tensor_d = torch.from_numpy(numpy_c) print(tensor_d)张量支持各种数学运算:
x = torch.tensor([1.0, 2.0, 3.0]) y = torch.tensor([4.0, 5.0, 6.0]) # 加法 print("x + y:", x + y) # 乘法(逐元素) print("x * y:", x * y) # 矩阵乘法 matrix_a = torch.randn(2, 3) matrix_b = torch.randn(3, 4) result = torch.mm(matrix_a, matrix_b) print("矩阵乘法结果 shape:", result.shape)3.2 自动求导机制(Autograd)
PyTorch 最大的亮点之一就是autograd自动求导模块。你不需要手动推导梯度公式,只要在前向传播过程中让张量带上梯度信息,PyTorch 就能自动计算所有参数的梯度。
核心用法是设置requires_grad=True:
import torch # 创建一个需要梯度的张量 x = torch.tensor(3.0, requires_grad=True) print("x:", x) # 定义一个函数 y = x^2 y = x ** 2 # 反向传播,计算 dy/dx y.backward() # 查看梯度,dy/dx = 2x = 6 print("梯度:", x.grad)在实际模型训练中,backward()是在计算损失之后调用的。PyTorch 会从损失张量出发,沿着计算图往回走,给所有requires_grad=True的参数填充.grad值。
一个更接近真实场景的示例:
import torch import torch.nn as nn # 定义一个简单的线性层 linear_layer = nn.Linear(in_features=4, out_features=1) # 创建输入数据 input_tensor = torch.randn(8, 4) # 前向传播 output = linear_layer(input_tensor) # 模拟一个损失值 target = torch.randn(8, 1) loss = ((output - target) ** 2).mean() # 反向传播 loss.backward() # 查看线性层的权重梯度 print("权重形状:", linear_layer.weight.shape) print("权重梯度:", linear_layer.weight.grad)3.3 torch.nn:搭建网络的基石
torch.nn模块提供了构建神经网络所需的各种层、激活函数、损失函数和容器类。
最常见的模型定义方式是通过继承nn.Module:
import torch import torch.nn as nn class SimpleNN(nn.Module): def __init__(self): super(SimpleNN, self).__init__() self.fc1 = nn.Linear(784, 128) self.relu = nn.ReLU() self.fc2 = nn.Linear(128, 10) def forward(self, x): x = self.fc1(x) x = self.relu(x) x = self.fc2(x) return x # 实例化模型 model = SimpleNN() print(model)这里需要强调的是,forward方法定义了前向传播的逻辑。PyTorch 会通过 Python 符号链接机制自动绑定反向传播,不需要你手动编写。
nn.Sequential是另一种更简洁的定义方式,适合堆叠简单层:
model = nn.Sequential( nn.Linear(784, 128), nn.ReLU(), nn.Linear(128, 10) )3.4 Dataset 与 Dataloader:数据管理规范
在深度学习中,数据不是一次性塞给模型的,而是要分批、随机打乱、多线程读取。PyTorch 用Dataset和DataLoader两个类来管理数据流程。
Dataset负责定义“如何读取一条数据”,DataLoader负责把多条数据打包成批量(batch)、打乱顺序(shuffle)以及多进程加载(num_workers)。
一个自定义 Dataset 的典型写法:
import torch from torch.utils.data import Dataset, DataLoader class MyDataset(Dataset): def __init__(self, data_list, label_list): self.data_list = data_list self.label_list = label_list def __len__(self): return len(self.data_list) def __getitem__(self, idx): x = self.data_list[idx] y = self.label_list[idx] return x, y # 模拟数据 data_list = [torch.randn(784) for _ in range(1000)] label_list = [torch.randint(0, 10, (1,)).item() for _ in range(1000)] # 构造数据集 dataset = MyDataset(data_list, label_list) dataloader = DataLoader(dataset, batch_size=32, shuffle=True, num_workers=0) # 遍历一个批次 for batch_x, batch_y in dataloader: print("batch_x shape:", batch_x.shape) print("batch_y shape:", batch_y.shape) break3.5 训练流程的通用模板
不管模型多复杂,训练流程都遵循同一个模板:
- 定义模型结构。
- 定义损失函数(如交叉熵
nn.CrossEntropyLoss、均方误差nn.MSELoss)。 - 定义优化器(如
optim.SGD、optim.Adam)。 - 循环迭代数据:
- 前向传播得输出。
- 计算损失。
- 梯度清零。
- 反向传播。
- 优化器更新参数。
一个极简的训练循环:
import torch import torch.nn as nn import torch.optim as optim # 定义模型、损失函数、优化器 model = nn.Linear(4, 1) criterion = nn.MSELoss() optimizer = optim.SGD(model.parameters(), lr=0.01) # 模拟数据 inputs = torch.randn(100, 4) targets = torch.randn(100, 1) # 训练 20 个 epoch for epoch in range(20): # 前向传播 outputs = model(inputs) # 计算损失 loss = criterion(outputs, targets) # 梯度清零,防止累积 optimizer.zero_grad() # 反向传播 loss.backward() # 更新参数 optimizer.step() if (epoch + 1) % 5 == 0: print(f'Epoch [{epoch+1}/20], Loss: {loss.item():.4f}')4. 2 小时速通实战:手写数字识别
理论铺垫完成,我们开始一个经典但完整的入门项目:MNIST 手写数字识别。这个项目麻雀虽小五脏俱全,可以完整覆盖数据加载、模型定义、训练、评估和保存加载的全部流程。
4.1 项目结构与准备
创建项目文件夹:
mnist_pytorch/ ├── train.py ├── test.py └── models/先确保你已经安装了torchvision,它能简化数据集的下载和预处理。安装命令:
pip install torchvision4.2 数据加载与预处理
文件:train.py
import torch import torch.nn as nn import torch.optim as optim import torchvision import torchvision.transforms as transforms from torch.utils.data import DataLoader # 定义数据预处理:将 PIL 图像转为 Tensor,并归一化到 [0, 1] transform = transforms.Compose([ transforms.ToTensor(), transforms.Normalize((0.1307,), (0.3081,)) ]) # 加载训练集 train_dataset = torchvision.datasets.MNIST( root='./data', train=True, transform=transform, download=True ) # 加载测试集 test_dataset = torchvision.datasets.MNIST( root='./data', train=False, transform=transform, download=True ) # 创建 DataLoader train_loader = DataLoader(train_dataset, batch_size=64, shuffle=True, num_workers=0) test_loader = DataLoader(test_dataset, batch_size=64, shuffle=False, num_workers=0)MNIST 数据集的每张图片是 28x28 的灰度图,像素范围是 0 到 255。ToTensor()会把像素值除以 255 归一化到 [0, 1],然后Normalize((0.1307,), (0.3081,))使用数据集的全局均值和标准差做标准化,加速模型收敛。
4.3 定义模型
手写数字识别任务相对简单,我们用两层全连接网络即可达到不错的精度。输入是一张展平后的 28x28=784 维向量,输出是 10 个类别的得分。
class MNISTNet(nn.Module): def __init__(self): super(MNISTNet, self).__init__() self.fc1 = nn.Linear(784, 256) self.relu1 = nn.ReLU() self.fc2 = nn.Linear(256, 128) self.relu2 = nn.ReLU() self.fc3 = nn.Linear(128, 10) def forward(self, x): # 输入形状: [batch_size, 1, 28, 28] # 展平为 [batch_size, 784] x = x.view(x.size(0), -1) x = self.fc1(x) x = self.relu1(x) x = self.fc2(x) x = self.relu2(x) x = self.fc3(x) return x这里要注意x.view(x.size(0), -1)的作用是把 4 维图像张量变为 2 维矩阵,因为全连接层只接受二维输入。
4.4 训练函数
def train_one_epoch(model, train_loader, criterion, optimizer, device, epoch): model.train() running_loss = 0.0 correct = 0 total = 0 for batch_idx, (data, target) in enumerate(train_loader): data, target = data.to(device), target.to(device) # 清空历史梯度 optimizer.zero_grad() # 前向传播 output = model(data) # 计算损失 loss = criterion(output, target) # 反向传播和优化 loss.backward() optimizer.step() # 统计损失和准确率 running_loss += loss.item() _, predicted = torch.max(output.data, 1) total += target.size(0) correct += (predicted == target).sum().item() if (batch_idx + 1) % 200 == 0: print(f'Epoch [{epoch}], Step [{batch_idx + 1}/{len(train_loader)}], ' f'Loss: {loss.item():.4f}') avg_loss = running_loss / len(train_loader) accuracy = 100.0 * correct / total print(f'Epoch [{epoch}] 训练损失: {avg_loss:.4f}, 训练准确率: {accuracy:.2f}%') return avg_loss, accuracy这行代码需要特别解释:
_, predicted = torch.max(output.data, 1)output的形状是[batch_size, 10],每一行是模型对 10 个数字类别的得分。torch.max在维度 1 上找最大值,返回两个值:最大值和对应的索引。我们不太关心最大值本身,只关心索引,也就是模型预测的类别,所以用_丢弃第一个返回值。
4.5 评估函数
def evaluate(model, test_loader, criterion, device): model.eval() test_loss = 0.0 correct = 0 total = 0 with torch.no_grad(): for data, target in test_loader: data, target = data.to(device), target.to(device) output = model(data) loss = criterion(output, target) test_loss += loss.item() _, predicted = torch.max(output.data, 1) total += target.size(0) correct += (predicted == target).sum().item() avg_loss = test_loss / len(test_loader) accuracy = 100.0 * correct / total print(f'测试集结果 - 平均损失: {avg_loss:.4f}, 准确率: {accuracy:.2f}%') return avg_loss, accuracy注意model.eval()和torch.no_grad()的区别。model.eval()是告诉模型切换到评估模式,影响 dropout 和 batch normalization 的行为;torch.no_grad()是告诉自动求导引擎不要构建计算图,节省内存和计算时间。两者通常配合使用,但作用不同。
4.6 主函数与完整代码
def main(): # 可选:设置设备 device = torch.device("cuda" if torch.cuda.is_available() else "cpu") print(f"使用设备: {device}") # 加载数据 transform = transforms.Compose([ transforms.ToTensor(), transforms.Normalize((0.1307,), (0.3081,)) ]) train_dataset = torchvision.datasets.MNIST( root='./data', train=True, transform=transform, download=True ) test_dataset = torchvision.datasets.MNIST( root='./data', train=False, transform=transform, download=True ) train_loader = DataLoader(train_dataset, batch_size=64, shuffle=True, num_workers=0) test_loader = DataLoader(test_dataset, batch_size=64, shuffle=False, num_workers=0) # 初始化模型、损失函数、优化器 model = MNISTNet().to(device) criterion = nn.CrossEntropyLoss() optimizer = optim.Adam(model.parameters(), lr=0.001) # 训练多个 epoch num_epochs = 5 best_accuracy = 0.0 for epoch in range(1, num_epochs + 1): print(f"\n========== Epoch {epoch} ==========") train_one_epoch(model, train_loader, criterion, optimizer, device, epoch) test_loss, test_accuracy = evaluate(model, test_loader, criterion, device) # 保存最佳模型 if test_accuracy > best_accuracy: best_accuracy = test_accuracy torch.save(model.state_dict(), 'best_model.pth') print("已保存最佳模型") print(f"\n训练完成!最佳测试准确率: {best_accuracy:.2f}%") if __name__ == "__main__": main()完整代码整合之后,在终端运行:
python train.py首次运行需要下载 MNIST 数据集,同时输出类似的训练日志:
使用设备: cuda ========== Epoch 1 ========== Epoch [1], Step [200/938], Loss: 0.3521 Epoch [1], Step [400/938], Loss: 0.2917 ... Epoch [5] 训练损失: 0.0582, 训练准确率: 98.12% 测试集结果 - 平均损失: 0.0718, 准确率: 97.65%4.7 模型预测与保存
训练完模型之后,单张图片的预测逻辑如下:
# 加载保存的模型参数 model = MNISTNet() model.load_state_dict(torch.load('best_model.pth')) model.eval() # 假设你有一张图片,预处理成和训练数据一样的格式 # 这里生成一张随机的 28x28 图像作为演示 import torchvision.transforms as transforms from PIL import Image import numpy as np def predict_image(image_path, model): # 读取图片并转为灰度 image = Image.open(image_path).convert('L') # 缩放到 28x28 image = image.resize((28, 28)) # 数据预处理 transform = transforms.Compose([ transforms.ToTensor(), transforms.Normalize((0.1307,), (0.3081,)) ]) input_tensor = transform(image) # 添加 batch 维度 [1, 1, 28, 28] input_tensor = input_tensor.unsqueeze(0) # 预测 with torch.no_grad(): output = model(input_tensor) _, predicted = torch.max(output, 1) return predicted.item() # 示例用法 # result = predict_image('test_digit.png', model) # print(f"预测结果: {result}")模型保存和加载,需要强调一个容易踩的坑。torch.save(model.state_dict(), ...)只保存参数,不保存网络结构,下次使用必须重新实例化模型类。另一种方式是torch.save(model, ...)保存整个模型,但这种方式在不同版本 Python/PyTorch 之间兼容性较差,不推荐用于生产。
5. 常见问题与排查思路
所有新手在跑深度学习代码时,都会遇到五花八门的报错。这里整理几个高频问题,并按排查优先级给出思路。
| 问题现象 | 常见原因 | 解决思路 |
|---|---|---|
CUDA out of memory | batch_size 设置过大,显存被占满 | 调小 batch_size,或使用torch.cuda.empty_cache()释放缓存 |
RuntimeError: Expected all tensors to be on the same device | 输入数据和模型不在同一个设备上 | 用.to(device)统一迁移模型和数据的设备 |
AttributeError: 'NoneType' object has no attribute shape | 输入数据为空,或图片路径错误 | 检查数据集路径、图片读取逻辑 |
| 训练损失不下降 | 学习率设置不当、数据没有归一化、模型结构有问题 | 尝试调低/调高学习率,检查数据预处理,先在小数据集上 debug |
| 加载模型时报 key 不一致错误 | 保存的模型参数和当前模型类定义不一致 | 检查模型类名、层名称是否一致 |
DataLoader worker报错 | num_workers 设置过大或 Windows 多进程问题 | 在 Windows 下把 num_workers 设为 0,或放到if __name__ == '__main__'中 |
5.1 CUDA out of memory 排查清单
这个错误非常高频,我单独说一下排查步骤:
- 检查当前显存占用:
nvidia-smi可以看到实时显存占用。 - 调小
batch_size,例如从 64 改为 32 或 16。 - 确保训练循环里有
optimizer.zero_grad(),否则梯度会在计算图中累积。 - 如果模型很大,考虑使用梯度累积、混合精度训练等进阶技巧。
- 使用
torch.cuda.empty_cache()清理未使用的缓存,但这只是应急手段,长期还是应该优化资源分配。
5.2 安装 PyTorch 后 CUDA 不可用
如果你安装的是 GPU 版本,但torch.cuda.is_available()返回 False,按以下顺序排查:
- 确认显卡驱动是否安装成功,运行
nvidia-smi。 - 确认安装的 PyTorch 版本匹配的 CUDA 版本是否不高于驱动支持的版本。
- 检查是否安装的是 CPU 版本,可以用
pip list | findstr torch查看版本号中是否包含+cu标识。 - 试着重新安装指定 CUDA 版本的 PyTorch。
# 查看 PyTorch 安装信息 python -c "import torch; print('PyTorch', torch.__version__)"6. 最佳实践与工程建议
跑通一个 MNIST 是第一步,真正在项目里落地深度学习,还需要注意很多工程细节。这里分享几条重要的实践建议。
6.1 数据与设备管理
- 设备判断统一封装:不要在代码里到处写
cuda,封装一个函数统一返回设备,便于切换。
def get_device(): return torch.device("cuda" if torch.cuda.is_available() else "cpu")- 数据打乱要慎重:训练集必须
shuffle=True,避免模型学到数据排列顺序;测试集通常shuffle=False,保证评估一致性和可复现性。 - 数据归一化是必须项:不要以为模型能自动适应原始数据范围。不同尺度特征会让梯度更新不稳定,收敛速度变慢甚至不收敛。
6.2 训练过程管理
- 设置随机种子:为了让实验结果可复现,在程序开头设置固定种子。
import random import numpy as np import torch def set_seed(seed=42): random.seed(seed) np.random.seed(seed) torch.manual_seed(seed) if torch.cuda.is_available(): torch.cuda.manual_seed_all(seed)- 定期保存模型检查点:不要只保存最后一个 epoch 的模型,最好每个 epoch 保存一次,文件名包含 epoch 和准确率。这样如果训练中断或过拟合,可以回退到之前的版本。
- 使用 TensorBoard 可视化:记录 loss、准确率和梯度分布,有助于系统性分析训练过程。
from torch.utils.tensorboard import SummaryWriter writer = SummaryWriter('runs/mnist_experiment') # 在训练循环中记录 # writer.add_scalar('Loss/train', loss.item(), epoch) # writer.add_scalar('Accuracy/test', accuracy, epoch) # 训练结束 # writer.close()6.3 安全边界与生产部署
- 不要在生产环境直接跑网上复制的训练脚本:先检查数据来源、路径、是否有后门代码。
- 模型部署前要做安全评估:尤其是涉及用户数据的场景,要确认模型经过了合法数据授权和脱敏处理。
- 生产环境优先使用 ONNX 或 TensorRT 导出:PyTorch 模型直接部署时依赖 Python 环境,性能不是最优。ONNX(Open Neural Network Exchange)是通用的模型交换格式,可以导出后在多种推理引擎上运行。
import torch # 导出为 ONNX dummy_input = torch.randn(1, 1, 28, 28) model = MNISTNet() torch.onnx.export( model, dummy_input, "mnist_model.onnx", input_names=["input"], output_names=["output"], dynamic_axes={"input": {0: "batch_size"}, "output": {0: "batch_size"}} )- 权限与最小化原则:在云端训练或部署时,尽量使用权限受限的账号,避免使用 root 权限运行训练任务。这能降低被恶意代码攻击的风险。
6.4 模型性能优化方向
当你的模型在训练集上准确率很好,但在测试集上表现一般时,大概率是过拟合。常见优化手段包括:
- 数据增强:对图像做随机旋转、裁剪、翻转,扩大训练集的有效多样性。
- 正则化:在损失函数里加 L2 正则(weight_decay),或者使用 Dropout 层。
- 早停(Early Stopping):观察验证集 loss,连续若干 epoch 不下降就停止训练。
- 学习率调度:训练过程中动态调整学习率,比如前几个 epoch 用大学习率快速收敛,后期用小学习率精细调整。
6.5 代码可维护性建议
在实际项目中,不建议把所有代码堆在一个脚本里。可以参考以下结构:
project/ ├── config.py # 配置参数集中管理 ├── data/ │ ├── dataset.py # Dataset 定义 │ └── transforms.py # 数据预处理 ├── models/ │ ├── net.py # 网络结构定义 │ └── __init__.py ├── utils/ │ ├── trainer.py # 训练函数 │ ├── evaluator.py # 评估函数 │ └── logger.py # 日志管理 ├── train.py # 训练入口 └── inference.py # 推理入口配置参数也尽量用配置文件集中管理,不要到处硬编码:
# config.yaml data: path: ./data batch_size: 64 num_workers: 4 model: hidden_size: 256 num_classes: 10 training: epochs: 20 learning_rate: 0.001 weight_decay: 0.00017. 从 MNIST 到真实项目的学习路线
跑通 MNIST 之后,你可能会觉得“这也太简单了,和实际项目差距太大”。确实如此。MNIST 是固定的、干净的、已经标准化好的数据,现实中几乎不会有这么好的条件。接下来你可以按这样的路径逐步提升:
- 多分类进阶:用 CIFAR-10 替换 MNIST,体会彩色图像、更多类别、更复杂的模型需求。
- 卷积神经网络(CNN):学习
nn.Conv2d、nn.MaxPool2d等卷积层,理解卷积操作是如何提取图像特征的。CNN 结构在计算机视觉任务中几乎是标配。 - 数据增强:使用
torchvision.transforms做随机旋转、裁剪、翻转,理解数据增强如何提升模型泛化能力。 - 迁移学习:使用 ImageNet 预训练的 ResNet、VGG 等模型,在自己数据集上做 fine-tune。这是实际项目中最高效的方式之一。
- 目标检测与分割:接触 Faster R-CNN、YOLO 或 U-Net 等结构,理解模型如何定位和识别多个目标。
- NLP 入门:学习词嵌入、LSTM、Transformer,然后快速过渡到大语言模型的应用。
- 模型部署:学习 ONNX 导出、量化(Quantization)和推理优化,了解模型上线前后的差距。
每一步都要动手写代码,只在教程里看懂不算懂。遇到报错记录下来,再反过来理解原理,这个循环是学习深度学习最有效的方式。
8. 写在后边
回到开头的问题:2026 年,TensorFlow 和 PyTorch 应该选哪个?我的建议很明确:如果你是新手,直接从 PyTorch 入手。它的设计更贴近人的思维方式,调试更友好,生态更活跃,学习材料也最丰富。TensorFlow 的很多核心概念和部署工具依然有价值,但在你掌握了 PyTorch 之后再针对性学习,会轻松很多。
框架只是工具,真正重要的是你对深度学习基础概念的理解:张量怎么流动、梯度怎么计算、损失函数怎么设计、模型怎么评估。这些底层逻辑是跨框架通用的,也是你未来深入 AI 领域最坚实的底座。
如果你今天跟着这篇文章完成了环境搭建、跑通了 MNIST 项目,恭喜你,你已经迈过了最困难的从 0 到 1 这一步。接下来,选一个有兴趣的方向,找一个开源项目,开始动手吧。