从零开始实现Yolo v1
文章目录
Yolo v1网络结构
yolo v1的网络结构如下图所示

其输入为 448 × 448 × 3 448\times448\times3 448×448×3的RGB图像,输出为 7 × 7 × 30 7\times7\times30 7×7×30的预测张量,其中 7 × 7 7\times7 7×7是空间重复, 30 30 30则包含两个部分,分别是预测框的位置和类别数, 0 , 1 , 2 , 3 , 4 0,1,2,3,4 0,1,2,3,4分别是 x , y , w , h , c x,y,w,h,c x,y,w,h,c,即坐标宽高和置信度, 5 , 6 , 7 , 8 , 9 5,6,7,8,9 5,6,7,8,9是第二组的 x , y , w , h , c x,y,w,h,c x,y,w,h,c, 10.. 10.. 10..则是20种类别,如果是当前类别,则置为 1 1 1否则置 0 0 0。
总计24层卷积层,如下表所示
| 层数 | 输入 | 卷积层 | 池化 |
|---|---|---|---|
| 1 | 448 2 × 3 448^2\times3 4482×3 | 7 2 × 64 , ÷ 2 7^2\times64,\div2 72×64,÷2 | 2 × 2 , ÷ 2 2\times2,\div2 2×2,÷2 |
| 2 | 112 2 × 64 112^2\times64 1122×64 | 3 2 × 192 3^2\times192 32×192 | 2 × 2 , ÷ 2 2\times2,\div2 2×2,÷2 |
| 3 | 56 2 × 192 56^2\times192 562×192 | 1 2 × 128 1^2\times128 12×128 | |
| 4 | 56 2 × 128 56^2\times128 562×128 | 3 2 × 256 3^2\times256 32×256 | |
| 5 | 56 2 × 256 56^2\times256 562×256 | 1 2 × 256 1^2\times256 12×256 | |
| 6 | 56 2 × 256 56^2\times256 562×256 | 3 2 × 512 3^2\times512 32×512 | 2 × 2 , ÷ 2 2\times2,\div2 2×2,÷2 |
| 7,9,11,13 | 28 2 × 512 28^2\times512 282×512 | 1 2 × 256 1^2\times256 12×256 | |
| 8,10,12,14 | 28 2 × 256 28^2\times256 282×256 | 3 2 × 512 3^2\times512 32×512 | |
| 15 | 28 2 × 512 28^2\times512 282×512 | 1 2 × 512 1^2\times512 12×512 | |
| 16 | 28 2 × 512 28^2\times512 282×512 | 3 2 × 1024 3^2\times1024 32×1024 | 2 × 2 , ÷ 2 2\times2,\div2 2×2,÷2 |
| 17,19 | 14 2 × 1024 14^2\times1024 142×1024 | 1 2 × 512 1^2\times512 12×512 | |
| 18,20 | 14 2 × 512 14^2\times512 142×512 | 3 2 × 1024 3^2\times1024 32×1024 | |
| 21 | 14 2 × 1024 14^2\times1024 142×1024 | 3 2 × 1024 3^2\times1024 32×1024 | |
| 22 | 14 2 × 1024 14^2\times1024 142×1024 | 3 2 × 1024 , ÷ 2 3^2\times1024,\div2 32×1024,÷2 | |
| 23 | 7 2 × 1024 7^2\times1024 72×1024 | 3 2 × 1024 3^2\times1024 32×1024 | |
| 24 | 7 2 × 1024 7^2\times1024 72×1024 | 3 2 × 1024 3^2\times1024 32×1024 | |
| 7 2 × 1024 7^2\times1024 72×1024 | 全链接 | ||
| 4096 4096 4096 | 全链接 |
其Pytorch实现为
import torch
import torch.nn as nn
GRID = 7
VOC_CLASSES = ['aeroplane', 'bicycle', 'bird', 'boat', 'bottle', 'bus', 'car', 'cat',
'chair', 'cow', 'diningtable', 'dog', 'horse', 'motorbike', 'person',
'pottedplant', 'sheep', 'sofa', 'train', 'tvmonitor']
NUM_CLASSES = len(VOC_CLASSES)
class YOLOv1(nn.Module):
def __init__(self, num_classes=NUM_CLASSES):
super().__init__()
self.conv = nn.Sequential(
nn.Conv2d(3, 64, 7, stride=2, padding=3), nn.LeakyReLU(0.1), nn.MaxPool2d(2, stride=2),
nn.Conv2d(64, 192, 3, padding=1), nn.LeakyReLU(0.1), nn.MaxPool2d(2, stride=2),
nn.Conv2d(192, 128, 1), nn.LeakyReLU(0.1),
nn.Conv2d(128, 256, 3, padding=1), nn.LeakyReLU(0.1),
nn.Conv2d(256, 256, 1), nn.LeakyReLU(0.1),
nn.Conv2d(256, 512, 3, padding=1), nn.LeakyReLU(0.1),nn.MaxPool2d(2, stride=2),
nn.Conv2d(512, 256, 1), nn.LeakyReLU(0.1),
nn.Conv2d(256, 512, 3, padding=1), nn.LeakyReLU(0.1),
nn.Conv2d(512, 256, 1), nn.LeakyReLU(0.1),
nn.Conv2d(256, 512, 3, padding=1), nn.LeakyReLU(0.1),
nn.Conv2d(512, 256, 1), nn.LeakyReLU(0.1),
nn.Conv2d(256, 512, 3, padding=1), nn.LeakyReLU(0.1),
nn.Conv2d(512, 256, 1), nn.LeakyReLU(0.1),
nn.Conv2d(256, 512, 3, padding=1), nn.LeakyReLU(0.1),
nn.Conv2d(512, 512, 1), nn.LeakyReLU(0.1),
nn.Conv2d(512, 1024, 3, padding=1), nn.LeakyReLU(0.1), nn.MaxPool2d(2, stride=2),
nn.Conv2d(1024, 512, 1), nn.LeakyReLU(0.1),
nn.Conv2d(512, 1024, 3, padding=1), nn.LeakyReLU(0.1),
nn.Conv2d(1024, 512, 1), nn.LeakyReLU(0.1),
nn.Conv2d(512, 1024, 3, padding=1), nn.LeakyReLU(0.1),
nn.Conv2d(1024, 1024, 3, padding=1), nn.LeakyReLU(0.1),
nn.Conv2d(1024, 1024, 3, padding=1), nn.LeakyReLU(0.1)
)
self.downsample = nn.AvgPool2d(2, stride=2)
self.fc = nn.Sequential(
nn.Linear(GRID**2 * 1024, 4096), nn.LeakyReLU(0.1), nn.Dropout(0.5),
nn.Linear(4096, GRID**2 * (num_classes + 10))
)
def forward(self, x):
x = self.conv(x)
x = self.downsample(x)
x = x.view(x.size(0), -1)
out = self.fc(x).view(-1, GRID, GRID, NUM_CLASSES + 10)
return out
损失函数
Yolo v1的损失函数为
L = λ c ∑ i = 0 S 2 ∑ j = 0 B 𝟙 i j [ ( x i − x ^ i ) 2 + ( y i − y ^ i ) 2 + ( ω i − ω ^ i ) 2 + ( h i − h ^ i ) 2 ] + ∑ i = 0 S 2 ∑ j = 0 B 𝟙 i j ( C i − C ^ i ) 2 + λ n ∑ i = 0 S 2 ∑ j = 0 B 𝟘 i j ( C i − C ^ j ) 2 + ∑ i = 0 S 2 𝟙 i j ∑ c ∈ c l a s s e s [ p i ( c ) − p ^ i ( c ) ] 2 \begin{aligned} L=&\lambda_c\sum^{S^2}_{i=0}\sum^B_{j=0}𝟙_{ij}\left[(x_i-\hat x_i)^2+(y_i-\hat y_i)^2+\left(\sqrt{\omega_i}-\sqrt{\hat\omega_i}\right)^2 +\left(\sqrt{h_i}-\sqrt{\hat h_i}\right)^2\right]\\ &+\sum^{S^2}_{i=0}\sum^{B}_{j=0}𝟙_{ij}(C_i-\hat C_i)^2+\lambda_n\sum^{S^2}_{i=0}\sum^{B}_{j=0}𝟘_{ij}(C_i-\hat C_j)^2\\ &+\sum^{S^2}_{i=0}𝟙_{ij}\sum_{c\in classes}\left[p_i(c)-\hat p_i(c) \right]^2 \end{aligned} L=λci=0∑S2j=0∑B𝟙ij[(xi−x^i)2+(yi−y^i)2+(ωi−ω^i)2+(hi−h^i)2]+i=0∑S2j=0∑B𝟙ij(Ci−C^i)2+λni=0∑S2j=0∑B𝟘ij(Ci−C^j)2+i=0∑S2𝟙ijc∈classes∑[pi(c)−p^i(c)]2
式中,
- S = 7 , B = 2 S=7, B=2 S=7,B=2分别是网格划分数和每格预测框数
- 𝟙 i j 𝟙_{ij} 𝟙ij(原文中是 𝟙 i j o b j 𝟙_{ij}^{obj} 𝟙ijobj)表示第 i i i格第 j j j框负责预测物体时为1,否则为0
- 𝟘 i j 𝟘_{ij} 𝟘ij(原文中是 𝟙 i j n o o b j 𝟙_{ij}^{noobj} 𝟙ijnoobj)表示第 i i i格第 j j j框不负责预测物体时为1,否则为0
- λ c = 0.5 , λ n = 0.5 \lambda_c=0.5, \lambda_n=0.5 λc=0.5,λn=0.5(原文 λ c o o r d , λ n o o b j \lambda_{coord},\lambda_{noobj} λcoord,λnoobj)
- x ^ , y ^ , w ^ , h ^ \hat x, \hat y, \hat w, \hat h x^,y^,w^,h^ 为预测值
- C i C_i Ci为预测置信度; C ^ i \hat C_i C^i为真实置信度
- p i ( c ) , p ^ i ( c ) p_i(c), \hat p_i(c) pi(c),p^i(c)为预测类别概率和真实类别
这三个组成部分分别表示
- 坐标定位损失
- 物体置信度损失
- 类别分类损失
在具体实现时,要注意 w , h w,h w,h需要开根号,而 x , y x,y x,y不需要
sqrt = lambda x: torch.sqrt(torch.clamp(x, min=1e-6))
class YOLOLoss(nn.Module):
def __init__(self, c=5.0, n=0.5):
super().__init__()
self.c, self.n = c, n
def forward(self, pred, target, mask):
xyp1, whp1, cp1, xyp2, whp2, cp2, clsp = torch.split(
pred, [2, 2, 1, 2, 2, 1, NUM_CLASSES], dim=-1)
xyt1, wht1, ct1, xyt2, wht2, ct2, clst = torch.split(
target, [2, 2, 1, 2, 2, 1, NUM_CLASSES], dim=-1)
m1, m2 = mask[..., 0:1], mask[..., 1:2]
cell_mask = torch.clamp(m1 + m2, max=1.0)
dxy1 = (xyp1 - xyt1) ** 2
dwh1 = (sqrt(whp1) - sqrt(wht1)) ** 2
dxy2 = (xyp2 - xyt2) ** 2
dwh2 = (sqrt(whp2) - sqrt(wht2)) ** 2
coordL = self.c * torch.sum(m1*(dxy1 + dwh1) + m2*(dxy2 + dwh2))
confL = torch.sum(m1*(cp1-ct1)**2 + m2*(cp2-ct2)**2)
confL += self.n * torch.sum((1-m1)*cp1**2 + (1-m2)*cp2**2)
clsL = torch.sum(cell_mask * (clsp - clst)**2)
return (coordL + confL + clsL)/pred.size(0)
更多推荐

所有评论(0)