4、深度学习基础回顾:卷积神经网络原理、Anchor机制、NMS非极大值抑制、IOU计算与损失函数
好,咱们进入第四讲。说实话,很多同学一上来就调YOLO的API,跑得挺欢,但一遇到模型不收敛、检测框乱飘,就彻底懵了。为啥?因为底层的这些概念没吃透。
今天这一章,我带你把这几个核心概念彻底捋一遍。卷积神经网络、Anchor、NMS、IOU、损失函数——说白了,这就是YOLO的“内功心法”。内功练好了,后面调参、改网络、甚至自己写检测器,都不在话下。
4.1 卷积神经网络(CNN)——YOLO的“眼睛”
YOLO能“看”到图像里的物体,靠的就是卷积神经网络。我个人习惯把CNN比作一个“特征提取器”。它不像全连接网络那样把每个像素都拉直,而是保留空间结构,用卷积核在图像上滑动。
4.1.1 卷积层在干什么?
说白了,卷积核就是一个小的滤波器。比如3x3的核,在图像上扫一遍,每个位置做点积。这能提取什么?边缘、纹理、角点……浅层提取低级特征,深层组合成高级语义。
关键参数:
- 卷积核大小(Kernel Size):常用3x3、5x5。我建议小核堆叠,效果比大核好,还省参数。
- 步长(Stride):每次移动的像素数。步长2相当于下采样。
- 填充(Padding):保持输出尺寸不变,常用“same”填充。
避坑指南: 我曾经在项目里为了省计算量,把步长设得太大,结果小目标直接“消失”了。后来我学乖了,小目标检测时,步长尽量用1,或者用空洞卷积。
4.1.2 池化层与激活函数
池化层,说白了就是“压缩”。最大池化取区域最大值,平均池化取平均值。它能减少参数,防止过拟合。
激活函数呢?ReLU最常用——max(0, x)。简单粗暴,解决了梯度消失问题。你想想看,如果没有激活函数,多层卷积就等价于一层线性变换,那还学个啥?
4.2 Anchor机制——YOLO的“先验知识”
Anchor是什么?我打个比方。你让一个小孩去认猫,你得先告诉他“猫大概长这样,有耳朵、有尾巴”。Anchor就是给模型预设的“框的模板”。
YOLO v2开始引入Anchor。它不再直接预测框的绝对位置,而是预测相对于Anchor的偏移量。这样模型更容易收敛。
4.2.1 Anchor怎么来的?
不是拍脑袋定的。是用K-means聚类,在训练集上统计出常见的框的宽高比。比如COCO数据集,常见的有小物体、中等物体、大物体,所以会设计9个不同尺度的Anchor。
| 特征图尺度 | 感受野 | Anchor尺寸(举例) | 适合检测 |
|---|---|---|---|
| 大(如52x52) | 小 | (10x13), (16x30), (33x23) | 小物体 |
| 中(如26x26) | 中 | (30x61), (62x45), (59x119) | 中等物体 |
| 小(如13x13) | 大 | (116x90), (156x198), (373x326) | 大物体 |
个人经验: 如果你换了一个新数据集,比如全是细长条物体(像铅笔),一定要重新聚类Anchor。我见过有人直接拿COCO的Anchor去检测交通标志,结果框全偏了。嗯,聚类这一步不能省。
4.3 IOU计算——框的“重合度”
IOU,全称Intersection over Union,交并比。说白了就是两个框重叠的程度。
公式很简单:IOU = 交集面积 / 并集面积。值越大,说明两个框越像。
4.3.1 代码实现
我直接给你看一段我常用的IOU计算代码,简洁高效:
def compute_iou(box1, box2):
# box格式: [x1, y1, x2, y2]
x1 = max(box1[0], box2[0])
y1 = max(box1[1], box2[1])
x2 = min(box1[2], box2[2])
y2 = min(box1[3], box2[3])
inter_area = max(0, x2 - x1) * max(0, y2 - y1)
box1_area = (box1[2] - box1[0]) * (box1[3] - box1[1])
box2_area = (box2[2] - box2[0]) * (box2[3] - box2[1])
union_area = box1_area + box2_area - inter_area
return inter_area / union_area if union_area > 0 else 0
注意: 这里有个坑。如果两个框完全不重叠,交集为0,IOU就是0。但有时候我们需要梯度能回传,所以后来有了GIOU、DIOU、CIOU。这些变体在IOU为0时也能提供梯度信息。我建议你在训练时直接用CIOU Loss,效果更稳。
4.4 NMS非极大值抑制——去掉“重复框”
模型预测时,同一个物体周围会冒出很多框。NMS就是把这些框“去重”,只保留最好的那个。
算法步骤很简单:
- 把所有框按置信度从高到低排序。
- 选置信度最高的框,保留它。
- 计算它与其他框的IOU,如果IOU大于阈值(比如0.5),就删掉那些框。
- 重复步骤2-3,直到没有框剩下。
你想想看,如果没有NMS,一张图里同一个行人可能被框了十几次,那画面简直没法看。
4.4.1 NMS的变体
标准NMS有个问题:如果两个物体挨得很近,IOU很大,其中一个会被误删。后来有了Soft-NMS,它不直接删框,而是降低置信度。还有DIoU-NMS,用DIoU代替IOU,对遮挡场景更友好。
避坑指南: 我曾经在密集人群检测中,用标准NMS,阈值设0.5,结果人挨着人的地方全被删了。后来换成Soft-NMS,召回率直接提升了5个点。嗯,场景不同,NMS的选择也不同。
4.5 损失函数——模型学习的“指挥棒”
损失函数告诉模型:你预测得对不对?差了多少?YOLO的损失函数一般包含三部分:
4.5.1 分类损失
判断框里是什么类别。常用交叉熵损失(Cross Entropy Loss)。公式不写了,你记住:它惩罚“预测概率分布”和“真实标签”之间的差异。
4.5.2 定位损失
判断框的位置准不准。早期用MSE(均方误差),但MSE对大框和小框的惩罚不公平。后来YOLO v5用了CIOU Loss,它同时考虑了重叠面积、中心点距离和宽高比。
4.5.3 置信度损失
判断框里有没有物体。这个损失让模型学会“这个位置有东西”还是“没东西”。
| 损失类型 | 常用函数 | 作用 |
|---|---|---|
| 分类损失 | 交叉熵 / Focal Loss | 让类别预测更准 |
| 定位损失 | CIOU / GIOU | 让框的位置更精确 |
| 置信度损失 | BCE Loss | 判断有无物体 |
个人经验: 训练初期,定位损失往往很大,模型会优先学“框在哪”。后期分类损失占主导。我习惯在训练时动态调整三个损失的权重,比如前50个epoch让定位损失权重高一些,后面再平衡。这个小技巧能让模型收敛更快。
4.6 总结
好,这一章的内容就这些。CNN是骨架,Anchor是模板,IOU是度量,NMS是后处理,损失函数是目标。五者缺一不可。
下一章,我们就要开始动手了——用OpenCV读取视频流,再结合YOLO做实时检测。到时候你会发现,今天讲的这些概念,全都会用上。嗯,做好准备。