ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

296类服装品牌Logo识别:细粒度图像分类数据集与训练实战

296类服装品牌Logo识别:细粒度图像分类数据集与训练实战 简介这是一份可用于图像分类模型训练与评估的服装品牌logo数据集覆盖Vans、Adidas、Supreme、Wild等296类知名潮流与运动品牌标识适合计算机视觉学习者、算法工程师以及需要快速搭建分类基线模型的开发者。数据已按文件夹划分为训练集和测试集训练集16446张、测试集3967张合计两万余张图片按类别集中存放可直接用ImageFolder加载免去整理标签与目录的步骤也能作为YOLOv5分类数据使用。压缩包共2000个文件主体为1998张jpg图片另含1个Python可视化脚本和1个json文件整体大小约174.45MB目录结构简单清晰。其中可视化脚本无需修改即可运行随机抽取图片展示并保存结果方便核对各类别数据质量与标注情况。目前已有204人学习/下载适合作为多类别Logo识别、图像分类实践或模型验证的数据支撑。1. 图像分类数据集296种服装品牌Logo识别值不值得做细粒度分类做服装电商或者品牌监测的工程师大概率遇到过一个尴尬场景运营丢过来一批用户晒单图问“这几张图的衣服是哪个牌子的能不能自动打标”。肉眼一眼就能认出来的优衣库、ZARA、耐克到了模型手里却常常翻车——不是认成近似品牌就是直接给个“背景”标签。这个问题的本质不是模型不行而是缺一个足够细、足够干净、已经划分好训练集/验证集/测试集的图像分类数据集。标题里这套大型296种服装品牌logo图像分类数据集对应的正是这个需求把细粒度品牌logo识别做成一个“开箱即跑”的分类任务而不是让每个团队都从爬图、清洗、标注开始重复造轮子。和CIFAR-10、ImageNet那种通用物体分类不同logo分类是典型的细粒度图像分类类别之间差距非常小。adidas的三叶草和adidas neo、HM和COS、优衣库和无印良品很多logo在缩略图上几乎只有边缘细节的差别。这类任务真正的门槛不在模型结构而在数据本身的区分度、划分是否合理、训练时有没有把同源图片泄漏到多个集合里。所以这篇落地笔记会围绕这套数据集做完三件事先讲清楚拿到数据集后该如何检查结构、再做一次可靠的类别划分复查然后给出可直接套用的图像分类训练方案最后把细粒度logo分类最容易踩的坑一条条列出来。2. 认识296类品牌Logo数据集结构、采样和划分策略2.1 先看目录结构再决定要不要信“已划分”拿到任何一套“已做数据集划分”的图像分类数据集第一件事不是急着训练而是先把目录结构和样本分布摸清楚。常见做法是train/val/test三个根目录每个目录下按类别分子文件夹文件夹名是品牌名或品牌ID。验证方式很简单统计每个子目录下的图片数量看三类集合的样本比例是否和你预期的8:1:1或7:2:1接近同时确认296个类别是否在每个集合里都完整出现。如果某个类别在train里有几百张、在val里只剩3张那这个类别的验证结果基本就是噪声。我会先写一个极简的统计脚本来做这件事脚本只依赖Python标准库不需要装任何深度学习框架就能在数据集根目录跑通。import os from collections import defaultdict root path/to/logo_dataset # 数据集根目录,下面挂着train/val/test splits [train, val, test] for split in splits: split_path os.path.join(root, split) classes os.listdir(split_path) counts [] for cls in classes: cls_path os.path.join(split_path, cls) n len(os.listdir(cls_path)) counts.append((cls, n)) counts.sort(keylambda x: x[1]) print(f[{split}] 类别数: {len(counts)}, 总样本数: {sum(c for _, c in counts)}) print( 最少样本类别:, counts[:5]) print( 最多样本类别:, counts[-5:])这段脚本的逻辑很简单先把每个split下的类别名读出来然后逐个统计每个类别的图片数量最后把最少和最多的类别分别打印出来。为什么要关注最少和最多因为296类logo数据天然是长尾分布大牌如耐克、阿迪的样本可能上千小众潮牌的样本可能只有几十张。如果某个类在val或test里数量少于5后面训练时的类别权重和评估指标都需要单独处理否则验证集上偶然性会非常大。统计完数量之后我还会做一次可视化检查。用t-SNE或者PCA把图片特征投影到二维平面按类别着色看同一类别的样本是否聚在一起。如果某个类别的点分散成好几簇说明这个品牌可能有多个子系列或者旧版logo混在一起训练时模型会学成一个“多中心”分布推理时对中间态样本会很不稳定。2.2 数据划分的三种策略各自有什么代价标题强调“已做数据集划分”但划分本身也有好坏之分。最常见的做法是纯随机划分按比例在所有图片上随机打散好处是代码简单、每个集合的类别分布基本一致坏处是如果数据里存在大量同一商品的不同拍摄角度图片纯随机划分会把同一组图同时分进train和test导致验证分数虚高——模型其实记住了那些图而不是真正学会了区分品牌。更稳妥的做法是分层划分先按类别分组在每个类别内部做随机切分。这样可以保证每一个品牌在三个集合中都存在避免某个类别因为随机种子问题被整个漏掉。缺点是操作比全局随机略复杂需要手动按类别循环做切分。还有一种更严格的方式是按拍摄场景或图片来源做分组划分比如同一个店铺、同一次上架活动的商品图只进train或val其中之一模拟真正的“跨场景识别”难度。这种划分会让准确率看起来比前两种低几个点但更接近真实线上场景。对于这套296种品牌logo数据集我建议至少做两层检查第一确认当前划分是否是分层划分——每个类别在train/val/test中都出现第二用图片相似度抽样检查是否存在同源图泄漏——随机抽10个类别每个类别抽5张train和5张test图人工对比是否来自同一商品或同一拍摄环境。如果确实存在泄漏后续训练出来的准确率只能当作上限参考不能作为上线指标。2.3 用分层切分重做一个不泄漏的划分如果检查下来发现当前划分不太满意或者你拿到的数据是未划分的原始文件夹自己动手重做一次划分并不复杂。这里给出一套基于scikit-learn的分层切分脚本按类别分层、可复现、支持按需调整比例。import os import shutil import random from sklearn.model_selection import train_test_split random.seed(42) src_root raw_images # 原始图片按类别分文件夹 dst_root logo_dataset_split # 输出目录 train_ratio, val_ratio 0.8, 0.1 # 剩下0.1作为test os.makedirs(dst_root, exist_okTrue) for split in [train, val, test]: os.makedirs(os.path.join(dst_root, split), exist_okTrue) for cls in os.listdir(src_root): cls_path os.path.join(src_root, cls) if not os.path.isdir(cls_path): continue imgs os.listdir(cls_path) train_imgs, tmp_imgs train_test_split(imgs, test_size(1 - train_ratio), random_state42) val_imgs, test_imgs train_test_split(tmp_imgs, test_sizeval_ratio / (1 - train_ratio), random_state42) for split, split_imgs in [(train, train_imgs), (val, val_imgs), (test, test_imgs)]: out_dir os.path.join(dst_root, split, cls) os.makedirs(out_dir, exist_okTrue) for img in split_imgs: shutil.copy(os.path.join(cls_path, img), os.path.join(out_dir, img))这段脚本的核心在于调用了两次train_test_split第一次把类别样本分为train和temporary第二次把temporary再分为val和test。关键参数random_state42保证了切分结果可复现换机器重跑结果一致。test_size的计算用了条件比例第一次抽出的tmp规模是20%第二次要在tmp内部再取50%作为val、50%作为test所以传入的test_size是0.1/0.20.5。这套脚本的局限性在于没有做同源去重。如果原始数据里同一个商品有10张不同角度的图片它们依然会被随机分到不同集合。要彻底解决需要在切分前按文件名前缀或MD5哈希做去重把同一组图绑定到一个group_id上再划分这个操作更复杂但只有做过你才会知道验证集分数有多少水分。3. 模型选型与训练ResNet还是ViT冻结还是全量微调3.1 细粒度Logo分类的模型选型逻辑296类品牌logo识别属于细粒度图像分类模型选型上优先考虑两个方向一是以ResNet为代表的CNN系列二是以ViT为代表的Transformer系列。对于这类任务我的经验是优先尝试在ImageNet上预训练过的ResNet50或ResNet101而不是一上来就上ViT。原因在于品牌logo之间的差异集中在局部纹理和边缘走势CNN的诱导偏置对这类任务有天然优势而且ResNet在中小规模数据上的收敛速度和稳定性明显更好。ViT需要更大数据量才能发挥优势296类logo数据集即便每类几百张总量也不过几万张ViT在这种规模下容易欠拟合。训练策略上常见做法是微调而非从零训练。加载一个预训练权重把最后的全连接层替换成296维输出整个网络用较小的学习率继续训练。需要决策的是冻结多少层如果每类样本只有几十张建议冻结ResNet前两个stage只微调后面stage和分类头如果每类样本超过两百张可以直接全量微调。这里的判断标准是数据量决定可训练参数量样本不足时强行全量微调只会让模型在训练集上过拟合验证集上快速退化。3.2 标签平滑、类别加权和增强配置训练细粒度分类时有几个小配置直接影响最终效果。第一个是标签平滑把交叉熵损失的one-hot标签从1替换成0.9和0.1/295可以缓解模型对训练集标签的过度自信对296类这样类别数较多的任务尤其有效。第二个是类别加权因为logo数据集天然长尾对小样本类别的loss乘以一个权重系数让模型不会完全被大样本类别带偏。第三个是数据增强控制在合理范围才不会让logo纹理失真。import torch import torch.nn as nn import torchvision.models as models from torch.utils.data import DataLoader, WeightedRandomSampler model models.resnet50(weightsmodels.ResNet50_Weights.IMAGENET1K_V1) in_features model.fc.in_features model.fc nn.Linear(in_features, 296) criterion nn.CrossEntropyLoss(label_smoothing0.1) class_counts torch.tensor([1200, 85, 300, ...]) # 按类别顺序填入实际样本数 weights 1.0 / class_counts.float() sampler WeightedRandomSampler(weights, num_sampleslen(weights) * 100, replacementTrue) train_loader DataLoader(train_dataset, batch_size64, samplersampler, num_workers8)这里的关键点有三个CrossEntropyLoss的label_smoothing参数直接实现标签平滑不需要自己改标签WeightedRandomSampler按每类样本数的倒数采样让少样本类别在同一个batch里出现的概率更高num_samples控制每个epoch的采样长度这里设置为weights长度乘100意味着每个epoch大约采样29600张图sample效率略高于原始数据集的随机采样。注意WeightedRandomSampler的sampler和DataLoader的shuffle参数不能同时设置shuffle必须保持默认False否则会报错。同时类别权重需要与数据集的类别顺序一一对应如果数据集用的是ImageFolder读取方式类别顺序是类名字母序必须把统计数量和这个顺序对齐否则加权就加错了对象。3.3 训练超参数的基准设置与调整空间对于296类logo分类一个比较稳妥的基准配置是输入分辨率224x224batch size 64初始学习率1e-4优化器AdamW权重衰减1e-4训练20到30个epoch。如果使用ResNet50单张V100上每epoch大约一分多钟整个训练在30到40分钟内可以完成。学习率调度上用Cosine Annealing或者阶梯下降每10个epoch把学习率乘0.1。一个常见分歧点是输入分辨率。有些团队会用128x128甚至96x96来提速但logo这种依赖小字边缘细节的任务分辨率不足会导致品牌名文字糊成一团准确率肉眼可见掉3到5个点。我的建议是不要低于160x160如果训练资源紧张优先保证分辨率而不是增大batch size。另外训练时开启混合精度能省下约一半显存batch size可以乘1.5到2倍对最终准确率没有明显负面影响。训练过程中建议每过5个epoch保存一次检查点同时记录val准确率。如果一个epoch内train准确率快速上升但val准确率停滞甚至下降说明已经过拟合应当停止训练而不是继续跑完全部epoch。这条经验对细粒度logo分类尤其重要因为类别间差异极小模型很容易靠着记住背景颜色和图片风格“作弊”而没有真正学会区分logo本身。4. 训练中的常见问题排查从翻车现场到正确做法4.1 坑一训练集准确率95%验证集却一直卡在70%现象train准确率一路涨到95%以上val准确率却在60%到70%之间震荡看不出上升趋势。很多人在这一步开始调整模型结构或者加大增强力度但往往解决不了问题。原因最可能是数据划分时存在同源图片泄漏或者模型严重依赖背景特征。同源泄漏就是同一商品的不同角度图同时出现在train和val中模型学到的是“这几张图的亮度/背景/拍摄台”而不是logo本身。另一种可能是训练集和验证集来自不同拍摄环境train都是白底商品图val都是用户实拍图特征分布差异过大。解决先做数据来源分组重划分保证同一来源的图片进同一个split然后再训练如果确认划分没问题就用类激活映射工具把模型关注的区域可视化看是不是聚焦在图片角落或背景上。如果是需要增加背景增强或裁剪增强让模型只能依赖logo区域做判断。4.2 坑二296个类别里有十几个类别的召回率接近0现象整体准确率看起来还行80%上下但逐个类别看指标时某几个品牌类别在val上的精确率和召回率都是0。原因这些类别在训练集中样本量太少比如只有3到5张图模型在采样的过程中几乎每轮都看不到它们。即便用了类别加权数据量过少时模型也学不到稳定的特征val中这几张图一旦拍摄角度稍有不同立刻识别失败。解决对这种数量极少的类别先做同类别数据增强把每张图通过旋转、裁剪、颜色抖动生成10到20张变体扩充后再训练。如果数据集本身已经划分好且不便重新生成可以在训练集单独对少数类别做过采样让每个batch里这些类别的出现次数不低于1次。这个方案不能完全解决问题但能把召回率从0拉到60%左右对批量识别场景已经够用。4.3 坑三val准确率稳定test准确率却掉10个点以上现象训练过程中val准确率稳定在88%左右训练完成后拿test集合做最终验收准确率只有75%上下。原因最常见的情况是val集合参与过调参或早停模型在val上做了隐式的“后验选择”val指标被高估另一种情况是test集合和val集合分布不一致比如train和val都来自A类场景test里混入了大量B类场景图片而B类场景与A类差异明显。解决模型训练完全结束后只能允许跑一次test禁止用test结果反复调整超参数。如果test准确率显著低于val优先检查test集合的图片来源与train/val是否一致而不是急着换模型。另外做模型选择时应该在val上挑最优epoch而不是在test上挑这是应对“val高test低”的最直接手段。4.4 坑四训练时显存溢出或者训练速度无法接受现象模型加载正常数据加载正常但只要开始训练几轮之后显存直接OOM或者一个epoch要跑近十分钟总训练时间几个小时起步。原因常见原因是batch size过大、输入分辨率过高或者DataLoader的num_workers设为0导致数据加载瓶颈。细粒度分类任务里很多人习惯用高分辨率224或299但batch size没有同步调低显存自然不够。解决先调低batch size比如从64降到32确认显存可控后再逐渐往回升num_workers设为4到8用CPU多进程预取图片可以明显降低GPU等待时间。如果显存仍然吃紧开启混合精度训练能省大约40%显存。注意混精训练时要把BatchNorm层保持fp32PyTorch的autocast会自动处理但不要手动把模型整体转成half。5. 从训练到验证一个可复现的小型微调工作流前面聊了结构检查、划分策略、模型选型和踩坑点这套296种服装品牌logo图像分类数据集的完整使用路径就清晰了。最后给出一套我自己常用的最小微调工作流可以直接作为基线在拿到已划分数据集后按顺序执行。import torch import torchvision.transforms as T from torch.utils.data import DataLoader from torchvision.datasets import ImageFolder transform_train T.Compose([ T.Resize((224, 224)), T.RandomHorizontalFlip(p0.3), T.ColorJitter(brightness0.2, contrast0.2), T.ToTensor(), T.Normalize(mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225]), ]) transform_eval T.Compose([ T.Resize((224, 224)), T.ToTensor(), T.Normalize(mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225]), ]) train_ds ImageFolder(logo_dataset/train, transformtransform_train) val_ds ImageFolder(logo_dataset/val, transformtransform_eval) train_loader DataLoader(train_ds, batch_size64, shuffleTrue, num_workers8) val_loader DataLoader(val_ds, batch_size64, shuffleFalse, num_workers8)这个配置里Resize到224x224是细粒度分类的基准选择除非GPU资源极度紧张不轻易降到128以下。RandomHorizontalFlip只给了0.3的概率而不是0.5是因为部分服装品牌logo带有文字方向过度翻转反而会生成反着的字母干扰模型学习真实特征。ColorJitter的brightness和contrast各0.2模拟真实拍摄环境中的光线差异但对于白底商品图过强的颜色增强没有必要。训练完之后的验证环节我最常做的事是输出混淆矩阵专门看哪些品牌两两之间容易被搞混。比如常见的“三叶草与neo”“ZARA与Stradivarius”“优衣库与GU”这类近似品牌如果混淆矩阵里这些品牌对出现高频错误说明模型还没有捕捉到它们logo之间的关键区分细节。此时可以回去检查训练集中这些类别的样本数量如果某一方样本过少按前面第4章的方法做类别均衡。验证准确率只能告诉你模型“有多好”但不会告诉你在实际场景里怎么用。我习惯在推理阶段加一个置信度阈值比如0.6置信度低于阈值的样本直接标记为“不确定”而不是强行走296选1。理由很直接logo分类的真实场景里经常出现品牌不在296类范围内的情况强行分类必然出错。加阈值之后系统会把低置信度图片转人工或者转入待确认队列这个操作在实际业务中比提升两个准确率点更有价值。做离线评估时记得保留一批从未参与训练/验证的图片做最终压力测试最好是不同季节、不同光线、不同拍摄设备下的品牌logo图。这是许多团队最容易跳过的环节——val集合在训练过程中被反复参考已经相当于训练的一部分只有在全新采集的数据上跑一次的准确率才是接近线上真实水平的数字。做完这套流程这套数据集的价值才算是真正被用满。希望这些经验帮到你。本文还有配套的精品资源点击获取
返回列表