Paper Portal: YOLOv3: una mejora incremental
Mejoras de Yolov3:
1. Usar Darknet53 como la columna vertebral
2. Predicción de características multiescala (similar a la estructura FPN)
3. Otros trucos .
La estructura de Yolov3:
La columna vertebral es la parte de extracción de características de Darknet53, donde Convolucional significa Conv+BN+LeakyReLU, y Residual significa conexión residual; la
imagen de entrada se extrae a través de la columna vertebral para extraer tres capas de capas de características , que se marcan respectivamente como característica0, característica1 y feature2 de la capa poco profunda a la capa profunda;
primero pase la función 2 a través de las capas convolucionales, emita la salida 2 a través de Convolucional + Conv en un lado, Convolucional y Upsampling en el otro lado, y luego conéctese a la función 1; las
funciones Concat generan la salida 1 a través de Convolucional + Conv en por un lado, Convolucional y Upsampling en el otro lado, y luego característica0 Conectado;
finalmente, las características de salida Concat a través de Convolucional+Conv;
entre ellas, la estructura de Capas Convolucionales es una pila Convolucional de 5 capas, y su tamaño de núcleo de convolución es [ 1 , 3 , 1 , 3 , 1 ] [1,3,1, 3,1][ 1 ,3 ,1 ,3 ,1 ] ;
Excepto por el primer Convolucional después de Concat y el primer Convolucional de entrada de imagen, entre otros Convolucionales, si el tamaño del núcleo de convolución es 3, el número de canales se duplicará; si el tamaño del núcleo de convolución es 1, el número de canales se reducirá a la mitad.
Salida de Yolov3:
La salida de la red es de tres capas.En la tarea de detección de objetivos COCO, cuando el tamaño de la imagen de entrada es (3.416.416), el resultado de salida es:
Out 0 (255,52,52), que se utiliza para predecir objetivos de tamaño pequeño
; 1 (255, 26,26), para predecir objetos de tamaño mediano
, Out 1 (255,13,13), para predecir objetos de gran tamaño .
Similar a Yolov2, donde 52x52, 26x26 y 13x13 representan las posiciones de anclaje predeterminadas; 255 = ( 4 + 1 + 80 ) ∗ 3 255=(4+1+80)*3255=( 4+1+80 )∗3 , 4 representa los parámetros de regresión objetivo, 1 representa la confianza objetivo, 80 representa la probabilidad condicional de 80 categorías y los últimos 3 representan el tamaño del ancla, es decir, hay 3 tamaños de anclas en cada posición (para cada capa de salida).
(El código solo implementa la parte de la estructura del modelo)
El intento de Yolov3:
En cuanto a la forma de predicción y la función de pérdida, el autor ha probado algunas estrategias que no han funcionado, entre las que destaca Focal Loss . El autor usa Focal Loss como la forma básica de la función de pérdida, lo que da como resultado una reducción de mAP en dos puntos, y el autor no está completamente seguro de por qué no funciona.
import torch
import torch.nn as nn
def convolutional(in_channels, out_channels, kernel_size, strid): # Conv+BN+LeakyReLU
padding = 1 if kernel_size == 3 else 0
return nn.Sequential(
nn.Conv2d(in_channels, out_channels, kernel_size, strid, padding, bias=False),
nn.BatchNorm2d(out_channels),
nn.LeakyReLU(0.1)
)
class Residual(nn.Module): # Residual结构
def __init__(self, in_channels, hidden_channels):
super(Residual, self).__init__()
self.residual_block = nn.Sequential(
convolutional(in_channels, hidden_channels, 1, 1),
convolutional(hidden_channels, in_channels, 3, 1)
)
def forward(self, x):
return x + self.residual_block(x) # x+F(x)
class Darknet53(nn.Module): # Darknet53的特征提取部分
def __init__(self):
super(Darknet53, self).__init__()
self.feature0 = nn.Sequential(
convolutional(3, 32, 3, 1),
convolutional(32, 64, 3, 2),
Residual(64, 32),
convolutional(64, 128, 3, 2),
*[Residual(128, 64) for i in range(2)],
convolutional(128, 256, 3, 2),
*[Residual(256, 128) for i in range(8)],
)
self.feature1 = nn.Sequential(
convolutional(256, 512, 3, 2),
*[Residual(512, 256) for i in range(8)],
)
self.feature2 = nn.Sequential(
convolutional(512, 1024, 3, 2),
*[Residual(1024, 512) for i in range(4)],
)
def forward(self, x):
feature0 = self.feature0(x) # 浅层特征
feature1 = self.feature1(feature0) # 中层特征
feature2 = self.feature2(feature1) # 深层特征
return feature0, feature1, feature2
class Convlayers(nn.Module): # 5个Convolutional的堆叠
def __init__(self, in_channels, hidden_channels):
super(Convlayers, self).__init__()
self.convlayers = nn.Sequential(
convolutional(in_channels, hidden_channels, 1, 1),
convolutional(hidden_channels, hidden_channels * 2, 3, 1),
convolutional(hidden_channels * 2, hidden_channels, 1, 1),
convolutional(hidden_channels, hidden_channels * 2, 3, 1),
convolutional(hidden_channels * 2, hidden_channels, 1, 1),
)
def forward(self, x):
return self.convlayers(x)
class Yolov3(nn.Module): # yolov3模型
def __init__(self):
super(Yolov3, self).__init__()
self.backbone = Darknet53()
self.convlayers2 = Convlayers(1024, 512)
self.convlayers1 = Convlayers(512 + 256, 256)
self.convlayers0 = Convlayers(256 + 128, 128)
self.final_conv2 = nn.Sequential(
convolutional(512, 1024, 3, 1),
nn.Conv2d(1024, 255, 1, 1, 0),
)
self.final_conv1 = nn.Sequential(
convolutional(256, 512, 3, 1),
nn.Conv2d(512, 255, 1, 1, 0),
)
self.final_conv0 = nn.Sequential(
convolutional(128, 256, 3, 1),
nn.Conv2d(256, 255, 1, 1, 0),
)
self.upsample2 = nn.Sequential(
convolutional(512, 256, 1, 1),
nn.Upsample(scale_factor=2)
)
self.upsample1 = nn.Sequential(
convolutional(256, 128, 1, 1),
nn.Upsample(scale_factor=2)
)
def forward(self, x):
# (B,256,52,52),(B,512,26,26),(B,1024,13,13)
feature0, feature1, feature2 = self.backbone(x) # 输入图像经过backbone提取到3层特征
f2 = self.convlayers2(feature2) # 深层特征经过Conolutional layers得到f2,(B,1024,13,13)-->(B,512,13,13)
out2 = self.final_conv2(f2) # f2经过Convolutional+Conv获得out2,(B,512,13,13)-->(B,255,13,13)
f1 = self.convlayers1( # f2经过Convolutional+Upsampling与中层特征拼接,再经过Conolutional layers得到f1
torch.cat([self.upsample2(f2), feature1], dim=1)) # (B,256,26,26)cat(B,512,26,26)-->(B,256,26,26)
out1 = self.final_conv1(f1) # f1经过Convolutional+Conv获得out1,(B,256,26,26)-->(B,255,26,26)
f0 = self.convlayers0( # f1经过Convolutional+Upsampling与浅层特征拼接,再经过Conolutional layers得到f0
torch.cat([self.upsample1(f1), feature0], dim=1)) # (B,128,52,52)cat(B,256,52,52)-->(B,128,52,52)
out0 = self.final_conv0(f0) # f0经过Convolutional+Conv获得out0,(B,128,52,52)-->(B,255,52,52)
return out0, out1, out2