代码手把手教你玩转YOLO系列:深度学习物体检测项目实战
从环境配置到模型部署,一步步掌握YOLO目标检测,让机器“看见”世界
在深度学习的众多领域中,目标检测无疑是最具吸引力和应用价值的方向之一。它不仅要求模型识别图像中的物体,还能精确地定位其位置,这为自动驾驶、视频监控、医疗诊断等众多应用场景打开了大门。在众多目标检测算法中,YOLO(You Only Look Once)系列因其出色的速度与精度的平衡,成为了工业界和学术界的热门选择。
我作为深度学习领域的实践者,一直坚信“动手是最好的老师”。在这篇文章中,我将带你一起通过代码和实操,从零开始玩转YOLO系列,完成一个属于自己的目标检测项目。无论你是刚入门的初学者,还是希望深入进阶的开发者,这篇手把手教程都能为你提供清晰的路径和实用的技巧。
🧠 一、理解YOLO系列:从原理到选择
在开始敲代码之前,我们首先需要了解YOLO系列的核心思想以及不同版本的特点,这样才能根据项目需求选择合适的模型。
1.1 YOLO的核心思想:一步到位的检测
与传统的“两阶段检测器”(如R-CNN系列)不同,YOLO将目标检测任务视为一个单一的回归问题。它只需要看一眼图像,就能同时预测出所有边界框及其类别概率。这种端到端的架构设计,使得YOLO在推理速度上具有显著优势,非常适合实时应用。
flowchart LR
A[输入图像] --> B[YOLO网络]
B --> C[输出特征图]
C --> D[边界框预测
位置与尺寸]
C --> E[类别概率预测]
D --> F[后处理
NMS等]
E --> F
F --> G[最终检测结果
多个边界框及其类别]
1.2 YOLO版本演进与特点
YOLO系列经过多次迭代,每个版本都在速度、精度或易用性方面进行了优化。下表对比了几个主要版本的特点,帮助你做出选择:
💡 如何选择?
初学者/快速项目:优先选择 YOLOv5,文档和教程极其丰富,踩坑成本低。
需要多任务处理:选择 YOLOv8,其统一的API可以轻松切换检测、分割和分类任务。
极致实时/边缘部署:选择 YOLOv10,其无NMS设计和高效的模型结构能带来显著的延迟降低。
本教程将主要以 YOLOv8 和 YOLOv10 为例,因为它们代表了最新的技术方向,且YOLOv8的生态非常成熟,而YOLOv10则是最新的实时检测王者。
🛠️ 二、项目实战环境搭建:工欲善其事
一个稳定、可复现的开发环境是深度学习项目成功的基石。我们将使用Anaconda来管理Python环境,并确保CUDA加速得到正确配置。
2.1 创建与激活虚拟环境
为了避免不同项目之间的依赖冲突,强烈建议为YOLO项目创建一个独立的虚拟环境。
创建一个名为yolo_env的Python 3.8虚拟环境
conda create -n yolo_env python=3.8
激活环境(Windows下无需conda)
conda activate yolo_env
2.2 安装PyTorch与CUDA支持
YOLO系列的官方实现都是基于PyTorch的。如果你有NVIDIA显卡,安装CUDA版本的PyTorch可以大幅加速训练和推理过程。
检查你的显卡和CUDA版本:在终端(或CMD)中输入:
nvidia-smi
记下输出中 CUDA Version 的值(例如 12.1)。你的显卡支持的CUDA版本必须大于或等于你计划安装的PyTorch版本所需的CUDA版本。
安装PyTorch:根据你的CUDA版本和操作系统,从 PyTorch官网 选择合适的安装命令。例如,对于CUDA 12.1:
使用pip安装(推荐)
pip install torch torchvision torchaudio --index-url download.pytorch.org/whl/cu121
2.3 安装Ultralytics库(YOLOv8/v10)
Ultralytics库是YOLOv8和v10的官方实现,安装非常简单。
安装Ultralytics库
pip install ultralytics
🔧 高级:从源码安装(便于修改和调试)
如果你希望修改底层代码或使用最新功能,可以从GitHub克隆源码并安装:
git clone https://github.com/ultralytics/ultralytics
cd ultralytics
pip install -e .
-e 参数表示以“可编辑”模式安装,这样你对代码的修改会立即生效,无需重新安装。
2.4 验证环境
安装完成后,可以通过以下简单代码验证环境是否配置成功。
import torch
from ultralytics import YOLO
# 检查PyTorch和CUDA是否可用
print(f"PyTorch version: {torch.__version__}")
print(f"CUDA available: {torch.cuda.is_available()}")
if torch.cuda.is_available():
print(f"CUDA version: {torch.version.cuda}")
print(f"Device name: {torch.cuda.get_device_name(0)}")
# 尝试加载一个YOLOv8模型(会自动下载权重)
model = YOLO('yolov8n.pt')
print("YOLO model loaded successfully!")
如果以上代码没有报错,并且正确输出了你的CUDA信息,恭喜你,你的环境已经准备就绪了!
📊 三、数据准备与标注:巧妇难为无米之炊
数据是深度学习模型的“燃料”。对于目标检测任务,你需要准备图像,并标注出其中你感兴趣的物体及其位置。
3.1 数据集目录结构
一个清晰的目录结构是良好项目的开始。YOLO系列对数据集的目录结构有特定要求,遵循这个结构可以避免很多后续的麻烦。
my_dataset/
├── images/
│ ├── train/ # 训练集图片
│ │ ├── img1.jpg
│ │ ├── img2.jpg
│ │ └── ...
│ ├── val/ # 验证集图片
│ └── test/ # 测试集图片(可选)
└── labels/
├── train/ # 训练集标签文件
│ ├── img1.txt
│ ├── img2.txt
│ └── ...
└── val/ # 验证集标签文件
3.2 数据标注:LabelImg
标注是将人类对图像中物体的“认知”转化为计算机可理解的数学表示的过程。LabelImg 是一款简单易用的标注工具。
# 安装LabelImg
pip install labelimg
# 启动LabelImg
labelimg
使用方法:
在LabelImg中,通过 Open Dir 选择你的图片文件夹(如 my_dataset/images/train)。
通过 Change Save Dir 设置对应的标签文件夹(如 my_dataset/labels/train)。
使用 Create RectBox 工具在图片上绘制边界框,并选择类别。
保存后,会自动生成一个与图片同名的 .txt 标签文件。
YOLO格式的标签文件:每个标签文件包含多行,每行对应一个物体,格式为:
其中,, , , 都是相对于图片宽和高的归一化坐标(即0到1之间的浮点数)。
🔄 VOC与YOLO格式转换(可选)
如果你的标注是VOC格式(.xml文件),可以使用以下代码将其转换为YOLO格式(.txt文件)。
import xml.etree.ElementTree as ET
import os
def convert_box(size, box):
dw = 1./size[0]
dh = 1./size[1]
x = (box[0] + box[1])/2.0
y = (box[2] + box[3])/2.0
w = box[1] - box[0]
h = box[3] - box[2]
return (x*dw, y*dh, w*dw, h*dh)
def convert_annotation(xml_path, output_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)
with open(output_txt_path, 'w') as out_file:
for obj in root.iter('object'):
cls = obj.find('name').text
if cls not in classes:
continue
cls_id = classes.index(cls)
xmlbox = obj.find('bndbox')
b = (float(xmlbox.find('xmin').text), float(xmlbox.find('xmax').text),
float(xmlbox.find('ymin').text), float(xmlbox.find('ymax').text))
bb = convert_box((w, h), b)
out_file.write(f"{cls_id} {bb[0]} {bb[1]} {bb[2]} {bb[3]}\n")
# 使用示例
classes = ['person', 'car'] # 你的类别列表
xml_dir = 'path/to/xml/files'
txt_dir = 'path/to/save/txt/files'
if not os.path.exists(txt_dir):
os.makedirs(txt_dir)
for xml_file in os.listdir(xml_dir):
if xml_file.endswith('.xml'):
xml_path = os.path.join(xml_dir, xml_file)
txt_filename = xml_file.replace('.xml', '.txt')
txt_path = os.path.join(txt_dir, txt_filename)
convert_annotation(xml_path, txt_path, classes)
3.3 数据集划分与配置文件
通常,我们需要将数据集划分为训练集、验证集和测试集。
数据集划分脚本:
import os
import random
import shutil
def split_dataset(image_dir, label_dir, output_dir, train_ratio=0.8, val_ratio=0.1, test_ratio=0.1):
# 确保比例之和为1
assert train_ratio + val_ratio + test_ratio == 1, "Ratios must sum to 1"
# 获取所有图片文件名(不带后缀)
image_files = [os.path.splitext(f)[0] for f in os.listdir(image_dir) if f.endswith(('.jpg', '.png', '.jpeg'))]
# 随机打乱
random.shuffle(image_files)
# 计算划分数量
total = len(image_files)
train_count = int(total * train_ratio)
val_count = int(total * val_ratio)
# 划分索引
train_files = image_files[:train_count]
val_files = image_files[train_count:train_count + val_count]
test_files = image_files[train_count + val_count:]
# 创建输出目录
for split in ['train', 'val', 'test']:
os.makedirs(os.path.join(output_dir, 'images', split), exist_ok=True)
os.makedirs(os.path.join(output_dir, 'labels', split), exist_ok=True)
# 复制文件到对应目录
def copy_files(files, split):
for file in files:
# 复制图片
for ext in ['.jpg', '.png', '.jpeg']:
src_img = os.path.join(image_dir, file + ext)
if os.path.exists(src_img):
shutil.copy(src_img, os.path.join(output_dir, 'images', split, file + ext))
break
# 复制标签
src_lbl = os.path.join(label_dir, file + '.txt')
if os.path.exists(src_lbl):
shutil.copy(src_lbl, os.path.join(output_dir, 'labels', split, file + '.txt'))
copy_files(train_files, 'train')
copy_files(val_files, 'val')
copy_files(test_files, 'test')
print(f"Dataset split: Train: {len(train_files)}, Val: {len(val_files)}, Test: {len(test_files)}")
# 使用示例
split_dataset('my_dataset/images/all', 'my_dataset/labels/all', 'my_dataset_split')
创建数据配置文件(data.yaml):
在项目根目录下创建一个 data.yaml 文件,告诉YOLO你的数据集信息。
path: ./my_dataset_split # 数据集根目录(相对于此yaml文件)
train: images/train # 训练集图片路径(相对于path)
val: images/val # 验证集图片路径
test: images/test # 测试集图片路径(可选)
nc: 2 # 类别数量
names: ['person', 'car'] # 类别名称列表
🚀 四、模型训练:让机器学习识别物体
一切准备就绪,现在可以开始训练你的自定义目标检测模型了!
4.1 训练命令行接口(CLI)
YOLOv8/v10提供了非常简洁的命令行接口,一条命令即可启动训练。
yolo task=detect mode=train model=yolov8n.pt data=data.yaml epochs=100 batch=16 imgsz=640 device=0
参数解释:
task=detect: 指定任务为目标检测。
mode=train: 指定运行模式为训练。
model=yolov8n.pt: 指定使用的预训练模型权重。n代表nano(最小最快的模型),还有s, m, l, x等尺寸可选。
data=data.yaml: 指定数据集配置文件路径。
epochs=100: 训练的轮数。可以根据数据集大小调整。
batch=16: 批次大小。根据你的显存大小调整,越大占用显存越多,训练速度可能越快。
imgsz=640: 训练时输入图像的尺寸(宽=高)。通常设置为640,可根据需求调整。
device=0: 指定训练设备。0表示使用第一个GPU,cpu则表示使用CPU。
4.2 Python代码训练
你也可以在Python代码中更灵活地控制训练过程。
from ultralytics import YOLO
# 加载预训练模型(会自动下载)
model = YOLO('yolov8n.pt')
# 开始训练
results = model.train(
data='data.yaml', # 数据集配置文件
epochs=100, # 训练轮数
batch=16, # 批次大小
imgsz=640, # 输入图像尺寸
device=0, # 使用GPU
name='my_exp', # 实验名称,结果会保存在runs/detect/my_exp
patience=50, # 早停机制,如果验证集mAP在50个epoch内没有提升就停止训练
save=True, # 保存最后和最好的模型权重
plots=True, # 绘制训练曲线和结果图
verbose=True # 打印训练日志
)
# 训练完成后,模型权重会保存在runs/detect/my_exp/weights/下
# best.pt: 验证集mAP最高的模型权重
# last.pt: 训练最后一个epoch的模型权重
4.3 训练过程监控与理解
训练开始后,你会看到一个控制台输出,实时显示训练进度和关键指标。理解这些指标对于调优模型至关重要。
训练完成后,Ultralytics会自动在 runs/detect/exp/ 目录下生成丰富的可视化结果,包括:
results.png: 训练过程中的所有指标变化曲线。
confusion_matrix.png: 混淆矩阵,展示各类别识别的准确性。
F1_curve.png: F1分数曲线(精确率和召回率的调和平均)。
PR_curve.png: 精确率-召回率曲线。
val_batchX_pred.jpg: 验证集上某批次图片的预测结果可视化。
🧪 五、模型验证与测试:检验学习成果
训练完成后,我们需要客观地评估模型性能,并进行预测测试。
5.1 在验证集上评估模型
使用训练好的最佳模型(best.pt)在验证集上进行评估。
yolo task=detect mode=val model=runs/detect/my_exp/weights/best.pt data=data.yaml batch=16 imgsz=640 device=0
或者Python代码:
# 加载训练好的最佳模型
model = YOLO('runs/detect/my_exp/weights/best.pt')
# 在验证集上评估
metrics = model.val()
# 打印关键指标
print(f"mAP50-95: {metrics.box.map:.4f}")
print(f"mAP50: {metrics.box.map50:.4f}")
print(f"Precision: {metrics.box.mp:.4f}")
print(f"Recall: {metrics.box.mr:.4f}")
5.2 对单张图片/视频进行预测
测试模型的实际效果。
# 预测单张图片
yolo task=detect mode=predict model=runs/detect/my_exp/weights/best.pt source=path/to/image.jpg
# 预测视频(会逐帧处理并保存结果视频)
yolo task=detect mode=predict model=runs/detect/my_exp/weights/best.pt source=path/to/video.mp4
# 预测摄像头实时画面(0表示默认摄像头)
yolo task=detect mode=predict model=runs/detect/my_exp/weights/best.pt source=0
Python代码示例:
# 加载模型
model = YOLO('runs/detect/my_exp/weights/best.pt')
# 对单张图片进行预测
results = model('path/to/image.jpg')
# 遍历并绘制结果
for r in results:
im_bgr = r.plot() # 绘制带框和标签的BGR图像
im_rgb = im_bgr[..., ::-1] # 转换为RGB
# 可以使用cv2.imshow显示或cv2.imwrite保存
# import cv2; cv2.imshow('YOLO Prediction', im_rgb); cv2.waitKey(0); cv2.destroyAllWindows()
🧩 六、模型优化与进阶技巧
当你的基础模型训练完成后,可以尝试以下技巧来进一步提升性能。
6.1 超参数调优
YOLO系列提供了许多可调的超参数。你可以通过调整它们来获得更好的性能。
关键超参数:
如何调整?
找到默认超参数文件:通常在 ultralytics/yolo/data/hyps/ 目录下,如 hyp.scratch-low.yaml。
复制并修改:将其复制到你的项目目录,修改你想要调整的参数值。
训练时指定:在训练命令中加上 hyps=path/to/your/hyp.yaml 参数。
yolo task=detect mode=train model=yolov8n.pt data=data.yaml epochs=100 batch=16 imgsz=640 hyps=my_hyp.yaml
6.2 模型架构微调
除了调整超参数,你还可以尝试修改模型本身的结构(高级用户)。
调整模型深度:修改模型配置文件(如 yolov8n.yaml),增加或减少 depth_multiple 参数来控制网络的层数。
调整模型宽度:修改模型配置文件中的 width_multiple 参数来控制每个层的通道数(宽度)。
使用不同的主干网络:YOLOv8支持更换主干网络(如使用EfficientNet, MobileNet等),但这需要对代码和架构有较深的理解。
🌐 七、模型部署:从实验室到生产环境
训练好模型后,最终的目标通常是将其部署到实际应用中。YOLOv8/v10支持导出为多种格式,以便在不同平台上部署。
7.1 导出为ONNX格式
ONNX(Open Neural Network Exchange)是一个开放的格式,被许多推理引擎和硬件支持。
yolo task=detect mode=export model=runs/detect/my_exp/weights/best.pt format=onnx opset=12 simplify
format=onnx: 指定导出格式为ONNX。
opset=12: 指定ONNX的opset版本,通常12或13兼容性较好。
simplify: 简化ONNX图,可以减小模型大小并有时加速推理。
7.2 使用OpenCV DNN进行C++/Python部署
导出的ONNX模型可以使用OpenCV的DNN模块进行推理,这是一个非常轻量级且广泛可用的选择。
import cv2
import numpy as np
# 加载ONNX模型
net = cv2.dnn.readNetFromONNX('best.onnx')
# 设置计算后端和目标设备(可选,使用CUDA加速需要OpenCV编译时支持CUDA)
# net.setPreferableBackend(cv2.dnn.DNN_BACKEND_CUDA)
# net.setPreferableTarget(cv2.dnn.DNN_TARGET_CUDA)
def detect_objects(image_path):
# 读取图像
original_image = cv2.imread(image_path)
[height, width, _] = original_image.shape
# 预处理:调整为模型输入大小并归一化
blob = cv2.dnn.blobFromImage(original_image, scalefactor=1/255.0, size=(640, 640), swapRB=True)
# 设置输入并执行前向传播
net.setInput(blob)
outputs = net.forward()
# 后处理:解析输出结果(这是一个简化的示例,实际处理更复杂)
# 输出形状通常是 [batch, num_boxes, 85] (85 = 4(box) + 1(objectness) + 80(classes))
# 需要解析出box坐标、置信度、类别ID,并进行NMS
# 这里省略复杂的后处理代码...
# 你需要参考YOLOv8的官方代码或ONNX输出的具体格式来实现完整的后处理
# 最后,将解析出的框画在原图上
# for box in boxes:
# x1, y1, x2, y2 = map(int, box[:4])
# cv2.rectangle(original_image, (x1, y1), (x2, y2), (0, 255, 0), 2)
# cv2.imshow('Detection', original_image)
# cv2.waitKey(0)
# cv2.destroyAllWindows()
⚠️ 注意:OpenCV DNN对ONNX模型的后处理支持有限,特别是YOLO的输出格式复杂(包含解码、NMS等)。更常见的做法是使用ONNX Runtime或TensorRT等推理引擎,它们提供了更完整的支持和更好的性能。
7.3 部署到移动端或边缘设备
对于移动端(Android/iOS)或嵌入式设备(如树莓派、Jetson),通常需要进一步优化模型。
转换为TFLite格式(适用于移动端):
yolo task=detect mode=export model=best.pt format=tflite
然后可以在Android/iOS应用中使用TensorFlow Lite库进行推理。
转换为TensorRT引擎(适用于NVIDIA Jetson等GPU设备):
首先导出为ONNX,然后使用TensorRT工具将ONNX转换为TensorRT引擎(.engine文件),可以获得极致的推理速度。
# 导出为ONNX(如果还未导出)
yolo task=detect mode=export model=best.pt format=onnx opset=12 simplify
# 使用TensorRT工具(trtexec)转换ONNX为TensorRT引擎
trtexec --onnx=best.onnx --saveEngine=best.engine --fp16
然后在TensorRT的C++或Python API中加载并使用这个引擎进行推理。
💎 总结与展望
通过这篇手把手教程,我们完成了从环境配置、数据准备、模型训练、验证测试到最终部署的全过程。掌握了YOLOv8/v10的使用方法,你就已经拥有了在图像中“看到”和“理解”物体的强大能力。
回顾我们的学习路径
timeline
title YOLO项目实战学习路径
section 环境准备
创建虚拟环境 : 安装Anaconda并创建yolo_env
安装依赖包 : 安装PyTorch & Ultralytics库
验证环境 : 检查CUDA与模型加载
section 数据处理
数据标注 : 使用LabelImg标注图片
格式转换 : (可选)VOC转YOLO格式
数据划分 : 划分训练/验证/测试集
section 模型训练
训练模型 : 使用CLI或Python代码训练
监控指标 : 理解损失与mAP等关键指标
可视化结果 : 分析生成的曲线和预测图
section 评估与优化
模型评估 : 在验证集上计算mAP等指标
预测测试 : 对新图片/视频进行检测
超参调优 : 调整学习率、数据增强等参数
section 部署应用
模型导出 : 转换为ONNX/TFLite等格式
推理集成 : 使用OpenCV/ONNX Runtime等推理
最终部署 : 部署到服务器、移动端或边缘设备
持续学习的方向
目标检测是一个快速发展的领域,YOLO系列也在不断进化。你可以继续探索:
关注最新进展:YOLOv10之后,必然会有新的版本诞生,保持关注和更新。
尝试其他检测器:了解和学习其他优秀的目标检测框架,如Faster R-CNN, DETR (Detection Transformer),以及RT-DETR(Real-Time DEtection TRansformer)等,拓宽视野。
深入底层原理:阅读YOLO系列的原始论文,深入理解其网络架构设计、损失函数设计等背后的思想。
解决实际问题:将你学到的知识应用到真实世界中,构建一个有价值的物体检测应用。
希望这篇教程能够成为你YOLO之旅的坚实起点。记住,深度学习是一门“实践出真知”的学科,多动手、多尝试、多思考,你一定能玩转YOLO系列,创造出令人惊叹的物体检测应用!