【Python学习打卡-Day33】你好,PyTorch!从“自动挡”到“手动挡”的深度学习之旅

📋 前言

各位坚持打卡的伙伴们,大家好!今天是学习之旅的第 33 天,我们将在这一天完成一次重要的"座驾升级"。

如果说之前我们使用的 scikit-learn 是一辆自动挡汽车 ------三行代码model.fit(), model.predict()就能轻松上路,那么从今天开始,我们就要学习驾驶一辆手动挡赛车 ------PyTorch。你需要亲手控制离合(数据)、挂挡(模型)、踩油门(训练),过程更复杂,但你能榨干发动机的每一分性能,实现更高阶的驾驶技巧。

准备好了吗?让我们从安装环境开始,正式进入深度学习的匠心世界!


一、核心知识点总结

1. 环境准备:为"赛车"修建专用赛道

  • 独立环境 :深度学习项目依赖复杂,创建一个全新的 Conda 环境 (conda create -n DL python=3.8) 是最佳实践。这就像为你的赛车修建一条专用赛道,避免与其他车辆(项目)互相干扰。
  • CPU vs. GPU
    • CPU (博士):核心少但能力强,擅长复杂的串行任务。
    • GPU (成百上千的小学生):核心多但能力单一,擅长简单的并行计算。
    • 深度学习的矩阵运算正是这种"简单重复"的工作,所以 GPU 是首选。
  • CUDA :NVIDIA 显卡的"官方加速驱动"。安装 PyTorch 时,需要选择与你电脑 CUDA 版本兼容的 GPU 版本,才能发挥显卡的威力。使用 nvidia-smi 命令可以查看你显卡支持的最高 CUDA 版本。

2. PyTorch 的"三位一体"

使用 PyTorch 构建模型,主要围绕三个核心概念:

(1) 数据:torch.Tensor (张量)

Tensor 是 PyTorch 世界的"血液"。它类似于 NumPy 的 ndarray,但有一个关键区别:Tensor 可以在 GPU 上进行计算。所有输入到模型的数据、模型内部的参数,最终都是以 Tensor 的形式存在的。

(2) 模型:nn.Module (骨架)

所有 PyTorch 模型都必须继承 nn.Module。这就像一个模型的"骨架",我们只需要做两件事来填充它:

  • __init__() (积木采购) :在这里定义模型需要的所有"层"(积木),比如线性层 nn.Linear、激活函数 nn.ReLU 等。
  • forward(self, x) (流水线组装) :在这里定义数据 x 如何依次流过 __init__ 中定义的各个层,最终得到输出。
(3) 训练:The Five-Step Loop (训练心法)

这是 PyTorch 的精髓,一个标准的训练循环包含雷打不动的五个步骤:

  1. 前向传播outputs = model(X_train) - 将数据喂给模型,得到预测结果。
  2. 计算损失loss = criterion(outputs, y_train) - 比较预测结果和真实标签,计算差距。
  3. 梯度清零optimizer.zero_grad() - 清除上一轮的梯度信息,准备新一轮计算。
  4. 反向传播loss.backward() - 根据损失,计算模型中每个参数应该调整的方向(梯度)。
  5. 参数更新optimizer.step() - 根据计算出的梯度,更新模型的所有参数。

这个循环,就是模型"学习"的过程。


二、实战作业:用 PyTorch 构建鸢尾花分类器

我们将使用经典的鸢尾花数据集,从零开始搭建、训练并评估一个简单的神经网络。

1. 我的代码 (CPU 版本)

python 复制代码
# -*- coding: utf-8 -*-
import torch
import torch.nn as nn
import torch.optim as optim
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler # 使用 StandardScaler 效果通常更好
import matplotlib.pyplot as plt

# --- 1. 环境检查 ---
print(f"PyTorch Version: {torch.__version__}")
use_cuda = torch.cuda.is_available()
device = torch.device("cuda:0" if use_cuda else "cpu")
print(f"当前使用的设备: {device}")
if use_cuda:
    print(f"CUDA 设备名称: {torch.cuda.get_device_name(0)}")

# --- 2. 数据准备 ---
# 加载数据
iris = load_iris()
X, y = iris.data, iris.target

# 划分数据集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42, stratify=y)

# 数据标准化
scaler = StandardScaler()
X_train = scaler.fit_transform(X_train)
X_test = scaler.transform(X_test)

# 转换为 PyTorch Tensors
X_train_tensor = torch.FloatTensor(X_train).to(device)
y_train_tensor = torch.LongTensor(y_train).to(device)
X_test_tensor = torch.FloatTensor(X_test).to(device)
y_test_tensor = torch.LongTensor(y_test).to(device)

# --- 3. 模型架构定义 ---
class MLP(nn.Module):
    def __init__(self, input_size, hidden_size, num_classes):
        super(MLP, self).__init__()
        self.fc1 = nn.Linear(input_size, hidden_size) # 输入层 -> 隐藏层
        self.relu = nn.ReLU()                         # 激活函数
        self.fc2 = nn.Linear(hidden_size, num_classes) # 隐藏层 -> 输出层
    
    def forward(self, x):
        out = self.fc1(x)
        out = self.relu(out)
        out = self.fc2(out)
        return out

# 实例化模型
input_dim = 4
hidden_dim = 10
output_dim = 3
model = MLP(input_dim, hidden_dim, output_dim).to(device) # 将模型移动到指定设备
print("模型结构:")
print(model)


# --- 4. 定义损失函数和优化器 ---
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=0.01)

# --- 5. 训练模型 ---
num_epochs = 500
train_losses = []
test_accuracies = []

for epoch in range(num_epochs):
    model.train() # 设置为训练模式
    
    # 前向传播
    outputs = model(X_train_tensor)
    # 计算损失
    loss = criterion(outputs, y_train_tensor)
    # 梯度清零
    optimizer.zero_grad()
    # 反向传播
    loss.backward()
    # 更新参数
    optimizer.step()
    
    train_losses.append(loss.item())

    # --- 在每个 epoch 后进行评估 ---
    model.eval() # 设置为评估模式
    with torch.no_grad(): # 在评估阶段不计算梯度
        test_outputs = model(X_test_tensor)
        _, predicted = torch.max(test_outputs.data, 1)
        correct = (predicted == y_test_tensor).sum().item()
        accuracy = 100 * correct / len(y_test)
        test_accuracies.append(accuracy)

    if (epoch + 1) % 50 == 0:
        print(f'Epoch [{epoch+1}/{num_epochs}], Loss: {loss.item():.4f}, Test Accuracy: {accuracy:.2f}%')

# --- 6. 结果可视化 ---
plt.figure(figsize=(12, 5))

# 绘制损失曲线
plt.subplot(1, 2, 1)
plt.plot(train_losses)
plt.title("Training Loss")
plt.xlabel("Epoch")
plt.ylabel("Loss")

# 绘制准确率曲线
plt.subplot(1, 2, 2)
plt.plot(test_accuracies)
plt.title("Test Accuracy")
plt.xlabel("Epoch")
plt.ylabel("Accuracy (%)")

plt.tight_layout()
plt.show()

# --- 7. 最终模型评估 ---
model.eval()
with torch.no_grad():
    final_outputs = model(X_test_tensor)
    _, final_predicted = torch.max(final_outputs.data, 1)
    final_correct = (final_predicted == y_test_tensor).sum().item()
    final_accuracy = 100 * final_correct / len(y_test)
    print(f'\n最终在测试集上的准确率: {final_accuracy:.2f}%')

三、学习心得:拥抱"过程"的乐趣

sklearnPyTorch,最直观的感受是代码量变多了,但自由度也变得无限大。这让我深刻体会到:

  1. 没有什么是理所当然的sklearn 把数据处理、模型训练、评估都封装好了,而 PyTorch 强迫我们去思考每一步的细节:数据为什么要归一化?为什么要转成 Tensor?训练循环的每一步到底在干什么?
  2. 过程即是掌控:虽然写了更多代码,但我对模型内部发生的事情有了前所未有的掌控感。我可以随时打印出中间层的输出,可以自定义损失函数,可以精细地调整学习率。这种"一切尽在掌握"的感觉非常棒。
  3. 思维的转变 :写 PyTorch 代码,更像是在搭建一个动态的系统,而不是调用一个静态的函数。这种从"使用者"到"创造者"的思维转变,是深度学习之旅中最宝贵的收获。

驾驶"手动挡"虽然初期会手忙脚乱,但当你熟练之后,人车合一的快感是"自动挡"无法给予的。PyTorch 正是如此。


最后,感谢 @浙大疏锦行 老师的引领,让我们从舒适区迈出,开启了这段充满挑战与乐趣的深度学习征程!

复制代码
相关推荐
wa的一声哭了2 小时前
矩阵分析 方阵幂级数与方阵函数
人工智能·python·线性代数·算法·自然语言处理·矩阵·django
cehuishi95272 小时前
python和arcgispro的实践(AI辅助编程)
服务器·前端·python
强子感冒了2 小时前
Java集合框架深度学习:从Iterable到ArrayList的完整继承体系
java·笔记·学习
老歌老听老掉牙2 小时前
SymPy 中矩阵乘法的顺序与元素类型分析
python·矩阵·sympy
来不及辣哎呀2 小时前
学习Java第六十二天——Hot 100-09-438. 找到字符串中所有字母异位词
java·开发语言·学习
bosins2 小时前
基于Python实现PDF文件个人隐私信息检查
开发语言·python·pdf
bosins2 小时前
基于Python开发PDF文件元数据查看器
开发语言·python·pdf
鸿途优学-UU教育2 小时前
2025搜狐教育年度盛典|UU教育CEO彭普杰:成人学习不止于知识传递,科技赋能背后更需温度守护
科技·学习
小北方城市网2 小时前
第 10 课:Python 全体系实战整合与职业进阶指南(完结篇)
大数据·开发语言·数据库·python