{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Capítulo 4: Deep Learning na Prática" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "![cover](cover4.jpeg)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Redes Neurais Convolucionais\n", "\n", "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](https://proceedings.neurips.cc/paper/2012/file/c399862d3b9d6b76c8436e924a68c45b-Paper.pdf)\". 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.\n", "\n", "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.\n", "\n", "### Aplicações Transformadoras em Visão Computacional\n", "\n", "A versatilidade e o poder das CNNs levaram à sua adoção em uma ampla gama de aplicações de visão computacional, incluindo:\n", "\n", "* **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.\n", "\n", "* **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.\n", "\n", "* **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.\n", "\n", "* **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.\n", "\n", "* **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.\n", "\n", "### Arquitetura em Camadas: Da Percepção à Abstração\n", "\n", "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.\n", "\n", "**Camadas Convolucionais: Encontrando os Padrões**\n", "\n", "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.\n", "\n", "Um [experimento interativo](https://cs.stanford.edu/people/karpathy/convnetjs/demo/cifar10.html) mostra como diferentes kernels extraem características distintas, ilustrando a capacidade das CNNs de aprender e adaptar-se a diferentes tipos de dados visuais.\n", "\n", "A seguir, uma ilustração do que seria uma CNN.\n", "\n", "\"cnn\"\n", "\n", "[Fonte de Figura](https://en.wikipedia.org/wiki/Convolutional_neural_network)\n", "\n", "**Camadas de Pooling: Simplificando a Informação Visual**\n", "\n", "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**.\n", "\n", "* **Max Pooling:** O Max Pooling extrai o valor máximo dentro de uma janela definida pelo kernel, destacando as características mais proeminentes.\n", "\n", "\"Histogramas\"\n", "\n", "([Fonte da figura](https://paperswithcode.com/methods/category/convolutional-neural-networks))\n", "\n", "\n", " ```\n", " Kernel 2x2:\n", " 1 3 -> Max Pooling -> 3\n", " 2 3\n", " ```\n", "\n", "* **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.\n", "\n", " ```\n", " Kernel 2x2:\n", " 1 3 -> Average Pooling -> 2\n", " 2 3\n", " ```\n", "\n", "**Flattening: Transformando Matrizes em Vetores**\n", "\n", "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:\n", "\n", "```\n", "Matriz 2x2: Vetor Coluna:\n", "1 2 1\n", "3 4 2\n", " 3\n", " 4\n", "```\n", "\n", "**Camadas Totalmente Conectadas: Integrando e Classificando**\n", "\n", "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.\n", "\n", "**Recursos extras:**\n", "\n", "- [CNN Explainer](https://poloclub.github.io/cnn-explainer/) - Visualização interativa\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Dataset\n", "\n", "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.\n", "\n", "### Conjunto de Treino, Teste e Validação\n", "\n", "Ao trabalhar com um dataset, é comum dividi-lo em três subconjuntos principais: conjunto de treino, conjunto de teste e conjunto de validação:\n", "\n", "
\n", " \"AI\"\n", "
\n", "\n", "\n", "- **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.\n", "\n", "- **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.\n", "\n", "- **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.\n", "\n", "Veja um exemplo comum de divisão do Dataset:\n", "\n", "
\n", " \"AI\"\n", "
\n", "\n", "\n", ">> 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.\n", "\n", "### Validação Cruzada (Cross-Validation)\n", "\n", "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.\n", "\n", "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.\n", "\n", "**Exemplo (k = 5):**\n", "\n", "Suponha um dataset com 10 amostras:\n", "\n", "```\n", "Iteração 1: Treino [3-10], Teste [1-2] → T T X X X X X X X X\n", "Iteração 2: Treino [1-2,5-10], Teste [3-4] → X X T T X X X X X X\n", "Iteração 3: Treino [1-4,7-10], Teste [5-6] → X X X X T T X X X X\n", "Iteração 4: Treino [1-6,9-10], Teste [7-8] → X X X X X X T T X X\n", "Iteração 5: Treino [1-8], Teste [9-10] → X X X X X X X X T T\n", "\n", "```\n", "\n", "\n", "### Principais Sites para Acesso a Datasets\n", "\n", "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:\n", "\n", "| Site | Endereço | Descrição |\n", "|----------------------------|------------------------------------------|---------------------------------------------------------------------------|\n", "| Kaggle | [Kaggle Datasets](https://www.kaggle.com/datasets) | Plataforma de ciência de dados com diversos datasets para competições e projetos. |\n", "| TensorFlow Datasets | [TensorFlow Datasets](https://www.tensorflow.org/datasets) | Biblioteca de datasets integrada à TensorFlow. |\n", "| ImageNet | [ImageNet](http://www.image-net.org/) | Banco de imagens grande e variado, comumente usado em visão computacional. |\n", "| UCI Machine Learning Repository | [UCI ML Repository](https://archive.ics.uci.edu/ml/index.php) | Repositório de conjuntos de dados de várias áreas, mantido pela Universidade da Califórnia, Irvine. | |\n", "| Hugging Face Datasets | [Hugging Face Datasets](https://huggingface.co/datasets) | Plataforma que fornece uma gama de datasets para tarefas de processamento de linguagem natural e visão computacional. |\n", "\n", "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.\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Classificação de Dígitos com Redes Convolucionais (CNNs) no TensorFlow\n", "\n", "Com a API **Keras**, integrada ao **[TensorFlow](https://www.tensorflow.org/?hl=pt-br)**, é 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**.\n", "\n", "**Importações e Pré-processamento**\n", "\n", "```python\n", "import tensorflow as tf\n", "from tensorflow.keras import layers, models\n", "import matplotlib.pyplot as plt\n", "import numpy as np\n", "\n", "# Carrega e normaliza os dados\n", "(x_train, y_train), (x_test, y_test) = tf.keras.datasets.mnist.load_data()\n", "x_train = x_train / 255.0\n", "x_test = x_test / 255.0\n", "```\n", "\n", "**Arquitetura da Rede Convolucional**\n", "\n", "```python\n", "model = models.Sequential([\n", " layers.Input(shape=(28, 28)),\n", " layers.Reshape((28, 28, 1)), \n", " layers.Conv2D(32, (3, 3), activation='relu'),\n", " layers.MaxPooling2D((2, 2)),\n", " layers.Conv2D(64, (3, 3), activation='relu'),\n", " layers.MaxPooling2D((2, 2)),\n", " layers.Flatten(),\n", " layers.Dense(128, activation='relu'),\n", " layers.Dense(64, activation='relu'),\n", " layers.Dense(10, activation='softmax')\n", "])\n", "```\n", "\n", "Explicando cada camada:\n", "\n", "* **`Reshape((28, 28, 1))`**: adiciona um canal à imagem (necessário para CNNs), transformando `(28, 28)` em `(28, 28, 1)`.\n", "\n", "* **`Conv2D(32, (3, 3), activation='relu')`**: aplica 32 filtros 3x3 sobre a imagem, extraindo **características locais**, como bordas e contornos.\n", "\n", "* **`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.\n", "\n", "* **`Conv2D(64, (3, 3), activation='relu')`**: mais uma camada de convolução, agora com 64 filtros, extraindo **características mais complexas**.\n", "\n", "* **`MaxPooling2D((2, 2))`**: nova etapa de redução de dimensionalidade.\n", "\n", "* **`Flatten()`**: transforma o volume 3D resultante das camadas convolucionais em um vetor 1D para ser passado às camadas densas.\n", "\n", "* **`Dense(128, activation='relu')`**: camada densa com 128 neurônios, conectando todos os dados extraídos.\n", "\n", "* **`Dense(64, activation='relu')`**: camada intermediária com 64 neurônios.\n", "\n", "* **`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.\n", "\n", "\n", "**Compilando o Modelo**\n", "\n", "```python\n", "model.compile(optimizer='adam',\n", " loss='sparse_categorical_crossentropy',\n", " metrics=['accuracy'])\n", "```\n", "\n", "A explicação permanece igual à versão anterior:\n", "\n", "* **Adam** é um otimizador robusto e eficiente.\n", "* A função de perda escolhida é adequada para classificação com rótulos inteiros.\n", "* A acurácia é usada como métrica de desempenho.\n", "\n", "\n", "**Treinamento e Avaliação**\n", "\n", "```python\n", "history = model.fit(x_train, y_train, epochs=10, validation_split=0.1)\n", "test_loss, test_acc = model.evaluate(x_test, y_test)\n", "print(f\"Precisão no teste: {test_acc:.2%}\")\n", "```\n", "\n", "Com as CNNs, é comum atingir **acurácias superiores a 98%** no MNIST com apenas algumas épocas de treino.\n", "\n", "\n", "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.\n", "\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Resultados**\n", "\n", "**Visualiza algumas previsões**\n", "\n", "```python\n", "# Faz previsões em 10 amostras do conjunto de teste\n", "predictions = model.predict(x_test[:10])\n", "\n", "# Visualiza as imagens e os rótulos previstos x verdadeiros\n", "plt.figure(figsize=(10, 2))\n", "for i in range(10):\n", " plt.subplot(1, 10, i + 1)\n", " plt.imshow(x_test[i], cmap='gray')\n", " pred_label = np.argmax(predictions[i])\n", " true_label = y_test[i]\n", " plt.title(f\"P:{pred_label}\\nT:{true_label}\", fontsize=8)\n", " plt.axis('off')\n", "plt.suptitle(\"Inferência com CNN: P = Previsto, T = Verdadeiro\")\n", "plt.show()\n", "```\n", "\n", "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.\n", "\n", "**Curvas de Acurácia e Erro**\n", "\n", "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.\n", "\n", "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).\n", "\n", "```python\n", "# Plota curvas de acurácia e perda\n", "plt.figure(figsize=(12, 4))\n", "\n", "# Acurácia\n", "plt.subplot(1, 2, 1)\n", "plt.plot(history.history['accuracy'], label='Treinamento')\n", "plt.plot(history.history['val_accuracy'], label='Validação')\n", "plt.title('Acurácia durante o Treinamento')\n", "plt.xlabel('Época')\n", "plt.ylabel('Acurácia')\n", "plt.legend()\n", "\n", "# Erro (Loss)\n", "plt.subplot(1, 2, 2)\n", "plt.plot(history.history['loss'], label='Treinamento')\n", "plt.plot(history.history['val_loss'], label='Validação')\n", "plt.title('Erro (Loss) durante o Treinamento')\n", "plt.xlabel('Época')\n", "plt.ylabel('Loss')\n", "plt.legend()\n", "\n", "plt.tight_layout()\n", "plt.show()\n", "```\n", "\n", "As curvas geralmente mostram:\n", "\n", "* Acurácia acima de **98%** no conjunto de treino e validação após poucas épocas.\n", "* Redução consistente da perda (loss), com sinais de **boa generalização**.\n", "\n", "**Matriz de Confusão**\n", "\n", "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.\n", "\n", "Com uma CNN bem treinada, a matriz tende a ficar **altamente concentrada na diagonal**, demonstrando precisão consistente em todas as classes.\n", "\n", "```python\n", "from sklearn.metrics import confusion_matrix, ConfusionMatrixDisplay\n", "\n", "# Faz previsões no conjunto completo de teste\n", "y_pred = model.predict(x_test)\n", "y_pred_classes = np.argmax(y_pred, axis=1)\n", "\n", "# Gera e exibe a matriz de confusão\n", "cm = confusion_matrix(y_test, y_pred_classes)\n", "disp = ConfusionMatrixDisplay(confusion_matrix=cm, display_labels=range(10))\n", "disp.plot(cmap='Blues', values_format='d')\n", "plt.title(\"Matriz de Confusão - CNN no MNIST\")\n", "plt.show()\n", "```\n", "\n", "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.\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Exercício Prático: Comparando Arquiteturas com e sem Camadas Convolucionais no Fashion MNIST\n", "\n", "Neste exercício, você irá **comparar duas arquiteturas de redes neurais** aplicadas ao dataset **Fashion MNIST**:\n", "\n", "1. Uma rede neural **densa** (sem convoluções), que você já implementou anteriormente.\n", "2. Uma rede neural com **camadas convolucionais (CNNs)**, usada frequentemente em tarefas de visão computacional.\n", "\n", "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.\n", "\n", "**Etapas**\n", "\n", "- **Carregue o dataset Fashion MNIST**\n", "- **Normalize os dados** para que os pixels fiquem no intervalo \\[0, 1]\n", "- **Visualize 10 imagens do conjunto de treino**\n", "- **Implemente duas redes**:\n", "\n", " * (a) Rede totalmente densa (sem convolução)\n", " * (b) Rede com camadas convolucionais\n", "- **Treine ambas as redes** com o mesmo otimizador (escolha um, como `adam` ou `sgd`)\n", "- **Compare os resultados**:\n", "\n", " * Curvas de acurácia e perda\n", " * Matrizes de confusão\n", "\n", "Escreva uma análise comparando os resultados:\n", "\n", "* Qual modelo teve melhor acurácia e menor perda?\n", "* A matriz de confusão de qual modelo apresentou menos erros?\n", "* O uso de camadas convolucionais fez diferença? Justifique com base nos resultados observados.\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Ferramentas para Anotação de Dados\n", "\n", "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:\n", "\n", "| Ferramenta | Tipo | Hospedagem | Tipos de Anotação | Recursos Adicionais | Formatos de Exportação |\n", "| -------------------------------------------------------------------------- | --------------------- | ----------------------- | ------------------------------------------------- | ------------------------------------------------------------ | -------------------------------- |\n", "| [Roboflow](https://roboflow.com/) | Freemium | Nuvem | Bounding boxes, segmentação | Augmentations automáticas, versionamento, integração com Git | YOLO, COCO, Pascal VOC, CSV |\n", "| [Labelbox](https://labelbox.com/) | Comercial | Nuvem | Bounding boxes, polígonos, keypoints, segmentação | Automação com IA, revisão em equipe, API REST | COCO, JSON, outros customizáveis |\n", "| [VGG Image Annotator (VIA)](http://www.robots.ox.ac.uk/~vgg/software/via/) | Gratuito, open-source | Local (navegador) | Bounding boxes, polígonos, pontos | Sem necessidade de servidor, extensível com JS | JSON, CSV, customizados |\n", "| [SuperAnnotate](https://superannotate.com/) | Freemium | Nuvem | Bounding boxes, segmentação, keypoints | IA assistida, dashboards, controle de papéis, CI/CD | YOLO, COCO, outros |\n", "| [MakeSense.ai](https://makesense.ai/) | Gratuito | Navegador | Bounding boxes, polígonos, keypoints | Uso anônimo, sem upload para servidores | YOLO, VOC XML, VGG JSON, CSV |\n", "| [Label Studio](https://labelstud.io/) | 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 |\n", "| [CVAT](https://cvat.org/) | Gratuito, open-source | Local ou Nuvem (Docker) | Bounding boxes, segmentação, keypoints, tracking | Suporte colaborativo, integração com OpenVINO, plugins | COCO, YOLO, XML, etc. |\n", "| [Scalabel](https://www.scalabel.ai/) | Gratuito, open-source | Local ou Nuvem | Bounding boxes, segmentação, tracking | Interface web simples, suporte a vídeo e 3D | JSON, COCO |\n", "| [Kili Technology](https://kili-technology.com/) | Comercial | Nuvem | Bounding boxes, texto, áudio, vídeo | IA assistida, controle de qualidade, revisão | JSON, outros |\n", "\n", "\n", "**Observações:**\n", "\n", "* **Hospedagem**: Algumas ferramentas permitem uso 100% local (ex: VIA, CVAT), ideal para contextos com restrições de privacidade.\n", "* **Anotação Multimodal**: Ferramentas como Label Studio e Kili oferecem suporte além de imagens, como texto e áudio.\n", "* **Automação**: Roboflow, SuperAnnotate e Kili oferecem pré-anotação com IA, útil para acelerar o processo em grandes datasets.\n", "* **Exportação**: A maioria suporta formatos padrão como YOLO, COCO e Pascal VOC.\n", "\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 🧠 Exercício: Coleta e Anotação de Imagens para Detecção de Objetos\n", "\n", "**Contexto do Problema:**\n", "\n", "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.\n", "\n", "**Tarefas:**\n", "\n", "* **Escolha um objeto simples**, preferencialmente presente nas categorias do dataset COCO.\n", "* **Busque e salve 15 imagens** reais contendo esse objeto (Google Imagens, Bing, Unsplash, Pixabay, etc.).\n", "* **Anote as imagens** utilizando **três ferramentas diferentes** de sua escolha, como: Roboflow, MakeSense.ai, Label Studio, CVAT, VGG Image Annotator (VIA), entre outras.\n", "* **Exporte as anotações no formato YOLOv11** (arquivo `.txt` com a notação padrão).\n", "* **Compare os arquivos gerados**, observando se existem diferenças na estrutura ou formato entre as ferramentas.\n", "\n", "**Requisitos Técnicos:**\n", "\n", "* As imagens devem ser nomeadas de forma padronizada: `img01.jpg`, `img02.jpg`, ..., `img15.jpg`.\n", "* As anotações devem conter **apenas uma classe** (o objeto escolhido).\n", "* Para cada ferramenta utilizada, organize os arquivos em uma pasta separada: `roboflow/`, `makesense/`, `labelstudio/`, etc.\n", "* Verifique se o formato YOLO está correto:\n", "\n", " ```\n", " \n", " ```\n", "\n", " (Todas as coordenadas devem estar normalizadas entre 0 e 1.)\n", "\n", "\n", "**Tarefa Final: Visualização da Anotação**\n", "\n", "Desenvolva um pequeno programa em Python que:\n", "\n", "* Receba como entrada uma imagem e seu arquivo de anotação (no formato YOLO).\n", "* Plote a imagem com a **caixa delimitadora** desenhada sobre o objeto anotado, como uma máscara ou retângulo.\n", "\n", "Exemplo de bibliotecas úteis: `matplotlib`, `opencv-python`.\n", "\n", "**(Opcional):**\n", "\n", "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.\n", "\n", "\n", "**Observações Importantes:**\n", "\n", "* Verifique se a ferramenta escolhida oferece **exportação gratuita** das anotações.\n", "* Prefira imagens com **licença aberta** ou de uso educacional. Sites como Unsplash, Pexels ou Pixabay são boas opções.\n", "* Certifique-se de **respeitar os direitos autorais** ao utilizar imagens da internet.\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Tutorial do YOLOv11\n", "\n", "Uma introdução rápida ao [YOLOv11 para detecção de objetos](https://docs.ultralytics.com/pt/models/yolo11/), incluindo instalação, treinamento e inferência.\n", "\n", "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.\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### YOLOv11 para Detecção de Objetos\n", "\n", "Este tutorial fornece uma introdução rápida ao YOLOv11 para detecção de objetos, incluindo instalação, treinamento e interpretação dos logs.\n", "\n", "\n", "**Instalação**\n", "\n", "Primeiro, instale a biblioteca `ultralytics`, que contém a implementação do YOLOv11. Você pode instalá-la usando o pip:\n", "\n", "```bash\n", "!pip install ultralytics\n", "```\n", "\n", "**Modelo e Dados**\n", "\n", "Antes de iniciar o treinamento, é importante entender os componentes utilizados:\n", "\n", "* **`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.\n", "* **`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`.\n", "\n", "**Exemplo de Treinamento**\n", "\n", "Aqui está um exemplo de como treinar o modelo YOLOv11 usando esse conjunto de dados:\n", "\n", "```python\n", "from ultralytics import YOLO\n", "import matplotlib.pyplot as plt\n", "import cv2\n", "\n", "# Carregar o modelo YOLO versão nano (leve)\n", "model = YOLO(\"yolo11n.pt\")\n", "\n", "# Treinar o modelo com o conjunto COCO8 por 3 épocas\n", "model.train(data='coco8.yaml', epochs=3)\n", "```\n", "\n", "**Resumo do Log Gerado**\n", "\n", "Durante o treinamento, o YOLOv11 exibe um log com informações úteis. A tabela abaixo resume os principais elementos:\n", "\n", "| Campo | Descrição |\n", "| ------------------- | ------------------------------------------------------------------ |\n", "| `epoch` | Época atual / total |\n", "| `gpu_mem` | Memória de GPU usada (GB) |\n", "| `box_loss` | Erro na predição das caixas delimitadoras |\n", "| `cls_loss` | Erro na classificação dos objetos |\n", "| `dfl_loss` | Erro na regressão refinada das caixas (Distribution Focal Loss) |\n", "| `instances` | Número de objetos anotados no batch |\n", "| `size` | Tamanho das imagens de entrada |\n", "| `metrics/precision` | Precisão: taxa de detecções corretas |\n", "| `metrics/recall` | Revocação: taxa de objetos corretamente detectados |\n", "| `metrics/mAP50` | Média da precisão com IoU de 0.5 |\n", "| `metrics/mAP50-95` | Média da precisão com IoU de 0.5 a 0.95 (mais rigoroso e completo) |\n", "\n", "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." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "### Exemplo de Detecção de Objetos\n", "\n", "Depois de treinar o modelo, você pode usá-lo para detectar objetos em uma imagem:\n", "\n", "```python\n", "from ultralytics import YOLO\n", "import matplotlib.pyplot as plt\n", "import cv2\n", "import urllib.request\n", "\n", "# Carregar o modelo YOLO\n", "model = YOLO(\"yolo11s.pt\")\n", "\n", "# Prever a imagem com o modelo\n", "#results = model(\"buses.jpeg\")\n", "# Baixar a imagem da URL\n", "url = \"http://farm9.staticflickr.com/8200/8282039486_c215950eba_z.jpg\"\n", "image_path = \"image.jpg\"\n", "urllib.request.urlretrieve(url, image_path)\n", "\n", "results = model(image_path)\n", "\n", "# Obter a imagem original\n", "img = cv2.imread(image_path) # carregando a imagem original\n", "img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) # converter de BGR para RGB para exibição no matplotlib\n", "\n", "# Obter a imagem com os resultados\n", "result_img = results[0].plot() # retorna a imagem com as caixas delimitadoras\n", "# Converter a imagem de BGR (OpenCV) para RGB (Matplotlib) para exibição correta\n", "result_img_rgb = cv2.cvtColor(result_img, cv2.COLOR_BGR2RGB)\n", "\n", "# Exibir as imagens antes e depois da detecção\n", "fig, axs = plt.subplots(1, 2, figsize=(15, 7))\n", "\n", "# Exibir a imagem original\n", "axs[0].imshow(img)\n", "axs[0].set_title('Imagem Original')\n", "axs[0].axis('off')\n", "\n", "# Exibir a imagem resultante\n", "axs[1].imshow(result_img_rgb)\n", "axs[1].set_title('Resultado da Detecção')\n", "axs[1].axis('off')\n", "\n", "plt.tight_layout()\n", "plt.show()\n", "```\n", "\n", "### Exemplo de Segmentação\n", "\n", "Você também pode usar o YOLOv11 para tarefas de segmentação:\n", "\n", "```python\n", "from ultralytics import YOLO\n", "import matplotlib.pyplot as plt\n", "import cv2\n", "\n", "# Carregar o modelo YOLO\n", "model = YOLO(\"yolo11n-seg.pt\")\n", "\n", "# Prever a imagem com o modelo\n", "#results = model(\"buses.jpeg\")\n", "# Baixar a imagem da URL\n", "url = \"http://farm9.staticflickr.com/8200/8282039486_c215950eba_z.jpg\"\n", "image_path = \"image.jpg\"\n", "urllib.request.urlretrieve(url, image_path)\n", "\n", "results = model(image_path)\n", "\n", "# Obter a imagem original\n", "img = cv2.imread(image_path) # carregando a imagem original\n", "img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) # converter de BGR para RGB para exibição no matplotlib\n", "\n", "# Obter a imagem com os resultados\n", "result_img = results[0].plot() # retorna a imagem com as caixas delimitadoras\n", "\n", "# Converter a imagem de BGR (OpenCV) para RGB (Matplotlib) para exibição correta\n", "result_img_rgb = cv2.cvtColor(result_img, cv2.COLOR_BGR2RGB)\n", "\n", "# Exibir as imagens antes e depois da detecção\n", "fig, axs = plt.subplots(1, 2, figsize=(15, 7))\n", "\n", "# Exibir a imagem original\n", "axs[0].imshow(img)\n", "axs[0].set_title('Imagem Original')\n", "axs[0].axis('off')\n", "\n", "# Exibir a imagem resultante\n", "axs[1].imshow(result_img_rgb)\n", "axs[1].set_title('Resultado da Segmentação')\n", "axs[1].axis('off')\n", "\n", "plt.tight_layout()\n", "plt.show()\n", "```\n", "\n", "### Exemplo de Estimativa de Pose\n", "\n", "O YOLOv11 também pode ser usado para estimativa de pose:\n", "\n", "```python\n", "from ultralytics import YOLO\n", "import cv2\n", "import matplotlib.pyplot as plt\n", "import cv2\n", "import urllib.request\n", "\n", "# Carregar o modelo\n", "model = YOLO(\"yolo11n-pose.pt\")\n", "\n", "# Fazer a predição na imagem\n", "url = \"http://farm4.staticflickr.com/3313/5727568334_874c48ec8e_z.jpg\"\n", "image_path = \"image.jpg\"\n", "urllib.request.urlretrieve(url, image_path)\n", "\n", "results = model(image_path)\n", "\n", "# Obter a imagem original\n", "img = cv2.imread(image_path) # carregando a imagem original\n", "img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) # converter de BGR para RGB para exibição no matplotlib\n", "\n", "# Obter a imagem original e os resultados\n", "result_img = results[0].plot() # Gera uma imagem anotada com bounding boxes e keypoints\n", "\n", "# Converter a imagem de BGR (OpenCV) para RGB (Matplotlib) para exibição correta\n", "result_img_rgb = cv2.cvtColor(result_img, cv2.COLOR_BGR2RGB)\n", "\n", "# Exibir as imagens antes e depois da detecção\n", "fig, axs = plt.subplots(1, 2, figsize=(15, 7))\n", "\n", "# Exibir a imagem original\n", "axs[0].imshow(img)\n", "axs[0].set_title('Imagem Original')\n", "axs[0].axis('off')\n", "\n", "# Exibir a imagem resultante\n", "axs[1].imshow(result_img_rgb)\n", "axs[1].set_title('Resultado da Pose')\n", "axs[1].axis('off')\n", "\n", "plt.tight_layout()\n", "plt.show()\n", "```\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Aplicação em um Vídeo\n", "\n", "Você também pode aplicar a detecção de objetos em vídeos:\n", "\n", "```python\n", "from ultralytics import YOLO\n", "import cv2\n", "import numpy as np\n", "\n", "# Carregar o modelo YOLO treinado\n", "model = YOLO(\"yolo11n.pt\")\n", "\n", "# Caminhos dos vídeos\n", "input_video_path = \"videoIN.mp4\"\n", "output_video_path = 'videoOUT.mp4'\n", "\n", "# Carregar o vídeo\n", "cap = cv2.VideoCapture(input_video_path)\n", "\n", "# Configurar o vídeo de saída com a mesma taxa de quadros e resolução do vídeo de entrada\n", "fourcc = cv2.VideoWriter_fourcc(*'mp4v')\n", "out = cv2.VideoWriter(output_video_path, fourcc, cap.get(cv2.CAP_PROP_FPS),\n", " (int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)), int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))))\n", "\n", "while cap.isOpened():\n", " ret, frame = cap.read()\n", " if not ret:\n", " break\n", "\n", " # Realizar a segmentação no frame\n", " results = model(frame)\n", "\n", " # Extrair o frame com segmentação anotada\n", " annotated_frame = results[0].plot() # `plot()` retorna a imagem com a segmentação aplicada\n", "\n", " # Escrever o frame anotado no vídeo de saída\n", " out.write(annotated_frame)\n", "\n", "# Liberar os objetos\n", "cap.release()\n", "out.release()\n", "cv2.destroyAllWindows()\n", "\n", "print(f\"Vídeo com segmentação salvo em: {output_video_path}\")\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Configurações avançadas do Yolo\n", "\n", "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.\n", "\n", "**Tabela de Configurações Avançadas do YOLOv11**\n", "\n", "| Parâmetro | Descrição | Valor Padrão | Treinamento | Predição | Tarefas Suportadas | |\n", "| ------------------------- | --------------------------------------------------------------------------------- | --------------- | ----------- | -------- | -------------------------------- | - |\n", "| `imgsz` | Tamanho da imagem (pode ser um valor único ou uma tupla). | 640 | ✅ | ✅ | Todas | |\n", "| `batch` | Tamanho do lote por GPU durante o treinamento. | 16 | ✅ | – | Todas | |\n", "| `epochs` | Número de épocas para o treinamento. | 100 | ✅ | – | Todas | |\n", "| `optimizer` | Otimizador utilizado (e.g., SGD, Adam, AdamW, RMSProp). | 'auto' | ✅ | – | Todas | |\n", "| `lr0` | Taxa de aprendizado inicial. | 0.01 | ✅ | – | Todas | |\n", "| `momentum` | Momentum para otimizadores baseados em momentum. | 0.937 | ✅ | – | Todas | |\n", "| `weight_decay` | Decaimento de peso (regularização L2). | 0.0005 | ✅ | – | Todas | |\n", "| `warmup_epochs` | Número de épocas de aquecimento para o ajuste gradual da taxa de aprendizado. | 3 | ✅ | – | Todas | |\n", "| `patience` | Número de épocas sem melhoria antes de interromper o treinamento antecipadamente. | 100 | ✅ | – | Todas | |\n", "| `pretrained` | Utilizar pesos pré-treinados (True, False ou caminho para o modelo). | True | ✅ | – | Todas | |\n", "| `conf` | Threshold de confiança para filtrar detecções. | 0.25 | – | ✅ | Detecção, Segmentação, Pose, OBB | |\n", "| `iou` | Threshold de IoU para Non-Maximum Suppression (NMS). | 0.7 | – | ✅ | Detecção, Segmentação, Pose, OBB | |\n", "| `max_det` | Número máximo de detecções por imagem. | 300 | – | ✅ | Detecção, Segmentação, Pose, OBB | |\n", "| `save_period` | Intervalo de épocas para salvar checkpoints durante o treinamento. | -1 | ✅ | – | Todas | |\n", "| `augment` | Aplicar aumentos de dados durante a inferência. | False | – | ✅ | Todas | |\n", "| `visualize` | Visualizar mapas de características intermediários para depuração. | False | ✅ | ✅ | Todas | |\n", "| `flipud` | Probabilidade de aplicar flip vertical durante o aumento de dados. | 0.0 | ✅ | – | Todas | |\n", "| `fliplr` | Probabilidade de aplicar flip horizontal durante o aumento de dados. | 0.5 | ✅ | – | Todas | |\n", "| `degrees` | Ângulo máximo para rotação aleatória durante o aumento de dados. | 0.0 | ✅ | – | Todas | |\n", "| `translate` | Fração máxima para translação aleatória durante o aumento de dados. | 0.1 | ✅ | – | Todas | |\n", "| `scale` | Fator de escala para aumento de dados. | 0.5 | ✅ | – | Todas | |\n", "| `shear` | Ângulo máximo para cisalhamento durante o aumento de dados. | 0.0 | ✅ | – | Todas | |\n", "| `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 | |\n", "| `copy_paste` | Aplicar técnica de copy-paste durante o aumento de dados. | 0.0 | ✅ | – | Detecção, Segmentação | |\n", "| `mask_ratio` | Threshold para binarização de máscaras em segmentação. | 0.5 | ✅ | ✅ | Segmentação | |\n", "| `kpt_thr` | Threshold de confiança para keypoints em estimativa de pose. | 0.2 | ✅ | ✅ | Pose Estimation | |\n", "| `resume` | Retomar treinamento a partir de um checkpoint salvo. | False | ✅ | – | Todas | |\n", "| `project` | Nome do diretório de projeto para salvar resultados. | 'runs' | ✅ | ✅ | Todas | |\n", "| `name` | Nome da execução específica dentro do projeto. | 'exp' | ✅ | ✅ | Todas | |\n", "| `exist_ok` | Permitir sobrescrever diretórios existentes. | False | ✅ | ✅ | Todas | |\n", "\n", "**Tarefas Suportadas pelo YOLOv11**\n", "\n", "O YOLOv11 é uma estrutura versátil que suporta as seguintes tarefas de visão computacional:\n", "\n", "* **Detecção de Objetos**: Identificação e localização de objetos em imagens ou vídeos.\n", "* **Segmentação de Instâncias**: Delineamento preciso dos contornos de objetos detectados.\n", "* **Classificação de Imagens**: Categorização de imagens em classes predefinidas.\n", "* **Estimativa de Pose**: Detecção e rastreamento de pontos-chave em corpos humanos.\n", "* **Detecção Orientada (OBB)**: Detecção de objetos com orientação rotacional para maior precisão.\n", "* \n", "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.\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Conhecendo o YOLOE\n", "\n", "Para uma compreensão mais aprofundada, consulte o artigo \"[YOLOE: Real-Time Seeing Anything](https://arxiv.org/html/2503.07465v1)\".\n", "\n", "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:\n", "\n", "* **Prompt de texto** (ex: \"gato\", \"bicicleta\")\n", "* **Prompt visual** (ex: imagem de referência)\n", "* **Sem prompt** (modo \"ver tudo\")\n", "\n", "Ele combina a arquitetura eficiente do YOLOv10 com técnicas de aprendizado zero-shot, permitindo detectar objetos fora das categorias pré-definidas.\n", "\n", "**Instalação**\n", "\n", "Instale o pacote Ultralytics com:\n", "\n", "```bash\n", "pip install ultralytics\n", "```\n", "Para verificar a instalação e obter mais detalhes, consulte o [guia de instalação da Ultralytics](https://docs.ultralytics.com/quickstart/).\n", "\n", "**Como usar o [YOLOE](https://docs.ultralytics.com/pt/models/yoloe/)**\n", "\n", "```python\n", "from ultralytics import YOLOE\n", "\n", "# Carregar o modelo YOLOE pré-treinado\n", "model = YOLOE('yoloe.pt')\n", "```\n", "\n", "- Inferência sem prompt (modo \"ver tudo\"):\n", "\n", "```python\n", "# Detectar todos os objetos sem fornecer um prompt\n", "results = model.predict(source='imagem.jpg')\n", "```\n", "\n", "- Inferência com prompt de texto:\n", "\n", "```python\n", "# Detectar objetos com base em um prompt de texto\n", "results = model.predict(source='imagem.jpg', prompt='cachorro')\n", "```\n", "\n", "- Inferência com prompt visual:\n", "\n", "```python\n", "# Detectar objetos com base em uma imagem de referência\n", "results = model.predict(source='imagem.jpg', prompt='referencia.jpg')\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Exemplos de uso:**\n", "\n", "- **Prompt free - Detectar todos os objetos sem fornecer um prompt:**\n", "\n", "[Download da imagem para testes](coco_crash.jpg)\n", "\n", "```python\n", "# Carregando a modelo\n", "model = YOLOE(\"yoloe-11s-seg-pf.pt\") # load a model. 11l -> muda para large\n", "\n", "# testando em uma iamgem\n", "results = model.predict(\"coco_crash.jpg\")\n", "\n", "# Exibindo os resultados\n", "results[0].show()\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", " \"AI\" \n", "
\n", "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Vamos agora utilizar um prompt:**\n", "\n", "[Download da imagem para testes](coco_sk8.jpg)\n", "\n", "```python\n", "from ultralytics import YOLOE\n", "\n", "# Initialize a YOLOE model\n", "model = YOLOE(\"yoloe-11l-seg.pt\") # ou selecione yoloe-11s/m-seg.pt para diferentes tamanhos\n", "\n", "# Escrevendo prompts\n", "names = [\"person\", \"skate\"]\n", "#names = [\"person on the sidewalk\"]\n", "model.set_classes(names, model.get_text_pe(names))\n", "\n", "# Executando a detecção com a imagem passada\n", "results = model.predict(\"coco_sk8.jpg\")\n", "\n", "# Show results\n", "results[0].show()\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Exercício: Cálculo do IoU (Intersection over Union)\n", "\n", "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.\n", "\n", "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:\n", "\n", "$$\n", "IoU = \\frac{\\text{Área da Interseção}}{\\text{Área da União}}\n", "$$\n", "\n", "* **Interseção**: área em que as duas caixas se sobrepõem.\n", "* **União**: soma das áreas das duas caixas, subtraindo a área em comum (para não contar duas vezes).\n", "\n", "O valor do IoU varia entre 0 e 1:\n", "\n", "* **0**: sem sobreposição.\n", "* **1**: sobreposição perfeita.\n", "\n", "**Representação das Caixas**\n", "\n", "As caixas são representadas por seus cantos superior esquerdo e inferior direito:\n", "\n", "```python\n", "[x1_a, y1_a, x2_a, y2_a, x1_b, y1_b, x2_b, y2_b]\n", "```\n", "\n", "Ou seja:\n", "\n", "* `x1_a`, `y1_a`: coordenadas do **canto superior esquerdo** da **Caixa A**\n", "* `x2_a`, `y2_a`: coordenadas do **canto inferior direito** da **Caixa A**\n", "* `x1_b`, `y1_b`: coordenadas do **canto superior esquerdo** da **Caixa B**\n", "* `x2_b`, `y2_b`: coordenadas do **canto inferior direito** da **Caixa B**\n", "\n", "**Exemplo**\n", "\n", "```python\n", "dados = [2, 2, 5, 5, 4, 3, 7, 6]\n", "```\n", "\n", "* Caixa A: (2, 2) até (5, 5)\n", "* Caixa B: (4, 3) até (7, 6)\n", "\n", "\n", "
\n", " \"AI\"\n", "
\n", "\n", "\n", "**Tarefa**\n", "\n", "- Calcule a área de interseção entre as duas caixas.\n", "- Calcule a área da união.\n", "- Use a fórmula do IoU para obter o valor final.\n", "- **(Opcional, mas altamente recomendado)**: **plote as duas caixas em um gráfico 2D** para visualizar claramente a interseção.\n", "\n", "> 📝 Dica: use bibliotecas como `matplotlib` para desenhar as caixas e facilitar a interpretação.\n", "\n", "**Função a implementar**\n", "\n", "```python\n", "def calcular_iou(dados):\n", " \"\"\"\n", " Calcula o Intersection over Union entre duas caixas a partir de uma lista com 8 valores.\n", "\n", " Parâmetros:\n", " dados: lista com 8 elementos no formato\n", " [x1_a, y1_a, x2_a, y2_a, x1_b, y1_b, x2_b, y2_b]\n", "\n", " Retorno:\n", " IoU: float\n", " \"\"\"\n", " # Sua implementação aqui\n", " pass\n", "```\n", "\n", "**Testes**\n", "\n", "```python\n", "# Teste 1\n", "Entrada: 2, 2, 5, 5, 4, 3, 7, 6\n", "Saída: 0.12\n", "\n", "# Teste 2\n", "Entrada: 1, 1, 4, 4, 1, 1, 4, 4\n", "Saída: 1.00\n", "\n", "# Teste 3\n", "Entrada: 0, 0, 6, 6, 2, 2, 4, 4\n", "Saída: 0.11\n", "\n", "# Teste 4\n", "Entrada: 0, 0, 2, 2, 3, 3, 5, 5\n", "Saída: 0.0\n", "\n", "```\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Exercício Yoloe\n", "\n", "**Contexto do Problema**\n", "\n", "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.\n", "\n", "
\n", " \"AI\" \n", "
\n", "
\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 💡 LLMs e Gemini para Visão Computacional\n", "\n", "**O que são LLMs?**\n", "\n", "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:\n", "\n", "* Compreender e gerar linguagem natural;\n", "* Traduzir idiomas;\n", "* Escrever e corrigir códigos;\n", "* Responder perguntas;\n", "* E, mais recentemente, **interpretar diferentes modalidades de dados** como imagens, vídeos e áudios (multimodalidade).\n", "\n", "**Gemini: IA Multimodal do Google**\n", "\n", "**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:\n", "\n", "* Texto,\n", "* Imagens,\n", "* Vídeos,\n", "* Áudios,\n", "* Documentos PDF e outros formatos.\n", "\n", "Você pode experimentar o Gemini diretamente no [**Google AI Studio**](https://aistudio.google.com/app), um ambiente web intuitivo para testar prompts, gerar conteúdos e interagir com modelos de forma visual e interativa.\n", "\n", "**Aplicações em Visão Computacional**\n", "\n", "Com o uso da API `google.generativeai`, é possível aplicar os modelos Gemini para:\n", "\n", "* Descrever imagens com riqueza de detalhes;\n", "* Detectar objetos, pessoas, ambientes e até emoções visuais;\n", "* Analisar conteúdo técnico como:\n", "\n", " * Exames médicos (ex: radiografias),\n", " * Plantas arquitetônicas,\n", " * Mapas e imagens científicas.\n", "\n", "**Exemplo Prático: Descrição Automática de Imagem com Gemini + Python**\n", "\n", "Neste exemplo, vamos:\n", "\n", "- Instalar bibliotecas necessárias;\n", "- Configurar a chave de API do Google;\n", "- Carregar uma imagem do Google Drive;\n", "- Enviar essa imagem para o modelo Gemini com uma pergunta;\n", "- Obter uma descrição gerada automaticamente.\n", "\n", "**Código Comentado**\n", "\n", "```python\n", "# Instalação das bibliotecas da API Gemini\n", "!pip install -U -q \"google\"\n", "!pip install -U -q \"google.genai\"\n", "\n", "# Bibliotecas necessárias\n", "import base64\n", "import os\n", "from PIL import Image\n", "from google import genai\n", "from google.genai import types\n", "from google.colab import userdata, drive\n", "\n", "# Monta o Google Drive para acessar a imagem\n", "drive.mount('/content/drive')\n", "\n", "# Configura a chave da API (deve estar cadastrada no ambiente do Colab)\n", "os.environ[\"GEMINI_API_KEY\"] = userdata.get(\"GOOGLE_API_KEY\")\n", "\n", "def generate():\n", " # Caminho da imagem no seu Google Drive\n", " image_path = '/content/drive/MyDrive/Google AI Studio/w1.png'\n", "\n", " # Tenta abrir a imagem usando PIL (Pillow)\n", " try:\n", " image = Image.open(image_path)\n", " print(f\"✅ Imagem carregada com sucesso: {image_path}\")\n", " except FileNotFoundError:\n", " print(\"❌ Erro: Arquivo de imagem não encontrado.\")\n", " return\n", " except Exception as e:\n", " print(f\"❌ Erro ao abrir a imagem: {e}\")\n", " return\n", "\n", " # Inicializa o cliente da API Gemini\n", " try:\n", " client = genai.Client(api_key=os.environ.get(\"GEMINI_API_KEY\"))\n", " model = \"gemini-2.0-flash-exp\" # Versão rápida e multimodal do Gemini\n", " except Exception as e:\n", " print(f\"❌ Erro ao configurar a API: {e}\")\n", " return\n", "\n", " try:\n", " print(\"🧠 Gerando descrição da imagem...\")\n", "\n", " # Lê os bytes da imagem para envio à API\n", " with open(image_path, 'rb') as f:\n", " image_data = f.read()\n", "\n", " # Cria a mensagem com duas partes: a imagem e a instrução textual\n", " contents = [\n", " types.Content(\n", " role=\"user\",\n", " parts=[\n", " types.Part.from_bytes(data=image_data, mime_type=\"image/png\"),\n", " types.Part.from_text(\"Descreva esta imagem em detalhes.\"),\n", " ],\n", " ),\n", " ]\n", "\n", " # Define configurações da geração, incluindo uma instrução de sistema\n", " generate_content_config = types.GenerateContentConfig(\n", " response_mime_type=\"text/plain\",\n", " system_instruction=[\n", " types.Part.from_text(\"Você é um assistente especializado em descrição detalhada de imagens.\"),\n", " ],\n", " )\n", "\n", " # Exibe cabeçalho da resposta\n", " print(\"\\n\" + \"=\"*50)\n", " print(\"📷 DESCRIÇÃO DA IMAGEM:\")\n", " print(\"=\"*50)\n", "\n", " # Geração da resposta em streaming (recebe aos poucos)\n", " for chunk in client.models.generate_content_stream(\n", " model=model,\n", " contents=contents,\n", " config=generate_content_config,\n", " ):\n", " print(chunk.text, end=\"\") # Imprime a resposta conforme ela chega\n", "\n", " print(\"\\n\" + \"=\"*50)\n", " print(\"✅ Fim da descrição.\")\n", "\n", " except Exception as e:\n", " print(f\"❌ Erro ao gerar descrição: {e}\")\n", "\n", "# Executa o programa principal\n", "if __name__ == \"__main__\":\n", " if not os.environ.get(\"GEMINI_API_KEY\"):\n", " print(\"⚠️ Erro: A chave da API não está configurada.\")\n", " else:\n", " generate()\n", "```\n", "\n", "**Dicas e Explorações Adicionais**\n", "\n", "* Teste diferentes modelos como `gemini-pro`, `gemini-2.5-flash`, etc.\n", "* 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\").\n", "* Modifique parâmetros da geração (como **temperatura**, para maior criatividade).\n", "* Use imagens diferentes, incluindo diagramas, fotos pessoais, mapas, imagens científicas.\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Exercício – Interface em Gradio para chat com imagem\n", "\n", "Utilizando um exemplo de [código disponível](gemini.py) 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.\n", "\n", "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.\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Introdução às Redes Generativas\n", "\n", "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.\n", "\n", "### Por que usar modelos generativos?\n", "\n", "* **Criação de Conteúdo**: gerar imagens, texto ou áudio novos (arte, roteiros, trilhas sonoras).\n", "* **Aumento de Dados**: sintetizar exemplos para treinar outros modelos em cenários com poucos dados reais.\n", "* **Compressão e Representação**: aprender uma representação latente que capture as características principais dos dados (por exemplo, em autoencoders).\n", "* **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.\n", "\n", "\n", "## Principais Famílias de Modelos Generativos\n", "\n", "- **Modelos de Mistura Gaussiana (GMM)**\n", "\n", " * **Ideia principal**: aproximam a distribuição dos dados por uma soma de distribuições normais multivariadas.\n", " * **Semântica prática**: cada componente gaussiano “modela” um cluster nos dados; a mistura (peso de cada componente) explica a probabilidade final.\n", " * **Pontos-chave**:\n", "\n", " * Fácil de entender e implementar em baixa dimensão.\n", " * Não escala bem para imagens de alta resolução.\n", " * Serve como introdução conceitual sobre “estimativa de densidade”.\n", "\n", "- **Variational Autoencoders (VAEs)**\n", "\n", " * **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$.\n", " * **Objetivo de Treinamento**: otimizar um limite inferior da evidência (ELBO), composto por:\n", "\n", " 1. Automínimo erro de reconstrução (por exemplo, MSE).\n", " 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)$.\n", " * **Pontos-chave**:\n", "\n", " * Permitem amostrar diretamente do espaço latente (gera variações suaves).\n", " * Produzem amostras que podem ficar um pouco “borradas” em imagens.\n", " * Treinamento relativamente estável e eficiente em comparação com GANs.\n", "\n", "- **Redes Generativas Adversariais (GANs)**\n", "\n", " * **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.\n", "\n", " * **Gerador ($G$)**: a partir de um vetor aleatório $z\\sim p(z)$, tenta gerar uma amostra $G(z)$ que pareça real.\n", " * **Discriminador ($D$)**: recebe amostras reais ou geradas e tenta distinguir “real” de “falso”.\n", " * **O jogo minimax**:\n", "\n", " $$\n", " \\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)))].\n", " $$\n", " * **Pontos-chave**:\n", "\n", " * Podem gerar imagens nítidas e de alta resolução.\n", " * Exigem cuidado para evitar instabilidades (colapso de modo, onde o gerador produz poucas variações).\n", " * Sucesso em domínios como geração de rostos (e.g., StyleGAN) e arte abstrata.\n", "\n", "- **Modelos de Difusão (Diffusion Models)**\n", "\n", " * **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.\n", "\n", " * **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.\n", " * **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$.\n", " * **Pontos-chave**:\n", "\n", " * Geralmente mais estáveis que GANs.\n", " * Produzem amostras de altíssima qualidade (sem artefatos típicos de GANs).\n", " * Inferir uma imagem exige dezenas ou centenas de etapas de denoising, tornando a geração mais lenta.\n", "\n", "Tabela comparativa dos principais modelos apresentados:\n", "\n", "| Modelo | Abordagem | Vantagens Principais | Limitações Notáveis | Aplicações Típicas |\n", "| ---------------------- | ----------------------------------- | -------------------------------------------------------------- | ------------------------------------------------ | ---------------------------------------------------------- |\n", "| **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 |\n", "| **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 |\n", "| **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 |\n", "| **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) |\n", "\n", "\n", "\n", "> **Observação**: a seguir, focaremos em **GANs** e **Stable Diffusion** (um diffusion model voltado para texto → imagem). \n", "\n", "## Redes Generativas Adversariais (GANs)\n", "\n", "**Conceito e Arquitetura Básica**\n", "\n", "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.\n", "\n", "**Gerador $G_\\theta$**\n", "\n", "* Recebe como entrada um vetor aleatório $z \\sim p(z)$, normalmente amostrado de uma distribuição gaussiana $\\mathcal{N}(0, I)$ ou uniforme.\n", "* Gera uma amostra sintética $G(z)$, com aparência similar à de dados reais.\n", "* É geralmente implementado com redes convolucionais transpostas (ou técnicas de upsampling).\n", "\n", "**Discriminador $D_\\phi$**\n", "\n", "* Recebe como entrada uma amostra $x$ (real ou gerada).\n", "* Retorna uma probabilidade $D(x) \\in [0, 1]$, indicando o quão \"real\" é a entrada.\n", "* É implementado como uma rede convolucional tradicional, atuando como um classificador binário.\n", "\n", "\n", "**Funções de Custo (Loss) e Treinamento**\n", "\n", "**Notação de Esperança Matemática $\\mathbb{E}$**\n", "\n", "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.\n", "\n", "**Objetivo do Discriminador**\n", "\n", "O discriminador busca **maximizar** a seguinte função de custo:\n", "\n", "$$\n", "\\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)))]\n", "$$\n", "\n", "> Onde:\n", ">\n", "> * $p_\\text{data}(x)$: distribuição real dos dados.\n", "> * $p(z)$: distribuição aleatória de entrada do gerador.\n", "> * $D(x)$: probabilidade atribuída pelo discriminador de que $x$ seja real.\n", "\n", "**Objetivo do Gerador**\n", "\n", "O gerador busca **minimizar** a capacidade do discriminador de identificar amostras falsas. Existem duas versões da loss:\n", "\n", "* **Versão original**:\n", "\n", "$$\n", "\\mathcal{L}_G = \\mathbb{E}_{z \\sim p(z)}[\\log(1 - D(G(z)))]\n", "$$\n", "\n", "* **Versão modificada (mais estável)**:\n", "\n", "$$\n", "\\mathcal{L}_G = \\mathbb{E}_{z \\sim p(z)}[-\\log D(G(z))]\n", "$$\n", "\n", "> Esta versão é mais comum na prática, pois evita gradientes muito pequenos nas fases iniciais do treinamento.\n", "\n", "**Treinamento Adversarial**\n", "\n", "O processo de treinamento alterna entre dois passos:\n", "\n", "1. **Atualização de $D_\\phi$**:\n", "\n", " * Amostra um batch de dados reais $x \\sim p_\\text{data}(x)$.\n", " * Amostra um batch de ruído $z \\sim p(z)$, gera $G(z)$.\n", " * Otimiza os parâmetros $\\phi$ do discriminador para maximizar $\\mathcal{L}_D$.\n", "\n", "2. **Atualização de $G_\\theta$**:\n", "\n", " * Gera novas amostras $G(z)$ com $z \\sim p(z)$.\n", " * Otimiza os parâmetros $\\theta$ do gerador para minimizar $\\mathcal{L}_G$.\n", "\n", "**Jogo Minimax**\n", "\n", "O treinamento das GANs é formulado como um **jogo de soma zero** entre duas redes com objetivos opostos:\n", "\n", "$$\n", "\\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)))]\n", "$$\n", "\n", "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$.\n", "\n", "**Variações Populares de GANs**\n", "\n", "* **DCGAN** (Deep Convolutional GAN):\n", " Estrutura convolucional profunda adaptada para imagens. Usa **Strided Convolutions**, **Batch Normalization**, e **LeakyReLU**.\n", "\n", "* **WGAN** (Wasserstein GAN):\n", " Substitui a função de perda original pela **distância de Wasserstein**, oferecendo **estabilidade e interpretabilidade**.\n", "\n", "* **StyleGAN / StyleGAN2**:\n", " 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**.\n", "\n", "**Exemplo Prático de GAN em PyTorch**\n", "\n", "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.\n", "\n", "```python\n", "import os\n", "import torch\n", "import torch.nn as nn\n", "import torch.optim as optim\n", "from torchvision import datasets, transforms, utils\n", "from torch.utils.data import DataLoader\n", "\n", "# 1) CONFIGURAÇÕES GERAIS\n", "device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\") # Usa GPU se disponível\n", "latent_dim = 100 # Dimensão do vetor latente (entrada do gerador)\n", "batch_size = 128 # Tamanho do lote\n", "lr = 0.0002 # Taxa de aprendizado\n", "epochs = 30 # Número de épocas de treinamento\n", "sample_dir = \"samples\" # Pasta para salvar imagens geradas\n", "os.makedirs(sample_dir, exist_ok=True)\n", "\n", "# 2) CARREGAMENTO DO DATASET (MNIST)\n", "transform = transforms.Compose([\n", " transforms.ToTensor(), # Converte imagens para tensores\n", " transforms.Normalize([0.5], [0.5]) # Normaliza para o intervalo [-1, 1]\n", "])\n", "dataset = datasets.MNIST(root=\"./data\", train=True, download=True, transform=transform)\n", "dataloader = DataLoader(dataset, batch_size=batch_size, shuffle=True)\n", "\n", "# 3) DEFINIÇÃO DO GERADOR\n", "class Generator(nn.Module):\n", " def __init__(self):\n", " super().__init__()\n", " self.net = nn.Sequential(\n", " nn.Linear(latent_dim, 256),\n", " nn.LeakyReLU(0.2, inplace=True),\n", " nn.Linear(256, 512),\n", " nn.BatchNorm1d(512),\n", " nn.LeakyReLU(0.2, inplace=True),\n", " nn.Linear(512, 1024),\n", " nn.BatchNorm1d(1024),\n", " nn.LeakyReLU(0.2, inplace=True),\n", " nn.Linear(1024, 28*28), # Saída do gerador deve ter o tamanho da imagem do MNIST (784)\n", " nn.Tanh() # Saída entre [-1, 1] para combinar com a normalização do dataset\n", " )\n", "\n", " def forward(self, z):\n", " img = self.net(z) # Gera imagem a partir de vetor latente\n", " return img.view(-1, 1, 28, 28) # Reshape para imagem (1 canal, 28x28)\n", "\n", "# 4) DEFINIÇÃO DO DISCRIMINADOR\n", "class Discriminator(nn.Module):\n", " def __init__(self):\n", " super().__init__()\n", " self.net = nn.Sequential(\n", " nn.Linear(28*28, 512),\n", " nn.LeakyReLU(0.2, inplace=True),\n", " nn.Linear(512, 256),\n", " nn.LeakyReLU(0.2, inplace=True),\n", " nn.Linear(256, 1),\n", " nn.Sigmoid() # Produz probabilidade de ser imagem real\n", " )\n", "\n", " def forward(self, img):\n", " flat = img.view(-1, 28*28) # Achata a imagem para vetor\n", " return self.net(flat)\n", "\n", "# 5) INSTANCIAÇÃO DE MODELOS, FUNÇÃO DE PERDA E OTIMIZADORES\n", "generator = Generator().to(device)\n", "discriminator = Discriminator().to(device)\n", "\n", "criterion = nn.BCELoss() # Binary Cross Entropy para classificação binária (real ou falsa)\n", "optim_G = optim.Adam(generator.parameters(), lr=lr, betas=(0.5, 0.999))\n", "optim_D = optim.Adam(discriminator.parameters(), lr=lr, betas=(0.5, 0.999))\n", "\n", "# 6) FUNÇÕES AUXILIARES PARA GERAR RÓTULOS\n", "def real_labels(size):\n", " return torch.ones(size, 1, device=device) # Rótulo \"1\" para imagens reais\n", "\n", "def fake_labels(size):\n", " return torch.zeros(size, 1, device=device) # Rótulo \"0\" para imagens falsas\n", "\n", "# 7) LOOP DE TREINAMENTO DO GAN\n", "for epoch in range(epochs):\n", " for i, (imgs, _) in enumerate(dataloader):\n", " batch_size_i = imgs.size(0)\n", " imgs = imgs.to(device)\n", "\n", " # ----- TREINAMENTO DO DISCRIMINADOR -----\n", " # Gera imagens falsas a partir de ruído\n", " z = torch.randn(batch_size_i, latent_dim, device=device)\n", " fake_imgs = generator(z)\n", "\n", " # Avalia imagens reais e falsas\n", " real_out = discriminator(imgs)\n", " fake_out = discriminator(fake_imgs.detach()) # Detach para não propagar gradientes para o gerador\n", "\n", " # Calcula a perda do discriminador\n", " loss_D_real = criterion(real_out, real_labels(batch_size_i))\n", " loss_D_fake = criterion(fake_out, fake_labels(batch_size_i))\n", " loss_D = (loss_D_real + loss_D_fake) / 2\n", "\n", " # Backpropagation e otimização do discriminador\n", " optim_D.zero_grad()\n", " loss_D.backward()\n", " optim_D.step()\n", "\n", " # ----- TREINAMENTO DO GERADOR -----\n", " # Tenta enganar o discriminador: quer que imagens falsas sejam classificadas como reais\n", " output = discriminator(fake_imgs)\n", " loss_G = criterion(output, real_labels(batch_size_i)) # Compara com rótulo real (1)\n", "\n", " # Backpropagation e otimização do gerador\n", " optim_G.zero_grad()\n", " loss_G.backward()\n", " optim_G.step()\n", "\n", " # Exibe progresso a cada 200 lotes\n", " if i % 200 == 0:\n", " print(f\"Epoch [{epoch+1}/{epochs}] Batch [{i}/{len(dataloader)}] \"\n", " f\"Loss D: {loss_D.item():.4f}, Loss G: {loss_G.item():.4f}\")\n", "\n", " # ----- GERA E SALVA AMOSTRAS -----\n", " with torch.no_grad():\n", " z = torch.randn(16, latent_dim, device=device)\n", " sample_imgs = generator(z).cpu()\n", " sample_grid = utils.make_grid(sample_imgs, nrow=4, normalize=True)\n", " utils.save_image(sample_grid, f\"{sample_dir}/epoch_{epoch+1:03d}.png\")\n", "\n", "print(\"Treinamento concluído! Amostras salvas na pasta 'samples/'.\")\n", "```\n", "\n", "### Resumo do que o código faz:\n", "\n", "* **Treina um GAN (Generative Adversarial Network)** usando o conjunto de dados MNIST.\n", "* O **gerador** aprende a criar imagens falsas de dígitos manuscritos.\n", "* O **discriminador** tenta distinguir imagens reais (do MNIST) de imagens falsas (do gerador).\n", "* Ambos são treinados de forma adversarial: um tenta enganar o outro.\n", "* Ao final de cada época, são geradas 16 imagens para avaliação visual do progresso do gerador.\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Modelos de Difusão e Stable Diffusion**\n", "\n", "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:\n", "\n", "$$\n", "x_t = \\sqrt{\\alpha_t}\\,x_{t-1} + \\sqrt{1 - \\alpha_t}\\,\\epsilon_t,\\quad \\epsilon_t \\sim \\mathcal{N}(0,I),\n", "$$\n", "\n", "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.\n", "\n", "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:\n", "\n", "$$\n", "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),\n", "$$\n", "\n", "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.\n", "\n", "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.\n", "\n", "\n", "**Visão Geral do Stable Diffusion**\n", "\n", "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.\n", "\n", "* **Espaço Latente (Latent Diffusion)**\n", " 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.\n", "\n", "* **Condicionamento em Texto**\n", " 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$.\n", "\n", "* **Pipeline de Geração**\n", "\n", " 1. Converter o prompt em token IDs e embeddings.\n", " 2. Amostrar ruído latente $z_T \\sim \\mathcal{N}(0,I)$ no espaço comprimido (por exemplo, $\\mathbb{R}^{4\\times64\\times64}$).\n", " 3. Para $t = T, T-1, \\dots, 1$:\n", "\n", " * A U-Net recebe $z_t$, o passo $t$ e os embeddings do texto.\n", " * Prediz o ruído $\\hat{\\epsilon}$.\n", " * Atualiza $z_{t-1}$ pela fórmula de denoising, usando $\\hat{\\epsilon}$ e os coeficientes $\\alpha_t$.\n", " 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$).\n", "\n", "* **Vantagens do Stable Diffusion**\n", "\n", " * Processar no espaço latente reduz dramaticamente o custo computacional comparado a difundir em pixels.\n", " * A qualidade das imagens geradas costuma ser muito alta, com poucos artefatos.\n", " * Além de texto→imagem, suporta tarefas como inpainting, outpainting e outras modalidades condicionadas (máscaras, rascunhos, etc.).\n", "\n", "**Exemplo Prático Funcional de Stable Diffusion**\n", "\n", "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.\n", "\n", "```python\n", "# Instalação das dependências (execute apenas uma vez no notebook/Colab)\n", "!pip install -q diffusers transformers accelerate safetensors\n", "\n", "import torch\n", "from diffusers import StableDiffusionPipeline\n", "from PIL import Image\n", "import matplotlib.pyplot as plt\n", "from IPython.display import display\n", "\n", "# Verifica se há GPU disponível\n", "device = \"cuda\" if torch.cuda.is_available() else \"cpu\"\n", "print(f\"Dispositivo em uso: {device}\")\n", "\n", "# Carrega o modelo Stable Diffusion 2.1\n", "model_id = \"stabilityai/stable-diffusion-2-1\"\n", "pipe = StableDiffusionPipeline.from_pretrained(\n", " model_id,\n", " torch_dtype=torch.float16 if device == \"cuda\" else torch.float32\n", ").to(device)\n", "\n", "# Define o prompt (descrição textual da imagem)\n", "prompt = \"Uma floresta encantada ao entardecer, estilo pintura digital\"\n", "\n", "# Parâmetros de geração\n", "num_steps = 50 # Passos de denoising\n", "guidance = 7.5 # Peso para aderência ao prompt\n", "\n", "# Função para gerar imagem a partir do prompt\n", "def gerar_imagem(prompt, steps=50, guidance_scale=7.5, height=512, width=512):\n", " with torch.autocast(device) if device == \"cuda\" else torch.no_grad():\n", " resultado = pipe(\n", " prompt=prompt,\n", " height=height,\n", " width=width,\n", " num_inference_steps=steps,\n", " guidance_scale=guidance_scale\n", " )\n", " return resultado.images[0]\n", "\n", "# Função para exibir imagem (compatível com diferentes ambientes)\n", "def exibir_imagem(imagem, titulo=\"Imagem Gerada\"):\n", " try:\n", " # Método 1: Para Jupyter/Colab - usando matplotlib\n", " plt.figure(figsize=(10, 10))\n", " plt.imshow(imagem)\n", " plt.axis('off')\n", " plt.title(titulo)\n", " plt.show()\n", " except:\n", " try:\n", " # Método 2: Para Jupyter/Colab - usando IPython display\n", " display(imagem)\n", " except:\n", " try:\n", " # Método 3: Método padrão do PIL\n", " imagem.show()\n", " except:\n", " # Método 4: Salvar arquivo localmente\n", " nome_arquivo = \"imagem_gerada.png\"\n", " imagem.save(nome_arquivo)\n", " print(f\"Imagem salva como: {nome_arquivo}\")\n", "\n", "# Geração e exibição da imagem\n", "print(\"Gerando imagem... (pode levar alguns minutos)\")\n", "imagem = gerar_imagem(prompt, num_steps, guidance)\n", "\n", "# Exibe a imagem usando múltiplos métodos\n", "exibir_imagem(imagem, f\"Prompt: {prompt}\")\n", "\n", "# Salva a imagem também\n", "imagem.save(\"floresta_encantada.png\")\n", "print(\"Imagem salva como: floresta_encantada.png\")\n", "\n", "# Função adicional para gerar múltiplas imagens\n", "def gerar_multiplas_imagens(prompt, quantidade=4, steps=50, guidance_scale=7.5):\n", " imagens = []\n", " for i in range(quantidade):\n", " print(f\"Gerando imagem {i+1}/{quantidade}...\")\n", " img = gerar_imagem(prompt, steps, guidance_scale)\n", " imagens.append(img)\n", " \n", " # Salva cada imagem\n", " nome_arquivo = f\"imagem_{i+1}.png\"\n", " img.save(nome_arquivo)\n", " \n", " # Exibe todas as imagens em uma grade\n", " fig, axes = plt.subplots(2, 2, figsize=(15, 15))\n", " fig.suptitle(f\"Prompt: {prompt}\", fontsize=16)\n", " \n", " for i, (img, ax) in enumerate(zip(imagens, axes.flat)):\n", " ax.imshow(img)\n", " ax.axis('off')\n", " ax.set_title(f\"Variação {i+1}\")\n", " \n", " plt.tight_layout()\n", " plt.show()\n", " \n", " return imagens\n", "\n", "# Exemplo de uso da função para múltiplas imagens\n", "# imagens_multiplas = gerar_multiplas_imagens(prompt, quantidade=4)\n", "\n", "```\n", "**Outros Modelos de Diffusão Relevantes**\n", "\n", "* **DDPM (Denoising Diffusion Probabilistic Models):** o design original, aplicando difusão diretamente em pixels.\n", "* **DDIM (Denoising Diffusion Implicit Models):** permite gerar amostras consistentes com muito menos passos via uma discretização alternativa.\n", "* **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.\n", "\n", "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.\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Referências e Conteúdo Extra\n", "- Anotações do Dataset:\n", " - [Roboflow](https://universe.roboflow.com/)\n", "- Vídeo(s)\n", " - [How computers learn to recognize objects instantly](https://youtu.be/Cgxsv1riJhI?si=uaMiD3RmEiJO-oBT)\n", " - [Convolutional Neural Networks (CNNs) explained](https://youtu.be/YRhxdVk_sIs?si=laZ1Z8S8TQnNCuva)\n", "- Outro(s)\n", " - [Interface com o Gradio](https://www.gradio.app/)\n", "\n", "\n" ] } ], "metadata": { "colab": { "provenance": [], "toc_visible": true }, "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.9.20" } }, "nbformat": 4, "nbformat_minor": 4 }