尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

YOLOv8训练自己数据集:从环境配置到模型调优的完整指南

发布时间:2026/9/25 23:40:44

资讯中心
01
ARTICLE

YOLOv8训练自己数据集:从环境配置到模型调优的完整指南

YOLOv8训练自己数据集:从环境配置到模型调优的完整指南
简介这份资源面向希望上手YOLOv8并完成自定义数据集训练的深度学习开发者与算法入门者聚焦目标检测与实例分割的实战落地。内容围绕YOLOv8的统一架构、训练效率与多任务支持展开并完整梳理在Ubuntu 22.04上搭建开发环境的流程涵盖NVIDIA驱动、CUDA、cuDNN与PyTorch的安装配置以及Anaconda虚拟环境创建、YOLOv8项目克隆与预训练权重下载等关键环节。资源包为1个PDF文件大小约1.23MB便于随身查阅与对照操作。文档还涉及数据集标注整理、训练参数配置与训练脚本运行等实践要点能帮助读者理解从环境准备到模型训练的完整链路并针对特定应用定制模型。目前已有6463人学习下载适合需要系统掌握YOLOv8实例分割训练流程的读者参考。1. YOLOv8 训练自己数据集从预训练权重到能用的检测模型你手上有一批标注好的图片可能是工地安全帽、农田病虫害、流水线缺陷也可能是实验室里攒了半年的样本。你想用 YOLOv8 训练自己的数据集但打开官方文档发现满屏都是yaml、epochs、imgsz不知道从哪下手。这篇笔记就是解决这件事的从环境配置、数据组织、配置文件编写到启动训练、看损失曲线、排查不收敛把整条链路走一遍。适合有 Python 基础、跑过几次深度学习脚本、但还没独立完成过一次目标检测训练的人。读完你应该能自己把一批标注数据变成可推理的.pt权重并且知道每一步为什么这么做、出问题先看哪里。2. YOLOv8 的检测头与数据流为什么它比 YOLOv5 更好上手2.1 从 anchor-based 到 anchor-free 的转变YOLOv8 和 YOLOv5 最大的结构差异在于检测头。YOLOv5 依赖预设的 anchor box训练前要用聚类算法算出适合自己数据集的 anchor 尺寸这一步对新手很不友好——anchor 选不好召回率直接掉一截。YOLOv8 换成了 anchor-free 的解耦头每个位置直接预测边界框的偏移量不再需要预先聚类 anchor。这意味着你拿到一个新数据集不用先跑kmeans算 anchor直接开训就行。另一个变化是分类和回归分支解耦。YOLOv5 的检测头里分类和框回归共享部分卷积特征YOLOv8 把它们拆成两条独立分支各自用不同的卷积堆叠。实际效果是分类置信度和定位精度之间的干扰变小了在小目标密集的场景里更明显。我做过一个对比同一批 3000 张的工业零件缺陷数据YOLOv5s 的 mAP50 是 0.72YOLOv8s 能到 0.78提升主要来自小缺陷的召回。2.2 数据增强策略与训练时的数据流YOLOv8 默认开启了 Mosaic 增强把四张图拼成一张训练。这个策略对小目标检测帮助很大因为它让模型在一张图里看到更多不同尺度的目标。但 Mosaic 有个副作用如果数据集里有很多长宽比极端的图拼接后会出现大量黑边模型可能学到无意义的边缘特征。常见做法是在训练最后 10 个 epoch 关掉 Mosaic让模型在真实分布上收尾。配置里对应的是close_mosaic参数设成 10 就行。数据流方面YOLOv8 的DataLoader会先读你写的data.yaml找到训练集和验证集的图片目录然后按labels目录里的.txt文件解析标注。每行格式是class_id x_center y_center width height全部归一化到 0 到 1 之间。这里有个容易翻车的点如果你的标注工具输出的是绝对像素坐标直接喂进去训练会完全不收敛因为模型以为所有框都在图片左上角一个像素范围内。2.3 环境配置CPU 版本和 GPU 版本的取舍热词里有人问ubuntu20.04搭建yolov8环境cpu版本我直接说结论CPU 版本只适合跑推理和验证流程训练基本不可用。YOLOv8n 在 CPU 上训一张 640x640 的图前向加反向大概要 1.5 秒10000 张图跑 100 个 epoch 就是 170 多个小时。如果你手头只有 CPU建议先用 500 张图跑 10 个 epoch 验证流程通不通确认数据格式和配置文件没问题再换到有 GPU 的机器上正式训。GPU 版本的环境配置我一般用 conda 建一个干净环境然后按 PyTorch 官方命令装对应 CUDA 版本的 torch最后pip install ultralytics。不要用pip install ultralytics自动拉 torch它可能给你装一个和系统 CUDA 不匹配的版本跑起来报CUDA error: no kernel image is available。装完用下面这段代码验证import torch from ultralytics import YOLO # 检查 CUDA 是否可用 print(CUDA available:, torch.cuda.is_available()) print(CUDA version:, torch.version.cuda) print(GPU name:, torch.cuda.get_device_name(0) if torch.cuda.is_available() else CPU only) # 加载一个预训练模型确认 ultralytics 能正常调用 model YOLO(yolov8n.pt) print(Model loaded, task:, model.task)这段代码先确认 torch 能识别到 GPU再确认 ultralytics 能加载模型。如果第一行输出False说明 CUDA 没配好训练时会自动回退到 CPU速度差几十倍。如果模型加载报错通常是ultralytics版本和 torch 版本不兼容先pip install ultralytics --upgrade再试。3. 把标注数据整理成 YOLOv8 能吃的格式3.1 目录结构images 和 labels 必须平行YOLOv8 对目录结构有硬性要求图片和标注文件要分开放但文件名必须一一对应。我一般这样组织dataset/ ├── images/ │ ├── train/ │ │ ├── 001.jpg │ │ └── 002.jpg │ └── val/ │ ├── 003.jpg │ └── 004.jpg ├── labels/ │ ├── train/ │ │ ├── 001.txt │ │ └── 002.txt │ └── val/ │ ├── 003.txt │ └── 004.txt └── data.yaml注意images/train/001.jpg对应的标注是labels/train/001.txt文件名必须完全一致扩展名不同没关系。YOLOv8 在加载时会自动把图片路径里的images替换成labels再把扩展名换成.txt去找标注。如果你把标注和图片放在同一个目录或者文件名对不上训练时会出现大量No labels found警告实际参与训练的图可能只有你标注的一半。3.2 从 Labelme 标注转 YOLO 格式的脚本很多人用 Labelme 标数据输出的是 JSON 文件里面是多边形点集。YOLOv8 检测任务需要的是矩形框所以要把多边形转成外接矩形再归一化。下面这个脚本处理单个 JSON 文件import json import os from PIL import Image def labelme_to_yolo(json_path, output_dir, class_names): 将 Labelme 的 JSON 标注转换为 YOLO 格式的 txt json_path: Labelme 导出的 json 文件路径 output_dir: 输出 txt 的目录 class_names: 类别名称列表顺序要和 data.yaml 里的 names 一致 with open(json_path, r, encodingutf-8) as f: data json.load(f) # 获取图片宽高用于归一化 img_w data[imageWidth] img_h data[imageHeight] lines [] for shape in data[shapes]: label shape[label] if label not in class_names: continue # 跳过不在类别列表里的标注 class_id class_names.index(label) points shape[points] # 多边形转外接矩形 xs [p[0] for p in points] ys [p[1] for p in points] x_min, x_max min(xs), max(xs) y_min, y_max min(ys), max(ys) # 计算中心点和宽高并归一化 x_center (x_min x_max) / 2.0 / img_w y_center (y_min y_max) / 2.0 / img_h width (x_max - x_min) / img_w height (y_max - y_min) / img_h # 过滤掉宽高为 0 的无效框 if width 0 or height 0: continue lines.append(f{class_id} {x_center:.6f} {y_center:.6f} {width:.6f} {height:.6f}) # 写入同名 txt base_name os.path.splitext(os.path.basename(json_path))[0] out_path os.path.join(output_dir, base_name .txt) with open(out_path, w) as f: f.write(\n.join(lines)) return len(lines) # 批量处理示例 class_names [helmet, vest, person] # 按你的数据集类别改 json_dir ./labelme_jsons out_dir ./labels/train os.makedirs(out_dir, exist_okTrue) for fname in os.listdir(json_dir): if fname.endswith(.json): n labelme_to_yolo(os.path.join(json_dir, fname), out_dir, class_names) print(f{fname}: {n} boxes)这个脚本的关键逻辑有三步读 JSON 拿到图片宽高和多边形点集把多边形转成外接矩形并归一化按 YOLO 格式写入 txt。参数class_names的顺序必须和data.yaml里的names完全一致否则类别会错位。比如你把helmet放在索引 0但data.yaml里names: [person, helmet, vest]那模型学到的“0”其实是 person推理时全乱。我一般会在转换完随机抽 5 张图用labelImg或labelme打开对应的 txt 可视化一下确认框的位置和类别都对。3.3 data.yaml 的四个必填字段data.yaml是 YOLOv8 训练的数据入口必须包含四个字段path: /home/user/dataset # 数据集根目录绝对路径 train: images/train # 训练集图片目录相对于 path val: images/val # 验证集图片目录相对于 path names: # 类别名称索引从 0 开始 0: helmet 1: vest 2: personpath写绝对路径最稳写相对路径时 YOLOv8 会以当前工作目录为基准容易找不到文件。train和val是相对于path的子路径不要写绝对路径。names可以用列表或字典字典形式更直观但索引必须从 0 连续。如果你有 3 个类别但只写了 2 个训练时遇到第 3 类的标注会直接报IndexError。注意val目录不能和train用同一批图。我见过有人图省事把训练集同时当验证集结果 mAP 虚高到 0.95实际推理时一塌糊涂。验证集至少要从训练集里独立分出 10% 到 20% 的图且不能有重复。4. 启动训练命令行参数怎么设、日志怎么看4.1 最小训练命令与六个关键参数环境配好、数据整理完训练本身一行命令就能启动yolo detect train \ data./dataset/data.yaml \ modelyolov8s.pt \ epochs100 \ imgsz640 \ batch16 \ device0 \ project./runs \ nameexp1data指向你的data.yamlmodel指定预训练权重epochs是训练轮数imgsz是输入图片尺寸batch是批大小device0表示用第一块 GPU。project和name控制输出目录最终权重和日志会存在./runs/exp1/下面。imgsz和batch是最需要根据显存调的。YOLOv8s 在 640 尺寸下batch16 大概占 6GB 显存。如果你显存只有 4GB把 batch 降到 8或者把 imgsz 降到 512。不要硬撑显存溢出报CUDA out of memory后训练直接中断前面跑的 epoch 虽然会保存但重新启动要手动指定resume。epochs设多少取决于数据集大小。我的经验是每类目标至少要有 200 个实例总图数在 2000 以上时100 个 epoch 基本够收敛。如果图少比如只有 500 张epoch 可以加到 300但要注意过拟合。判断过拟合看验证集的mAP50和train/box_loss如果训练损失还在降但验证 mAP 连续 20 个 epoch 不升反降就是过拟合了该停。4.2 损失曲线和 mAP 曲线怎么读训练启动后runs/exp1/目录下会生成results.csv里面记录了每个 epoch 的损失和指标。用下面这段代码画损失曲线import pandas as pd import matplotlib.pyplot as plt df pd.read_csv(./runs/exp1/results.csv) df.columns df.columns.str.strip() # 列名可能有空格 fig, axes plt.subplots(1, 2, figsize(14, 5)) # 损失曲线 axes[0].plot(df[epoch], df[train/box_loss], labeltrain box loss) axes[0].plot(df[epoch], df[train/cls_loss], labeltrain cls loss) axes[0].plot(df[epoch], df[val/box_loss], labelval box loss) axes[0].plot(df[epoch], df[val/cls_loss], labelval cls loss) axes[0].set_xlabel(epoch) axes[0].set_ylabel(loss) axes[0].legend() axes[0].set_title(Loss curves) # mAP 曲线 axes[1].plot(df[epoch], df[metrics/mAP50(B)], labelmAP50) axes[1].plot(df[epoch], df[metrics/mAP50-95(B)], labelmAP50-95) axes[1].set_xlabel(epoch) axes[1].set_ylabel(mAP) axes[1].legend() axes[1].set_title(mAP curves) plt.tight_layout() plt.savefig(./runs/exp1/curves.png, dpi150)train/box_loss和train/cls_loss应该整体下降中间有波动正常。如果 box loss 降到 0.5 以下还在降但 cls loss 卡在 2.0 不动通常是类别不平衡——某个类的样本太少模型倾向于全预测成多数类。解决办法是在data.yaml里给少数类加权重或者用focal_loss替换默认的 BCE loss。metrics/mAP50(B)是 IoU 阈值 0.5 时的平均精度mAP50-95是 0.5 到 0.95 每隔 0.05 取一个阈值再平均。前者看模型“能不能找到目标”后者看“框得准不准”。如果 mAP50 高但 mAP50-95 低说明模型能找到目标但框的位置不够精确可以试试调大box损失的权重或者检查标注框是不是画得太松。4.3 训练中断后怎么恢复训练到一半断电或者手动停了重新启动时加resumeTrueyolo detect train resume model./runs/exp1/weights/last.ptlast.pt是最近一个 epoch 的权重里面保存了优化器状态和当前 epoch 数恢复后会从断点继续。注意不要用best.pt恢复best.pt只保存了模型参数没有优化器状态恢复后相当于重新开始训练只是初始权重好一点。如果last.pt损坏了比如写入时断电只能从best.pt重新训但可以把epochs设小一点比如原来 100 就设 30因为best.pt已经是一个不错的起点了。5. 训练不收敛、显存溢出、mAP 虚高五个血泪踩坑记录5.1 现象loss 一直是 nan训练完全没效果原因标注文件里有坐标超出 0 到 1 范围的值。Labelme 转 YOLO 时如果图片宽高读错了或者手动改过标注归一化后的坐标可能大于 1 或小于 0。YOLOv8 在计算损失时会对坐标做 log 运算负数直接产生 nan。解决写个脚本扫一遍所有 txt把坐标不在 [0,1] 范围内的行找出来import os def check_labels(label_dir): bad_files [] for fname in os.listdir(label_dir): if not fname.endswith(.txt): continue path os.path.join(label_dir, fname) with open(path) as f: for i, line in enumerate(f): parts line.strip().split() if len(parts) ! 5: bad_files.append((fname, i, 字段数不对)) continue coords [float(x) for x in parts[1:]] if any(c 0 or c 1 for c in coords): bad_files.append((fname, i, f坐标越界: {coords})) return bad_files bad check_labels(./labels/train) for item in bad[:20]: print(item) print(f共发现 {len(bad)} 处问题)扫出来之后要么重新转换要么手动把越界的坐标截断到 [0,1]。我一般直接重新跑转换脚本因为手动改容易漏。5.2 现象训练到第 10 个 epoch 突然报 CUDA out of memory原因Mosaic 增强在训练前期会随机拼接图片某些批次拼出来的图尺寸比imgsz大显存占用波动。如果batch设得刚好卡在显存上限遇到大图就溢出。解决把batch降到原来的 70% 左右比如 16 改成 8 或 12。或者加ampFalse关掉混合精度训练混合精度虽然省显存但某些显卡上会有数值不稳定问题。如果不想降 batch可以把imgsz从 640 降到 512显存占用大概减少 35%。5.3 现象mAP50 很高但实际推理时漏检严重原因验证集和训练集有重叠图片或者验证集的分布和实际场景差太远。我见过一个工地安全帽数据集验证集全是白天光照充足的图训练集里也混了一部分同样的图mAP50 跑到 0.92但拿到夜间监控画面上推理召回率不到 0.3。解决验证集必须和训练集完全独立且要覆盖实际部署时的场景变化。如果实际场景有夜间、雨天、遮挡验证集里就要有这些情况的图。另外可以用yolo detect val命令单独跑一次验证看每个类别的 AP如果某个类 AP 特别低说明这个类的样本太少或标注质量差。5.4 现象训练完推理所有框都偏大一圈原因标注时框画得太松把目标周围的背景也框进去了。YOLOv8 学到的就是“大框”推理时自然往外扩。解决重新检查标注把框收紧到目标边缘。如果标注量太大改不过来可以在训练时调小box损失的权重让模型不那么严格地拟合标注框。但这是治标治本是重新标。我现在的习惯是标完先抽 20 张用脚本把框画到图上肉眼过一遍再开训。5.5 现象换了 GPU 后训练速度反而变慢原因新 GPU 的 CUDA 版本和 PyTorch 编译时的 CUDA 版本不匹配PyTorch 回退到了 CPU 或者用了兼容模式。热词里有人问gtx1660ti跑yolov81660ti 是 Turing 架构算力 7.5支持 FP16但如果你装的 PyTorch 是给 Ampere 架构编译的可能跑不出应有速度。解决用torch.cuda.get_device_capability()看当前 GPU 的算力然后去 PyTorch 官网找对应 CUDA 版本的安装命令。1660ti 建议用 CUDA 11.8 加 PyTorch 2.0 以上的版本开ampTrue混合精度YOLOv8s 在 640 尺寸下大概能跑到 60 FPS 推理。6. 用验证集反推标注质量一个被低估的调优技巧训练完拿到best.pt只是第一步真正决定模型能不能用的是标注质量。我现在的习惯是训练前先跑一次yolo detect val用预训练权重在验证集上推理看模型“零样本”能检出多少。如果预训练模型在你数据集上的 mAP50 已经超过 0.3说明你的类别和 COCO 的 80 类有重叠标注格式也没大问题。如果低于 0.1要么类别太特殊要么标注有系统性错误。更细的做法是看混淆矩阵。YOLOv8 训练完会在runs/exp1/下生成confusion_matrix.png横轴是预测类别纵轴是真实类别。对角线越深越好非对角线上的亮块说明模型把 A 类误判成 B 类。我做过一个烟草病虫害数据集混淆矩阵显示“健康叶”和“早期病斑”互相误判严重回去看标注发现这两类的边界确实模糊早期病斑只有几个像素标注时全凭感觉。后来把这两类合并成一类“叶片异常”mAP 直接从 0.61 跳到 0.79。还有一个技巧是用yolo detect predict在验证集上跑推理把预测结果和真实标注叠在一起看。下面这段代码把预测框和真实框画到同一张图上import cv2 from ultralytics import YOLO model YOLO(./runs/exp1/weights/best.pt) img_path ./dataset/images/val/003.jpg label_path ./dataset/labels/val/003.txt # 推理 results model(img_path, conf0.25) img cv2.imread(img_path) h, w img.shape[:2] # 画真实框绿色 with open(label_path) as f: for line in f: cls, xc, yc, bw, bh map(float, line.split()) x1 int((xc - bw/2) * w) y1 int((yc - bh/2) * h) x2 int((xc bw/2) * w) y2 int((yc bh/2) * h) cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) # 画预测框红色 for box in results[0].boxes: x1, y1, x2, y2 map(int, box.xyxy[0].tolist()) cv2.rectangle(img, (x1, y1), (x2, y2), (0, 0, 255), 2) cv2.imwrite(./compare_003.jpg, img)绿色是真实标注红色是模型预测。如果红框和绿框位置差很多说明模型没学好回去查标注如果红框比绿框大一圈说明标注框太松如果某个目标只有绿框没有红框说明模型漏检看这个类别的样本是不是太少。这个对比图我一般会抽 10 到 20 张看比盯着 mAP 数字有用得多。最后说一个我自己的教训不要等训练完才看数据。我现在的工作流是标完数据先跑转换脚本然后随机抽 30 张画框检查确认没问题再开训。这一步花 20 分钟能省掉后面几小时的无效训练。希望帮到你。本文还有配套的精品资源点击获取
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

◈

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

◐

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

▲

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。