YOLO如何划分图像网格单元?YOLO与SSD的CNN结构及Python代码问询
YOLO 核心逻辑与CNN结构设计
YOLO 网格划分逻辑
- 输入图像统一缩放到固定尺寸(YOLOv1默认448×448),划分为S×S个等大的网格单元(YOLOv1默认S=7)
- 若某个目标的真实标注框中心坐标落在某一个网格内,该网格就唯一负责预测这个目标
- 每个网格输出固定数量的预测框、置信度得分和类别概率,推理阶段通过非极大值抑制过滤冗余结果得到最终检测框
YOLO CNN结构演进
- v1版本:基于GoogLeNet改造,包含24个卷积层提取视觉特征,最后接2个全连接层输出所有预测结果。缺点是全连接层限制输入图像尺寸,小目标检测效果较差。
- v2版本:改用Darknet19骨干网(19个卷积层+5个池化层),移除全连接层,支持多尺度训练,引入锚框机制大幅提升定位精度。
- v3及后续版本:改用Darknet53骨干网,加入残差结构解决深度网络梯度消失问题,采用多尺度特征融合输出,不同尺寸目标的检测效果均有明显提升。
SSD 核心逻辑与CNN结构设计
SSD是单阶段多尺度检测模型,兼顾检测速度与精度,结构设计特点如下:
- 骨干网络采用修改后的VGG16:将原VGG16的最后两个全连接层替换为卷积层,降低参数量,保留低层特征的细节信息。
- 额外新增6个卷积层,逐步减小特征图尺寸,形成多尺度特征金字塔。
- 每个尺度的特征图独立负责检测对应大小的目标:大尺寸特征图(对应低层特征,细节丰富)检测小目标,小尺寸特征图(对应高层特征,语义丰富)检测大目标。
- 每个特征图的每个空间位置预设多个不同宽高比的锚框,直接预测锚框的偏移量、置信度和类别,无需后续全连接层处理。
简化版Python实现(基于PyTorch)
以下仅保留核心网络结构逻辑,完整实现还需补充损失计算、锚框编解码、非极大值抑制等模块。
import torch import torch.nn as nn # 简化版YOLOv1网络结构 class SimpleYOLOv1(nn.Module): def __init__(self, grid_size=7, num_boxes=2, num_classes=20): super(SimpleYOLOv1, self).__init__() self.S = grid_size self.B = num_boxes self.C = num_classes # 特征提取卷积层 self.features = nn.Sequential( nn.Conv2d(3, 64, kernel_size=7, stride=2, padding=3), nn.LeakyReLU(0.1), nn.MaxPool2d(2, stride=2), nn.Conv2d(64, 192, kernel_size=3, padding=1), nn.LeakyReLU(0.1), nn.MaxPool2d(2, stride=2), nn.Conv2d(192, 256, kernel_size=3, padding=1), nn.LeakyReLU(0.1), nn.Conv2d(256, 512, kernel_size=3, padding=1), nn.LeakyReLU(0.1), nn.MaxPool2d(2, stride=2), nn.Conv2d(512, 1024, kernel_size=3, padding=1), nn.LeakyReLU(0.1), nn.MaxPool2d(2, stride=2) ) # 预测头 self.predict = nn.Sequential( nn.Linear(1024 * 7 * 7, 4096), nn.LeakyReLU(0.1), nn.Dropout(0.5), nn.Linear(4096, self.S * self.S * (self.B * 5 + self.C)) ) def forward(self, x): x = self.features(x) x = x.view(x.size(0), -1) x = self.predict(x) # 输出维度:(batch_size, S, S, B*5 + C) return x.view(-1, self.S, self.S, self.B * 5 + self.C)
# 简化版SSD网络结构 class SimpleSSD(nn.Module): def __init__(self, num_classes=21): super(SimpleSSD, self).__init__() self.num_classes = num_classes # VGG16基础层,输出多尺度特征 self.base_layers = nn.Sequential( # 第1个卷积块,输出尺寸:(batch, 64, 300, 300) nn.Conv2d(3, 64, kernel_size=3, padding=1), nn.ReLU(), nn.Conv2d(64, 64, kernel_size=3, padding=1), nn.ReLU(), nn.MaxPool2d(2, stride=2), # 第2个卷积块,输出尺寸:(batch, 128, 150, 150) nn.Conv2d(64, 128, kernel_size=3, padding=1), nn.ReLU(), nn.Conv2d(128, 128, kernel_size=3, padding=1), nn.ReLU(), nn.MaxPool2d(2, stride=2), # 第3个卷积块,输出尺寸:(batch, 256, 75, 75) nn.Conv2d(128, 256, kernel_size=3, padding=1), nn.ReLU(), nn.Conv2d(256, 256, kernel_size=3, padding=1), nn.ReLU(), nn.Conv2d(256, 256, kernel_size=3, padding=1), nn.ReLU(), nn.MaxPool2d(2, stride=2, ceil_mode=True), # 第4个卷积块,输出尺寸:(batch, 512, 38, 38) 用于检测小目标 nn.Conv2d(256, 512, kernel_size=3, padding=1), nn.ReLU(), nn.Conv2d(512, 512, kernel_size=3, padding=1), nn.ReLU(), nn.Conv2d(512, 512, kernel_size=3, padding=1), nn.ReLU() ) # 额外卷积层,输出更高层、更小尺寸的特征 self.extra_layers = nn.Sequential( nn.Conv2d(512, 1024, kernel_size=3, padding=6, dilation=6), nn.ReLU(), nn.Conv2d(1024, 1024, kernel_size=1), nn.ReLU(), # 输出尺寸 (batch, 1024, 19, 19) nn.Conv2d(1024, 256, kernel_size=1), nn.ReLU(), nn.Conv2d(256, 512, kernel_size=3, stride=2, padding=1), nn.ReLU(), # 输出尺寸 (batch, 512, 10, 10) nn.Conv2d(512, 128, kernel_size=1), nn.ReLU(), nn.Conv2d(128, 256, kernel_size=3, stride=2, padding=1), nn.ReLU() # 输出尺寸 (batch, 256, 5, 5) ) # 每个尺度特征对应的预测头,每个位置预设4个锚框,输出通道为4*(num_classes + 4) self.predict_heads = nn.ModuleList([ nn.Conv2d(512, 4*(num_classes + 4), kernel_size=3, padding=1), nn.Conv2d(1024, 4*(num_classes + 4), kernel_size=3, padding=1), nn.Conv2d(512, 4*(num_classes + 4), kernel_size=3, padding=1), nn.Conv2d(256, 4*(num_classes + 4), kernel_size=3, padding=1) ]) def forward(self, x): features = [] x = self.base_layers(x) features.append(x) for idx, layer in enumerate(self.extra_layers): x = layer(x) # 收集符合要求的多尺度特征 if idx == 1 or idx == 3 or idx ==5: features.append(x) outputs = [] for feat, head in zip(features, self.predict_heads): pred = head(feat) # 调整维度为 (batch, num_anchors, num_classes + 4) pred = pred.permute(0, 2, 3, 1).contiguous().view(pred.size(0), -1, self.num_classes + 4) outputs.append(pred) # 合并所有尺度的预测结果 return torch.cat(outputs, dim=1)
内容的提问来源于stack exchange,提问作者Uygar Usta
相关产品推荐
相关产品推荐

