A arquitetura Network in Network (NiN) introduziu conceitos fundamentais que alteraram a forma como projetamos redes neurais convolucionais (CNNs). Enquanto modelos como AlexNet e VGG focam no empilhamento de camadas de convolução e pooling seguidas por camadas densas, a NiN propõe uma abordagem onde pequenas sub-redes são integradas diretamente nos blocos convolucionais.
O Conceito do Bloco NiN
Em arquiteturas tradicionais, as camadas convolucionais utilizam filtros lineares seguidos por funções de ativação não lineares para extrair características. A NiN substitui essa estrutura por "micro-redes" compostas por perceptrons multicamadas (MLP). Na prática, isso é implementado através de convoluções de 1×1.
Uma camada convolucional de 1×1 funciona como uma camada totalmente conectada aplicada de forma independente a cada pixel em todos os canais de entrada. Isso permite que a rede aprenda interações complexas entre os canais sem alterar a estrutura espacial (altura e largura) do mapa de características.
import torch
from torch import nn
def criar_bloco_nin(canais_entrada, canais_saida, tamanho_kernel, passo, preenchimento):
"""
Constrói um bloco NiN consistindo em uma convolução padrão
seguida por duas convoluções de 1x1 (MLPs).
"""
return nn.Sequential(
nn.Conv2d(canais_entrada, canais_saida, kernel_size=tamanho_kernel, stride=passo, padding=preenchimento),
nn.ReLU(),
nn.Conv2d(canais_saida, canais_saida, kernel_size=1),
nn.ReLU(),
nn.Conv2d(canais_saida, canais_saida, kernel_size=1),
nn.ReLU()
)
Estrutura Global e Global Average Pooling
Diferente de seus antecessores, a NiN elimina as camadas densas (Fully Connected) ao final da rede. Em vez disso, ela utiliza um bloco NiN cuja saída possui o mesmo número de canais que o número de classes do problema. Em seguida, aplica-se uma camada de Global Average Pooling (GAP).
O GAP calcula a média de cada mapa de características, resultando em um vetor que pode ser alimentado diretamente em uma função Softmax. Essa abordagem oferece duas vantagens principais:
- Redução de parâmetros: Camadas densas são responsáveis pela maior parte dos parâmetros em redes como a AlexNet. Ao eliminá-las, o risco de overfitting diminui drasticamente.
- Robustez espacial: A rede torna-se mais resiliente a trenslações na imagem de entrada.
def construir_modelo_nin(num_classes=10):
return nn.Sequential(
criar_bloco_nin(1, 96, tamanho_kernel=11, passo=4, preenchimento=0),
nn.MaxPool2d(3, stride=2),
criar_bloco_nin(96, 256, tamanho_kernel=5, passo=1, preenchimento=2),
nn.MaxPool2d(3, stride=2),
criar_bloco_nin(256, 384, tamanho_kernel=3, passo=1, preenchimento=1),
nn.MaxPool2d(3, stride=2),
nn.Dropout(0.5),
# Bloco final ajustado para o número de classes
criar_bloco_nin(384, num_classes, tamanho_kernel=3, passo=1, preenchimento=1),
nn.AdaptiveAvgPool2d((1, 1)),
nn.Flatten()
)
modelo = construir_modelo_nin()
Validação da Arquitetura
Podemos verificar a evolução das dimensões dos dados através das camadas do modelo para garantir que a redução espacial e a agregação final ocorram conforme o esperado.
entrada_teste = torch.randn(size=(1, 1, 224, 224))
for camada in modelo:
entrada_teste = camada(entrada_teste)
print(f"Camada: {camada.__class__.__name__:<20} | Shape de Saída: {entrada_teste.shape}")
Preparação de Dados e Treinamento
Para o treinamento, o dataset Fashion-MNIST é comumente utilizado. Como as imagens originais são 28x28, é necessário realizar um redimensionamento para 224x224 para manter a compatibilidade com os hiperparâmetros originais da NiN inspirados na AlexNet.
from torchvision import datasets, transforms
from torch.utils.data import DataLoader
def carregar_dados_mnist(lote_tamanho, redimensionar=None):
transformacoes = [transforms.ToTensor()]
if redimensionar:
transformacoes.insert(0, transforms.Resize(redimensionar))
composicao = transforms.Compose(transformacoes)
treino_set = datasets.FashionMNIST(root="./data", train=True, transform=composicao, download=True)
teste_set = datasets.FashionMNIST(root="./data", train=False, transform=composicao, download=True)
return (DataLoader(treino_set, batch_size=lote_tamanho, shuffle=True),
DataLoader(teste_set, batch_size=lote_tamanho, shuffle=False))
# Configuração de Hiperparâmetros
taxa_aprendizado, epocas, batch_size = 0.1, 5, 128
treino_loader, teste_loader = carregar_dados_mnist(batch_size, redimensionar=224)
A NiN provou que o uso de micro-redes dentro de camadas convolucionais e a substituição de camadas densas por pooling global são estratégias eficazes para criar modelos mais profundos e eficientes. Esses conceitos pavimentaram o caminho para arquiteturas modernas como Inception e ResNet.