尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

混凝土骨料粒度图像识别:数据集划分、类别字典与迁移学习实战

发布时间:2026/9/27 23:08:34

资讯中心
01
ARTICLE

混凝土骨料粒度图像识别:数据集划分、类别字典与迁移学习实战

混凝土骨料粒度图像识别:数据集划分、类别字典与迁移学习实战
简介这份资源面向从事混凝土材料分析、工业质检及计算机视觉研究的人员提供了一套已划分完毕的混凝土骨料粒度图像识别分类数据集可直接用于图像分类模型训练与验证。数据按train与val两个目录组织训练集600张、验证集270张共9个类别涵盖A、B、C三组骨料在8、16、32三种粒度下的组合并附类别字典json文件方便标签映射与结果解读。压缩包共902个文件以900张jpg图像为主体另含1个Python可视化脚本和1个json字典整体约43.03MB体积轻便易于传输与部署。可视化脚本支持随机传入4张图片即可展示样本效果并保存至当前目录便于快速检查数据分布与图像质量。该数据集可直接作为YOLOv5分类任务的数据源配合作者在神经网络改进方向的系列博文适合需要快速验证分类算法、开展骨料粒度识别实验的初学者与进阶开发者。目前已有62人学习下载。1. 混凝土骨料粒度图像识别从一份划分好的数据集说起混凝土骨料粒度直接决定拌合物的和易性、硬化后的强度与耐久性但传统筛分法测粒度分布耗时、离线无法在搅拌站皮带上实时反馈。用图像识别替代筛分核心卡点从来不是模型结构而是数据现场拍回来的骨料堆叠、遮挡、光照不均标注口径不统一训练集和验证集按同一批料划分导致指标虚高。这份「混凝土骨料粒度图像识别分类」数据集把划分好的训练/验证/测试目录、类别字典文件和 Python 数据可视化脚本一起给出解决的正是从拿到原始图像到能跑通第一版分类器之间的脏活。它适合两类人一类是想切入工业视觉但缺干净数据的算法工程师另一类是做建材检测、搅拌站质量控制、需要把粒度识别落地的现场技术人员。下面按「数据长什么样 → 怎么读 → 怎么训 → 坑在哪」的顺序拆开讲所有步骤都能照着复现。2. 数据集结构与类别字典先搞清楚目录和标签怎么对应2.1 划分好的目录长什么样一份能直接用的图像分类数据集目录结构决定了你后面写 DataLoader 时要不要额外写映射逻辑。常见做法是按ImageFolder的约定组织即每个类别一个子目录训练、验证、测试三份并列。下面是我一般会先跑的目录探查脚本用来确认层级和每类样本数避免拿到手就闷头训练。import os from collections import Counter ROOT concrete_aggregate # 数据集根目录按实际路径改 for split in [train, val, test]: split_dir os.path.join(ROOT, split) if not os.path.isdir(split_dir): print(f[跳过] 未找到 {split_dir}) continue # 每个子目录名即类别名统计该类图像数量 counter Counter() for cls in sorted(os.listdir(split_dir)): cls_dir os.path.join(split_dir, cls) if os.path.isdir(cls_dir): n len([f for f in os.listdir(cls_dir) if f.lower().endswith((.jpg, .jpeg, .png, .bmp))]) counter[cls] n total sum(counter.values()) print(f {split} 共 {total} 张 ) for cls, n in counter.items(): print(f {cls:20} {n:6} 张 占比 {n/total:.1%})这段代码的逻辑很直白遍历三个 split把每个类别子目录里的图像文件计数。参数上唯一需要改的是ROOT其余不用动。重点看两个输出一是三个 split 的类别名是否完全一致如果train有 5 类而val只有 4 类说明划分时漏了类训练时ImageFolder会按各自目录重建类别索引导致标签错位二是每类占比如果某一类在训练集里占比超过 60%后面评估时准确率会被这一类带偏需要看混淆矩阵而不是只看总 acc。2.2 类别字典文件怎么读、怎么用类别字典文件通常是class_dict.json或classes.txt是图像分类里最容易被忽视、又最容易翻车的一环。它的作用是固定「类别名 ↔ 整数索引」的映射。ImageFolder默认按目录名字母序生成索引一旦你手动重排目录、或者在不同机器上文件系统排序不一致索引就会漂移模型输出的argmax对应的类别名就错了。所以正确做法是训练前读字典用字典的顺序显式构建类别列表而不是依赖ImageFolder的隐式排序。import json with open(concrete_aggregate/class_dict.json, r, encodingutf-8) as f: class_dict json.load(f) # 形如 {0: fine, 1: medium, 2: coarse} # 统一转成 int 索引并按索引排序保证顺序稳定 idx2name {int(k): v for k, v in class_dict.items()} class_names [idx2name[i] for i in sorted(idx2name)] print(类别顺序, class_names) # 校验目录里的类别集合必须和字典完全一致 import os dir_classes set(os.listdir(concrete_aggregate/train)) assert dir_classes set(class_names), \ f目录类别 {dir_classes} 与字典 {set(class_names)} 不一致参数说明class_dict.json的键是字符串形式的索引值是对应的粒度类别名比如细骨料、中骨料、粗骨料具体以你拿到的字典为准。assert那行是关键防线它把「目录类别」和「字典类别」做集合比对不一致就直接报错而不是等到训练完看结果才发现标签对不上。如果字典里类别数多于目录说明有类没采到样本少于目录说明目录里有字典没登记的类两种情况都要先回去补数据或改字典不要硬训。2.3 用可视化脚本先看数据再训练拿到数据集先可视化是我这些年最省时间的习惯。很多「模型不收敛」的问题其实是数据本身有问题某类全是过曝、某类样本几乎一样、图像尺寸差异极大。数据集自带的 Python 可视化脚本一般做两件事——抽样展示每类图像、统计尺寸和通道分布。下面给一个可复用的抽样网格脚本不依赖数据集自带脚本的具体实现逻辑等价。import os, random import matplotlib.pyplot as plt from PIL import Image def show_grid(split_dir, n_per_class4, seed42): random.seed(seed) classes sorted(os.listdir(split_dir)) fig, axes plt.subplots(len(classes), n_per_class, figsize(3 * n_per_class, 3 * len(classes))) for r, cls in enumerate(classes): cls_dir os.path.join(split_dir, cls) imgs [f for f in os.listdir(cls_dir) if f.lower().endswith((.jpg, .png, .jpeg, .bmp))] picks random.sample(imgs, min(n_per_class, len(imgs))) for c, name in enumerate(picks): img Image.open(os.path.join(cls_dir, name)).convert(RGB) ax axes[r][c] if len(classes) 1 else axes[c] ax.imshow(img) ax.set_title(f{cls}\n{img.size}, fontsize8) ax.axis(off) plt.tight_layout() plt.savefig(sample_grid.png, dpi120) print(已保存 sample_grid.png) show_grid(concrete_aggregate/train)逻辑说明按类别逐行抽样每类随机取n_per_class张标题里带上类别名和原始尺寸。seed固定是为了让每次抽到的图一致方便对比。看这张网格图时重点盯三件事同一类里图像尺寸是否差异过大差异大就必须统一 resize否则 batch 拼不起来、不同类之间视觉差异是否明显如果细骨料和粗骨料肉眼都难分模型更难、有没有明显错标比如粗骨料目录里混进一张细骨料。这一步花五分钟能省掉后面几小时的调参玄学。3. 从零跑通第一版分类器DataLoader、训练与评估3.1 构建 DataLoader 的三个必调参数数据目录和字典确认无误后下一步是把它喂给模型。图像分类的标准流程是Dataset DataLoader但骨料图像有几个特殊性现场拍摄尺寸不统一、光照差异大、类别可能不均衡。下面这段构建代码把关键参数都显式写出来方便你按自己数据改。import torch from torch.utils.data import DataLoader from torchvision import datasets, transforms IMG_SIZE 224 # 统一输入尺寸骨料纹理在 224 下基本够用 BATCH_SIZE 32 MEAN [0.485, 0.456, 0.406] # ImageNet 统计量迁移学习时沿用 STD [0.229, 0.224, 0.225] train_tf transforms.Compose([ transforms.Resize((IMG_SIZE, IMG_SIZE)), transforms.RandomHorizontalFlip(), # 骨料无方向性翻转安全 transforms.RandomRotation(15), # 轻微旋转增强 transforms.ColorJitter(0.2, 0.2, 0.2), # 抗现场光照波动 transforms.ToTensor(), transforms.Normalize(MEAN, STD), ]) val_tf transforms.Compose([ transforms.Resize((IMG_SIZE, IMG_SIZE)), transforms.ToTensor(), transforms.Normalize(MEAN, STD), ]) train_ds datasets.ImageFolder(concrete_aggregate/train, transformtrain_tf) val_ds datasets.ImageFolder(concrete_aggregate/val, transformval_tf) train_loader DataLoader(train_ds, batch_sizeBATCH_SIZE, shuffleTrue, num_workers4, pin_memoryTrue) val_loader DataLoader(val_ds, batch_sizeBATCH_SIZE, shuffleFalse, num_workers4, pin_memoryTrue) print(类别索引, train_ds.class_to_idx)参数说明逐个讲。IMG_SIZE设 224 是迁移学习的通用值骨料粒度靠纹理和颗粒边界区分224 分辨率下纹理信息基本保留再大显存吃紧、收益有限。BATCH_SIZE32 是单卡 8G 显存下的稳妥值显存够可以往上加但要注意学习率同步放大。RandomHorizontalFlip对骨料是安全的因为颗粒没有固定朝向但如果你做的是有方向性的缺陷检测这个增强就要慎用。ColorJitter是抗现场光照波动的关键骨料在皮带上的光照随时间和位置变化不加这个增强模型很容易记住「亮某类」这种伪特征。num_workers设 4 是经验值设太大在部分系统上反而因进程调度变慢设 0 则单进程加载会成为瓶颈。3.2 迁移学习训练循环与关键超参骨料数据集通常规模有限从零训一个 CNN 容易过拟合常见做法是用预训练骨干网络做迁移学习只替换最后的分类头。下面是一个最小可跑的训练循环用 ResNet 系列骨干冻结前几层、只训后段和分类头。import torch.nn as nn from torchvision import models device torch.device(cuda if torch.cuda.is_available() else cpu) num_classes len(train_ds.classes) model models.resnet18(weightsmodels.ResNet18_Weights.DEFAULT) # 冻结前两个 stage保留底层通用特征减少小数据集过拟合 for name, param in model.named_parameters(): if name.startswith((conv1, layer1, layer2)): param.requires_grad False model.fc nn.Linear(model.fc.in_features, num_classes) model model.to(device) criterion nn.CrossEntropyLoss() # 只把 requires_gradTrue 的参数交给优化器 optimizer torch.optim.AdamW( filter(lambda p: p.requires_grad, model.parameters()), lr1e-3, weight_decay1e-4) scheduler torch.optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max20) EPOCHS 20 best_acc 0.0 for epoch in range(EPOCHS): model.train() for imgs, labels in train_loader: imgs, labels imgs.to(device), labels.to(device) optimizer.zero_grad() loss criterion(model(imgs), labels) loss.backward() optimizer.step() scheduler.step() # 验证 model.eval() correct total 0 with torch.no_grad(): for imgs, labels in val_loader: imgs, labels imgs.to(device), labels.to(device) pred model(imgs).argmax(1) correct (pred labels).sum().item() total labels.size(0) acc correct / total print(fepoch {epoch1:02d} val_acc{acc:.4f}) if acc best_acc: best_acc acc torch.save(model.state_dict(), best_aggregate.pth) print(最佳验证准确率, best_acc)逻辑说明冻结conv1/layer1/layer2是因为这些层学的是边缘、颜色等通用特征骨料数据量不足以重新学它们冻结后既省显存又抗过拟合。AdamW的weight_decay1e-4是正则项配合冻结策略进一步压过拟合。CosineAnnealingLR让学习率按余弦曲线衰减比固定学习率更容易收敛到好的点。T_max20要和EPOCHS对齐否则学习率衰减节奏会乱。保存best_aggregate.pth是按验证准确率存最优而不是存最后一个 epoch避免过拟合后的模型被留下。3.3 评估不能只看准确率混淆矩阵与每类指标骨料类别如果样本不均衡总准确率会骗人。比如粗骨料占 70%模型全预测粗骨料也有 70% 准确率但细骨料全错。所以评估必须落到每类的 precision/recall 和混淆矩阵。import numpy as np from sklearn.metrics import classification_report, confusion_matrix model.eval() all_preds, all_labels [], [] with torch.no_grad(): for imgs, labels in val_loader: imgs imgs.to(device) pred model(imgs).argmax(1).cpu().numpy() all_preds.extend(pred) all_labels.extend(labels.numpy()) print(classification_report(all_labels, all_preds, target_namestrain_ds.classes, digits4)) cm confusion_matrix(all_labels, all_preds) print(混淆矩阵\n, cm)classification_report会给出每类的 precision、recall、f1重点看 f1 最低的那一类它往往就是数据量最少或视觉上最难分的类。混淆矩阵则告诉你错在哪如果细骨料大量被预测成中骨料说明这两类在特征空间里重叠要么补数据要么在损失函数里给这两类加权。这一步是判断「模型能不能用」的分水岭总 acc 高但某类 f1 只有 0.5上线后那一类就是事故点。4. 避坑与排查骨料图像分类最常见的五个翻车点4.1 训练集和验证集来自同一批料指标虚高现象验证准确率轻松到 0.98但换一批新拍的骨料图像准确率掉到 0.6。原因划分数据时按图像随机分同一堆骨料的相邻帧被分到了训练和验证两侧模型记住了这批料的纹理而不是学到了粒度特征。解决划分必须按「批次/料堆」分组同一批料的图像只能进同一个 split。如果数据集已经划分好先确认划分依据必要时自己按文件名前缀或采集时间重新分组划分。4.2 类别字典与目录顺序不一致标签整体错位现象训练 loss 正常下降验证 acc 也涨但推理时输出的类别名和肉眼判断完全对不上。原因ImageFolder按目录名字母序生成索引而类别字典是另一套顺序两者没对齐。解决按 2.2 的做法训练前用字典显式构建类别列表并做集合校验推理时用同一份字典做索引到名称的映射绝不依赖隐式排序。4.3 图像尺寸差异过大导致 batch 拼接失败或形变严重现象报错stack expects each tensor to be equal size或者统一 resize 后骨料被拉成细长条纹理失真。原因现场图像分辨率不统一直接 resize 到正方形会改变长宽比。解决先统计尺寸分布若长宽比差异大用Resize短边 CenterCrop或RandomResizedCrop保持比例而不是硬拉成正方形。骨料颗粒接近各向同性轻微形变可接受但极端长宽比必须处理。4.4 光照和背景成为伪特征现象模型在验证集上表现好但换一个光照条件或换一条皮带背景就崩。原因数据里光照、背景和类别偶然相关模型学了背景而不是骨料。解决增强里加ColorJitter和随机亮度对比度扰动采集时尽量让不同类别在多种光照下都有样本如果背景固定考虑先做前景分割再分类。4.5 显存不足或训练极慢现象CUDA out of memory或每个 epoch 耗时远超预期。原因BATCH_SIZE过大、num_workers设置不当、图像分辨率过高。解决先把BATCH_SIZE减半试再降IMG_SIZE到 160 或 128 看精度损失是否可接受num_workers从 4 开始调观察 GPU 利用率利用率低说明数据加载是瓶颈可适当加大开启pin_memoryTrue加速主机到显存拷贝。5. 把分类结果变成粒度分布一个可复用的统计技巧分类器跑通只是第一步现场真正要的是「这批骨料的粒度分布」也就是每个粒度类别占多少比例。这里有个容易忽略的点分类器在验证集上的准确率不等于现场统计的无偏估计因为现场各类比例和验证集不同且误分类会系统性偏移分布。我一般会做两件事来校准。第一件是用测试集估计混淆矩阵然后对现场预测结果做比例校正。假设现场预测出各类的原始占比为向量 p混淆矩阵为 MM[i][j] 表示真实类 i 被预测成 j 的比例那么真实占比 r 满足 p M^T r解这个线性方程组即可得到校正后的分布。代码很短import numpy as np from sklearn.metrics import confusion_matrix # cm 为测试集混淆矩阵行真实列预测 cm confusion_matrix(all_labels, all_preds) M cm / cm.sum(axis1, keepdimsTrue) # 行归一化 # 现场预测的原始占比假设已统计 p_raw np.array([0.5, 0.3, 0.2]) # 按你的类别顺序填 # 解 M^T r p_raw并裁剪到非负、归一化 r np.linalg.lstsq(M.T, p_raw, rcondNone)[0] r np.clip(r, 0, None) r r / r.sum() print(校正后粒度分布, dict(zip(train_ds.classes, r.round(4))))参数说明p_raw是现场推理后按类别统计的原始占比顺序必须和train_ds.classes一致。np.linalg.lstsq用最小二乘解因为混淆矩阵可能接近奇异直接求逆不稳定。clip和归一化保证结果是非负且和为 1 的合法分布。这个校正能把误分类带来的系统性偏差压下去实测在类别不均衡时比直接用原始占比靠谱得多。第二件是定期用筛分数据做锚点校验。图像识别再准也要和实际筛分结果对齐我一般每周抽一批料同时做图像识别和筛分把两者的分布画在同一张图上对比。如果偏差持续超过阈值说明模型漂移了需要补新数据重训。这个习惯听起来笨但它是把实验室指标变成现场可信数据的关键也是我踩过「模型指标漂亮、现场不认」的坑之后养成的。最后说个我自己的教训骨料图像分类最贵的成本从来不是 GPU而是标注和划分数据的时间。拿到一份划分好的数据集先别急着调模型把 2.1 到 2.3 的目录探查、字典校验、可视化三步老老实实跑一遍八成的问题在这一步就暴露了。模型结构换来换去收益往往不如把数据划分和标签对齐做扎实。希望帮到你。本文还有配套的精品资源点击获取
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

◈

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

◐

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

▲

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。