简介:面向目标检测与计算机视觉研究的野生动物识别数据集,包含8089张野外水域固定视角拍摄的清晰动物图片,覆盖大角斑羚、大象、长颈鹿、犀牛等9类常见动物。图片未做数据增强,已同时提供yolo与voc两种标注格式,总计19285个标注框,可直接用于训练、验证与算法横向对比。压缩包约371.87MB,文件总数为2000个,以xml标注文件与txt说明文件为主,xml对应VOC格式的检测框坐标,txt对应YOLO格式的类别与归一化坐标,便于主流检测框架直接读取。该压缩包目前已有458人学习下载,适合正在开展动物检测、生态监控或目标识别研究的科研人员与高年级学生。目录结构按JPEGImages、Annotations、labels三个文件夹组织,分别存放原始图像、VOC标注和YOLO标注,省去手动格式转换步骤;同时各类别标签框数统计清晰,例如大角斑羚2642框、黑斑羚3907框、长颈鹿2067框等,方便用户按类别分布筛选与数据均衡,也可作为模型评估的基准数据集。
1. 野生动物数据集 8089 张双格式包:为什么 YOLO+VOC 双格式值得直接上手
对做目标检测的人来说,标注成本往往比训练成本多一个数量级。这个 8089 张、9 种动物的野生动物数据集 yolo+voc 双格式包,就是一包开箱即用的标注好的数据集:图片分好了训练集和验证集,每张图的目标都带框,YOLO 的 txt 和 VOC 的 xml 两套标注同时存在。拿到它,省掉的是最枯燥的标框阶段,以及格式转换里最容易写错的坐标换算。
它解决的实际问题有三个。第一,9 类动物的目标框以万计,自己用 labelImg 标至少一个月,这个包直接跳过;第二,双格式让你既能在 YOLOv8 里直接开训,也能把 VOC 部分喂给 mmdetection 这类传统检测管线,做 yolo 系列对比时不用重标数据;第三,8089 张的规模对野生动物监测、鸟类识别系统这类项目刚好够用,既不会因为数据太少过拟合,也不至于大到单卡跑不动。
适合谁:准备做动物检测毕设、野生动物监测、鸟类识别系统,或者第一次想完整跑通 yolov8 训练自己的数据集流程的开发者。前提是会基本 Python 和命令行操作,有 6G 以上显存的显卡。后文从拆包、核对标注开始,一直讲到训练、排错和部署,路径是完整的一条。
2. 拆开数据集看门道:目录结构、标注格式和 9 类动物的类别映射
2.1 YOLO 与 VOC 两种标注格式的差异:坐标体系与归一化
拿到 zip 的第一件事不是解压就开训,而是先确认两套标注长什么样。YOLO 格式是每张图对应一个同名 txt,每行五个数字:类别编号、归一化后的 x_center、y_center、宽、高,全部缩放到 0 到 1 之间。VOC 格式是每张图对应一个 xml,用 xmin、ymin、xmax、ymax 四个像素坐标描述目标框,同时带着图片尺寸、文件名、类别名这些上下文信息。
两套体系的差异直接决定你怎么写转换脚本和 data.yaml。YOLO 的归一化坐标不依赖图片尺寸,图片缩放后标注依然有效,训练时对图做随机缩放也不用操心;VOC 的像素坐标直观,方便人读和调试,但图片一换分辨率,坐标就得重算。yolo 系列对比时经常有人把二者搞混:YOLOv5、YOLOv8 原生吃 txt,labelImg 标完默认输出 xml,这个包两种都给,正好省掉一次手动转换。
两类标注的关键差异可以对照着看:
| 对比项 | YOLO txt | VOC XML |
|---|---|---|
| 坐标体系 | 归一化 0~1 | 像素绝对值 |
| 单行内容 | 类别编号 + x_center y_center w h | object 节点内 bndbox 四坐标 |
| 是否依赖图片尺寸 | 否 | 是 |
| 换分辨率后 | 无需处理 | 必须重新转换 |
| 典型标注工具 | 脚本生成 | labelImg 默认输出 |
表里最关键的是第一行。你写核对脚本时,凡是看到 x_center 超过 1 或宽高为负,基本就是格式混了。这类包里的双格式大多是先用 labelImg 标成 xml,再用脚本统一转成 txt,所以两套标注理论上逐图对应,实际打包时却经常出现漏文件、改文件名的问题,这是 2.3 要处理的事。
2.2 8089 张图的目录结构:train/val 划分与类别编号
这类双格式数据包最常见的目录是 images 和 labels 平级,下面分 train 和 val,VOC 的 xml 单独放在 Annotations 目录;少数打包者会把 VOC 和 YOLO 分别放到两个根目录下。别急着改结构,先跑一遍下面的命令,把实际布局摸清楚。
# 解压后先看整体结构,确认 jpg/txt/xml 三类文件各自的位置 find . -maxdepth 2 -type d | sort # 三个数量对起来看:图片 / YOLO 标注 / VOC 标注应基本一致 find . -type f -name "*.jpg" | wc -l find . -type f -name "*.txt" | wc -l find . -type f -name "*.xml" | wc -l # 统计 train 标签里 9 个类别的分布,提前发现长尾问题 awk '{print $1}' labels/train/*.txt | sort | uniq -c | sort -rn第一条命令只输出两层目录,避免 .cache、__MACOSX 这类垃圾目录干扰判断。第二条三条要对照着看:txt 数量明显比 jpg 少,说明有图漏标,训练时会白白跳过这些样本;xml 数量和 txt 不一致,说明之前转换时丢过框。第三条 awk 把每个 txt 的第一列类别编号拉出来做直方图,8089 张图里如果某个类别只有几十个框,后面训练就得考虑过采样。
类别编号本身没有语义,编号和类名的对应关系在包的 classes.txt 或 label_map.txt 里,常见按字母序或标注先后顺序排,狮子可能是 0 也可能是 5。这个顺序是后续所有工作的锚点,先用 cat 看一遍,确认无误后原样抄进 data.yaml 的 names 字段。顺序一旦错位,训练照常跑,出来的模型类别全乱。
2.3 用 Python 脚本核对标注与图片是否对齐
双格式包最怕打包时漏文件、改文件名导致标注对不上图。图片缺一张最多丢一个样本,标签错位则会让模型学到一个错框,且极难排查。处理数据集用于 yolov8 训练时,最忌讳的就是跳过核对这一步。我一般会在训练前跑一遍下面这个脚本,把三件最重要的事一次查完。
# check_labels.py:训练前扫描 YOLO 标注的完整性 from pathlib import Path img_dir = Path("images/train") label_dir = Path("labels/train") num_classes = 9 # 改成 classes.txt 里的实际类别数 imgs = {p.stem: p for p in img_dir.glob("*") if p.suffix.lower() in (".jpg", ".jpeg", ".png")} labels = {p.stem: p for p in label_dir.glob("*.txt")} print("图片数:", len(imgs), "标注数:", len(labels)) print("有图无标注:", sorted(set(imgs) - set(labels))[:10]) print("有标注无图:", sorted(set(labels) - set(imgs))[:10]) bad_lines = 0 empty_files = 0 for stem, lp in labels.items(): lines = lp.read_text(encoding="utf-8").strip().splitlines() if not lines: # 空 txt 在 YOLO 训练里会被直接跳过 empty_files += 1 continue for line in lines: parts = line.split() if len(parts) != 5: # 标准行是 5 个字段,多或少都是异常 bad_lines += 1 continue cls_id = int(parts[0]) x, y, w, h = map(float, parts[1:]) if cls_id < 0 or cls_id >= num_classes: bad_lines += 1 if not (0 <= x <= 1 and 0 <= y <= 1) or w <= 0 or h <= 0: bad_lines += 1 print("空标注文件:", empty_files, "异常标注行:", bad_lines)脚本分三层。第一层用文件主名(stem)做集合比对,找图片和标注文件名不一致的样本,这是打包时最常见的损伤;第二层查空 txt,一张图一个框都没有在 YOLO 里不合法;第三层逐行检查五字段:类别编号越界、坐标不在 0 到 1、宽高非正数都会被记录。跑完如果异常行超过两位数,建议回到 4.1 的转换流程重新核一遍 xml,别硬着头皮训练。
注意一个细节:glob 时显式过滤了后缀。有些包把 .jpeg 和 .jpg 混用,如果你的包后缀不统一,把后缀集合改成实际值再跑。
3. 把双格式数据集喂给 YOLOv8:训练前的 data.yaml 与最小命令
3.1 准备 data.yaml:路径、类别名和 train/val 指向
YOLOv8 的训练入口是 data.yaml,它比模型参数更容易写错。很多第一次跑 yolov8 训练自己的数据集的人,卡点不在显卡,而在这里:路径写成相对路径、names 顺序和 txt 编号对不上、val 指向空目录。下面这份是这个数据集最典型的最小配置。
# wildlife.yaml:根路径写绝对路径,避免从别的目录启动时找不到数据 path: /data/wildlife_yolo # 改成你解压后的实际路径 train: images/train val: images/val names: 0: elephant 1: giraffe 2: lion 3: zebra 4: tiger 5: bear 6: deer 7: fox 8: monkeypath 是数据集根目录,train 和 val 是相对它的子路径,Ultralytics 会自动拼成 /data/wildlife_yolo/images/train。names 的顺序必须和 2.2 里 classes.txt 完全一致,因为 YOLO txt 里只存编号,编号对应的类名全由 data.yaml 解释。如果包的 classes.txt 顺序和你写的不一样,改 yaml,而不是去改几百个标注文件。
上面这份类别名是按常见野生动物包的字母序写的示例,开工前以包里的 classes.txt 为准替换成实际 9 类。类名建议英文小写,中文类名在后续画混淆矩阵、转 ONNX 时容易出编码问题,不值得为可读性冒这个险。
3.2 最小训练命令:从 yolo 预训练模型权重起步
配置就绪后,训练命令可以压到一行。新手阶段别一上来调一堆增强参数,先用最小组件把流程跑通。
# 最小训练命令:yolov8n 预训练权重起步,先验证数据管线再谈精度 yolo train model=yolov8n.pt data=wildlife.yaml epochs=100 imgsz=640 batch=16 device=0四个核心参数说明。model 指定 yolo 预训练模型权重:yolov8n.pt 是 nano 版,约 6M,第一次跑流程用它最快,yolo 预训练模型下载这一步由工具自动完成,网络正常时几十秒就完。data 指向刚才的 yaml。epochs 建议先写 100,配合默认的早停,实际不会真跑满。batch 看显存,6G 卡跑 640 分辨率用 16 问题不大,显存不够就降到 8。device=0 指定第一块 GPU,没 GPU 就写 device=cpu,只是慢一个量级。
想换更强模型时把 yolov8n.pt 换成 yolov8s.pt 或 yolov8m.pt 即可,这就是 yolo 系列对比最直观的权衡:n 快但精度低,m 慢但准,同一个数据集、同一份 data.yaml,参数不用改。训练中途断了可以用 resume=True 从断点继续,不用从头再来,这个参数算是训练流程里的后悔药。
3.3 训练过程看什么:loss 曲线和混淆矩阵不骗人
训练不是把命令丢进去就完事,要盯三个信号。第一个是 loss 曲线。YOLOv8 的 yolo 损失函数由三块组成:box_loss 走 CIoU 系列做框回归,cls_loss 做分类,dfl_loss 细化框边缘分布。在 runs/train/exp 下打开 results.csv 看列变化,或者用 TensorBoard 看曲线。val/box_loss 在 30 个 epoch 后还在明显下降,说明没收敛;train loss 一直降、val loss 开始回头,就是过拟合。
第二个是学习率。Ultralytics 默认余弦退火,前几个 epoch 在 warmup,loss 不降是正常的。看到前 3 个 epoch loss 纹丝不动就以为卡死而中断,是新手最常见的误操作。
第三个是多类别数据集的混淆矩阵。训练结束会自动产出 confusion_matrix.png 和按行归一化的版本。这里说清楚一个困惑很多人的点:yolo 混淆矩阵总合不唯一是正常的——normalized 版本按行归一,每行总和是 1,整张矩阵总和等于类别数而不是 1;raw 版本的总和等于验证集的目标框总数。判断标准看对角线占比和背景列:背景列高说明误检多,两个类互相串说明视觉太像或标注有偏差。
注意:第一次训练建议先只跑 30 个 epoch 验证流程,确认 val 能正常出 mAP 再放长 epochs。用 30 个 epoch 发现数据问题,比 100 个 epoch 后发现要省整整一天时间。
4. VOC 格式怎么用:XML 转 YOLO 的脚本与四个边界坑
4.1 40 行解析 VOC XML 的转换脚本与参数说明
包里自称 yolo+voc 双格式,但实际使用时大概率要自己再转一次:比如你想改用 YOLOv5 或 mmdetection,而包里的 VOC 部分可能有漏标的 object 节点、重复框、类名拼写不一致。与其信现成转换工具,不如写一个 40 行的脚本,出问题时你能当场改。
# voc2yolo.py:VOC XML 转 YOLO txt,像素坐标转归一化 import xml.etree.ElementTree as ET from pathlib import Path def voc2yolo(xml_path, class_map, out_dir): root = ET.parse(xml_path).getroot() size = root.find("size") img_w = int(size.find("width").text) img_h = int(size.find("height").text) lines = [] for obj in root.iter("object"): name = obj.find("name").text.strip() if name not in class_map: # 类名不在映射表:跳过并打印,而不是让脚本崩溃 print("跳过未知类名:", name, xml_path.name) continue box = obj.find("bndbox") xmin = float(box.find("xmin").text) ymin = float(box.find("ymin").text) xmax = float(box.find("xmax").text) ymax = float(box.find("ymax").text) # 像素坐标 -> 归一化;钳制到 [0,1] 防止 xml 里坐标越界 x_c = min(max((xmin + xmax) / 2 / img_w, 0), 1) y_c = min(max((ymin + ymax) / 2 / img_h, 0), 1) w = min(max((xmax - xmin) / img_w, 0), 1) h = min(max((ymax - ymin) / img_h, 0), 1) lines.append(f"{class_map[name]} {x_c:.6f} {y_c:.6f} {w:.6f} {h:.6f}") out = out_dir / (Path(xml_path).stem + ".txt") out.write_text("\n".join(lines) + "\n", encoding="utf-8") # 类名映射:顺序必须和 classes.txt 一致 class_map = {"elephant": 0, "giraffe": 1, "lion": 2} xml_dir = Path("Annotations") out_dir = Path("labels/train") out_dir.mkdir(exist_ok=True) for xf in xml_dir.glob("*.xml"): voc2yolo(xf, class_map, out_dir)脚本的三个关键决策。第一,类名不在映射表时跳过而不是报错,野生动物包的 xml 里偶尔混着拼错的类名,你需要的是在日志里看到它,而不是让整个转换中断;第二,坐标全部转 float 再除以图片宽高,避免 xml 里出现空字符串时报错;第三,输出保留 6 位小数,640 分辨率下精度够用,文件体积也控制得住。
转换完成后,把 2.3 的核对脚本再跑一遍,确认 txt 的类别编号都在 0 到 8 之间。这一步是数据链路的最后一道闸门,漏过去的问题会延迟到训练中途才爆出来,到时候更难定位。
4.2 边界坑一:小目标归一化后宽度变成 0
现象:转换后核对脚本报出几十条宽高为 0 的标注,且集中在远距离的小动物上。
原因:野生动物图片里目标经常在远处,原始框可能只有 3 到 4 个像素宽。归一化后是 0.004,用 6 位小数保留没问题;但有些转换工具为了省空间只留 2 位小数,直接变成 0.00,YOLO 加载时把宽高为 0 的框当无效样本丢掉。
解决:在转换脚本里对原始像素宽高做过滤,小于 3 像素的框直接丢弃并打印文件名。3 像素的目标在 640 分辨率下对训练几乎无意义,留着只会让 loss 抖动,不值得保留。
4.3 边界坑二:中文路径和图片格式混用
现象:训练时图片加载数量正常,但某几类 AP 一直是 0;或者 cv2.imread 返回 None,图片根本读不出来。
原因:中文路径在部分 OpenCV 构建下读不了文件;包内同时存在 .jpg、.jpeg、.png,类名或文件名里还有空格,Ultralytics 的 glob 匹配对空格和中文都很敏感。
解决:解压后第一件事把根目录改成纯英文,例如 wildlife_8089,内部文件用脚本统一重命名;图片格式统一成 jpg,后缀不一致的用 os.rename 改掉。这类问题属于数据包的玄学重灾区,排查成本远高于修改成本,所以我把这一步放在所有操作之前。
4.4 边界坑三:类别顺序与预训练权重不匹配
现象:训练 100 个 epoch 后 loss 正常,但混淆矩阵类别全是乱序,mAP 上不去。
原因:VOC 的 xml 存的是类名,YOLO 的 txt 存的是编号,映射完全取决于你的 class_map。如果转换脚本按中文名排序、classes.txt 按字母序排序,映射就整体错位,模型学到的 elephant 框被当成 giraffe。
解决:转换前先 cat classes.txt,严格按它的顺序写 class_map,不要在转换脚本里自己排序。顺序确认后,转换、核对、训练三步之间不要再动类别文件的顺序。这个坑最阴险的地方是它不报错,一切指标正常,只有部署时才露馅。
4.5 边界坑四:验证集里出现训练集图片
现象:mAP 高到 0.9 以上,实际推理却很拉胯,漏检明显。
原因:打包者随机分 train/val 时没去重,或者 val 目录直接复制了 train 的图片。模型在验证时见到训练过的图,指标虚高,这是数据包最常见的自我欺骗。
解决:先做文件名去重,再用 md5 兜底处理改名复制的情况。
# 检查 train/val 图片是否重复,重复会让 mAP 虚高到失真 from pathlib import Path train = set(p.name for p in Path("images/train").glob("*")) val = set(p.name for p in Path("images/val").glob("*")) dup = train & val print("重复图片数量:", len(dup)) if dup: # 重复文件从 val 挪回 train,保证验证集对模型完全陌生 for name in dup: (Path("images/val") / name).rename(Path("images/train") / name)文件名比对只覆盖复制场景;同一张图被改个名字再进 val,得用 md5 或感知哈希才能查出来。验证集的原则一句话:模型训练时绝对不能碰过这些图,否则部署一定翻车。
5. 训练时会翻车的 5 个常见问题:现象、原因与排查顺序
数据检查做完,训练还是可能出幺蛾子。下面 5 个问题按真实出现频率排,每个都按现象、原因、解决的顺序写。排查时从上往下走,大多数时候第一条就能对上。
5.1 现象:loss 降得很低,mAP 却只有 0.3
原因:野生动物数据集的长尾问题。9 类动物里狮子、老虎可能占了 70% 的框,狐狸、鹿只有几十张。mAP 是各类别 AP 的平均,稀有类把整体指标拉下来了。loss 低不代表每个类都学好了,它只是整体平均的低。
解决:训练完先看 per-class AP,在 runs/train/exp 的 results.csv 里每一类都有单独曲线,找到拖后腿的类。常见做法是对稀有类过采样:把狐狸、鹿所在的图片在训练目录里复制多份,再配合 mosaic 增强,模型看到稀有类的次数就上来了。也有一部分问题出在标注本身,稀有类的框如果标得糙,模型更难学会。
5.2 现象:训练到一半 loss 变 NaN,BN 层崩溃
原因:yolo 训练中 bn 崩溃,多半是 batch 太小加学习率太高。batch=2 时 BatchNorm 的方差估计波动大,一个异常样本就能把统计量带崩;学习率 0.01 对小数据集又太高,loss 一步就飞了。另外,标注里如果有 width=0 的框,也会在计算损失时产出 NaN。
解决:把学习率降到 0.001,batch 至少提到 8;同时用 2.3 的脚本过滤畸形框。排查顺序是先查标注,再降学习率。标注有问题时改什么参数都没用,这是血泪经验换来的顺序。
5.3 现象:GPU 显存占用只有 30%,训练却慢得出奇
原因:CPU 预处理成了瓶颈。野生动物原图常是 3000x2000 的大图,每次 letterbox 缩放、颜色增强都在 CPU 上做,workers 默认值太小,GPU 在等数据。显存占用低不等于负载低,要先看 GPU 利用率。
解决:训练命令加 workers=8 和 cache=True。cache=True 会把预处理后的图片缓存进内存,第二次迭代开始速度直接翻倍,缺点是吃内存,32G 以下机器慎开。图片入库前统一把长边压到 1280,能显著缓解第一次读图时的卡顿。先 nvidia-smi 看利用率,如果只有 30% 上下波动,就是这个原因没跑。
5.4 现象:验证集 AP 全部为 0,输出全是背景
原因:data.yaml 的 val 路径写错或 labels/val 目录为空。模型训练时看到的 val 图片没有对应标注,验证时所有预测都被当成误检,AP 自然为 0。另一个常见来源是 names 数量和 txt 里的最大编号对不上,模型把类别维度都预测错了。
解决:先查目录,用 find labels/val -name '*.txt' | wc -l 看数量,数量为 0 就是路径指错;再用文件命名规则检查 yaml 里 train/val 是否写反。最后用训练好的权重对单张 val 图跑一次 yolo predict,看输出里有没有框,能快速定位是数据问题还是模型问题。
5.5 现象:混淆矩阵的总和怎么算都不等于 1
原因:这是对 yolo 混淆矩阵总合不唯一的常见误解。Ultralytics 输出两张图:raw 版本存的是计数,总和等于验证集所有目标框的数量;normalized 版本按行归一化,每行和为 1,整张矩阵总和等于类别数。两个都对,只是口径不同,硬要按列加起来看当然对不上。
解决:只看 normalized 版本的对角线和背景列,背景列高说明误检多,某类行和对角线差得多说明该类最难学。不要花时间去让矩阵总和等于 1,那是白费力气。把时间省下来去补稀缺类的数据,收益大得多。
6. 把 9 类野生动物模型做成可用服务:混淆矩阵验证、ONNX 导出与预处理一致性
6.1 用混淆矩阵和 per-class PR 曲线找出短板类
训练跑完,别急着部署。先看 runs/train/exp 下三样东西:PR_curve.png 看整体精度-召回权衡,confusion_matrix_normalized.png 看类别间串扰,results.csv 里看每类的 AP。9 类模型里通常有两三类 AP 明显偏低,优先补这几类的数据,比盲目加 epoch 有效。best.pt 是按验证集 mAP 选的,如果验证集有脏数据,best 可能不准,所以我会同时留 last.pt 做参照,两个权重在验证集上各跑一遍,选择泛化更好的那个。
6.2 导出 ONNX 并做一次推理验证
# 导出 ONNX:固定 imgsz,部署端必须和训练一致 yolo export model=runs/train/exp/weights/best.pt format=onnx imgsz=640导出后输出张量形状是 (1, 13, 8400),13 是 4 个框坐标加 9 个类别概率,8400 是三个尺度特征图上的锚点总数,这是 YOLOv8 无锚框设计的固定结构。用一段极简代码验证文件能否正常推理。
# 快速验证导出文件能正常推理,输入用全零也足以暴露形状错误 import onnxruntime as ort import numpy as np sess = ort.InferenceSession("best.onnx") out = sess.run(None, {"images": np.zeros((1, 3, 640, 640), dtype=np.float32)})[0] print(out.shape) # 期望 (1, 13, 8400)这一步能拦住大多数部署事故:shape 对不上、输出全是 NaN、opset 不兼容,都会在这里现形,而不是等上了线才被发现。
6.3 预处理一致性:letterbox 和归一化必须复刻训练
部署侧翻车最多的是预处理不一致。直接 cv2.resize 会把图片压变形,动物框的比例全错,mAP 掉 10 个点都不意外。必须复刻训练时的 letterbox:保持长宽比缩放,再补边到 640。
# letterbox.py:部署前预处理,和训练保持完全一致 import cv2 import numpy as np def letterbox(img, new_size=640): h, w = img.shape[:2] r = new_size / max(h, w) # 等比缩放系数 nw, nh = int(round(w * r)), int(round(h * r)) resized = cv2.resize(img, (nw, nh)) canvas = np.full((new_size, new_size, 3), 114, dtype=np.uint8) # 灰色补边 canvas[:nh, :nw] = resized return canvas, (nw, nh), r img = cv2.imread("test.jpg") padded, (nw, nh), r = letterbox(img) inputs = padded.astype(np.float32) / 255.0 # 归一化到 [0,1]r 是缩放系数,模型输出的归一化坐标要先乘以 r,再减去补边偏移,才得到原图坐标。补边值 114 是训练时的默认值,用 0 补会引入分布偏移。推理端记得保留 (nw, nh) 和 r,坐标解码时要反向换算。后处理还有两个默认参数要记:conf 阈值 0.25,NMS 的 IoU 阈值 0.45,和训练时保持一致,调大 conf 会牺牲召回,均衡场景不要乱动。
我自己的习惯是,每换一个数据集,把 2.3 的核对脚本、4.1 的转换脚本、6.3 的 letterbox 函数固定成三个独立脚本,复制到新项目里改路径就行。这套流程走下来,8089 张的双格式包基本半天能出第一版权重。最值钱的不是训练那几小时,而是训练前把标注查干净、训练后把预处理对齐这两件事。希望帮到你。
本文还有配套的精品资源,点击获取