YOLO图片检测怎么整

之前在服务器上跑了YOLO的图像检测,是2022年的工作,用的是当年的YOLOv5 6.0。旧代码、旧权重、旧数据配置都还在,但现在服务器上的 Conda 环境已经没有一个能直接丝滑运行它。好吧,深度学习工程就是这样,模型还在,环境先老了。

现在要捡起来做新的项目,旧工程不要动,新任务另起炉灶。旧目录留作复现和旧权重检测;新的训练任务准备使用当前最新的 Ultralytics YOLO,建新环境、新目录、新数据集。

旧项目长什么样

旧目录长这样:

/home/junxie/data/yolo_lh/
├── train.py
├── detect.py
├── val.py
├── requirements.txt
├── models/yolov5n.yaml
├── data/*.yaml
├── runs/train/
├── runs/detect/
├── best.pt / last.pt
└── yolov5-6.0/

大致情况是:

环境 情况
base PyTorch 是 CPU 版,不能用 GPU
py3_noise311_candidate PyTorch 2.5.1 + CUDA 12.1,可识别 GPU,但缺少 cv2
mspy3 没有 PyTorch
swd310 PyTorch CUDA 版本高于当前驱动支持范围
swd310_cupy122 CUDA 动态库不完整

所以,在这些已有环境里东补一个包、西改一个版本不太靠谱。看起来省事,最后大概率变成“为什么昨天能跑今天不能跑”。做人可以糊涂,环境还是别糊涂。

旧结果怎么留

旧训练主要都是 yolov5n,图像尺寸 640,batch size 16,从零训练。

几个主要结果如下:

结果目录 数据配置 Epoch 最后一轮 mAP@0.5 最后一轮 mAP@0.5:0.95
runs/train/20220401_05_50 data/ice.yaml 200 0.97781 0.65803
runs/train/0407 data/ice_0407.yaml 原计划 300,实际到 284 0.80549 0.51122
runs/train/20220408 data/ice_0407.yaml 300 0.96759 0.64546

如果要用旧的三分类模型,优先认这个:

/home/junxie/data/yolo_lh/runs/train/20220408/weights/best.pt

根目录下的 best.pt 和这些训练目录里的 best.pt 哈希都不同,来源暂时不清楚。这个最好不要在正式结果里直接用,除非先确认它对应的类别定义和训练来源。

旧数据集 data/ice_0407.yaml 是三分类:

train: ./data/0407_1/train/images
val: ./data/0407_1/val/images
test: ./data/0407_1/test/images
nc: 3
names: ['Icequake', 'Airgun', 'LFS']

文件数是:

划分 图片数 标签数
train 482 482
val 120 120
test 120 120

另一个 data/0406 里面图片数和标签数明显不一致。这个不一定错,因为无目标图片可能没有标签;但差距很大,重新用之前必须确认它们到底是负样本,还是当年漏标了。

新任务应该怎么做

新任务这样干:

生成图片
        ↓
确定检测类别和标注规则
        ↓
保留一份未经修改的原始图片
        ↓
用 LabelImg 标注矩形框并保存为 YOLO 格式
        ↓
人工复查标签
        ↓
按事件、日期或台站划分 train / val / test
        ↓
整理 images/ 和 labels/ 镜像目录
        ↓
编写 dataset.yaml
        ↓
上传到 gpu1
        ↓
先跑 3 epoch 小样例
        ↓
检查标签图、loss、GPU 和输出目录
        ↓
正式训练
        ↓
独立验证和测试
        ↓
用 best.pt 检测新图片

生成图片

原始图片建议满足:

长边大约 800–1600 像素;

图片间尽量采用一致的宽高比;

不需要全部是正方形;

不要为了训练提前放大低分辨率图片;

避免大量无关白边、标题、图例和界面元素;

保留未经缩放的原始图片。

对于时频图或波形图,可以考虑统一生成成:

640 × 640
800 × 800
1024 × 1024

训练时可使用 imgsz=640。

标注先用 LabelImg

最不能省的是标注复查和小样例训练。一个类别编号错了,或者相邻时间窗被随机分到训练集和验证集里,训练曲线可能看起来很好,但科学上不太站得住。

我用LabelImg来进行标注,虽然它已经停止开发,但简单矩形框标注还是够用。它适合本地有图形界面的电脑,不适合装到无桌面的服务器上。

本地安装LabelImg

python -m pip install labelImg
labelImg

如果沿用旧三分类,先建一个 predefined_classes.txt

Icequake
Airgun
LFS

顺序就是类别编号:

0 = Icequake
1 = Airgun
2 = LFS

这个顺序必须和后面的 dataset.yaml 完全一致。标注到一半再改类别顺序,很容易把自己坑晕。

当然新的分类自己看着办。

建议本地标注目录这样放:

project/
├── raw_images/
├── annotation_work/
│   ├── images_all/
│   ├── labels_all/
│   └── predefined_classes.txt
└── split_manifest.csv

raw_images/ 只读保留所有原始图片,不要直接在唯一一份原始图片上改名、覆盖和删除。未来找不到原始数据时,通常会对今天的自己很有意见。

LabelImg 里主要就是:

  1. Open Dir 选择 images_all/
  2. Change Save Dir 选择 labels_all/
  3. 格式切换为 YOLO
  4. W 新建矩形框;
  5. Ctrl+S 保存;
  6. D 下一张,A 上一张。

YOLO 标签文件每个目标一行:

class_id x_center y_center width height

后四个数字都是 0 到 1 之间的归一化坐标。无目标图片可以作为负样本,但最好保留同名空 .txt,这样能区分“已经检查过没有目标”和“还没标注”。

对于波形图或时频图,还要先说清楚框到底表示什么:事件时间范围?频率范围?还是整段信号?这不是软件问题,是科学定义问题。

新数据目录

推荐在服务器上另建:

/home/junxie/data/yolo_new_dataset/
├── images/
│   ├── train/
│   ├── val/
│   └── test/
├── labels/
│   ├── train/
│   ├── val/
│   └── test/
├── dataset.yaml
└── split_manifest.csv

图片和标签必须同名,例如:

images/train/2026_001.png
labels/train/2026_001.txt

dataset.yaml 可以先写成:

path: /home/junxie/data/yolo_new_dataset
train: images/train
val: images/val
test: images/test

names:
  0: Icequake
  1: Airgun
  2: LFS

起始划分可以是:

train: 70%
val:   15%
test:  15%

但这不是铁律。更重要的是不要数据泄漏。同一个物理事件、同一天、同一台站、同一连续记录,或者同一个原始文件派生出来的一组图,最好作为一个 group,只进入一个划分。

我会额外保存一个 split_manifest.csv

filename,split,group_id,class_source,checked
example001.png,train,event_001,Icequake,yes
example002.png,train,event_001,Icequake,yes
example101.png,val,event_021,Airgun,yes

这比口头说“我随机分了一下”靠谱多了。

新环境:yolo_modern

当前新建了一个环境和新目录:

Conda 环境:yolo_modern
工作目录:/home/junxie/data/yolo_modern

登录服务器:

ssh gpu1
source /home/junxie/opt/anaconda/etc/profile.d/conda.sh

创建环境:

conda create -n yolo_modern python=3.11 -y
conda activate yolo_modern
which python
python --version

环境提示符显示 (yolo_modern),但找不到 python,先修:

conda install -n yolo_modern python=3.11 pip -y
conda deactivate
conda activate yolo_modern
hash -r

服务器驱动检查时是 535.171.04,用 CUDA 12.1 构建的 PyTorch 比较稳妥:

python -m pip install torch==2.5.1 torchvision==0.20.1 --index-url https://download.pytorch.org/whl/cu121
python -m pip install ultralytics

记录环境:

mkdir -p /home/junxie/data/yolo_modern
python -m pip freeze > /home/junxie/data/yolo_modern/requirements-lock.txt

检查:

python -c "import torch; print(torch.__version__); print(torch.cuda.is_available()); print(torch.cuda.get_device_name(0))"
yolo checks

如果安装当天 PyTorch 或 Ultralytics 官方命令变了,以当天官方说明为准。不要安装一个当前驱动带不动的 CUDA 版本。

先跑 3 epoch 小样例

不要一上来就正式训练。先用小模型和 3 个 epoch 检查流程:

mkdir -p /home/junxie/data/yolo_modern
cd /home/junxie/data/yolo_modern
conda activate yolo_modern

yolo detect train \
  model=yolo26n.pt \
  data=/home/junxie/data/yolo_new_dataset/dataset.yaml \
  epochs=3 \
  imgsz=640 \
  batch=16 \
  device=1 \
  workers=8 \
  project=/home/junxie/data/yolo_modern/runs/train \
  name=smoke_test

这里 yolo26n.pt 是检查当天官方文档里较新的小模型选择。如果更想稳一点,也可以用 yolo11n.pt。不要一开始就上大模型,流程都没通时,大模型只是更贵的错误提示器。

小样例主要看:

  • 类别数和类别名是否正确;
  • 有没有缺失或损坏标签;
  • 训练和验证示意图中的框是否对;
  • loss 是否正常;
  • GPU 是否真的在用;
  • 输出目录是否写对。

小样例通过后,再正式训练:

yolo detect train \
  model=yolo26n.pt \
  data=/home/junxie/data/yolo_new_dataset/dataset.yaml \
  epochs=200 \
  imgsz=640 \
  batch=16 \
  device=1 \
  workers=8 \
  patience=50 \
  project=/home/junxie/data/yolo_modern/runs/train \
  name=new_dataset_yolo26n_20260728

这些参数不一定是最优参数。batch=16imgsz=640 只是沿用旧项目;阈值、图像尺寸和增强策略都应该在小样例通过后再单独比较。

正式训练建议放到 tmux 里:

tmux new -s yolo_train

暂时离开:

Ctrl+B,然后按 D

重新进入:

tmux attach -t yolo_train

怎么看训练有没有卡住

另开一个 SSH 终端,先找进程:

ps -u junxie -o user,pid,stat,etime,time,%cpu,%mem,cmd | grep -E "yolo|train" | grep -v grep

按 PID 确认:

ps -p PID号 -o user,pid,stat,etime,time,%cpu,%mem,cmd

看 GPU 1:

nvidia-smi dmon -i 1 -s pucm -d 1

单次看到 GPU-Util 0% 不代表训练停了,可能正好在读数据、验证、保存权重或切 epoch。更靠谱的是看 results.csv 有没有推进:

watch -n 10 "tail -n 3 /home/junxie/data/yolo_modern/runs/train/new_dataset_yolo26n/results.csv"

如果连续 5 到 10 分钟同时出现 GPU 利用率为 0、results.csv 不更新、训练终端没输出、CPU 占用接近 0,才比较像真的卡住了。不要因为 nvidia-smi 看了一眼是 0% 就冲上去杀进程,冲动是科研训练的敌人。

训练结果和检测

正式训练输出目录大概是:

/home/junxie/data/yolo_modern/runs/train/new_dataset_yolo26n_20260728/

重点看:

weights/best.pt
weights/last.pt
results.csv
results.png
confusion_matrix.png
PR_curve.png
val_batch*_pred.jpg

正式检测优先用 best.pt

conda activate yolo_modern

yolo detect predict \
  model=/home/junxie/data/yolo_modern/runs/train/new_dataset_yolo26n_20260728/weights/best.pt \
  source=/home/junxie/data/yolo_new_images \
  imgsz=640 \
  conf=0.25 \
  iou=0.45 \
  device=1 \
  save=True \
  save_txt=True \
  save_conf=True \
  project=/home/junxie/data/yolo_modern/runs/detect \
  name=new_images_20260728

输出在:

/home/junxie/data/yolo_modern/runs/detect/new_images_20260728/

conf=0.25iou=0.45 只是起始值。正式批量检测前,还是要用独立验证集看误检和漏检,再决定阈值。

单独验证模型:

yolo detect val \
  model=/home/junxie/data/yolo_modern/runs/train/new_dataset_yolo26n_20260728/weights/best.pt \
  data=/home/junxie/data/yolo_new_dataset/dataset.yaml \
  imgsz=640 \
  batch=16 \
  device=1 \
  project=/home/junxie/data/yolo_modern/runs/val \
  name=new_dataset_yolo26n_20260728

最后至少记录:

  • 每类 precision 和 recall;
  • mAP@0.5;
  • mAP@0.5:0.95;
  • 混淆矩阵;
  • 独立 test 集结果;
  • 数据划分方法;
  • 软件版本、权重、阈值、图像尺寸和数据版本。

旧 YOLOv5 路线怎么保留

如果只是想用 2022 年旧权重做检测,不必马上迁移模型。可以另建旧版环境:

ssh gpu1
source /home/junxie/opt/anaconda/etc/profile.d/conda.sh
conda create -n yolo5_legacy python=3.8 -y
conda activate yolo5_legacy
cd /home/junxie/data/yolo_lh

旧模型检测格式是:

python detect.py \
  --weights /home/junxie/data/yolo_lh/runs/train/20220408/weights/best.pt \
  --source /绝对路径/待检测图片目录 \
  --imgsz 640 \
  --conf-thres 0.25 \
  --iou-thres 0.45 \
  --device 1 \
  --save-txt \
  --save-conf \
  --project /home/junxie/data/yolo_lh/runs/detect \
  --name legacy_new_images

旧版从零复现训练:

python train.py \
  --weights '' \
  --cfg models/yolov5n.yaml \
  --data data/ice_0407.yaml \
  --hyp data/hyps/hyp.scratch.yaml \
  --epochs 300 \
  --batch-size 16 \
  --imgsz 640 \
  --device 1 \
  --workers 8 \
  --project runs/train \
  --name 20220408_reproduction

不过,对新的正式任务,我还是倾向于新版 Ultralytics 路线。旧版不丢,是为了有历史和基线;新版另开,是为了别把旧账和新账搅在一起。

每次运行前看一眼

最后给未来的自己留一个短检查清单:

ssh gpu1
source /home/junxie/opt/anaconda/etc/profile.d/conda.sh
conda activate yolo_modern
nvidia-smi
which python
python --version
python -c "import torch; print(torch.__version__, torch.cuda.is_available())"

确认这些事:

  • 当前环境确实是 yolo_modern
  • 指定 GPU 有足够空闲显存;
  • torch.cuda.is_available()True
  • dataset.yaml 路径无歧义;
  • 类别名和编号顺序正确;
  • 输出目录和实验名清楚;
  • 先跑了 3 epoch 小样例;
  • 没有覆盖旧数据和旧结果。

这篇记录最重要的不是某条命令,而是边界:旧工程留着,新工程另建;原始数据留着,训练数据另拷;小样例先跑,正式结果再说。这样麻烦一点,但比后面追问“这个结果到底从哪里来的”要省命。

Comments

← 花钱买了票看了电影也来评《八仙!》 Blog