从零开始掌握 PyTorch:yunjey/pytorch-tutorial 完全指南

从零开始掌握 PyTorch:yunjey/pytorch-tutorial 完全指南

# 从零开始掌握 PyTorch:yunjey/pytorch-tutorial 完全指南

## 一、引言

PyTorch 是目前最受开发者欢迎的深度学习框架之一,它以其动态计算图、Pythonic 的编程风格和强大的 GPU 加速能力而闻名。对于想要入门深度学习的初学者来说,拥有一份系统、完整且易于理解的教程至关重要。今天我们要介绍的是 GitHub 上备受好评的开源项目——**yunjey/pytorch-tutorial**,该项目目前已获得超过 32,000 颗星标,是学习 PyTorch 的绝佳资源。

本文将带领读者从环境搭建开始,逐步深入到 PyTorch 的核心功能,通过大量的实战代码示例,帮助读者快速掌握这个强大的深度学习框架。无论你是计算机科学专业的学生、研究人员,还是希望转型进入 AI 领域的开发者,这份教程都将为你提供坚实的技术基础。

## 二、环境搭建

### 2.1 系统要求

在开始学习 PyTorch 之前,我们需要确保开发环境满足基本要求。PyTorch 支持多种操作系统,包括 Windows、Linux 和 macOS。对于硬件方面,虽然 CPU 也能运行 PyTorch,但为了获得更好的训练体验,建议配备一块 NVIDIA 显卡,因为 PyTorch 的 CUDA 加速能够显著提升训练速度。

### 2.2 安装 Python

PyTorch 需要 Python 3.6 或更高版本。建议使用 Anaconda 来管理 Python 环境,这样可以方便地创建独立的开发环境,避免不同项目之间的依赖冲突。

“`python
# 如果还没有安装 Anaconda,可以从官网下载安装包
# 安装完成后,打开终端或命令行,执行以下命令创建新环境

conda create -n pytorch_env python=3.9
conda activate pytorch_env

# 上述命令创建了一个名为 pytorch_env 的新环境,并激活了它
“`

### 2.3 安装 PyTorch

安装 PyTorch 有多种方式,最简单的方法是使用 pip 或 conda。官方提供了在线安装命令生成器,可以根据你的系统和 CUDA 版本生成对应的安装命令。

“`bash
# 使用 pip 安装 PyTorch(CPU 版本,适合初学者和没有 NVIDIA 显卡的用户)
pip install torch torchvision

# 使用 pip 安装 PyTorch(CUDA 版本,适合有 NVIDIA 显卡的用户)
pip install torch torchvision torchaudio –index-url https://download.pytorch.org/whl/cu118

# 或者使用 conda 安装(CUDA 11.8 版本)
conda install pytorch torchvision pytorch-cuda=11.8 -c pytorch -c nvidia
“`

安装完成后,可以通过以下代码验证安装是否成功:

“`python
import torch

# 打印 PyTorch 版本
print(f”PyTorch 版本: {torch.__version__}”)

# 检查 CUDA 是否可用
print(f”CUDA 是否可用: {torch.cuda.is_available()}”)

# 如果 CUDA 可用,打印 GPU 数量和名称
if torch.cuda.is_available():
print(f”GPU 数量: {torch.cuda.device_count()}”)
print(f”GPU 名称: {torch.cuda.get_device_name(0)}”)
“`

### 2.4 安装 Jupyter Notebook

Jupyter Notebook 是进行深度学习实验的理想工具,它允许我们在浏览器中编写和运行代码,并即时查看结果。

“`bash
# 安装 Jupyter Notebook
pip install jupyter notebook

# 启动 Jupyter Notebook
jupyter notebook
“`

## 三、PyTorch 核心概念详解

### 3.1 张量(Tensors)

张量是 PyTorch 中的基本数据结构,可以看作是多维数组。在 PyTorch 中,几乎所有的操作都是围绕张量展开的。理解张量是掌握 PyTorch 的第一步。

“`python
import torch

# 创建一个简单的张量
# 方法一:直接使用数据创建
x = torch.tensor([[1, 2, 3], [4, 5, 6]])
print(f”张量 x:\n{x}”)
print(f”张量形状: {x.shape}”)
print(f”张量数据类型: {x.dtype}”)

# 方法二:创建未初始化的张量
y = torch.empty(3, 4) # 创建一个 3×4 的未初始化张量
print(f”\n未初始化张量 y:\n{y}”)

# 方法三:创建随机张量
z = torch.rand(2, 3) # [0, 1) 均匀分布
print(f”\n随机张量 z:\n{z}”)

# 方法四:创建全零或全一张量
zeros = torch.zeros(2, 3)
ones = torch.ones(2, 3)
print(f”\n全零张量:\n{zeros}”)
print(f”\n全一张量:\n{ones}”)

# 方法五:从 NumPy 数组创建张量
import numpy as np
np_array = np.array([[1, 2], [3, 4]])
tensor_from_np = torch.from_numpy(np_array)
print(f”\n从 NumPy 创建的张量:\n{tensor_from_np}”)
“`

### 3.2 张量运算

张量支持丰富的数学运算,包括基本的算术运算、矩阵运算和三角函数等。

“`python
# 张量运算示例
a = torch.tensor([[1, 2], [3, 4]], dtype=torch.float32)
b = torch.tensor([[5, 6], [7, 8]], dtype=torch.float32)

# 加法
c = a + b
print(f”加法结果:\n{c}”)

# 矩阵乘法
d = torch.matmul(a, b)
print(f”\n矩阵乘法结果:\n{d}”)

# 或者使用 @ 运算符
e = a @ b
print(f”\n使用 @ 运算符的矩阵乘法:\n{e}”)

# 求和、均值、最大值
print(f”\n求和: {a.sum()}”)
print(f”均值: {a.mean()}”)
print(f”最大值: {a.max()}”)

# 获取最大值的位置索引
max_val, max_idx = a.max(dim=1)
print(f”每行最大值: {max_val}”)
print(f”每行最大值的索引: {max_idx}”)
“`

### 3.3 自动微分(Autograd)

自动微分是 PyTorch 最强大的特性之一,它能够自动计算梯度,极大地简化了神经网络的训练过程。

“`python
# 自动微分示例
# 创建需要梯度的张量
x = torch.tensor([2.0, 3.0], requires_grad=True)

# 定义一个函数 y = x^2 + 2x + 1
y = x**2 + 2*x + 1

# 计算梯度
y.sum().backward() # 对标量调用 backward()

# 打印梯度 dy/dx = 2x + 2
print(f”梯度: {x.grad}”)
# 当 x = [2, 3] 时,梯度应该是 [6, 8]

# 禁用梯度计算
with torch.no_grad():
z = x * 2
print(f”\n在 no_grad 块中创建的变量: {z}”)
print(f”z.requires_grad: {z.requires_grad}”)
“`

### 3.4 神经网络模块(nn.Module)

`torch.nn.Module` 是 PyTorch 中构建神经网络的基类。所有自定义的神经网络都应该继承自这个类。

“`python
import torch.nn as nn
import torch.nn.functional as F

# 定义一个简单的前馈神经网络
class SimpleNet(nn.Module):
def __init__(self, input_size, hidden_size, num_classes):
super(SimpleNet, self).__init__()
# 定义网络层
self.fc1 = nn.Linear(input_size, hidden_size)
self.relu = nn.ReLU()
self.fc2 = nn.Linear(hidden_size, hidden_size)
self.fc3 = nn.Linear(hidden_size, num_classes)

def forward(self, x):
# 前向传播
out = self.fc1(x)
out = self.relu(out)
out = self.fc2(out)
out = self.relu(out)
out = self.fc3(out)
return out

# 创建网络实例
model = SimpleNet(input_size=784, hidden_size=128, num_classes=10)
print(f”网络结构:\n{model}”)

# 打印网络参数
total_params = sum(p.numel() for p in model.parameters())
print(f”\n总参数量: {total_params:,}”)

# 测试前向传播
test_input = torch.randn(32, 784) # 批量大小 32,输入维度 784
output = model(test_input)
print(f”输出形状: {output.shape}”) # 应该是 [32, 10]
“`

## 四、一步步实战教程

### 4.1 实战一:手写数字识别(MNIST)

MNIST 数据集是深度学习入门的经典数据集,包含了 0-9 的手写数字图像。接下来,我们将使用 PyTorch 构建一个完整的图像分类模型。

“`python
import torch
import torch.nn as nn
import torch.optim as optim
import torch.nn.functional as F
from torch.utils.data import DataLoader, TensorDataset
import torchvision
import torchvision.transforms as transforms
import matplotlib.pyplot as plt

# 第一步:准备数据
# 定义数据预处理操作
transform = transforms.Compose([
transforms.ToTensor(), # 将 PIL Image 或 numpy.ndarray 转换为 tensor
transforms.Normalize((0.5,), (0.5,)) # 归一化到 [-1, 1]
])

# 下载并加载 MNIST 训练集和测试集
train_dataset = torchvision.datasets.MNIST(
root=’./data’,
train=True,
download=True,
transform=transform
)

test_dataset = torchvision.datasets.MNIST(
root=’./data’,
train=False,
download=True,
transform=transform
)

# 创建数据加载器
batch_size = 64
train_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True)
test_loader = DataLoader(test_dataset, batch_size=batch_size, shuffle=False)

# 可视化部分训练数据
print(f”训练集样本数: {len(train_dataset)}”)
print(f”测试集样本数: {len(test_dataset)}”)

# 获取一个批次的数据
images, labels = next(iter(train_loader))
print(f”每个批次的图像形状: {images.shape}”) # [batch_size, 1, 28, 28]
print(f”每个批次的标签形状: {labels.shape}”) # [batch_size]

# 第二步:定义卷积神经网络模型
class CNN(nn.Module):
def __init__(self):
super(CNN, self).__init__()
# 第一个卷积层
# 输入: [batch, 1, 28, 28] -> 输出: [batch, 16, 24, 24]
self.conv1 = nn.Conv2d(1, 16, kernel_size=5, padding=0)

# 第二个卷积层
# 输入: [batch, 16, 12, 12] -> 输出: [batch, 32, 8, 8]
self.conv2 = nn.Conv2d(16, 32, kernel_size=5, padding=0)

# 全连接层
# 输入: [batch, 32*4*4] -> 输出: [batch, 10]
self.fc1 = nn.Linear(32 * 4 * 4, 10)

# Dropout 防止过拟合
self.dropout = nn.Dropout(0.5)

def forward(self, x):
# 第一个卷积块: 卷积 -> ReLU -> 池化
x = F.relu(self.conv1(x)) # [batch, 16, 24, 24]
x = F.max_pool2d(x, 2) # [batch, 16, 12, 12]

# 第二个卷积块
x = F.relu(self.conv2(x)) # [batch, 32, 8, 8]
x = F.max_pool2d(x, 2) # [batch, 32, 4, 4]

# 展平张量
x = x.view(x.size(0), -1) # [batch, 32*4*4]

# 全连接层
x = self.dropout(x)
x = self.fc1(x)
return x

# 创建模型实例
device = torch.device(‘cuda’ if torch.cuda.is_available() else ‘cpu’)
model = CNN().to(device)
print(f”\n使用设备: {device}”)
print(f”模型结构:\n{model}”)

# 第三步:定义损失函数和优化器
criterion = nn.CrossEntropyLoss() # 交叉熵损失,适合分类问题
optimizer = optim.Adam(model.parameters(), lr=0.001) # Adam 优化器

# 第四步:训练模型
def train_epoch(model, train_loader, criterion, optimizer, device):
model.train() # 设置为训练模式
running_loss = 0.0
correct = 0
total = 0

for images, labels in train_loader:
# 将数据移动到设备上
images, labels = images.to(device), labels.to(device)

# 前向传播
outputs = model(images)
loss = criterion(outputs, labels)

# 反向传播和优化
optimizer.zero_grad() # 清零梯度
loss.backward() # 反向传播
optimizer.step() # 更新参数

# 统计
running_loss += loss.item()
_, predicted = outputs.max(1)
total += labels.size(0)
correct += predicted.eq(labels).sum().item()

epoch_loss = running_loss / len(train_loader)
epoch_acc = 100.0 * correct / total
return epoch_loss, epoch_acc

def evaluate(model, test_loader, criterion, device):
model.eval() # 设置为评估模式
running_loss = 0.0
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)
loss = criterion(outputs, labels)

running_loss += loss.item()
_, predicted = outputs.max(1)
total += labels.size(0)
correct += predicted.eq(labels).sum().item()

test_loss = running_loss / len(test_loader)
test_acc = 100.0 * correct / total
return test_loss, test_acc

# 开始训练
num_epochs = 5
print(“\n开始训练…”)
print(“-” * 60)

for epoch in range(num_epochs):
train_loss, train_acc = train_epoch(model, train_loader, criterion, optimizer, device)
test_loss, test_acc = evaluate(model, test_loader, criterion, device)

print(f”Epoch [{epoch+1}/{num_epochs}] “
f”Train Loss: {train_loss:.4f}, Train Acc: {train_acc:.2f}% | “
f”Test Loss: {test_loss:.4f}, Test Acc: {test_acc:.2f}%”)

print(“-” * 60)
print(“训练完成!”)

# 第五步:保存和加载模型
torch.save(model.state_dict(), ‘mnist_cnn.pth’)
print(“\n模型已保存到 mnist_cnn.pth”)

# 加载模型
model.load_state_dict(torch.load(‘mnist_cnn.pth’))
print(“模型加载成功!”)

# 第六步:可视化预测结果
model.eval()
with torch.no_grad():
sample_images, sample_labels = next(iter(test_loader))
sample_images = sample_images.to(device)
outputs = model(sample_images)
_, predicted = outputs.max(1)

# 显示前 10 个样本的预测结果
fig, axes = plt.subplots(2, 5, figsize=(12, 5))
for i, ax in enumerate(axes.flat):
img = sample_images[i].cpu().squeeze() # 移除批次和通道维度
ax.imshow(img, cmap=’gray’)
ax.set_title(f’真实: {sample_labels[i]} | 预测: {predicted[i].item()}’)
ax.axis(‘off’)
plt.tight_layout()
plt.savefig(‘prediction_results.png’)
plt.show()
“`

### 4.2 实战二:循环神经网络(RNN)进行文本分类

接下来,我们使用循环神经网络来处理文本分类任务。RNN 特别适合处理序列数据,如文本、时间序列等。

“`python
import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import Dataset, DataLoader
import numpy as np

# 第一步:准备数据
# 这里我们使用简单的情感分类数据集(正面/负面评论)
# 数据集包含一些示例句子及其标签

# 示例数据
texts = [
“这部电影太棒了”,
“我非常不喜欢这个产品”,
“服务态度很差”,
“学习PyTorch很有趣”,
“这个餐厅的食物很难吃”,
“我对这个结果非常满意”,
“糟糕透顶的经历”,
“这是一次愉快的购物体验”
]

labels = [1, 0, 0, 1, 0, 1, 0, 1] # 1: 正面, 0: 负面

# 构建词汇表
def build_vocab(texts):
vocab = {‘‘: 0, ‘‘: 1} # 特殊 token
for text in texts:
for char in text:
if char not in vocab:
vocab[char] = len(vocab)
return vocab

vocab = build_vocab(texts)
print(f”词汇表大小: {len(vocab)}”)
print(f”词汇表: {vocab}”)

# 文本到索引的转换
def text_to_indices(text, vocab, max_len=20):
indices = [vocab.get(char, vocab[‘‘]) for char in text]
# 填充或截断到固定长度

View Project

Stars: 32345

如果内容对您有帮助,欢迎打赏

您的支持是我继续创作的动力

前往打赏页面

评论区

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注