Python 进阶提升(十):深度学习框架(PyTorch、TensorFlow)
1. 引言
2. 环境准备
在动手写代码之前,先把开发环境搭好。下面分别介绍 PyTorch 和 TensorFlow 的安装方法,以及如何验证安装是否成功。
2.1 安装 PyTorch
PyTorch 官方推荐使用 pip 安装。最简单的 CPU 版本安装命令如下:
pip install torch torchvision
如果你有 NVIDIA 显卡并希望使用 GPU 加速,需要根据你的 CUDA 版本选择对应的安装命令。建议访问 PyTorch 官网 获取最新的安装命令,例如:
# 以 CUDA 12.1 为例
pip install torch torchvision --index-url https://download.pytorch.org/whl/cu121
CPU 版与 GPU 版如何选?
- CPU 版:安装简单、体积小,适合入门学习和跑小规模模型,但训练速度较慢。
- GPU 版:需要 NVIDIA 显卡和正确的 CUDA 驱动,训练速度可提升数十倍,适合正式训练和实验。
- 如果只是跟着本文学习,CPU 版完全够用;后续需要加速时再升级到 GPU 版即可。
2.2 安装 TensorFlow
TensorFlow 同样可以通过 pip 一键安装:
pip install tensorflow
默认安装的是 CPU 版。如果你需要 GPU 支持,可以安装对应的 GPU 版本:
pip install tensorflow-gpu
注意:TensorFlow 的 GPU 版本对 CUDA 和 cuDNN 版本有严格的要求,安装前请查阅 TensorFlow 官方文档 确认版本兼容性。如果只是入门学习,CPU 版即可满足需求。
2.3 验证安装是否成功
安装完成后,运行下面的 Python 代码验证两个框架是否都能正常导入,并查看版本号:
import torch
import tensorflow as tf
print("PyTorch 版本:", torch.__version__)
print("TensorFlow 版本:", tf.__version__)
# 检查 PyTorch 是否可用 GPU
print("PyTorch CUDA 可用:", torch.cuda.is_available())
# 检查 TensorFlow 是否可用 GPU
print("TensorFlow GPU 可用:", tf.config.list_physical_devices('GPU'))
如果输出类似下面的结果,说明环境准备就绪:
PyTorch 版本: 2.3.1
TensorFlow 版本: 2.16.1
PyTorch CUDA 可用: False
TensorFlow GPU 可用: []
上面示例中
CUDA 可用为False、GPU 可用为空列表,说明当前是 CPU 环境,这并不影响后续学习。如果你配置了 GPU,这里会显示True和对应的 GPU 设备信息。
在完成 Python 基础语法的学习后,我们终于来到了一个激动人心的阶段——深度学习框架。PyTorch 和 TensorFlow 是目前最主流的两个深度学习框架,它们让复杂的神经网络构建、训练与部署变得触手可及。
本章将带你快速了解这两个框架的核心概念、基本用法与典型应用场景,帮助你建立对深度学习开发的整体认知,为后续深入实战打下基础。
2. 为什么需要深度学习框架
在没有框架的年代,实现一个神经网络需要手动编写大量矩阵运算、反向传播和梯度更新代码,不仅繁琐,而且极易出错。深度学习框架的出现,把这一切封装成了简洁、高效的 API。
使用框架带来的核心收益包括:
- 自动求导:框架自动计算梯度,无需手写反向传播。
- GPU 加速:一行代码即可将计算迁移到 GPU,大幅提升训练速度。
- 模块化组件:卷积层、循环层、优化器等现成组件开箱即用。
- 生态丰富:预训练模型、可视化工具、部署方案一应俱全。
3. PyTorch 核心概念
PyTorch 由 Facebook(Meta)团队开发,以其「动态计算图」和「Python 风格」著称,深受研究社区喜爱。
3.1 张量(Tensor)
张量是 PyTorch 中最基础的数据结构,可以理解为多维数组。它与 NumPy 的 ndarray 类似,但额外支持 GPU 加速与自动求导。
import torch
# 从列表创建张量
x = torch.tensor([[1, 2], [3, 4]])
print(x)
# 创建全零张量
zeros = torch.zeros(2, 3)
print(zeros)
# 随机张量
rand = torch.rand(2, 2)
print(rand)
# 查看形状
print(x.shape) # torch.Size([2, 2])
3.2 自动求导(Autograd)
PyTorch 的核心机制是自动求导。只要张量设置了 requires_grad=True,框架就会自动记录所有运算,并在调用 backward() 时计算梯度。
import torch
# 创建一个需要梯度的张量
x = torch.tensor([2.0], requires_grad=True)
# 定义一个简单函数 y = x^2
y = x ** 2
# 反向传播,自动计算 dy/dx = 2x = 4
y.backward()
print(x.grad) # tensor([4.])
3.3 构建神经网络
PyTorch 通过 torch.nn.Module 组织网络结构。下面是一个简单的两层神经网络:
import torch
import torch.nn as nn
class SimpleNet(nn.Module):
def __init__(self):
super().__init__()
self.fc1 = nn.Linear(784, 128)
self.fc2 = nn.Linear(128, 10)
def forward(self, x):
x = torch.relu(self.fc1(x))
x = self.fc2(x)
return x
model = SimpleNet()
print(model)
3.4 训练循环
PyTorch 的训练过程非常直观,通常包含前向传播、计算损失、反向传播和参数更新四个步骤:
import torch
import torch.nn as nn
import torch.optim as optim
# 定义模型、损失函数和优化器
model = SimpleNet()
criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(model.parameters(), lr=0.01)
# 模拟一批数据
inputs = torch.randn(32, 784)
labels = torch.randint(0, 10, (32,))
# 训练一步
optimizer.zero_grad() # 清空梯度
outputs = model(inputs) # 前向传播
loss = criterion(outputs, labels) # 计算损失
loss.backward() # 反向传播
optimizer.step() # 更新参数
print(f"Loss: {loss.item():.4f}")
4. TensorFlow 核心概念
TensorFlow 由 Google 开发,以其「生产级部署能力」和「静态图优化」著称,在企业级应用与移动端部署中占据优势。
4.1 张量(Tensor)
TensorFlow 同样以张量为核心数据结构,通过 tf.constant、tf.Variable 等 API 创建:
import tensorflow as tf
# 创建常量张量
x = tf.constant([[1, 2], [3, 4]])
print(x)
# 创建变量(可训练参数)
w = tf.Variable(tf.random.normal([2, 2]))
print(w)
# 查看形状
print(x.shape) # (2, 2)
4.2 自动求导
TensorFlow 使用 tf.GradientTape 记录运算并计算梯度:
import tensorflow as tf
# 创建可训练变量
x = tf.Variable(2.0)
# 在梯度带内记录运算
with tf.GradientTape() as tape:
y = x ** 2
# 计算 dy/dx = 2x = 4
grad = tape.gradient(y, x)
print(grad) # tf.Tensor(4.0, shape=(), dtype=float32)
4.3 构建神经网络
TensorFlow 推荐使用 tf.keras 高层 API 构建模型,代码简洁且易于上手:
import tensorflow as tf
# 使用 Sequential 构建模型
model = tf.keras.Sequential([
tf.keras.layers.Dense(128, activation='relu', input_shape=(784,)),
tf.keras.layers.Dense(10)
])
model.summary()
4.4 模型训练
Keras 提供了高度封装的 compile 和 fit 方法,几行代码即可完成训练:
import tensorflow as tf
# 构建模型
model = tf.keras.Sequential([
tf.keras.layers.Dense(128, activation='relu', input_shape=(784,)),
tf.keras.layers.Dense(10)
])
# 编译模型
model.compile(
optimizer='sgd',
loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
metrics=['accuracy']
)
# 模拟数据
import numpy as np
x_train = np.random.randn(1000, 784).astype('float32')
y_train = np.random.randint(0, 10, (1000,))
# 训练模型
model.fit(x_train, y_train, epochs=5, batch_size=32)
5. 两大框架对比
| 维度 | PyTorch | TensorFlow |
|---|---|---|
| 计算图 | 动态图,灵活易调试 | 静态图为主,Keras 提供动态体验 |
| 学习曲线 | 更贴近 Python 直觉 | Keras 高层 API 简单,底层较复杂 |
| 社区生态 | 研究领域占优 | 生产部署与移动端占优 |
| 调试体验 | 可直接使用 Python 调试器 | 静态图调试相对复杂 |
| 部署方案 | TorchScript、ONNX | TensorFlow Serving、TFLite |
| 典型用户 | 学术界、研究团队 | 工业界、大规模部署团队 |
6. 如何选择
7. 实战:用 PyTorch 训练一个手写数字识别模型
理论讲得再多,不如动手跑一个完整的项目。下面我们用 PyTorch 完成一个经典的手写数字识别任务(MNIST),把前面学到的张量、自动求导、模型构建和训练循环串起来。
7.1 数据加载
PyTorch 通过 torchvision.datasets 提供常用数据集,DataLoader 负责批量加载与打乱数据:
import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import DataLoader
from torchvision import datasets, transforms
# 数据预处理:转为张量并归一化到 [0, 1]
transform = transforms.Compose([
transforms.ToTensor(),
transforms.Normalize((0.1307,), (0.3081,))
])
# 下载并加载训练集与测试集
train_dataset = datasets.MNIST(root='./data', train=True, download=True, transform=transform)
test_dataset = datasets.MNIST(root='./data', train=False, download=True, transform=transform)
train_loader = DataLoader(train_dataset, batch_size=64, shuffle=True)
test_loader = DataLoader(test_dataset, batch_size=64, shuffle=False)
# 查看一个批次的数据形状
images, labels = next(iter(train_loader))
print(images.shape) # torch.Size([64, 1, 28, 28])
print(labels.shape) # torch.Size([64])
7.2 模型定义
MNIST 图片是 28×28 的灰度图,我们把它展平为 784 维向量,送入一个三层全连接网络:
class MNISTNet(nn.Module):
def __init__(self):
super().__init__()
self.fc1 = nn.Linear(784, 256)
self.fc2 = nn.Linear(256, 128)
self.fc3 = nn.Linear(128, 10)
def forward(self, x):
x = x.view(-1, 784) # 展平为 [batch, 784]
x = torch.relu(self.fc1(x))
x = torch.relu(self.fc2(x))
x = self.fc3(x) # 输出 10 个类别的 logits
return x
model = MNISTNet()
print(model)
7.3 训练循环
训练过程与前面 3.4 节一致:前向传播 → 计算损失 → 反向传播 → 更新参数。这里我们训练 3 个 epoch:
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
model = MNISTNet().to(device)
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)
epochs = 3
for epoch in range(epochs):
model.train()
total_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()
total_loss += loss.item()
avg_loss = total_loss / len(train_loader)
print(f"Epoch {epoch + 1}/{epochs}, Loss: {avg_loss:.4f}")
运行结果示例:
Epoch 1/3, Loss: 0.2781
Epoch 2/3, Loss: 0.1124
Epoch 3/3, Loss: 0.0786
7.4 模型评估
7.5 用 TensorFlow 实现同一任务
看完了 PyTorch 的实现,我们再用 TensorFlow 的 Keras 高层 API 完成同一个 MNIST 手写数字识别任务,感受一下两种框架在编码风格上的差异。
7.5.1 数据加载
Keras 内置了 MNIST 数据集,通过 tf.keras.datasets.mnist.load_data() 即可直接获取,无需手动下载:
import tensorflow as tf
# 加载 MNIST 数据集(首次运行会自动下载)
(x_train, y_train), (x_test, y_test) = tf.keras.datasets.mnist.load_data()
# 归一化到 [0, 1],并展平为 784 维向量
x_train = x_train.reshape(-1, 784).astype('float32') / 255.0
x_test = x_test.reshape(-1, 784).astype('float32') / 255.0
print(x_train.shape) # (60000, 784)
print(y_train.shape) # (60000,)
7.5.2 模型构建
Keras 使用 Sequential 顺序模型,逐层堆叠即可,无需手写 forward:
model = tf.keras.Sequential([
tf.keras.layers.Dense(256, activation='relu', input_shape=(784,)),
tf.keras.layers.Dense(128, activation='relu'),
tf.keras.layers.Dense(10) # 输出 10 个类别的 logits
])
model.summary()
7.5.3 模型训练
Keras 通过 compile 配置优化器与损失函数,再用 fit 一行完成训练:
model.compile(
optimizer='adam',
loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
metrics=['accuracy']
)
history = model.fit(x_train, y_train, epochs=3, batch_size=64, validation_split=0.1)
运行结果示例:
Epoch 1/3, loss: 0.3124, accuracy: 0.9081, val_loss: 0.1421, val_accuracy: 0.9562
Epoch 2/3, loss: 0.1187, accuracy: 0.9642, val_loss: 0.1023, val_accuracy: 0.9687
Epoch 3/3, loss: 0.0812, accuracy: 0.9756, val_loss: 0.0894, val_accuracy: 0.9721
7.5.4 模型评估
Keras 提供了现成的 evaluate 方法,直接返回损失与准确率:
test_loss, test_acc = model.evaluate(x_test, y_test, verbose=0)
print(f"Test Accuracy: {test_acc * 100:.2f}%")
运行结果示例:
Test Accuracy: 97.21%
7.5.5 两个框架的对比
把两套实现放在一起,差异一目了然:
| 对比维度 | PyTorch | TensorFlow (Keras) |
|---|---|---|
| 代码行数 | 约 60 行,需手写训练循环 | 约 25 行,fit 封装训练细节 |
| 数据加载 | torchvision.datasets + DataLoader | tf.keras.datasets 内置,直接返回 NumPy 数组 |
| 模型定义 | 继承 nn.Module,手写 forward | Sequential 逐层堆叠,无需 forward |
| 训练方式 | 显式循环:前向 → 损失 → 反向 → 更新 | compile + fit 一行完成 |
| 评估方式 | 手写循环统计准确率 | evaluate 直接返回指标 |
| 灵活性 | 高,训练过程完全可控 | 较低,但开发效率高 |
| 学习曲线 | 需理解自动求导与训练细节 | 上手快,适合快速原型 |
可以看到,Keras 用更少的代码完成了同样的任务,把训练、评估等细节封装得更加彻底;而 PyTorch 则把每一步都暴露给你,让你对训练过程有更精细的控制。两者没有绝对的好坏——追求快速落地选 Keras,追求灵活可控选 PyTorch,这也是第 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, 1)
total += labels.size(0)
correct += (predicted == labels).sum().item()
accuracy = 100.0 * correct / total
print(f"Test Accuracy: {accuracy:.2f}%")
运行结果示例:
Test Accuracy: 97.85%
短短 3 个 epoch,这个简单的三层网络就能在 MNIST 测试集上达到约 98% 的准确率。你可以尝试增加 epoch、调整隐藏层大小或加入 Dropout,观察准确率的变化——这正是深度学习实验的乐趣所在。
选择框架没有绝对的对错,更多取决于你的目标场景:
- 做研究、写论文、快速验证想法:优先选择 PyTorch,其动态图机制让调试和实验更加灵活。
- 做产品落地、大规模部署、移动端应用:TensorFlow 的 Serving 与 Lite 生态更加成熟。
- 初学者入门:两者皆可。若追求快速上手,TensorFlow 的 Keras 高层 API 更友好;若想深入理解原理,PyTorch 的显式训练循环更有帮助。
7.6 常见错误与排查
初学深度学习框架时,报错几乎是不可避免的。下面整理了初学者最容易踩的 5 个坑,并给出解决方案和代码示例,帮你少走弯路。
7.6.1 维度不匹配(Shape Mismatch)
错误现象:前向传播时报错,提示 mat1 and mat2 shapes cannot be multiplied 或 size mismatch。
原因:输入数据的维度与网络第一层期望的维度不一致。例如 nn.Linear(784, 256) 期望输入是 [batch, 784],但传入的却是 [batch, 1, 28, 28]。
解决方案:在送入全连接层前,用 view 或 reshape 把输入展平:
# 错误写法:直接传入 4 维张量
# outputs = model(images) # 报错!
# 正确写法:先展平为 [batch, 784]
images = images.view(images.size(0), -1)
outputs = model(images)
排查技巧:在报错前打印张量形状,快速定位问题:
print(images.shape) # torch.Size([64, 1, 28, 28])
# 发现是 4 维,需要展平
7.6.2 梯度未清零(Gradient Accumulation)
错误现象:训练时 loss 不下降,甚至越来越大,或者模型参数更新异常。
原因:PyTorch 默认会累积梯度。如果每轮迭代没有调用 optimizer.zero_grad(),梯度会不断叠加,导致参数更新方向错误。
解决方案:在每次反向传播前清空梯度:
for images, labels in train_loader:
optimizer.zero_grad() # 关键:清空上一轮的梯度
outputs = model(images)
loss = criterion(outputs, labels)
loss.backward()
optimizer.step()
排查技巧:检查训练循环中是否遗漏了 zero_grad()。如果 loss 出现周期性波动或持续增大,优先怀疑梯度累积问题。
7.6.3 设备不一致(CPU/GPU 混用)
错误现象:报错 Expected all tensors to be on the same device,或训练速度异常缓慢。
原因:模型在 GPU 上,但数据仍在 CPU 上;或者部分张量在 CPU、部分在 GPU,导致运算失败。
解决方案:把模型和数据都迁移到同一设备:
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
model = MNISTNet().to(device)
for images, labels in train_loader:
images, labels = images.to(device), labels.to(device) # 数据也要迁移
outputs = model(images)
排查技巧:打印各对象的设备信息:
print(next(model.parameters()).device) # cuda:0 或 cpu
print(images.device) # 应与模型一致
7.6.4 忘记调用 model.train() / model.eval()
错误现象:训练时结果正常,但评估时准确率异常低;或训练时 Dropout、BatchNorm 行为不符合预期。
原因:模型默认处于训练模式。如果评估时忘记切换到 eval() 模式,Dropout 仍会随机丢弃神经元,BatchNorm 仍使用 batch 统计量,导致评估结果失真。
解决方案:训练和评估前显式切换模式:
# 训练阶段
model.train()
for images, labels in train_loader:
...
# 评估阶段
model.eval()
with torch.no_grad():
for images, labels in test_loader:
...
排查技巧:评估时务必同时使用 model.eval() 和 torch.no_grad(),前者关闭 Dropout 等训练专用层,后者关闭梯度计算以节省内存。
7.6.5 安装与导入失败(ModuleNotFoundError)
错误现象:import torch 或 import tensorflow as tf 报 ModuleNotFoundError。
原因:未安装对应框架,或安装到了错误的 Python 环境(如系统 Python 与虚拟环境混用)。
解决方案:使用 pip 安装,并确认当前环境:
# 安装 PyTorch(CPU 版)
pip install torch torchvision
# 安装 TensorFlow
pip install tensorflow
# 确认安装成功
python -c "import torch; print(torch.__version__)"
python -c "import tensorflow as tf; print(tf.__version__)"
排查技巧:如果安装了仍报错,检查是否激活了正确的虚拟环境:
# 查看当前 Python 路径
which python
# 查看已安装的包
pip list | grep -E "torch|tensorflow"
以上 5 个错误覆盖了安装、张量操作和训练循环中最常见的问题。遇到报错时不要慌,先看错误信息定位到具体环节,再对照上面的解决方案逐一排查。多读报错、多打印形状、多检查设备,是快速成长为深度学习老手的不二法门。
7. 小结
本章我们介绍了 PyTorch 与 TensorFlow 两大深度学习框架的核心概念,包括张量、自动求导、模型构建与训练流程,并通过对比帮助你理解各自的优势与适用场景。
无论选择哪个框架,扎实的 Python 基础都是最重要的前提。建议你动手运行本章代码,感受张量运算与自动求导的魅力,再选择一个方向深入实践。
8. 练习
9. 参考资料
学习深度学习框架,官方文档永远是最权威的参考。下面列出本文引用的官方文档链接,以及几个值得深入学习的资源,供你进一步探索。
官方文档
- PyTorch 官方文档:涵盖张量、自动求导、
nn.Module等全部核心 API,是使用 PyTorch 时最权威的查询手册。 - TensorFlow 官方文档:包含 Keras 高层 API 与底层接口的完整说明,适合在开发时随时查阅。
- MNIST 数据集说明:手写数字数据集的原始出处,介绍了数据集的构成、格式与背景,是理解本文实战任务的第一手资料。
推荐学习资源
- 《动手学深度学习》(Dive into Deep Learning):由李沐等人编写的开源教材,PyTorch 与 TensorFlow 双版本并行,理论与代码结合紧密,非常适合系统入门。
- Fast.ai 的 Practical Deep Learning for Coders:一门面向程序员的实战课程,强调「先跑通、再理解」,能帮你快速建立对深度学习开发的整体手感。
- CS231n:Convolutional Neural Networks for Visual Recognition:斯坦福大学的经典计算机视觉课程,虽然以图像分类为主线,但对神经网络训练、调参的讲解深入浅出,值得反复研读。
- 使用 PyTorch 创建一个 3 层全连接网络,并在随机数据上完成一次训练迭代。
- 使用 TensorFlow Keras 构建一个包含 Dropout 层的模型,观察训练过程中的过拟合现象。
- 对比两个框架中张量的形状、数据类型与设备迁移(CPU/GPU)的 API 差异。
- 尝试用
torch.nn.Sequential和tf.keras.Sequential分别构建结构相同的网络,体会两者的异同。
更多推荐


所有评论(0)