Capítulo 4: Deep Learning na Prática#

cover

Redes Neurais Convolucionais#

O ano de 2012 foi um marco na área de visão computacional com a publicação do artigo inovador “ImageNet Classification with Deep Convolutional Neural Networks”. Nesse trabalho, Alex Krizhevsky, Ilya Sutskever e Geoffrey Hinton apresentaram a arquitetura AlexNet, uma Rede Neural Convolucional (CNN) profunda que superou significativamente os métodos tradicionais na classificação de imagens em larga escala. Utilizando o conjunto de dados ImageNet, essa conquista despertou grande interesse e impulsionou a pesquisa em CNNs, consolidando-as como a principal abordagem em diversas tarefas de visão computacional.

As CNNs surgiram como uma força motriz nessa revolução, promovendo avanços extraordinários em áreas como reconhecimento de imagens, segmentação e muito mais. Inspiradas na estrutura e funcionamento do sistema visual humano, essas redes neurais artificiais possuem uma capacidade impressionante de interpretar e extrair significado de dados visuais.

Aplicações Transformadoras em Visão Computacional#

A versatilidade e o poder das CNNs levaram à sua adoção em uma ampla gama de aplicações de visão computacional, incluindo:

  • Reconhecimento de Objetos: CNNs identificam e classificam objetos em imagens com alta precisão, mesmo em cenários complexos com múltiplos objetos e fundos confusos.

  • Detecção de Objetos: Além de reconhecer, as CNNs podem localizar e delinear objetos em uma imagem, fornecendo informações valiosas sobre suas posições e tamanhos.

  • Segmentação de Imagens: CNNs segmentam imagens em diferentes regiões semânticas, rotulando cada pixel com a classe correspondente, como céu, edifícios, carros e pessoas.

  • Reconhecimento Facial: CNNs são amplamente utilizadas para identificar e reconhecer rostos em imagens e vídeos, com aplicações em segurança, autenticação e mídia social.

  • Análise de Imagens Médicas: CNNs auxiliam médicos no diagnóstico de doenças, identificando padrões em radiografias, tomografias e outras imagens médicas.

Arquitetura em Camadas: Da Percepção à Abstração#

Uma CNN típica é composta por várias camadas interconectadas, cada uma com um papel crucial na extração de características e no processo de aprendizagem. Essa estrutura em camadas permite que a rede aprenda representações hierárquicas de dados visuais, evoluindo de padrões simples para características complexas.

Camadas Convolucionais: Encontrando os Padrões

No coração de uma CNN estão as camadas convolucionais, responsáveis por identificar e extrair características visuais significativas de uma imagem de entrada. Essas camadas utilizam filtros especializados, conhecidos como kernels, que percorrem a imagem realizando operações matemáticas para detectar padrões como bordas, cantos e texturas. À medida que a informação passa pelas camadas convolucionais, a rede desenvolve a capacidade de reconhecer padrões cada vez mais abstratos e complexos.

Um experimento interativo mostra como diferentes kernels extraem características distintas, ilustrando a capacidade das CNNs de aprender e adaptar-se a diferentes tipos de dados visuais.

A seguir, uma ilustração do que seria uma CNN.

cnn

Fonte de Figura

Camadas de Pooling: Simplificando a Informação Visual

Após as camadas convolucionais, as camadas de pooling entram em ação para reduzir a dimensionalidade dos mapas de características gerados. Essas camadas agregam informações espaciais, preservando características importantes enquanto descartam variações mínimas. Esse processo torna a representação da imagem mais compacta e aumenta a robustez da rede a pequenas variações na posição e na escala do objeto. Existem diferentes tipos de pooling, sendo os mais comuns o Max Pooling e o Average Pooling.

  • Max Pooling: O Max Pooling extrai o valor máximo dentro de uma janela definida pelo kernel, destacando as características mais proeminentes.

Histogramas

(Fonte da figura)

 Kernel 2x2:
 1 3   ->  Max Pooling  ->  3
 2 3
  • Average Pooling: O Average Pooling calcula a média dos valores dentro da janela do kernel, suavizando as características e reduzindo a sensibilidade a pequenas variações.

 Kernel 2x2:
 1 3   ->  Average Pooling  ->  2
 2 3

Flattening: Transformando Matrizes em Vetores

Antes que as características extraídas pelas camadas convolucionais e de pooling sejam passadas para a camada totalmente conectada, elas precisam ser “achatadas” em um vetor unidimensional. Esse processo, chamado de flattening, organiza os valores dos mapas de características em uma única coluna, como no exemplo a seguir:

Matriz 2x2:       Vetor Coluna:
1 2               1
3 4               2
                  3
                  4

Camadas Totalmente Conectadas: Integrando e Classificando

As camadas totalmente conectadas, também conhecidas como camadas densas, representam a fase final no pipeline de uma CNN. Essas camadas recebem o vetor de características achatado e o utilizam para realizar a tarefa de classificação ou regressão. Em uma camada totalmente conectada, cada neurônio está conectado a todos os neurônios da camada anterior, permitindo que a rede aprenda relações complexas entre as características extraídas e faça previsões finais.

Recursos extras:

Dataset#

Um dataset é uma coleção organizada de dados utilizados para fins específicos, como treinamento e avaliação de modelos de machine learning. Esses dados podem incluir informações como imagens, textos, números ou qualquer outro tipo relevante para uma tarefa específica. Os datasets são geralmente divididos em subconjuntos para diferentes estágios do processo de machine learning.

Conjunto de Treino, Teste e Validação#

Ao trabalhar com um dataset, é comum dividi-lo em três subconjuntos principais: conjunto de treino, conjunto de teste e conjunto de validação:

AI
  • Conjunto de Treino (Training Set): Este subconjunto é usado para treinar o modelo de machine learning. O algoritmo aprende com esses dados, ajustando seus parâmetros para fazer previsões precisas. Geralmente, o conjunto de treino é o maior subconjunto do dataset.

  • Conjunto de Teste (Test Set): O conjunto de teste é usado para avaliar o desempenho do modelo após o treinamento. Ele ajuda a verificar quão bem o modelo generaliza para novos dados que não foram vistos durante o treinamento. O conjunto de teste deve ser representativo dos dados reais que o modelo encontrará em produção.

  • Conjunto de Validação (Validation Set): O conjunto de validação é usado durante o processo de treinamento para ajustar os hiperparâmetros do modelo e prevenir overfitting. Ele fornece um feedback sobre o desempenho do modelo em diferentes configurações antes da avaliação final no conjunto de teste.

Veja um exemplo comum de divisão do Dataset:

AI

Overfitting ocorre quando o modelo aprende excessivamente os detalhes e ruídos do conjunto de treino, perdendo a capacidade de generalização para novos dados — ou seja, ele vai muito bem nos dados vistos, mas tem desempenho ruim em dados inéditos.

Validação Cruzada (Cross-Validation)#

A validação cruzada é uma técnica usada para avaliar o desempenho de um modelo de forma mais confiável, especialmente quando o dataset é pequeno.

Na forma mais comum, chamada k-fold cross-validation, o dataset é dividido em k partes. O modelo é treinado k vezes, cada vez usando uma parte diferente como teste e as demais como treino.

Exemplo (k = 5):

Suponha um dataset com 10 amostras:

Iteração 1: Treino [3-10],     Teste [1-2]     →  T T X X X X X X X X
Iteração 2: Treino [1-2,5-10], Teste [3-4]     →  X X T T X X X X X X
Iteração 3: Treino [1-4,7-10], Teste [5-6]     →  X X X X T T X X X X
Iteração 4: Treino [1-6,9-10], Teste [7-8]     →  X X X X X X T T X X
Iteração 5: Treino [1-8],      Teste [9-10]    →  X X X X X X X X T T

Principais Sites para Acesso a Datasets#

Aqui está uma tabela com alguns dos principais sites que fornecem acesso a datasets para machine learning, incluindo imagens, textos e outros tipos de dados:

Site

Endereço

Descrição

Kaggle

Kaggle Datasets

Plataforma de ciência de dados com diversos datasets para competições e projetos.

TensorFlow Datasets

TensorFlow Datasets

Biblioteca de datasets integrada à TensorFlow.

ImageNet

ImageNet

Banco de imagens grande e variado, comumente usado em visão computacional.

UCI Machine Learning Repository

UCI ML Repository

Repositório de conjuntos de dados de várias áreas, mantido pela Universidade da Califórnia, Irvine.

Hugging Face Datasets

Hugging Face Datasets

Plataforma que fornece uma gama de datasets para tarefas de processamento de linguagem natural e visão computacional.

O Hugging Face é uma plataforma popular que fornece uma vasta coleção de datasets para tarefas de processamento de linguagem natural (NLP) e visão computacional. Eles oferecem uma interface fácil de usar e uma variedade de ferramentas para trabalhar com modelos de transformadores, incluindo o BERT, GPT e muito mais. O Hugging Face também fornece uma biblioteca de datasets que pode ser facilmente integrada aos seus projetos de machine learning.

Classificação de Dígitos com Redes Convolucionais (CNNs) no TensorFlow#

Com a API Keras, integrada ao TensorFlow, é possível montar e treinar Redes Neurais Convolucionais (CNNs) com poucas linhas de código. As CNNs são especialmente eficazes para tarefas de visão computacional, como o reconhecimento de dígitos no dataset MNIST.

Importações e Pré-processamento

import tensorflow as tf
from tensorflow.keras import layers, models
import matplotlib.pyplot as plt
import numpy as np

# Carrega e normaliza os dados
(x_train, y_train), (x_test, y_test) = tf.keras.datasets.mnist.load_data()
x_train = x_train / 255.0
x_test = x_test / 255.0

Arquitetura da Rede Convolucional

model = models.Sequential([
    layers.Input(shape=(28, 28)),
    layers.Reshape((28, 28, 1)), 
    layers.Conv2D(32, (3, 3), activation='relu'),
    layers.MaxPooling2D((2, 2)),
    layers.Conv2D(64, (3, 3), activation='relu'),
    layers.MaxPooling2D((2, 2)),
    layers.Flatten(),
    layers.Dense(128, activation='relu'),
    layers.Dense(64, activation='relu'),
    layers.Dense(10, activation='softmax')
])

Explicando cada camada:

  • Reshape((28, 28, 1)): adiciona um canal à imagem (necessário para CNNs), transformando (28, 28) em (28, 28, 1).

  • Conv2D(32, (3, 3), activation='relu'): aplica 32 filtros 3x3 sobre a imagem, extraindo características locais, como bordas e contornos.

  • MaxPooling2D((2, 2)): reduz a dimensionalidade ao pegar o valor máximo de cada região 2x2, diminuindo o custo computacional e preservando as características mais importantes.

  • Conv2D(64, (3, 3), activation='relu'): mais uma camada de convolução, agora com 64 filtros, extraindo características mais complexas.

  • MaxPooling2D((2, 2)): nova etapa de redução de dimensionalidade.

  • Flatten(): transforma o volume 3D resultante das camadas convolucionais em um vetor 1D para ser passado às camadas densas.

  • Dense(128, activation='relu'): camada densa com 128 neurônios, conectando todos os dados extraídos.

  • Dense(64, activation='relu'): camada intermediária com 64 neurônios.

  • Dense(10, activation='softmax'): camada de saída com 10 neurônios (um para cada dígito). A função softmax transforma os valores em probabilidades.

Compilando o Modelo

model.compile(optimizer='adam',
              loss='sparse_categorical_crossentropy',
              metrics=['accuracy'])

A explicação permanece igual à versão anterior:

  • Adam é um otimizador robusto e eficiente.

  • A função de perda escolhida é adequada para classificação com rótulos inteiros.

  • A acurácia é usada como métrica de desempenho.

Treinamento e Avaliação

history = model.fit(x_train, y_train, epochs=10, validation_split=0.1)
test_loss, test_acc = model.evaluate(x_test, y_test)
print(f"Precisão no teste: {test_acc:.2%}")

Com as CNNs, é comum atingir acurácias superiores a 98% no MNIST com apenas algumas épocas de treino.

As Redes Convolucionais (CNNs) são o padrão ouro em tarefas de visão computacional. Neste exemplo, vimos como elas extraem características automaticamente das imagens e atingem alta performance com pouca engenharia manual.

Resultados

Visualiza algumas previsões

# Faz previsões em 10 amostras do conjunto de teste
predictions = model.predict(x_test[:10])

# Visualiza as imagens e os rótulos previstos x verdadeiros
plt.figure(figsize=(10, 2))
for i in range(10):
    plt.subplot(1, 10, i + 1)
    plt.imshow(x_test[i], cmap='gray')
    pred_label = np.argmax(predictions[i])
    true_label = y_test[i]
    plt.title(f"P:{pred_label}\nT:{true_label}", fontsize=8)
    plt.axis('off')
plt.suptitle("Inferência com CNN: P = Previsto, T = Verdadeiro")
plt.show()

A rede convolucional apresenta alta precisão nas previsões, mesmo em amostras visuais variadas. Esse comportamento é esperado, pois as CNNs capturam padrões espaciais relevantes nas imagens, como contornos e formas, o que aumenta a robustez do modelo em tarefas visuais.

Curvas de Acurácia e Erro

As curvas de acurácia mostram o quão bem o modelo está acertando as classificações ao longo das épocas de treinamento. Já a curva de erro (loss) indica o quanto o modelo está errando — ou seja, a diferença entre as previsões e os valores reais.

Em modelos com CNNs, é comum observar acurácia mais alta e erro mais baixo, tanto em treino quanto em validação, se comparado a modelos puramente densos (sem convolução).

# Plota curvas de acurácia e perda
plt.figure(figsize=(12, 4))

# Acurácia
plt.subplot(1, 2, 1)
plt.plot(history.history['accuracy'], label='Treinamento')
plt.plot(history.history['val_accuracy'], label='Validação')
plt.title('Acurácia durante o Treinamento')
plt.xlabel('Época')
plt.ylabel('Acurácia')
plt.legend()

# Erro (Loss)
plt.subplot(1, 2, 2)
plt.plot(history.history['loss'], label='Treinamento')
plt.plot(history.history['val_loss'], label='Validação')
plt.title('Erro (Loss) durante o Treinamento')
plt.xlabel('Época')
plt.ylabel('Loss')
plt.legend()

plt.tight_layout()
plt.show()

As curvas geralmente mostram:

  • Acurácia acima de 98% no conjunto de treino e validação após poucas épocas.

  • Redução consistente da perda (loss), com sinais de boa generalização.

Matriz de Confusão

A matriz de confusão permite analisar detalhadamente os acertos e erros do modelo para cada classe. As linhas representam as classes reais e as colunas as classes previstas. Os valores na diagonal indicam acertos (classe prevista = classe real), enquanto os valores fora da diagonal mostram os erros de classificação.

Com uma CNN bem treinada, a matriz tende a ficar altamente concentrada na diagonal, demonstrando precisão consistente em todas as classes.

from sklearn.metrics import confusion_matrix, ConfusionMatrixDisplay

# Faz previsões no conjunto completo de teste
y_pred = model.predict(x_test)
y_pred_classes = np.argmax(y_pred, axis=1)

# Gera e exibe a matriz de confusão
cm = confusion_matrix(y_test, y_pred_classes)
disp = ConfusionMatrixDisplay(confusion_matrix=cm, display_labels=range(10))
disp.plot(cmap='Blues', values_format='d')
plt.title("Matriz de Confusão - CNN no MNIST")
plt.show()

Com o uso de camadas convolucionais, o modelo costuma errar menos em dígitos com formatos semelhantes (como 4 e 9, ou 3 e 5), pois aprende a diferenciar padrões finos com maior eficácia do que redes densas simples.

Exercício Prático: Comparando Arquiteturas com e sem Camadas Convolucionais no Fashion MNIST#

Neste exercício, você irá comparar duas arquiteturas de redes neurais aplicadas ao dataset Fashion MNIST:

  1. Uma rede neural densa (sem convoluções), que você já implementou anteriormente.

  2. Uma rede neural com camadas convolucionais (CNNs), usada frequentemente em tarefas de visão computacional.

O objetivo é entender como a adição de camadas convolucionais influencia na acurácia, erro e qualidade das previsões, comparando as curvas de aprendizado e as matrizes de confusão de ambas as abordagens.

Etapas

  • Carregue o dataset Fashion MNIST

  • Normalize os dados para que os pixels fiquem no intervalo [0, 1]

  • Visualize 10 imagens do conjunto de treino

  • Implemente duas redes:

    • (a) Rede totalmente densa (sem convolução)

    • (b) Rede com camadas convolucionais

  • Treine ambas as redes com o mesmo otimizador (escolha um, como adam ou sgd)

  • Compare os resultados:

    • Curvas de acurácia e perda

    • Matrizes de confusão

Escreva uma análise comparando os resultados:

  • Qual modelo teve melhor acurácia e menor perda?

  • A matriz de confusão de qual modelo apresentou menos erros?

  • O uso de camadas convolucionais fez diferença? Justifique com base nos resultados observados.

Ferramentas para Anotação de Dados#

A anotação de dados é essencial para preparar conjuntos de imagens para tarefas de detecção e classificação. A tabela abaixo compara algumas das ferramentas mais utilizadas — gratuitas, open-source ou comerciais — considerando características como tipos de anotação, suporte a automação, hospedagem e formatos de exportação:

Ferramenta

Tipo

Hospedagem

Tipos de Anotação

Recursos Adicionais

Formatos de Exportação

Roboflow

Freemium

Nuvem

Bounding boxes, segmentação

Augmentations automáticas, versionamento, integração com Git

YOLO, COCO, Pascal VOC, CSV

Labelbox

Comercial

Nuvem

Bounding boxes, polígonos, keypoints, segmentação

Automação com IA, revisão em equipe, API REST

COCO, JSON, outros customizáveis

VGG Image Annotator (VIA)

Gratuito, open-source

Local (navegador)

Bounding boxes, polígonos, pontos

Sem necessidade de servidor, extensível com JS

JSON, CSV, customizados

SuperAnnotate

Freemium

Nuvem

Bounding boxes, segmentação, keypoints

IA assistida, dashboards, controle de papéis, CI/CD

YOLO, COCO, outros

MakeSense.ai

Gratuito

Navegador

Bounding boxes, polígonos, keypoints

Uso anônimo, sem upload para servidores

YOLO, VOC XML, VGG JSON, CSV

Label Studio

Gratuito, open-source

Local ou Nuvem

Multimodal (imagem, texto, áudio, vídeo)

Projetos via JSON, auto-label com IA, self-hosted

JSON, CSV, customizável

CVAT

Gratuito, open-source

Local ou Nuvem (Docker)

Bounding boxes, segmentação, keypoints, tracking

Suporte colaborativo, integração com OpenVINO, plugins

COCO, YOLO, XML, etc.

Scalabel

Gratuito, open-source

Local ou Nuvem

Bounding boxes, segmentação, tracking

Interface web simples, suporte a vídeo e 3D

JSON, COCO

Kili Technology

Comercial

Nuvem

Bounding boxes, texto, áudio, vídeo

IA assistida, controle de qualidade, revisão

JSON, outros

Observações:

  • Hospedagem: Algumas ferramentas permitem uso 100% local (ex: VIA, CVAT), ideal para contextos com restrições de privacidade.

  • Anotação Multimodal: Ferramentas como Label Studio e Kili oferecem suporte além de imagens, como texto e áudio.

  • Automação: Roboflow, SuperAnnotate e Kili oferecem pré-anotação com IA, útil para acelerar o processo em grandes datasets.

  • Exportação: A maioria suporta formatos padrão como YOLO, COCO e Pascal VOC.

🧠 Exercício: Coleta e Anotação de Imagens para Detecção de Objetos#

Contexto do Problema:

Neste exercício, você irá simular o início da construção de um dataset personalizado para treinar um modelo de detecção de objetos baseado no YOLOv11. A proposta é escolher um objeto simples e comum (ex: garrafa, celular, copo, livro, óculos, etc.), coletar imagens reais da internet e realizar manualmente a anotação das regiões de interesse utilizando três ferramentas diferentes.

Tarefas:

  • Escolha um objeto simples, preferencialmente presente nas categorias do dataset COCO.

  • Busque e salve 15 imagens reais contendo esse objeto (Google Imagens, Bing, Unsplash, Pixabay, etc.).

  • Anote as imagens utilizando três ferramentas diferentes de sua escolha, como: Roboflow, MakeSense.ai, Label Studio, CVAT, VGG Image Annotator (VIA), entre outras.

  • Exporte as anotações no formato YOLOv11 (arquivo .txt com a notação padrão).

  • Compare os arquivos gerados, observando se existem diferenças na estrutura ou formato entre as ferramentas.

Requisitos Técnicos:

  • As imagens devem ser nomeadas de forma padronizada: img01.jpg, img02.jpg, …, img15.jpg.

  • As anotações devem conter apenas uma classe (o objeto escolhido).

  • Para cada ferramenta utilizada, organize os arquivos em uma pasta separada: roboflow/, makesense/, labelstudio/, etc.

  • Verifique se o formato YOLO está correto:

    <classe> <x_center_norm> <y_center_norm> <width_norm> <height_norm>
    

    (Todas as coordenadas devem estar normalizadas entre 0 e 1.)

Tarefa Final: Visualização da Anotação

Desenvolva um pequeno programa em Python que:

  • Receba como entrada uma imagem e seu arquivo de anotação (no formato YOLO).

  • Plote a imagem com a caixa delimitadora desenhada sobre o objeto anotado, como uma máscara ou retângulo.

Exemplo de bibliotecas úteis: matplotlib, opencv-python.

(Opcional):

Grave um pequeno vídeo demonstrando a ferramenta que você considerou mais interessante, explicando por quê. Inclua também a execução do seu código de visualização da anotação.

Observações Importantes:

  • Verifique se a ferramenta escolhida oferece exportação gratuita das anotações.

  • Prefira imagens com licença aberta ou de uso educacional. Sites como Unsplash, Pexels ou Pixabay são boas opções.

  • Certifique-se de respeitar os direitos autorais ao utilizar imagens da internet.

Tutorial do YOLOv11#

Uma introdução rápida ao YOLOv11 para detecção de objetos, incluindo instalação, treinamento e inferência.

O YOLOv11 da Ultralytics é a versão mais recente do renomado modelo de detecção de objetos em tempo real e segmentação de imagens. Construído sobre avanços de ponta em aprendizado profundo e visão computacional, o YOLOv11 oferece um desempenho incomparável em termos de velocidade e precisão. Ele é projetado para ser fácil de usar, permitindo que tanto iniciantes quanto especialistas em aprendizado de máquina possam utilizá-lo eficazmente.

YOLOv11 para Detecção de Objetos#

Este tutorial fornece uma introdução rápida ao YOLOv11 para detecção de objetos, incluindo instalação, treinamento e interpretação dos logs.

Instalação

Primeiro, instale a biblioteca ultralytics, que contém a implementação do YOLOv11. Você pode instalá-la usando o pip:

!pip install ultralytics

Modelo e Dados

Antes de iniciar o treinamento, é importante entender os componentes utilizados:

  • yolo11n.pt: é a versão nano do YOLOv11, um modelo leve e otimizado para dispositivos com recursos limitados. Ideal para testes rápidos e prototipagem.

  • coco8.yaml: é um subconjunto do conjunto COCO (Common Objects in Context), com apenas 8 imagens e 80 classes. Ele é usado para fins de demonstração e testes rápidos, e já está embutido na biblioteca ultralytics.

Exemplo de Treinamento

Aqui está um exemplo de como treinar o modelo YOLOv11 usando esse conjunto de dados:

from ultralytics import YOLO
import matplotlib.pyplot as plt
import cv2

# Carregar o modelo YOLO versão nano (leve)
model = YOLO("yolo11n.pt")

# Treinar o modelo com o conjunto COCO8 por 3 épocas
model.train(data='coco8.yaml', epochs=3)

Resumo do Log Gerado

Durante o treinamento, o YOLOv11 exibe um log com informações úteis. A tabela abaixo resume os principais elementos:

Campo

Descrição

epoch

Época atual / total

gpu_mem

Memória de GPU usada (GB)

box_loss

Erro na predição das caixas delimitadoras

cls_loss

Erro na classificação dos objetos

dfl_loss

Erro na regressão refinada das caixas (Distribution Focal Loss)

instances

Número de objetos anotados no batch

size

Tamanho das imagens de entrada

metrics/precision

Precisão: taxa de detecções corretas

metrics/recall

Revocação: taxa de objetos corretamente detectados

metrics/mAP50

Média da precisão com IoU de 0.5

metrics/mAP50-95

Média da precisão com IoU de 0.5 a 0.95 (mais rigoroso e completo)

Essas métricas ajudam a monitorar o desempenho do modelo. Idealmente, as perdas (losses) devem diminuir e as métricas (mAP, precision, recall) devem aumentar ao longo das épocas.

Exemplo de Detecção de Objetos#

Depois de treinar o modelo, você pode usá-lo para detectar objetos em uma imagem:

from ultralytics import YOLO
import matplotlib.pyplot as plt
import cv2
import urllib.request

# Carregar o modelo YOLO
model = YOLO("yolo11s.pt")

# Prever a imagem com o modelo
#results = model("buses.jpeg")
# Baixar a imagem da URL
url = "http://farm9.staticflickr.com/8200/8282039486_c215950eba_z.jpg"
image_path = "image.jpg"
urllib.request.urlretrieve(url, image_path)

results = model(image_path)

# Obter a imagem original
img = cv2.imread(image_path)  # carregando a imagem original
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)  # converter de BGR para RGB para exibição no matplotlib

# Obter a imagem com os resultados
result_img = results[0].plot()  # retorna a imagem com as caixas delimitadoras
# Converter a imagem de BGR (OpenCV) para RGB (Matplotlib) para exibição correta
result_img_rgb = cv2.cvtColor(result_img, cv2.COLOR_BGR2RGB)

# Exibir as imagens antes e depois da detecção
fig, axs = plt.subplots(1, 2, figsize=(15, 7))

# Exibir a imagem original
axs[0].imshow(img)
axs[0].set_title('Imagem Original')
axs[0].axis('off')

# Exibir a imagem resultante
axs[1].imshow(result_img_rgb)
axs[1].set_title('Resultado da Detecção')
axs[1].axis('off')

plt.tight_layout()
plt.show()

Exemplo de Segmentação#

Você também pode usar o YOLOv11 para tarefas de segmentação:

from ultralytics import YOLO
import matplotlib.pyplot as plt
import cv2

# Carregar o modelo YOLO
model = YOLO("yolo11n-seg.pt")

# Prever a imagem com o modelo
#results = model("buses.jpeg")
# Baixar a imagem da URL
url = "http://farm9.staticflickr.com/8200/8282039486_c215950eba_z.jpg"
image_path = "image.jpg"
urllib.request.urlretrieve(url, image_path)

results = model(image_path)

# Obter a imagem original
img = cv2.imread(image_path)  # carregando a imagem original
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)  # converter de BGR para RGB para exibição no matplotlib

# Obter a imagem com os resultados
result_img = results[0].plot()  # retorna a imagem com as caixas delimitadoras

# Converter a imagem de BGR (OpenCV) para RGB (Matplotlib) para exibição correta
result_img_rgb = cv2.cvtColor(result_img, cv2.COLOR_BGR2RGB)

# Exibir as imagens antes e depois da detecção
fig, axs = plt.subplots(1, 2, figsize=(15, 7))

# Exibir a imagem original
axs[0].imshow(img)
axs[0].set_title('Imagem Original')
axs[0].axis('off')

# Exibir a imagem resultante
axs[1].imshow(result_img_rgb)
axs[1].set_title('Resultado da Segmentação')
axs[1].axis('off')

plt.tight_layout()
plt.show()

Exemplo de Estimativa de Pose#

O YOLOv11 também pode ser usado para estimativa de pose:

from ultralytics import YOLO
import cv2
import matplotlib.pyplot as plt
import cv2
import urllib.request

# Carregar o modelo
model = YOLO("yolo11n-pose.pt")

# Fazer a predição na imagem
url = "http://farm4.staticflickr.com/3313/5727568334_874c48ec8e_z.jpg"
image_path = "image.jpg"
urllib.request.urlretrieve(url, image_path)

results = model(image_path)

# Obter a imagem original
img = cv2.imread(image_path)  # carregando a imagem original
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)  # converter de BGR para RGB para exibição no matplotlib

# Obter a imagem original e os resultados
result_img = results[0].plot()  # Gera uma imagem anotada com bounding boxes e keypoints

# Converter a imagem de BGR (OpenCV) para RGB (Matplotlib) para exibição correta
result_img_rgb = cv2.cvtColor(result_img, cv2.COLOR_BGR2RGB)

# Exibir as imagens antes e depois da detecção
fig, axs = plt.subplots(1, 2, figsize=(15, 7))

# Exibir a imagem original
axs[0].imshow(img)
axs[0].set_title('Imagem Original')
axs[0].axis('off')

# Exibir a imagem resultante
axs[1].imshow(result_img_rgb)
axs[1].set_title('Resultado da Pose')
axs[1].axis('off')

plt.tight_layout()
plt.show()

Aplicação em um Vídeo#

Você também pode aplicar a detecção de objetos em vídeos:

from ultralytics import YOLO
import cv2
import numpy as np

# Carregar o modelo YOLO treinado
model = YOLO("yolo11n.pt")

# Caminhos dos vídeos
input_video_path = "videoIN.mp4"
output_video_path = 'videoOUT.mp4'

# Carregar o vídeo
cap = cv2.VideoCapture(input_video_path)

# Configurar o vídeo de saída com a mesma taxa de quadros e resolução do vídeo de entrada
fourcc = cv2.VideoWriter_fourcc(*'mp4v')
out = cv2.VideoWriter(output_video_path, fourcc, cap.get(cv2.CAP_PROP_FPS),
                      (int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)), int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))))

while cap.isOpened():
    ret, frame = cap.read()
    if not ret:
        break

    # Realizar a segmentação no frame
    results = model(frame)

    # Extrair o frame com segmentação anotada
    annotated_frame = results[0].plot()  # `plot()` retorna a imagem com a segmentação aplicada

    # Escrever o frame anotado no vídeo de saída
    out.write(annotated_frame)

# Liberar os objetos
cap.release()
out.release()
cv2.destroyAllWindows()

print(f"Vídeo com segmentação salvo em: {output_video_path}")

Configurações avançadas do Yolo#

Abaixo, apresento uma tabela atualizada com as principais configurações avançadas que podem ser utilizadas tanto no treinamento quanto na predição com o YOLOv11, abrangendo todas as tarefas suportadas.

Tabela de Configurações Avançadas do YOLOv11

Parâmetro

Descrição

Valor Padrão

Treinamento

Predição

Tarefas Suportadas

imgsz

Tamanho da imagem (pode ser um valor único ou uma tupla).

640

Todas

batch

Tamanho do lote por GPU durante o treinamento.

16

Todas

epochs

Número de épocas para o treinamento.

100

Todas

optimizer

Otimizador utilizado (e.g., SGD, Adam, AdamW, RMSProp).

‘auto’

Todas

lr0

Taxa de aprendizado inicial.

0.01

Todas

momentum

Momentum para otimizadores baseados em momentum.

0.937

Todas

weight_decay

Decaimento de peso (regularização L2).

0.0005

Todas

warmup_epochs

Número de épocas de aquecimento para o ajuste gradual da taxa de aprendizado.

3

Todas

patience

Número de épocas sem melhoria antes de interromper o treinamento antecipadamente.

100

Todas

pretrained

Utilizar pesos pré-treinados (True, False ou caminho para o modelo).

True

Todas

conf

Threshold de confiança para filtrar detecções.

0.25

Detecção, Segmentação, Pose, OBB

iou

Threshold de IoU para Non-Maximum Suppression (NMS).

0.7

Detecção, Segmentação, Pose, OBB

max_det

Número máximo de detecções por imagem.

300

Detecção, Segmentação, Pose, OBB

save_period

Intervalo de épocas para salvar checkpoints durante o treinamento.

-1

Todas

augment

Aplicar aumentos de dados durante a inferência.

False

Todas

visualize

Visualizar mapas de características intermediários para depuração.

False

Todas

flipud

Probabilidade de aplicar flip vertical durante o aumento de dados.

0.0

Todas

fliplr

Probabilidade de aplicar flip horizontal durante o aumento de dados.

0.5

Todas

degrees

Ângulo máximo para rotação aleatória durante o aumento de dados.

0.0

Todas

translate

Fração máxima para translação aleatória durante o aumento de dados.

0.1

Todas

scale

Fator de escala para aumento de dados.

0.5

Todas

shear

Ângulo máximo para cisalhamento durante o aumento de dados.

0.0

Todas

hsv_h, hsv_s, hsv_v

Ajustes de matiz, saturação e valor para aumento de dados no espaço HSV.

0.015, 0.7, 0.4

Todas

copy_paste

Aplicar técnica de copy-paste durante o aumento de dados.

0.0

Detecção, Segmentação

mask_ratio

Threshold para binarização de máscaras em segmentação.

0.5

Segmentação

kpt_thr

Threshold de confiança para keypoints em estimativa de pose.

0.2

Pose Estimation

resume

Retomar treinamento a partir de um checkpoint salvo.

False

Todas

project

Nome do diretório de projeto para salvar resultados.

‘runs’

Todas

name

Nome da execução específica dentro do projeto.

‘exp’

Todas

exist_ok

Permitir sobrescrever diretórios existentes.

False

Todas

Tarefas Suportadas pelo YOLOv11

O YOLOv11 é uma estrutura versátil que suporta as seguintes tarefas de visão computacional:

  • Detecção de Objetos: Identificação e localização de objetos em imagens ou vídeos.

  • Segmentação de Instâncias: Delineamento preciso dos contornos de objetos detectados.

  • Classificação de Imagens: Categorização de imagens em classes predefinidas.

  • Estimativa de Pose: Detecção e rastreamento de pontos-chave em corpos humanos.

  • Detecção Orientada (OBB): Detecção de objetos com orientação rotacional para maior precisão.

Cada uma dessas tarefas pode ser executada utilizando modelos específicos do YOLOv11, como yolo11n.pt para detecção, yolo11n-seg.pt para segmentação, yolo11n-cls.pt para classificação, yolo11n-pose.pt para estimativa de pose e yolo11n-obb.pt para detecção orientada.

Conhecendo o YOLOE#

Para uma compreensão mais aprofundada, consulte o artigo “YOLOE: Real-Time Seeing Anything”.

YOLOE (You Only Look Once – Everything) é um modelo de detecção e segmentação de objetos em tempo real que permite identificar qualquer objeto com base em:

  • Prompt de texto (ex: “gato”, “bicicleta”)

  • Prompt visual (ex: imagem de referência)

  • Sem prompt (modo “ver tudo”)

Ele combina a arquitetura eficiente do YOLOv10 com técnicas de aprendizado zero-shot, permitindo detectar objetos fora das categorias pré-definidas.

Instalação

Instale o pacote Ultralytics com:

pip install ultralytics

Para verificar a instalação e obter mais detalhes, consulte o guia de instalação da Ultralytics.

Como usar o YOLOE

from ultralytics import YOLOE

# Carregar o modelo YOLOE pré-treinado
model = YOLOE('yoloe.pt')
  • Inferência sem prompt (modo “ver tudo”):

# Detectar todos os objetos sem fornecer um prompt
results = model.predict(source='imagem.jpg')
  • Inferência com prompt de texto:

# Detectar objetos com base em um prompt de texto
results = model.predict(source='imagem.jpg', prompt='cachorro')
  • Inferência com prompt visual:

# Detectar objetos com base em uma imagem de referência
results = model.predict(source='imagem.jpg', prompt='referencia.jpg')

Exemplos de uso:

  • Prompt free - Detectar todos os objetos sem fornecer um prompt:

Download da imagem para testes

# Carregando a modelo
model = YOLOE("yoloe-11s-seg-pf.pt")  # load a model. 11l -> muda para large

# testando em uma iamgem
results = model.predict("coco_crash.jpg")

# Exibindo os resultados
results[0].show()
AI

Vamos agora utilizar um prompt:

Download da imagem para testes

from ultralytics import YOLOE

# Initialize a YOLOE model
model = YOLOE("yoloe-11l-seg.pt")  # ou selecione yoloe-11s/m-seg.pt para diferentes tamanhos

# Escrevendo prompts
names = ["person", "skate"]
#names = ["person on the sidewalk"]
model.set_classes(names, model.get_text_pe(names))

# Executando a detecção com a imagem passada
results = model.predict("coco_sk8.jpg")

# Show results
results[0].show()

Exercício: Cálculo do IoU (Intersection over Union)#

O Intersection over Union (IoU) é uma métrica essencial em tarefas de detecção de objetos na visão computacional. Ela mede o quanto duas caixas delimitadoras (bounding boxes) se sobrepõem.

Essa métrica é uma aplicação do Índice de Jaccard, utilizado originalmente para medir a similaridade entre conjuntos. No contexto de visão computacional, aplicamos o conceito às áreas de duas regiões:

\[ IoU = \frac{\text{Área da Interseção}}{\text{Área da União}} \]
  • Interseção: área em que as duas caixas se sobrepõem.

  • União: soma das áreas das duas caixas, subtraindo a área em comum (para não contar duas vezes).

O valor do IoU varia entre 0 e 1:

  • 0: sem sobreposição.

  • 1: sobreposição perfeita.

Representação das Caixas

As caixas são representadas por seus cantos superior esquerdo e inferior direito:

[x1_a, y1_a, x2_a, y2_a, x1_b, y1_b, x2_b, y2_b]

Ou seja:

  • x1_a, y1_a: coordenadas do canto superior esquerdo da Caixa A

  • x2_a, y2_a: coordenadas do canto inferior direito da Caixa A

  • x1_b, y1_b: coordenadas do canto superior esquerdo da Caixa B

  • x2_b, y2_b: coordenadas do canto inferior direito da Caixa B

Exemplo

dados = [2, 2, 5, 5, 4, 3, 7, 6]
  • Caixa A: (2, 2) até (5, 5)

  • Caixa B: (4, 3) até (7, 6)

AI

Tarefa

  • Calcule a área de interseção entre as duas caixas.

  • Calcule a área da união.

  • Use a fórmula do IoU para obter o valor final.

  • (Opcional, mas altamente recomendado): plote as duas caixas em um gráfico 2D para visualizar claramente a interseção.

📝 Dica: use bibliotecas como matplotlib para desenhar as caixas e facilitar a interpretação.

Função a implementar

def calcular_iou(dados):
    """
    Calcula o Intersection over Union entre duas caixas a partir de uma lista com 8 valores.

    Parâmetros:
        dados: lista com 8 elementos no formato
               [x1_a, y1_a, x2_a, y2_a, x1_b, y1_b, x2_b, y2_b]

    Retorno:
        IoU: float
    """
    # Sua implementação aqui
    pass

Testes

# Teste 1
Entrada: 2, 2, 5, 5, 4, 3, 7, 6
Saída: 0.12

# Teste 2
Entrada: 1, 1, 4, 4, 1, 1, 4, 4
Saída: 1.00

# Teste 3
Entrada: 0, 0, 6, 6, 2, 2, 4, 4
Saída: 0.11

# Teste 4
Entrada: 0, 0, 2, 2, 3, 3, 5, 5
Saída: 0.0

Exercício Yoloe#

Contexto do Problema

Você já conseguiu usar o modelo YOLOE para detectar pessoas e skates em uma imagem, mas agora precisa filtrar apenas aquela(s) pessoa(s) que realmente está(ão) sobre o skate. Para isso, você vai implementar uma função que, a partir das caixas delimitadoras (bounding boxes) retornadas pelo modelo para as classes “person” e “skateboard”, calcula a sobreposição (IoU) e retorna somente as pessoas cujo box se sobrepõe suficientemente a algum skate.

AI

💡 LLMs e Gemini para Visão Computacional#

O que são LLMs?

LLMs (Large Language Models) são modelos de linguagem de larga escala, como GPT (OpenAI), Gemini (Google DeepMind), entre outros. Esses modelos são treinados com grandes volumes de texto e possuem capacidades notáveis para:

  • Compreender e gerar linguagem natural;

  • Traduzir idiomas;

  • Escrever e corrigir códigos;

  • Responder perguntas;

  • E, mais recentemente, interpretar diferentes modalidades de dados como imagens, vídeos e áudios (multimodalidade).

Gemini: IA Multimodal do Google

Gemini é a família de modelos de IA generativa do Google, desenvolvida pelo Google DeepMind. Os modelos mais recentes, como o Gemini 1.5, 2.0 e 2.5, são multimodais, ou seja, aceitam como entrada:

  • Texto,

  • Imagens,

  • Vídeos,

  • Áudios,

  • Documentos PDF e outros formatos.

Você pode experimentar o Gemini diretamente no Google AI Studio, um ambiente web intuitivo para testar prompts, gerar conteúdos e interagir com modelos de forma visual e interativa.

Aplicações em Visão Computacional

Com o uso da API google.generativeai, é possível aplicar os modelos Gemini para:

  • Descrever imagens com riqueza de detalhes;

  • Detectar objetos, pessoas, ambientes e até emoções visuais;

  • Analisar conteúdo técnico como:

    • Exames médicos (ex: radiografias),

    • Plantas arquitetônicas,

    • Mapas e imagens científicas.

Exemplo Prático: Descrição Automática de Imagem com Gemini + Python

Neste exemplo, vamos:

  • Instalar bibliotecas necessárias;

  • Configurar a chave de API do Google;

  • Carregar uma imagem do Google Drive;

  • Enviar essa imagem para o modelo Gemini com uma pergunta;

  • Obter uma descrição gerada automaticamente.

Código Comentado

# Instalação das bibliotecas da API Gemini
!pip install -U -q "google"
!pip install -U -q "google.genai"

# Bibliotecas necessárias
import base64
import os
from PIL import Image
from google import genai
from google.genai import types
from google.colab import userdata, drive

# Monta o Google Drive para acessar a imagem
drive.mount('/content/drive')

# Configura a chave da API (deve estar cadastrada no ambiente do Colab)
os.environ["GEMINI_API_KEY"] = userdata.get("GOOGLE_API_KEY")

def generate():
    # Caminho da imagem no seu Google Drive
    image_path = '/content/drive/MyDrive/Google AI Studio/w1.png'

    # Tenta abrir a imagem usando PIL (Pillow)
    try:
        image = Image.open(image_path)
        print(f"✅ Imagem carregada com sucesso: {image_path}")
    except FileNotFoundError:
        print("❌ Erro: Arquivo de imagem não encontrado.")
        return
    except Exception as e:
        print(f"❌ Erro ao abrir a imagem: {e}")
        return

    # Inicializa o cliente da API Gemini
    try:
        client = genai.Client(api_key=os.environ.get("GEMINI_API_KEY"))
        model = "gemini-2.0-flash-exp"  # Versão rápida e multimodal do Gemini
    except Exception as e:
        print(f"❌ Erro ao configurar a API: {e}")
        return

    try:
        print("🧠 Gerando descrição da imagem...")

        # Lê os bytes da imagem para envio à API
        with open(image_path, 'rb') as f:
            image_data = f.read()

        # Cria a mensagem com duas partes: a imagem e a instrução textual
        contents = [
            types.Content(
                role="user",
                parts=[
                    types.Part.from_bytes(data=image_data, mime_type="image/png"),
                    types.Part.from_text("Descreva esta imagem em detalhes."),
                ],
            ),
        ]

        # Define configurações da geração, incluindo uma instrução de sistema
        generate_content_config = types.GenerateContentConfig(
            response_mime_type="text/plain",
            system_instruction=[
                types.Part.from_text("Você é um assistente especializado em descrição detalhada de imagens."),
            ],
        )

        # Exibe cabeçalho da resposta
        print("\n" + "="*50)
        print("📷 DESCRIÇÃO DA IMAGEM:")
        print("="*50)

        # Geração da resposta em streaming (recebe aos poucos)
        for chunk in client.models.generate_content_stream(
            model=model,
            contents=contents,
            config=generate_content_config,
        ):
            print(chunk.text, end="")  # Imprime a resposta conforme ela chega

        print("\n" + "="*50)
        print("✅ Fim da descrição.")

    except Exception as e:
        print(f"❌ Erro ao gerar descrição: {e}")

# Executa o programa principal
if __name__ == "__main__":
    if not os.environ.get("GEMINI_API_KEY"):
        print("⚠️ Erro: A chave da API não está configurada.")
    else:
        generate()

Dicas e Explorações Adicionais

  • Teste diferentes modelos como gemini-pro, gemini-2.5-flash, etc.

  • Altere o prompt textual para variar o foco da descrição (ex: “Descreva o humor das pessoas”, “Liste todos os objetos visíveis”, “Explique o que pode estar acontecendo na cena”).

  • Modifique parâmetros da geração (como temperatura, para maior criatividade).

  • Use imagens diferentes, incluindo diagramas, fotos pessoais, mapas, imagens científicas.

Exercício – Interface em Gradio para chat com imagem#

Utilizando um exemplo de código disponível como modelo, crie uma interface gráfica interativa usando a biblioteca Gradio que funcione como uma espécie de ChatGPT para conversar sobre uma imagem.

A interface deve permitir que o usuário carregue uma imagem, faça perguntas relacionadas a essa imagem, e receba respostas baseadas no conteúdo visual e no contexto das perguntas anteriores, mantendo um histórico da conversa.

Introdução às Redes Generativas#

As redes generativas (ou modelos generativos) são algoritmos de aprendizado de máquina que tentam capturar a distribuição dos dados de treinamento para, a partir disso, gerar novas amostras semelhantes às originais. Enquanto modelos discriminativos aprendem a distinguir categorias (por exemplo, “é gato” ou “não é gato”), modelos generativos buscam entender “como” os dados foram criados, de modo a sintetizar exemplares que sigam o mesmo padrão estatístico.

Por que usar modelos generativos?#

  • Criação de Conteúdo: gerar imagens, texto ou áudio novos (arte, roteiros, trilhas sonoras).

  • Aumento de Dados: sintetizar exemplos para treinar outros modelos em cenários com poucos dados reais.

  • Compressão e Representação: aprender uma representação latente que capture as características principais dos dados (por exemplo, em autoencoders).

  • Análise de Distribuição: avaliar se certas regiões de espaço de dados são pouco exploradas ou gerar variações seguras para testes.

Principais Famílias de Modelos Generativos#

  • Modelos de Mistura Gaussiana (GMM)

    • Ideia principal: aproximam a distribuição dos dados por uma soma de distribuições normais multivariadas.

    • Semântica prática: cada componente gaussiano “modela” um cluster nos dados; a mistura (peso de cada componente) explica a probabilidade final.

    • Pontos-chave:

      • Fácil de entender e implementar em baixa dimensão.

      • Não escala bem para imagens de alta resolução.

      • Serve como introdução conceitual sobre “estimativa de densidade”.

  • Variational Autoencoders (VAEs)

    • Ideia principal: combinam um encoder que mapeia dados de entrada \(x\) para uma distribuição latente \(q_\phi(z \mid x)\) (normalmente \(\mathcal{N}(\mu, \sigma^2)\)) e um decoder que reconstrói \(x\) a partir de \(z\).

    • Objetivo de Treinamento: otimizar um limite inferior da evidência (ELBO), composto por:

      1. Automínimo erro de reconstrução (por exemplo, MSE).

      2. Divergência de Kullback–Leibler (\(\mathrm{KL}\)) entre \(q_\phi(z \mid x)\) e uma distribuição pré-definida \(p(z)\), tipicamente \(\mathcal{N}(0,I)\).

    • Pontos-chave:

      • Permitem amostrar diretamente do espaço latente (gera variações suaves).

      • Produzem amostras que podem ficar um pouco “borradas” em imagens.

      • Treinamento relativamente estável e eficiente em comparação com GANs.

  • Redes Generativas Adversariais (GANs)

    • Ideia principal: GANs consistem em duas redes neurais que competem entre si em um jogo de soma zero. A primeira, chamada Gerador (\(G\)), busca criar amostras que pareçam reais; a segunda, chamada Discriminador (\(D\)), tenta distinguir o que é real do que foi gerado. Essa competição força ambas as redes a melhorarem constantemente.

      • Gerador (\(G\)): a partir de um vetor aleatório \(z\sim p(z)\), tenta gerar uma amostra \(G(z)\) que pareça real.

      • Discriminador (\(D\)): recebe amostras reais ou geradas e tenta distinguir “real” de “falso”.

    • O jogo minimax:

      \[ \min_G \max_D \; \mathbb{E}_{x\sim p_\text{data}}[\log D(x)] \;+\; \mathbb{E}_{z\sim p(z)}[\log(1 - D(G(z)))]. \]
    • Pontos-chave:

      • Podem gerar imagens nítidas e de alta resolução.

      • Exigem cuidado para evitar instabilidades (colapso de modo, onde o gerador produz poucas variações).

      • Sucesso em domínios como geração de rostos (e.g., StyleGAN) e arte abstrata.

  • Modelos de Difusão (Diffusion Models)

    • Ideia principal: Modelos de Difusão simulam um processo de ruído progressivo em uma imagem real e treinam uma rede neural para reverter esse ruído passo a passo, recuperando a imagem original. Esse processo permite gerar novas amostras a partir de puro ruído, com alta qualidade e estabilidade.

    • Processo para frente (“noising”): parte-se de uma amostra real \(x_0\) e, em cada passo \(t\), adiciona-se ruído gaussiano para obter \(x_t\). Quando \(t\) for grande (por ex., \(T\approx1000\)), \(x_T\) é praticamente ruído puro.

    • Processo reverso (“denoising”): uma rede neural (\(\epsilon_\theta\)) é treinada para remover ruído de \(x_t\) e recuperar \(x_{t-1}\), até chegar a \(x_0\).

    • Pontos-chave:

      • Geralmente mais estáveis que GANs.

      • Produzem amostras de altíssima qualidade (sem artefatos típicos de GANs).

      • Inferir uma imagem exige dezenas ou centenas de etapas de denoising, tornando a geração mais lenta.

Tabela comparativa dos principais modelos apresentados:

Modelo

Abordagem

Vantagens Principais

Limitações Notáveis

Aplicações Típicas

GMM

Mistura de gaussianas

Simples, interpretável, útil para dados de baixa dimensão

Escala mal para imagens ou dados complexos

Clusterização, estimativa de densidade básica

VAE

Codificação latente probabilística

Treinamento estável, interpolações suaves no espaço latente

Geração de imagens pode ser borrada

Reconstrução e geração de dados; compressão

GAN

Competição Gerador vs Discriminador

Gera imagens realistas e de alta qualidade

Instabilidade no treinamento, colapso de modo

Geração de faces (StyleGAN), arte, deepfakes

Modelos de Difusão

Ruído + reversão progressiva

Amostras de altíssima qualidade, sem artefatos típicos de GANs

Lento na geração (múltiplas etapas de denoising)

Imagens hiper-realistas, arte, medicina, vídeo (e.g. SDXL)

Observação: a seguir, focaremos em GANs e Stable Diffusion (um diffusion model voltado para texto → imagem).

Redes Generativas Adversariais (GANs)#

Conceito e Arquitetura Básica

As Redes Generativas Adversariais (GANs) são uma classe de modelos generativos compostos por duas redes neurais que competem entre si em um jogo adversarial: o Gerador tenta produzir amostras realistas, enquanto o Discriminador tenta distinguir se uma amostra é real ou foi gerada.

Gerador \(G_\theta\)

  • Recebe como entrada um vetor aleatório \(z \sim p(z)\), normalmente amostrado de uma distribuição gaussiana \(\mathcal{N}(0, I)\) ou uniforme.

  • Gera uma amostra sintética \(G(z)\), com aparência similar à de dados reais.

  • É geralmente implementado com redes convolucionais transpostas (ou técnicas de upsampling).

Discriminador \(D_\phi\)

  • Recebe como entrada uma amostra \(x\) (real ou gerada).

  • Retorna uma probabilidade \(D(x) \in [0, 1]\), indicando o quão “real” é a entrada.

  • É implementado como uma rede convolucional tradicional, atuando como um classificador binário.

Funções de Custo (Loss) e Treinamento

Notação de Esperança Matemática \(\mathbb{E}\)

A notação \(\mathbb{E}_{x \sim p(x)}[f(x)]\) representa a esperança matemática (ou média esperada) da função \(f(x)\) quando a variável \(x\) é amostrada de uma distribuição \(p(x)\). Na prática, essa média é estimada por um minibatch de amostras durante o treinamento.

Objetivo do Discriminador

O discriminador busca maximizar a seguinte função de custo:

\[ \mathcal{L}_D = \mathbb{E}_{x \sim p_\text{data}(x)}[\log D(x)] + \mathbb{E}_{z \sim p(z)}[\log(1 - D(G(z)))] \]

Onde:

  • \(p_\text{data}(x)\): distribuição real dos dados.

  • \(p(z)\): distribuição aleatória de entrada do gerador.

  • \(D(x)\): probabilidade atribuída pelo discriminador de que \(x\) seja real.

Objetivo do Gerador

O gerador busca minimizar a capacidade do discriminador de identificar amostras falsas. Existem duas versões da loss:

  • Versão original:

\[ \mathcal{L}_G = \mathbb{E}_{z \sim p(z)}[\log(1 - D(G(z)))] \]
  • Versão modificada (mais estável):

\[ \mathcal{L}_G = \mathbb{E}_{z \sim p(z)}[-\log D(G(z))] \]

Esta versão é mais comum na prática, pois evita gradientes muito pequenos nas fases iniciais do treinamento.

Treinamento Adversarial

O processo de treinamento alterna entre dois passos:

  1. Atualização de \(D_\phi\):

    • Amostra um batch de dados reais \(x \sim p_\text{data}(x)\).

    • Amostra um batch de ruído \(z \sim p(z)\), gera \(G(z)\).

    • Otimiza os parâmetros \(\phi\) do discriminador para maximizar \(\mathcal{L}_D\).

  2. Atualização de \(G_\theta\):

    • Gera novas amostras \(G(z)\) com \(z \sim p(z)\).

    • Otimiza os parâmetros \(\theta\) do gerador para minimizar \(\mathcal{L}_G\).

Jogo Minimax

O treinamento das GANs é formulado como um jogo de soma zero entre duas redes com objetivos opostos:

\[ \min_G \max_D \; \mathbb{E}_{x \sim p_\text{data}(x)}[\log D(x)] + \mathbb{E}_{z \sim p(z)}[\log(1 - D(G(z)))] \]

Esse é o jogo minimax: o discriminador tenta maximizar a função, e o gerador tenta minimizá-la. No equilíbrio, \(G\) produz amostras indistinguíveis dos dados reais e \(D(x) \approx 0{,}5\).

Variações Populares de GANs

  • DCGAN (Deep Convolutional GAN): Estrutura convolucional profunda adaptada para imagens. Usa Strided Convolutions, Batch Normalization, e LeakyReLU.

  • WGAN (Wasserstein GAN): Substitui a função de perda original pela distância de Wasserstein, oferecendo estabilidade e interpretabilidade.

  • StyleGAN / StyleGAN2: Introduz camadas de controle de estilo e um mapeamento intermediário para o vetor \(z\), permitindo controle sobre características visuais como iluminação, expressão, e pose.

Exemplo Prático de GAN em PyTorch

Abaixo, um exemplo mínimo para treinar um GAN em um conjunto de imagens (por exemplo, MNIST ou CIFAR-10). Aqui usamos MNIST (imagens 28×28 em escala de cinza) apenas para fins ilustrativos.

import os
import torch
import torch.nn as nn
import torch.optim as optim
from torchvision import datasets, transforms, utils
from torch.utils.data import DataLoader

# 1) CONFIGURAÇÕES GERAIS
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")  # Usa GPU se disponível
latent_dim = 100      # Dimensão do vetor latente (entrada do gerador)
batch_size = 128      # Tamanho do lote
lr = 0.0002           # Taxa de aprendizado
epochs = 30           # Número de épocas de treinamento
sample_dir = "samples"  # Pasta para salvar imagens geradas
os.makedirs(sample_dir, exist_ok=True)

# 2) CARREGAMENTO DO DATASET (MNIST)
transform = transforms.Compose([
    transforms.ToTensor(),                     # Converte imagens para tensores
    transforms.Normalize([0.5], [0.5])         # Normaliza para o intervalo [-1, 1]
])
dataset = datasets.MNIST(root="./data", train=True, download=True, transform=transform)
dataloader = DataLoader(dataset, batch_size=batch_size, shuffle=True)

# 3) DEFINIÇÃO DO GERADOR
class Generator(nn.Module):
    def __init__(self):
        super().__init__()
        self.net = nn.Sequential(
            nn.Linear(latent_dim, 256),
            nn.LeakyReLU(0.2, inplace=True),
            nn.Linear(256, 512),
            nn.BatchNorm1d(512),
            nn.LeakyReLU(0.2, inplace=True),
            nn.Linear(512, 1024),
            nn.BatchNorm1d(1024),
            nn.LeakyReLU(0.2, inplace=True),
            nn.Linear(1024, 28*28),  # Saída do gerador deve ter o tamanho da imagem do MNIST (784)
            nn.Tanh()  # Saída entre [-1, 1] para combinar com a normalização do dataset
        )

    def forward(self, z):
        img = self.net(z)  # Gera imagem a partir de vetor latente
        return img.view(-1, 1, 28, 28)  # Reshape para imagem (1 canal, 28x28)

# 4) DEFINIÇÃO DO DISCRIMINADOR
class Discriminator(nn.Module):
    def __init__(self):
        super().__init__()
        self.net = nn.Sequential(
            nn.Linear(28*28, 512),
            nn.LeakyReLU(0.2, inplace=True),
            nn.Linear(512, 256),
            nn.LeakyReLU(0.2, inplace=True),
            nn.Linear(256, 1),
            nn.Sigmoid()  # Produz probabilidade de ser imagem real
        )

    def forward(self, img):
        flat = img.view(-1, 28*28)  # Achata a imagem para vetor
        return self.net(flat)

# 5) INSTANCIAÇÃO DE MODELOS, FUNÇÃO DE PERDA E OTIMIZADORES
generator = Generator().to(device)
discriminator = Discriminator().to(device)

criterion = nn.BCELoss()  # Binary Cross Entropy para classificação binária (real ou falsa)
optim_G = optim.Adam(generator.parameters(), lr=lr, betas=(0.5, 0.999))
optim_D = optim.Adam(discriminator.parameters(), lr=lr, betas=(0.5, 0.999))

# 6) FUNÇÕES AUXILIARES PARA GERAR RÓTULOS
def real_labels(size):
    return torch.ones(size, 1, device=device)  # Rótulo "1" para imagens reais

def fake_labels(size):
    return torch.zeros(size, 1, device=device)  # Rótulo "0" para imagens falsas

# 7) LOOP DE TREINAMENTO DO GAN
for epoch in range(epochs):
    for i, (imgs, _) in enumerate(dataloader):
        batch_size_i = imgs.size(0)
        imgs = imgs.to(device)

        # ----- TREINAMENTO DO DISCRIMINADOR -----
        # Gera imagens falsas a partir de ruído
        z = torch.randn(batch_size_i, latent_dim, device=device)
        fake_imgs = generator(z)

        # Avalia imagens reais e falsas
        real_out = discriminator(imgs)
        fake_out = discriminator(fake_imgs.detach())  # Detach para não propagar gradientes para o gerador

        # Calcula a perda do discriminador
        loss_D_real = criterion(real_out, real_labels(batch_size_i))
        loss_D_fake = criterion(fake_out, fake_labels(batch_size_i))
        loss_D = (loss_D_real + loss_D_fake) / 2

        # Backpropagation e otimização do discriminador
        optim_D.zero_grad()
        loss_D.backward()
        optim_D.step()

        # ----- TREINAMENTO DO GERADOR -----
        # Tenta enganar o discriminador: quer que imagens falsas sejam classificadas como reais
        output = discriminator(fake_imgs)
        loss_G = criterion(output, real_labels(batch_size_i))  # Compara com rótulo real (1)

        # Backpropagation e otimização do gerador
        optim_G.zero_grad()
        loss_G.backward()
        optim_G.step()

        # Exibe progresso a cada 200 lotes
        if i % 200 == 0:
            print(f"Epoch [{epoch+1}/{epochs}]  Batch [{i}/{len(dataloader)}]  "
                  f"Loss D: {loss_D.item():.4f}, Loss G: {loss_G.item():.4f}")

    # ----- GERA E SALVA AMOSTRAS -----
    with torch.no_grad():
        z = torch.randn(16, latent_dim, device=device)
        sample_imgs = generator(z).cpu()
        sample_grid = utils.make_grid(sample_imgs, nrow=4, normalize=True)
        utils.save_image(sample_grid, f"{sample_dir}/epoch_{epoch+1:03d}.png")

print("Treinamento concluído! Amostras salvas na pasta 'samples/'.")

Resumo do que o código faz:#

  • Treina um GAN (Generative Adversarial Network) usando o conjunto de dados MNIST.

  • O gerador aprende a criar imagens falsas de dígitos manuscritos.

  • O discriminador tenta distinguir imagens reais (do MNIST) de imagens falsas (do gerador).

  • Ambos são treinados de forma adversarial: um tenta enganar o outro.

  • Ao final de cada época, são geradas 16 imagens para avaliação visual do progresso do gerador.

Modelos de Difusão e Stable Diffusion

O processo de diffusion models consiste em dois momentos principais: inserir ruído progressivamente numa imagem real e, em seguida, aprender a reverter esse processo para gerar novas amostras. Primeiro, parte-se de uma imagem original \(x_0\) e adiciona-se ruído gaussiano em vários passos \(t=1,2,\dots,T\), de modo que, quando \(t\) é grande, a imagem se transforma em praticamente puro ruído. Formalmente, cada passo é descrito como:

\[ x_t = \sqrt{\alpha_t}\,x_{t-1} + \sqrt{1 - \alpha_t}\,\epsilon_t,\quad \epsilon_t \sim \mathcal{N}(0,I), \]

onde \(\alpha_t \in (0,1)\) controla o nível de ruído em \(t\). À medida que avança-se de \(t=1\) até \(T\), o modelo aplica mais ruído até que \(x_T\) seja quase aleatório.

O segundo momento é o processo inverso, chamado denoising. Treina-se uma rede \(\epsilon_\theta(x_t, t)\) para estimar o ruído \(\epsilon_t\) presente em \(x_t\). Com essa predição \(\hat{\epsilon} = \epsilon_\theta(x_t, t)\), obtém-se uma aproximação de \(x_{t-1}\) a partir de:

\[ x_{t-1} \approx \frac{1}{\sqrt{\alpha_t}}\Bigl(x_t - \frac{1 - \alpha_t}{\sqrt{1 - \bar\alpha_t}}\,\hat{\epsilon}\Bigr) + \sigma_t\,n,\quad n\sim \mathcal{N}(0,I), \]

onde \(\bar\alpha_t = \prod_{s=1}^{t} \alpha_s\) e \(\sigma_t\) é um termo de ruído adaptativo para garantir diversidade nas amostras. Ao repetir esse passo regressivamente de \(t=T\) até \(t=1\), chega-se a uma nova imagem \(x_0\) gerada pelo modelo.

Esse tipo de arquitetura costuma ser mais estável que GANs porque não envolve um jogo adversarial direto. Além disso, tende a produzir imagens de alta qualidade e com grande diversidade, podendo ser facilmente condicionado a textos, máscaras, esboços ou outros sinais auxiliares.

Visão Geral do Stable Diffusion

O Stable Diffusion é uma implementação eficiente de diffusion models para geração de imagens a partir de texto, baseada em duas ideias-chave: trabalhar no espaço latente e usar um mecanismo de atenção para incorporar informações textuais.

  • Espaço Latente (Latent Diffusion) Em vez de aplicar difusão diretamente em pixels de alta resolução (por exemplo, tensores \(512 \times 512 \times 3\)), o Stable Diffusion aproveita um autoencoder pré-treinado (VAE) para comprimir cada imagem para um espaço de dimensão muito menor (por exemplo, \(\mathbb{R}^{4\times 64\times 64}\)). No treinamento, adiciona-se ruído apenas nesse espaço latente. Na geração, amostra-se um vetor latente puro (\(z_T \sim \mathcal{N}(0, I)\)) e aplica-se o processo de denoising nesse espaço reduzido. Ao fim, o decoder do VAE reconstrói a imagem em pixels na resolução original.

  • Condicionamento em Texto Um encoder de texto (tipicamente o CLIP Text Encoder) converte o prompt em embeddings. Esses vetores de texto entram em uma U-Net de denoising via cross-attention: em cada passo de remoção de ruído, a U-Net recebe o latente atual \(z_t\), o índice do passo \(t\) e os embeddings do texto, e produz uma estimativa de ruído \(\hat{\epsilon}\) alinhada ao significado do prompt. Para reforçar a fidelidade ao texto, adota-se classifier-free guidance, que mistura a predição de ruído com e sem contexto textual usando um fator de peso \(w\).

  • Pipeline de Geração

    1. Converter o prompt em token IDs e embeddings.

    2. Amostrar ruído latente \(z_T \sim \mathcal{N}(0,I)\) no espaço comprimido (por exemplo, \(\mathbb{R}^{4\times64\times64}\)).

    3. Para \(t = T, T-1, \dots, 1\):

      • A U-Net recebe \(z_t\), o passo \(t\) e os embeddings do texto.

      • Prediz o ruído \(\hat{\epsilon}\).

      • Atualiza \(z_{t-1}\) pela fórmula de denoising, usando \(\hat{\epsilon}\) e os coeficientes \(\alpha_t\).

    4. Quando \(z_0\) é obtido, passa-se pelo decoder do VAE para gerar a imagem final em alta resolução (por exemplo, \(512\times512\times3\)).

  • Vantagens do Stable Diffusion

    • Processar no espaço latente reduz dramaticamente o custo computacional comparado a difundir em pixels.

    • A qualidade das imagens geradas costuma ser muito alta, com poucos artefatos.

    • Além de texto→imagem, suporta tarefas como inpainting, outpainting e outras modalidades condicionadas (máscaras, rascunhos, etc.).

Exemplo Prático Funcional de Stable Diffusion

A seguir, um exemplo simples que já funciona em um notebook (local ou Colab). Ele usa a biblioteca diffusers para carregar um pipeline do Stable Diffusion, gerar uma imagem a partir de um prompt e exibi-la.

# Instalação das dependências (execute apenas uma vez no notebook/Colab)
!pip install -q diffusers transformers accelerate safetensors

import torch
from diffusers import StableDiffusionPipeline
from PIL import Image
import matplotlib.pyplot as plt
from IPython.display import display

# Verifica se há GPU disponível
device = "cuda" if torch.cuda.is_available() else "cpu"
print(f"Dispositivo em uso: {device}")

# Carrega o modelo Stable Diffusion 2.1
model_id = "stabilityai/stable-diffusion-2-1"
pipe = StableDiffusionPipeline.from_pretrained(
    model_id,
    torch_dtype=torch.float16 if device == "cuda" else torch.float32
).to(device)

# Define o prompt (descrição textual da imagem)
prompt = "Uma floresta encantada ao entardecer, estilo pintura digital"

# Parâmetros de geração
num_steps = 50         # Passos de denoising
guidance = 7.5         # Peso para aderência ao prompt

# Função para gerar imagem a partir do prompt
def gerar_imagem(prompt, steps=50, guidance_scale=7.5, height=512, width=512):
    with torch.autocast(device) if device == "cuda" else torch.no_grad():
        resultado = pipe(
            prompt=prompt,
            height=height,
            width=width,
            num_inference_steps=steps,
            guidance_scale=guidance_scale
        )
    return resultado.images[0]

# Função para exibir imagem (compatível com diferentes ambientes)
def exibir_imagem(imagem, titulo="Imagem Gerada"):
    try:
        # Método 1: Para Jupyter/Colab - usando matplotlib
        plt.figure(figsize=(10, 10))
        plt.imshow(imagem)
        plt.axis('off')
        plt.title(titulo)
        plt.show()
    except:
        try:
            # Método 2: Para Jupyter/Colab - usando IPython display
            display(imagem)
        except:
            try:
                # Método 3: Método padrão do PIL
                imagem.show()
            except:
                # Método 4: Salvar arquivo localmente
                nome_arquivo = "imagem_gerada.png"
                imagem.save(nome_arquivo)
                print(f"Imagem salva como: {nome_arquivo}")

# Geração e exibição da imagem
print("Gerando imagem... (pode levar alguns minutos)")
imagem = gerar_imagem(prompt, num_steps, guidance)

# Exibe a imagem usando múltiplos métodos
exibir_imagem(imagem, f"Prompt: {prompt}")

# Salva a imagem também
imagem.save("floresta_encantada.png")
print("Imagem salva como: floresta_encantada.png")

# Função adicional para gerar múltiplas imagens
def gerar_multiplas_imagens(prompt, quantidade=4, steps=50, guidance_scale=7.5):
    imagens = []
    for i in range(quantidade):
        print(f"Gerando imagem {i+1}/{quantidade}...")
        img = gerar_imagem(prompt, steps, guidance_scale)
        imagens.append(img)
        
        # Salva cada imagem
        nome_arquivo = f"imagem_{i+1}.png"
        img.save(nome_arquivo)
    
    # Exibe todas as imagens em uma grade
    fig, axes = plt.subplots(2, 2, figsize=(15, 15))
    fig.suptitle(f"Prompt: {prompt}", fontsize=16)
    
    for i, (img, ax) in enumerate(zip(imagens, axes.flat)):
        ax.imshow(img)
        ax.axis('off')
        ax.set_title(f"Variação {i+1}")
    
    plt.tight_layout()
    plt.show()
    
    return imagens

# Exemplo de uso da função para múltiplas imagens
# imagens_multiplas = gerar_multiplas_imagens(prompt, quantidade=4)

Outros Modelos de Diffusão Relevantes

  • DDPM (Denoising Diffusion Probabilistic Models): o design original, aplicando difusão diretamente em pixels.

  • DDIM (Denoising Diffusion Implicit Models): permite gerar amostras consistentes com muito menos passos via uma discretização alternativa.

  • Score-Based Models: estimam o gradiente do log-densidade \(\nabla_x \log p_t(x)\) em cada etapa, em vez de predizer explicitamente o ruído.

Essas variantes compartilham a mesma ideia central: adicionar ruído a partir de dados reais e então aprender a reverter esse processo, mas diferem na forma de parametrizar ou acelerar a sequência de passos. Cada uma pode ser adaptada a diferentes cenários de geração, seja por velocidade, qualidade ou requisitos de hardware.

Referências e Conteúdo Extra#