news 2026/10/3 4:49:28

草莓目标检测数据集:YOLO+VOC双格式开箱即用

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
草莓目标检测数据集:YOLO+VOC双格式开箱即用

简介:目标检测是计算机视觉的基础任务,其核心在于高质量标注数据与模型训练流程的无缝衔接。VOC格式凭借标注直观、评估标准统一等优势,长期作为农业视觉数据的事实标准;YOLO格式则以归一化坐标和扁平结构适配主流轻量模型,显著提升训练效率。二者协同可兼顾科研可复现性与工程落地速度。本文聚焦果实识别典型场景——草莓检测,提供一套经严格校验的双格式数据集,覆盖目录结构、坐标一致性、划分逻辑等关键环节,并配套验证脚本与YOLOv8/Detectron2实战配置,帮助农林AI工程师、高校课题组及算法实习生跳过繁琐的数据预处理,快速构建高鲁棒性田间检测baseline。

1. 草莓目标检测训练数据集:为什么一个带 YOLO + VOC 双格式的 ZIP 包,能省掉你三天数据预处理时间?

你手头刚接了个农业视觉项目——要识别采摘机器人视野里的成熟草莓,但标注工具导出的 XML 文件和你本地 YOLOv8 训练脚本根本不兼容;你试过用labelImg导出 VOC 格式,又得手动写脚本转成labels/下的.txt;更糟的是,YOLO 标签要求归一化坐标,而你用CVAT标注时忘了关“绝对坐标”开关,结果训练时 bbox 全飞出图外,loss 不降反升……这种翻车不是玄学,是数据格式链路上的断点。这个名为“草莓目标检测训练数据集-含yolo格式数据集和VOC格式数据集.zip”的压缩包,本质是一套开箱即用的跨框架数据基线:它同时提供符合 PASCAL VOC 规范的Annotations/+JPEGImages/目录结构,以及适配 YOLO 系列(v5/v8/v10)的images/+labels/+train/val/test.txt分割文件。它不解决模型精度问题,但直接封死了从标注到训练最常卡住的三个环节:目录结构错位、坐标未归一化、划分比例不一致。适合正在用 PyTorch 生态做田间果实识别的农林 AI 工程师、高校作物表型课题组学生,以及需要快速验证草莓检测 baseline 的算法实习生——你不需要重标一张图,也不用调试转换脚本,解压后就能cd yolov8 && python train.py --data data.yaml跑起来。


2. 从 ZIP 解压到可训练:双格式数据集的物理结构与加载逻辑

这个 ZIP 包不是简单把两套文件塞进去,而是按工业级数据交付标准组织的。我解压后第一件事永远是tree -L 2看骨架,而不是急着跑训练。下面是你实际会看到的目录树(已剔除.gitignore和README.md等辅助文件):

strawberry_dataset/ ├── voc_format/ # 符合 PASCAL VOC 2007/2012 规范 │ ├── Annotations/ # 每张图对应一个 .xml,含 <bndbox> 坐标(像素值) │ ├── ImageSets/ # 含 Main/ 子目录,内有 train.txt/val.txt/test.txt(仅文件名,无路径) │ ├── JPEGImages/ # 所有原始图像,.jpg 格式,尺寸不统一(320×240 到 1920×1080 都有) │ └── SegmentationClass/ # 空目录(说明该数据集不做实例分割) ├── yolo_format/ # 适配 YOLO v5/v8/v10 的 flat 结构 │ ├── images/ # 同 voc_format/JPEGImages/ 内容,但软链接或硬拷贝 │ ├── labels/ # 每张图对应一个 .txt,每行 "class_id x_center y_center width height"(归一化 0~1) │ ├── train/ # 符号链接指向 images/ 和 labels/ 下子集 │ ├── val/ │ └── test/ └── dataset_info.json # 关键元数据:类别数=1(strawberry),总图数=2147,train:val:test=7:2:1

提示:dataset_info.json是你判断数据质量的第一道筛子。打开它,重点看"class_names"是否为["strawberry"](不是"berry"或"red_fruit"),"split_ratio"是否明确写了{"train": 0.7, "val": 0.2, "test": 0.1}。如果 JSON 里只有"total_images": 2147却没写划分比例,说明ImageSets/Main/下的train.txt可能是人工写的,需校验其行数是否 ≈ 2147×0.7≈1503 行。

2.1 VOC 格式:为什么它仍是农业数据集的“事实标准”

VOC 格式在农业视觉领域长期被保留,核心原因有三:

  1. 标注工具兼容性:LabelImg、CVAT、VIA默认导出 XML,且<xmin><ymin><xmax><ymax>像素坐标对农技人员最直观(“左上角第32像素,宽128像素”比“归一化中心0.421”好理解);
  2. 评估协议绑定:PASCAL VOC 的 mAP@0.5 计算逻辑(IoU 阈值固定为 0.5,插值积分)仍是多数论文 baseline 的默认指标,尤其当你要和Faster R-CNN、Mask R-CNN对比时;
  3. 数据增强鲁棒性:VOC 的 XML 中<size>节点明确记录了原图宽高,做albumentations或torchvision.transforms的 resize/crop 时,能反推原始 bbox 像素位置,避免归一化坐标的浮点误差累积。

但注意:VOC 本身不定义训练/验证划分,全靠ImageSets/Main/下的文本文件控制。你必须确认train.txt里的文件名(不含扩展名)在JPEGImages/中真实存在,且Annotations/下有同名.xml。我曾遇到某版本数据集train.txt里混入了.png名字,导致voc2yolo.py脚本读取 XML 时抛FileNotFoundError。

2.2 YOLO 格式:归一化坐标的三个硬约束与验证方法

YOLO 格式的核心是labels/*.txt中每行的 5 个数值:class_id x_center y_center width height,全部为 0~1 的浮点数。这里藏着三个必须死守的约束:

  • x_center/y_center 是 bbox 中心点相对于图像宽/高的比例,不是左上角;
  • width/height 是 bbox 宽高占图像宽/高的比例,不是右下角坐标;
  • 所有值必须严格 ∈ [0,1],哪怕 bbox 贴边(如x_center=0.001),绝不能出现负数或 >1(常见于 resize 后未重算坐标)。

验证方法极简:写一个检查脚本(见下),它会遍历所有.txt文件,打印出越界坐标行及对应图像名:

# check_yolo_labels.py import os from pathlib import Path labels_dir = Path("yolo_format/labels") images_dir = Path("yolo_format/images") for label_path in labels_dir.rglob("*.txt"): try: with open(label_path, "r") as f: lines = f.readlines() for i, line in enumerate(lines): parts = list(map(float, line.strip().split())) if len(parts) != 5: print(f"⚠️ {label_path.name}:{i+1} 行字段数≠5: {line.strip()}") continue _, xc, yc, w, h = parts # 检查归一化范围 if not (0 <= xc <= 1 and 0 <= yc <= 1 and 0 < w <= 1 and 0 < h <= 1): img_name = label_path.stem + ".jpg" img_path = images_dir / img_name if not img_path.exists(): img_path = img_path.with_suffix(".png") # 兼容 png print(f"❌ {label_path.name}:{i+1} 坐标越界: xc={xc:.3f} yc={yc:.3f} w={w:.3f} h={h:.3f} → {img_path.name}") except Exception as e: print(f"💥 {label_path.name} 读取失败: {e}")

运行后若输出为空,则 YOLO 标签物理正确。这是你启动训练前唯一必须通过的检查——YOLO 模型不会报错,但会 silently ignore 越界 bbox,导致 recall 彻底崩坏。

2.3 双格式一致性:如何用 10 行代码验证 VOC 与 YOLO 的标签完全对齐

双格式最大的隐患不是单个格式错,而是两个格式描述同一张图时 bbox 不一致。比如 VOC XML 里标注了 3 个草莓,YOLO.txt里只写了 2 行;或同一个草莓,VOC 的<xmin>是 123,YOLO 算出的x_center却对应 125 像素位置。这种错位会导致你在 VOC 流程中测出 85% mAP,切换到 YOLO 训练却只有 62%,以为是模型问题,实则是数据污染。

我用以下脚本做一致性快检(原理:对每张图,解析 VOC XML 得到像素 bbox 列表,再用图像宽高转成 YOLO 归一化格式,与labels/下同名.txt内容逐行比对):

# verify_voc_yolo_sync.py import xml.etree.ElementTree as ET from pathlib import Path voc_ann_dir = Path("voc_format/Annotations") yolo_label_dir = Path("yolo_format/labels") images_dir = Path("voc_format/JPEGImages") def voc_to_yolo_bbox(xmin, ymin, xmax, ymax, img_w, img_h): x_center = (xmin + xmax) / 2 / img_w y_center = (ymin + ymax) / 2 / img_h width = (xmax - xmin) / img_w height = (ymax - ymin) / img_h return [x_center, y_center, width, height] for ann_path in voc_ann_dir.rglob("*.xml"): img_name = ann_path.stem + ".jpg" img_path = images_dir / img_name if not img_path.exists(): continue # 读取 VOC XML 获取原始尺寸和 bbox tree = ET.parse(ann_path) root = tree.getroot() size = root.find("size") img_w = int(size.find("width").text) img_h = int(size.find("height").text) voc_bboxes = [] for obj in root.findall("object"): bbox = obj.find("bndbox") xmin = int(bbox.find("xmin").text) ymin = int(bbox.find("ymin").text) xmax = int(bbox.find("xmax").text) ymax = int(bbox.find("ymax").text) voc_bboxes.append(voc_to_yolo_bbox(xmin, ymin, xmax, ymax, img_w, img_h)) # 读取 YOLO label yolo_path = yolo_label_dir / f"{ann_path.stem}.txt" if not yolo_path.exists(): print(f"❗ {ann_path.name} 在 YOLO labels 中缺失") continue with open(yolo_path, "r") as f: yolo_lines = [list(map(float, line.strip().split()[1:])) for line in f if line.strip()] # 比较数量和坐标(容忍 1e-3 浮点误差) if len(voc_bboxes) != len(yolo_lines): print(f"❌ {ann_path.name} bbox 数量不一致: VOC={len(voc_bboxes)} vs YOLO={len(yolo_lines)}") continue for i, (voc_bb, yolo_bb) in enumerate(zip(voc_bboxes, yolo_lines)): if not all(abs(v - y) < 1e-3 for v, y in zip(voc_bb, yolo_bb)): print(f"⚠️ {ann_path.name} 第{i+1}个bbox坐标偏移: VOC{voc_bb} ≠ YOLO{yolo_bb}")

参数说明:1e-3是浮点比较容差,因不同库(OpenCV/PIL)读图宽高可能有 ±1 像素差异;voc_to_yolo_bbox函数严格遵循 YOLO 官方定义,x_center是(xmin+xmax)/2/img_w,不是(xmax-xmin)/2/img_w——后者是常见错误。

运行此脚本,若全程无输出,则双格式 100% 对齐。这是你决定是否信任该数据集的分水岭。


3. YOLOv8 训练实战:从 data.yaml 配置到 epoch 选择的完整链路

拿到yolo_format/目录后,你离训练只剩 3 步:写data.yaml、调参、启动。别跳过data.yaml—— 这是 YOLOv8 的数据契约,写错一个字段,训练会静默失败(比如train: ./images/train写成train: images/train少了./,它会去当前目录找,而非相对data.yaml路径)。

3.1 data.yaml:农业场景下的 7 个必填字段与 2 个易错陷阱

YOLOv8 的data.yaml是 YAML 格式,但实际只认 7 个 key。以下是针对草莓数据集的最小可行配置(路径均以yolo_format/为根):

# strawberry_data.yaml train: ../yolo_format/train/ # 注意:必须是相对路径,且以 ../ 开头(因 ultralytics 默认在 ultralytics/ 目录下运行) val: ../yolo_format/val/ # 同上,不能写成 ./yolo_format/val/ test: ../yolo_format/test/ # 如果 test 集存在,务必声明,否则 evaluate 时会报错 nc: 1 # class number,草莓只有 1 类,必须是整数 names: ['strawberry'] # class names,必须是 list,且顺序与 labels/*.txt 中 class_id 一一对应 # 以下为可选但强烈建议的字段 download: '' # 留空,不自动下载 auto_download: false # 关闭自动下载,避免覆盖本地数据

易错陷阱 1:路径的相对基准
Ultralytics 的train.py默认工作目录是ultralytics/(即 pip install ultralytics 后的包目录),所以train:字段必须写成../yolo_format/train/,让 Python 解析时从ultralytics/上一级开始找。如果你把data.yaml放在yolo_format/内部并写train: train/,训练会报No images found。

易错陷阱 2:names必须是 list
写成names: strawberry(字符串)或names: "strawberry"(带引号字符串)都会导致KeyError: 'strawberry'。YOLOv8 内部用names[0]取类名,所以必须是['strawberry']。

3.2 训练命令与关键参数:为什么--batch 16在草莓数据上比--batch 32更稳

草莓图像有两大特性:小目标密集(单图常有 10~50 个草莓,直径常 <32px)和光照不均(大棚内阴影/反光导致 contrast 剧变)。这决定了你不能照搬 COCO 的默认参数。以下是我在 RTX 3090 上实测的推荐命令:

yolo detect train \ data=strawberry_data.yaml \ model=yolov8n.pt \ # 用 nano 版本起步,收敛快,显存占用低 epochs=100 \ # 农业数据量小(2147 张),100 足够,过拟合风险高 batch=16 \ # 小目标多时,batch 太大会稀释梯度,16 是甜点 imgsz=640 \ # 输入尺寸,640 平衡速度与小目标检出率(试过 1280,mAP↑2%但速度↓60%) name=strawberry_nano_v1 \ # 实验名,自动建 logs/strawberry_nano_v1/ 目录 patience=10 \ # 早停,val/mAP50 连续 10 epoch 不升则 stop lr0=0.01 \ # 初始学习率,比默认 0.01 略低(农业数据噪声大) optimizer='AdamW' \ # AdamW 比 SGD 更抗光照噪声 cache=True # 开启内存缓存,加速 dataloader(草莓图尺寸不一,cache 后提速 2.3x)

参数逻辑说明:

  • batch=16:草莓小目标多,大 batch 会让每个 mini-batch 的正样本(strawberry bbox)占比过低,梯度更新方向不稳定。实测batch=32时 loss 曲线抖动剧烈,batch=16更平滑;
  • imgsz=640:低于 640(如 320)时小草莓漏检率飙升;高于 640(如 1280)虽提升 recall,但 precision 下降(误检叶片纹理),且val_map50增益 <0.5%,不值得;
  • cache=True:草莓图分辨率跨度大(320×240 到 1920×1080),Dataloader 每次都要 decode + resize,开启 cache 后首次加载慢,后续 epoch 速度翻倍。

3.3 验证与推理:如何用val.py定量诊断模型瓶颈

训练完别急着部署,先用val.py做深度诊断。YOLOv8 的val不只是打个 mAP,它会生成confusion_matrix.png、PR_curve.png、F1_curve.png三张图,每张都直指问题:

yolo detect val \ data=strawberry_data.yaml \ model=runs/detect/strawberry_nano_v1/weights/best.pt \ conf=0.001 \ # 设极低置信度阈值,确保所有预测都被计入统计 iou=0.5 \ # VOC 标准 IoU 阈值 save_json=True \ # 输出 coco-style json,用于细粒度分析 split='val' # 指定验证集

重点关注confusion_matrix.png:

  • 如果对角线(strawberry→strawberry)很亮,但其他格子(如 strawberry→background)也亮,说明误检多,根源常是背景复杂(藤蔓/土壤纹理被当成草莓);
  • 如果对角线暗,且大量预测落在background格子,说明漏检严重,需检查 anchor 匹配(小目标常用kmeans重聚 anchor)或增加imgsz;
  • 如果background→strawberry格子亮,说明假阳性高,应加强数据增强中的Mosaic和MixUp,或降低conf阈值。

血泪经验:我第一次训草莓模型时confusion_matrix显示 38% 的预测落进background,排查发现是voc_format/JPEGImages/里混入了 12 张纯黑图(夜间采集失败),YOLO 把它们全判成 background。删掉这些图后,漏检率直降 22%。


4. VOC 格式迁移:当你要对接 Faster R-CNN 或 Detectron2 时的三步改造

虽然 YOLO 流程最快,但很多农业科研项目仍要求用 Faster R-CNN(因其可解释性好,能可视化 ROI Align 特征)或 Detectron2(支持 Mask R-CNN 做草莓成熟度分割)。这时 VOC 格式就是你的起点。但voc_format/目录不能直接喂给 Detectron2——它需要register_coco_instances,而 VOC 不是 COCO。你需要一次轻量改造:

4.1 生成 COCO-style JSON:用pascal_voc_to_coco.py转换(非暴力重标)

Detectron2 官方示例要求 COCO 格式 JSON,但重标 2147 张图不现实。我们用pascal_voc_to_coco.py(来自detectron2社区维护的 utils)做无损转换:

pip install pycocotools wget https://raw.githubusercontent.com/facebookresearch/detectron2/main/tools/convert_datasets/pascal_voc.py # 修改 pascal_voc.py 中的 _get_voc_instances_meta(),将 class_names 改为 ['strawberry'] python pascal_voc.py \ --input-dir voc_format/ \ --output-dir coco_strawberry/ \ --year 2024 \ --split train \ --classes strawberry

注意:pascal_voc.py默认输出coco_strawberry/annotations/instances_train2024.json,但它的categories字段是{"id": 1, "name": "strawberry", "supercategory": "none"},Detectron2 要求supercategory不能为空字符串,需手动改为"supercategory": "fruit"(否则register_coco_instances会报KeyError: 'supercategory')。

4.2 Detectron2 数据注册:5 行代码搞定 dataset_dict 构建

Detectron2 的核心是DatasetCatalog.register(),它要求你提供一个返回list[dict]的函数,每个 dict 至少含file_name、image_id、height、width、annotations。对草莓数据集,最简实现如下:

# register_strawberry.py from detectron2.data import DatasetCatalog, MetadataCatalog from detectron2.structures import BoxMode import os import xml.etree.ElementTree as ET def load_strawberry_voc(root_dir, split="train"): root = os.path.join(root_dir, "voc_format") ann_dir = os.path.join(root, "Annotations") img_dir = os.path.join(root, "JPEGImages") imgset_path = os.path.join(root, "ImageSets", "Main", f"{split}.txt") with open(imgset_path) as f: img_ids = [line.strip() for line in f] dataset_dicts = [] for idx, img_id in enumerate(img_ids): record = {} img_path = os.path.join(img_dir, f"{img_id}.jpg") tree = ET.parse(os.path.join(ann_dir, f"{img_id}.xml")) root_elem = tree.getroot() record["file_name"] = img_path record["image_id"] = idx record["height"] = int(root_elem.find("size/height").text) record["width"] = int(root_elem.find("size/width").text) anns = [] for obj in root_elem.findall("object"): bbox = obj.find("bndbox") anns.append({ "bbox": [ int(bbox.find("xmin").text), int(bbox.find("ymin").text), int(bbox.find("xmax").text), int(bbox.find("ymax").text) ], "bbox_mode": BoxMode.XYXY_ABS, "category_id": 0, # strawberry 类别 ID,从 0 开始 "iscrowd": 0 }) record["annotations"] = anns dataset_dicts.append(record) return dataset_dicts # 注册 DatasetCatalog.register("strawberry_train", lambda: load_strawberry_voc("./", "train")) MetadataCatalog.get("strawberry_train").set(thing_classes=["strawberry"])

关键点:BoxMode.XYXY_ABS表示 bbox 是(xmin,ymin,xmax,ymax)像素坐标,不是归一化;category_id必须是整数,且thing_classes列表索引即 ID,所以["strawberry"]对应 ID 0。

4.3 Faster R-CNN 配置微调:针对小目标的 anchor 与 ROI head 优化

草莓是典型小目标(<32px),Faster R-CNN 默认 anchor([32, 64, 128, 256, 512])完全不匹配。必须重设 anchor sizes:

# config.py from detectron2.config import get_cfg cfg = get_cfg() cfg.merge_from_file("configs/COCO-Detection/faster_rcnn_R_50_FPN_3x.yaml") cfg.DATASETS.TRAIN = ("strawberry_train",) cfg.DATASETS.TEST = ("strawberry_val",) cfg.DATALOADER.NUM_WORKERS = 4 cfg.MODEL.WEIGHTS = "detectron2://COCO-Detection/faster_rcnn_R_50_FPN_3x/137849485/model_final_280758.pkl" cfg.SOLVER.IMS_PER_BATCH = 4 # 小目标 batch 要小 cfg.SOLVER.BASE_LR = 0.0025 # 比 COCO 默认 0.02 低 10 倍 # ⬇️ 小目标 anchor 重设(核心!) cfg.MODEL.ANCHOR_GENERATOR.SIZES = [[16], [32], [64], [128], [256]] # 缩小 base size cfg.MODEL.RPN.BATCH_SIZE_PER_IMAGE = 256 # 增加 RPN 正样本采样数 cfg.MODEL.ROI_HEADS.BATCH_SIZE_PER_IMAGE = 128 # ROI head 采样数同步增 cfg.MODEL.ROI_HEADS.NUM_CLASSES = 1

为什么这样改:原 anchor 最小是 32,但草莓常 16~24px,RPN 几乎无法生成正样本。将SIZES改为[[16],[32],...]后,P2 层(stride=4)的 anchor 覆盖 16px 目标,召回率提升 37%。BATCH_SIZE_PER_IMAGE加大是为了在小目标稀疏时保证 ROI head 有足够正样本训练。


5. 避坑指南:草莓数据集训练中 5 个高频翻车点与当场解决方案

注意:以下问题均来自真实项目现场,不是理论假设。每一条都附带现象 → 原因 → 解决闭环,可直接抄作业。

5.1 现象:训练 loss 曲线前 10 epoch 爆涨,之后 flatline 不降

原因:yolo_format/labels/下存在空.txt文件(对应图中无草莓),YOLOv8 默认将空 label 视为background,但nc=1时无 background 类,导致 loss 计算崩溃。
解决:运行find yolo_format/labels -name "*.txt" -size 0c -delete删除所有空文件;再用 2.2 节的check_yolo_labels.py确保无越界。

5.2 现象:val_map50卡在 0.000,但train/box_loss持续下降

原因:data.yaml中val:路径写错,YOLO 读不到验证集图像,val阶段实际在用训练集做验证(但 metrics 计算逻辑异常)。
解决:检查val路径是否指向真实存在的images/目录(不是labels/),并在yolo_format/val/下运行ls | head -5确认有图;用python -c "from ultralytics.utils.dataloaders import create_dataloader; dl = create_dataloader('yolo_format/val/', 640, 16, 0); print(next(iter(dl))[0].shape)"测试 dataloader 是否能正常 yield。

5.3 现象:推理时大量草莓被框在叶子上,且置信度 >0.9

原因:数据集中voc_format/JPEGImages/有 37 张图是纯叶片特写(无草莓),但ImageSets/Main/train.txt里包含了它们,导致模型学到“绿色纹理=草莓”。
解决:用grep -L "<object>" voc_format/Annotations/*.xml | xargs -I {} basename {} .xml找出无 object 的 XML 文件名,再从train.txt中删除这些名字;重新生成yolo_format/labels/(用voc2yolo.py脚本)。

5.4 现象:yolo detect predict输出的 bbox 坐标错位,草莓被框在图外

原因:yolo_format/images/下混入了 PNG 图像,但labels/中同名.txt是按 JPG 尺寸计算的归一化坐标(PNG 尺寸常比 JPG 大 1~2px)。
解决:统一图像格式——用mogrify -format jpg -quality 95 voc_format/JPEGImages/*.png && rm voc_format/JPEGImages/*.png批量转 JPG;再用 2.2 节脚本重验坐标。

5.5 现象:train时 GPU 显存占用 98%,但nvidia-smi显示utilization只有 10%

原因:cache=False且图像尺寸不一,Dataloader 频繁 decode/reisze,CPU 成瓶颈,GPU 等待数据。
解决:强制开启cache=True;若内存不足,加--workers 8(Linux)或--workers 4(Windows)提升 dataloader 进程数;终极方案:用imgsz=640+rect=True(矩形推理)减少 padding。


6. 进阶技巧:用 Grad-CAM 可视化定位“为什么模型总漏检青草莓”

YOLO 训练完,val_map50达到 78.3%,看似不错,但实地测试发现:红草莓检出率 92%,青草莓仅 41%。这不是数据不平衡(青草莓在数据集中占比 23%),而是模型学到了“红色=草莓”的肤浅特征。要根治,必须定位模型关注区域——Grad-CAM 是最轻量的可解释工具。

6.1 三步注入 Grad-CAM 到 YOLOv8 推理流程

YOLOv8 官方不内置 Grad-CAM,但你可以用torchcam库无缝接入。以下是在predict.py中插入的最小改动:

# gradcam_predict.py from ultralytics import YOLO from torchcam.methods import GradCAM import torch import cv2 import numpy as np model = YOLO("runs/detect/strawberry_nano_v1/weights/best.pt") cam = GradCAM(model=model.model, target_layer=model.model.model[-1]) # 最后一个 Detect 层 img_path = "test_green_strawberry.jpg" img = cv2.imread(img_path) img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) results = model(img_rgb, verbose=False) # 获取 Grad-CAM 热力图 with torch.no_grad(): out = model.model(torch.from_numpy(img_rgb).permute(2,0,1).float().unsqueeze(0)/255.0) activation_map = cam(out[0].unsqueeze(0)) # shape: [1, H, W] # 叠加热力图到原图 heatmap = activation_map[0].numpy() heatmap = cv2.resize(heatmap, (img.shape[1], img.shape[0])) heatmap = np.uint8(255 * heatmap) heatmap = cv2.applyColorMap(heatmap, cv2.COLORMAP_JET) superimposed = cv2.addWeighted(img, 0.6, heatmap, 0.4, 0) cv2.imwrite("gradcam_green.jpg", superimposed)

关键点:target_layer=model.model.model[-1]指向 YOLOv8 的 Detect head(含 3 个 stride 的输出),这是最有效的 CAM 层;out[0].unsqueeze(0)是因为cam()要求输入是[B,C,H,W],而out是[B,3,80,H,W](3 个 head),取第一个 head 即可。

6.2 从热力图诊断青草莓漏检根源:两个典型模式

我用上述脚本对 50 张青草莓图跑 Grad-CAM,发现两种主导模式:

  • 模式 A(占 64%):热力图集中在果实顶部反光点,而非整个果体——模型把“高光”当成了草莓判据;
  • 模式 B(占 28%):热力图覆盖整片叶子,但草莓区域几乎无响应——模型把“绿色背景”当成了草莓必要条件。

针对性改进:

  • 对模式 A:在train时加hsv_v=0.7(降低 value 通道饱和度),抑制反光干扰;
  • 对模式 B:在voc_format/Annotations/中,手动为青草莓添加leaf_occlusion属性(XML 中加<occluded>1</occluded>),然后在load_strawberry_voc()函数中,对occluded==1的 bbox 添加is_hard=True标签,最后在 loss 中对 hard bbox 加权(loss *= 1.5)。

6.3 一个血泪习惯:每次新数据加入,先跑

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/3 4:48:53

人口密度公里格网栅格数据实战:从裁剪统计到重采样

简介&#xff1a;面向地理信息系统分析、城市与区域规划及社会科学研究人员&#xff0c;这份资源提供了中国人口密度公里格网栅格数据。数据按一公里乘一公里的网格组织&#xff0c;包含覆盖全国的人口密度栅格图层及配套矢量边界与投影定义文件&#xff0c;可用于人口分布可视…

作者头像 李华
网站建设 2026/10/3 4:48:52

手机GNSS原始数据定位:MATLAB实现WLS/EKF/MHE/RTS算法对比全解析

这阵子一直在折腾手机GNSS原始数据的定位算法&#xff0c;从Android的GnssLogger导出的txt日志开始&#xff0c;到MATLAB里解析观测值、算卫星位置&#xff0c;再到把WLS、EKF、MHE、RTS四种算法挨个实现对比了一轮。整个过程踩了不少坑&#xff0c;但结果挺有价值——四种算法…

作者头像 李华
网站建设 2026/10/3 4:47:40

Codex 接入 Unity 与 Godot:AI 编程助手游戏开发实战指南

游戏开发这行有个很现实的问题&#xff1a;创意从来不缺&#xff0c;缺的是把创意落地的速度。一个人做独立游戏&#xff0c;美术、关卡、数值、剧情、UI 全得自己扛&#xff0c;写代码写到凌晨三点是常态。最近一段时间我一直在折腾 Codex 这类 AI 编程助手&#xff0c;把它接…

作者头像 李华
网站建设 2026/10/3 4:46:36

QGroundControl打不开?从运行库到OpenGL渲染的完整排查指南

1. 问题现象与根因拆解先说结论&#xff1a;QGroundControl&#xff08;下面简称QGC&#xff09;和华科尔地面站这类软件&#xff0c;安装完双击没反应、转圈就消失、或者报错弹窗&#xff0c;九成以上不是安装包坏了&#xff0c;而是系统和运行环境的问题。这类软件底层依赖Qt…

作者头像 李华
网站建设 2026/10/3 4:46:25

KV Cache共享与隔离:从口令实验到推理优化实践

1. 从一句口令实验说起&#xff1a;共享状态到底共享了什么第一次看到“共享状态&#xff0c;隔离问题”这个说法&#xff0c;是在一次内部技术分享的标题里。当时我以为是讲分布式系统里的一致性协议&#xff0c;点进去才发现&#xff0c;主讲人拿一个口令生成实验做引子&…

作者头像 李华
网站建设 2026/10/3 4:45:08

OpenShell实操指南:跨平台Shell增强框架与统一终端配置

1. 项目概述&#xff1a;OpenShell到底是什么天天泡在终端里的人&#xff0c;大概率都有过这样的体验&#xff1a;换了台新电脑&#xff0c;重新折腾一遍shell配置&#xff0c;从.bashrc到.zshrc到各种插件管理器&#xff0c;一搞就是一下午。更别提公司发的Windows笔记本和家里…

作者头像 李华