简介:《yolov8详细教程.docx》是一份面向目标检测初学者和课程实训学生的完整YOLOv8入门教程,聚焦从零搭建YOLOv8环境、完成图片标注与模型训练全流程。文档先梳理YOLOv8相比YOLOv5的骨干网络、Anchor-Free检测头、C2f模块及多尺度融合等创新点,再逐步展示Anaconda、PyCharm安装配置,PyTorch安装、镜像源加速、YOLO测试以及LabelImg标注和图像拆分等关键环节,并特别提示中文路径、安装确认等常见坑点,实用性很强。资源为单一docx格式文档,共1个文件,压缩包大小4.97MB,便于对照实操和课后复习,目前已有318人学习下载,适合作为课程配套资料或自学参考。
1. 为什么那么多教程写完之后,YOLOv8还是跑不起来
跑通 YOLOv8 大概只要十分钟,但把损失函数曲线画清楚、把模型训练到能用的水平、再部署到 RK3588 这类边缘设备上,大多数人至少要卡一个下午。YOLOv8 这套代码本身封装得足够贴心,环境配置、数据集组织、训练和导出都被收敛成了命令行。正因为封装太好,出了问题反而更难定位:loss 不降到底怪学习率、怪标签、还是怪模型结构?这篇内容就按“yolov8 详细教程”最该覆盖的流程展开,从环境搭建到训练自己的数据,再到画损失曲线、做 head 改进,最后落到 onnx 导出和边缘部署,每一步的参数、命令和坑都标出来。适合刚把 yolov8 跑通、但还没在自己的数据上拿到可用模型的人。
2. 搭建 YOLOv8 环境:从空目录到画出网络结构图
环境配置是劝退新手的第一道坎。很多教程默认你已经装好了 CUDA、PyTorch 和一堆依赖,实际动手时往往在 torch 版本和显卡驱动上先翻车。这里按最小可复现的顺序走:建虚拟环境、装依赖、跑一次推理、把模型结构图导出来看一眼。
2.1 环境搭建步骤:conda + PyTorch 版本怎么选
无论是 Windows 还是 Linux,我一般都用 conda 建一个独立环境,避免把系统 Python 搞乱。
conda create -n yolov8 python=3.10 -y conda activate yolov8 pip install ultralytics pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118第一行创建 Python 3.10 的环境,第二行激活。第三行安装 ultralytics 包,它会顺带把 YOLOv8 需要的依赖装好。第四行单独装 GPU 版 PyTorch,这里指定 cu118 表示 CUDA 11.8 版本。如果你的显卡是 GTX1660Ti 这类 Turing 架构,CUDA 11.8 是兼容性最好的选择;如果是 RTX 30/40 系列,也可以换成 cu121 或更新的版本。
装完先验证 GPU 可用性:
python -c "import torch; print(torch.__version__, torch.cuda.is_available())"如果输出最后是 False,说明 PyTorch 没识别到显卡,多半是驱动版本太老或者装了 CPU 版 torch。这时候不要急着重装,先用nvidia-smi看驱动支持的 CUDA 版本,再按那个版本去 PyTorch 官网选对应的安装命令。训练时还要留意一个坑:torch.cuda.is_available()返回 True 不代表训练一定能用,实际跑到device=0的时候也可能因为显存不足报错,这个后面避坑章再展开。
2.2 下载 yolov8 和预训练权重:一条命令把推理跑起来
很多人会问“yolov8 哪里下载”,其实不需要单独下载源码包和权重文件。ultralytics 的设计是:你写一条预测命令,权重会自动下载到当前目录,源码则随 pip 包一起安装。
yolo detect predict model=yolov8s.pt source=https://ultralytics.com/images/bus.jpg这条命令会先检查本地有没有yolov8s.pt,没有就自动下载,然后对示例图片做目标检测,把结果保存到runs/detect/predict目录。如果下载特别慢或者失败,你可以手动把权重文件放进当前目录,再把命令里的model=web/model/path改成本地路径:
yolo detect predict model=./weights/yolov8s.pt source=./test.jpg save=True参数save=True表示保存标注后的图片,conf=0.25可以过滤低置信度框,iou=0.7控制 NMS 的重复框抑制强度。实际调参时,我习惯先不动这两个值,跑通流程后再根据漏检和误检情况回调。
模型选哪个?官方预训练权重按体积分成了 n、s、m、l、x 五档。我的建议是:先用 n 或 s 把代码流程跑通,确认环境和数据集没问题,再换大模型追求精度。在 6GB 显存的 GTX1660Ti 上,直接训练 l 或 x 会非常痛苦,但拿它们做推理是没问题的。
| 权重 | 典型用途 | 1660Ti 训练建议 |
|---|---|---|
| yolov8n.pt | 流程验证、快速原型 | 可以训,batch 16 以内 |
| yolov8s.pt | 大多数小项目起步 | 可以训,batch 8 左右 |
| yolov8m.pt | 精度优先 | batch 4,容易接近显存上限 |
| yolov8l/x.pt | 服务器离线训练 | 基本不建议在 6GB 卡上训 |
2.3 网络结构图怎么画:先导出 onnx,再用工具打开
网上搜“yolov8 网络结构图”会看到很多论文风格的图,但那些图和你实际训练的模型不一定对得上。自己导出一张最靠谱。常见做法是先把模型导出成 onnx,再用 Netron 打开,可以交互式查看每一层的输入输出形状。
from ultralytics import YOLO model = YOLO("yolov8s.pt") model.export(format="onnx", opset=12, dynamic=True)导出后会生成yolov8s.onnx。opset=12是兼容性较好的算子集版本,dynamic=True允许动态输入尺寸。用 Netron 打开 onnx 文件,你能看到 backbone 里的 C2f 模块、SPPF,以及 head 的三个检测分支。这个图比任何手绘结构图都准确,排查“输出维度对不上”这类问题时特别有用。
如果你不想装额外软件,也可以用 torchinfo 直接打印参数量和每层输出形状:
pip install torchinfo然后在脚本里读取模型结构,但这里有个细节:YOLOv8 的模型结构是由 yaml 配置动态构建的,你直接print(model)会看到一长串模块调用,不适合快速理解。我通常先看 yaml 文件里的 backbone 和 head 两个段落,再结合 onnx 图去核对。yaml 里定义了每个模块的类型、通道数和重复次数,看懂它比看懂代码快得多。
3. 训练自己的数据集:目录、data.yaml 和关键参数含义
跑通推理只是热身,真正让 yolov8 为你所用,必须训练自己的数据集。这一章从头过一遍:标注完的数据怎么整理、data.yaml 怎么写、训练命令里每个参数到底在干什么,以及 1660Ti 这种低显存显卡怎么保住训练不中断。
3.1 标注工具与数据目录:VOC 转 YOLO 的边界坑
不管用什么标注工具,最终 YOLO 需要的标签格式是统一的:每张图片对应一个同名 txt,每行写class x_center y_center width height,四个坐标值都归一化到 0~1 之间。目录结构建议按下面这样组织:
datasets/mydata/ ├── images/ │ ├── train/ │ └── val/ ├── labels/ │ ├── train/ │ └── val/ └── data.yaml假设你手里的标注是 LabelImg 导出的 VOC XML,就要做一个转换。下面这段脚本把 XML 里的绝对坐标转成 YOLO 归一化格式:
import xml.etree.ElementTree as ET def voc2yolo(xml_path, out_txt_path, classes): tree = ET.parse(xml_path) root = tree.getroot() size = root.find("size") w = int(size.find("width").text) h = int(size.find("height").text) lines = [] for obj in root.findall("object"): name = obj.find("name").text if name not in classes: continue cls_id = classes.index(name) box = obj.find("bndbox") x1 = float(box.find("xmin").text) y1 = float(box.find("ymin").text) x2 = float(box.find("xmax").text) y2 = float(box.find("ymax").text) x_center = (x1 + x2) / 2 / w y_center = (y1 + y2) / 2 / h box_w = (x2 - x1) / w box_h = (y2 - y1) / h lines.append(f"{cls_id} {x_center:.6f} {y_center:.6f} {box_w:.6f} {box_h:.6f}") with open(out_txt_path, "w") as f: f.write("\n".join(lines))这段脚本的核心是坐标归一化:(x1 + x2) / 2 / w计算中心点 x,(x2 - x1) / w计算框宽。容易出错的是classes列表顺序必须和后面 data.yaml 里的 names 完全一致,类别索引从 0 开始。很多人犯的第一个错就是把“第 1 类”写成了 1,结果模型训练时默认留了一个背景类,导致怎么训都不收敛。另外,XML 里如果出现空框或者坐标越界,建议在转换时直接跳过,不要留到训练时让数据处理崩溃。
3.2 data.yaml 文件:路径写错会静默失败
data.yaml 是训练命令唯一的数据入口,里面的路径解释特别容易踩坑。下面是一个最小例子:
path: datasets/mydata train: images/train val: images/val nc: 2 names: 0: cat 1: dog注意path是相对于你执行训练命令的工作目录而言的,不是相对于 yaml 文件所在目录。如果你把 data.yaml 放在项目根目录,而训练命令在别处执行,相对路径就会失效。我一般建议用绝对路径,或者把命令和 yaml 放在同一层目录执行,能少排很多错。
nc是类别总数,names必须从 0 开始编号。如果数据里实际有 3 类,但你只写了 nc: 2,训练不会直接报错,而是把所有第三类的标签过滤掉,验证集的 mAP 会莫名变低,不容易察觉。所以在写 yaml 之前,建议先扫描一遍标签目录,统计类别编号的最大值,确认它小于 nc。
3.3 启动训练命令:训练参数含义逐个说清楚
数据就绪后,训练命令如下:
yolo detect train \ data=datasets/mydata/data.yaml \ model=yolov8s.pt \ epochs=50 \ imgsz=640 \ batch=8 \ workers=4 \ device=0 \ cache=False \ patience=10每个参数都不是摆设。epochs=50是训练轮数,小数据集 50 轮足够,大数据集建议 100 轮以上,配合早停机制反而更稳。imgsz=640是训练分辨率,不是越大越好,越大的图越吃显存,而且如果没有特别小的目标,640 是默认最优解。batch=8是每次送入显卡的图片数,它直接决定显存占用。workers=4是数据加载线程数,Windows 上太高容易报 DataLoader worker 错误,Linux 可以开到 8 或 16。cache=False表示不把图片缓存到内存,如果内存够大、想加速,可以改为cache=True。patience=10表示如果连续 10 个 epoch 验证指标不提升就提前结束,这是省时间的关键。
有个容易被忽略的参数是lr0,默认 0.01。用默认预训练权重继续训练时,这个值一般不用动;但如果是从零开始训练,0.01 对某些数据集来说偏大,loss 会在前几个 epoch 出现剧烈波动甚至 NaN。遇到这种情况,先把lr0=0.001压下来再试。
下面这张表整理了最常用的几个训练参数,方便快速查询:
| 参数 | 默认值 | 作用 | 建议 |
|---|---|---|---|
| model | - | 预训练权重或 yaml | 首次训练用 s,验证流程用 n |
| epochs | 100 | 训练轮数 | 小数据集 50,大数据集 100+ |
| imgsz | 640 | 输入分辨率 | 有大量小目标再考虑 1280,否则慎用 |
| batch | 16 | 单卡 batch size | 1660Ti 建议 8,OOM 时降到 4 |
| workers | 8 | 数据加载进程数 | Windows 用 4,Linux 用 8 |
| patience | 100 | 早停等待轮数 | 设 10~20,省时间 |
| cache | False | 是否缓存数据 | 内存小建议 False |
| amp | True | 混合精度训练 | 自定义网络报 NaN 时先关掉 |
3.4 低显存显卡训练:1660Ti 上跑 yolov8 的具体配置
GTX1660Ti 只有 6GB 显存,这是大多数人入门的第一块卡,也是最容易 OOM 的组合。我的经验是:模型用yolov8s.pt,imgsz=640,batch=8,amp=True可以正常训进去。如果还报显存不足,优先调 batch,不要调 imgsz,因为 imgsz 降低会影响小目标检测能力,batch 降低则可以通过梯度累积来补偿。
yolo detect train \ data=datasets/mydata/data.yaml \ model=yolov8s.pt \ epochs=100 \ imgsz=640 \ batch=4 \ nbs=64 \ workers=4 \ device=0nbs是名义 batch size。它的原理是:当实际 batch=4 时,梯度会累积 16 步再进行参数更新,模拟出 batch=64 的效果。这个参数在低显存场景下特别实用,能保住大 batch 带来的稳定梯度,代价只是训练时间略长。开着amp混合精度也能省将近一半显存,但如果你自己改了网络结构,某个自定义层对 fp16 不友好,就得先把amp=False跑一轮验证结果正常再开。
4. 从损失曲线到模型改进:可视化与 head 优化
训练启动后,不能干等日志。把损失函数曲线图画出来是判断训练是否健康的最快手段。曲线形态不对,后面做再多改进都是白搭。这一章先解决“怎么画损失函数曲线图”,再讲两个常见改进方向:head 改进和协调注意力机制。
4.1 画损失函数曲线图:用 results.csv 一行代码看清训练状态
训练过程中会自动生成runs/detect/train/results.csv,里面按 epoch 记录了 box_loss、cls_loss、dfl_loss、精确率和 mAP 等指标。直接用 matplotlib 画出来:
import csv import matplotlib.pyplot as plt rows = list(csv.DictReader(open("runs/detect/train/results.csv"))) metrics = ["train/box_loss", "train/cls_loss", "train/dfl_loss"] plt.figure(figsize=(10, 6)) for m in metrics: values = [float(r[m]) if r.get(m) else float("nan") for r in rows] plt.plot(values, label=m) plt.legend() plt.grid(True) plt.xlabel("epoch") plt.ylabel("loss") plt.savefig("loss_curve.png")这段代码先读取 results.csv,再按列名取训练损失。r.get(m)判断列是否存在,防止某些版本的 CSV 列名不一致导致 KeyError。跑完之后看三点:loss 是否整体下降、是否有突然跳变、训练 loss 和验证 loss 的差距是否越来越大。如果 loss 在前 10 个 epoch 快速下降,后面缓慢波动,这是正常形态。如果 loss 一直横着不动,先回数据环节查标签,别急着改网络。
results.csv里每列对应一个 epoch 的最终值,如果你想看更细的曲线,可以自己写回调在每个 batch 后记录。但实际排查问题用 epoch 级曲线就够了,batch 级曲线噪声太大,反而看不出趋势。
4.2 YOLOv8 head 改进:两个值得动手的方向
YOLOv8 的检测头是解耦头,分类分支和回归分支分开输出。很多人说“yolov8 head 改进”,最常见的两个方向是:加注意力机制、加小目标检测层。我的建议是,先想清楚瓶颈在哪。如果漏检的主要是小目标,优先考虑增加 P2 输出层,让网络在更高分辨率特征图上产生预测;如果误检多、分类不准,再考虑在 head 前插入注意力模块。
P2 层的改动比想象中复杂,因为它会新增一个特征金字塔层,并且要改 head 的输入通道数。相比之下,加协调注意力机制是侵入性更小、回退更容易的路径。协调注意力(Coordinate Attention)在 channel 注意力基础上把位置信息也压进特征图,对目标定位和小目标召回有稳定帮助,而且模块轻量,很适合作为第一个动手实验。
4.3 给 YOLOv8 加一个协调注意力模块:最小改动路径
下面定义了一个常见的 CoordAtt 模块:
import torch from torch import nn class CoordAtt(nn.Module): def __init__(self, inp, oup, reduction=32): super().__init__() self.pool_h = nn.AdaptiveAvgPool2d((None, 1)) self.pool_w = nn.AdaptiveAvgPool2d((1, None)) mip = max(8, inp // reduction) self.conv1 = nn.Conv2d(inp, mip, kernel_size=1, bias=False) self.bn1 = nn.BatchNorm2d(mip) self.act = nn.ReLU(inplace=True) self.conv_h = nn.Conv2d(mip, oup, kernel_size=1, bias=False) self.conv_w = nn.Conv2d(mip, oup, kernel_size=1, bias=False) def forward(self, x): h, w = x.shape[-2], x.shape[-1] x_h = self.pool_h(x) x_w = self.pool_w(x).permute(0, 1, 3, 2) y = torch.cat([x_h, x_w], dim=2) y = self.act(self.bn1(self.conv1(y))) x_h, x_w = torch.split(y, [h, w], dim=2) x_w = x_w.permute(0, 1, 3, 2) a_h = self.conv_h(x_h).sigmoid() a_w = self.conv_w(x_w).sigmoid() return x * a_h * a_w这个模块先沿横向和纵向分别做全局池化,拼接到一起卷积,再拆开、上采样成注意力权重,最后乘回原特征图。pool_h的输出形状是(B, C, H, 1),pool_w是(B, C, 1, W),拼接时沿 H 维拼成一个长条,再通过卷积融合横向和纵向信息。reduction=32控制中间通道数,显存不够时可以调大到 64,效果差异不大。
把它集成到 YOLOv8 里,需要三步:把模块类写进ultralytics/nn/modules/conv.py或单独文件,在ultralytics/nn/tasks.py的模块映射里注册类名,再在模型 yaml 的 backbone 末尾或 neck 的某个位置插入一行,例如${某个层编号} = CoordAtt。注意,yaml 里的模块序号不能写错,写错了模型能构建出来但结构不是你想要的。改完先跑一次前向传播,用一张假图片验证输出尺寸正常,再开训练。这一步能躲开 80% 的改网络翻车。
5. 避坑:YOLOv8 训练与部署的常见问题排查
训练过程里的很多“玄学”问题,最后查下来都是数据和配置的低级错误。这里列五条我踩过、也帮别人排查过的典型问题,按“现象、原因、解决”的方式写清楚。
5.1 训练刚开始 loss 就变成 NaN
现象:第一个 epoch 还没结束,日志里的 box_loss 就是 nan,训练直接翻车。
原因:最常见的是学习率太大,导致梯度爆炸;其次是开启了 AMP 混合精度,而自定义层里出现不稳定的中间值;还有个隐蔽原因是标签文件里有无效坐标,比如框宽高为 0 或坐标值出现 inf。
解决:先关掉 AMP,把amp=False加进训练命令;再把lr0从 0.01 降到 0.001 重试。如果还是 NaN,写脚本检查所有标签文件,过滤掉宽高小于等于 0 的框。顺序不要反,先确认数据没问题,再动优化器参数,否则会一边调一边怀疑数据,浪费很多时间。
5.2 训练中途 CUDA out of memory
现象:前几个 epoch 正常,跑到某个 batch 突然中断,终端报RuntimeError: CUDA out of memory。
原因:显存占用随训练波动,验证阶段的某些大分辨率图片或梯度累积峰值越过了显存上限。另一个常见原因是cache=True把整个数据集缓存在内存/显存里,6GB 卡直接击穿。
解决:把batch从 8 降到 4,cache改为 False,workers降到 4。如果还是时不时断,在训练脚本里加上torch.cuda.empty_cache()之后再做验证。实际经验是 1660Ti 上跑 yolov8s + batch 8 + imgsz 640 勉强能过,但建议保守用 batch 4 +nbs=64,稳定优先。
5.3 训练能完成,但推理完全没结果
现象:训练日志显示 mAP 有数值,但用训练好的权重去推理测试图,一个框都不出,或者输出的类别全是错的。
原因:确定下标签类别编号。标注工具如果导出的类别编号从 1 开始,转换时没减 1,模型训练时会认为还有第 0 类背景。训练过程不会报错,因为背景类也能计算损失,只是推理时背景占比过大,正常目标全被过滤掉了。
解决:检查任一标签 txt,读出第一列的最大值,必须小于 data.yaml 里的 nc。写这样一个小检查脚本:
import os label_dir = "datasets/mydata/labels/train" max_cls = 0 for f in os.listdir(label_dir): with open(os.path.join(label_dir, f)) as fp: for line in fp: cls_id = int(line.strip().split()[0]) max_cls = max(max_cls, cls_id) print("max class id:", max_cls)如果最大值已经是 nc,说明类别偏移了,转换脚本里减 1 重新生成标签。这个错误非常隐蔽,尤其是在你只用几类数据做测试的时候。
5.4 loss 在下降,但 mAP 一直是 0
现象:训练时三类 loss 都在降,看起来一切正常,但是metrics/mAP50(B)始终是 0。
原因:验证集里没有真实标签,或者验证集的图片和标签没对应上。比如标签文件里没有和图片同名的 txt,验证时每个预测都找不到匹配的 ground truth,mAP 自然为 0。还有一种情况是验证集本身太小,少数几张图里恰好没有目标。
解决:统计验证集目录里图片数量和标签数量,确认一一对应。再用下面命令单独验证一下:
yolo detect val model=runs/detect/train/weights/best.pt data=datasets/mydata/data.yaml如果 val 输出里Instances一栏是 0,说明验证集根本没有读到标签。这时候优先检查 data.yaml 的 val 路径,而不是模型。
5.5 导出 onnx 后精度下降,部署到 RK3588 更明显
现象:PyTorch 模型直接推理效果很好,导出 onnx 后精度略降,再转换到 RKNN 或 TensorRT 后小目标大量漏检。
原因:onnx 导出时的图像预处理和后处理与训练时不完全一致,比如颜色通道顺序、letterbox 填充方式、归一化系数。另一个是量化校准集数量不够,典型的错误是只拿 20 张图片做 int8 量化,导致激活值范围统计不准。
解决:导出时固定opset=12,避免使用过新算子导致转换工具兼容性差。部署端的前处理要和训练端严格保持一致,YOLOv8 的训练预处理是 BGR 转 RGB、除以 255、letterbox 填充灰边。量化校准图至少准备 200~300 张,且要包含和实际业务场景相近的样本,不能只挑清晰的图,要混入模糊、遮挡和小目标样本。校准集越贴近真实分布,量化掉点越小。
6. 部署最后一步:导出、量化模型和边缘设备上的精度验证技巧
训练完的模型不能只在电脑上自嗨,总要落到 RK3588 或 Orin 这类设备上跑。这两个平台的部署路径不一样:RK3588 用 RKNN-Toolkit2 把 onnx 转成 rknn 模型,Orin 上走 TensorRT。但无论哪条路,第一步都是统一导出 onnx。
yolo detect export \ model=runs/detect/train/weights/best.pt \ format=onnx \ opset=12导出后先对比 onnx 和 PyTorch 的输出,误差超过 1e-3 就要检查导出参数。同一批验证图片,分别用 .pt 和 .onnx 推理,对比检测框坐标和置信度。很多教程跳过了这一步,直接转换到边缘设备,最后精度掉了却不知道该怪谁。
在 RK3588 上做 int8 量化时,我习惯先从 fp16 版本起步,确认整个链路通了之后再量化为 int8。量化后的精度验证不要只看 mAP,还要看具体类别的召回变化。训练好的模型往往对某一类特别敏感,量化后最容易先崩的就是那些小目标或低对比度目标。把量化模型在 100 张真实场景图上跑一遍,对比每个类别的检出数,比看整体 mAP 更直观。
如果你只在边缘设备上做检测而不做分割任务,部署时还可以关掉一些不用的输出项,比如只保留[0]这个输出,能减少转换和推理开销。Orin 上如果显存充足,优先用 fp16,不要一上来就 int8,省一次量化调优的麻烦。
最后说一个我的习惯:任何模型改动前,先跑一次 fp32 baseline,把 mAP50、mAP50-95 和每类召回率存下来。后面无论是加注意力、改 head、做量化,都拿这次结果对比,而不是凭感觉说“好像提升了”。这个习惯帮我避免了很多自欺欺人的实验结论,也让模型迭代有据可查。希望帮到你。
本文还有配套的精品资源,点击获取