Capítulo 4: Deep Learning na Prática#
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.

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.

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:
CNN Explainer - Visualização interativa
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:

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:

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 |
Plataforma de ciência de dados com diversos datasets para competições e projetos. |
|
TensorFlow Datasets |
Biblioteca de datasets integrada à TensorFlow. |
|
ImageNet |
Banco de imagens grande e variado, comumente usado em visão computacional. |
|
UCI Machine Learning Repository |
Repositório de conjuntos de dados de várias áreas, mantido pela Universidade da Califórnia, Irvine. |
|
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:
Uma rede neural densa (sem convoluções), que você já implementou anteriormente.
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
ousgd
)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 |
---|---|---|---|---|---|
Freemium |
Nuvem |
Bounding boxes, segmentação |
Augmentations automáticas, versionamento, integração com Git |
YOLO, COCO, Pascal VOC, CSV |
|
Comercial |
Nuvem |
Bounding boxes, polígonos, keypoints, segmentação |
Automação com IA, revisão em equipe, API REST |
COCO, JSON, outros customizáveis |
|
Gratuito, open-source |
Local (navegador) |
Bounding boxes, polígonos, pontos |
Sem necessidade de servidor, extensível com JS |
JSON, CSV, customizados |
|
Freemium |
Nuvem |
Bounding boxes, segmentação, keypoints |
IA assistida, dashboards, controle de papéis, CI/CD |
YOLO, COCO, outros |
|
Gratuito |
Navegador |
Bounding boxes, polígonos, keypoints |
Uso anônimo, sem upload para servidores |
YOLO, VOC XML, VGG JSON, CSV |
|
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 |
|
Gratuito, open-source |
Local ou Nuvem (Docker) |
Bounding boxes, segmentação, keypoints, tracking |
Suporte colaborativo, integração com OpenVINO, plugins |
COCO, YOLO, XML, etc. |
|
Gratuito, open-source |
Local ou Nuvem |
Bounding boxes, segmentação, tracking |
Interface web simples, suporte a vídeo e 3D |
JSON, COCO |
|
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 bibliotecaultralytics
.
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 |
---|---|
|
Época atual / total |
|
Memória de GPU usada (GB) |
|
Erro na predição das caixas delimitadoras |
|
Erro na classificação dos objetos |
|
Erro na regressão refinada das caixas (Distribution Focal Loss) |
|
Número de objetos anotados no batch |
|
Tamanho das imagens de entrada |
|
Precisão: taxa de detecções corretas |
|
Revocação: taxa de objetos corretamente detectados |
|
Média da precisão com IoU de 0.5 |
|
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 |
|
---|---|---|---|---|---|---|
|
Tamanho da imagem (pode ser um valor único ou uma tupla). |
640 |
✅ |
✅ |
Todas |
|
|
Tamanho do lote por GPU durante o treinamento. |
16 |
✅ |
– |
Todas |
|
|
Número de épocas para o treinamento. |
100 |
✅ |
– |
Todas |
|
|
Otimizador utilizado (e.g., SGD, Adam, AdamW, RMSProp). |
‘auto’ |
✅ |
– |
Todas |
|
|
Taxa de aprendizado inicial. |
0.01 |
✅ |
– |
Todas |
|
|
Momentum para otimizadores baseados em momentum. |
0.937 |
✅ |
– |
Todas |
|
|
Decaimento de peso (regularização L2). |
0.0005 |
✅ |
– |
Todas |
|
|
Número de épocas de aquecimento para o ajuste gradual da taxa de aprendizado. |
3 |
✅ |
– |
Todas |
|
|
Número de épocas sem melhoria antes de interromper o treinamento antecipadamente. |
100 |
✅ |
– |
Todas |
|
|
Utilizar pesos pré-treinados (True, False ou caminho para o modelo). |
True |
✅ |
– |
Todas |
|
|
Threshold de confiança para filtrar detecções. |
0.25 |
– |
✅ |
Detecção, Segmentação, Pose, OBB |
|
|
Threshold de IoU para Non-Maximum Suppression (NMS). |
0.7 |
– |
✅ |
Detecção, Segmentação, Pose, OBB |
|
|
Número máximo de detecções por imagem. |
300 |
– |
✅ |
Detecção, Segmentação, Pose, OBB |
|
|
Intervalo de épocas para salvar checkpoints durante o treinamento. |
-1 |
✅ |
– |
Todas |
|
|
Aplicar aumentos de dados durante a inferência. |
False |
– |
✅ |
Todas |
|
|
Visualizar mapas de características intermediários para depuração. |
False |
✅ |
✅ |
Todas |
|
|
Probabilidade de aplicar flip vertical durante o aumento de dados. |
0.0 |
✅ |
– |
Todas |
|
|
Probabilidade de aplicar flip horizontal durante o aumento de dados. |
0.5 |
✅ |
– |
Todas |
|
|
Ângulo máximo para rotação aleatória durante o aumento de dados. |
0.0 |
✅ |
– |
Todas |
|
|
Fração máxima para translação aleatória durante o aumento de dados. |
0.1 |
✅ |
– |
Todas |
|
|
Fator de escala para aumento de dados. |
0.5 |
✅ |
– |
Todas |
|
|
Ângulo máximo para cisalhamento durante o aumento de dados. |
0.0 |
✅ |
– |
Todas |
|
|
Ajustes de matiz, saturação e valor para aumento de dados no espaço HSV. |
0.015, 0.7, 0.4 |
✅ |
– |
Todas |
|
|
Aplicar técnica de copy-paste durante o aumento de dados. |
0.0 |
✅ |
– |
Detecção, Segmentação |
|
|
Threshold para binarização de máscaras em segmentação. |
0.5 |
✅ |
✅ |
Segmentação |
|
|
Threshold de confiança para keypoints em estimativa de pose. |
0.2 |
✅ |
✅ |
Pose Estimation |
|
|
Retomar treinamento a partir de um checkpoint salvo. |
False |
✅ |
– |
Todas |
|
|
Nome do diretório de projeto para salvar resultados. |
‘runs’ |
✅ |
✅ |
Todas |
|
|
Nome da execução específica dentro do projeto. |
‘exp’ |
✅ |
✅ |
Todas |
|
|
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()

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:
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 Ax2_a
,y2_a
: coordenadas do canto inferior direito da Caixa Ax1_b
,y1_b
: coordenadas do canto superior esquerdo da Caixa Bx2_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)

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.

💡 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:
Automínimo erro de reconstrução (por exemplo, MSE).
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:
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:
Versão modificada (mais estável):
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:
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\).
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:
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:
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:
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
Converter o prompt em token IDs e embeddings.
Amostrar ruído latente \(z_T \sim \mathcal{N}(0,I)\) no espaço comprimido (por exemplo, \(\mathbb{R}^{4\times64\times64}\)).
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\).
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#
Anotações do Dataset:
Vídeo(s)
Outro(s)