前言

Hello,大家好,我是GISer Liu😁,一名热爱AI技术的GIS开发者。本系列是作者参加DataWhale 2025年6月份Yolo原理组队学习的技术笔记文档,这里整理为博客,希望能帮助Yolo的开发者少走弯路!

🚀 终于来到了 YOLOv8 实战入门教程!在目标检测领域,YOLO(You Only Look Once)系列以其高效和精准而闻名。本文旨在提供一个从零开始、保姆级的实践指南,帮助初学者快速掌握使用 ultralytics 框架和 YOLOv8 模型完成一个完整的项目。

为了避免与其他dw学习者文章同质化,作者不采用常见的猫狗或官方COCO数据集,而是从kaggle选择了一个更具挑战性也更实用的场景——城市交通车辆检测。通过这个项目,你将学到:

  1. 完整的项目流程:从环境搭建、数据集准备,到模型训练、推理和结果分析,体验AI项目开发全链路。
  2. YOLOv8核心操作:掌握 ultralytics 库的核心API,理解如何配置、训练和使用YOLO模型。
  3. 实践中的思考与泛化:学习如何将一个基础教程(如笔记中的犬种识别)的知识,迁移和应用到一个全新的项目中。

无论你是刚入门的大学生,还是希望将YOLOv8应用于自己项目的开发者,相信本教程都能为你提供坚实的起点。OK,让我们开始吧!


一、项目整体流程概览

在正式开始编码之前,我们先通过一个流程图来了解整个项目的生命周期。这有助于我们建立一个清晰的“地图”,知道自己身在何处,下一步要去哪里。

核心步骤解读

  • 环境搭建:创建独立的Python环境并安装所有必需的库,这是“善其事,利其器”的第一步。
  • 数据集准备:获取车辆检测数据集,并理解其目录结构和标注格式。
  • 数据探索:可视化部分样本和标注,确保数据质量,这是模型成功的基础。
  • 配置文件:编写data.yaml,这是告知YOLOv8训练数据在哪、类别是什么的“说明书”。
  • 模型训练:加载YOLOv8模型,并使用我们的数据对其进行“教导”。
  • 模型推理:使用训练好的模型在新的图片和视频上进行预测,检验学习成果。
  • 结果分析:查看模型的性能指标,并可视化预测结果。

二、环境搭建:构建你的炼丹炉

一个稳定、隔离的开发环境是项目顺利进行的前提。我们将使用 conda 来管理Python环境。

1. 创建并激活Conda环境

打开你的终端(Terminal),执行以下命令。这会创建一个名为 yolo-car,使用Python 3.10 的新环境。

# -- 创建conda环境 --
# (在terminal中运行,假设你已有anaconda/miniconda)
conda create -n yolo-car python=3.10 -y

# -- 激活环境 --
conda activate yolo-car

# -- 安装ipykernel,以便在Jupyter Notebook中使用此环境 --
# 记得在Notebook中选择内核为'yolo-car'
conda install ipykernel -y

2. 安装核心依赖库

环境激活后,我们需要安装PyTorch、Ultralytics等核心库。

① 安装PyTorch

PyTorch是驱动我们模型训练的深度学习框架。请访问 PyTorch官网,根据你的操作系统和GPU情况(是否有NVIDIA显卡,CUDA版本等)选择对应的安装命令。以下是一个基于CUDA 12.1的示例:

# 推荐使用conda安装,能更好地处理CUDA依赖
conda install pytorch torchvision torchaudio pytorch-cuda=12.1 -c pytorch -c nvidia -y

思考与探索 🤔:为什么推荐用conda安装PyTorch?因为conda在处理复杂的二进制依赖(如CUDA工具包)时比pip更强大,可以减少很多环境配置的麻烦。

② 安装Ultralytics及其他工具

ultralytics 是YOLOv8的官方库,封装了训练、推理等所有功能。我们同时安装其他辅助库。

# -- 安装YOLOv8官方库 --
pip install ultralytics

# -- 安装其他常用工具库 --
# numpy, pandas: 数据处理
# matplotlib, seaborn: 可视化
# opencv-python: 图像处理
# pyyaml: 用于处理YAML配置文件
pip install numpy pandas matplotlib seaborn opencv-python pyyaml --quiet

至此,我们的“炼丹炉”就搭建完毕了!

③在线云环境

作者这里使用的是colab的在线云环境,如果前面安装不太方便,可以直接在云环境中运行;

作者这里使用的是colab!🙂👌


三、数据集:模型的“食物”

本项目使用Kaggle上的 Cars Detection Dataset,其中包含了五类常见车辆的图像和YOLO格式的标注。

1. 数据集下载与观察

如果你在使用Kaggle Notebook环境,可以直接通过kagglehub下载。本地用户需要先安装kaggle库并配置好API密钥后,使用API下载或直接在网站上手动下载解压。

# --- 中文注释:导入必要的库 ---
import os
import glob as glob  # 用于查找文件路径
import matplotlib.pyplot as plt # 用于绘图
import cv2 # OpenCV库,用于图像处理
import requests # 用于网络请求
import random # 用于生成随机数
import numpy as np # 数值计算库
import shutil # 用于文件操作
import yaml # 用于读写YAML文件

from ultralytics import YOLO # 导入YOLOv8核心库

# 设置随机种子,保证实验可复现
np.random.seed(42)

# --- 中文注释:在Kaggle环境中下载并挂载数据集 ---
# 如果你在本地运行,请手动下载并解压数据集,然后将路径指向你的本地文件夹
# Kaggle Notebook 会自动处理以下代码
import kagglehub
# 下载车辆检测数据集
abdallahwagih_cars_detection_path = kagglehub.dataset_download('abdallahwagih/cars-detection')

print('数据集导入完成.')
print(f'数据集路径: {abdallahwagih_cars_detection_path}')

# 定义数据集根目录 (请根据你的实际情况修改)
# 在Kaggle中,路径通常是 '/kaggle/input/cars-detection/Cars Detection'
DATA_ROOT = os.path.join(abdallahwagih_cars_detection_path, 'Cars Detection')

在线云环境中数据会被下载到根目录下面:

2. 数据探索:眼见为实

在训练前,务必检查一下数据!看看图片是否正常,标注框是否准确。YOLO的标注格式是 [class_id, x_center, y_center, width, height],所有值都是相对于图像宽高归一化的。

我们编写几个辅助函数来可视化它们。

# --- 中文注释:定义类别名称和颜色 ---
# 必须与数据集中label的id顺序对应: 0: Ambulance, 1: Bus, ...
class_names = ['Ambulance', 'Bus', 'Car', 'Motorcycle', 'Truck']
colors = np.random.uniform(0, 255, size=(len(class_names), 3)) # 为每个类别随机生成一种颜色

# --- 中文注释:将YOLO格式的坐标转换为边角坐标 (xmin, ymin, xmax, ymax) ---
def yolo2bbox(bboxes):
    """
    将 [x_center, y_center, width, height] 格式的YOLO bbox
    转换为 [xmin, ymin, xmax, ymax] 格式
    """
    xmin = bboxes[0] - bboxes[2] / 2
    ymin = bboxes[1] - bboxes[3] / 2
    xmax = bboxes[0] + bboxes[2] / 2
    ymax = bboxes[1] + bboxes[3] / 2
    return xmin, ymin, xmax, ymax

# --- 中文注释:在图像上绘制边界框和标签 ---
def plot_box(image, bboxes, labels):
    """
    输入图像、bbox列表和标签列表,返回绘制好的图像
    """
    # 获取图像的原始高和宽,用于反归一化
    h, w, _ = image.shape
    for box_num, box in enumerate(bboxes):
        # 将YOLO格式坐标转为边角坐标
        x1, y1, x2, y2 = yolo2bbox(box)

        # 反归一化,得到在原图上的像素坐标
        xmin = int(x1 * w)
        ymin = int(y1 * h)
        xmax = int(x2 * w)
        ymax = int(y2 * h)

        # 获取当前框对应的类别名称
        class_name = class_names[int(labels[box_num])]
        # 获取该类别的颜色
        color = colors[class_names.index(class_name)]

        # 使用OpenCV绘制矩形框
        cv2.rectangle(image, (xmin, ymin), (xmax, ymax), color=color, thickness=2)

        # --- 绘制类别标签背景和文字 ---
        font_scale = 0.8
        font_thickness = 2
        text_size, _ = cv2.getTextSize(class_name, cv2.FONT_HERSHEY_SIMPLEX, font_scale, font_thickness)
        text_w, text_h = text_size
        # 绘制填充的矩形作为文字背景
        cv2.rectangle(image, (xmin, ymin - text_h - 5), (xmin + text_w, ymin - 5), color, -1)
        # 写入类别文字
        cv2.putText(image, class_name, (xmin, ymin - 5), cv2.FONT_HERSHEY_SIMPLEX, font_scale, (255, 255, 255), font_thickness)
        
    return image

# --- 中文注释:随机抽取并展示样本 ---
def plot_samples(image_dir, label_dir, num_samples=4):
    """
    从给定的目录中随机选择N个样本并进行可视化
    """
    all_image_paths = glob.glob(os.path.join(image_dir, '*.jpg'))
    all_label_paths = {os.path.basename(p).split('.')[0]: p for p in glob.glob(os.path.join(label_dir, '*.txt'))}
    
    plt.figure(figsize=(15, 12))
    
    # 随机选择 N 张图片
    selected_images = random.sample(all_image_paths, num_samples)
    
    for i, image_path in enumerate(selected_images):
        image = cv2.imread(image_path)
        
        # 找到对应的标签文件
        image_name = os.path.basename(image_path).split('.')[0]
        label_path = all_label_paths.get(image_name)
        
        bboxes = []
        labels = []
        if label_path:
            with open(label_path, 'r') as f:
                for line in f:
                    parts = line.strip().split(' ')
                    labels.append(parts[0])
                    bboxes.append([float(p) for p in parts[1:]])
        
        # 绘制标注框
        result_image = plot_box(image, bboxes, labels)
        
        plt.subplot(2, 2, i + 1)
        # Matplotlib 显示的是RGB, OpenCV读的是BGR,需要转换
        plt.imshow(cv2.cvtColor(result_image, cv2.COLOR_BGR2RGB))
        plt.axis('off')
        
    plt.tight_layout()
    plt.show()

# --- 中文注释:可视化训练集中的4个样本 ---
print("可视化训练集样本...")
plot_samples(
    image_dir=os.path.join(DATA_ROOT, 'train/images'),
    label_dir=os.path.join(DATA_ROOT, 'train/labels'),
    num_samples=4
)

思考🤔:为什么要花时间做可视化?这一步能帮你快速发现潜在问题,例如:标签错误、类别ID不匹配、标注框异常等。所谓“Garbage in, garbage out”,高质量的数据是模型性能的根本保障。

3. 创建data.yaml配置文件

这个YAML文件是连接你的数据集和YOLOv8训练引擎的桥梁。它告诉模型:

  • 训练图片和标签在哪里 (train)
  • 验证图片和标签在哪里 (val)
  • 测试图片和标签在哪里 (test)
  • 总共有哪些类别,以及它们的名称 (names)
# --- 中文注释:创建并配置 data.yaml ---

# 数据集提供的YAML文件可能路径不正确,我们需要动态修改它
source_yaml_path = os.path.join(DATA_ROOT, 'data.yaml')
# 将其复制到一个可写目录,例如当前工作目录
destination_yaml_path = './data.yaml' 
shutil.copyfile(source_yaml_path, destination_yaml_path)

print(f"已将 {source_yaml_path} 复制到 {destination_yaml_path}")

# --- 读取并修改YAML文件 ---
with open(destination_yaml_path, 'r') as f:
    data_yaml = yaml.safe_load(f)

# 更新'train'和'val'的路径为绝对路径,确保YOLO能找到它们
data_yaml['train'] = os.path.join(DATA_ROOT, 'train/images')
data_yaml['val'] = os.path.join(DATA_ROOT, 'valid/images')
data_yaml['test'] = os.path.join(DATA_ROOT, 'test/images')

# 确保 'names' 字段与我们的 class_names 变量一致
data_yaml['names'] = class_names
# 删除 'path' 字段,因为我们已经为 train/val/test 提供了绝对路径
if 'path' in data_yaml:
    del data_yaml['path']


# --- 将更新后的配置写回文件 ---
with open(destination_yaml_path, 'w') as f:
    yaml.dump(data_yaml, f)

print(f"已更新 {destination_yaml_path} 的内容:")
with open(destination_yaml_path, 'r') as f:
    print(f.read())

现在,我们有了一个指向正确数据路径的 data.yaml 文件,可以随时用于训练。

在云环境中有时候,文件是只读的,因此需要copy一份复制到可写位置后了再更新,代码如下:

import yaml
import shutil
import os

# 定义data.yaml的源路径和目标路径
source_yaml_path = '/kaggle/input/cars-detection/Cars Detection/data.yaml'
destination_yaml_path = '/content/data.yaml'

# 将data.yaml文件复制到可写位置
shutil.copyfile(source_yaml_path, destination_yaml_path)

print(f"已复制 {source_yaml_path}{destination_yaml_path}")

# 读取复制的data.yaml文件
with open(destination_yaml_path, 'r') as f:
    data_yaml = yaml.safe_load(f)

# 更新'train'和'val'路径为相对于data.yaml文件的路径
data_yaml['train'] = '/kaggle/input/cars-detection/Cars Detection/train/images'
data_yaml['val'] = '/kaggle/input/cars-detection/Cars Detection/valid/images'
# data.yaml中的'path'参数通常用作相对路径的基础目录
# 由于我们下面使用绝对路径进行训练和验证,可以将其设置为空字符串或相关基础路径(如果需要)
# 为了清晰起见,我们将其设置为数据集的基础目录(尽管下面的绝对路径并不严格需要它)
data_yaml['path'] = '/kaggle/input/cars-detection/Cars Detection'


# 将更新后的内容写回复制的data.yaml文件
with open(destination_yaml_path, 'w') as f:
    yaml.dump(data_yaml, f)

print(f"已更新 {destination_yaml_path} 中的正确路径。")

四、模型训练:释放GPU的力量

万事俱备,只欠东风!现在我们可以开始训练模型了。

# --- 中文注释:模型训练 ---

# --- 1. 加载模型 ---
# 我们从头开始构建一个YOLOv8n (nano) 模型。
# 'yolov8n.yaml' 定义了模型的结构。
# 你也可以使用预训练权重 'yolov8n.pt' 来进行微调(迁移学习),这通常会更快收敛。
model = YOLO("yolov8n.yaml") 

# --- 2. 开始训练 ---
# data: 指向我们刚刚配置好的 data.yaml 文件
# epochs: 训练轮次。对于这个数据集,100轮是一个不错的起点。
# imgsz: 训练时图像的尺寸。640是YOLOv8的常用尺寸。
# device: 使用哪个设备进行训练,'cpu'或'0' (代表第一块GPU)。
results = model.train(
    data=destination_yaml_path, 
    epochs=100, 
    imgsz=640,
    device=0, # 如果有GPU,设置为0;如果没有,设置为'cpu'
    name='yolov8n_car_detection' # 本次实验的名称,结果将保存在 runs/detect/yolov8n_car_detection 目录下
)

# --- 3. 验证模型性能 ---
# 训练完成后,会自动在验证集上进行一次评估。
# 我们也可以手动调用 model.val() 来重新评估。
# 'best.pt' 是在验证集上表现最好的模型权重。
print("在验证集上评估模型性能...")
metrics = model.val(data=destination_yaml_path)

思考 🤔:

  • 模型选择:为什么用 yolov8nn (nano) 是YOLOv8系列中最小的模型,参数最少,训练速度最快,非常适合入门和快速迭代。如果追求更高精度,可以尝试yolov8s, yolov8m等更大的模型,但需要更多的计算资源和训练时间。
  • 超参数epochs, imgsz, batch(批大小)是影响训练最重要的几个参数。epochs太少模型学不会,太多会过拟合。imgsz更大通常精度更高但更慢。batch越大训练越稳定,但越吃显存。你需要根据自己的硬件情况进行调整。

训练过程中的日志、图表(如损失曲线、PR曲线)和模型权重都会保存在runs/detect/yolov8n_car_detection目录下,这是我们分析模型表现的重要依据。


五、模型推理:让模型“看”世界

训练完成后,我们得到了一个性能不错的车辆检测模型!让我们用它来检测一些新的图像和视频。

1. 图像推理

我们从测试集中随机选择一张图片进行预测。

# --- 单张图片推理 ---
from ultralytics import YOLO
import os
import matplotlib.pyplot as plt
import matplotlib.image as mpimg

# 1. 指定模型路径(确认这个路径存在)
best_model_path = '/content/runs/detect/train8/weights/best.pt'
print(f"模型路径: {best_model_path}")

# 2. 加载模型
model = YOLO(best_model_path)

# 3. 指定测试图片(改成你实际存在的图片路径)
test_image_path = '/kaggle/input/cars-detection/Cars Detection/test/images/08c8b73e0c2e296e_jpg.rf.effa65856584463c08848031cab357b9.jpg'  # 示例路径
print(f"测试图片: {test_image_path}")

# 4. 运行预测(会自动创建输出目录)
results = model.predict(
    source=test_image_path,
    project='/content/runs/detect',  # 基础目录
    name='predict',  # 会在基础目录下创建这个子目录
    exist_ok=True,  # 允许覆盖已有目录
    save=True  # 确保保存预测结果
)

# 5. 查找最新预测结果
predict_dir = '/content/runs/detect/predict'  # 这是预测结果的实际保存位置
predicted_image_path = os.path.join(predict_dir, os.path.basename(test_image_path))

# 6. 显示结果
if os.path.exists(predicted_image_path):
    print(f"预测结果保存在: {predicted_image_path}")
    img = mpimg.imread(predicted_image_path)
    plt.figure(figsize=(10, 8))
    plt.imshow(img)
    plt.title("predict result")
    plt.axis('off')
    plt.show()
else:
    print("预测失败!可能原因:")
    print(f"1. 测试图片路径不存在: {test_image_path}")
    print(f"2. 模型路径不正确: {best_model_path}")
    print(f"3. 输出目录无写入权限: {predict_dir}")
    print("请检查以上路径是否正确!")

单张图片识别

2. 视频推理

视频推理同样简单,只需将 source 指向视频文件即可。

# --- 中文注释:视频推理 ---
# 首先,你需要准备一个测试视频。这里我们假设在 /kaggle/input/testvideo/ 目录下有一个 Input.mp4
# 请将 'path/to/your/video.mp4' 替换为你的视频真实路径
test_video_path = '/content/runs/detect/video/cars.mp4'

# 检查视频文件是否存在
if not os.path.exists(test_video_path):
    print(f"错误:视频文件 '{test_video_path}' 不存在。请修改路径或上传一个测试视频。")
else:
    # 使用CLI进行视频推理
    !yolo task=detect mode=predict model={best_model_path} source={test_video_path}

    # --- 中文注释:在Notebook中播放结果视频 ---
    # 定义一个函数,用于在Jupyter/Kaggle环境中显示视频
    from IPython.display import HTML
    from base64 import b64encode
    
    def show_video(file_name, width=640):
        mp4 = open(file_name,'rb').read()
        data_url = "data:video/mp4;base64," + b64encode(mp4).decode()
        return HTML(f"""
        <video width="{width}" controls>
            <source src="{data_url}" type="video/mp4">
        </video>""")

    # 找到最新的预测结果文件夹
    list_of_predict_dirs = glob.glob('./runs/detect/predict*')
    latest_predict_dir = max(list_of_predict_dirs, key=os.path.getctime)
    
    # 找到预测生成的视频文件(可能是.avi或.mp4)
    output_video_path = glob.glob(os.path.join(latest_predict_dir, '*.avi'))
    if not output_video_path:
        output_video_path = glob.glob(os.path.join(latest_predict_dir, '*.mp4'))

    if output_video_path:
        # 如果原始输出是avi,最好转换为mp4以获得更好的浏览器兼容性
        final_video_path = os.path.join(latest_predict_dir, 'output.mp4')
        !ffmpeg -y -loglevel panic -i {output_video_path[0]} {final_video_path}
        
        print("视频推理完成,正在显示结果...")
        display(show_video(final_video_path, width=960))
    else:
        print("未找到输出的视频文件。")


总结

本文我们从零开始,完整地走完了一个基于YOLOv8的车辆检测项目。通过这个过程,我们学习了代码的实现,更重要的是建立了解决实际问题的思维框架。

回顾一下,我们取得的成果:

掌握了项目全流程:从环境搭建到模型部署,亲手实践了AI项目的每一个关键步骤。
学会了泛化与迁移:将基础教程的知识成功应用到了一个全新的、更复杂的车辆检测场景中,这是非常有价值的能力。
理解了YOLOv8的核心用法:熟悉了ultralytics库,学会了如何准备数据、配置data.yaml、训练模型以及进行推理。

目标检测是一个很有趣的领域。希望这个实战教程能为各位读者打开一扇门,激发你探索更多可能性的热情。接下来你可以尝试去创造一个属于你自己的目标检测应用😎🎉!


今天我们就学习到这里🏆🎉👌!


文章参考


拓展阅读


💖 感谢您的耐心阅读!

如果您觉得本文对您理解和实践YOLOv8有所帮助,请考虑点赞、收藏或分享给更多有需要的朋友。您的支持是我持续创作优质内容的动力!欢迎在评论区交流讨论,共同进步。

Logo

GitCode 天启AI是一款由 GitCode 团队打造的智能助手,基于先进的LLM(大语言模型)与多智能体 Agent 技术构建,致力于为用户提供高效、智能、多模态的创作与开发支持。它不仅支持自然语言对话,还具备处理文件、生成 PPT、撰写分析报告、开发 Web 应用等多项能力,真正做到“一句话,让 Al帮你完成复杂任务”。

更多推荐