深度学习-物体检测-YOLO系列

0 阅读16分钟

代码手把手教你玩转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系列,创造出令人惊叹的物体检测应用!