之前在服务器上跑了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 |
ms、py3 |
没有 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 里主要就是:
Open Dir选择images_all/;Change Save Dir选择labels_all/;- 格式切换为
YOLO; W新建矩形框;Ctrl+S保存;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=16 和 imgsz=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.25 和 iou=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