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
nbloques 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