文章目录
- 一、概述
- 二、安装YOLOv8
-
- [2.1 在家目录创建python虚拟环境](#2.1 在家目录创建python虚拟环境)
- [2.2 安装YOLOv8](#2.2 安装YOLOv8)
- [2.3 验证YOLO](#2.3 验证YOLO)
- 三、配置训练项目
-
- [3.1 配置项目目录](#3.1 配置项目目录)
- [3.2 添加数据集配置colony.yaml](#3.2 添加数据集配置colony.yaml)
- [3.3 编写混放数据拆分脚本scripts/split_mixed.py](#3.3 编写混放数据拆分脚本scripts/split_mixed.py)
- [3.4 添加校验计数脚本scripts/check_labels.py](#3.4 添加校验计数脚本scripts/check_labels.py)
- [3.5 添加训练命令脚本](#3.5 添加训练命令脚本)
- [3.6 添加导出ONNX脚本scripts/export_onnx.py](#3.6 添加导出ONNX脚本scripts/export_onnx.py)
- 四、训练
-
- [4.1 下载预训练权重](#4.1 下载预训练权重)
- [4.2 安装依赖环境](#4.2 安装依赖环境)
- [4.3 自动化训练脚本](#4.3 自动化训练脚本)
- [4.4 数据准备](#4.4 数据准备)
- [4.5 训练](#4.5 训练)
生命练习生
诗号:
①步乱世之劫,横扫武道顶峰;辟黑夜之光,一问天下英雄。
②征尘幂,孤锋披,今古几人堪敌兮,仰空长啸万里,再战天下境逆。
③半壁翻云刃,满江酩酊红。千古光阴一霎时,且醉浪涛中。
来历成谜的枪界高人,为人正直勇敢、豪放不羁,枪法修为高深莫测,外表看似黩武无文,实则博览群书,常弄墨吟诗,自有一套探索生命的人生哲理,坚持以生命练习追求武道巅峰。
一、概述
YOLOv8 是由 Ultralytics 公司(YOLOv5 的开发者)于 2023 年 1 月 发布的目标检测模型,是该系列的最新旗舰版本。它并非由 YOLO 原作者 Joseph Redmon 或 YOLOv4 团队开发,而是 Ultralytics 基于 YOLOv5 架构深度改进的成果。
YOLOv8 原生支持多种视觉任务,无需切换代码库:
- 目标检测(Detection)
- 实例分割(Segmentation)
- 姿态估计(Pose/Keypoints)
- 图像分类(Classification)
- 旋转框检测(OBB,后续版本加入)
配置环境:
- 系统:Ubuntu 22.04.4 LTS
- 主机:dell-PowerEdge-R740
- Linux内核:6.8.0-138-generic
二、安装YOLOv8
2.1 在家目录创建python虚拟环境
需要先安装python3,可以直接安装即可。
通过"cd ~"切换到家目录,避开权限要求。
bash
cd ~
python3 -m venv yolov8_env
安装后启动下虚拟环境,虚拟环境下的activate
bash
source ~/yolov8_env/bin/activate
控制台显示虚拟环境的名字就标识虚拟环境启动成功了:
查看python是否时虚拟环境的python
bash
which python

添加虚拟环境别名,方便后续调用
bash
# 添加别名
echo "alias yoloenv='source ~/yolov8_env/bin/activate'" >> ~/.bashrc
# 生效别名
source ~/.bashrc
# 查看别名是否注册
alias yoloenv

直接输入别名,即可使用别名,会自动激活环境
bash
yoloenv

2.2 安装YOLOv8
接上步,激活虚拟环境
升级pip
bash
pip install --upgrade pip

安装PyTorch
bash
pip install torch torchvision --index-url https://download.pytorch.org/whl/cu121
等待安装完毕,时间有点久,慢慢等

安装ultralytics
这个要很久,非常久,慢慢等吧。
bash
pip install ultralytics

2.3 验证YOLO
bash
# 用别称激活虚拟环境
yoloenv
# 导入库
python -c "import torch; print('PyTorch:', torch.__version__); print('CUDA:', torch.cuda.is_available()); print('GPU:', torch.cuda.get_device_name(0) if torch.cuda.is_available() else 'N/A')"
# 查看yolo版本
yolo version
如下图,虚拟环境正常启动,相关类库安装完毕:

三、配置训练项目
3.1 配置项目目录
在账号目录下创建项目文件夹
text
~/colony_yolov8/
├── images/
│ ├── train/ # 训练图
│ └── val/ # 验证图
├── labels/
│ ├── train/ # 训练标注(与图同名)
│ └── val/
├── colony.yaml # 数据集配置
├── scripts/ # 工具脚本
│ ├── split_mixed.py # 混放数据 → 标准结构
│ ├── check_labels.py # 标注校验 + 每类统计
│ ├── train.sh # 训练命令
│ └── export_onnx.py # 导出 ONNX(后续部署用)
└── runs/ # 训练输出(自动生成)
~/colony_raw/ # 原始数据文件夹
生成项目目录:
bash
mkdir -p ~/colony_yolov8/{images/{train,val},labels/{train,val},scripts,runs}
创建完毕后可以输出下文件夹结构,确认下:
bash
cd ~/colony_yolov8
ls -R
这是项目文件夹结构:

生成原始数据目录:
bash
mkdir ~/colony_raw
3.2 添加数据集配置colony.yaml
直接添加配置
bash
cat > ~/colony_yolov8/colony.yaml << 'EOF'
# YOLOv8 7 类菌落数据集配置
path: /home/dell/colony_yolov8
train: images/train
val: images/val
names:
0: TPC
1: Mold
2: Yeast
3: Coliforms
4: TAB
5: TMold
6: ARB
EOF
3.3 编写混放数据拆分脚本scripts/split_mixed.py
参考YOLO的推荐,将原始数据图和 txt 混放在一个目录的,拆到 images/ + labels/,并按 8:2 分 train/val。
创建py文件,然后将脚本写进去:
python
#!/usr/bin/env python3
"""
把混放的图+txt 拆到 YOLOv8 标准结构(images/ 和 labels/ 分离),并按比例分 train/val。
用法:
python split_mixed.py --src ~/colony_raw --dst ~/colony_yolov8 --val-ratio 0.2
"""
import random, shutil, argparse, sys
from pathlib import Path
def main():
p = argparse.ArgumentParser()
p.add_argument("--src", default=str(Path.home() / "colony_raw"))
p.add_argument("--dst", default=str(Path.home() / "colony_yolov8"))
p.add_argument("--val-ratio", type=float, default=0.2)
p.add_argument("--seed", type=int, default=42)
a = p.parse_args()
src = Path(a.src)
dst = Path(a.dst)
random.seed(a.seed)
for s in ["train", "val"]:
(dst / "images" / s).mkdir(parents=True, exist_ok=True)
(dst / "labels" / s).mkdir(parents=True, exist_ok=True)
imgs = sorted([p for p in src.iterdir() if p.suffix.lower() in (".jpg", ".jpeg", ".png")])
if not imgs:
print(f"❌ {src} 里没有图片")
sys.exit(1)
random.shuffle(imgs)
n_val = int(len(imgs) * a.val_ratio)
missing = []
for i, img in enumerate(imgs):
txt = img.with_suffix(".txt")
if not txt.exists():
missing.append(img.name)
continue
split = "val" if i < n_val else "train"
shutil.copy2(img, dst / "images" / split / img.name)
shutil.copy2(txt, dst / "labels" / split / txt.name)
n_train = len(imgs) - n_val - len(missing)
print(f"✅ train: {n_train} val: {n_val} 缺标注跳过: {len(missing)}")
if missing:
print("缺标注示例:", missing[:5])
if __name__ == "__main__":
main()
3.4 添加校验计数脚本scripts/check_labels.py
python
#!/usr/bin/env python3
"""校验 7 类标注格式 + 统计每类数量"""
from pathlib import Path
from collections import Counter
import sys
ROOT = Path.home() / "colony_yolov8"
NAMES = ["TPC", "Mold", "Yeast", "Coliforms", "TAB", "TMold", "ARB"]
def check(split):
img_dir = ROOT / "images" / split
lbl_dir = ROOT / "labels" / split
imgs = {p.stem for p in img_dir.glob("*.*") if p.suffix.lower() in (".jpg", ".jpeg", ".png")}
txts = {p.stem for p in lbl_dir.glob("*.txt")}
print(f"\n=== {split} ===")
print(f"图: {len(imgs)} 标注: {len(txts)}")
miss_lbl = imgs - txts
miss_img = txts - imgs
if miss_lbl:
print(f"❌ 缺标注 {len(miss_lbl)} 张: {sorted(miss_lbl)[:5]}")
if miss_img:
print(f"❌ 缺图 {len(miss_img)} 个: {sorted(miss_img)[:5]}")
counter = Counter()
errs = 0
for txt in lbl_dir.glob("*.txt"):
for ln, line in enumerate(txt.read_text().strip().splitlines(), 1):
if not line.strip():
continue
parts = line.split()
if len(parts) != 5:
print(f"❌ {txt.name}:{ln} 字段数={len(parts)}")
errs += 1; continue
try:
cid = int(parts[0])
vals = list(map(float, parts[1:]))
except ValueError:
print(f"❌ {txt.name}:{ln} 非数字")
errs += 1; continue
if not (0 <= cid <= 6):
print(f"❌ {txt.name}:{ln} class_id={cid} 越界(0-6)")
errs += 1; continue
if not all(0 <= v <= 1 for v in vals):
print(f"❌ {txt.name}:{ln} 坐标越界")
errs += 1; continue
if vals[2] <= 0 or vals[3] <= 0:
print(f"❌ {txt.name}:{ln} 宽高<=0")
errs += 1; continue
counter[cid] += 1
print(f"错误: {errs}")
print("每类目标数:")
for i, name in enumerate(NAMES):
print(f" {i} {name:<10} {counter[i]}")
return errs
if __name__ == "__main__":
total = check("train") + check("val")
sys.exit(1 if total else 0)
3.5 添加训练命令脚本
bash
cat > ~/colony_yolov8/scripts/train.sh << 'SHEOF'
#!/bin/bash
set -e
source ~/yolov8_env/bin/activate
cd ~/colony_yolov8
yolo detect train \
data=colony.yaml \
model=yolov8s.pt \
epochs=150 \
imgsz=640 \
batch=16 \
device=0 \
project=runs \
name=colony_7cls_v1 \
patience=30 \
augment=True
SHEOF
3.6 添加导出ONNX脚本scripts/export_onnx.py
这一步主要是出于合规考虑:如果后续需要商用,推理阶段改用 ONNX 模型即可规避相关争议。
python
#!/usr/bin/env python3
"""导出 best.pt → best.onnx"""
import argparse
from pathlib import Path
from ultralytics import YOLO
def main():
p = argparse.ArgumentParser()
p.add_argument("--weights", default=str(Path.home() / "colony_yolov8/runs/colony_7cls_v1/weights/best.pt"))
p.add_argument("--imgsz", type=int, default=640)
p.add_argument("--out", default=str(Path.home() / "colony_yolov8/best.onnx"))
a = p.parse_args()
model = YOLO(a.weights)
onnx_path = model.export(format="onnx", imgsz=a.imgsz, simplify=True)
print(f"✅ ONNX 导出: {onnx_path}")
if __name__ == "__main__":
main()
四、训练
4.1 下载预训练权重
地址:https://github.com/ultralytics/assets/releases/download/v8.4.0/yolov8s.pt
建议直接手动下载,我直接用迅雷下载下载了,再拷贝到服务器的,github网络太不稳定了。
4.2 安装依赖环境
onnx和onnxslim
我使用清华镜像源下载,加快速度
bash
yoloenv # 启动虚拟环境
pip install onnx onnxslim -i https://pypi.tuna.tsinghua.edu.cn/simple
4.3 自动化训练脚本
python
#!/usr/bin/env python3
"""
终极版自动化训练流水线 (高健壮性、实时进度条、防崩溃)
"""
import os
import shutil
import subprocess
import sys
import time
import argparse
from pathlib import Path
# ================= 基础路径配置 =================
HOME = Path.home()
RAW_DIR = HOME / "colony_raw" # 原始数据目录
PROJECT_DIR = HOME / "colony_yolov8" # 项目根目录
SCRIPTS_DIR = PROJECT_DIR / "scripts" # 脚本目录
YAML_PATH = PROJECT_DIR / "colony.yaml" # 数据配置
RUNS_DIR = PROJECT_DIR / "runs" # 训练输出根目录
PROD_ONNX_DIR = HOME / "colony_service" # 生产部署目录
def run_cmd(cmd, desc):
"""
静默运行命令(适用于快速的数据划分、校验等)
"""
print(f"\n>>> {desc}...")
try:
result = subprocess.run(cmd, shell=True, check=True, text=True,
stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
lines = result.stdout.strip().split('\n')
for line in lines[-5:]:
print(f" {line}")
print(f"✅ {desc} 成功")
return True
except subprocess.CalledProcessError as e:
print(f"\n❌ 【{desc} 失败】")
print("-" * 50)
err_lines = e.stdout.strip().split('\n')
for line in err_lines[-20:]:
print(f" {line}")
print("-" * 50)
print(f"💡 建议:请检查上方报错信息。如果是数据问题(如缺标注、格式错),请修正后重试。")
return False
def run_cmd_realtime(cmd, desc):
"""
实时运行命令(适用于 YOLO 训练,会逐行刷新显示进度条)
"""
print(f"\n>>> {desc} (实时进度展示)...")
try:
# 直接开启子进程,不捕获到内存,而是流式读取
process = subprocess.Popen(
cmd, shell=True, text=True,
stdout=subprocess.PIPE, stderr=subprocess.STDOUT, bufsize=1
)
# 逐行读取并立刻打印到屏幕
for line in iter(process.stdout.readline, ''):
print(line, end='', flush=True)
process.stdout.close()
return_code = process.wait()
if return_code != 0:
print(f"\n❌ 【{desc} 失败】进程退出码: {return_code}")
return False
print(f"\n✅ {desc} 成功")
return True
except Exception as e:
print(f"\n❌ 【{desc} 发生异常】: {e}")
return False
def main():
parser = argparse.ArgumentParser()
parser.add_argument("--force-retrain", action="store_true", help="强制从 yolov8s.pt 重新训练,不基于旧模型增量")
args = parser.parse_args()
print("="*60)
print(" 🚀 菌落识别 YOLOv8 终极自动化流水线")
print("="*60)
try:
# 1. 检查原始数据
if not RAW_DIR.exists():
print(f"❌ 找不到原始数据目录: {RAW_DIR}")
return 1
# 支持 jpg, png, jpeg, bmp
imgs = (list(RAW_DIR.glob("*.jpg")) + list(RAW_DIR.glob("*.png")) +
list(RAW_DIR.glob("*.jpeg")) + list(RAW_DIR.glob("*.bmp")))
if not imgs:
print(f"❌ {RAW_DIR} 里没有图片,流程终止。")
return 1
txts = list(RAW_DIR.glob("*.txt"))
print(f"📦 发现原始数据: {len(imgs)} 张图, {len(txts)} 个标注文件")
# 2. 动态清理旧数据目录
print("\n>>> 清理旧的 images/train, images/val 目录...")
for split in ["train", "val"]:
img_dir = PROJECT_DIR / "images" / split
lbl_dir = PROJECT_DIR / "labels" / split
if img_dir.exists(): shutil.rmtree(img_dir)
if lbl_dir.exists(): shutil.rmtree(lbl_dir)
img_dir.mkdir(parents=True)
lbl_dir.mkdir(parents=True)
# 3. 自动划分数据
if not run_cmd(f"python {SCRIPTS_DIR}/split_mixed.py --src {RAW_DIR} --dst {PROJECT_DIR} --val-ratio 0.2", "划分训练集与验证集"):
return 1
# 4. 校验标注格式
if not run_cmd(f"python {SCRIPTS_DIR}/check_labels.py", "校验标注格式"):
print("💡 提示:校验未通过。请修复对应 txt 文件的格式或坐标越界问题。")
return 1
# 5. 决定训练起点
run_name = f"colony_{time.strftime('%Y%m%d_%H%M')}"
run_dir = RUNS_DIR / run_name
prev_bests = sorted(RUNS_DIR.glob("colony_*/weights/best.pt"), key=os.path.getmtime, reverse=True)
if args.force_retrain or not prev_bests:
model_arg = "yolov8s.pt"
mode_msg = "从头开始训练 (基于 yolov8s.pt)"
else:
model_arg = str(prev_bests[0])
mode_msg = f"增量训练 (基于上次最优 {prev_bests[0].parent.parent.name})"
print(f"\n🧠 模式: {mode_msg}")
# 6. 启动训练 (此处改为 run_cmd_realtime 以显示进度条)
train_cmd = (
f"yolo detect train "
f"data={YAML_PATH} "
f"model={model_arg} "
f"epochs=150 "
f"imgsz=640 "
f"batch=16 "
f"device=0 "
f"project={RUNS_DIR} "
f"name={run_name} "
f"patience=30 "
f"augment=True"
)
if not run_cmd_realtime(train_cmd, f"YOLOv8 模型训练 ({run_name})"):
print("💡 提示:训练过程出错。请检查是否显存溢出 (OOM) 或数据集 yaml 有误。")
return 1
# 7. 获取最佳模型
best_pt = run_dir / "weights" / "best.pt"
if not best_pt.exists():
print(f"❌ 训练结束但找不到 best.pt ({best_pt})")
return 1
# 8. 评估模型
if not run_cmd(f"yolo detect val model={best_pt} data={YAML_PATH} imgsz=640", "验证集 mAP 评估"):
return 1
# 9. 导出 ONNX
PROD_ONNX_DIR.mkdir(parents=True, exist_ok=True)
if not run_cmd(f"python {SCRIPTS_DIR}/export_onnx.py --weights {best_pt} --out {run_dir}/best.onnx", "导出 ONNX 模型"):
return 1
# 复制到生产目录
try:
shutil.copy2(run_dir / "best.onnx", PROD_ONNX_DIR / "best.onnx")
print(f"🚀 ONNX 已部署至: {PROD_ONNX_DIR}/best.onnx")
except Exception as e:
print(f"⚠️ ONNX 复制到生产目录失败: {e}")
# 10. 打印总结
print("\n" + "="*60)
print(" 🎉 全流程完成!")
print(f" 📁 历史存档: {run_dir}")
print(f" 📁 PyTorch: {best_pt}")
print(f" 📁 部署 ONNX: {PROD_ONNX_DIR}/best.onnx")
print("="*60)
# 11. 清理临时切分目录,释放空间(保护核心原始数据 colony_raw)
print("\n>>> 清理临时切分目录,释放磁盘空间...")
try:
for split in ["train", "val"]:
img_dir = PROJECT_DIR / "images" / split
lbl_dir = PROJECT_DIR / "labels" / split
if img_dir.exists(): shutil.rmtree(img_dir)
if lbl_dir.exists(): shutil.rmtree(lbl_dir)
print("✅ 临时切分目录已清理。原始数据安全保留在 colony_raw 中。")
except Exception as e:
print(f"⚠️ 清理失败: {e}")
# 返回结果
return 0
except KeyboardInterrupt:
print("\n\n🛑 用户手动中断了流水线 (Ctrl+C)。")
return 130
except Exception as e:
print(f"\n❌ 发生意外错误: {e}")
import traceback
traceback.print_exc()
return 1
if __name__ == "__main__":
# 使用 sys.exit 传递返回码,方便外部监控脚本判断是否成功
sys.exit(main())
4.4 数据准备
参考下文进行数据标记准备:
WSL下Ubuntu系统安装label studio用来标记训练图形
通过scp等将数据直接拷到colony_raw文件夹中。
4.5 训练
python
# 启动虚拟环境
yoloenv
# 启动python脚本自动计算
python ~/amas-train-pipeline/auto_train.py
训练结果,能正常跑完:

