Otimização de Backbone para YOLO11: Uma Abordagem Minimalista com VanillaNet

A pesquisa intitulada "VanillaNet: The Power of Minimalism in Deep Learning", desenvolvida por Hanting Chen, Yunhe Wang, Jianyuan Guo e Dacheng Tao, com contribuições da Huawei Noah's Ark Lab e da University of Sydney, propõe uma arquitetura de rede neural inovadora com foco no design minimalista. Esta rede, denominada VanillaNet, foi concebida para evitar as complexidades comuns em modelos de deep learning modernos, como alta profundidade, conexões residuais (shortcuts) e mecanismos avançados de atenção.

A essência da VanillaNet reside na sua estrutura direta e compacta, otimizada para ambientes com recursos computacionais limitados. Cada camada é cuidadosamente projetada para ser eficiente, e uma estratégia de treinamento exclusiva permite a fusão de funções de ativação não lineares pós-treinamento, restaurando a simplicidade da arquitetura original para inferência. Demonstrou-se, através de extensos testes, que a VanillaNet pode atingir desempenho comparável ao de redes neurais profundas e transformadores visuais amplamente reconhecidos, validando o potencial do minimalismo.

As últimas décadas testemunharam um avanço notável das redes neurais, muitas vezes impulsionado pelo aumento da complexidade e profundidade para melhorar a performance. Modelos como AlexNet e ResNet estabeleceram novos padrões em reconhecimento de imagem. No entanto, essa complexidade crescente introduz desafios significativos na implementação e consumo de recursos. Por exemplo, operações de atalho em ResNet exigem grande largura de banda de memória, e mecanismos de atenção em Swin Transformers demandam engenharia sofisticada.

A arquitetura da VanillaNet adere aos princípios fundamentais do design de redes neurais, incorporando um módulo inicial (stem), um corpo principal composto por estágios sequenciais e uma camada densa para classificação. Distintamente, a VanillaNet emprega apenas uma camada convolucional por estágio, o que resulta em uma rede notavelmente simplificada. A ausência de conexões de atalho foi uma escolha deliberada, pois os autores constataram que tais adições ofereciam um ganho de desempenho marginal.

Para otimizar o desempenho da VanillaNet, foi introduzida uma técnica de "treinamento profundo". Inicialmente, durante a fase de treinamento, um par de camadas convolucionais com funções de ativação é utilizado no lugar de uma única camada. À medida que o treinamento avança, o número dessas camadas não lineares é gradualmente reduzido, culminando na fusão das duas camadas convolucionais em uma única para a fase de inferência, minimizando assim o tempo de processamento.

A validação da VanillaNet foi realizada utilizando o conjunto de dados ImageNet. Estudos de ablação confirmaram a eficácia dos módulos propostos, incluindo as funções de ativação em série e a estratégia de treinamento profundo. Adicionalmente, visualizações dos mapas de características da VanillaNet foram empregadas para analisar como a rede assimila informações visuais.

Esta pesquisa explora a viabilidade de construir redes neurais de alto desempenho sem recorrer a arquiteturas complexas, como atalhos, alta profundidade ou camadas de atenção, promovendo uma mudança de paradigma em direção à simplicidade. Os resultados dos experimentos confirmam que a VanillaNet oferece performance competitiva em tarefas de classificação de imagens em larga escala, comparável a redes profundas e transformadores visuais, destacando a promessa do minimalismo no aprendizado profundo.

A integração da VanillaNet no backbone do YOLO11 demonstra resultados promissores. Embora a rede seja intensiva em termos de uso de memória, os ganhos de precisão em comparação com modelos como o FastNet são notáveis.

import torch
import torch.nn as nn
from timm.layers import weight_init # Utilizado para inicialização de pesos

__all__ = [
    'vanillanet_v5', 'vanillanet_v6', 'vanillanet_v7', 'vanillanet_v8',
    'vanillanet_v9', 'vanillanet_v10', 'vanillanet_v11', 'vanillanet_v12',
    'vanillanet_v13', 'vanillanet_v13_large', 'vanillanet_v13_adaptive_pool'
]

class UnidadeAtivacaoDinamica(nn.ReLU):
    """
    Unidade de ativação personalizada que pode ser fundida para inferência.
    Incorpora uma convolução depthwise e Batch Normalization, com opção de fusão.
    """
    def __init__(self, num_canais, kernel_tamanho_ativacao=3, modo_inferencia=False):
        super(UnidadeAtivacaoDinamica, self).__init__()
        self.modo_inferencia = modo_inferencia
        # Pesos para a convolução depthwise
        self.kernel_conv = torch.nn.Parameter(
            torch.randn(num_canais, 1, 2 * kernel_tamanho_ativacao + 1, 2 * kernel_tamanho_ativacao + 1)
        )
        self.bias_conv = None # Bias será adicionado após fusão com BN
        self.normalizacao_batch = nn.BatchNorm2d(num_canais, eps=1e-6)
        self.num_canais = num_canais
        self.kernel_tamanho_ativacao = kernel_tamanho_ativacao
        weight_init.trunc_normal_(self.kernel_conv, std=.02)

    def forward(self, x_input):
        activated_x = super(UnidadeAtivacaoDinamica, self).forward(x_input)
        if self.modo_inferencia:
            # Em modo de inferência, BN é fundido, usa o bias_conv
            return torch.nn.functional.conv2d(
                activated_x,
                self.kernel_conv,
                self.bias_conv,
                padding=(2 * self.kernel_tamanho_ativacao + 1) // 2,
                groups=self.num_canais
            )
        else:
            # Em modo de treinamento, BN é separado
            return self.normalizacao_batch(
                torch.nn.functional.conv2d(
                    activated_x,
                    self.kernel_conv,
                    padding=self.kernel_tamanho_ativacao,
                    groups=self.num_canais
                )
            )

    def _fundir_bn_com_kernel(self, conv_kernel, bn_module):
        """Funde os parâmetros do BatchNorm nos pesos da convolução."""
        mean = bn_module.running_mean
        variance = bn_module.running_var
        gamma = bn_module.weight # Scale
        beta = bn_module.bias   # Shift
        epsilon = bn_module.eps
        std_dev = (variance + epsilon).sqrt()
        scale_factor = (gamma / std_dev).reshape(-1, 1, 1, 1)
        
        # Ajusta o kernel
        novo_kernel = conv_kernel * scale_factor
        # Ajusta o bias
        novo_bias = beta + (0 - mean) * gamma / std_dev
        return novo_kernel, novo_bias

    def para_modo_inferencia(self):
        """Converte o módulo para o modo de inferência, fundindo BN."""
        if not self.modo_inferencia:
            fused_kernel, fused_bias = self._fundir_bn_com_kernel(self.kernel_conv, self.normalizacao_batch)
            self.kernel_conv.data = fused_kernel
            self.bias_conv = torch.nn.Parameter(torch.zeros(self.num_canais))
            self.bias_conv.data = fused_bias
            self.__delattr__('normalizacao_batch') # Remove BN após fusão
            self.modo_inferencia = True


class BlocoMinimalista(nn.Module):
    """
    Bloco fundamental da VanillaNet, com caminho de treinamento e inferência.
    Durante o treinamento, usa duas convoluções 1x1 com Leaky ReLU intermediária,
    que se fundem em uma única convolução 1x1 para inferência.
    """
    def __init__(self, canais_entrada, canais_saida, kernel_tamanho_ativacao=3, passo_pooling=2,
                 modo_inferencia=False, pooling_adaptativo=None):
        super().__init__()
        self.fator_leaky_relu = 1 # Coeficiente para Leaky ReLU
        self.modo_inferencia = modo_inferencia

        if self.modo_inferencia:
            self.camada_conv_final = nn.Conv2d(canais_entrada, canais_saida, kernel_size=1)
        else:
            self.seq_treino_1 = nn.Sequential(
                nn.Conv2d(canais_entrada, canais_entrada, kernel_size=1),
                nn.BatchNorm2d(canais_entrada, eps=1e-6),
            )
            self.seq_treino_2 = nn.Sequential(
                nn.Conv2d(canais_entrada, canais_saida, kernel_size=1),
                nn.BatchNorm2d(canais_saida, eps=1e-6)
            )

        if not pooling_adaptativo:
            self.camada_pooling = nn.Identity() if passo_pooling == 1 else nn.MaxPool2d(passo_pooling)
        else:
            self.camada_pooling = nn.Identity() if passo_pooling == 1 else nn.AdaptiveMaxPool2d((pooling_adaptativo, pooling_adaptativo))

        self.ativacao_personalizada = UnidadeAtivacaoDinamica(canais_saida, kernel_tamanho_ativacao)

    def forward(self, x_input):
        if self.modo_inferencia:
            x = self.camada_conv_final(x_input)
        else:
            x = self.seq_treino_1(x_input)
            x = torch.nn.functional.leaky_relu(x, self.fator_leaky_relu)
            x = self.seq_treino_2(x)

        x = self.camada_pooling(x)
        x = self.ativacao_personalizada(x)
        return x

    def _fundir_bn_conv_weights(self, conv_layer, bn_layer, conv_bias=True):
        """Funde BatchNorm nos pesos e bias de uma camada convolucional."""
        conv_kernel = conv_layer.weight
        conv_bias_val = conv_layer.bias if conv_bias and conv_layer.bias is not None else 0
        
        running_mean = bn_layer.running_mean
        running_var = bn_layer.running_var
        gamma = bn_layer.weight
        beta = bn_layer.bias
        eps = bn_layer.eps
        std_dev = (running_var + eps).sqrt()
        
        scale_factor = (gamma / std_dev).reshape(-1, 1, 1, 1)
        
        fused_kernel = conv_kernel * scale_factor
        fused_bias = beta + (conv_bias_val - running_mean) * gamma / std_dev
        return fused_kernel, fused_bias

    def preparar_para_inferencia(self):
        """Converte o bloco para o modo de inferência, fundindo convoluções e BN."""
        if not self.modo_inferencia:
            # Funde BN da primeira convolução
            k1, b1 = self._fundir_bn_conv_weights(self.seq_treino_1[0], self.seq_treino_1[1], conv_bias=False)
            self.seq_treino_1[0].weight.data = k1
            self.seq_treino_1[0].bias = nn.Parameter(b1) # Adiciona bias explicitamente

            # Funde BN da segunda convolução
            k2, b2 = self._fundir_bn_conv_weights(self.seq_treino_2[0], self.seq_treino_2[1])

            # Agora funde as duas convoluções 1x1 e seus biases
            self.camada_conv_final = self.seq_treino_2[0] # Reusa a segunda conv para armazenar o resultado fundido
            
            # Matrizes de convolução 1x1 são basicamente matrizes densas em cada posição espacial
            # A fusão de duas convs 1x1 é uma multiplicação de matrizes.
            # k2 tem forma (out_channels, in_channels, 1, 1)
            # k1 tem forma (in_channels, in_channels, 1, 1) - canais intermediários
            
            # Para multiplicação de matrizes: transponha (1,3) para obter (out_channels, 1, 1, in_channels),
            # em seguida transponha (1,3) novamente para o formato final
            fused_kernel_data = torch.matmul(k2.squeeze(3).squeeze(2).transpose(0, 1), # (C_in_2, C_out_1)
                                             k1.squeeze(3).squeeze(2)).transpose(0, 1).unsqueeze(2).unsqueeze(3) # (C_out_2, C_in_1)
            
            self.camada_conv_final.weight.data = fused_kernel_data
            
            # Fusão dos biases: b2 + (b1 * k2_sum_across_spatial_dims)
            fused_bias_data = b2 + (b1.view(1, -1, 1, 1) * k2).sum(3).sum(2).sum(1)
            self.camada_conv_final.bias.data = fused_bias_data

            self.__delattr__('seq_treino_1')
            self.__delattr__('seq_treino_2')
            self.ativacao_personalizada.para_modo_inferencia()
            self.modo_inferencia = True


class RedeNeuralVanilla(nn.Module):
    """
    Implementação da arquitetura VanillaNet, uma rede neural minimalista.
    """
    def __init__(self, canais_entrada_img=3, num_classes_saida=1000, dimensoes_estagios=[96, 192, 384, 768],
                 taxa_dropout=0, kernel_ativacao=3, passos_estagios=[2, 2, 2, 1],
                 modo_inferencia=False, pooling_adaptativo=None, **kwargs):
        super().__init__()
        self.modo_inferencia = modo_inferencia
        self.fator_leaky_relu = 1 # Coeficiente para Leaky ReLU global

        if self.modo_inferencia:
            self.bloco_raiz_prod = nn.Sequential(
                nn.Conv2d(canais_entrada_img, dimensoes_estagios[0], kernel_size=4, stride=4),
                UnidadeAtivacaoDinamica(dimensoes_estagios[0], kernel_ativacao)
            )
        else:
            self.bloco_raiz_treino_1 = nn.Sequential(
                nn.Conv2d(canais_entrada_img, dimensoes_estagios[0], kernel_size=4, stride=4),
                nn.BatchNorm2d(dimensoes_estagios[0], eps=1e-6),
            )
            self.bloco_raiz_treino_2 = nn.Sequential(
                nn.Conv2d(dimensoes_estagios[0], dimensoes_estagios[0], kernel_size=1, stride=1),
                nn.BatchNorm2d(dimensoes_estagios[0], eps=1e-6),
                UnidadeAtivacaoDinamica(dimensoes_estagios[0], kernel_ativacao)
            )

        self.lista_estagios = nn.ModuleList()
        for i in range(len(passos_estagios)):
            current_adaptive_pool = pooling_adaptativo[i] if pooling_adaptativo else None
            estagio = BlocoMinimalista(
                canais_entrada=dimensoes_estagios[i],
                canais_saida=dimensoes_estagios[i + 1],
                kernel_tamanho_ativacao=kernel_ativacao,
                passo_pooling=passos_estagios[i],
                modo_inferencia=modo_inferencia,
                pooling_adaptativo=current_adaptive_pool
            )
            self.lista_estagios.append(estagio)
        self.num_estagios = len(passos_estagios)

        self._inicializar_pesos_rede(self) # Inicializa pesos
        
        # Calcula as larguras de saída dos feature maps para verificar a arquitetura
        with torch.no_grad():
            dummy_input = torch.randn(1, 3, 640, 640)
            dummy_output = self.forward(dummy_input)
            self.larguras_saida = [fmap.size(1) for fmap in dummy_output]

    def _inicializar_pesos_rede(self, modulo):
        """Inicializa pesos para convoluções e camadas lineares."""
        if isinstance(modulo, (nn.Conv2d, nn.Linear)):
            weight_init.trunc_normal_(modulo.weight, std=.02)
            if modulo.bias is not None:
                nn.init.constant_(modulo.bias, 0)

    def ajustar_fator_leaky_relu(self, fator):
        """Ajusta o fator de Leaky ReLU em todos os blocos e no stem."""
        for estagio in self.lista_estagios:
            estagio.fator_leaky_relu = fator
        self.fator_leaky_relu = fator

    def forward(self, x_input):
        saidas_rede = []
        if self.modo_inferencia:
            x = self.bloco_raiz_prod(x_input)
        else:
            x = self.bloco_raiz_treino_1(x_input)
            x = torch.nn.functional.leaky_relu(x, self.fator_leaky_relu)
            x = self.bloco_raiz_treino_2(x)
        saidas_rede.append(x)
        
        for estagio_idx in range(self.num_estagios):
            x = self.lista_estagios[estagio_idx](x)
            saidas_rede.append(x)
        return saidas_rede

    def _fundir_seq_conv_bn(self, conv_layer, bn_layer):
        """Função auxiliar para fundir um Conv2d com um BatchNorm2d."""
        conv_kernel = conv_layer.weight
        conv_bias = conv_layer.bias if conv_layer.bias is not None else 0
        
        running_mean = bn_layer.running_mean
        running_var = bn_layer.running_var
        gamma = bn_layer.weight
        beta = bn_layer.bias
        eps = bn_layer.eps
        std = (running_var + eps).sqrt()
        
        scale_factor = (gamma / std).reshape(-1, 1, 1, 1)
        
        fused_kernel = conv_kernel * scale_factor
        fused_bias = beta + (conv_bias - running_mean) * gamma / std
        return fused_kernel, fused_bias

    def converter_para_producao(self):
        """
        Converte toda a rede para o modo de inferência, fundindo BatchNorms
        e convoluções em camadas únicas.
        """
        if not self.modo_inferencia:
            # Converte e funde o bloco raiz
            self.bloco_raiz_treino_2[2].para_modo_inferencia() # Ativação do stem
            
            k_s1, b_s1 = self._fundir_seq_conv_bn(self.bloco_raiz_treino_1[0], self.bloco_raiz_treino_1[1])
            self.bloco_raiz_treino_1[0].weight.data = k_s1
            self.bloco_raiz_treino_1[0].bias = nn.Parameter(b_s1)

            k_s2, b_s2 = self._fundir_seq_conv_bn(self.bloco_raiz_treino_2[0], self.bloco_raiz_treino_2[1])
            
            # Funde as duas convoluções do stem
            # k_s2 tem forma (C_out_2, C_in_2, 1, 1)
            # k_s1 tem forma (C_out_1, C_in_1, K, K)
            
            fused_stem_kernel = torch.einsum('oi,icjk->ocjk', k_s2.squeeze(3).squeeze(2),
                                             self.bloco_raiz_treino_1[0].weight.data)
            self.bloco_raiz_treino_1[0].weight.data = fused_stem_kernel
            
            fused_stem_bias = b_s2 + (self.bloco_raiz_treino_1[0].bias.data.view(1, -1, 1, 1) * k_s2).sum(3).sum(2).sum(1)
            self.bloco_raiz_treino_1[0].bias.data = fused_stem_bias
            
            self.bloco_raiz_prod = nn.Sequential(*[self.bloco_raiz_treino_1[0], self.bloco_raiz_treino_2[2]])
            self.__delattr__('bloco_raiz_treino_1')
            self.__delattr__('bloco_raiz_treino_2')

            # Converte cada estágio individualmente
            for i in range(self.num_estagios):
                self.lista_estagios[i].preparar_para_inferencia()

            self.modo_inferencia = True


def obter_modelo_vanillanet_v5(**kwargs):
    """Retorna uma VanillaNet de 5 estágios."""
    return RedeNeuralVanilla(dims=[128 * 4, 256 * 4, 512 * 4, 1024 * 4], passos_estagios=[2, 2, 2], **kwargs)


def obter_modelo_vanillanet_v6(**kwargs):
    """Retorna uma VanillaNet de 6 estágios."""
    return RedeNeuralVanilla(dims=[128 * 4, 256 * 4, 512 * 4, 1024 * 4, 1024 * 4], passos_estagios=[2, 2, 2, 1], **kwargs)


def obter_modelo_vanillanet_v7(**kwargs):
    """Retorna uma VanillaNet de 7 estágios."""
    return RedeNeuralVanilla(dims=[128 * 4, 128 * 4, 256 * 4, 512 * 4, 1024 * 4, 1024 * 4], passos_estagios=[1, 2, 2, 2, 1], **kwargs)


def obter_modelo_vanillanet_v8(**kwargs):
    """Retorna uma VanillaNet de 8 estágios."""
    return RedeNeuralVanilla(dims=[128 * 4, 128 * 4, 256 * 4, 512 * 4, 512 * 4, 1024 * 4, 1024 * 4],
                       passos_estagios=[1, 2, 2, 1, 2, 1], **kwargs)


def obter_modelo_vanillanet_v9(**kwargs):
    """Retorna uma VanillaNet de 9 estágios."""
    return RedeNeuralVanilla(dims=[128 * 4, 128 * 4, 256 * 4, 512 * 4, 512 * 4, 512 * 4, 1024 * 4, 1024 * 4],
                       passos_estagios=[1, 2, 2, 1, 1, 2, 1], **kwargs)


def obter_modelo_vanillanet_v10(**kwargs):
    """Retorna uma VanillaNet de 10 estágios."""
    return RedeNeuralVanilla(
        dims=[128 * 4, 128 * 4, 256 * 4, 512 * 4, 512 * 4, 512 * 4, 512 * 4, 1024 * 4, 1024 * 4],
        passos_estagios=[1, 2, 2, 1, 1, 1, 2, 1],
        **kwargs)


def obter_modelo_vanillanet_v11(**kwargs):
    """Retorna uma VanillaNet de 11 estágios."""
    model = RedeNeuralVanilla(
        dimensoes_estagios=[128 * 4, 128 * 4, 256 * 4, 512 * 4, 512 * 4, 512 * 4, 512 * 4, 512 * 4, 1024 * 4, 1024 * 4],
        passos_estagios=[1, 2, 2, 1, 1, 1, 1, 2, 1],
        **kwargs)
    return model


def obter_modelo_vanillanet_v12(**kwargs):
    """Retorna uma VanillaNet de 12 estágios."""
    return RedeNeuralVanilla(
        dims=[128 * 4, 128 * 4, 256 * 4, 512 * 4, 512 * 4, 512 * 4, 512 * 4, 512 * 4, 512 * 4, 1024 * 4, 1024 * 4],
        passos_estagios=[1, 2, 2, 1, 1, 1, 1, 1, 2, 1],
        **kwargs)


def obter_modelo_vanillanet_v13(**kwargs):
    """Retorna uma VanillaNet de 13 estágios."""
    return RedeNeuralVanilla(
        dims=[128 * 4, 128 * 4, 256 * 4, 512 * 4, 512 * 4, 512 * 4, 512 * 4, 512 * 4, 512 * 4, 512 * 4, 1024 * 4,
              1024 * 4],
        passos_estagios=[1, 2, 2, 1, 1, 1, 1, 1, 1, 2, 1],
        **kwargs)


def obter_modelo_vanillanet_v13_large(**kwargs):
    """Retorna uma VanillaNet de 13 estágios com largura de canal aumentada (x1.5)."""
    return RedeNeuralVanilla(
        dims=[128 * 6, 128 * 6, 256 * 6, 512 * 6, 512 * 6, 512 * 6, 512 * 6, 512 * 6, 512 * 6, 512 * 6, 1024 * 6,
              1024 * 6],
        passos_estagios=[1, 2, 2, 1, 1, 1, 1, 1, 1, 2, 1],
        **kwargs)


def obter_modelo_vanillanet_v13_adaptive_pool(**kwargs):
    """Retorna uma VanillaNet de 13 estágios com largura de canal aumentada (x1.5) e pooling adaptativo."""
    return RedeNeuralVanilla(
        dims=[128 * 6, 128 * 6, 256 * 6, 512 * 6, 512 * 6, 512 * 6, 512 * 6, 512 * 6, 512 * 6, 512 * 6, 1024 * 6,
              1024 * 6],
        passos_estagios=[1, 2, 2, 1, 1, 1, 1, 1, 1, 2, 1],
        pooling_adaptativo=[0, 38, 19, 0, 0, 0, 0, 0, 0, 10, 0], # Dimensões adaptativas para pooling
        **kwargs)

Após a integração do backbone VanillaNet e a execução dos testes com o YOLO11, os seguintes resultados foram observados:

Ultralytics 8.3.0 🚀 Python-3.9.20 torch-2.0.1 CUDA:0 (NVIDIA GeForce RTX 3060 Laptop GPU, 12288MiB)

YOLO11-VanillaNet summary: 205 layers, 29,772,192 parameters, 0 gradients, 150.5 GFLOPs

                Class    Images   Instances       Box(P          R      mAP50  mAP50-95): 100%|██████████| 8/8 [00:04<00:00,  1.75it/s]

                  all       128         929       0.797        0.614      0.695      0.565

                 person        61         254       0.96         0.568      0.788      0.603

                bicycle         3           6       0.893        0.167      0.201      0.179

                    car        12          46       0.763        0.21       0.256      0.151

             motorcycle         4           5       0.73         1          0.995      0.902

               airplane         5           6       0.824        0.788      0.913      0.692

                    bus         5           7       0.821        0.714      0.718      0.647

                  train         3           3       0.445        1          0.863      0.642

                  truck         5          12       0.864        0.5        0.522      0.425

                   boat         2           6       0.586        0.476      0.554      0.379

          traffic light         4          14       0.725        0.143      0.267      0.188

              stop sign         2           2       1            0          0.595      0.526

                  bench         5           9       0.409        0.556      0.513      0.448

                   bird         2          16       0.957        0.938      0.991      0.69

                    cat         4           4       0.844        0.75       0.774      0.709

                    dog         9           9       0.987        0.778      0.899      0.718

                  horse         1           2       0.918        1          0.995      0.921

               elephant         4          17       0.827        0.941      0.902      0.757

                   bear         1           1       0.802        1          0.995      0.995

                  zebra         2           4       0.685        1          0.995      0.945

                giraffe         4           9       0.62         1          0.956      0.801

               backpack         4           6       1            0.533      0.669      0.619

               umbrella         4          18       0.711        0.944      0.921      0.716

                handbag         9          19       1            0.218      0.365      0.295

                    tie         6           7       0.715        0.571      0.688      0.492

               suitcase         2           4       0.861        0.75       0.776      0.68

                frisbee         5           5       0.668        0.4        0.631      0.463

                   skis         1           1       0            0          0          0

              snowboard         2           7       0.81         0.857      0.944      0.684

            sports ball         6           6       0.943        0.167      0.171      0.137

                   kite         2          10       1            0.128      0.315      0.083

           baseball bat         4           4       1            0          0.264      0.234

         baseball glove         4           7       1            0.191      0.289      0.261

           skateboard         3           5       0.979        0.4        0.648      0.43

          tennis racket         5           7       1            0.365      0.464      0.381

                 bottle         6          18       0.897        0.222      0.24       0.212

             wine glass         5          16       0.668        0.755      0.733      0.423

                    cup        10          36       0.838        0.431      0.631      0.379

                   fork         6           6       0.606        0.273      0.471      0.367

                  knife         7          16       1            0.366      0.605      0.323

                  spoon         5          22       0.888        0.36       0.542      0.333

                   bowl         9          28       0.951        0.69       0.743      0.621

                 banana         1           1       0.839        1          0.995      0.995

               sandwich         2           2       0.761        1          0.995      0.92

                 orange         1           4       0.45         1          0.995      0.805

               broccoli         4          11       0.727        0.455      0.514      0.418

                 carrot         3          24       0.442        0.75       0.703      0.529

                hot dog         1           2       0.825        1          0.995      0.921

                  pizza         5           5       0.89         0.8        0.862      0.783

                  donut         2          14       1            0.974      0.995      0.811

                   cake         4           4       1            0.479      0.794      0.721

                  chair         9          35       0.86         0.703      0.889      0.646

                  couch         5           6       0.563        0.438      0.788      0.634

           potted plant         9          14       0.844        0.929      0.945      0.744

                    bed         3           3       0.931        1          0.995      0.895

           dining table        10          13       0.857        0.615      0.756      0.608

                 toilet         2           2       0.878        1          0.995      0.895

                     tv         2           2       0.909        1          0.995      0.895

                 laptop         2           3       0.525        0.333      0.525      0.451

                  mouse         2           2       0            0          0          0

                 remote         5           8       1            0.236      0.407      0.343

             cell phone         5           8       0.735        0.125      0.154      0.122

              microwave         3           3       0.929        1          0.995      0.764

                   oven         5           5       0.917        0.8        0.797      0.657

                   sink         4           6       0.751        0.505      0.65       0.555

           refrigerator         5           5       0.607        0.8        0.906      0.631

                   book         6          29       0.754        0.422      0.576      0.278

                  clock         8           9       1            0.737      0.874      0.762

                   vase         2           2       0.762        0.5        0.521      0.414

               scissors         1           1       0.86         1          0.995      0.796

             teddy bear         6          21       0.948        0.878      0.971      0.809

             toothbrush         2           5       0.853        1          0.995      0.849

Speed: 0.8ms preprocess, 22.1ms inference, 0.0ms loss, 1.7ms postprocess per image

Results saved to runs\train\exp_yolo11-VanillaNet_datasets_coco1283


Tags: YOLO11 VanillaNet DeepLearning ComputerVision NeuralNetwork

Publicado em 7-29 02:10