ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

从零构建快餐食物图像分类数据集:清洗、训练与避坑实践

从零构建快餐食物图像分类数据集:清洗、训练与避坑实践 简介数据集包含约1.8万张真实拍摄的快餐食物图片覆盖烤土豆、汉堡、脆皮鸡肉、甜甜圈、薯条、热狗、披萨、三明治、墨西哥卷和塔可共10个常见类别每张图均有明确标注可用于图像分类、目标检测等计算机视觉任务的模型训练与算法验证。资源包共2000个文件以jpg图片为主附带Python脚本和JSON标签映射文件便于读取与划分训练集/验证集整体大小约483MB适合人工智能初学者、算法工程师及高校相关专业学生作为实验数据。已有381人学习下载。借助该数据集读者可快速开展迁移学习、模型调参、数据增强等实践也可用于快餐识别、餐饮自动化等场景的技术验证还能结合标注文件分析类别分布与数据质量提升图像分类项目的完整落地能力。 做图像分类的朋友应该都有过这种体验想找个现成的数据集练手翻来覆去不是MNIST、CIFAR-10这种玩具级数据就是ImageNet这种动辄上百G的大块头。MNIST太简单训练完除了会用框架没啥收获ImageNet又太大光下载和预处理就劝退一半人。我前段时间正好需要一个中等规模、贴近真实生活场景的数据集来测试一个分类模型的泛化能力找了一圈没有特别合适的索性自己动手整理了一套10种常见快餐食物图像分类数据集。整个过程走下来从选类别、抓图、清洗到标注格式梳理踩了不少坑也总结了一些经验这里完整记录下来给同样需要自建数据集的朋友一个参考。这篇东西适合谁看呢一是准备入门图像分类、但不想用MNIST这类“幼儿园级别”数据集的初学者二是需要特定垂直领域数据尤其是食品类做算法验证的工程师三是想了解如何从零构建一份可用数据集、对数据工程流程感兴趣的读者。快餐食物这个方向有个天然优势图片来源极其丰富类别外观差异相对明显但也有大量容易混淆的边界情况比如汉堡和三明治、不同形态的炸鸡用来做分类模型训练和调优非常合适。1. 数据集的整体设计与类别选型1.1 为什么选择快餐食物以及这10个类别是怎么定的先说选类别的逻辑。市面上公开的食品数据集有几个比较有名的比如Food-101、Food-5K但它们的类别划分偏西餐正餐很多类别的图片风格高度统一摆盘精美、背景干净和真实快餐消费场景纸盒包装、桌面杂乱、灯光昏黄差距不小。我想要的是一个“更像手机随手拍”的数据集这样模型训练完扔到真实环境里才不会水土不服。最终锁定了10个类别汉堡、薯条、披萨、炸鸡、沙拉、三明治、墨西哥卷Burrito、寿司卷Sushi Roll、甜甜圈、冰淇淋。选择标准有三条第一覆盖度。10个类别要覆盖主餐汉堡、披萨、炸鸡、三明治、墨西哥卷、配餐薯条、沙拉、甜点甜甜圈、冰淇淋、以及相对跨界一点的主食寿司卷类别之间有明显的语义差异但也存在边界模糊的样本这样训练出来的模型才有区分度。第二真实场景高频出现。这几个类别是国内外快餐店、外卖平台上出现频率最高的品类后续如果要接外卖平台的图片审核、菜品识别、智能推荐等场景这套数据可以直接复用。第三类内差异足够大类间差异又不至于太小。快餐食物有个特点同一类别的食物不同品牌、不同门店做出来外观差异非常明显。比如“汉堡”有单层牛肉堡、双层炸鸡堡、迷你小汉堡每个样子都不一样“薯条”有细薯条、粗薯条、红薯条之分。这种类内多样性恰恰是训练鲁棒模型最需要的东西。1.2 数据集规模和各类别分布我给自己定的目标是每类900到1100张图片总共大约一万张。这个量级是经过考量的分类任务毕竟不是检测任务一万张图片、10个类别配合数据增强和迁移学习足够把模型的准确率推到90%以上而如果再往上加到每个类别几千张清洗和标注的人力成本会指数级上升收益却很有限。分布上刻意做了一点不均匀处理。薯条和汉堡这类常见食物图片最多各1000张上下寿司卷和沙拉这类相对“小众”一些的各850张左右。保留这种轻微的不平衡也方便测试模型在类别数量不均衡情况下的表现。实际分配数量如下类别图片数量约汉堡1050薯条1000披萨950炸鸡920三明治900墨西哥卷880沙拉860寿司卷850甜甜圈930冰淇淋9801.3 这份数据集的定位和适用场景这项数据集的主打优势是贴近真实视角。我特意筛选了大量消费场景拍摄的图片快餐店就餐桌面、外卖包装盒、手持咬了一口的状态这些图片普遍存在背景杂乱、光照不均、部分遮挡的问题比精心拍摄的菜单图要“难搞”得多。模型在这种数据上训练出来的效果直接上线到手机端识别或外卖图片理解任务基本上不需要做太多的域适应。当然它也存在局限。快餐食物和传统菜肴不一样包装、酱料、摆盘方式都在随品牌营销策略快速变化所以这份数据集的时效性需要关注——不是说三个月后就失效了而是如果要做长期跟踪需要定期补充新样式图片。这一点我在实际使用中体会很深后面会详细说。2. 数据采集与清洗的完整流程2.1 图片来源的三个渠道以及筛选标准快餐食物图片的获取渠道比想象中多但质量参差不齐。我试了三条路分别说下感受。第一是开源数据集抽取。Food-101、FoodX等数据集里本身就有快餐类别的图片可以直接按类别抽出来用。这是最省力的方式图片质量整体不错缺点是Viewpoint相对单一很多是标准俯拍图或摆拍图多样性不够。我把Food-101里的主要类别抽出来后发现单靠它撑不起一万张的目标还得靠其他来源补。第二是开放图片搜索抓取。Bing图片搜索、Google图片、还有国内的百度图片都开放了搜索接口用爬虫按关键词批量拉取。这里有个关键坑从搜索引擎抓图必须设定好语言和时间窗口。比如搜“汉堡”和搜“hamburger”得到的结果风格差异非常大中文语料里“汉堡”通常是本地快餐店实拍或者外卖展示图而英文语料更偏向美式标准宣传图。我最后是混合使用中英文关键词并特别关注了一年内新增的图片这样得到的类别内风格才够杂。第三是自己拍摄补充。数据缺失比较严重的类别比如寿司卷、墨西哥卷的某些形态我直接去了几个快餐厅用手机拍了几十张补充进去。比例不用高但真实感极强对提升模型泛化能力很有帮助。自拍图补进去的效果在后面训练中体现得很明显。2.2 清洗阶段最重要的一件事筛掉“非食物”图片这一步是我认为整个数据工程中最容易低估的工作量没有之一。搜索引擎抓回来的图大约有四成是不能用的大概分以下几类含有大面积水印和文字覆盖的图很多快餐测评图会被自媒体打上巨大的水印图片的主体也许是对的但文字会严重干扰模型让它学到“带logo的就是汉堡”这种错误模式。这种图必须删。包含人脸且人脸占比很大的图快餐图片里经常有人正在吃东西的场景人脸占据了大量画面。保留这些图会导致模型把“人脸”也当成判别特征之一等部署到无人售货柜或外卖分类场景时会出大问题。凡是人脸占了超过三分之一画面的直接删。重复图和近似图同一张图可能被不同网站反复转载爬下来数据里肉眼很难发现重复但训练时会偏置。需要用感知哈希pHash做去重相似度高于阈值的只保留一张。GIF动图和分辨率过低的图分类模型输入一般会缩放到224×224低于200×200的图放大后糊成一团直接弃用。GIF动图在抽帧时容易抽到中间过渡帧也不保留。食物被包装完全遮挡的图比如汉堡装在不透明纸袋里只能看到一个纸袋或者炸鸡被外卖盒完全盖住。这种图虽然有“食物”标签但实际像素里根本看不到食物本体留在数据集里只会制造噪声。清洗的标准操作流程是先用脚本自动筛掉重复、低分辨率和非JPG/PNG格式的图然后人工逐个过目一遍把水印严重、人脸占比过高、遮挡过度的图挑出来删掉。一万张图人工过一遍大概花了我两个晚上好在快餐图片辨认难度不高速度还算快。2.3 格式统一与文件规范清洗完的图片我统一转换为JPG格式因为JPG对照片类图像压缩友好而且绝大多数训练框架默认支持。文件名规范为类别名_序号.jpg比如hamburger_001.jpg。像素尺寸没有强制缩到统一大小只保证最短边不低于224像素因为训练环节还要做随机裁剪先统一尺寸反而会丢失细节。图片保留EXIF信息我没有刻意抹除因为快餐食物图片不涉及隐私传感数据保留EXIF也不影响模型训练。但如果后续要发布这套数据集的公开版本EXIF信息建议用工具统一清洗掉里面有GPS信息的话容易有隐私风险。这一点放这里多说一句避免有人踩坑。提示清洗阶段的删除决策最好记录到txt文件里别直接物理删除。我就吃过一次亏——删掉的“垃圾图”里有一批后来发现是某个测试集的关键场景图重新补拍浪费了一下午。保留一张“废弃清单”成本极低但能让你随时追溯清洗逻辑。3. 数据集的组织格式与训练配置3.1 目录组织与标注格式选择数据集目录结构是最经典的ImageNet风格按类别区分文件夹fastfood_dataset/ ├── train/ │ ├── hamburger/ │ ├── fries/ │ ├── pizza/ │ ├── fried_chicken/ │ ├── sandwich/ │ ├── burrito/ │ ├── salad/ │ ├── sushi_roll/ │ ├── donut/ │ └── ice_cream/ ├── val/ │ └── ... └── test/ └── ...这种结构对PyTorch的ImageFolder接口和TensorFlow的image_dataset_from_directory都是开箱即用不需要额外写解析逻辑。至于分类任务的标注文件用每类一个文件夹就足够不需要生成VOC或COCO那样的bbox标注和JSON文件——那个是目标检测和分割任务用的做纯分类别画蛇添足。3.2 数据划分比例和划分的注意事项划分比例我选用8:1:1训练集80%、验证集10%、测试集10%。这个比例在数据集规模1万左右时比较平衡验证集样本量也有接近1000张评估出来的指标不会忽高忽低。这里有几个划分时容易忽略的坑划分前必须先去重。如果原始数据里有完全相同的图同时出现在训练集和测试集里那测试集评估的意义就废了。先去重、再划分顺序不能反。划分要按类别分层。不要用RandomSplit不分层直接切这会导致某个类别在测试集里数量不足甚至缺失。用train_test_split时要设置stratify参数或者用splitfolders这类库保持类别比例。不同来源的图要打散分配。自拍的几十张图不要全塞训练集或全塞测试集尽量分散开否则测试集难度会被低估或高估。3.3 训练配置与数据增强方案模型的训练配置我这里直接给出一个可以跑的方案。用ResNet50做backbone加载ImageNet预训练权重冻结前40个block只训练后面的分类层学习率设为0.001跑20个epoch得到基线结果。之后再解冻最后两个残差块学习率降到0.0001再微调15个epoch一般准确率能再往上拉两三个点。数据增强方面快餐图像分类需要特别注意颜色扰动和随机旋转因为实际场景中快餐店的灯光色温差异极大——同一份薯条在暖黄色灯光下和户外自然光下拍摄色温差异可以导致模型判错。我在PyTorch里用的增强配置是from torchvision import transforms train_transform transforms.Compose([ transforms.RandomResizedCrop(224, scale(0.7, 1.0)), transforms.RandomRotation(15), transforms.ColorJitter(brightness0.3, contrast0.3, saturation0.3, hue0.1), transforms.RandomHorizontalFlip(), transforms.ToTensor(), transforms.Normalize(mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225]) ])验证集和测试集只做尺寸缩放和中心裁剪不引入随机增强保证评估指标的稳定性。3.4 从数据到模型的完整流程回顾整个数据集的构建到训练流程大概是这样先明确任务目标10类快餐食物分类然后确定类别列表和每个类别的图片规模接着做数据采集多源混合补齐再做清洗和去重然后统一格式、按类别归档最后划分训练集验证集测试集配置数据增强和模型训练。整个周期我实际花了大约一周。如果单纯从网上爬图两天就能爬完一万张但真正的瓶颈在清洗和人工复核环节这部分占了将近三天时间。训练环节反而最快单卡GPU跑ResNet5015分钟一个epoch半天就拿到最终结果。4. 常见问题与训练中的避坑经验4.1 类别混淆问题哪些快餐类别最容易互相错认模型训练完我专门看了混淆矩阵发现几个固定的混淆点这些也是这类任务里躲不开的难题。汉堡和吉事果或卷饼之间的混淆。做一个双层汉堡面包顶被挤扁视觉特征会和面饼卷起来的形态很像。尤其当拍摄角度是平视而非俯视时汉堡和墨西哥卷的轮廓高度相似。解决思路是在数据增强中加入小幅透视变换模拟不同拍摄角度让模型学会更多视角下的特征。炸鸡和甜甜圈的混淆。这听起来不可思议但确实发生了。问题出在“表面纹理”上——美式炸鸡的脆皮在特定光线下呈现金黄色颗粒状纹理而糖霜甜甜圈的表面也是类似的高光纹理。再加上两者经常出现在同一类偏暖色调的环境光里模型偶尔会把炸鸡判成甜甜圈。后来我补充了一批纹理更清晰的炸鸡近景图和更多带包装纸的甜甜圈图改善了不少。薯条错判成其他类别的情况。外卖盒里露出几根薯条旁边有汉堡的一角模型可能因为“画面里有什么”而误判但快餐图片里多食物同框实在太多了。我做的调整是训练时增加随机裁剪的比例强制模型学会关注主体区域而不是整张图的全局特征。4.2 数据不平衡问题与调整策略由于我的数据集本身比例差异不大这个问题不算严重但训练集里三类图片数量偏少沙拉、寿司卷、墨西哥卷确实对最终准确率有影响这三类的准确率平均比其他类别低2%到3%。遇到这种情况最直接的手段是类别加权采样。PyTorch里给WeightedRandomSampler传入每个类别的样本数量倒数作为权重让模型在每个epoch里对小类别的图片有更高概率采样到。我配置之后三个少数类别的准确率平均回升了1.5个百分点。另一种更省事的办法是用Focal Loss替代交叉熵损失它天然给难分类样本更高的权重在后面微调阶段对少样本类别帮助很大。不过如果数据集本身规模不大不要从第一轮训练就用Focal Loss容易让早期训练震荡建议先交叉熵跑到70%上下再切换。4.3 过拟合问题与正则化一万张图对ResNet50这种千万参数模型来说如果不做数据增强过拟合分分钟找上门——表现是训练集准确率接近99%验证集卡在85%上不去。除了前文说的数据增强我用了两个额外的正则化手段一是Dropout在最后的全连接层前面加一层nn.Dropout(p0.3)二是Label Smoothing把标签从纯0/1改成一个软标签分布比如真实类别的概率设为0.9其余9个类别各分0.011能有效防止模型对训练集过于自信。这两个操作叠加后验证集准确率提升了将近两个点也算是一个性价比非常高的调整。注意Label Smoothing的系数不要设太大0.1到0.2之间比较常见。我之前贪心试过0.3结果模型收敛变慢准确率不升反降。这个参数跟数据集本身的标签质量和类别数量都有关需要实际对比后再确认。4.4 训练过程中的实用检查方法训练过程中除了看loss和accuracy曲线我还习惯每隔几个epoch把模型在验证集上预测错误的图挑出来看看。这个习惯救了我好几次——有一次发现模型把大量“带包装纸的半透明冰淇淋”预测成沙拉抽查样本后发现是我在数据清洗时把一批冷饮杯装的沙拉误标成了冰淇淋。要不是定期人工查看错误样本这种标注噪声会一直潜伏在数据集里拖后腿。另外建议把测试集固定住不要每次训练都随机重划。数据集本身就是我们自己整理的测试集一旦变动对比实验的公平性就没有保障了。我把test目录固定好之后后面做了好几版模型对比结论才站得住脚。5. 后续扩展方向与个人感受5.1 数据集可以怎么扩展食物图像分类这个方向可以玩的花样不少简单列几个我打算尝试的扩展方向。加入卡路里或营养属性把分类标签升级成多任务输出除了类别同时预测大概的热量区间这对健康管理类应用有价值。层级化分类先粗分类别主餐、配餐、甜点再细分类目汉堡/三明治/披萨这样对结构更复杂的应用场景更友好。多标签化真实的快餐场景里一张图经常包含多个食物改成多标签分类这幅图里有薯条、有汉堡、没有冰淇淋更符合实际需求。目标检测的扩展把图像分类升级到目标检测用YOLO系列模型在同样图片上框出每个食物区域这也是热搜词里“yolov8训练自己的数据集”那条所对应的需求方向。做这项工作前需要把图片的标注从文件夹分类改成VOC或COCO格式工作量会增加不少但数据采集和清洗的思路是完全复用的。5.2 个人使用下来的一些体会最后聊几句真实的体验。构建这10种常见快餐食物图像分类数据集的过程让我最意外的是数据清洗对模型效果的影响远大于模型结构选择和调参。我拿同一套数据、同一个ResNet50清洗前和清洗后训练的准确率差距达到了六七个点这个差距比我换一遍模型架构还要明显。很多刚开始接触深度学习的同学把精力全投入到模型优化上动不动就换backbone、调学习率但数据质量本身不过关的话后面做得再多也是白搭。用这套快餐数据集训练出来的模型我在一个外卖出餐照片分类的小demo里做了验证真实图片上Top-1准确率能到87%左右Top-5在96%上下可见一万张规模、真实场景风格的数据集配合迁移学习在垂直领域分类任务上的表现并不比大型公开数据集差多少。这对我后续规划类似项目是一个很好的参考也希望能给读到这篇内容的朋友带来一些启发。本文还有配套的精品资源点击获取
返回列表