Reconhecimento de Imagens Meteorológicas com PyTorch

Este guia técnico detalha a implementação de um modelo de Deep Learning utilizando a biblioteca PyTorch para classificar diferentes condições climáticas através de visão computacional. O processo abrange desde o pré-processamento de dados até a inferência final.

Configuração do Ambiente e Dependências

Iniciamos configurando o ambiente com as bibliotecas necessárias para manipulação de tensores, processamento de imagens e construção da arquitetura neural.

import torch
import torch.nn as nn
import torch.nn.functional as F
from torchvision import datasets, transforms, models
from torch.utils.data import DataLoader, random_split
import pathlib
import matplotlib.pyplot as plt
from PIL import Image
import os
import json

# Definição do hardware para processamento
device_engine = torch.device("cuda" if torch.cuda.is_available() else "cpu")
print(f"Utilizando dispositivo: {device_engine}")

Preparação e Augmentação de Dados

Para garantir que o modelo seja robusto, aplicamos técnicas de data augmentation. As imagens são redimensionadas para um padrão de 224x224 pixels e normalizadas com base nas estatísticas do ImageNet.

# Configuração das transformações de imagem
pipeline_transform = transforms.Compose([
    transforms.Resize([224, 224]),
    transforms.RandomHorizontalFlip(p=0.5),
    transforms.ToTensor(),
    transforms.Normalize(mean=[0.485, 0.456, 0.226], 
                         std=[0.229, 0.224, 0.225])
])

# Carregamento do dataset via ImageFolder
path_dataset = './weather_photos/'
dataset_completo = datasets.ImageFolder(root=path_dataset, transform=pipeline_transform)

# Mapeamento de classes
nomes_classes = dataset_completo.classes
num_classes = len(nomes_classes)
print(f"Classes identificadas: {nomes_classes}")

# Divisão em treino (80%) e teste (20%)
tamanho_treino = int(0.8 * len(dataset_completo))
tamanho_teste = len(dataset_completo) - tamanho_treino
set_treino, set_teste = random_split(dataset_completo, [tamanho_treino, tamanho_teste])

# DataLoaders para iteração em lotes
batch_size = 32
loader_treino = DataLoader(set_treino, batch_size=batch_size, shuffle=True)
loader_teste = DataLoader(set_teste, batch_size=batch_size, shuffle=False)

Arquitetura da Rede Neural Convolucional (CNN)

A rede projetada utiliza camadas de convolução seguidas por normalização em lote (Batch Normalization) para acelerar a convergência e estabilizar o treinamento.

class WeatherClassifier(nn.Module):
    def __init__(self, num_target_classes):
        super(WeatherClassifier, self).__init__()
        
        # Bloco Convolucional 1
        self.feature_extractor = nn.Sequential(
            nn.Conv2d(3, 12, kernel_size=5),
            nn.BatchNorm2d(12),
            nn.ReLU(),
            nn.Conv2d(12, 12, kernel_size=5),
            nn.BatchNorm2d(12),
            nn.ReLU(),
            nn.MaxPool2d(2, 2),
            
            # Bloco Convolucional 2
            nn.Conv2d(12, 24, kernel_size=5),
            nn.BatchNorm2d(24),
            nn.ReLU(),
            nn.Conv2d(24, 24, kernel_size=5),
            nn.BatchNorm2d(24),
            nn.ReLU(),
            nn.MaxPool2d(2, 2)
        )
        
        # Camada de classificação final
        # Cálculo baseado na redução dimensional (224 -> 50 após pools e convs)
        self.classifier = nn.Linear(24 * 50 * 50, num_target_classes)

    def forward(self, x):
        x = self.feature_extractor(x)
        x = x.view(x.size(0), -1) # Flatten
        x = self.classifier(x)
        return x

modelo_clima = WeatherClassifier(num_classes).to(device_engine)

Ciclo de Treinamento e Validação

Utilizamos a função de perda CrossEntropyLoss e o otimizador Adam para ajustar os pesos. O processo monitora a acurácia em ambos os conjuntos a cada época.

criterio = nn.CrossEntropyLoss()
otimizador = torch.optim.Adam(modelo_clima.parameters(), lr=1e-4)

def executar_treino(epochs=30):
    historico = {'treino_acc': [], 'teste_acc': []}
    
    for epoch in range(epochs):
        modelo_clima.train()
        total_loss, total_correct = 0, 0
        
        for imagens, labels in loader_treino:
            imagens, labels = imagens.to(device_engine), labels.to(device_engine)
            
            # Forward pass
            outputs = modelo_clima(imagens)
            loss = criterio(outputs, labels)
            
            # Backward pass e otimização
            otimizador.zero_grad()
            loss.backward()
            otimizador.step()
            
            total_correct += (outputs.argmax(1) == labels).type(torch.float).sum().item()
            total_loss += loss.item()
            
        acc_treino = total_correct / len(set_treino)
        
        # Avaliação no set de teste
        modelo_clima.eval()
        acc_teste_total = 0
        with torch.no_grad():
            for imagens, labels in loader_teste:
                imagens, labels = imagens.to(device_engine), labels.to(device_engine)
                outputs = modelo_clima(imagens)
                acc_teste_total += (outputs.argmax(1) == labels).type(torch.float).sum().item()
        
        acc_teste = acc_teste_total / len(set_teste)
        print(f"Época {epoch+1}/{epochs} | Treino Acc: {acc_treino:.4f} | Teste Acc: {acc_teste:.4f}")
        
    return historico

# Iniciar o treinamento
# executar_treino(epochs=50)

Predição em Novas Imagens

Para prever uma imagem externa, aplicamos o mesmo pré-processamento e utilizamos a função softmax para interpretar as probabilidades de saída.

def predizer_clima(caminho_imagem):
    # Carregar imagem e aplicar transforms
    img = Image.open(caminho_imagem).convert('RGB')
    img_tensor = pipeline_transform(img).unsqueeze(0).to(device_engine)
    
    modelo_clima.eval()
    with torch.no_grad():
        output = modelo_clima(img_tensor)
        probabilidades = torch.softmax(output, dim=1)
        classe_id = torch.argmax(probabilidades).item()
        
    label_predito = nomes_classes[classe_id]
    confianca = probabilidades[0][classe_id].item()
    
    print(f"Resultado: {label_predito} ({confianca*100:.2f}%)")
    plt.imshow(img)
    plt.title(f"Predição: {label_predito}")
    plt.axis('off')
    plt.show()

# Exemplo de uso:
# predizer_clima('exemplo_nuvens.jpg')

Ao comparar otimizadores, observa-se que o Adam apresenta uma convergência inicial mais argessiva, atingindo níveis altos de precisão rapidamente, enquanto o SGD (Stochastic Gradient Descent) tende a ser mais estável a longo prazo, embora exija mais épocas para atingir o platô de performance.

Tags: Pytorch ComputerVision CNN deep-learning image-classification

Publicado em 8-4 21:28