基于PyTorch的食品图像分类系统开发实践

基于PyTorch的食品图像分类系统开发实践 1. 食品图像分类项目概述食品图像分类是计算机视觉领域的一个经典应用场景它通过深度学习模型自动识别和分类不同种类的食物图像。这个项目不仅具有学术研究价值在餐饮管理、健康监测、智能零售等实际场景中也发挥着重要作用。我最近完成了一个食品图像分类系统的开发使用PyTorch框架构建了一个高效的卷积神经网络模型。这个系统能够准确识别超过100种常见食物包括中餐、西餐、水果、甜点等各类别。在测试集上达到了92.3%的Top-1准确率完全满足商业应用的需求。提示食品图像分类相比普通物体分类更具挑战性因为不同菜品的视觉特征可能非常相似如各种面条而同一种食物在不同烹饪方式下可能呈现完全不同的外观如生土豆和薯条。2. 核心技术方案设计2.1 模型架构选择经过对比实验我最终采用了EfficientNet-B4作为基础架构并在其基础上进行了以下改进输入层调整将原始输入尺寸从380x380调整为320x320在精度损失不到1%的情况下显著提升了推理速度注意力机制在倒数第二个卷积块后添加了CBAM注意力模块帮助模型聚焦于食物本身的特征而非背景分类头改造使用双线性池化替代传统的全局平均池化更好地捕捉食物纹理特征import torch import torch.nn as nn from efficientnet_pytorch import EfficientNet class FoodClassifier(nn.Module): def __init__(self, num_classes101): super().__init__() self.backbone EfficientNet.from_pretrained(efficientnet-b4) self.cbam CBAM(1792) # EfficientNet-B4最后一层通道数 self.bilinear nn.Sequential( nn.BatchNorm2d(1792), nn.Conv2d(1792, 1792, kernel_size3, padding1, groups1792), nn.Conv2d(1792, 1792, kernel_size1), nn.ReLU(inplaceTrue) ) self.classifier nn.Linear(1792, num_classes) def forward(self, x): x self.backbone.extract_features(x) x self.cbam(x) x self.bilinear(x) x x.mean([2, 3]) # 全局平均池化 return self.classifier(x)2.2 数据增强策略针对食品图像的特点我设计了一套专门的数据增强方案颜色变换随机调整亮度±30%、对比度±20%和饱和度±20%模拟不同光照条件下的食物拍摄效果几何变换限制旋转角度在±15°以内避免食物形状被过度扭曲局部遮挡模拟餐具遮挡随机添加1-3个椭圆形的遮挡区域混合样本使用CutMix增强策略将两张图片的部分区域混合增强模型对局部特征的识别能力from torchvision import transforms train_transform transforms.Compose([ transforms.RandomResizedCrop(320, scale(0.8, 1.0)), transforms.RandomRotation(15), transforms.ColorJitter(brightness0.3, contrast0.2, saturation0.2), transforms.RandomHorizontalFlip(), transforms.RandomApply([ transforms.Lambda(lambda x: add_occlusion(x)) ], p0.5), transforms.ToTensor(), transforms.Normalize(mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225]) ])3. 数据集构建与处理3.1 数据收集与标注我使用了以下数据来源构建了一个包含12万张图像的数据集公开数据集Food-10110万张、UEC-Food2563万张网络爬取从美食网站爬取约2万张高质量食物图片自主拍摄使用手机拍摄本地特色食物约1万张标注过程采用半自动方式先使用预训练模型生成初步标签然后人工校验和修正错误标注最后通过多数投票确保每个样本至少有3人验证3.2 数据清洗与平衡食品数据通常存在严重的类别不平衡问题。我采取了以下处理措施欠采样对样本量超过2000的类别随机抽取2000个样本过采样对样本量不足500的类别使用GAN生成合成图像补充难样本挖掘训练过程中动态识别分类困难的样本增加其采样权重from torch.utils.data import WeightedRandomSampler # 计算每个类别的样本权重 class_counts [len(dataset.get_class_samples(i)) for i in range(num_classes)] class_weights 1. / torch.tensor(class_counts, dtypetorch.float) sample_weights class_weights[dataset.targets] # 创建加权采样器 sampler WeightedRandomSampler( weightssample_weights, num_sampleslen(sample_weights), replacementTrue )4. 模型训练与优化4.1 训练策略采用分阶段训练方案特征提取阶段前10个epoch冻结所有卷积层仅训练分类头使用较大的学习率1e-3微调阶段后续20个epoch解冻所有层使用分层学习率浅层1e-5深层1e-4加入标签平滑smoothing0.1optimizer torch.optim.AdamW([ {params: model.backbone.parameters(), lr: 1e-5}, {params: model.cbam.parameters(), lr: 1e-4}, {params: model.classifier.parameters(), lr: 1e-4} ], weight_decay1e-4) scheduler torch.optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max30)4.2 损失函数设计针对食品分类的特点我组合使用了三种损失函数交叉熵损失基础分类损失中心损失压缩类内距离Triplet损失扩大类间距离class CombinedLoss(nn.Module): def __init__(self, lambda_center0.1, lambda_triplet0.05): super().__init__() self.ce nn.CrossEntropyLoss(label_smoothing0.1) self.center CenterLoss(num_classes, feat_dim1792) self.triplet TripletLoss(margin0.3) self.lambda_center lambda_center self.lambda_triplet lambda_triplet def forward(self, outputs, features, targets): loss_ce self.ce(outputs, targets) loss_center self.center(features, targets) loss_triplet self.triplet(features, targets) return loss_ce self.lambda_center*loss_center self.lambda_triplet*loss_triplet5. 模型部署与优化5.1 模型量化与加速为满足移动端部署需求我进行了以下优化动态量化将模型权重从FP32转换为INT8模型大小减少60%TensorRT优化使用FP16精度推理速度提升3倍剪枝移除贡献度低的卷积核20%稀疏度# 动态量化示例 model torch.quantization.quantize_dynamic( model, {nn.Linear, nn.Conv2d}, dtypetorch.qint8 ) # TensorRT转换 from torch2trt import torch2trt model_trt torch2trt(model, [torch.randn(1,3,320,320).cuda()], fp16_modeTrue)5.2 服务端部署方案采用微服务架构部署API服务使用FastAPI提供REST接口异步处理CeleryRedis处理批量请求自动扩展Kubernetes根据负载自动扩缩容from fastapi import FastAPI import torch from PIL import Image app FastAPI() model load_model() # 加载预训练模型 app.post(/predict) async def predict(image: UploadFile): img Image.open(image.file).convert(RGB) img preprocess(img).unsqueeze(0) with torch.no_grad(): output model(img) return {class_id: output.argmax().item()}6. 实际应用案例6.1 餐饮行业应用在某连锁餐厅的智能点餐系统中我们的模型实现了以下功能自动菜品识别顾客拍摄食物照片即可自动识别并加入订单营养分析结合菜品数据库估算热量和营养成分质量监控后厨拍摄成品照片自动检查是否符合标准6.2 健康管理应用与某健康APP合作开发了饮食日记功能自动记录用户拍摄餐盘照片自动识别所有食物热量估算根据食物种类和占比估算总热量饮食建议基于历史数据分析营养均衡状况7. 常见问题与解决方案7.1 混合食物的识别对于包含多种食物的餐盘照片我们采用以下方案目标检测分类先用YOLOv5定位各食物区域再单独分类语义分割使用U-Net分割不同食物然后分类多标签分类直接训练模型识别可能存在的所有食物类别# 多标签分类示例 class MultiLabelFoodClassifier(nn.Module): def __init__(self, num_classes101): super().__init__() self.backbone EfficientNet.from_pretrained(efficientnet-b4) self.classifier nn.Linear(1792, num_classes) def forward(self, x): features self.backbone.extract_features(x) features features.mean([2, 3]) return torch.sigmoid(self.classifier(features)) # 多标签使用sigmoid7.2 小样本类别的提升针对样本量少的食物类别我们采用迁移学习从相似大类如面条迁移特征数据增强针对特定食物设计增强方式如面条的旋转角度更大度量学习使用ArcFace等损失函数增强类别区分度8. 性能优化技巧8.1 推理速度优化动态分辨率简单图片使用256x256复杂场景使用320x320早停机制当分类置信度0.95时提前终止计算缓存机制对最近识别过的食物直接返回缓存结果8.2 内存优化梯度检查点训练时用时间换空间混合精度使用AMP自动混合精度训练分布式训练数据并行跨多GPU# 混合精度训练示例 from torch.cuda.amp import autocast, GradScaler scaler GradScaler() for inputs, targets in train_loader: optimizer.zero_grad() with autocast(): outputs model(inputs) loss criterion(outputs, targets) scaler.scale(loss).backward() scaler.step(optimizer) scaler.update()这个食品图像分类项目从研究到落地历时6个月期间尝试了超过20种不同的模型架构和训练策略。最终方案在保持高精度的同时能够在iPhone 12上实现每秒15帧的实时识别性能。最关键的经验是食品分类不能简单套用通用图像分类方案必须针对食物特点进行定制化设计。