简介:牡蛎状态检测数据集同时包含训练集、验证集与测试集,共1,058张真实水产养殖场景图片,面向智能渔业监测、海产加工分拣及海洋生态研究等应用场景,提供YOLO格式的边界框与类别标签,精细划分闭合、过渡、开放三种生理状态。整个资源包共约2,000个文件,以txt标注文件与jpg图像为主体,另含yaml配置文件与docx说明文档,压缩包整体大小约35MB,可直接接入YOLOv5/v8等主流检测框架。所有标注经水产专家校验,边界框定位精准度超过95%,并覆盖不同光照、水质、生长阶段与摆放角度,可支撑养殖健康度评估模型训练、异常行为预警及自动化分拣系统开发。目前已有66人浏览学习,适合科研人员、算法工程师及水产专业师生作为模型训练和教学实践的优质数据基底。
1. 牡蛎状态检测数据集:水产分拣自动化绕不开的第一批“老师”
做水产养殖或加工自动化的人,多半见过这样的场景:牡蛎是开壳还是闭壳、还活着还是已经死亡、能不能进深加工线,过去全凭老师傅手摸眼看,现在想换成摄像头加视觉模型,把这套经验固化下来。问题是模型不会天生认得牡蛎,它需要一批带标注的样本当老师,“牡蛎状态检测数据集.zip”就是干这个的包:把牡蛎的状态定义成类别,标好框,打包成 zip 给你。适合谁?想训练自动分拣模型的水产工程师、做细粒度目标检测落地的视觉开发,以及正在折腾“yolov8 训练自己的数据集”想找个真实场景练手的人。下面按我处理这类水产数据集的完整流程,从解压到部署拆一遍,数据集的坑往往比模型的坑更多,这是开场白。
2. 解压先于训练:先把这个 zip 里的数据形态和标注体系摸清楚
拿到任何数据集,第一反应都应该是“先别急着训练”。牡蛎状态检测数据集以 zip 形式分发,说明作者大概率从 Windows 打包上传,里面图片和标注文件的组织方式、编码习惯都带着固定的生产痕迹。你要是跳过解压这一步,直接写训练脚本,后面大概率会撞上路径、编码、格式三类问题,返工成本比训练本身高一个量级。
2.1 拿到 zip 先做三件事:校验完整性、解压、盘点目录结构
我一般会先把 zip 当“黑匣子”处理:先验完整性,再解压,最后再决定用什么训练框架。直接双击解压,解到一半报“CRC 失败”,那一堆图片到底哪些是坏的你根本说不清,以后每次训练报错都得回头怀疑数据。
# 1. 先校验,不要急着解压。zip 在传输中断、网盘转存时非常容易损坏, # 用 unzip -t 逐个文件检查 CRC 校验值,输出末尾会告诉你 FAILED 的数量 unzip -t 牡蛎状态检测数据集.zip # 2. 校验通过后解压。-O GBK 用于解决 Windows 中文名在 Linux/macOS 下乱码, # 如果你的 unzip 版本不支持 -O,见下面的注意事项 unzip -O GBK 牡蛎状态检测数据集.zip -d oyster_dataset # 3. 打印目录树,先把图片和标注的组织方式摸清 find oyster_dataset -maxdepth 3 -type f | head -60这三条命令里,unzip -t是最容易被跳过的。我见过不止一次有人拿损坏的 zip 硬解,结果图片缺了一半,训练时 albumentations 读到坏图直接抛异常,还以为是增强库的 bug。-O GBK在部分 macOS 自带的 bsdtar 上不生效,遇到乱码可以用ditto -x -k或安装p7zip后用7z x解压,7z 对中文编码的处理要友善得多。第三步的find是为了让你一眼看到图片目录和标注目录的层级关系:如果看到images/和labels/平级,大概率是 YOLO 系的 txt 标注;看到JPEGImages/配Annotations/,多半是 VOC 的 XML;看到annotations/下躺着 json,基本是 COCO 格式。这一步决定了后面所有转换脚本怎么写。
提示:数据集的 zip 里有部分资源会二次打包成 rar 或带密码的 zip,遇到这种情况先记录外层 zip 的 manifest,不要急着删源文件,解压工具的报错信息就是第一份数据质量报告。
2.2 状态类别从哪来:开壳、闭壳、存活与规格的标注口径
“状态检测”这四个字听着宽泛,落到标注上其实是给每个牡蛎的边界框打一个类别标签。常见口径有两套:一套按“开壳 / 闭壳”分,服务于加工分拣线,决定牡蛎能不能直接进清洗和取肉工序;另一套按“存活 / 死亡 / 空壳”分,服务于养殖健康监控,用来在上岸分拣时把死蚝挑出去,避免污染批量净化池。
这两套口径的标注难度差别很大,也决定了模型的落地形态。按开闭壳分,你面对的是细粒度问题:开壳和闭壳的牡蛎外形轮廓几乎一样,差异全在壳缝那条边缘线上,这和车牌检测(CCPD 数据集那种)完全不同——车牌类别之间是整块的结构差异,而牡蛎的类间差异只在几毫米的缝隙宽度上;按存活分,难在“死亡”这个类别天然稀缺,死蚝在养殖池里往往会被捞走,能采集到的样本少得可怜,这和 HRSC2016 遥感船舶检测、鸟类目标检测数据集面临的类别不平衡还不是一回事——鸟类数据集至少每个物种都有足够正样本,牡蛎的死亡类往往是长尾的尾巴尖。
拿到 zip 后第一件事,看标签里到底定义了哪几个类。如果标签文件里的 class id 最多到 2 或 3,说明作者只做了粗分类;如果类别数很多(比如区分规格、是否带泥污、是否破损),训练方案要立刻调整。我遇到过最头疼的情况是“半开”状态:壳缝在 2-3 毫米时,不同标注人员的判断完全随机,这一类的样本在标签里反复横跳。所以一开始就要把标注口径定死,不然模型只会学到一团浆糊。
2.3 标注格式先对齐:VOC、COCO 还是 YOLO txt
数据集的标注格式直接决定你用什么框架、写什么转换脚本。这个 zip 里可能是三种格式之一,也可能混合存在。不理解格式差异就硬套训练脚本,轻则报“Format not supported”,重则坐标全错但训练能跑完,模型推理时框完全不在牡蛎身上——这类问题最难排查。
| 标注格式 | 标注载体 | 坐标形式 | 典型适用框架 |
|---|---|---|---|
| VOC XML | 每张图一个 xml 文件 | 左上角 xmin/ymin、右下角 xmax/ymax 像素坐标 | MMDetection、Detectron2 |
| COCO JSON | 所有图共享一个 json 文件 | bbox 为 [x, y, w, h] 像素坐标,另有 polygons 区域 | MMDetection、Detectron2 |
| YOLO txt | 每张图一个 txt 文件 | 类别 id + 归一化中心点 x/y + 归一化宽高 | Ultralytics YOLOv5/v8 |
识别方法很简单:看目录结构。有Annotations/目录且里面是 xml,就是 VOC;有_annotations.coco.json或annotations/instances_train.json是 COCO;有labels/且每个图片文件名对应一个 txt 是 YOLO。注意一个反直觉的事实:COCO 的 bbox 写的是[x, y, width, height],但很多人从 VOC 转过去时习惯只抄左上角坐标,没把宽高算出来,这是格式转换里最常见的隐性 bug。
注意:YOLO txt 的坐标是归一化后的浮点数,范围在 0 到 1 之间。如果发现 txt 里的坐标大于 1,说明这份标注原本是像素坐标,被直接存成了 txt,原作者自己就没走完转换流程。
3. 把原始标注转成 YOLO 能吃的格式:转换脚本和数据集划分
不管 zip 里是 VOC 还是 COCO,我最终都会转成 YOLO txt。原因不是 YOLO 多高级,而是 Ultralytics 的生态最省心:数据组织方式一目了然,训练脚本不用写,验证和导出也顺手。转格式的活本身不难,难在坐标归一化的细节,以及训练集和验证集的划分逻辑。
3.1 为什么要转格式:训练框架与标注格式的匹配关系
MMDetection 和 Detectron2 都能吃 VOC/COCO,但你要为它们写完整的配置文件、注册数据集类,这套流程对刚入手数据集的人来说太重了。YOLO 系列只要一个 data.yaml 加目录摆好,yolo detect train一条命令就能跑起来。另外,转成 YOLO txt 后,每张图的标注是独立的小文件,训练时读哪个图就加载哪个 txt,天然适合随机读取;COCO 那种一个大 json 的格式,在数据量大时加载和解析都有额外开销。
转换脚本的难点在于:图纸的像素坐标和归一化坐标之间的换算,必须拿原图的宽高做分母,而不是拿标注里的某个最大值。我见过有人偷懒,直接拿所有 xmax 的最大值当分母,结果图片长宽比不同,转换出来的框全部偏移。这种错是最坑的——loss 能正常下降,mAP 也有 0.6 以上,但可视化一看,所有框都偏向某个方向,模型学的是“偏移后的位置”,而不是牡蛎的位置。
3.2 转换脚本:VOC XML 转 YOLO txt
我处理 VOC 标注时用的脚本长这样,核心是读 XML 树、提取 bndbox、按图片宽高归一化。因为不知道 zip 里的类别名,这里我把类别表留成参数,你自己按 2.2 节盘点出来的类填进去。
import xml.etree.ElementTree as ET from pathlib import Path # 类别顺序一旦确定就不能改,转换、训练、推理三处保持一致 # 按 zip 里实际的 class label 填写,这里给的是加工分拣线的例子 CLASS_NAMES = ["oyster_closed", "oyster_open", "oyster_dead"] def voc_to_yolo(xml_path, class_names): tree = ET.parse(xml_path) root = tree.getroot() # 图片宽高必须从 xml 里的 size 节点读,不能自己去查原图 img_w = int(root.find("size/width").text) img_h = int(root.find("size/height").text) if img_w == 0 or img_h == 0: raise ValueError(f"{xml_path} 的 size 字段异常,图片可能损坏") lines = [] for obj in root.findall("object"): name = obj.find("name").text if name not in class_names: # 不在类别表里的目标不能直接丢弃,先打日志,否则会丢样本 print(f"[skip] {xml_path} 里有未定义类别: {name}") continue cls_id = class_names.index(name) box = obj.find("bndbox") x1 = float(box.find("xmin").text) y1 = float(box.find("ymin").text) x2 = float(box.find("xmax").text) y2 = float(box.find("ymax").text) # 归一化坐标:中心点除以宽高,宽高也除以宽高 x_center = ((x1 + x2) / 2) / img_w y_center = ((y1 + y2) / 2) / img_h w = (x2 - x1) / img_w h = (y2 - y1) / img_h # 越界和非法框先拦下来,不急着 clamp if w <= 0 or h <= 0 or x_center < 0 or x_center > 1: print(f"[warn] {xml_path} 里 {name} 的框异常: {x1},{y1},{x2},{y2}") continue lines.append(f"{cls_id} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}") return lines # 遍历所有 xml,生成同名 txt for xml_path in Path("oyster_dataset/Annotations").glob("*.xml"): lines = voc_to_yolo(xml_path, CLASS_NAMES) if not lines: continue img_name = xml_path.stem + ".txt" out_path = Path("oyster_dataset/labels") / img_name out_path.write_text("\n".join(lines))逻辑说明分三块。第一,类别顺序写死在脚本里,后续 data.yaml 的 names 必须和它完全一致,这是“yolov8 训练自己的数据集”时新手最容易踩的地方——类别名在 yaml 里写反了不会报错,模型只会学错映射。第二,所有坐标换算都用 xml 里的 size 字段,不要用 PIL 现量原图尺寸,因为 xml 的 size 是标注时用的基准图尺寸,如果数据集里混有缩放过的图片,两者会有偏差。第三,w <= 0的异常不要顺手 clamp,宁可跳过也不要硬修,这类标注多半是 xmax 和 xmin 写反了,需要回到源数据修而不是在转换时掩盖问题。
3.3 训练集/验证集划分:按批次和场景分,别随机抽
牡蛎数据集的采集方式决定了它有一个隐藏的大坑:同一只牡蛎会被拍进多张图。如果是视频抽帧得来的数据,相邻帧之间的目标位置变化极小,随机抽样划分验证集,同一个牡蛎几乎必然同时出现在训练集和验证集里,验证指标虚高得很漂亮,一到产线换一批完全没见过的牡蛎就现原形。这和 REID 数据集要防止同一身份出现在训练和测试集是同一个逻辑,也和车辆检测数据集 BDD100K 强调“按视频片段划分”而不是“按帧划分”是一样的道理。
import random from pathlib import Path from collections import defaultdict def split_by_scene(image_paths, val_ratio=0.2, seed=42): # 假设图片名形如 farmA_batch1_0001.jpg,去掉最后一段帧号作为场景键 # 同一批次、同一画面序列的图必须全部落在同一侧 groups = defaultdict(list) for p in image_paths: parts = p.stem.split("_") scene_key = "_".join(parts[:-1]) # 去掉帧号 groups[scene_key].append(p) keys = sorted(groups.keys()) random.Random(seed).shuffle(keys) val_n = max(1, int(len(keys) * val_ratio)) val_keys = set(keys[:val_n]) train_files, val_files = [], [] for key, paths in groups.items(): if key in val_keys: val_files.extend(paths) else: train_files.extend(paths) return train_files, val_files image_paths = list(Path("oyster_dataset/images").glob("*.jpg")) # 按场景名分完,再把对应 labels 一起移动到 train/val 目录 train_files, val_files = split_by_scene(image_paths, val_ratio=0.2) print(f"train: {len(train_files)}, val: {len(val_files)}")这里的核心是scene_key怎么构造。数据集图片如果是机构名加日期加序号的形式,用“去掉末尾序号”的方式聚合;如果是拍摄采集的,得看文件名里有没有批次标识。实在没有批次标识,可以退一步按图片的拍摄时间粗分。这一步很玄学,但效果立竿见影:不按场景划分的模型,验证集 mAP 虚高 10 个点以上;按场景划分后,才真正逼近产线的真实表现。
4. 用 YOLOv8 训练牡蛎状态检测:参数配置与超参实验
数据格式收拾干净,就到了训练环节。我用 Ultralytics 训练自己的数据集做这类任务,不是因为默认参数有多聪明,而是它的命令行接口把数据组织、训练、验证、导出串得很顺,出问题容易定位。但默认参数是为 COCO 那种大而全的数据集调的,用在牡蛎这种细粒度状态检测上,必须动几个关键参数。
4.1 写 data.yaml:类别顺序一旦定下就不要改
data.yaml 是这个方案的“坐标系”,训练、验证、推理甚至导出都读它。牡蛎状态检测的 yaml 长这样:
# 路径相对 data.yaml 所在目录,建议把 yaml 放在数据集根目录下 path: ./oyster_dataset train: images/train val: images/val # names 的键就是类别 id,必须和第 3 章 CLASS_NAMES 的索引一致 names: 0: oyster_closed 1: oyster_open 2: oyster_deadpath字段支持绝对路径和相对路径,我建议写成相对于 yaml 文件的位置,这样整个数据集目录拷到服务器、拷到另一台 Windows 机器都不用改配置。train和val是图片目录的路径,Ultralytics 会自动检查每个图片目录对应的 labels 目录(把images换成labels)。这里有个常见的翻车细节:Ultralytics 要求 labels 目录名必须和 images 目录名一一对应,比如images/train对labels/train,不能所有标注都堆在一个labels/下。
4.2 训练命令:关掉 Mosaic 增强的时机
牡蛎状态检测训练命令,我一般起一个轻量模型先跑通流程,再用中杯模型跑正式实验:
# 第一个实验先轻量跑通,验证数据没问题再上大模型 yolo detect train \ data=oyster.yaml \ model=yolov8s.pt \ epochs=100 \ imgsz=640 \ batch=16 \ close_mosaic=10 \ patience=20 \ device=0逐项说参数。model=yolov8s.pt会把 COCO 的预训练权重做迁移起点,比从头训练省一半时间;初次跑通不建议直接上 YOLOv8x,数据没验干净时大模型只会放大问题。imgsz=640对大部分产线摄像头画面够用,开壳状态的壳体缝隙在 640 分辨率下大约占十几个像素,再低就看不清了,不建议为了提速降到 480。close_mosaic=10是这一条命令里最关键的参数:YOLOv8 默认开着 Mosaic 增强,把四张图拼成一张训练,但牡蛎的“开壳”特征恰恰是壳缝细线,被 Mosaic 切碎拼贴后完全学不到细节,我在最后 10 个 epoch 把它关掉,让模型在正常构图下微调一遍。patience=20是早停耐心值,验证集 mAP 连续 20 轮不涨就停,省时间。
这套参数跑 100 轮,通常 60-80 轮就能看到收敛。训练时盯住两处:一是 loss 曲线的下降趋势,尾盘如果有明显上翘说明学习率没配好,Ultrlytics 默认的 lr 在大多数场景下够用;二是第一个 epoch 结束后随便找一张验证图,确认模型在输出框能大致套住牡蛎,而不是框住整个筐体。框错位置是数据问题,千万别在调参上浪费时间。
4.3 看指标不是只看 mAP50:从 PR 曲线到每类别 AP
训练结束输出一堆指标,大部分人和我一样先看 mAP50。但牡蛎状态检测这种类别不平衡明显的场景,mAP50 会被样本多的类别“顶起来”。比如闭壳样本占 80%,即使开壳和死亡类完全没学会,mAP50 也可能有 0.85,看着很美,产线一用就翻车。
正确姿势是看验证集输出里的每个类别的 AP 值:oyster_closed的 mAP 通常能到 0.95,但oyster_open和oyster_dead如果只有 0.6 左右,说明这两个细粒度状态没学好。还可以在runs/detect/train/下找到 PR 曲线图,看曲线的形状:如果曲线尾部突然垂下去,说明置信度阈值调低时有一批假阳性的背景框冲进来;如果 PR 曲线根本起不来,大概率是标注不一致——半开状态又被标注成 open 又标成 closed。这时候回到数据清洗,比换网络结构有效得多。
5. 牡蛎状态检测的 5 个常见坑:从标注到部署的血泪经验
训练能跑通和能落地是两码事。我把做这类水产视觉项目踩过的坑,按“现象、原因、解决”的方式整理出来,多数坑在别的目标检测数据集上不常见,但到了牡蛎这种细粒度、高反光、样本不均衡的场景,全是高频问题。
5.1 开壳和闭壳的边界样本:这个“半开”算哪类?
现象:模型训练结束后,验证集里大量误判集中在“微张”的牡蛎上,几张壳缝只有几毫米的图片要么被捡成 open,要么被漏检成 closed,且两种错误方向都有,没有统一模式。
原因:标注人员在标注时按自己的直觉理解“开壳”,有人把壳缝露出的就算 open,有人坚持要开口超过一定程度才算。同一个 zip 里的标签口径不一致,模型学到的不是“开壳特征”,而是“两种标注者的平均偏好”。另一个深层原因是只有两个类别,但数据真实分布里存在“半开”这个过渡态,把它强行归进任一类都会引入噪声。
解决:先统计每个类别的样本量,如果 open 类里有相当一部分是壳缝小于一个阈值(比如 1 厘米),用脚本把这些框的宽度或者缝宽的近似值算出来,做个直方图看数据分布。有两个方向可选:一是新增 half_open 类别,重新标注一部分边界样本,让模型多学一个类,代价是产线逻辑也要跟着变;二是在标注规范里明确“壳缝达到 1 厘米以上才算 open”,然后清洗掉或重标不满足条件的框。我一般先做二,因为生产现场的判定标准本来就该清晰,一个模糊的三分类会让分拣逻辑更难写。
5.2 牡蛎壳的反光和黏液:高光把模型带偏了
现象:模型在产线原始光照下评估,假阳性数量比训练时的验证集翻了一倍,仔细看检测框,圈住的往往不是牡蛎轮廓,而是壳面上高光区域的边缘。
原因:牡蛎壳表面有釉质层,在强光或侧光下会产生镜面反射,这种高光区域在图像里的边缘特征和壳缝的边缘特征高度相似。训练数据如果主要在柔光条件下采集,模型没有机会见过强反光样本,部署时的光照一变,它就“学会”去检测高光,而不是检测牡蛎。
解决:采集阶段就要考虑产线的真实光照,用偏振片或者漫射光源压制镜面反光;训练阶段不要盲目加 HSV 增强,牡蛎壳的色泽、纹理是鉴别状态的重要线索,H 和 S 扰动太大会把特征洗掉。我一般只加亮度和轻微对比度扰动,幅度控制在原始值的正负 20% 以内,模拟不同光照强度,而不是不同光谱颜色。这地方的经验是:把产线摄像头拍到的高光图单独收集成一个小测试集,专门用来验证模型对反光的鲁棒性,比在训练集里盲目堆增强有效得多。
5.3 死亡牡蛎样本太少:类别不平衡怎么补
现象:验证时 dead 类召回率只有 0.3 以下,模型几乎把死牡蛎全部识别成闭壳活体。检查标注文件,dead 类的标注框数量只有其他类的零头。
原因:牡蛎死亡后壳会自然张开,从视觉上更接近 open 而不是 closed,但如果样本太少,模型根本学不到“这张图里的牡蛎和旁边那张活得闭着壳的哪里不一样”。这是典型的类别不平衡问题,跟 PHM2012 那种设备状态监测数据里的故障样本稀缺是同一个逻辑——负类(正常)泛滥,正类(故障/死亡)稀缺,模型天然倾向把样本往多数类推。
解决:如果 dead 样本实在少到个位数,先不要强行训三分类,把问题退化成二分类“异常/正常”,把 open 和 dead 合并成异常类,先把异常筛选逻辑跑通。如果数据量还能接受,就用难例挖掘:拿当前模型去预测验证集,把预测错的 dead 图片挑出来,检查是不是标注漏了、还是这类样本长得太像 open,定向补标。另外不要用简单的过采样复制同一张图,模型会过拟合到重复样本上,严重时连续 20 个 epoch 验证集 loss 不降反升。
5.4 模型学到的是背景而不是牡蛎
现象:在训练数据所在的养殖场测试效果不错,换到一个新的加工厂、换了传送带颜色和光照环境,mAP 直接掉 20 个点。
原因:牡蛎状态检测数据集如果来源单一,图片里牡蛎永远出现在同一种蓝塑料筐、同一种传送带光线里,模型会把“蓝色筐边缘”和“牡蛎区域”的特征耦合在一起。训练时验证集也是同一场景抽的,指标看不出问题,换场景立刻现形。这和 BDD100K 等车辆数据集强调“多城市、多天气”是同一个思路:泛化能力来自数据来源的多样性,而不是模型有多强。
解决:划分验证集时必须以场景为单位,第 3.3 节的按场景分组就是为这个准备的;训完后再单独留一个完全没有参与训练的场景做“场景外测试”。部署策略上,如果产线场景确实差异很大,我一般会在新的场景先用个几十张图做快速微调,而不是指望一个通用模型吃遍所有厂。模型的泛化边界是数据给的,跨场景的自由度你得自己掂量。
5.5 zip 解压后图片打不开或标注对不上:数据清洗的顺序
现象:训练刚跑第一个 epoch,日志刷出一堆警告,提示某张图片读取失败、某个标签文件没有对应图片,甚至有一次在验证阶段程序直接崩了。
原因:压缩包传输损坏是一类;另一类是采集端写出的图片文件后缀是 jpg,实际内容却是 png 或 bmp,图像库按 jpg 解码直接报错;还有一类是标注文件比图片多或比图片少,图片和标注对不齐。
解决:训练前先跑一遍清洗脚本:遍历数据集所有图片,用 cv2 或 PIL 实际解码一次,能成功读成数组的才保留;再比对图片文件名和标签文件名,去掉没有对应关系的孤儿文件。这一步花不了多少时间,但能杜绝训练中途的黑匣子报错。我还习惯在清洗后重新计算一遍所有图片的分辨率分布,如果图片尺寸参差,训练时 imgsz 要选择能兼容大多数图的数值,同时在 data.yaml 里不做任何 resize 预处理,让 Ultralytics 的 letterbox 自动处理长宽比。
注意:数据集清洗的结果要保留一份清洗日志,记录删了哪些文件、为什么删。训练效果不理想时,回头查数据清洗日志比猜模型参数靠谱得多。
6. 把模型钉在真实需求上:混淆矩阵、ONNX 导出与产线验证
模型训练完只是拿到一个高分的“候选人”,离落地还有两步:诊断它到底漏在哪、错在哪,然后用产线实拍数据验证,而不是用验证集指标自嗨。
6.1 先看混淆矩阵,再看 mAP
训练输出目录里有一张 confusion_matrix.png,这张图比 mAP 更能回答“状态检测到底行不行”。正确预测都在主对角线上,你要重点看两个位置:降低置信度阈值后,背景列上有没有大量牡蛎漏检框;open 和 closed 之间有没有系统性的互相错认。如果 open 的大量跑到 closed 上,说明类别间的特征分界确实模糊,这时候要么去补标注规范,要么考虑把分类问题改成回归问题——预测“壳缝宽度”而不是“是否开壳”。这是模棱两可的状态检测任务里常被忽略的一条路。
6.2 导成 ONNX 再上产线
训练好的 PyTorch 权重不适合直接部署,我一般导出成 ONNX,再根据产线设备转 TensorRT 或 OpenVINO:
# 导出 ONNX,imgsz 要和训练一致,训练是 640 导出也是 640 yolo export model=runs/detect/train/weights/best.pt format=onnx imgsz=640 # 再转 TensorRT(NVIDIA 设备): yolo export model=runs/detect/train/weights/best.pt format=engine imgsz=640导出时最容易忽略的是动态输入尺寸。如果产线摄像头分辨率不是 640x640,我建议导出带动态轴的 ONNX,或者按产线实际分辨率导出一份专用 engine,而不是让部署端强行缩放。缩放这一步不处理好,小目标牡蛎在降采样后很可能直接消失。
6.3 小批量试点再全量推广
最后的建议是按“小批量试点”的思路推进:先在一条产线上挂机跑一周,每天记录模型输出和人工复检的差异。这一周里你会看到最重要的东西——漏检的“半开”牡蛎是不是集中在某个光照时段,死蚝误判是不是和传送带速度有关。收集这一周的真实误判样本,回去补标注、微调一轮,再上第二条线。我自己的习惯是保留每一条产线的独立验证集,不把所有数据倒在一起训练,因为养殖环境的水质、光照、品种差异会让模型在区域间的泛化能力下降。这个数据集是这个方向的起点,不是终点,数据越攒越值钱。希望这些流程能让你少走一点我走过的弯路,希望帮到你。
本文还有配套的精品资源,点击获取