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.