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 可用FalseGPU 可用 为空列表,说明当前是 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.constanttf.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 提供了高度封装的 compilefit 方法,几行代码即可完成训练:

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. 两大框架对比

维度PyTorchTensorFlow
计算图动态图,灵活易调试静态图为主,Keras 提供动态体验
学习曲线更贴近 Python 直觉Keras 高层 API 简单,底层较复杂
社区生态研究领域占优生产部署与移动端占优
调试体验可直接使用 Python 调试器静态图调试相对复杂
部署方案TorchScript、ONNXTensorFlow 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 两个框架的对比

把两套实现放在一起,差异一目了然:

对比维度PyTorchTensorFlow (Keras)
代码行数约 60 行,需手写训练循环约 25 行,fit 封装训练细节
数据加载torchvision.datasets + DataLoadertf.keras.datasets 内置,直接返回 NumPy 数组
模型定义继承 nn.Module,手写 forwardSequential 逐层堆叠,无需 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 multipliedsize mismatch

原因:输入数据的维度与网络第一层期望的维度不一致。例如 nn.Linear(784, 256) 期望输入是 [batch, 784],但传入的却是 [batch, 1, 28, 28]

解决方案:在送入全连接层前,用 viewreshape 把输入展平:

# 错误写法:直接传入 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 torchimport tensorflow as tfModuleNotFoundError

原因:未安装对应框架,或安装到了错误的 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:斯坦福大学的经典计算机视觉课程,虽然以图像分类为主线,但对神经网络训练、调参的讲解深入浅出,值得反复研读。
  1. 使用 PyTorch 创建一个 3 层全连接网络,并在随机数据上完成一次训练迭代。
  2. 使用 TensorFlow Keras 构建一个包含 Dropout 层的模型,观察训练过程中的过拟合现象。
  3. 对比两个框架中张量的形状、数据类型与设备迁移(CPU/GPU)的 API 差异。
  4. 尝试用 torch.nn.Sequentialtf.keras.Sequential 分别构建结构相同的网络,体会两者的异同。

更多推荐