CIFAR-10/100 数据集 20 类粗粒度标签实战:PyTorch 加载与分层分类
CIFAR-100粗粒度分类实战PyTorch双标签加载与分层模型设计1. 理解CIFAR-100的层次化标签体系CIFAR-100数据集最显著的特征是其双重标签系统。每张32x32的彩色图像不仅包含100个细粒度类别标签如苹果、蘑菇还关联着20个粗粒度的大类标签如水果和蔬菜。这种层次结构为计算机视觉研究提供了独特的实验场景粗粒度分类20类识别高级语义类别细粒度分类100类区分更具体的子类别层次关系每个粗粒度类别包含5个细粒度类别如水果和蔬菜包含苹果、蘑菇、橙子等# CIFAR-100标签结构示例 coarse_labels [ 水生哺乳动物, 鱼类, 花卉, 食品容器, 水果和蔬菜, 家用电器, 家具, 昆虫, 大型食肉动物, 人造户外物品, 自然户外场景, 大型杂食动物, 中型哺乳动物, 无脊椎动物, 人物, 爬行动物, 小型哺乳动物, 树木, 交通工具1, 交通工具2 ] fine_labels { 水果和蔬菜: [苹果, 蘑菇, 橙子, 梨, 甜椒], 家用电器: [钟表, 电脑键盘, 台灯, 电话, 电视机] # 其他大类省略... }这种结构特别适合研究层次化分类模型知识迁移从粗粒度到细粒度多任务学习同时预测粗细标签提示粗粒度标签在数据量不足时能提供更强的监督信号而细粒度标签适合需要高精度的场景。2. PyTorch数据加载器实现我们需要自定义Dataset类来同时加载两种标签。关键点在于正确处理CIFAR-100的二进制文件格式2.1 数据集目录结构cifar-100-python/ ├── train # 训练集 ├── test # 测试集 ├── meta # 标签名称元数据2.2 自定义Dataset类import torch from torch.utils.data import Dataset import pickle import numpy as np class CIFAR100WithCoarse(Dataset): def __init__(self, root, trainTrue, transformNone): self.transform transform self.data [] self.fine_labels [] self.coarse_labels [] # 加载数据文件 file train if train else test with open(f{root}/cifar-100-python/{file}, rb) as fo: dict pickle.load(fo, encodingbytes) # 转换数据格式 self.data dict[bdata].reshape(-1, 3, 32, 32).transpose(0, 2, 3, 1) self.fine_labels dict[bfine_labels] self.coarse_labels dict[bcoarse_labels] # 加载标签名称 with open(f{root}/cifar-100-python/meta, rb) as fo: meta pickle.load(fo, encodingbytes) self.fine_label_names [t.decode(utf8) for t in meta[bfine_label_names]] self.coarse_label_names [t.decode(utf8) for t in meta[bcoarse_label_names]] def __len__(self): return len(self.data) def __getitem__(self, idx): img self.data[idx] fine_label self.fine_labels[idx] coarse_label self.coarse_labels[idx] if self.transform: img self.transform(img) return img, (coarse_label, fine_label)2.3 数据增强策略对于32x32的小尺寸图像推荐使用以下增强组合from torchvision import transforms train_transform transforms.Compose([ transforms.ToPILImage(), transforms.RandomHorizontalFlip(), transforms.RandomRotation(15), transforms.ToTensor(), transforms.Normalize(mean[0.507, 0.487, 0.441], std[0.267, 0.256, 0.276]) ]) test_transform transforms.Compose([ transforms.ToTensor(), transforms.Normalize(mean[0.507, 0.487, 0.441], std[0.267, 0.256, 0.276]) ])3. 分层分类模型架构设计3.1 基础特征提取网络我们使用改进的ResNet-18作为基础架构import torch.nn as nn import torchvision.models as models class HierarchicalResNet(nn.Module): def __init__(self): super().__init__() # 加载预训练ResNet并修改输入层 resnet models.resnet18(pretrainedTrue) resnet.conv1 nn.Conv2d(3, 64, kernel_size3, stride1, padding1, biasFalse) resnet.maxpool nn.Identity() # 移除初始下采样 # 特征提取部分 self.features nn.Sequential( resnet.conv1, resnet.bn1, resnet.relu, resnet.layer1, resnet.layer2, resnet.layer3, resnet.layer4, resnet.avgpool ) # 分类头 self.coarse_head nn.Linear(512, 20) # 粗粒度分类 self.fine_head nn.Linear(512, 100) # 细粒度分类 def forward(self, x): x self.features(x) x x.view(x.size(0), -1) return self.coarse_head(x), self.fine_head(x)3.2 层次感知损失函数设计考虑标签层次结构的损失函数class HierarchicalLoss(nn.Module): def __init__(self, alpha0.3): super().__init__() self.alpha alpha # 粗粒度损失权重 self.ce_coarse nn.CrossEntropyLoss() self.ce_fine nn.CrossEntropyLoss() def forward(self, outputs, targets): coarse_out, fine_out outputs coarse_target, fine_target targets # 计算两种损失 loss_coarse self.ce_coarse(coarse_out, coarse_target) loss_fine self.ce_fine(fine_out, fine_target) # 组合损失 return self.alpha * loss_coarse (1 - self.alpha) * loss_fine4. 训练策略与评估4.1 分层训练流程def train_model(model, criterion, dataloaders, optimizer, num_epochs100): for epoch in range(num_epochs): # 每个epoch包含训练和验证阶段 for phase in [train, val]: if phase train: model.train() else: model.eval() running_loss 0.0 coarse_correct 0 fine_correct 0 total 0 for inputs, (coarse_labels, fine_labels) in dataloaders[phase]: inputs inputs.to(device) coarse_labels coarse_labels.to(device) fine_labels fine_labels.to(device) optimizer.zero_grad() with torch.set_grad_enabled(phase train): coarse_outputs, fine_outputs model(inputs) loss criterion((coarse_outputs, fine_outputs), (coarse_labels, fine_labels)) if phase train: loss.backward() optimizer.step() # 统计指标 running_loss loss.item() * inputs.size(0) _, coarse_preds torch.max(coarse_outputs, 1) _, fine_preds torch.max(fine_outputs, 1) coarse_correct torch.sum(coarse_preds coarse_labels) fine_correct torch.sum(fine_preds fine_labels) total inputs.size(0) epoch_loss running_loss / total coarse_acc coarse_correct.double() / total fine_acc fine_correct.double() / total print(f{phase} Epoch {epoch}: Loss{epoch_loss:.4f}, fCoarse Acc{coarse_acc:.4f}, Fine Acc{fine_acc:.4f})4.2 性能评估指标除了常规的准确率我们还应该关注指标计算公式意义分层准确率粗/细粒度分类正确率评估不同层次性能一致性误差细粒度预测与粗粒度不一致的比例评估层次一致性混淆矩阵分析细粒度类别在粗粒度类别内的分布发现困难样本def evaluate_hierarchy(model, dataloader): model.eval() confusion np.zeros((20, 5, 5)) # 20个粗类每个粗类5个细类 with torch.no_grad(): for inputs, (coarse_labels, fine_labels) in dataloader: inputs inputs.to(device) coarse_labels coarse_labels.cpu().numpy() fine_labels fine_labels.cpu().numpy() coarse_out, fine_out model(inputs) _, fine_preds torch.max(fine_out, 1) fine_preds fine_preds.cpu().numpy() for c, f_true, f_pred in zip(coarse_labels, fine_labels, fine_preds): f_true_in_c f_true % 5 # 粗类内的相对索引 f_pred_in_c f_pred % 5 confusion[c, f_true_in_c, f_pred_in_c] 1 # 计算每个粗类内部的分类准确率 class_acc [] for c in range(20): class_acc.append(np.diag(confusion[c]).sum() / confusion[c].sum()) return confusion, class_acc5. 进阶技巧与优化方向5.1 知识蒸馏应用利用粗粒度标签指导细粒度分类class HierarchicalDistillationLoss(nn.Module): def __init__(self, temp2.0, alpha0.7): super().__init__() self.temp temp self.alpha alpha self.ce nn.CrossEntropyLoss() self.kl nn.KLDivLoss(reductionbatchmean) def forward(self, outputs, targets): coarse_out, fine_out outputs coarse_target, fine_target targets # 标准交叉熵损失 loss_fine self.ce(fine_out, fine_target) # 知识蒸馏损失 with torch.no_grad(): coarse_probs torch.softmax(coarse_out / self.temp, dim1) # 将粗粒度概率映射到细粒度空间 fine_from_coarse self._map_coarse_to_fine(coarse_probs) fine_student torch.log_softmax(fine_out / self.temp, dim1) loss_distill self.kl(fine_student, fine_from_coarse) * (self.temp**2) return self.alpha * loss_fine (1 - self.alpha) * loss_distill def _map_coarse_to_fine(self, coarse_probs): # 创建从粗粒度到细粒度的映射矩阵 mapping torch.zeros(20, 100) for c in range(20): mapping[c, c*5:(c1)*5] 1/5 mapping mapping.to(coarse_probs.device) return torch.matmul(coarse_probs, mapping)5.2 模型轻量化策略针对嵌入式设备部署的优化方案通道剪枝基于粗粒度重要性的卷积核剪枝量化感知训练8整数量化注意力机制添加轻量级SE模块class SELayer(nn.Module): def __init__(self, channel, reduction16): super().__init__() self.avg_pool nn.AdaptiveAvgPool2d(1) self.fc nn.Sequential( nn.Linear(channel, channel // reduction), nn.ReLU(inplaceTrue), nn.Linear(channel // reduction, channel), nn.Sigmoid() ) def forward(self, x): b, c, _, _ x.size() y self.avg_pool(x).view(b, c) y self.fc(y).view(b, c, 1, 1) return x * y6. 实际应用案例6.1 零售商品分级系统利用CIFAR-100的层次结构构建商品分类系统零售商品分类体系 ├── 食品 │ ├── 水果 │ ├── 零食 │ └── 饮料 ├── 电子产品 │ ├── 手机 │ └── 电脑 └── 家居用品 ├── 清洁用品 └── 厨具6.2 多粒度图像检索基于层次标签构建检索系统class HierarchicalRetriever: def __init__(self, model, database): self.model model self.database database # (paths, coarse_labels, fine_labels) def query(self, image, topk5, levelfine): with torch.no_grad(): features self.model.features(image) coarse, fine self.model.coarse_head(features), self.model.fine_head(features) if level coarse: _, pred torch.max(coarse, 1) mask (self.database[coarse] pred.item()) else: _, pred torch.max(fine, 1) mask (self.database[fine] pred.item()) # 返回同类别的topk最相似图像 distances compute_similarity(features, self.database[features][mask]) indices np.argsort(distances)[:topk] return self.database[paths][mask][indices]在医疗影像分析中类似的层次结构也很有价值——比如先区分影像模态X光/CT/MRI再识别具体病症。