YOLO实战:高效猫狗目标检测
引言
在计算机视觉领域,目标检测技术一直是研究热点与应用重点。相比单纯的图像分类,目标检测不仅需要判断图像中是否包含目标,还需要精确标注目标的位置信息。猫狗识别作为目标检测的经典应用场景,在宠物监控、动物保护、智能门禁等领域具有广泛应用价值。
本文将详细介绍如何使用当前流行的 YOLOv5 算法实现一个高效的猫狗识别系统,从环境搭建到模型训练,再到结果评估,全程提供可运行的代码与详细解释。即使是深度学习初学者,也能通过本文的步骤指导,成功搭建属于自己的猫狗目标检测系统。
技术栈选择
在开始实战前,我们先明确本项目所使用的技术栈:
- 深度学习框架:PyTorch 1.10+(动态图机制,便于调试,生态完善)
- 目标检测算法:YOLOv5(兼顾速度与精度,易于部署,社区活跃)
- 数据处理:OpenCV(图像处理)、Pillow(图像读写)、NumPy(数值计算)
- 可视化工具:Matplotlib(结果可视化)、TensorBoard(训练过程监控)
- 开发环境:Python 3.8+、CUDA 11.3(GPU 加速,可选但推荐)
选择 YOLOv5 的原因在于:它是目前工业界应用最广泛的目标检测算法之一,相比 Faster R-CNN 等两阶段算法,具有更快的推理速度;相比 SSD 等单阶段算法,具有更高的检测精度。同时,YOLOv5 提供了完善的预训练模型和代码库,极大降低了开发门槛。
环境搭建
基础环境配置
首先需要安装必要的 Python 库,建议使用 Anaconda 创建独立虚拟环境,避免版本冲突:
# 创建虚拟环境
conda create -n yolov5-catdog python=3.8
conda activate yolov5-catdog
# 安装PyTorch(根据自身CUDA版本选择,此处以CUDA 11.3为例)
pip install torch==1.10.1+cu113 torchvision==0.11.2+cu113 torchaudio==0.10.1 -f https://download.pytorch.org/whl/cu113/torch_stable.html
# 安装YOLOv5依赖
pip install opencv-python==4.5.5.64
pip install matplotlib==3.5.1
pip install numpy==1.21.5
pip install pandas==1.4.2
pip install tqdm==4.64.0
pip install pillow==9.0.1
pip install scipy==1.7.3
pip install tensorboard==2.9.0
YOLOv5 代码库获取
直接从 GitHub 克隆官方代码库(本文使用 5.0 版本,稳定性较好):
git clone https://github.com/ultralytics/yolov5.git -b v5.0
cd yolov5
克隆完成后,目录结构如下(核心目录说明):
yolov5/ ├── data/ # 数据集配置文件 ├── models/ # 模型配置文件 ├── utils/ # 工具函数 ├── train.py # 训练脚本 ├── detect.py # 推理脚本 ├── requirements.txt # 依赖列表 └── weights/ # 预训练权重(需自行下载)
数据集准备与处理
数据集选择
本项目使用 Kaggle 经典的 "Dogs vs. Cats" 数据集,该数据集包含 25000 张猫狗图片(训练集 20000 张,测试集 5000 张)。但原始数据集只有分类标签,没有目标检测所需的边界框标注,因此需要进行标注处理。
也可以直接使用已标注好的猫狗目标检测数据集,可通过以下方式获取:
- Kaggle 搜索 "cat dog detection dataset"
- Roboflow 平台搜索 "cat and dog"(推荐,已处理为 YOLO 格式)
本文以 Roboflow 上的 "Cat and Dog Detection" 数据集为例,该数据集包含 1700 + 张图片,每张图片都标注了猫狗的边界框信息,格式为 YOLO 所需的 txt 格式。
数据集结构
YOLO 系列算法要求的数据集结构如下:
dataset/
├── images/ # 存放所有图片
│ ├── train/ # 训练集图片
│ ├── val/ # 验证集图片
│ └── test/ # 测试集图片
└── labels/ # 存放所有标注文件(与images对应)
├── train/
├── val/
└── test/
每个标注文件(.txt)与对应图片同名,内容格式为:
<class_id> <x_center> <y_center> <width> <height>
其中:
- class_id:类别索引(本文 0 代表 cat,1 代表 dog)
- x_center, y_center:目标中心坐标(相对于图片宽度和高度的归一化值)
- width, height:目标宽高(相对于图片宽度和高度的归一化值)
数据集配置文件
在 YOLOv5 的data目录下创建catdog.yaml配置文件,内容如下:
# train and val data paths
train: ../dataset/images/train/
val: ../dataset/images/val/
test: ../dataset/images/test/
# number of classes
nc: 2
# class names
names: ['cat', 'dog']
该文件定义了训练 / 验证 / 测试集的路径、类别数量及类别名称,是 YOLOv5 训练时识别数据集的关键配置。
数据增强处理
为提高模型的泛化能力,需要对训练数据进行增强处理。YOLOv5 在data/hyp.scratch.yaml中定义了默认的数据增强参数,主要包括:
# 图片尺寸相关
imgsz: 640
# 颜色空间增强
hsv_h: 0.015 # 色相调整
hsv_s: 0.7 # 饱和度调整
hsv_v: 0.4 # 明度调整
# 几何变换增强
degrees: 0.0 # 旋转角度
translate: 0.1 # 平移比例
scale: 0.5 # 缩放比例
shear: 0.0 # 剪切角度
flipud: 0.0 # 上下翻转概率
fliplr: 0.5 # 左右翻转概率
mosaic: 1.0 # 马赛克增强概率
mixup: 0.0 # 混合增强概率
我们可以根据猫狗数据集的特点调整这些参数,例如适当增加旋转角度(degrees: 15.0)和缩放比例(scale: 0.7),因为猫狗在图像中的姿态和大小变化较大。
模型配置与预训练权重
模型选择
YOLOv5 提供了多个版本的模型配置,从 nano 到 xlarge,精度和速度依次提升:
| 模型 | 大小 (MB) | 输入尺寸 | COCO mAP@0.5 | 推理速度 (ms) |
|---|---|---|---|---|
| YOLOv5n | 1.9 | 640x640 | 28.4 | 4 |
| YOLOv5s | 7.4 | 640x640 | 37.2 | 7 |
| YOLOv5m | 24.0 | 640x640 | 45.3 | 15 |
| YOLOv5l | 46.5 | 640x640 | 48.8 | 24 |
| YOLOv5x | 86.7 | 640x640 | 50.7 | 40 |
对于猫狗识别任务,考虑到精度和速度的平衡,本文选择 YOLOv5s 模型(models/yolov5s.yaml),其配置文件核心内容如下:
# parameters
nc: 80 # 原始为80类,我们将在训练时覆盖为2类
depth_multiple: 0.33 # 模型深度因子
width_multiple: 0.50 # 模型宽度因子
# anchors
anchors:
- [10,13, 16,30, 33,23] # P3/8
- [30,61, 62,45, 59,119] # P4/16
- [116,90, 156,198, 373,326] # P5/32
# backbone
backbone:
# [from, number, module, args]
[[-1, 1, Conv, [64, 6, 2, 2]], # 0-P1/2
[-1, 1, Conv, [128, 3, 2, 1]], # 1-P2/4
[-1, 3, C3, [128]],
[-1, 1, Conv, [256, 3, 2, 1]], # 3-P3/8
[-1, 6, C3, [256]],
[-1, 1, Conv, [512, 3, 2, 1]], # 5-P4/16
[-1, 9, C3, [512]],
[-1, 1, Conv, [1024, 3, 2, 1]],# 7-P5/32
[-1, 3, C3, [1024]],
[-1, 1, SPPF, [1024, 5]], # 9
]
# head
head:
[[-1, 1, Conv, [512, 1, 1, 1]],
[-1, 1, nn.Upsample, [None, 2, 'nearest']],
[[-1, 6], 1, Concat, [1]], # cat backbone P4
[-1, 3, C3, [512, False]], # 13
[-1, 1, Conv, [256, 1, 1, 1]],
[-1, 1, nn.Upsample, [None, 2, 'nearest']],
[[-1, 4], 1, Concat, [1]], # cat backbone P3
[-1, 3, C3, [256, False]], # 17 (P3/8-small)
[-1, 1, Conv, [256, 3, 2, 1]],
[[-1, 14], 1, Concat, [1]], # cat head P4
[-1, 3, C3, [512, False]], # 20 (P4/16-medium)
[-1, 1, Conv, [512, 3, 2, 1]],
[[-1, 10], 1, Concat, [1]], # cat head P5
[-1, 3, C3, [1024, False]], # 23 (P5/32-large)
[[17, 20, 23], 1, Detect, [nc, anchors]], # Detect(P3, P4, P5)
]
预训练权重下载
使用预训练权重进行迁移学习,可以大幅提高训练效率和模型性能。YOLOv5 的预训练权重可从官方 GitHub 下载:
# 在yolov5目录下创建weights文件夹
mkdir weights
cd weights
# 下载YOLOv5s预训练权重
wget https://github.com/ultralytics/yolov5/releases/download/v5.0/yolov5s.pt
这些预训练权重是在 COCO 数据集(包含 80 类常见目标)上训练得到的,我们将利用这些权重初始化模型的前几层,只微调后几层以适应猫狗识别任务。
模型训练
训练脚本配置
YOLOv5 提供了统一的训练脚本train.py,我们需要通过命令行参数指定关键配置:
python train.py \
--img 640 \ # 输入图片尺寸
--batch 16 \ # 批次大小(根据GPU显存调整)
--epochs 100 \ # 训练轮数
--data data/catdog.yaml \ # 数据集配置文件
--cfg models/yolov5s.yaml \ # 模型配置文件
--weights weights/yolov5s.pt \ # 预训练权重
--name catdog_exp \ # 实验名称
--cache \ # 缓存数据到内存,加速训练
--device 0 # 使用的GPU编号(-1表示CPU)
关键参数说明:
--img:输入图片尺寸,YOLOv5 默认使用 640x640,可根据需求调整(如 416、800)--batch:批次大小,需根据 GPU 显存调整,1080Ti/2080Ti 可设为 16-32,V100 可设为 64--epochs:训练轮数,猫狗识别任务 100-200 轮通常足够--device:指定 GPU,多 GPU 可设为0,1
训练过程解析
训练过程主要分为以下几个阶段:
-
数据加载与预处理:
- 读取
catdog.yaml配置,加载训练集和验证集 - 应用数据增强(如旋转、翻转、色彩抖动等)
- 将图片和标注转换为模型输入格式
- 读取
-
模型初始化:
- 根据
yolov5s.yaml构建模型结构 - 加载预训练权重
yolov5s.pt - 替换输出层为 2 类(cat 和 dog)
- 根据
-
损失函数定义:
YOLOv5 的损失函数由三部分组成:- 定位损失(Bounding Box Loss):使用 CIoU Loss
- 置信度损失(Confidence Loss):使用 BCEWithLogitsLoss
- 分类损失(Classification Loss):使用 BCEWithLogitsLoss
代码实现(简化版):
class ComputeLoss:
def __init__(self, model):
self.bce = nn.BCEWithLogitsLoss(reduction='none')
self.hyp = hyp # 超参数
self.nc = model.nc # 类别数
self.anchors = model.anchors # 锚框
def __call__(self, p, targets):
# p: 模型输出 (3个尺度的预测)
# targets: 真实标签 [img_idx, class_id, x, y, w, h]
lcls = torch.zeros(1, device=targets.device) # 分类损失
lbox = torch.zeros(1, device=targets.device) # 定位损失
lobj = torch.zeros(1, device=targets.device) # 置信度损失
# 处理每个尺度的预测
for i, pi in enumerate(p):
# 匹配锚框与目标
b, a, gj, gi = self.build_targets(pi, targets, i)
tobj = torch.zeros_like(pi[..., 0], device=targets.device) # 目标置信度
# 计算定位损失 (CIoU)
box_loss = ciou(pi[b, a, gj, gi], targets[b, 2:6])
lbox += box_loss.mean()
# 计算分类损失
if self.nc > 1:
cls_loss = self.bce(pi[b, a, gj, gi, 5:5+self.nc],
targets[b, 1].long().unsqueeze(1))
lcls += cls_loss.mean()
# 计算置信度损失
tobj[b, a, gj, gi] = 1.0 # 正样本置信度设为1
lobj += self.bce(pi[..., 4], tobj).mean()
# 应用损失权重
lbox *= self.hyp['box']
lobj *= self.hyp['obj']
lcls *= self.hyp['cls']
return (lbox + lobj + lcls) * batch_size, torch.cat((lbox, lobj, lcls)).detach()
-
优化器与学习率调度:
- 优化器:默认使用 SGD,也可改为 Adam(在
train.py中修改) - 学习率调度:使用余弦退火调度,初始学习率 0.01,最终衰减到 0
- 优化器:默认使用 SGD,也可改为 Adam(在
-
训练循环:
for epoch in range(start_epoch, epochs):
model.train()
mloss = torch.zeros(4, device=device) # 总损失, box, obj, cls
# 训练一个epoch
for i, (imgs, targets, paths, _) in enumerate(train_loader):
imgs = imgs.to(device, non_blocking=True).float() / 255.0 # 归一化到[0,1]
targets = targets.to(device)
# 前向传播
with torch.cuda.amp.autocast(amp):
pred = model(imgs) # 模型预测
loss, loss_items = compute_loss(pred, targets) # 计算损失
# 反向传播
scaler.scale(loss).backward()
# 梯度裁剪,防止梯度爆炸
if opt.clip > 0.0:
scaler.unscale_(optimizer)
torch.nn.utils.clip_grad_norm_(model.parameters(), opt.clip)
# 参数更新
scaler.step(optimizer)
scaler.update()
optimizer.zero_grad()
# 记录损失
mloss = (mloss * i + loss_items) / (i + 1)
# 打印训练信息
if i % 10 == 0:
print(f'Epoch {epoch}/{epochs}, Batch {i}/{len(train_loader)}, Loss: {mloss[0].item():.4f}')
# 每轮训练后在验证集评估
if (epoch + 1) % 10 == 0:
metrics, _ = val.run(data_dict,
batch_size=batch_size,
imgsz=imgsz,
model=model,
single_cls=opt.single_cls,
dataloader=val_loader,
save_dir=save_dir,
plots=False)
print(f'Val mAP@0.5: {metrics["mAP_0.5"]:.4f}')
训练监控与调优
-
TensorBoard 监控:
YOLOv5 会自动将训练日志写入runs/train/catdog_exp目录,可通过以下命令查看:
tensorboard --logdir runs/train/catdog_exp
-
可监控的指标包括:
- 训练 / 验证损失(总损失、定位损失、置信度损失、分类损失)
- 评估指标(mAP@0.5、mAP@0.5:0.95)
- 学习率变化曲线
- 验证集预测结果可视化
-
常见问题与调优:
- 过拟合:表现为训练损失低但验证损失高
- 解决方案:增加数据增强强度、减少模型复杂度(如改用 yolov5n)、增加正则化(调整
hyp.scratch.yaml中的weight_decay)
- 解决方案:增加数据增强强度、减少模型复杂度(如改用 yolov5n)、增加正则化(调整
- 收敛缓慢:损失下降缓慢或停滞
- 解决方案:调整学习率(增大初始学习率)、检查数据标注是否正确、使用更大的预训练权重
- 类别不平衡:某一类别的 AP 远低于另一类
- 解决方案:在损失函数中增加 minority 类的权重、平衡训练集中两类的数量
- 过拟合:表现为训练损失低但验证损失高
模型评估
训练完成后,模型权重会保存在runs/train/catdog_exp/weights目录下,包括:
last.pt:最后一轮的权重best.pt:验证集性能最好的权重(推荐使用)
评估指标计算
使用 YOLOv5 提供的验证脚本评估模型性能:
python val.py \
--data data/catdog.yaml \
--weights runs/train/catdog_exp/weights/best.pt \
--img 640 \
--iou 0.5 \ # IoU阈值
--task test \ # 评估测试集
--device 0
目标检测常用评估指标:
- Precision(精确率):预测为正的样本中真正为正的比例
- Recall(召回率):所有正样本中被正确预测的比例
- mAP@0.5:在 IoU=0.5 时的平均精度均值
- mAP@0.5:0.95:在 IoU 从 0.5 到 0.95(步长 0.05)时的平均精度均值
对于猫狗识别任务,良好的模型在测试集上应达到 mAP@0.5 > 0.9。
结果可视化
使用以下代码可视化模型的检测结果:
import cv2
import torch
import matplotlib.pyplot as plt
from yolov5.utils.general import non_max_suppression, scale_coords
from yolov5.utils.torch_utils import select_device
# 加载模型
device = select_device('0')
model = torch.hub.load('ultralytics/yolov5', 'custom', path='runs/train/catdog_exp/weights/best.pt', device=device)
# 设置模型参数
model.conf = 0.5 # 置信度阈值
model.iou = 0.45 # NMS IoU阈值
model.classes = [0, 1] # 只检测猫和狗
# 读取测试图片
img_path = 'dataset/images/test/cat_123.jpg'
img = cv2.imread(img_path)
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) # 转换为RGB格式
# 模型预测
results = model(img)
# 解析预测结果
pred = results.pred[0] # 预测框
for *xyxy, conf, cls in reversed(pred):
# 绘制边界框
cv2.rectangle(img,
(int(xyxy[0]), int(xyxy[1])),
(int(xyxy[2]), int(xyxy[3])),
(0, 255, 0), 2) # 绿色框,线宽2
# 绘制类别和置信度
label = f'{model.names[int(cls)]} {conf:.2f}'
cv2.putText(img, label,
(int(xyxy[0]), int(xyxy[1])-10),
cv2.FONT_HERSHEY_SIMPLEX, 0.9, (0, 255, 0), 2)
# 显示结果
plt.figure(figsize=(10, 8))
plt.imshow(img)
plt.axis('off')
plt.show()
# 保存结果
result_img = cv2.cvtColor(img, cv2.COLOR_RGB2BGR)
cv2.imwrite('detection_result.jpg', result_img)
该代码会在图像中用绿色框标记检测到的猫或狗,并显示类别和置信度。以下是几种典型场景的检测效果分析:
- 单一目标:当图片中只有一只猫或狗时,模型通常能准确检测,置信度较高(>0.9)
- 多目标:当图片中有多只猫 / 狗时,模型能区分不同个体并分别标注
- 遮挡场景:部分遮挡的目标仍能被检测,但置信度可能下降
- 小目标:图像中占比较小的猫狗可能出现漏检,可通过提高输入尺寸(如 800x800)改善
模型部署
训练好的模型可以部署到多种平台,满足不同应用需求。
本地 Python 部署
直接使用 YOLOv5 提供的detect.py脚本进行本地推理:
python detect.py \
--source test_images/ \ # 输入图片/视频目录
--weights runs/train/catdog_exp/weights/best.pt \
--img 640 \
--conf 0.5 \
--save-txt \ # 保存标注结果
--save-conf \ # 保存置信度
--output inference/outputs/ # 输出目录
摄像头实时检测
将--source指定为摄像头编号(0 为默认摄像头),即可实现实时检测:
python detect.py \
--source 0 \
--weights runs/train/catdog_exp/weights/best.pt \
--img 640 \
--conf 0.5
轻量化部署
对于边缘设备部署,需要对模型进行轻量化处理:
-
模型导出为 ONNX 格式:
python export.py \
--weights runs/train/catdog_exp/weights/best.pt \
--include onnx \
--img 640
2. 使用 OpenCV 部署 ONNX 模型:
import cv2
import numpy as np
# 加载ONNX模型
net = cv2.dnn.readNetFromONNX('best.onnx')
net.setPreferableBackend(cv2.dnn.DNN_BACKEND_OPENCV)
net.setPreferableTarget(cv2.dnn.DNN_TARGET_CPU) # 若有GPU可改为DNN_TARGET_CUDA
# 读取图片并预处理
img = cv2.imread('test.jpg')
img_size = 640
h, w = img.shape[:2]
scale = min(img_size/h, img_size/w)
new_h, new_w = int(h*scale), int(w*scale)
img_resized = cv2.resize(img, (new_w, new_h))
img_padded = np.zeros((img_size, img_size, 3), dtype=np.uint8)
img_padded[:new_h, :new_w] = img_resized
# 模型推理
blob = cv2.dnn.blobFromImage(img_padded, 1/255.0, (img_size, img_size), swapRB=True)
net.setInput(blob)
outputs = net.forward()
# 后处理(NMS)
class_ids = []
confidences = []
boxes = []
outputs = np.squeeze(outputs)
for output in outputs:
scores = output[5:]
class_id = np.argmax(scores)
confidence = scores[class_id]
if confidence > 0.5:
# 计算边界框坐标
x_center = output[0] * img_size
y_center = output[1] * img_size
width = output[2] * img_size
height = output[3] * img_size
x1 = int(x_center - width/2)
y1 = int(y_center - height/2)
x2 = int(x_center + width/2)
y2 = int(y_center + height/2)
# 调整坐标到原始图片尺寸
x1 = int(x1 / scale)
y1 = int(y1 / scale)
x2 = int(x2 / scale)
y2 = int(y2 / scale)
class_ids.append(class_id)
confidences.append(float(confidence))
boxes.append([x1, y1, x2, y2])
# 非极大值抑制
indices = cv2.dnn.NMSBoxes(boxes, confidences, 0.5, 0.45)
for i in indices.flatten():
x1, y1, x2, y2 = boxes[i]
label = f'{"cat" if class_ids[i]==0 else "dog"} {confidences[i]:.2f}'
cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2)
cv2.putText(img, label, (x1, y1-10), cv2.FONT_HERSHEY_SIMPLEX, 0.9, (0, 255, 0), 2)
cv2.imshow('Cat/Dog Detection', img)
cv2.waitKey(0)
cv2.destroyAllWindows()
总结与展望
本文详细介绍了基于 YOLOv5 的猫狗目标检测系统的实现过程,从环境搭建、数据集准备、模型训练到评估部署,提供了完整的代码示例和理论解释。通过迁移学习和数据增强技术,我们训练的模型在测试集上达到了较高的检测精度(mAP@0.5 > 0.9),能够满足实际应用需求。
未来可以从以下几个方向进行改进:
-
模型优化:
- 尝试更先进的模型(如 YOLOv8、Faster R-CNN)
- 采用模型压缩技术(量化、剪枝)进一步提高推理速度
-
数据集扩展:
- 增加更多场景的猫狗图片(如不同光照、姿态、背景)
- 加入更细粒度的类别(如不同品种的猫 / 狗)
-
功能扩展:
- 结合跟踪算法(如 DeepSORT)实现猫狗的实时跟踪
- 增加行为识别模块(如站立、卧倒、行走等动作分类)
通过不断优化,该系统可应用于智能宠物喂食器、宠物行为分析、流浪猫狗监测等实际场景,具有较高的实用价值。
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐


所有评论(0)