本篇博客将带你一步步从零开始,完成 YOLOv4 的环境配置、数据集准备与训练,并涵盖常见的优化和问题解决。本文将以 Darknet 框架下的 YOLOv4 实现为主,因为它是由 YOLOv4 原作者团队维护和优化的官方版本,能够提供最原汁原味的体验和性能。
我们将涵盖以下内容:
- 环境配置详解:Python、CUDA、cuDNN、GCC、Make 等前置依赖,以及 Darknet 的编译。
- 数据集准备与预处理:目标检测数据集的格式要求、常用标注工具、数据文件组织。
- 参数配置与训练流程:核心配置文件详解、迁移学习、训练命令、日志解读。
- 常见问题及解决方案:编译错误、CUDA 问题、数据路径错误、训练过程中的 NaN 等。
- 超参数调优技巧:学习率策略、Batch Size、数据增强等对训练效果的影响。
- 性能评估与可视化分析:mAP 计算、检测结果可视化。
重要提示: 训练深度学习模型,尤其是 YOLOv4 这样参数量较大的模型,通常需要一块性能较好的 NVIDIA GPU。本文的环境配置部分将重点介绍如何在 Linux 系统下配置 CUDA 环境,这是 GPU 训练的基石。
1. 环境配置详解
这是进行深度学习训练的第一步,也是最容易遇到问题的一步。耐心、仔细是关键。
操作系统:
推荐使用 Linux (如 Ubuntu 18.04 或 20.04)。虽然 Darknet 也可以在 Windows 上编译,但在 Linux 环境下配置 CUDA 和各种库更加方便和稳定。
硬件要求:
- GPU: NVIDIA GPU 是必须的,显存越大越好 (建议 8GB 或以上)。
- CPU: 性能适中即可。
- 内存: 建议 16GB 或以上。
- 硬盘空间: 需要足够的空间存储数据集、预训练权重和训练过程中生成的权重文件 (几十 GB 到几百 GB 不等)。
前置依赖:
在安装 Darknet 之前,你需要安装一些基础的开发工具和库。
- GCC/G++: C/C++ 编译器。通常 Linux 发行版自带。
- Make: 构建工具。通常 Linux 发行版自带。
- CMake: 用于构建软件的工具。 Bash
sudo apt update sudo apt install cmake
- Python: 推荐安装 Python 3.6 或更高版本。Anaconda 或 Miniconda 是管理 Python 环境的好选择。 Bash
# 使用 Miniconda 为例 wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh bash Miniconda3-latest-Linux-x86_64.sh # 按照提示安装 # 创建并激活虚拟环境 (可选但强烈推荐) conda create -n yolov4_env python=3.8 conda activate yolov4_env # 安装一些常用库 pip install numpy opencv-python matplotlib
- CUDA Toolkit: NVIDIA 提供的并行计算平台,是 GPU 加速的关键。版本选择取决于你的 GPU 型号和操作系统。务必检查你的显卡支持的最高 CUDA 版本。 可以在 NVIDIA 官网查找兼容性。
- 安装步骤: 访问 NVIDIA CUDA Toolkit Archive (CUDA Toolkit Archive | NVIDIA Developer) 下载对应版本的安装包 (选择 runfile 方式通常更灵活)。
- 重要: 安装时可以选择不安驱动,如果你已经通过其他方式安装了驱动。如果选择安装驱动,务必确认安装的驱动版本与你的显卡兼容。
- 安装完成后: 将 CUDA 的 bin 目录添加到系统环境变量
PATH
中,将 lib64 目录添加到LD_LIBRARY_PATH
中。通常在~/.bashrc
文件中添加: Bash
然后执行export PATH=/usr/local/cuda/bin:$PATH export LD_LIBRARY_PATH=/usr/local/cuda/lib64:$LD_LIBRARY_PATH
source ~/.bashrc
使其生效。 - 验证安装: 打开新的终端,运行
nvcc -V
。如果显示 CUDA 版本信息,则安装成功。
- cuDNN: NVIDIA 深度学习加速库。它依赖于 CUDA。cuDNN 版本必须与 CUDA 版本兼容。
- 下载: 访问 NVIDIA cuDNN Download (https://developer.nvidia.com/cudnn/downloads) 下载对应 CUDA 版本的 cuDNN (需要注册 NVIDIA 开发者账号)。下载
cuDNN Library for Linux
的 tar 文件。 - 安装: 解压下载的文件,将
include
目录下的头文件复制到 CUDA 的include
目录,将lib64
目录下的库文件复制到 CUDA 的lib64
目录。 Bashtar -xvf cudnn-*-archive.tar.xz sudo cp cuda/include/cudnn.h /usr/local/cuda/include/ sudo cp cuda/lib64/libcudnn* /usr/local/cuda/lib64/ sudo chmod a+r /usr/local/cuda/lib64/libcudnn*
- 验证安装: 可以运行 CUDA 提供的示例 (
deviceQuery
,bandwidthTest
) 来验证 CUDA 和驱动,cuDNN 通常没有独立的验证程序,但如果后续 Darknet 编译开启 cuDNN 成功,则说明安装正确。
- 下载: 访问 NVIDIA cuDNN Download (https://developer.nvidia.com/cudnn/downloads) 下载对应 CUDA 版本的 cuDNN (需要注册 NVIDIA 开发者账号)。下载
- OpenCV: 用于图像处理和可视化的库。Darknet 可以选择性地使用 OpenCV 来进行图像加载和结果可视化。 Bash
sudo apt install libopencv-dev
安装 Darknet:
我们将使用 AlexeyAB 维护的 Darknet 开源项目,它是目前最活跃和功能最完善的 Darknet 版本。
- 克隆仓库: Bash
git clone https://github.com/AlexeyAB/darknet.git cd darknet
- 修改 Makefile: 这是编译 Darknet 的关键步骤。根据你的需求和环境修改
Makefile
文件。使用任意文本编辑器打开Makefile
: Bash
保存并关闭# 开启 GPU 支持 (必须) GPU=1 # 开启 cuDNN 加速 (强烈推荐) CUDNN=1 # 开启 OpenCV 支持 (推荐,用于可视化) OPENCV=1 # 开启 LIBSO 支持 (可选,用于编译为动态库,方便 Python 调用) # LIBSO=1 # 根据你的 CUDA 版本设置 ARCH (Compute Capability) # 如果不确定,可以运行 nvidia-smi 查看 GPU 型号,然后到 NVIDIA 官网查询对应的 Compute Capability # 例如,对于 RTX 30系列,ARCH=86 # 如果是较旧的显卡,可能需要设置多个 ARCH,例如 ARCH=61 75 # 或者尝试不设置 ARCH,让 make 自动检测 (有时会出错) # ARCH= -gencode arch=compute_61,code=[sm_61,compute_61] \ # -gencode arch=compute_75,code=[sm_75,compute_75] # ... (找到 ARCH 相关的行,根据注释或你的显卡修改) # 如果使用了 CUDNN_HALF=1,需要 GPU 支持 FP16 (通常是 Volta 及更新架构) # CUDNN_HALF=1 # 如果使用了 OPENCV=1,确保 PKG_CONFIG_PATH 包含了你的 OpenCV 安装路径 # (如果使用 apt 安装,通常不需要额外设置)
Makefile
。 - 编译: Bash
编译过程可能需要几分钟到十几分钟,取决于你的 CPU 性能。如果一切顺利,你会看到编译成功的提示,并在make
darknet
目录下生成可执行文件darknet
。
验证安装:
- 运行
./darknet
,如果能看到 Darknet 的使用说明,说明编译成功。 - 运行一个简单的测试 (需要下载预训练权重,后面会介绍): Bash
wget https://github.com/AlexeyAB/darknet/releases/download/darknet_yolo_v3_optimal/yolov4.weights
1 ./darknet detector test cfg/coco.data cfg/yolov4.cfg yolov4.weights data/dog.jpg # 如果能看到检测结果图片 predictions.jpg,说明环境配置和 Darknet 基本功能正常 ```
2. 数据集准备与预处理
训练目标检测模型需要大量的标注数据。每个数据样本包含一张图片和其中所有目标的位置和类别信息。
数据集格式 (YOLO Darknet 格式):
Darknet 期望的标注格式是针对每张图片生成一个同名的 .txt
文件。例如,图片文件是 image.jpg
,对应的标注文件就是 image.txt
。
在 .txt
文件中,每一行代表图片中的一个目标,格式如下:
<class_id> <center_x> <center_y> <width> <height>
<class_id>
: 目标的类别 ID,从 0 开始计数。例如,如果你有 "cat" 和 "dog" 两个类别,"cat" 是 0,"dog" 是 1。<center_x>
,<center_y>
,<width>
,<height>
: 这些都是 归一化 的浮点数,范围在 [0.0, 1.0] 之间。<center_x>
: 目标框中心的 X 坐标,除以图片宽度进行归一化。<center_y>
: 目标框中心的 Y 坐标,除以图片高度进行归一化。<width>
: 目标框的宽度,除以图片宽度进行归一化。<height>
: 目标框的高度,除以图片高度进行归一化。
例子:
如果一张 640x480 的图片中有一个目标框,左上角坐标 (100, 50),右下角坐标 (300, 450),类别 ID 是 0 (猫)。
- 中心 X: (100 + 300) / 2 = 200
- 中心 Y: (50 + 450) / 2 = 250
- 宽度: 300 - 100 = 200
- 高度: 450 - 50 = 400
归一化后:
<center_x>
: 200 / 640 = 0.3125<center_y>
: 250 / 480 = 0.5208<width>
: 200 / 640 = 0.3125<height>
: 400 / 480 = 0.8333
则对应的 .txt
文件中的一行就是:
0 0.3125 0.5208 0.3125 0.8333
常用标注工具:
如果你有自己的图片需要从头标注,可以使用以下工具:
- LabelImg: (推荐) 开源的图形界面标注工具,支持多种格式输出 (包括 YOLO)。安装和使用都很方便。
- VGG Image Annotator (VIA): 基于 Web 的标注工具。
- Makesense.ai: 在线标注工具。
数据集文件组织:
通常将数据集组织成以下结构:
your_dataset/
├── images/ # 存放所有图片文件 (.jpg, .png等)
│ ├── image1.jpg
│ ├── image2.png
│ └── ...
└── labels/ # 存放所有标注文件 (.txt),与 images 下的文件一一对应且同名
├── image1.txt
├── image2.txt
└── ...
创建数据文件:
Darknet 需要几个文件来了解你的数据集:
.names
文件: 包含所有类别的名称,每行一个。例如obj.names
:
注意顺序要和你的标注中的cat dog person
<class_id>
对应。.data
文件: 告诉 Darknet 数据集的相关信息。例如obj.data
:classes = 3 # 类别数量 train = data/train.txt # 训练图片列表文件路径 valid = data/val.txt # 验证图片列表文件路径 (可选) names = data/obj.names # .names 文件路径 backup = /path/to/backup/weights/ # 训练过程中保存权重文件的目录 # eval=coco # 评估方式 (可选,通常不需要设置)
- 训练图片列表 (
train.txt
): 包含所有用于训练的图片文件的绝对路径或相对路径,每行一个。 - 验证图片列表 (
val.txt
): (可选) 包含所有用于验证的图片文件的路径,格式同train.txt
。用于在训练过程中评估模型性能。
你可以编写一个简单的 Python 脚本来生成 train.txt
和 val.txt
文件,将 images
目录下的图片路径随机划分到这两个文件中。
Python
import os
import random
image_dir = 'your_dataset/images/'
train_file = 'data/train.txt'
val_file = 'data/val.txt'
split_ratio = 0.9 # 90% 训练,10% 验证
image_list = [os.path.join(image_dir, img) for img in os.listdir(image_dir) if img.endswith(('.jpg', '.png', '.jpeg'))]
random.shuffle(image_list)
train_count = int(len(image_list) * split_ratio)
train_list = image_list[:train_count]
val_list = image_list[train_count:]
with open(train_file, 'w') as f:
for img_path in train_list:
f.write(img_path + '\n')
with open(val_file, 'w') as f:
for img_path in val_list:
f.write(img_path + '\n')
print(f"Generated {train_file} with {len(train_list)} images")
print(f"Generated {val_file} with {len(val_list)} images")
将上述 Python 脚本放在 darknet
目录下,修改 image_dir
路径,运行即可生成 data/train.txt
和 data/val.txt
(确保 darknet
目录下有 data
文件夹)。
3. 参数配置与训练流程详解
训练 YOLOv4 需要配置模型结构、训练参数以及指定数据集信息。
模型配置文件 (.cfg
):
YOLOv4 的模型结构和许多训练参数都定义在 .cfg
文件中,例如 cfg/yolov4.cfg
。你需要根据你的数据集修改这个文件。
找到文件末尾的 [yolo]
层及其前面的 [convolutional]
层。对于每个 [yolo]
层 (通常有 3 个,对应不同尺度的检测):
-
[convolutional]
层 (在[yolo]
前): 修改filters
参数。其值应为(classes + 5) * 3
。classes
: 你的数据集的类别数量。5
: 表示边界框的 (center_x, center_y, width, height) 和目标性得分 (objectness score)。3
: 表示该层对应的 Anchor 数量 (YOLOv4 在每个检测层使用 3 个 Anchor)。 例如,如果你的数据集有 3 个类别,则filters = (3 + 5) * 3 = 24
。
-
[yolo]
层: 修改classes
参数为你的类别数量。 修改anchors
参数。这一步非常重要! 默认的 anchors 是在 COCO 数据集上聚类得到的,你需要根据你的数据集重新聚类生成适合你的目标的 anchors。如何生成 Anchors: Darknet 提供了工具来根据你的数据集计算最佳的 anchors。运行以下命令:
Bash./darknet detector calc_anchors data/obj.data -cfg cfg/yolov4.cfg -num_of_clusters 9 -width 416 -height 416 # data/obj.data: 你的数据文件 # cfg/yolov4.cfg: 你的配置文件 # num_of_clusters: Anchor 数量 (通常 YOLOv4 使用 9 个) # width/height: 你训练时将使用的输入图片尺寸 (比如 416x416)
该命令会输出计算得到的 9 个 anchors (9个数值对)。将这些数值复制粘贴到
cfg/yolov4.cfg
中所有[yolo]
层的anchors
参数后面,并删除原有的 anchors。确保格式正确,例如:anchors = 10,13, 16,30, 33,23, 30,61, 62,45, 59,119, 116,90, 156,198, 373,326
(注意逗号和空格)。
其他重要参数 (在文件顶部):
batch
: 训练时使用的图片数量。越大越稳定,但越耗显存。subdivisions
: 将一个 batch 分割成多少份送入 GPU。实际送入 GPU 的图片数量是batch / subdivisions
。这是为了在显存不足时模拟大 batch size。例如batch=64
,subdivisions=16
,则每次送入 4 张图片。width
,height
: 网络输入的图片尺寸。通常是 32 的倍数,例如 416x416, 608x608。更大的尺寸通常精度更高,但速度更慢且需要更多显存。learning_rate
: 初始学习率。policy
: 学习率衰减策略 (如 steps, polynomial, exponential)。steps
,scales
: 与policy=steps
配合使用,定义在哪些迭代次数 (steps
) 将学习率乘以对应的因子 (scales
) 进行衰减。max_batches
: 总共训练的迭代次数。建议设置为classes * 2000
,但不小于 6000。burn_in
: 在前burn_in
迭代内,学习率会从一个很小的值逐渐增加到初始学习率。mosaic
,cutmix
: 数据增强技术,默认开启 (1)。hue
,saturation
,exposure
: 颜色抖动参数。random
: 是否使用随机尺寸输入进行训练 (每 10 个迭代随机改变一次输入尺寸,范围从width/32*32
到width
,步长为 32)。默认开启 (1)。这有助于提高模型的鲁棒性。
迁移学习 (Transfer Learning):
从预训练权重开始训练可以大大缩短训练时间并提高性能,尤其是在数据集较小的情况下。
- 下载预训练权重: 下载在大型数据集 (如 COCO 或 ImageNet) 上预训练的 YOLOv4 权重。
- 在 ImageNet 上预训练的权重 (只包含 backbone):
yolov4.conv.137
(约 160MB) Bashwget https://github.com/AlexeyAB/darknet/releases/download/darknet_yolo_v3_optimal/yolov4.conv.137
- 在 COCO 上预训练的完整权重:
yolov4.weights
(约 245MB) Bashwget https://github.com/AlexeyAB/darknet/releases/download/darknet_yolo_v4_pre/yolov4.weights
yolov4.conv.137
从头开始在你的数据集上训练,因为yolov4.weights
是在 80 个类别的 COCO 上训练的,直接在少量类别的数据集上微调可能会遇到类别不匹配的问题 (虽然 Darknet 也能处理,但从 conv 权重开始更规范)。 - 在 ImageNet 上预训练的权重 (只包含 backbone):
开始训练:
使用 Darknet 的 detector train
命令开始训练。
Bash
./darknet detector train data/obj.data cfg/yolov4.cfg yolov4.conv.137 -map
# data/obj.data: 你的数据文件路径
# cfg/yolov4.cfg: 你的配置文件路径
# yolov4.conv.137: 用于迁移学习的预训练权重文件路径 (如果是从头训练,可以省略或使用一个较小的 backbone 权重如 darknet53.conv.74)
# -map: (可选) 在训练过程中定期计算验证集上的 mAP (Mean Average Precision),用于监控训练效果。这会稍微降低训练速度。
训练开始后,你会看到类似以下的输出:
...
Loading weights from yolov4.conv.137...Done!
Learning Rate: 0.00261
Batch Size: 64, Subdivisions: 16
...
Epoch Iteration Region Avg IOU Class Avg IOU Object Avg IOU Total Avg IOU Recall Precision Avg Loss Rate
1 100 0.7580 0.9032 0.7890 0.8167 0.88 0.91 0.543210 0.001000
1 200 0.7801 0.9115 0.8103 0.8340 0.90 0.93 0.487654 0.002610
...
Saving weights to /path/to/backup/weights/yolov4_xxxx.weights
- Iteration: 当前的训练迭代次数。
- Region Avg IOU: 预测框与真实框的平均 IoU。
- Class Avg IOU: 预测框中正确分类的目标的平均 IoU。
- Object Avg IOU: 预测框中包含目标的平均 IoU。
- Total Avg IOU: 整体平均 IoU。
- Recall: 召回率。
- Precision: 精确率。
- Avg Loss: 平均损失值。这是监控训练是否收敛的重要指标,应随着训练进行逐渐下降。
- Rate: 当前的学习率。
Darknet 会定期在 backup
目录 (在 .data
文件中指定) 下保存权重文件 (.weights
),通常是每 100 次迭代保存一次 .weights
文件,以及每 1000 次迭代保存一次完整的 .weights
文件。
中断与恢复训练:
如果你需要中断训练 (例如关机或显存不足),可以直接关闭终端。下次训练时,使用以下命令从保存的最新权重文件继续:
Bash
./darknet detector train data/obj.data cfg/yolov4.cfg /path/to/backup/weights/yolov4_last.weights -map
# 将 yolov4_last.weights 替换为你想要恢复的权重文件路径
Darknet 会查找指定权重文件同目录下的 _last.weights
文件作为最新的保存点。
4. 常见问题及解决方案
在 YOLOv4 训练过程中,可能会遇到各种问题。以下是一些常见问题及其解决方法:
- 编译错误 (尤其是 CUDA 相关):
- 问题:
undefined reference to 'cudaFree'
或其他 CUDA 函数。 - 原因: CUDA Toolkit 或驱动安装不正确,或者 Makefile 中的 CUDA 路径配置错误。
- 解决方案: 重新安装 CUDA Toolkit,确保
PATH
和LD_LIBRARY_PATH
包含正确的 CUDA 路径。检查 Makefile 中的GPU
和CUDNN
是否开启,以及ARCH
是否与你的显卡兼容。 - 问题:
fatal error: cudnn.h: No such file or directory
- 原因: cuDNN 没有正确安装或复制到 CUDA 目录。
- 解决方案: 检查 cuDNN 文件是否正确复制到
/usr/local/cuda/include
和/usr/local/cuda/lib64
。确保文件权限正确 (chmod a+r ...
)。 - 问题:
undefined reference to 'cv::'
或其他 OpenCV 函数。 - 原因: OpenCV 没有正确安装,或者 Makefile 中的
OPENCV
没有开启。 - 解决方案: 安装
libopencv-dev
,确保OPENCV=1
在 Makefile 中。
- 问题:
- CUDA/cuDNN 版本不兼容:
- 问题: 运行时报 CUDA 或 cuDNN 相关错误,提示版本不匹配。
- 原因: 安装的 CUDA Toolkit、cuDNN 和 NVIDIA 驱动之间版本不兼容。
- 解决方案: 仔细查阅 NVIDIA 官方文档,确认你的 GPU、驱动、CUDA 和 cuDNN 之间的兼容性,然后安装对应的版本。通常建议先安装驱动,再安装兼容的 CUDA 和 cuDNN。
- 数据集路径错误:
- 问题:
Couldn't open file data/obj.data
或Cannot load image data/train.txt
中的图片。 - 原因:
.data
文件中指定的路径错误,或者train.txt
/val.txt
中指定的图片路径错误。 - 解决方案: 仔细检查
obj.data
文件中的train
,valid
,names
,backup
路径是否正确,以及train.txt
/val.txt
文件中的图片路径是否准确。使用绝对路径可以避免相对路径带来的问题。
- 问题:
- 配置文件错误 (
.cfg
):- 问题:
Error in cfg file...
或训练开始后立即崩溃。 - 原因:
.cfg
文件中有语法错误,或者修改filters
,classes
,anchors
时计算或复制错误。 - 解决方案: 仔细检查你修改的
[convolutional]
和[yolo]
层中的filters
,classes
,anchors
参数是否正确。filters
必须是(classes + 5) * 3
。确保anchors
格式正确。
- 问题:
- 训练过程中出现 NaN Loss:
- 问题: 训练输出的
Avg Loss
突然变成nan
。 - 原因: 学习率过高、数据异常 (标注错误、图片损坏) 或模型不稳定。
- 解决方案:
- 降低学习率: 初始学习率可能过高,尝试减小一点。
- 检查数据集: 检查训练集中是否有标注错误、空白标注文件、损坏的图片或尺寸极小的图片。
- 减小 Batch Size: 减小
batch
和subdivisions
。 - 从上一个稳定权重恢复: 从出现 NaN 之前的最后一个正常权重文件恢复训练。
- 关闭或调整数据增强: 有时过于激进的数据增强可能导致问题。
- 问题: 训练输出的
- 显存不足 (Out of Memory):
- 问题: 训练过程中报 CUDA 内存错误。
- 原因: Batch size 或输入图片尺寸过大,超过了 GPU 显存容量。
- 解决方案:
- 减小
batch
和/或 增大subdivisions
: 实际送入 GPU 的图片数量是batch / subdivisions
,减小这个值可以降低显存占用。 - 减小输入图片尺寸: 修改
.cfg
文件中的width
和height
。 - 开启
CUDNN_HALF=1
: 如果你的 GPU 支持 FP16,开启这个选项可以减少显存使用 (需要修改 Makefile 并重新编译)。
- 减小
5. 超参数调优技巧
超参数的选择对模型的训练效果至关重要。调优是一个需要经验和耐心的过程。
以下是一些关键的超参数和调优技巧:
- 学习率 (
learning_rate
,policy
,steps
,scales
):- 学习率是影响训练收敛速度和最终性能的最重要参数之一。
- 通常从一个较小的初始学习率开始 (
0.001
或0.00261
是常见的起始值)。 - 使用学习率衰减策略 (
policy=steps
) 是必要的,可以在训练后期减小学习率,帮助模型更稳定地收敛到最优解。steps
和scales
需要根据max_batches
合理设置。例如,在总迭代次数的 80% 和 90% 进行两次衰减,每次衰减因子为 0.1 (scales=0.1,0.1
)。 - 如果训练初期 loss 下降缓慢,可以尝试增大初始学习率。如果 loss 震荡或出现 NaN,说明学习率可能过高,需要减小。
- Batch Size (
batch
,subdivisions
):- Batch size 越大,梯度估计越准确,训练通常越稳定,也更容易收敛到更好的局部最优解。但受限于显存。
- 尽量使用你能负担的最大 Batch size。如果显存不足,可以通过增大
subdivisions
来模拟大 Batch size。
- 输入图片尺寸 (
width
,height
):- 更大的输入尺寸通常能提高对小目标的检测能力,从而提高精度。
- 缺点是需要更多显存,推理速度也会变慢。
- 可以先使用较小的尺寸 (如 416x416) 进行快速实验,确定其他参数后,再尝试更大的尺寸 (如 608x608) 进行最终训练。
random=1
(随机尺寸训练) 是一个很有效的正则化手段,能提高模型对不同尺寸目标的鲁棒性。
- Anchors:
- Anchors 的选择对模型的收敛速度和性能有很大影响。务必根据你的数据集重新聚类生成。
- 数据增强 (
mosaic
,cutmix
,hue
,saturation
,exposure
等):- 数据增强是提高模型泛化能力的重要手段。Darknet 内置了多种数据增强方法。
mosaic
和cutmix
是很有效的增强技术,通常建议开启。- 颜色抖动参数 (
hue
,saturation
,exposure
) 可以适度调整,模拟不同的光照条件。 - 如果数据集较小,可以考虑使用更强的数据增强。如果数据集很大,可以适度减弱。
- 优化器参数:
- Darknet 使用 SGD 优化器。
.cfg
文件中的momentum
(动量) 和decay
(权重衰减) 是重要的优化器参数。默认值通常工作良好,一般不需要大幅调整。decay
用于防止过拟合,较大的decay
会惩罚大的权重。
- Darknet 使用 SGD 优化器。
- 训练迭代次数 (
max_batches
):- 训练到 loss 收敛并且验证集性能不再提升即可停止。设置过大的
max_batches
只会浪费计算资源,甚至可能导致过拟合。 - 监控训练日志中的
Avg Loss
和-map
选项输出的 mAP 来判断何时停止。
- 训练到 loss 收敛并且验证集性能不再提升即可停止。设置过大的
调优策略:
- 从基础开始: 使用推荐的默认参数和在 ImageNet 上预训练的
yolov4.conv.137
开始训练。 - 监控指标: 密切关注训练日志中的
Avg Loss
和验证集上的 mAP。 - 一次调整少量参数: 避免一次修改太多超参数,否则很难判断哪个修改起作用了。
- 学习率优先: 通常先从学习率开始调优。
- Batch Size 限制: 在显存允许范围内选择最大的 Batch Size 或通过
subdivisions
模拟。 - Anchor 聚类: 务必为你的数据集重新计算 anchors。
- 数据增强: 根据数据集大小和特性调整数据增强参数。
- 耐心和实验: 超参数调优是一个实验过程,需要耐心尝试不同的组合。
6. 性能评估与可视化分析
训练完成后,你需要评估模型的性能并可视化检测结果。
性能评估 (mAP):
mAP (Mean Average Precision) 是目标检测中最常用的评估指标,它综合考虑了模型的准确率和召回率。Darknet 可以方便地计算 mAP。
修改 obj.data
文件,确保 valid
路径指向你的验证集图片列表文件。
运行以下命令计算 mAP:
Bash
./darknet detector map data/obj.data cfg/yolov4.cfg /path/to/your_trained_weights.weights
# data/obj.data: 你的数据文件
# cfg/yolov4.cfg: 你的配置文件
# /path/to/your_trained_weights.weights: 你训练好的权重文件路径 (通常选择验证集上 mAP 最高或 loss 最低的权重)
输出会显示不同 IoU 阈值下的 Average Precision (AP) 以及它们的平均值 (mAP)。例如:
...
Calculating mAP (mean average precision)...
For class_id = 0 name = cat: AP@0.50 = 0.856789
For class_id = 1 name = dog: AP@0.50 = 0.923456
For class_id = 2 name = person: AP@0.50 = 0.891234
avg mAP@0.50 = 0.890493
...
AP@0.50
: 在 IoU 阈值为 0.5 时的平均精确率。avg mAP@0.50
: 所有类别在 IoU 阈值为 0.5 时的平均 mAP。
通常,avg mAP@0.50
是最重要的评估指标。一些比赛或研究也会关注更高 IoU 阈值下的 mAP (如 mAP@0.75 或 mAP@0.5:0.95,表示在 IoU 从 0.5 到 0.95 以 0.05 为步长的多个阈值下的平均 mAP)。
可视化分析:
将模型应用到新的图片或视频上,直观地查看检测结果。
检测单张图片:
Bash
./darknet detector test data/obj.data cfg/yolov4.cfg /path/to/your_trained_weights.weights /path/to/your_image.jpg
# data/obj.data: 你的数据文件
# cfg/yolov4.cfg: 你的配置文件
# /path/to/your_trained_weights.weights: 训练好的权重文件路径
# /path/to/your_image.jpg: 要检测的图片路径
# -thresh 0.5: (可选) 设置置信度阈值,只显示置信度高于该值的检测框 (默认通常是 0.25)
检测结果会保存在 predictions.jpg
文件中。
检测视频:
Bash
./darknet detector demo data/obj.data cfg/yolov4.cfg /path/to/your_trained_weights.weights /path/to/your_video.mp4 -thresh 0.5 -out_filename results.avi
# ... (前面参数同上)
# /path/to/your_video.mp4: 要检测的视频文件路径
# -out_filename results.avi: (可选) 将检测结果保存为视频文件
这会在窗口中实时显示检测结果。
绘制 loss/mAP 曲线:
Darknet 的训练日志包含了每次迭代的 loss 和 mAP 信息。你可以编写一个 Python 脚本来解析日志文件,并使用 Matplotlib 等库绘制 loss 曲线和 mAP 曲线,以便更直观地分析训练过程。
Python
import matplotlib.pyplot as plt
def parse_log(log_file):
iterations = []
losses = []
ious = []
maps = [] # 如果使用了 -map 选项
with open(log_file, 'r') as f:
for line in f:
if 'Iteration' in line and 'Avg Loss' in line:
parts = line.split()
iterations.append(int(parts[1]))
losses.append(float(parts[-2]))
ious.append(float(parts[5])) # Region Avg IOU
if 'avg mAP@0.50' in line: # 如果使用了 -map 选项
parts = line.split()
# 找到 avg mAP 值,注意日志格式可能略有不同
try:
map_val = float(parts[-1])
maps.append((iterations[-1], map_val)) # 记录迭代次数和mAP
except (ValueError, IndexError):
pass # 处理解析错误
return iterations, losses, ious, maps
# 修改为你的日志文件路径
log_file = 'path/to/your_darknet_train.log'
iterations, losses, ious, maps = parse_log(log_file)
# 绘制 Loss 和 IOU 曲线
plt.figure(figsize=(12, 5))
plt.subplot(1, 2, 1)
plt.plot(iterations, losses)
plt.xlabel('Iteration')
plt.ylabel('Average Loss')
plt.title('Training Loss Curve')
plt.subplot(1, 2, 2)
plt.plot(iterations, ious)
plt.xlabel('Iteration')
plt.ylabel('Region Avg IOU')
plt.title('Training Region Avg IOU Curve')
plt.tight_layout()
plt.show()
# 绘制 mAP 曲线 (如果解析到 mAP 数据)
if maps:
map_iterations, map_values = zip(*maps)
plt.figure(figsize=(6, 5))
plt.plot(map_iterations, map_values)
plt.xlabel('Iteration')
plt.ylabel('mAP@0.50')
plt.title('Validation mAP Curve')
plt.show()
将上述脚本保存为 .py
文件,修改 log_file
路径并运行,即可看到训练过程的曲线图。
结论
通过本篇博客,你应该已经了解了 YOLOv4 在 Darknet 框架下的环境配置、数据集准备、模型训练、问题解决、超参数调优以及性能评估的全流程。从实际动手的角度出发,我们详细讲解了每一个步骤,并提供了相应的命令和代码示例。
YOLOv4 是一个强大的目标检测模型,掌握其训练过程是进行目标检测项目的基础。希望这篇博客能够帮助你成功地在自己的数据集上训练出高性能的 YOLOv4 模型。
记住,深度学习模型的训练是一个不断尝试和优化的过程。耐心、细致地检查每一步,并结合训练日志进行分析,你一定能取得好结果。
下一步:
- 尝试在更大的数据集或更复杂的场景下训练 YOLOv4。
- 探索 YOLOv4 的其他变种 (如 YOLOv4-tiny) 或更新的模型 (如 YOLOv5, YOLOv7, YOLOv8)。
- 将训练好的模型部署到实际应用中 (如使用 Darknet 的 C++ API 或将其转换为其他框架格式如 ONNX)。