PyTorch 入门实践:表格数据分类任务记录
深度学习是人工智能领域的关键方向。PyTorch 作为一款开源框架,以其直观、灵活的特性显著降低了入门门槛。本文记录了初次接触 PyTorch 并应用于表格数据分类任务的学习过程与核心步骤。
PyTorch 简述
PyTorch 由 Facebook AI 研究团队 (FAIR) 于 2016 年推出。其设计强调与 Python 语言的深度契合,提供易读、简洁的接口和动态计算图特性,便于理解内部机制与实验调试。PyTorch 已成为当前主流的深度学习框架之一。
学习动机
深度学习常被视为需要深厚专业背景的领域。PyTorch 的设计理念则致力于降低学习曲线,具备基础的 Python 编程能力和数学概念即可开始实践。其动态计算图特性为模型调试和迭代提供了显著便利,尤其适合初学者快速构建神经网络并体会深度学习的核心流程。(契机源于一位学长的推荐)。
环境与资源
- 平台: Google Colab。提供免费的 GPU 资源,避免了本地环境配置的复杂性。
- 核心学习资源:
- YouTube 教程系列: “Learn PyTorch in 5 Projects” by @OmarMAtef (https://www.youtube.com/watch?v=E0bwEAWmVEM)
- 配套 GitHub 代码库 (https://github.com/omaratef3221/pytorch_tutorials)
- 数据集: Kaggle 的 Rice type classification (水稻类型分类任务)。
实践一:表格数据分类
本次练习目标在于熟悉 PyTorch 核心工作流,并将其应用于表格数据分类任务。流程涵盖数据加载、模型构建、训练及评估。
数据集:Rice Type Classification
数据集目标为区分不同类型的水稻,包含颗粒的几何属性:
Area
: 颗粒面积MajorAxisLength
: 主轴长度MinorAxisLength
: 副轴长度Eccentricity
: 偏心率ConvexArea
: 凸包面积EquivDiameter
: 等效直径Extent
: 边界框像素比例Perimeter
: 周长Roundness
: 圆度AspectRation
: 纵横比Class
: 水稻类型 (目标变量,二分类)。
1. 数据获取与环境配置
首要步骤是将 Kaggle 数据集导入 Colab 环境,并加载必要的 Python 库。
# 安装 opendatasets 库 (Kaggle 数据集下载)
!pip install opendatasets --quiet
import opendatasets as od
# 下载数据集
dataset_url = 'https://www.kaggle.com/datasets/mssmartypants/rice-type-classification'
od.download(dataset_url) # 需输入 Kaggle 凭据
# 核心 PyTorch 模块
import torch
import torch.nn as nn
from torch.optim import Adam
from torch.utils.data import DataLoader, Dataset
from torchsummary import summary
# 数据处理与评估
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
import pandas as pd
import numpy as np
# 可视化
import matplotlib.pyplot as plt
# 设备配置 (优先 GPU)
device = 'cuda' if torch.cuda.is_available() else 'cpu'
print(f"Using device: {device}")
说明: opendatasets
简化了 Kaggle 数据获取流程。导入的库构成了 PyTorch 项目的基础框架。
2. 数据加载与预处理
加载数据至 Pandas DataFrame,进行初步清洗,并按标准划分为训练集、验证集和测试集。
# 加载数据
data_df = pd.read_csv("/content/rice-type-classification/riceClassification.csv")
# 数据清洗
data_df.dropna(inplace=True) # 移除缺失值
data_df.drop(['id'], axis=1, inplace=True) # 移除无关 ID 列
print(f"Processed data shape: {data_df.shape}") # (18185, 11)
# 分离特征与标签
x = data_df.iloc[:, :-1].values # 特征 (NumPy 数组)
y = data_df.iloc[:, -1].values # 标签 (NumPy 数组)
# 数据集划分 (70% 训练, 15% 验证, 15% 测试)
x_train, x_temp, y_train, y_temp = train_test_split(x, y, test_size=0.3)
x_val, x_test, y_val, y_test = train_test_split(x_temp, y_temp, test_size=0.5)
print(f"Train: {x_train.shape}, Val: {x_val.shape}, Test: {x_test.shape}")
说明: id
列无预测价值,常规清洗移除缺失值。数据集划分遵循机器学习标准实践:训练集用于学习模型参数,验证集用于调参与防止过拟合,测试集用于最终评估泛化能力。
3. 构建 PyTorch Dataset 与 DataLoader
Dataset
封装单个样本访问,DataLoader
实现批量加载、数据打乱与并行处理,提升训练效率。
class RiceDataset(Dataset):
def __init__(self, features, labels):
# 转换数据为 PyTorch 张量并移至目标设备
self.features = torch.tensor(features, dtype=torch.float32).to(device)
self.labels = torch.tensor(labels, dtype=torch.float32).to(device)
def __len__(self):
return len(self.features)
def __getitem__(self, idx):
return self.features[idx], self.labels[idx]
# 创建数据集实例
train_dataset = RiceDataset(x_train, y_train)
val_dataset = RiceDataset(x_val, y_val)
test_dataset = RiceDataset(x_test, y_test)
# 创建 DataLoader
BATCH_SIZE = 8
train_loader = DataLoader(train_dataset, batch_size=BATCH_SIZE, shuffle=True)
val_loader = DataLoader(val_dataset, batch_size=BATCH_SIZE, shuffle=True)
test_loader = DataLoader(test_dataset, batch_size=BATCH_SIZE)
说明: 实现 Dataset
类明确了数据访问逻辑。to(device)
确保数据与模型位于同一计算设备(GPU/CPU)。DataLoader
的 batch_size
和 shuffle
参数对训练效率和模型泛化性至关重要。
4. 神经网络模型构建
针对表格数据,构建一个基础的全连接网络 (Fully Connected Network)。
HIDDEN_SIZE = 10 # 隐藏层神经元数量
class RiceClassifier(nn.Module):
def __init__(self, input_size):
super().__init__()
self.input_layer = nn.Linear(input_size, HIDDEN_SIZE)
self.output_layer = nn.Linear(HIDDEN_SIZE, 1)
self.sigmoid = nn.Sigmoid()
def forward(self, x):
x = self.input_layer(x)
x = self.output_layer(x)
x = self.sigmoid(x)
return x
# 实例化模型 (输入特征数为 x.shape[1])
model = RiceClassifier(x.shape[1]).to(device)
说明: 继承 nn.Module
并定义 __init__
(层结构) 与 forward
(数据流) 是 PyTorch 模型的标准模式。选择 Sigmoid
激活函数适配二分类任务的概率输出。
5. 模型结构概览
使用 torchsummary
快速查看模型结构与参数量。
summary(model, (x.shape[1],))
----------------------------------------------------------------
Layer (type) Output Shape Param #
================================================================
Linear-1 [-1, 10] 110
Linear-2 [-1, 1] 11
Sigmoid-3 [-1, 1] 0
================================================================
Total params: 121
Trainable params: 121
Non-trainable params: 0
----------------------------------------------------------------
...
说明: summary
输出清晰展示了模型层级、输出维度及参数总量,有助于理解模型复杂度。
6. 训练流程:损失函数、优化器与循环
配置训练核心组件并执行迭代优化。
# 损失函数与优化器
criterion = nn.BCELoss() # 二元交叉熵 (Binary Cross Entropy)
optimizer = Adam(model.parameters(), lr=0.001) # Adam 优化器
EPOCHS = 100 # 训练轮数
# 记录指标
train_losses, val_losses = [], []
train_accs, val_accs = [], []
for epoch in range(EPOCHS):
# ----- 训练阶段 -----
model.train()
epoch_train_loss = 0.0
epoch_train_correct = 0
for features, targets in train_loader:
optimizer.zero_grad() # 清零梯度
outputs = model(features).squeeze(1) # 前向传播 [batch_size, 1] -> [batch_size]
loss = criterion(outputs, targets) # 计算损失
loss.backward() # 反向传播
optimizer.step() # 更新参数
epoch_train_loss += loss.item()
epoch_train_correct += ((outputs.round() == targets).sum().item())
avg_train_loss = epoch_train_loss / len(train_loader)
avg_train_acc = 100.0 * epoch_train_correct / len(train_dataset)
train_losses.append(avg_train_loss)
train_accs.append(avg_train_acc)
# ----- 验证阶段 -----
model.eval()
epoch_val_loss = 0.0
epoch_val_correct = 0
with torch.no_grad():
for features, targets in val_loader:
outputs = model(features).squeeze(1)
loss = criterion(outputs, targets)
epoch_val_loss += loss.item()
epoch_val_correct += ((outputs.round() == targets).sum().item())
avg_val_loss = epoch_val_loss / len(val_loader)
avg_val_acc = 100.0 * epoch_val_correct / len(val_dataset)
val_losses.append(avg_val_loss)
val_accs.append(avg_val_acc)
# 输出当前 Epoch 结果
print(f"Epoch {epoch+1:03d}/{EPOCHS}: "
f"Train Loss: {avg_train_loss:.4f}, Train Acc: {avg_train_acc:.2f}%, "
f"Val Loss: {avg_val_loss:.4f}, Val Acc: {avg_val_acc:.2f}%")
说明: 训练循环包含标准步骤:梯度清零 (zero_grad
)、前向传播 (forward
)、损失计算 (criterion
)、反向传播 (backward
)、参数更新 (step
)。model.train()
/model.eval()
切换训练/评估模式。指标记录用于监控过程。
7. 模型评估
在独立测试集上评估模型的最终泛化性能。
model.eval()
test_correct = 0
with torch.no_grad():
for features, targets in test_loader:
outputs = model(features).squeeze(1)
test_correct += ((outputs.round() == targets).sum().item())
test_accuracy = 100.0 * test_correct / len(test_dataset)
print(f"Final Test Accuracy: {test_accuracy:.4f}%")
Final Test Accuracy: 98.6437%
说明: 测试集评估是衡量模型泛化到未见数据能力的关键步骤。98.64% 的准确率表明模型在该数据集上表现良好。
8. 训练过程可视化
绘制损失和准确率曲线以分析训练动态。
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(15, 5))
# 损失曲线
ax1.plot(train_losses, label='Training Loss')
ax1.plot(val_losses, label='Validation Loss')
ax1.set_title('Training & Validation Loss')
ax1.set_xlabel('Epochs')
ax1.set_ylabel('Loss')
ax1.legend()
ax1.set_ylim(bottom=0)
# 准确率曲线
ax2.plot(train_accs, label='Training Accuracy')
ax2.plot(val_accs, label='Validation Accuracy')
ax2.set_title('Training & Validation Accuracy')
ax2.set_xlabel('Epochs')
ax2.set_ylabel('Accuracy (%)')
ax2.legend()
ax2.set_ylim([80, 100])
plt.tight_layout()
plt.show()
说明: 损失曲线稳定下降且训练/验证损失趋势接近,准确率曲线同步上升,表明模型有效学习且无明显过拟合现象。可视化是监控训练状态的有效工具。
9. 单样本预测 (注意事项)
对新样本进行预测需遵循与训练数据相同的预处理流程。重要提示:原始代码未包含显式数据标准化。神经网络通常受益于输入特征的缩放(归一化/标准化)。若训练时应用了此类处理,预测时的新样本必须使用相同的参数进行变换。
# 示例:假设使用最大值进行简单缩放 (Min-Max Scaling 变体)。实际应基于训练集统计量。
# 此处仅作演示,实际项目需在预处理阶段统一处理并保存参数。
temp_df = pd.read_csv("/content/rice-type-classification/riceClassification.csv").drop('id', axis=1)
max_vals = temp_df.max().values[:-1] # 获取各特征最大值 (排除标签列)
# 构造一个新样本 (示例值)
new_sample = np.array([2353, 81, 42, 0.75, 12, 12, 0.70, 927, 0.85, 1.5])
scaled_sample = new_sample / max_vals # 应用模拟缩放
# 预测
model.eval()
with torch.no_grad():
# 将样本转为张量并添加批次维度 [1, num_features]
input_tensor = torch.tensor(scaled_sample.reshape(1, -1), dtype=torch.float32).to(device)
prediction_prob = model(input_tensor)
predicted_class = prediction_prob.round().item() # 四舍五入得类别 (0/1)
print(f"Predicted Probability: {prediction_prob.item():.4f}")
print(f"Predicted Class: {predicted_class}")
Predicted Probability: 0.0000
Predicted Class: 0.0
说明: 单样本预测需注意输入张量的形状 ([batch_size=1, num_features]
)。模型输出为属于正类的概率,通过 round()
获得最终类别。此步骤模拟了模型在实际应用中的推理过程。
总结
本次实践系统性地应用 PyTorch 完成了一个表格数据分类任务。从数据加载、预处理、模型构建、训练、评估到可视化,完整地走通了核心流程。尽管模型结构较为基础,但达到了较高的测试准确率(98.64%)。训练/验证曲线表明学习过程稳定有效。
通过这次探索,加深了对 PyTorch 基础概念(Dataset
, DataLoader
, nn.Module
, 训练循环)的理解,为后续处理更复杂的模型和任务奠定了基础。感谢相关学习资源提供的清晰指引。