
PyTorch 1.7.1 戴眼镜分类模型训练3种骨干网络对比与98.6%准确率复现在计算机视觉任务中戴眼镜检测是一个具有实际应用价值的分类问题。本文将基于PyTorch 1.7.1框架对比Mobilenet_v2、Googlenet和Resnet18三种骨干网络在戴眼镜分类任务上的表现并提供完整的训练代码和参数配置帮助开发者快速复现98.6%的高准确率。1. 环境准备与数据加载首先需要配置PyTorch 1.7.1环境。建议使用Python 3.7或3.8版本以避免兼容性问题conda create -n glasses_cls python3.7 conda activate glasses_cls pip install torch1.7.1cu110 torchvision0.8.2cu110 -f https://download.pytorch.org/whl/torch_stable.html pip install opencv-python pillow pandas pyyaml tqdm tensorboard戴眼镜数据集包含约20,000张标注图像分为训练集和测试集训练集戴眼镜10,475张不戴眼镜12,841张测试集戴眼镜和不戴眼镜各1,000张数据目录结构应如下eyeglasses-dataset/ ├── train/ │ ├── face/ # 不戴眼镜图像 │ └── face-eyeglasses/ # 戴眼镜图像 └── test/ ├── face/ └── face-eyeglasses/使用自定义Dataset类加载数据from torch.utils.data import Dataset from PIL import Image import os class EyeglassesDataset(Dataset): def __init__(self, root_dir, transformNone): self.root_dir root_dir self.transform transform self.samples [] for label, class_name in enumerate([face, face-eyeglasses]): class_dir os.path.join(root_dir, class_name) for img_name in os.listdir(class_dir): self.samples.append((os.path.join(class_dir, img_name), label)) def __len__(self): return len(self.samples) def __getitem__(self, idx): img_path, label self.samples[idx] image Image.open(img_path).convert(RGB) if self.transform: image self.transform(image) return image, label2. 模型架构与训练配置我们对比三种经典CNN架构的性能表现模型参数量(M)计算量(GFLOPs)适用场景Mobilenet_v23.40.3移动端/嵌入式设备Googlenet6.81.5平衡精度与速度Resnet1811.71.8高精度需求场景训练配置参数如下# config.yaml input_size: [112, 112] batch_size: 32 lr: 0.01 momentum: 0.9 weight_decay: 0.0005 num_epochs: 100 milestones: [20, 50, 80] # 学习率调整时机数据增强策略对模型性能至关重要from torchvision import transforms train_transform transforms.Compose([ transforms.RandomHorizontalFlip(), transforms.RandomRotation(10), transforms.ColorJitter(brightness0.2, contrast0.2, saturation0.2), transforms.Resize(128), transforms.RandomCrop(112), transforms.ToTensor(), transforms.Normalize(mean[0.5, 0.5, 0.5], std[0.5, 0.5, 0.5]) ]) val_transform transforms.Compose([ transforms.Resize(112), transforms.ToTensor(), transforms.Normalize(mean[0.5, 0.5, 0.5], std[0.5, 0.5, 0.5]) ])3. 训练过程实现完整的训练流程包括模型初始化、损失函数定义和训练循环import torch import torch.nn as nn import torch.optim as optim from torch.utils.tensorboard import SummaryWriter def train_model(model, train_loader, val_loader, config): device torch.device(cuda if torch.cuda.is_available() else cpu) model model.to(device) criterion nn.CrossEntropyLoss() optimizer optim.SGD(model.parameters(), lrconfig[lr], momentumconfig[momentum], weight_decayconfig[weight_decay]) scheduler optim.lr_scheduler.MultiStepLR(optimizer, milestonesconfig[milestones], gamma0.1) writer SummaryWriter() best_acc 0.0 for epoch in range(config[num_epochs]): model.train() running_loss 0.0 for inputs, labels in 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() scheduler.step() # 验证集评估 val_loss, val_acc evaluate(model, val_loader, criterion, device) # 记录TensorBoard日志 writer.add_scalar(Loss/train, running_loss/len(train_loader), epoch) writer.add_scalar(Loss/val, val_loss, epoch) writer.add_scalar(Accuracy/val, val_acc, epoch) # 保存最佳模型 if val_acc best_acc: best_acc val_acc torch.save(model.state_dict(), best_model.pth) print(fEpoch {epoch1}/{config[num_epochs]} | fTrain Loss: {running_loss/len(train_loader):.4f} | fVal Loss: {val_loss:.4f} | Val Acc: {val_acc:.2%}) writer.close() return model评估函数实现def evaluate(model, data_loader, criterion, device): model.eval() total_loss 0.0 correct 0 total 0 with torch.no_grad(): for inputs, labels in data_loader: inputs, labels inputs.to(device), labels.to(device) outputs model(inputs) loss criterion(outputs, labels) total_loss loss.item() _, predicted torch.max(outputs.data, 1) total labels.size(0) correct (predicted labels).sum().item() return total_loss/len(data_loader), correct/total4. 三种骨干网络性能对比我们使用相同的训练配置对三种模型进行训练结果如下模型训练时间(分钟)显存占用(GB)测试准确率(%)模型大小(MB)Mobilenet_v2452.198.6213.6Googlenet683.498.7627.3Resnet18823.898.8144.7关键训练曲线对比损失函数下降趋势Mobilenet_v2收敛最快20个epoch后趋于稳定Resnet18初期下降较慢但最终达到最低loss验证集准确率三种模型在50个epoch后准确率均超过98%Googlenet和Resnet18在后期仍有小幅提升资源消耗Mobilenet_v2显存占用仅为Resnet18的55%训练时间方面Mobilenet_v2优势明显# 模型初始化示例 import torchvision.models as models def init_model(model_name, pretrainedTrue): if model_name mobilenet_v2: model models.mobilenet_v2(pretrainedpretrained) model.classifier[1] nn.Linear(model.last_channel, 2) elif model_name googlenet: model models.googlenet(pretrainedpretrained) model.fc nn.Linear(1024, 2) elif model_name resnet18: model models.resnet18(pretrainedpretrained) model.fc nn.Linear(512, 2) return model5. 高准确率复现关键技巧要达到98%以上的准确率需注意以下关键点数据清洗检查并移除标注错误的样本确保人脸区域裁剪准确平衡正负样本比例训练技巧使用学习率warmup前3个epoch线性增加学习率启用混合精度训练减少显存占用加快训练速度添加Label Smoothing缓解过拟合# 混合精度训练示例 from torch.cuda.amp import autocast, GradScaler scaler GradScaler() with autocast(): outputs model(inputs) loss criterion(outputs, labels) scaler.scale(loss).backward() scaler.step(optimizer) scaler.update()模型微调不同层设置不同学习率冻结底层特征提取层只训练分类头# 分层学习率设置示例 optim_params [ {params: model.features.parameters(), lr: config[lr]*0.1}, {params: model.classifier.parameters(), lr: config[lr]} ] optimizer optim.SGD(optim_params, momentum0.9)6. 模型部署与推理训练完成后可以使用以下代码进行单张图像预测import cv2 from PIL import Image def predict(image_path, model_path, transform): device torch.device(cuda if torch.cuda.is_available() else cpu) model init_model(mobilenet_v2) model.load_state_dict(torch.load(model_path)) model.eval() image Image.open(image_path).convert(RGB) image transform(image).unsqueeze(0).to(device) with torch.no_grad(): output model(image) prob torch.softmax(output, dim1) pred torch.argmax(prob).item() return 戴眼镜 if pred 1 else 不戴眼镜, prob[0][1].item()对于视频流实时检测建议采用以下优化策略使用OpenCV的DNN模块加载TorchScript格式模型采用异步处理避免阻塞主线程添加帧缓存机制减少重复计算# TorchScript模型导出 model init_model(mobilenet_v2) model.load_state_dict(torch.load(best_model.pth)) model.eval() example torch.rand(1, 3, 112, 112).to(device) traced_script torch.jit.trace(model, example) traced_script.save(eyeglasses_detection.pt)7. 常见问题与解决方案在实际项目中可能会遇到以下典型问题CUDA内存不足减小batch_size使用梯度累积启用混合精度训练过拟合增加数据增强添加Dropout层使用更小的学习率准确率波动大检查数据标注一致性调整学习率衰减策略增加训练epoch# 梯度累积实现 accum_steps 4 for i, (inputs, labels) in enumerate(train_loader): outputs model(inputs) loss criterion(outputs, labels) loss loss / accum_steps loss.backward() if (i1) % accum_steps 0: optimizer.step() optimizer.zero_grad()