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=0S2j=0B𝟙ij[(xix^i)2+(yiy^i)2+(ωi ω^i )2+(hi h^i )2]+i=0S2j=0B𝟙ij(CiC^i)2+λni=0S2j=0B𝟘ij(CiC^j)2+i=0S2𝟙ijcclasses[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)
Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐