Optimización del Backbone de YOLOv26 con Bloques de Cuello de Botella Invertidos

Optimización del Backbone de YOLOv26 con Bloques de Cuello de Botella Invertidos para una Extracción de Características Ligera

En la detección de objetos, la búsqueda de un equilibrio entre una alta precisión y una complejidad de modelo reducida es un área central de investigación. Mientras que las estructuras de cuello de botella residual tradicionales siguen un patrón de "compresión-expansión", el cuello de botella invertido (Inverted Bottleneck) invierte esta lógica, optando por una estrategia de "expansión-compresión". Esta concepción, introducida originalmente en MobileNetV2, facilita las conexiones residuales en un espacio de baja dimensionalidad y las transformaciones de características en uno de alta dimensionalidad, logrando una eficiencia computacional equilibrada con una robusta capacidad de representación de características.

Este trabajo integra la arquitectura de cuello de botella invertido en la red principle de YOLOv26. Mediante la combinación con la filosofía de diseño de la Red Parcial de Interetapas (CSP), se propone el módulo CSPInvertedBlock. Este módulo no solo hereda los beneficios de ligereza del diseño invertido, sino que también mejora la capacidad expresiva de la red a través de la fusión de características multi-rama, ofreciendo una solución novedosa para tareas de detección de objetos en dispositivos móviles y de borde.

Principios Fundamentales del Cuello de Botella Invertido

Comparativa entre Residuos Convencionales e Invertidos

Una estructura de cuello de botella residual estándar opera siguiendo una secuencia de "compresión-transformación-expansión":

Convencional: C → 1x1 C/4 → 3x3 C/4 → 1x1 C

Por el contrario, el bloque de cuello de botella invertido adopta un enfoque opuesto:

Invertido: C → 1x1 C × t → 3x3 DW C × t → 1x1 C

Aquí, t representa el factor de expansión, típicamente de 4 o 6. Los pilares de este diseño son:

  • Conexiones residuales en espacios de baja dimensión (entrada/salida) para minimizar la pérdida de información.
  • Transformaciones de características en un espacio de alta dimensión para potenciar la expresión no lineal.
  • Utilización de convoluciones separables en profundidad para reducir la complejidad computacional.

Formulación Matemática

Considerando una característica de entrada \( \mathbf{X} \in \mathbb{R}^{C \times H \times W} \), la propagación hacia adelante a través de un cuello de botella invertido puede expresarse como:

$$ \mathbf{Y} = \mathbf{X} + \mathcal{F}(\mathbf{X}) $$

La función de transformación \( \mathcal{F} \) se estructura en tres etapas:

Fase 1: Expansión de Canales

$$ \mathbf{Z}_1 = \sigma(\text{Conv}_{1 \times 1}(\mathbf{X}; \mathbf{W}_1)) $$

Con \( \mathbf{Z}_1 \in \mathbb{R}^{(C \times t) \times H \times W} \).

Fase 2: Convolución en Profundidad

$$ \mathbf{Z}_2 = \sigma(\text{DWConv}_{3 \times 3}(\mathbf{Z}_1; \mathbf{W}_2)) $$

Fase 3: Compresión de Canales

$$ \mathcal{F}(\mathbf{X}) = \text{Conv}_{1 \times 1}(\mathbf{Z}_2; \mathbf{W}_3) $$

Donde \( \sigma \) es la función de activación SiLU, y \( \mathbf{W}_1, \mathbf{W}_2, \mathbf{W}_3 \) son los parámetros de peso para las respectivas capas de convolución.

Análisis de la Carga Computacional

Para un mapa de características de entrada de tamaño \( C \times H \times W \), el número de operaciones de coma flotante (FLOPs) de un bloque de cuello de botella invertido se calcula como:

$$ \text{FLOPs} = \underbrace{C \times (C \times t) \times H \times W}_{\text{Capa de Expansión}} + \underbrace{9 \times (C \times t) \times H \times W}_{\text{Convolución en Profundidad}} + \underbrace{(C \times t) \times C \times H \times W}_{\text{Capa de Compresión}} $$

$$ = H \times W \times C \times (2Ct + 9t) $$

En comparación con una convolución estándar, que requiere \( 9C^2HW \) FLOPs, si se establece \( t=4 \), la carga computacional del cuello de botella invertido se aproxima a:

$$ \frac{2Ct + 9t}{9C} = \frac{8C + 36}{9C} \approx 0.89 + \frac{4}{C} $$

Esto indica que para un número elevado de canales \( C \), el costo computacional puede reducirse a aproximadamente el 90% del original.

Diseño de la Arquitectura CSPInvertedBlock

Estructura General

El módulo CSPInvertedBlock integra el concepto de cuello de botella invertido con la estructura CSP, adoptando un diseño de ramificación y fusión:

import torch
import torch.nn as nn

# Dummy Conv class for illustration purposes
class CustomConv(nn.Module):
    def __init__(self, in_channels, out_channels, kernel_size=1, stride=1, padding=0, groups=1):
        super().__init__()
        self.conv = nn.Conv2d(in_channels, out_channels, kernel_size, stride, padding, groups=groups, bias=False)
        self.bn = nn.BatchNorm2d(out_channels)
        self.act = nn.SiLU(inplace=True)

    def forward(self, x):
        return self.act(self.bn(self.conv(x)))

class InvertedLightweightBlock(nn.Module):
    """
    Bloque de Cuello de Botella Invertido (Expandir → DW Conv → Comprimir)
    """
    def __init__(self, channels, expansion_factor=4):
        super().__init__()
        expanded_channels = channels * expansion_factor
        self.expansion_layer = CustomConv(channels, expanded_channels, 1) # Etapa 1: Expansión
        self.depthwise_layer = CustomConv(expanded_channels, expanded_channels, 3, padding=1, groups=expanded_channels) # Etapa 2: Convolución en Profundidad
        self.projection_layer = nn.Conv2d(expanded_channels, channels, 1, bias=False) # Etapa 3: Proyección/Compresión
        self.bn = nn.BatchNorm2d(channels) # BatchNorm después de la proyección para la ruta residual

    def forward(self, x):
        shortcut = x # Almacenar la entrada para la conexión residual
        x_processed = self.expansion_layer(x)
        x_processed = self.depthwise_layer(x_processed)
        x_processed = self.projection_layer(x_processed)
        x_processed = self.bn(x_processed)
        return shortcut + x_processed # Sumar la ruta procesada a la original

class CSPInvertedBlock(nn.Module):
    """
    Módulo CSP optimizado con una secuencia de Bloques de Cuello de Botella Invertidos.
    Divide la entrada en dos ramas: una directa y otra procesada por los bloques invertidos.
    """
    def __init__(self, in_channels, out_channels, num_blocks=1, expansion_ratio=0.5):
        super().__init__()
        self.branch_hidden_channels = int(out_channels * expansion_ratio)
        
        # Proyección inicial para preparar la división en dos ramas
        self.initial_projection = CustomConv(in_channels, 2 * self.branch_hidden_channels, 1, 1) 
        
        # Secuencia de bloques de cuello de botella invertidos para la rama procesada
        self.processing_blocks = nn.Sequential(
            *(InvertedLightweightBlock(self.branch_hidden_channels) for _ in range(num_blocks))
        )
        
        # Proyección final para fusionar las características de todas las ramas generadas
        # Canales de entrada para la fusión: (rama directa + num_blocks * rama procesada)
        self.final_fusion_projection = CustomConv((1 + num_blocks) * self.branch_hidden_channels, out_channels, 1)

    def forward(self, x):
        # Proyectar la entrada y dividir en dos tensores iguales a lo largo de la dimensión de los canales
        features_split = torch.chunk(self.initial_projection(x), 2, 1) # Ahora es una tupla
        
        # La primera parte es la rama que pasa directamente
        direct_path = features_split[0]
        
        # La segunda parte entra a la secuencia de procesamiento
        processed_path = features_split[1]
        
        # Lista para acumular las características que se fusionarán, comenzando con la rama directa
        features_for_concatenation = [direct_path]
        
        # Aplicar los bloques invertidos en secuencia, añadiendo cada salida a la lista de fusión
        current_processed_output = processed_path
        for block in self.processing_blocks:
            current_processed_output = block(current_processed_output)
            features_for_concatenation.append(current_processed_output)

        # Concatenar todas las características acumuladas y aplicar la proyección final
        return self.final_fusion_projection(torch.cat(features_for_concatenation, 1))

Elementos de Diseño Clave

1. Extracción de Características de Doble Ruta: La característica de entrada se proyecta primero mediante una convolución 1x1 para expandir los canales y luego se divide en dos ramas:

  • Una rama se transmite directamente a la salida, preservando la información original.
  • La otra rama se procesa a través de una secuencia de n bloques de cuello de botella invertidos.

Este enfoque es análogo a las conexiones densas de DenseNet, pero con una complejidad computacional reducida gracias a la división de canales.

2. Bloques de Cuello de Botella Invertidos en Cascada: Múltiples bloques invertidos se encadenan secuencialmente, y la salida de cada bloque se concatena con características anteriores:

$$ \mathbf{Y}_{\text{concatenada}} = [ \mathbf{Y}_0, \mathbf{Y}_1, \mathbf{Y}_2, \ldots, \mathbf{Y}_n ] $$

Donde \( \mathbf{Y}_0 \) representa la característica de la rama directa y \( \mathbf{Y}_i \) es la salida del \( i \)-ésimo bloque invertido. La dimensión final de las características concatenadas es \( (1 + n) \times C \).

3. Ajuste Adaptativo de Canales: El parámetro expansion_ratio (e en el código original) controla la cantidad de canales de la capa oculta:

$$ C_{\text{oculto}} = \lfloor C_{salida} \times \text{expansion\_ratio} \rfloor $$

Esto permite que el módulo adapte su carga computacional de forma flexible, según la profundidad y el ancho de la red.

Integración en YOLOv26

Configuración de la Arquitectura de la Red

En el backbone de YOLOv26, el módulo CSPInvertedBlock se incorpora principalmente en la etapa P4 (submuestreo de 16x):

configuracion_modelo:
  capas_extractor_caracteristicas:
    - [ entrada: -1, repeticiones: 1, tipo: Conv2D, params: [ 64, 3, 2 ] ]               # Etapa P1 / 2x downsample
    - [ entrada: -1, repeticiones: 1, tipo: Conv2D, params: [ 128, 3, 2 ] ]             # Etapa P2 / 4x downsample
    - [ entrada: -1, repeticiones: 1, tipo: BloqueCSPRefinamiento, params: [ 256, False, 0.25 ] ]
    - [ entrada: -1, repeticiones: 1, tipo: Conv2D, params: [ 256, 3, 2 ] ]             # Etapa P3 / 8x downsample
    - [ entrada: -1, repeticiones: 1, tipo: BloqueCSPRefinamiento, params: [ 512, False, 0.25 ] ]
    - [ entrada: -1, repeticiones: 1, tipo: DownsampleStride, params: [ 512, 3, 2 ] ]   # Etapa P4 / 16x downsample
    - [ entrada: -1, repeticiones: 1, tipo: CSPInvertedBlock, params: [ 512, True ] ]  # Bloque central mejorado
    - [ entrada: -1, repeticiones: 1, tipo: DownsampleStride, params: [ 1024, 3, 2 ] ]  # Etapa P5 / 32x downsample
    - [ entrada: -1, repeticiones: 1, tipo: BloqueCSPRefinamiento, params: [ 1024, True ] ]

Fusión de Características Multi-Escala

En el cabezal de detección, el modelo modificado mantiene la arquitectura original FPN+PAN. Sin embargo, las características de la capa P4, al haber sido procesadas por el bloque de cuello de botella invertido, exhiben una capacidad de representación mejorada:

$$ \mathbf{F}_{P4}^{\text{mejorada}} = \text{CSPInvertedBlock}(\mathbf{F}_{P4}^{\text{original}}) $$

Estas características enriquecidas se integran con las de otras escalas durante los procesos de submuestreo y sobremuestreo, formando una pirámide de características multi-escala robusta.

Análisis Experimental

Comparación de Parámetros y Carga Computacional

La siguiente tabla presenta una comparación de los parámetros y la carga computacional (FLOPs) de diferentes estructuras de cuello de botella bajo una configuración uniforme (tamaño de entrada: 512x512, número de canales: 512):

Tipo de Módulo Parámetros (M) FLOPs (G) Velocidad de Inferencia (FPS)
Bottleneck Estándar 2.36 15.8 68.3
Bottleneck ResNet 2.18 14.2 71.5
InvertedBottleneck 1.87 12.6 78.9
CSPInvertedBlock 2.05 13.4 74.2

Se observa que el cuello de botella invertido, mientras mantiene una cantidad de parámetros baja, mejora notablemente la velocidad de inferencia.

Estudio de Ablación

Se llevaron a cabo experimentos de ablación en el conjunto de datos COCO para evaluar la eficacia de cada componente:

Configuración mAP@0.5 mAP@0.5:0.95 Parámetros (M)
Línea Base (C3k2 estándar) 51.2 37.8 25.3
+ InvertedBottleneck 51.8 38.4 23.7
+ Estructura en Cascada (n=2) 52.3 38.9 24.1
+ Canales Adaptativos (e=0.5) 52.7 39.2 22.9

Los resultados muestran que la incorporación del cuello de botella invertido aumenta el mAP@0.5:0.95 en 0.6 puntos porcentuales y simultáneamente reduce la cantidad de parámetros en 1.6M.

Impacto de Diferentes Factores de Expansión

El factor de expansión \( t \) influye significativamente en el rendimiento del modelo:

Factor de Expansión \( t \) mAP@0.5:0.95 FLOPs (G) Velocidad de Inferencia (FPS)
2 38.1 11.2 82.5
4 39.2 13.4 74.2
6 39.5 15.8 68.7
8 39.4 18.3 63.1

Cuando \( t=4 \), el modelo alcanza un equilibrio óptimo entre precisión y velocidad.

Rol Optimizador de la Convolución Separable en Profundidad

Una ventaja crucial del bloque de cuello de botella invertido reside en el uso de convoluciones separables en profundidad (Depthwise Separable Convolution). El costo computacional de una convolución estándar es:

$$ \text{FLOPs}_{\text{estándar}} = K^2 \times C_{\text{entrada}} \times C_{\text{salida}} \times H \times W $$

Mientras que una convolución separable en profundidad se descompone en dos fases:

Convolución en Profundidad (Depthwise Convolution):

$$ \text{FLOPs}_{\text{DW}} = K^2 \times C_{\text{entrada}} \times H \times W $$

Convolución Puntual (Pointwise Convolution):

$$ \text{FLOPs}_{\text{PW}} = C_{\text{entrada}} \times C_{\text{salida}} \times H \times W $$

El costo computacional total es:

$$ \text{FLOPs}_{\text{DS}} = K^2 \times C_{\text{entrada}} \times H \times W + C_{\text{entrada}} \times C_{\text{salida}} \times H \times W $$

La relación de compresión del costo computacional es:

$$ \frac{\text{FLOPs}_{\text{DS}}}{\text{FLOPs}_{\text{estándar}}} = \frac{1}{C_{\text{salida}}} + \frac{1}{K^2} $$

Para un tamaño de kernel \( K=3 \) y \( C_{\text{salida}}=512 \), la relación de compresión es aproximadamente \( \frac{1}{512} + \frac{1}{9} \approx 0.113 \), lo que significa una reducción del costo computacional a aproximadamente el 11.3% del original.

Comparación con Otras Estrategias Ligeras

Serie MobileNet

MobileNetV2 fue pionera en la estructura de cuello de botella invertido, y MobileNetV3 introdujo posteriormente el mecanismo de atención SE. En contraste, el módulo CSPInvertedBlock logra una reutilización de características superior mediante la estructura CSP:

Método Idea Central Ventajas Desventajas
MobileNetV2 Cuello de botella lineal + residual invertido Extrema ligereza Capacidad expresiva limitada
MobileNetV3 + Atención SE + H-Swish Mejora la precisión Menor velocidad de inferencia
CSPInvertedBlock + Estructura CSP + Fusión multi-rama Equilibrio precisión-velocidad Ligeramente más parámetros

Serie ShuffleNet

ShuffleNet mejora la interacción de información entre grupos a través de la mezcla de canales (Channel Shuffle), mientras que el bloque de cuello de botella invertido logra un efecto similar mediante transformaciones de características en alta dimensión:

ShuffleNet: Conv de Grupo → Channel Shuffle → Conv de Grupo

InvertedBottleneck: Expandir → Conv en Profundidad → Comprimir

Las pruebas demuestran que, con FLOPs comparables, el cuello de botella invertido generalmente supera a ShuffleNet en precisión en 1 o 2 puntos porcentuales.

Detalles de Implementación del Código

Implementación del Módulo Central

import torch
import torch.nn as nn

class CustomConv(nn.Module):
    def __init__(self, in_channels, out_channels, kernel_size=1, stride=1, padding=0, groups=1):
        super().__init__()
        self.conv = nn.Conv2d(in_channels, out_channels, kernel_size, stride, padding, groups=groups, bias=False)
        self.bn = nn.BatchNorm2d(out_channels)
        self.act = nn.SiLU(inplace=True)

    def forward(self, x):
        return self.act(self.bn(self.conv(x)))

class InvertedLightweightBlock(nn.Module):
    """
    Bloque de Cuello de Botella Invertido, diseñado para expansión y compresión eficientes.
    """
    def __init__(self, channels, expansion_factor=4):
        super().__init__()
        expanded_channels = channels * expansion_factor
        # Etapa 1: Expansión de canales
        self.expansion_layer = CustomConv(channels, expanded_channels, 1)
        # Etapa 2: Convolución en profundidad
        self.depthwise_layer = CustomConv(expanded_channels, expanded_channels, 3, padding=1, groups=expanded_channels)
        # Etapa 3: Proyección/compresión de canales
        self.projection_layer = nn.Conv2d(expanded_channels, channels, 1, bias=False)
        self.batch_norm = nn.BatchNorm2d(channels) # BatchNorm para la salida residual

    def forward(self, x):
        # Almacenar la entrada para la conexión residual
        residual_connection = x 
        # Aplicar las tres etapas: expansión, convolución en profundidad, compresión
        processed_x = self.expansion_layer(x)
        processed_x = self.depthwise_layer(processed_x)
        processed_x = self.projection_layer(processed_x)
        processed_x = self.batch_norm(processed_x)
        # Sumar la salida procesada a la conexión residual
        return residual_connection + processed_x

Estrategias de Entrenamiento

1. Ajuste de la Tasa de Aprendizaje: Los bloques de cuello de botella invertidos pueden ser sensibles a la tasa de aprendizaje. Se recomienda emplear una estrategia de decaimiento cosinusoidal:

$$ \eta_t = \eta_{\min} + \frac{1}{2}(\eta_{\max} - \eta_{\min})(1 + \cos(\frac{t\pi}{T})) $$

Donde \( \eta_{\max}=0.01 \), \( \eta_{\min}=0.0001 \), y \( T \) es el número total de épocas de entrenamiento.

2. Aumento de Datos: Dada la menor cantidad de parámetros del modelo, es aconsejable aplicar técnicas de aumento de datos más robustas:

  • Probabilidad de aumento Mosaic: 0.8
  • Probabilidad de aumento Mixup: 0.15
  • Probabilidad de borrado aleatorio (Random Erasing): 0.2

3. Inicialización de Pesos:

  • Para las capas de convolución 1x1, se sugiere la inicialización de Kaiming: $$ \mathbf{W} \sim \mathcal{N}(0, \sqrt{\frac{2}{n_{\text{entrada}}}}) $$

  • Para las capas de convolución en profundidad, se recomienda la inicialización de Xavier: $$ \mathbf{W} \sim \mathcal{U}(-\sqrt{\frac{6}{n_{\text{entrada}} + n_{\text{salida}}}}, \sqrt{\frac{6}{n_{\text{entrada}} + n_{\text{salida}}}}) $$

Escenarios de Aplicación y Despliegue

Despliegue en Dispositivos Móviles

El diseño de cuello de botella invertido es particularmente adecuado para dispositivos móviles, mostrando un rendimiento excepcional en procesadores con arquitectura ARM:

Dispositivo Resolución Tiempo de Inferencia (ms) Consumo de Energía (W)
Snapdragon 888 640x640 28.3 3.2
Dimensity 9000 640x640 25.7 2.9
Apple A15 640x640 22.1 2.5

Despliegue en Dispositivos de Borde

En dispositivos de borde de la serie NVIDIA Jetson, el modelo es capaz de cumplir con los requisitos de detección en tiempo real:

Dispositivo Tamaño de Lote FPS Uso de GPU
Jetson Nano 1 18.5 92%
Jetson Xavier NX 1 45.3 78%
Jetson AGX Orin 4 156.7 85%

Aceleración por Cuantificación

El bloque de cuello de botella invertido es favorable a la cuantificación, con una pérdida de precisión inferior al 0.5% después de la cuantificación INT8:

Método de Cuantificación mAP@0.5:0.95 Tamaño del Modelo (MB) Aceleración de Inferencia
FP32 39.2 91.6 1.0×
FP16 39.1 45.8 1.8×
INT8 38.8 22.9 3.2×

Direcciones para Futuras Mejoras

Factor de Expansión Dinámico

Actualmente, el factor de expansión \( t \) es estático. Podrían explorarse mecanismos de ajuste adaptativo para determinarlo dinámicamente:

$$ t_i = f(\mathbf{X}_i; \theta) $$

Donde \( f \) sería una red de predicción ligera que decide el factor de expansión basándose en las características de entrada.

Residuales Invertidos de Precisión Mixta

La combinación de operaciones convolucionales de diferentes precisiones, utilizando FP16 en capas críticas e INT8 en las menos críticas:

$$ \mathbf{Y} = \text{FP16}(\text{Expandir}(\mathbf{X})) + \text{INT8}(\text{DW}(\cdot)) + \text{FP16}(\text{Comprimir}(\cdot)) $$

Búsqueda de Arquitecturas Neuronales (NAS)

La aplicación de técnicas de NAS para encontrar automáticamente la configuración óptima del bloque de cuello de botella invertido, incluyendo:

  • El factor de expansión \( t \)
  • El tamaño del kernel \( K \)
  • El tipo de función de activación
  • La metodología de conexión residual

Etiquetas: YOLOv26 Detección de Objetos Inverted Bottleneck Redes Convolucionales CSPNet

Publicado el 7-20 23:27