Interpretación detallada de YOLOV5

Explicación detallada del algoritmo de detección YOLOV5

prefacio del estudio

Este artículo explica principalmente en detalle el algoritmo de detección de objetivos basado en el aprendizaje profundo, tomando YOLOV5 como ejemplo;

El proceso general de detección de objetivos basado en el aprendizaje profundo.

La detección de objetos basada en el aprendizaje profundo incluye principalmente dos partes: capacitación y pruebas.
inserte la descripción de la imagen aquí
inserte la descripción de la imagen aquí

fase de entrenamiento

El propósito del entrenamiento es utilizar el conjunto de datos de entrenamiento para aprender los parámetros de la red de detección, donde el conjunto de datos de entrenamiento contiene una gran cantidad de imágenes visuales e información de etiquetas (posición y
categoría del objeto). El proceso principal de la fase de capacitación incluye el preprocesamiento de datos, la red de detección, la coincidencia de etiquetas y el cálculo de pérdidas.

1. Preprocesamiento de datos

El propósito del preprocesamiento de datos es mejorar la diversidad de los datos de entrenamiento, mejorando así la capacidad de detección de la red de detección.
Los métodos de preprocesamiento adoptados por YOLOV5 incluyen principalmente: voltear, hacer zoom, girar, transformación de la gama de colores y
voltear mosaico:

image = image.transpose(Image.FLIP_LEFT_RIGHT)  #利用PIL库对图片直接翻转
box[:, [0,2]] = iw - box[:, [2,0]] #翻转图片后要对目标框同时进行调整

Zoom:

#由于实际图像w和h不是相等的,所以采用不失真的resize,将长边resize到和输入尺寸一样大小,然后其余部分
放上灰条

scale = min(w/iw, h/ih) #iw、ih是数据集中图像实际尺寸,w,h为网络输入的图像尺寸,scale为图像缩放因子
nw = int(iw*scale) #图像宽缩放后尺寸
nh = int(ih*scale) #图像长缩放后尺寸
dx = (w-nw)//2  #缩放后图像放在灰度图像上的位置
dy = (h-nh)//2 #缩放后图像放在灰度图像上的位置
image   = image.resize((nw,nh), Image.BICUBIC)  #将输入图像插值到实际缩放后的尺寸大小
new_image   = Image.new('RGB', (w,h), (128,128,128)) #生成一个三通的,大小为(w,h)的灰度图像
new_image.paste(image, (dx, dy)) #将缩放后的实际图像放在灰度图像 (dx, dy)的位置上
image_data  = np.array(new_image, np.float32) #再转换成数组格式

distorsión:

new_ar = iw/ih * self.rand(1-jitter,1+jitter) / self.rand(1-jitter,1+jitter) #iw、ih是数据集中图像实际尺寸,jitter扭曲因子
scale = self.rand(.25, 2)
if new_ar < 1:
   nh = int(scale*h)
   nw = int(nh*new_ar)
else:
   nw = int(scale*w)
   nh = int(nw/new_ar)
   image = image.resize((nw,nh), Image.BICUBIC)

Transformación de la gama de colores:

r  = np.random.uniform(-1, 1, 3) * [hue, sat, val] + 1
hue, sat, val   = cv2.split(cv2.cvtColor(image_data, cv2.COLOR_RGB2HSV)) #j将图片转换成HSV格式,再把每个通道分离开来
dtype  = image_data.dtype
x  = np.arange(0, 256, dtype=r.dtype)
lut_hue = ((x * r[0]) % 180).astype(dtype)
lut_sat = np.clip(x * r[1], 0, 255).astype(dtype)
lut_val = np.clip(x * r[2], 0, 255).astype(dtype)
image_data = cv2.merge((cv2.LUT(hue, lut_hue), cv2.LUT(sat, lut_sat), cv2.LUT(val, lut_val))) image_data = cv2.cvtColor(image_data, cv2.COLOR_HSV2RGB)

Mosaico:

train_annotation_path = '1.txt'
with open(train_annotation_path, encoding='utf-8') as f:
    train_lines = f.readlines()
jitter = 0.3
h, w = [640,640]
min_offset_x = rand(0.3, 0.7)
min_offset_y = rand(0.3, 0.7)
image_datas = []
box_datas   = []
index       = 0
lines = sample(train_lines, 3)
lines.append(train_lines[index])
shuffle(lines)  #从训练集中随机取4张图片进行拼接
for line in lines:
    line_content = line.split()
    image = Image.open(line_content[0])
    image = cvtColor(image)
    iw, ih = image.size
    box = np.array([np.array(list(map(int,box.split(',')))) for box in line_content[1:]])
    new_ar = iw / ih * rand(1 - jitter, 1 + jitter) / rand(1 - jitter, 1 + jitter)
    scale = rand(.4, 1)
    if new_ar < 1:
        nh = int(scale * h)
        nw = int(nh * new_ar)
    else:
        nw = int(scale * w)
        nh = int(nw / new_ar)
    image = image.resize((nw, nh), Image.BICUBIC)
    if index == 0:  #分别计算出四张图片分别摆放的位置
        dx = int(w * min_offset_x) - nw
        dy = int(h * min_offset_y) - nh
    elif index == 1:
        dx = int(w * min_offset_x) - nw
        dy = int(h * min_offset_y)
    elif index == 2:
        dx = int(w * min_offset_x)
        dy = int(h * min_offset_y)
    elif index == 3:
        dx = int(w * min_offset_x)
        dy = int(h * min_offset_y) - nh
    new_image = Image.new('RGB', (w, h), (128, 128, 128))
    new_image.paste(image, (dx, dy))
    image_data = np.array(new_image)

    index = index + 1
    box_data = []
    if len(box) > 0:  #对box重新进行处理,超出边界,都要将其限制在图像里面
        np.random.shuffle(box)
        box[:, [0, 2]] = box[:, [0, 2]] * nw / iw + dx
        box[:, [1, 3]] = box[:, [1, 3]] * nh / ih + dy
        box[:, 0:2][box[:, 0:2] < 0] = 0
        box[:, 2][box[:, 2] > w] = w
        box[:, 3][box[:, 3] > h] = h
        box_w = box[:, 2] - box[:, 0]
        box_h = box[:, 3] - box[:, 1]
        box = box[np.logical_and(box_w > 1, box_h > 1)]
        box_data = np.zeros((len(box), 5))
        box_data[:len(box)] = box

    image_datas.append(image_data)
    box_datas.append(box_data)
cutx = int(w * min_offset_x)
cuty = int(h * min_offset_y)
new_image = np.zeros([h, w, 3])
new_image[:cuty, :cutx, :] = image_datas[0][:cuty, :cutx, :]
new_image[cuty:, :cutx, :] = image_datas[1][cuty:, :cutx, :]
new_image[cuty:, cutx:, :] = image_datas[2][cuty:, cutx:, :]
new_image[:cuty, cutx:, :] = image_datas[3][:cuty, cutx:, :] 
new_image       = np.array(new_image, np.uint8)
merge_bbox = []
for i in range(len(bboxes)): #在四张拼接图上面对框进行调整,防止其超出界限
    for box in bboxes[i]:
          tmp_box = []
          x1, y1, x2, y2 = box[0], box[1], box[2], box[3]

          if i == 0:
             if y1 > cuty or x1 > cutx:
                  continue
             if y2 >= cuty and y1 <= cuty:
                        y2 = cuty
             if x2 >= cutx and x1 <= cutx:
                        x2 = cutx

           if i == 1:
                    if y2 < cuty or x1 > cutx:
                        continue
                    if y2 >= cuty and y1 <= cuty:
                        y1 = cuty
                    if x2 >= cutx and x1 <= cutx:
                        x2 = cutx

            if i == 2:
                    if y2 < cuty or x2 < cutx:
                        continue
                    if y2 >= cuty and y1 <= cuty:
                        y1 = cuty
                    if x2 >= cutx and x1 <= cutx:
                        x1 = cutx

             if i == 3:
                    if y1 > cuty or x2 < cutx:
                        continue
                    if y2 >= cuty and y1 <= cuty:
                        y2 = cuty
                    if x2 >= cutx and x1 <= cutx:
                        x1 = cutx
             tmp_box.append(x1)
             tmp_box.append(y1)
             tmp_box.append(x2)
             tmp_box.append(y2)
             tmp_box.append(box[-1])
             merge_bbox.append(tmp_box)

2. Red de detección

La red de detección generalmente incluye una red troncal de extracción de características, una red de fusión de características y una red de predicción.

Red troncal de extracción de características
YOLOV5 utiliza CSPDarknet como red de extracción de características, y la estructura se muestra en la figura:

(1) La estructura de enfoque
es en realidad la operación de índice de corte de la matriz, y los valores se toman cada dos píxeles en las direcciones w y h de cada canal, y finalmente la imagen de un canal se convierte en imágenes de cuatro canales, y finalmente 3 canales se convierten en 12 canales, y la información del espacio de la imagen se convierte a la dimensión del canal,
inserte la descripción de la imagen aquí

class Focus(nn.Module):
    def __init__(self, c1, c2, k=1, s=1, p=None, g=1, act=True):  # ch_in, ch_out, kernel, stride, padding, groups
        super(Focus, self).__init__()
        self.conv = Conv(c1 * 4, c2, k, s, p, g, act)

    def forward(self, x):
        return self.conv(torch.cat([x[..., ::2, ::2], x[..., 1::2, ::2], x[..., ::2, 1::2], x[..., 1::2, 1::2]], 1))

(2) La estructura Csplayer
toma prestada de la estructura CSPnet. De hecho, la estructura residual está anidada en la estructura residual: self.cv2 es un borde residual grande y self.m es una estructura residual anidada.

class C3(nn.Module):
    # CSP Bottleneck with 3 convolutions
    def __init__(self, c1, c2, n=1, shortcut=True, g=1, e=0.5):  # ch_in, ch_out, number, shortcut, groups, expansion
        super(C3, self).__init__()
        c_ = int(c2 * e)  # hidden channels
        self.cv1 = Conv(c1, c_, 1, 1)
        self.cv2 = Conv(c1, c_, 1, 1)
        self.cv3 = Conv(2 * c_, c2, 1)  # act=FReLU(c2)
        self.m = nn.Sequential(*[Bottleneck(c_, c_, shortcut, g, e=1.0) for _ in range(n)])

    def forward(self, x):
        return self.cv3(torch.cat((self.m(self.cv1(x)), self.cv2(x)), dim=1))

class Bottleneck(nn.Module):
    # Standard bottleneck
    def __init__(self, c1, c2, shortcut=True, g=1, e=0.5):  # ch_in, ch_out, shortcut, groups, expansion
        super(Bottleneck, self).__init__()
        c_ = int(c2 * e)  # hidden channels
        self.cv1 = Conv(c1, c_, 1, 1)
        self.cv2 = Conv(c_, c2, 3, 1, g=g)
        self.add = shortcut and c1 == c2

    def forward(self, x):
        return x + self.cv2(self.cv1(x)) if self.add else self.cv2(self.cv1(x))

(3) Estructura SPP
La extracción de características se realiza mediante agrupación máxima con diferentes tamaños de núcleo de agrupación para mejorar el campo receptivo de la red.

class SPP(nn.Module):
    # Spatial pyramid pooling layer used in YOLOv3-SPP
    def __init__(self, c1, c2, k=(5, 9, 13)):
        super(SPP, self).__init__()
        c_ = c1 // 2  # hidden channels
        self.cv1 = Conv(c1, c_, 1, 1)
        self.cv2 = Conv(c_ * (len(k) + 1), c2, 1, 1)
        self.m = nn.ModuleList([nn.MaxPool2d(kernel_size=x, stride=1, padding=x // 2) for x in k])

    def forward(self, x):
        x = self.cv1(x)
        return self.cv2(torch.cat([x] + [m(x) for m in self.m], 1))

inserte la descripción de la imagen aquí

Red de agregación de funciones
YOLOV5 utiliza la estructura de FPN + PAN para la agregación de características y agrega las capas de características extraídas de las últimas tres partes de la red troncal. Las características superficiales se empalman con las características profundas mediante muestreo descendente, y las características profundas también se muestrearán ascendentemente. para combinar con las características superficiales. Para el empalme, al mismo tiempo, se adoptará la estructura CSP2 diseñada en referencia a CSPnet para fortalecer la capacidad de fusión de características de la red.

red de predicción
YOLOV5 usa una capa convolucional para obtener el resultado final

yolo_head = nn.Conv2d(c1, len(anchors_mask[2]) * (5 + num_classes), 1) #最后的输出通道数为3个特征层上的预测结果,包括预测框四个参数、置信度、,所有类别的概率

3. Asignación de etiquetas y cálculo de pérdidas.

La asignación de etiquetas tiene como objetivo principal proporcionar valores reales para la predicción del detector. En la detección de objetivos, los criterios para la asignación de etiquetas incluyen criterios de intersección, criterios de distancia, criterios de estimación de probabilidad y coincidencias binarias. Luego, según los resultados de la clasificación de etiquetas, la función de pérdida se utiliza para calcular la pérdida de tareas como la clasificación y la regresión, y el peso de la red de detección se actualiza mediante el algoritmo de retropropagación. Las funciones de pérdida de clasificación comúnmente utilizadas incluyen la pérdida de entropía cruzada función, función de pérdida de enfoque y función de pérdida suave L1. ', función de pérdida IOU de intersección y relación, función de pérdida GIOU


La selección de muestras positivas durante el entrenamiento de YOLOV5 se divide en dos pasos: encontrar el marco de mayor prioridad, hacer coincidir puntos característicos;
Encuentra el cuadro de mayor prioridad
YOLOV5 establece 9 fotogramas a priori en tres capas de entidades. Utilice estos 9 fotogramas a priori y GT para calcular la relación de aspecto. Divida el ancho y el alto del fotograma a priori por el ancho y alto de GT, y divida el ancho y alto de GT por el primer ancho y alto del marco de inspección,toma el mayor de los dos, y luego compare estos 9 ratios con los umbrales establecidos de antemano,Si es menor que el umbral, significa que el tamaño del marco anterior está cerca del marco real y puede usarse como muestra positiva para el entrenamiento.

Coincidir puntos característicos
Seleccionamos el cuadro de mayor prioridad en el paso anterior y determinamos el tamaño, pero aún no hemos determinado la posición del cuadro.Entonces calculamos en qué cuadrícula cae el cuadro real, luego el punto característico en la esquina superior izquierda de la cuadrícula es el punto característico responsable de la predicción. Al mismo tiempo, para aumentar el número de muestras positivas, encuentre los dos más cercano al punto central de la cuadrícula del marco real, por lo que un cuadro real corresponderá a tres puntos característicos, y el tamaño del cuadro anterior en cada punto característico está determinado por el paso anterior.

Cálculo de pérdidas
La pérdida consta de tres partes: pérdida de regresión, pérdida de confianza y pérdida de clasificación;
pérdida de regresión: Utilice los parámetros de ajuste obtenidos por la red para corregir el cuadro anterior obtenido previamente para obtener el cuadro predicho, y use el cuadro real y el cuadro predicho para calcular la pérdida de IOU;

pérdida de confianza: Calcule la pérdida de entropía cruzada según si los puntos característicos y las muestras positivas y negativas contienen objetos;

pérdida de clasificación: Calcule la pérdida de entropía cruzada según el tipo de cuadro real y el tipo de resultado predicho;

fase de prueba

Ingrese la imagen de prueba en la red de detección entrenada para obtener el resultado de la predicción y luego realice operaciones de posprocesamiento, como decodificación y supresión de valores no máximos, y finalmente identifique la categoría y la información de ubicación del objeto en la imagen;

NMS
La supresión de valores no máximos es en realidad lo mismo que el principio de clasificación de burbujas, excepto que nms primero extrae el marco de predicción con la mayor confianza en una determinada categoría y luego realiza el cálculo del pagaré en él y en otros marcos de predicción restantes. la superposición es grande, el cuadro de predicción se eliminará. Si la superposición es pequeña, el cuadro se generará al mismo tiempo; todos los cuadros de predicción con una gran superposición se eliminarán;

Me remito al código y blog de Bubbliiiiing, muchas gracias

Supongo que te gusta

Origin blog.csdn.net/qq_50027359/article/details/127861763
Recomendado
Clasificación