ResNet18多标签分类教程:1小时快速验证想法
引言
作为一名研究生,当导师建议尝试多标签分类任务时,你可能既兴奋又忐忑。兴奋的是这是一个前沿且有挑战性的课题,忐忑的是本地训练速度太慢,验证一个想法可能要等上好几天。别担心,本文将带你用ResNet18快速搭建多标签分类模型,1小时内验证你的思路可行性。
多标签分类与传统的单标签分类不同,它允许一张图片同时属于多个类别。比如一张图片可以同时包含"猫"和"沙发"两个标签。ResNet18作为经典的卷积神经网络,虽然最初设计用于单标签分类,但经过适当修改完全可以胜任多标签任务。
本文将使用PyTorch框架,基于CIFAR-10数据集(虽然它本身是单标签的,但我们可以模拟多标签场景),教你如何:
- 修改ResNet18的网络结构
- 准备多标签数据集
- 快速训练并验证模型
- 评估多标签分类效果
1. 环境准备与数据模拟
1.1 安装必要库
首先确保你的环境安装了PyTorch。如果你使用CSDN星图镜像,可以直接选择预装PyTorch的镜像,省去安装步骤。
pip install torch torchvision numpy matplotlib1.2 模拟多标签数据
由于CIFAR-10原本是单标签数据集,我们需要模拟多标签场景。这里采用简单的策略:对每张图片随机分配1-3个标签。
import torch from torchvision import datasets, transforms import numpy as np # 数据转换 transform = transforms.Compose([ transforms.ToTensor(), transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5)) ]) # 加载CIFAR-10 train_dataset = datasets.CIFAR10(root='./data', train=True, download=True, transform=transform) test_dataset = datasets.CIFAR10(root='./data', train=False, download=True, transform=transform) # 模拟多标签:每张图片随机分配1-3个标签 def create_multilabel(dataset): multilabel_data = [] for img, label in dataset: # 随机选择1-3个额外标签 num_extra = np.random.randint(0, 3) extra_labels = np.random.choice(10, num_extra, replace=False) # 合并原始标签和额外标签 all_labels = np.unique(np.append(label, extra_labels)) # 创建多标签向量 (10维,对应10个类别) label_vec = torch.zeros(10) label_vec[all_labels] = 1 multilabel_data.append((img, label_vec)) return multilabel_data train_multilabel = create_multilabel(train_dataset) test_multilabel = create_multilabel(test_dataset)2. 修改ResNet18网络结构
2.1 理解ResNet18原始结构
ResNet18原始设计用于单标签分类,其最后一层是全连接层,输出维度等于类别数(如CIFAR-10就是10)。对于多标签分类,我们需要做两处关键修改:
- 将最后的全连接层输出维度保持为类别数
- 将softmax激活函数改为sigmoid,因为每个标签是独立的二元分类问题
2.2 代码实现修改
import torchvision.models as models import torch.nn as nn class MultiLabelResNet18(nn.Module): def __init__(self, num_classes=10): super(MultiLabelResNet18, self).__init__() # 加载预训练的ResNet18 self.resnet = models.resnet18(pretrained=True) # 修改最后的全连接层,保持输出维度不变 in_features = self.resnet.fc.in_features self.resnet.fc = nn.Linear(in_features, num_classes) def forward(self, x): x = self.resnet(x) # 使用sigmoid激活函数替代softmax return torch.sigmoid(x)3. 训练多标签分类模型
3.1 定义损失函数和评估指标
多标签分类使用二元交叉熵损失(BCELoss),评估指标常用精确率、召回率和F1分数。
import torch.optim as optim from sklearn.metrics import f1_score, accuracy_score # 初始化模型 model = MultiLabelResNet18(num_classes=10) device = torch.device("cuda" if torch.cuda.is_available() else "cpu") model.to(device) # 损失函数和优化器 criterion = nn.BCELoss() optimizer = optim.Adam(model.parameters(), lr=0.001) # 评估函数 def evaluate(model, dataloader, threshold=0.5): model.eval() all_preds = [] all_labels = [] with torch.no_grad(): for inputs, labels in dataloader: inputs, labels = inputs.to(device), labels.to(device) outputs = model(inputs) preds = (outputs > threshold).float() all_preds.append(preds.cpu()) all_labels.append(labels.cpu()) all_preds = torch.cat(all_preds, dim=0) all_labels = torch.cat(all_labels, dim=0) f1 = f1_score(all_labels, all_preds, average='samples') accuracy = accuracy_score(all_labels, all_preds) return f1, accuracy3.2 训练循环
from torch.utils.data import DataLoader # 创建数据加载器 train_loader = DataLoader(train_multilabel, batch_size=64, shuffle=True) test_loader = DataLoader(test_multilabel, batch_size=64, shuffle=False) # 训练参数 num_epochs = 10 # 为了快速验证,我们只训练10个epoch for epoch in range(num_epochs): model.train() running_loss = 0.0 for i, (inputs, labels) in enumerate(train_loader): inputs, labels = inputs.to(device), labels.to(device) # 清零梯度 optimizer.zero_grad() # 前向传播 outputs = model(inputs) loss = criterion(outputs, labels) # 反向传播和优化 loss.backward() optimizer.step() running_loss += loss.item() # 每个epoch结束后评估 train_f1, train_acc = evaluate(model, train_loader) test_f1, test_acc = evaluate(model, test_loader) print(f'Epoch {epoch+1}/{num_epochs}, Loss: {running_loss/len(train_loader):.4f}, ' f'Train F1: {train_f1:.4f}, Test F1: {test_f1:.4f}, ' f'Train Acc: {train_acc:.4f}, Test Acc: {test_acc:.4f}')4. 结果分析与优化建议
4.1 理解输出结果
多标签分类的输出是一个概率向量,每个元素表示对应标签存在的概率。我们需要设置一个阈值(通常为0.5)来决定是否预测该标签存在。
# 示例预测 model.eval() sample_img, _ = test_multilabel[0] with torch.no_grad(): output = model(sample_img.unsqueeze(0).to(device)) predicted_labels = (output > 0.5).int().squeeze().cpu().numpy() print("预测标签向量:", predicted_labels)4.2 常见优化方向
- 数据层面:
- 使用真实的多标签数据集(如COCO、Pascal VOC)
数据增强:随机裁剪、旋转、颜色变换等
模型层面:
- 尝试更深的ResNet变体(如ResNet50)
- 添加注意力机制
使用标签相关性信息
训练技巧:
- 学习率调度
- 早停法防止过拟合
- 类别平衡采样
总结
通过本教程,你已经学会了如何快速修改ResNet18进行多标签分类任务验证:
- 数据准备:理解了如何模拟多标签数据集,为真实场景做准备
- 模型修改:掌握了将单标签分类网络改为多标签分类的关键修改点
- 训练评估:学会了多标签分类特有的损失函数和评估指标
- 快速验证:在1小时内完成了从数据准备到模型评估的全流程
💡获取更多AI镜像
想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。