scieee AI-readable full text Open interactive document viewer

Inteligência Artificial - Redes Neurais (dos fundamentos às arquiteturas CNN e RNN)

Palhares Junior, Eduard; SOUZA, WENNDISSON DA; De Jesus dos Santos, Alyson; Lopes Martiniano, Alexandre; RODRIGUES E SILVA, NIVALDO

Abstract

Vivemos em uma era definida pela complexidade dos dados e pela busca por uma inteligência artificial cada vez mais avançada. No epicentro desta revolução está o Deep Learning, ou Aprendizagem Profunda, um subcampo do Machine Learning que permite aos sistemas emular o raciocínio humano por meio de redes neurais profundas. Esta tecnologia tem sido a força motriz por trás de avanços extraordinários, da visão computacional ao processamento de linguagem natural. Este livro foi desenvolvido para guiar o leitor por uma jornada imersiva e acessível no universo do Deep Learning, desde os fundamentos das redes neurais até a construção de modelos de ponta. O conteúdo foi cuidadosamente planejado para atender tanto iniciantes que dão seus primeiros passos em redes neurais quanto profissionais que desejam dominar arquiteturas complexas. No primeiro módulo, são apresentados os conceitos essenciais do Deep Learning, incluindo o funcionamento dos neurônios artificiais, as funções de ativação e o papel de frameworks como TensorFlow e PyTorch. O segundo módulo aprofunda-se em arquiteturas fundamentais, como Redes Neurais Convolucionais (CNNs) para análise de imagens e Redes Neurais Recorrentes (RNNs) para a compreensão de sequências e textos. No terceiro módulo, abordamos os desafios do treinamento de modelos profundos, a otimização de hiperparâmetros, técnicas para evitar o overfitting e a avaliação de performance. Já o módulo final propõe um estudo de caso aplicado à área da saúde, demonstrando como modelos de Deep Learning podem ser usados para o diagnóstico de doenças a partir de imagensmédicas, transformando a precisão e a velocidade da medicina moderna. Mais do que um guia técnico, este livro busca despertar no leitor a capacidade de inovar e a curiosidade para explorar as fronteiras da inteligência artificial. Ao aplicar os conhecimentos adquiridos, o leitor será capaz de construir soluções que não apenas interpretam dados, mas que também percebem, criam e raciocinam. Que esta leitura inspire descobertas, estimule o pensamento criativo e fortaleça sua jornada no fascinante campo da Aprendizagem Profunda.

Full text

25-287138 CDD-006.3 Dados Internacionais de Catalogação na Publicação (CIP) (Câmara Brasileira do Livro, SP, Brasil) Inteligência artificial [livro eletrônico] : redes neurais : (dos fundamentos às arquiteturas CNN e RNN) / Eduardo Palhares Jr....[et al.] ; prefácio por Nivaldo Rodrigues e Silva. -- Manaus, AM : Ed. dos Autores, 2025. PDF Outros autores: Wenndisson da Silva Souza, Alyson de Jesus dos Santos, Alexandre Lopes Martiniano, Nivaldo Rodrigues e Silva. Vários colaboradores. Bibliografia. ISBN 978-65-01-59137-7 1. Ciência da computação 2. Informação - Sistema de armazenagem e recuperação 3. Inteligência artificial - Inovações tecnológicas 4. Redes neurais (Ciência da computação) I. Palhares Jr., Eduardo. II. Souza, Wenndisson da Silva. III. Santos, Alyson de Jesus dos. IV. Martiniano, Alexandre Lopes. V. Silva, Nivaldo Rodrigues e. Índices para catálogo sistemático: 1. Inteligência artificial : Ciência da computação 006.3 Aline Graziele Benitez - Bibliotecária - CRB-1/3129 DOI: 10.5281/zenodo.16411289 Expediente do IFAM MINISTÉRIO DA EDUCAÇÃO SECRETARIA DE EDUCAÇÃO PROFISSIONAL E TECNOLÓGICA INSTITUTO FEDERAL DE EDUCAÇÃO, CIÊNCIA E TECNOLOGIA DO AMAZONAS Reitor Jaime Cavalcante Alves Pró-Reitor de Administração Fábio Teixeira Lima Pró-Reitor de Gestão de Pessoas Leandro Amorim Damasceno Pró-Reitora de Ensino Rosângela Santos da Silva Pró-Reitora de Extensão Maria Francisca Morais de Lima Pró-Reitor de Pesquisa, Pós-Graduação e Inovação Paulo Henrique Rocha Aride Diretor Geral do Campus Manaus Distrito Industrial Nivaldo Rodrigues e Silva Expediente do Projeto CITHA MINISTÉRIO DA EDUCAÇÃO SECRETARIA DE EDUCAÇÃO PROFISSIONAL E TECNOLÓGICA INSTITUTO FEDERAL DE EDUCAÇÃO, CIÊNCIA E TECNOLOGIA DO AMAZONAS Gestores Nivaldo Rodrigues e Silva Samirames da Silva Fleury Alyson de Jesus dos Santos Maria Cassiana Andrade Braga Adanilton Rabelo de Andrade Coordenadores Tiago Francisco Andrade Diocesano Jaidson Brandão da Costa Elcivan dos Santos Silva Martinho Correia Barros Adelino Maia Galvão Filho Expediente de Produção Autores Eduardo Palhares Jr. Wenndisson da Silva Souza Alyson de Jesus dos Santos Alexandre Lopes Martiniano Nivaldo Rodrigues e Silva Avaliação Pedagógica Samirames da Silva Fleury Diagramadores Wenndisson da Silva Souza Eduardo Palhares Jr. Fabio Serra Ribeiro Couto Revisores de Texto Alexandre Lopes Martiniano Alyson de Jesus dos Santos Inteligência Artificial — Redes Neurais (dos fundamentos às arquiteturas CNN e RNN) Autores Eduardo Palhares Jr. Wenndisson da Silva Souza Alyson de Jesus dos Santos Alexandre Lopes Martiniano Nivaldo Rodrigues e Silva Prefácio por Nivaldo Rodrigues e Silva Revisão Alexandre Lopes Martiniano Alyson de Jesus dos Santos 1ª Edição Manaus - AM 2025 Lista de Expressões para Enriquecimento de Conteúdo Este material foi cuidadosamente estruturado para apoiar sua jornada de aprendizado. Ao longo dos capítulos, você encontrará diversas chamadas sinalizadas por ícones especiais, que ajudarão a destacar pontos-chave e enriquecer sua compreensão. Durante a diagramação, esses ícones serão inseridos conforme as indicações dos autores, guiando você para diferentes tipos de conteúdo e atividades que potencializam seu estudo. Fique Alerta! Destaque para conceitos, expressões e trechos fundamentais que merecem sua atenção especial para a compreensão do conteúdo. Iniciando o diálogo... Espaço para reflexão crítica. Aqui você será convidado(a) a problematizar os temas abordados, relacionando-os com sua experiência e buscando conexões relevantes para aprofundar seu aprendizado. Conhecendo um pouco mais! Indicação de fontes complementares, como livros, entrevistas, vídeos, aplicativos, links e outros recursos para ampliar seu conhecimento sobre o tema. Caso Prático Aplicação direta do conteúdo em exemplos concretos, para facilitar a fixação e demonstrar a utilidade do que foi aprendido. Copie e Teste! Trechos de código prontos para serem copiados e executados, para que você possa experimentar, validar e explorar na prática os conceitos estudados. Prefácio Vivemos em uma era definida pela complexidade dos dados e pela busca por uma inteligência artificial cada vez mais avançada. No epicentro desta revolução está o Deep Learning, ou Aprendizagem Profunda, um subcampo do Machine Learning que permite aos sistemas emular o raciocínio humano por meio de redes neurais profundas. Esta tecnologia tem sido a força motriz por trás de avanços extraordinários, da visão computacional ao processamento de linguagem natural. Este livro foi desenvolvido para guiar o leitor por uma jornada imersiva e acessível no universo do Deep Learning, desde os fundamentos das redes neurais até a construção de modelos de ponta. O conteúdo foi cuidadosamente planejado para atender tanto iniciantes que dão seus primeiros passos em redes neurais quanto profissionais que desejam dominar arquiteturas complexas. No primeiro módulo, são apresentados os conceitos essenciais do Deep Learning, incluindo o funcionamento dos neurônios artificiais, as funções de ativação e o papel de frameworks como TensorFlow e PyTorch. O segundo módulo aprofunda-se em arquiteturas fundamentais, como Redes Neurais Convolucionais (CNNs) para análise de imagens e Redes Neurais Recorrentes (RNNs) para a compreensão de sequências e textos. No terceiro módulo, abordamos os desafios do treinamento de modelos profundos, a otimização de hiperparâmetros, técnicas para evitar o overfitting e a avaliação de performance. Já o módulo final propõe um estudo de caso aplicado à área da saúde, demonstrando como modelos de Deep Learning podem ser usados para o diagnóstico de doenças a partir de imagens médicas, transformando a precisão e a velocidade da medicina moderna. Mais do que um guia técnico, este livro busca despertar no leitor a capacidade de inovar e a curiosidade para explorar as fronteiras da inteligência artificial. Ao aplicar os conhecimentos adquiridos, o leitor será capaz de construir soluções que não apenas interpretam dados, mas que também percebem, criam e raciocinam. Que esta leitura inspire descobertas, estimule o pensamento criativo e fortaleça sua jornada no fascinante campo da Aprendizagem Profunda. Projeto de Capacitação e Interiorização em Tecnologias Habilitadoras na Amazônia - CITHA O projeto CITHA surge com o objetivo de fortalecer a economia da Amazônia por meio do incentivo ao empreendedorismo local e do desenvolvimento sustentável. Sua proposta é capacitar profissionais e impulsionar a criação de startups voltadas para a bioeconomia, além de apoiar cooperativas locais na melhoria de seus processos produtivos. A implementação de tecnologias inovadoras é uma das estratégias centrais do projeto, visando oferecer soluções eficientes que atendam às necessidades regionais, como a otimização dos recursos naturais e a melhoria da infraestrutura local. Ao longo de sua execução, o projeto se compromete a integrar os diversos stakeholders, como governos, empresas, ONGs e comunidades, por meio da capacitação da mão de obra local. O objetivo é formar um capital intelectual qualificado, capaz de apoiar uma governança eficiente, promover a inovação e assegurar a sustentabilidade. O CITHA dedica-se à criação de processos internos que incentivem o desenvolvimento de novos métodos e tecnologias, adaptáveis às particularidades do território amazônico. Em síntese, o projeto CITHA visa criar um ciclo de desenvolvimento que não só incentive o empreendedorismo, mas também promova a modernização das estruturas locais, elevando a qualidade de vida das populações da Amazônia. Focado em áreas como bioeconomia, inovação e transferência de tecnologia, o projeto busca estabelecer um ecossistema mais forte e autossustentável, capaz de responder eficientemente às demandas do mercado e da sociedade. CAPÍTULO 1. REDES NEURAIS DENSAS volumes de dados para treinamento. Além disso, problemas como o ”desvanecimento do gradiente”dificultavam o treinamento de redes mais profundas, limitando seu uso a problemas relativamente simples [3]. Foi somente no início dos anos 2000, com o avanço do poder computacional, a disponibilidade massiva de dados digitais e o desenvolvimento de algoritmos eficientes de treinamento, que o Deep Learning começou a mostrar resultados superiores. A introdução de arquiteturas profundas com múltiplas camadas, como as redes neurais convolucionais (CNNs) e recorrentes (RNNs), possibilitou o aprendizado de representações hierárquicas dos dados, capturando padrões cada vez mais abstratos e complexos [2, 3]. Atualmente, o Deep Learning está no centro de diversas inovações tecnológicas, sendo aplicado em áreas como processamento de linguagem natural, visão computacional, jogos e veículos autônomos, entre outras [1]. 1.1.2 Comparação com aprendizado tradicional O aprendizado tradicional de máquina, também conhecido como aprendizado superficial, geralmente depende de técnicas onde o engenheiro de dados ou cientista precisa definir manualmente as características (features) relevantes que descrevem o problema. Esse processo, chamado de engenharia de características, exige conhecimento profundo do domínio e pode ser bastante trabalhoso [3]. Por exemplo, para uma tarefa de classificação de imagens usando aprendizado tradicional, especialistas precisam extrair manualmente características como bordas, texturas e formas, utilizando técnicas de processamento de imagens clássicas [2]. Essas características são então utilizadas como entrada para algoritmos como máquinas de vetores de suporte (SVM), árvores de decisão ou regressão logística. Em contrapartida, o Deep Learning automatiza a extração de características, trabalhando diretamente com os dados brutos. Redes neurais profundas são capazes de aprender múltiplos níveis de representação, das características mais simples nas camadas iniciais até conceitos abstratos nas camadas profundas. Isso elimina a necessidade da engenharia manual intensiva e, muitas vezes, resulta em desempenho superior, especialmente quando há grande quantidade de dados disponíveis [1]. Além disso, o Deep Learning possui a capacidade de generalizar melhor para dados não vistos, desde que bem treinado, e consegue adaptar-se a diversos tipos de dados, incluindo imagens, texto, áudio e séries temporais, tornando-se uma ferramenta versátil para uma ampla gama de aplicações [2, 3]. 1.1.3 Exemplos de aplicação em contextos amazônicos A região amazônica é um ambiente de extrema importância ecológica e social, com vastos recursos naturais e desafios complexos. O Deep Learning tem sido empregado para apoiar o desenvolvimento sustentável e a conservação ambiental nessa região por meio de diversas aplicações inovadoras: •Monitoramento do desmatamento: Através do processamento de imagens de satélite, modelos de Deep Learning são treinados para identificar áreas de desmatamento ilegal em tempo quase real. Isso permite que órgãos ambientais tomem medidas rápidas para conter atividades ilegais, contribuindo para a preservação da floresta. 15 CAPÍTULO 1. REDES NEURAIS DENSAS •Previsão climática: Modelos baseados em redes neurais recorrentes analisam dados históricos meteorológicos para prever eventos climáticos extremos, como enchentes e secas. Essas previsões auxiliam na preparação da população e na tomada de decisões governamentais e agrícolas. •Agricultura de precisão: Sistemas inteligentes que utilizam Deep Learning ajudam pequenos agricultores a monitorar a saúde das plantações, detectar doenças precocemente e otimizar o uso de recursos como água e fertilizantes, aumentando a produtividade e minimizando impactos ambientais. •Reconhecimento de espécies e biodiversidade: Aplicações de visão computacional auxiliam na identificação automática de espécies animais e vegetais a partir de imagens e sons, facilitando pesquisas científicas e o monitoramento da biodiversidade local. Essas aplicações demonstram como o Deep Learning pode ser uma poderosa ferramenta para promover o equilíbrio entre desenvolvimento econômico, inclusão social e preservação ambiental na Amazônia. Fique Alerta! Apesar do enorme potencial, o sucesso do Deep Learning depende fortemente da qualidade e quantidade dos dados disponíveis, do poder computacional e do conhecimento técnico para a modelagem e interpretação dos resultados. Conhecendo um pouco mais! Para aprofundar seus conhecimentos, recomendamos a leitura do livro Deep Learning, de Ian Goodfellow, Yoshua Bengio e Aaron Courville, além dos recursos disponíveis na biblioteca do IFAM e cursos especializados em plataformas como Coursera e edX. 1.1.4 Aplicando seus conhecimentos 1. Explique com suas palavras o que diferencia o Deep Learning do aprendizado tradicional. Cite exemplos práticos. 2. Pesquise e descreva três aplicações do Deep Learning fora da Amazônia que você considera impactantes. 3. Analise um artigo científico que use Deep Learning para monitorar o meio ambiente. Quais dados foram usados? Quais foram os resultados? 4. Discuta os principais desafios que a região amazônica enfrenta para aplicar Deep Learning em larga escala. 5. Implemente um perceptron simples em Python para resolver um problema de classificação binária (por exemplo, AND lógico). 16 CAPÍTULO 1. REDES NEURAIS DENSAS 1.2 Perceptron para o problema lógico AND Caso Prático Nesta seção, implementamos um perceptron simples em Python para resolver o problema do AND lógico, um exemplo clássico para ilustrar redes neurais básicas. O problema consiste em treinar uma rede neural com duas entradas binárias (0 ou 1), de forma que ela aprenda a reproduzir a função lógica AND, cuja saída é 1 apenas quando ambas as entradas forem 1. Este é um ótimo ponto de partida para introduzir conceitos como pesos, bias, função de ativação e atualização por erro. Além de didático, este caso prático mostra na prática como um modelo simples pode aprender padrões por meio de iteração e ajuste de parâmetros, o mesmo princípio que será expandido em redes mais complexas ao longo do livro. Copie e Teste! import numpy as np class Perceptron: def __init__(self, input_size, learning_rate=0.1): self.weights = np.zeros(input_size + 1) # +1 para o bias self.lr = learning_rate def activation(self, x): return 1if x >= 0 else 0 def predict(self, x): x_with_bias = np.insert(x, 0, 1) # Adiciona bias na entrada weighted_sum = np.dot(self.weights, x_with_bias) return self.activation(weighted_sum) def train(self, training_inputs, labels, epochs=10): for _in range(epochs): for x_val, label in zip(training_inputs, labels): prediction = self.predict(x_val) error = label - prediction x_with_bias = np.insert(x_val, 0, 1) self.weights += self.lr * error * x_with_bias if __name__ == '__main__': inputs = np.array([ [0, 0], [0, 1], [1, 0], [1, 1] ]) 17 CAPÍTULO 1. REDES NEURAIS DENSAS labels = np.array([0, 0, 0, 1]) perceptron = Perceptron(input_size=2) perceptron.train(inputs, labels, epochs=10) print("Pesos finais:", perceptron.weights) print("Testes:") for x_val in inputs: print(f"Entrada: {x_val}, Saída: {perceptron.predict(x_val )}") Fique Alerta! Observe que o perceptron utiliza uma função de ativação simples (degrau) e ajusta os pesos iterativamente para minimizar erros. Saída Esperada Pesos finais: [-0.2 0.2 0.1] Testes: Entrada: [0 0], Saída: 0 Entrada: [0 1], Saída: 0 Entrada: [1 0], Saída: 0 Entrada: [1 1], Saída: 1 Explicação detalhada do código •Inicialização: O perceptron é criado com pesos zerados, incluindo um peso extra para o bias, que permite deslocar a função de ativação. •Função de ativação: A função degrau retorna 1 se a soma ponderada das entradas for maior ou igual a zero, caso contrário, retorna 0. É o critério de decisão do perceptron. •Previsão (predict): Recebe uma entrada, adiciona o bias, calcula o produto escalar entre pesos e entradas, e aplica a função de ativação para gerar a saída. •Treinamento: Para cada época (ciclo de treinamento), o perceptron avalia cada entrada, calcula o erro (diferença entre saída desejada e saída prevista) e atualiza os pesos proporcionalmente ao erro e à taxa de aprendizado. •Dados de treinamento: O problema lógico AND tem quatro combinações de entradas binárias e as saídas esperadas correspondentes. •Resultado: Após o treinamento, os pesos ajustados são impressos e o perceptron é testado para confirmar que aprendeu a função AND corretamente. Importância do Perceptron Este exemplo simples ilustra o princípio básico das redes neurais artificiais: ajustar parâmetros internos para mapear entradas a saídas desejadas. Embora o perceptron tenha limitações (como não resolver problemas não linearmente separáveis), ele é a base para arquiteturas mais complexas que compõem o Deep Learning. 18 CAPÍTULO 1. REDES NEURAIS DENSAS Conhecendo um pouco mais! • Neural Networks and Deep Learning - Coursera (Andrew Ng) • Deep Learning - 3Blue1Brown (YouTube) 19 CAPÍTULO 1. REDES NEURAIS DENSAS 1.3 Neurônio Artificial e Funções de Ativação 1.3.1 Modelo matemático do neurônio Um neurônio artificial é a unidade fundamental das redes neurais. Inspirado nos neurônios biológicos, ele recebe um conjunto de entradas x1, x2, . . . , xn, cada uma com um peso associado w1, w2, . . . , wn, e calcula uma saída baseada em uma função de ativação. z= n X i=1 wixi+b(1.1) y=ϕ(z)(1.2) Onde: •z: soma ponderada das entradas (também chamada de entrada líquida) •b: termo de bias, que permite o deslocamento da função de ativação •ϕ(z): função de ativação que determina a saída do neurônio Conhecendo um pouco mais! O termo de bias bfunciona como um limiar ajustável. Mesmo que todas as entradas sejam zero, o neurônio ainda pode produzir uma saída diferente de zero, graças ao bias. 1.3.2 Funções de ativação As funções de ativação determinam a saída de um neurônio com base na entrada líquida. Abaixo, listamos as mais utilizadas: Função Degrau (Step Function) ϕ(z) = (1,se z≥0 0,caso contrário (1.3) É usada em classificadores binários simples, como o Perceptron original. 20 CAPÍTULO 1. REDES NEURAIS DENSAS Figura 1.1: Gráfico da função degrau Fique Alerta! Apesar de simples, a função degrau não é diferenciável, o que dificulta seu uso em redes neurais treinadas com gradiente descendente [3]. Função Sigmoide ϕ(z) = 1 1 + e−z(1.4) Transforma a saída em um valor contínuo entre 0 e 1. Ideal para problemas de classificação binária. Figura 1.2: Gráfico da função sigmoide 21 CAPÍTULO 1. REDES NEURAIS DENSAS Função Tanh (Tangente Hiperbólica) ϕ(z) = tanh(z) = ez−e−z ez+e−z(1.5) Semelhante à sigmoide, mas com saída entre -1 e 1. Costuma ser preferida por apresentar saída centrada em zero [2]. Figura 1.3: Gráfico da função tangente hiperbólica Função ReLU (Rectified Linear Unit) ϕ(z) = max(0, z)(1.6) Atualmente, é a função mais usada em redes profundas por sua simplicidade e eficiência [1, 2]. Figura 1.4: Gráfico da função ReLU 22 CAPÍTULO 1. REDES NEURAIS DENSAS Fique Alerta! A ReLU pode ”morrer”durante o treinamento se os pesos a tornarem negativa para todas as entradas. Uma variação chamada Leaky ReLU resolve isso [2]. 1.3.3 Interpretação gráfica Cada função de ativação possui uma curva característica. As curvas mostram como o neurônio responde a diferentes valores da entrada líquida z. • A função degrau é abrupta e não contínua. • A sigmoide e a tanh são suaves, mas saturam em extremos (problema de gradiente). • A ReLU é linear para valores positivos e zero para negativos. Essas diferenças impactam diretamente o aprendizado da rede neural. Ao longo do livro, veremos quando e por que escolher cada uma dessas funções. 1.3.4 Testando as Funções de Ativação Caso Prático No próximo exemplo, construiremos um neurônio artificial em Python com o objetivo de observar como diferentes funções de ativação influenciam sua saída. A estrutura do neurônio é simples: ele realiza o somatório ponderado das entradas e aplica uma função não linear sobre o resultado, produzindo assim sua saída. Ao variar apenas a função de ativação, mantendo os mesmos valores de entrada, pesos e bias, poderemos visualizar como cada função responde de maneira distinta ao mesmo estímulo. Esse tipo de comparação é fundamental para entender o papel da não linearidade no aprendizado de redes neurais profundas. Com esse estudo de caso, você irá: • Implementar passo a passo um neurônio artificial com Python puro. • Aplicar as funções Degrau, Sigmoide, Tanh e ReLU no mesmo cenário. • Analisar os efeitos das diferentes ativações sobre a saída do neurônio. Esse exercício marca a transição entre a teoria matemática do neurônio e sua implementação prática em código, preparando o terreno para construir redes neurais maiores nos capítulos seguintes. Copie e Teste! import numpy as np # Funções de ativação 23 CAPÍTULO 1. REDES NEURAIS DENSAS def step(z): return 1if z >= 0 else 0 def sigmoid(z): return 1 / (1 + np.exp(-z)) def tanh(z): return np.tanh(z) def relu(z): return np.maximum(0, z) # Neurônio simples def neuron(x, w, b, activation_func): z = np.dot(w, x) + b return activation_func(z) # Exemplo x_input = np.array([0.5, 0.8]) weights = np.array([0.4, -0.6]) bias = 0.1 print("Entradas:", x_input, "Pesos:", weights, "Bias:", bias) print("Soma ponderada (z):", np.dot(weights, x_input) + bias) # z = 0.4*0.5 + (-0.6)*0.8 + 0.1 = 0.2 - 0.48 + 0.1 = -0.18 print("-" * 30) print("Saída com Degrau:", neuron(x_input, weights, bias, step)) print("Saída com Sigmoide:", neuron(x_input, weights, bias, sigmoid)) print("Saída com Tanh:", neuron(x_input, weights, bias, tanh)) print("Saída com ReLU:", neuron(x_input, weights, bias, relu)) Saída Esperada Entradas: [0.5 0.8] Pesos: [0.4 -0.6] Bias: 0.1 Soma ponderada (z): -0.17999999999999997 ------------------------------ Saída com Degrau: 0 Saída com Sigmoide: 0.45512110762641994 Saída com Tanh: -0.17808086811733015 Saída com ReLU: 0.0 1.3.5 Aplicando seus conhecimentos 1. Variação de Entradas: Altere os valores da entrada x= [0.5,0.8] para outros vetores, como [1.0,1.0],[−0.5,0.3], ou [0,0]. Como as saídas do neurônio mudam para cada função de ativação? 24 CAPÍTULO 1. REDES NEURAIS DENSAS Exemplo didático Vamos analisar uma rede simples com as seguintes características: m= 64, n0= 100, n1= 50, n2= 10 •m: número de exemplos no lote (batch size), •n0: número de neurônios na camada de entrada (100 características), •n1: número de neurônios na primeira camada oculta, •n2: número de neurônios na camada de saída. Quantas operações são feitas? A quantidade de operações de multiplicação e soma em cada camada pode ser estimada como: Camada 1: m×(n0×n1) = 64 ×(100 ×50) = 64 ×5000 Camada 2: m×(n1×n2) = 64 ×(50 ×10) = 64 ×500 Total de operações: 64 ×(100 ×50 + 50 ×10) = 64 ×(5000 + 500) = 64 ×5500 = 352000 operações Fique Alerta! Isso significa que, para processar 64 exemplos de entrada, essa rede realiza cerca de 352 mil operações aritméticas (sem contar funções de ativação ou normalização). Isso nos dá uma noção do esforço computacional envolvido, e esse número cresce rapidamente com redes maiores e mais profundas. 31 CAPÍTULO 1. REDES NEURAIS DENSAS 1.5 Função de Custo e Retropropagação Nesta seção, discutiremos como as redes neurais aprendem ajustando seus pesos com base no erro entre a saída prevista e a real. Veremos duas funções de custo comuns e uma introdução intuitiva à retropropagação. 1.5.1 Funções de Custo Erro Quadrático Médio (MSE) O Erro Quadrático Médio (MSE, do inglês Mean Squared Error) é uma das funções de custo mais tradicionais e amplamente utilizadas, especialmente em problemas de regressão, onde o objetivo é prever valores contínuos. A fórmula é dada por: J(θ) = 1 m m X i=1 y(i)−ˆy(i)2 Aqui: •mé o número total de exemplos de treino. •y(i)é o valor real (rótulo) do exemplo i. •ˆy(i)é o valor previsto pela rede para o exemplo i. O MSE calcula a média das diferenças quadradas entre o valor previsto e o real. Por elevar ao quadrado essas diferenças, erros maiores são penalizados com força crescente. Isso ajuda a rede a focar em corrigir grandes discrepâncias. Fique Alerta! Quando o valor previsto está longe do valor real, o erro aumenta de forma quadrática, penalizando fortemente previsões ruins. Exemplo: Suponha que a saída real de uma planta seja 5 cm de crescimento e o modelo preveja 3 cm. O erro para esse exemplo seria (5 −3)2= 4. Agora, se o modelo prever 1 cm, o erro será (5 −1)2= 16, quatro vezes maior, mostrando que erros maiores são mais punidos. Entropia Cruzada (Cross-Entropy) Para problemas de classificação, especialmente quando queremos decidir entre duas ou mais classes, o erro quadrático pode não ser a melhor escolha. Nesses casos, a função de custo mais comum é a entropia cruzada, que mede a distância entre duas distribuições de probabilidade: a verdadeira e a prevista [3]. A fórmula para classificação binária é: J(θ) = −1 m m X i=1 y(i)log ˆy(i)+ (1 −y(i))log 1−ˆy(i) Aqui, y(i)é0ou1,eˆy(i)é a probabilidade prevista da classe 1. 32 CAPÍTULO 1. REDES NEURAIS DENSAS A entropia cruzada penaliza com muita força as previsões erradas feitas com alta confiança. Por exemplo, se a rede prevê 0.99 para a classe 1, mas o rótulo real é 0, o erro será muito alto. Isso força o modelo a ser cauteloso e ajustar as probabilidades para refletir melhor a incerteza. Fique Alerta! Ela é mais sensível a classificações incorretas do que o MSE, ajudando a melhorar a qualidade do modelo em tarefas de classificação. Exemplo: Se a saída verdadeira é 1 (positivo) e a rede prevê 0.9, o erro será baixo, porque a previsão está correta e confiante. Mas se a rede prevê 0.1, o erro será grande, pois há uma alta confiança na classe errada. 1.5.2 A Lógica por Trás da Retropropagação O aprendizado da rede neural acontece ajustando os pesos para minimizar a função de custo. Para isso, usamos o algoritmo de retropropagação, que permite calcular eficientemente o gradiente (a taxa de variação) da função de custo em relação a cada peso. O processo pode ser entendido em três passos principais: •Cálculo das derivadas: Calcula-se como a função de custo muda conforme alteramos cada peso, usando cálculo diferencial. •Propagação do erro para trás: O erro da camada de saída é propagado camada a camada, da saída até a entrada, para atualizar todos os pesos intermediários. •Atualização dos pesos: Usando o gradiente descendente, os pesos são ajustados na direção que diminui o erro, melhorando a performance da rede. Uma analogia para ajudar fixar: Imagine que você está em uma montanha (função de custo) e quer chegar ao ponto mais baixo (erro mínimo). O gradiente é como o mapa da inclinação do terreno: indica em que direção você deve caminhar para descer. A retropropagação é o processo de calcular essa inclinação em cada passo, camada por camada [9]. 1.5.3 Fluxo do Gradiente Para redes neurais profundas, o cálculo dos gradientes não é trivial, pois depende da composição de várias funções, cada camada aplica uma transformação diferente. O segredo para calcular o gradiente corretamente está na regra da cadeia do cálculo diferencial, que permite decompor a derivada da função de custo em produtos de derivadas das funções de ativação e das transformações lineares de cada camada. No entanto, durante esse processo, podem ocorrer dois problemas clássicos: •Gradientes que desaparecem: O valor do gradiente fica muito pequeno à medida que volta para as primeiras camadas, impedindo que essas camadas aprendam adequadamente [2]. •Gradientes explosivos: O valor do gradiente cresce demais, causando instabilidade e dificultando a convergência do treinamento [3]. 33 CAPÍTULO 1. REDES NEURAIS DENSAS Fique Alerta! Para mitigar esses problemas, são utilizadas funções de ativação adequadas, como a ReLU (que ajuda a evitar gradientes muito pequenos) e técnicas como normalização de dados e inicialização cuidadosa dos pesos. Resumo visual do fluxo do gradiente: ∂J ∂W (L),∂J ∂b(L) | {z } gradientes da última camada → · · · → ∂J ∂W (1) ,∂J ∂b(1) | {z } gradientes da primeira camada Onde W(l)eb(l)são os pesos e vieses da camada l. 1.5.4 Cálculo do Gradiente com Entropia Cruzada Vamos simular uma rede simples com uma única camada de saída com ativação sigmoide. Dado um exemplo de entrada, queremos calcular o valor da função de custo e o gradiente da função de custo em relação à saída da rede. Seja: y=rótulo real ∈ {0,1} ˆy=saída prevista pela rede (probabilidade) A função de custo por exemplo é: J=−[ylog(ˆy) + (1 −y)log(1 −ˆy)] O gradiente da função de custo em relação à saída ˆyé: ∂J ∂ˆy=−y ˆy+1−y 1−ˆy Este valor indica como a saída da rede deve mudar para reduzir o erro. Sabendo que ˆy=sigmoid(z)e, portanto, ∂ˆy ∂z = ˆy(1−ˆy), podemos usar a regra da cadeia para encontrar o gradiente da função de custo em relação à entrada da função de ativação, z: ∂J ∂z =∂J ∂ˆy·∂ˆy ∂z =−y ˆy+1−y 1−ˆy·ˆy(1 −ˆy) = −y(1 −ˆy) + (1 −y)ˆy= ˆy−y Este resultado simplificado, ˆy−y, é crucial para a retropropagação e é o que será calculado no código a seguir para obter dz. Copie e Teste! import numpy as np # Entradas e pesos x = np.array([[0.5, 1.2, -0.3]]) # uma amostra com 3 features w = np.array([[0.4], [-0.6], [0.1]]) # pesos (3x1) b = np.array([[0.1]]) # viés 34 CAPÍTULO 1. REDES NEURAIS DENSAS # Função sigmoide def sigmoid(z_val): return 1 / (1 + np.exp(-z_val)) # Forward pass z = np.dot(x, w) + b # saida linear y_pred = sigmoid(z) # saida da rede (probabilidade) # Supõe-se que o rótulo verdadeiro seja 1 y_true = 1 # Gradiente da função de custo (entropia cruzada) em relação a z ( para sigmoide na saída): # dJ/dz = y_pred - y_true dz = y_pred - y_true # Gradiente em relação aos pesos e ao viés dw = np.dot(x.T, dz) db = dz # (soma de dz para múltiplas amostras no batch) print("y_pred =", y_pred) print("dz =", dz) print("dw =", dw) print("db =", db) Saída Esperada y_pred = [[0.38936077]] dz = [[-0.61063923]] dw = [[-0.30531962] [-0.73276708] [ 0.18319177]] db = [[-0.61063923]] A saída prevista pela rede (y_pred) foi aproximadamente 0.3894. Como o valor real (y_true) era 1, o erro (dz =y_pred−y_true) foi de aproximadamente -0.6106. Este valor negativo indica que a previsão da rede está consideravelmente abaixo do valor esperado (1.0), e os pesos (w) e o viés (b) precisam ser ajustados para aumentar a saída da rede na próxima iteração. O gradiente dw (com valores aproximados [[-0.30531962], [-0.73276708], [0.18319177]]) mostra como cada peso deve ser modificado para reduzir esse erro. A ideia é que, durante a atualização (geralmente wnovo =wantigo −α·dw, onde αé a taxa de aprendizagem), os pesos sejam ajustados na direção que aumenta a previsão: • Para o primeiro peso,dw[0] é aproximadamente -0.3053. Como a entrada x[0] (0.5) é positiva, um gradiente negativo resultará em um aumento deste peso, o que contribuirá para aumentar y_pred. • Para o segundo peso,dw[1] é aproximadamente -0.7328. A entrada x[1] (1.2) também é positiva. Assim, este peso também será aumentado, o que também visa elevar 35 CAPÍTULO 1. REDES NEURAIS DENSAS y_pred. O texto original mencionava que este peso ”contribuiu negativamente para o erro e será corrigido na direção positiva”; de fato, seu valor atual (w[1] = -0.6) multiplicado pela entrada (x[1] = 1.2) dá -0.72, puxando a previsão para baixo. O ajuste aumentará w[1]. • Para o terceiro peso,dw[2] é aproximadamente 0.1832. A entrada x[2] (-0.3) é negativa. Um gradiente positivo para uma entrada negativa resultará em uma diminuição deste peso. Diminuir w[2] (por exemplo, de 0.1 para um valor menor) fará com que o produto x[2]*w[2] (que é -0.3 * w[2]) se torne menos negativo (ou mais positivo), o que também ajuda a aumentar y_pred. Da mesma forma, o gradiente db (aproximadamente -0.6106) indica que o viés btambém será aumentado para ajudar a rede a produzir um valor mais próximo de 1.0. 36 CAPÍTULO 1. REDES NEURAIS DENSAS 1.6 Treinamento e Implementação com Keras Aqui, apresentaremos passo a passo como montar e treinar uma rede neural densa utilizando a biblioteca Keras, amplamente usada para projetos de Deep Learning em Python. Ao final, o leitor será capaz de compreender a estrutura de uma rede, separar dados para treinamento e avaliar o desempenho do modelo com métricas simples. 1.6.1 Montagem de uma rede densa Redes densas, também chamadas de fully connected, são aquelas em que cada neurônio de uma camada está conectado a todos os neurônios da camada seguinte. Utilizaremos a API sequencial da Keras para construir nossa rede de forma simples. Copie e Teste! from keras.models import Sequential from keras.layers import Dense # Criação do modelo sequencial modelo = Sequential() modelo.add(Dense(units=8, activation='relu', input_shape=(4,))) # Camada oculta modelo.add(Dense(units=1, activation='sigmoid')) # Camada de saída Este modelo tem 1 camada oculta (8 neurônios, ReLU) e saída binária (Sigmoide). A função Dense define uma camada totalmente conectada (ou densa) da rede neural. Cada neurônio dessa camada recebe todas as saídas da camada anterior como entrada. O parâmetro input_shape=(4,) indica que cada amostra de entrada possui 4 atributos, ou seja, o vetor de entrada tem dimensão 4. Na camada oculta, usamos a função de ativação ReLU (Unidade Linear Retificada), que introduz não linearidade no modelo e ajuda a rede a aprender padrões complexos. Ela transforma valores negativos em zero e mantém valores positivos inalterados. Já na camada de saída, aplicamos a função de ativação Sigmoide, que converte o resultado em um valor entre 0 e 1. Isso é especialmente útil em tarefas de classificação binária, pois permite interpretar a saída como a probabilidade da classe positiva. 1.6.2 Separação de dados Para o exemplo, utilizaremos o famoso conjunto de dados Iris e o adaptaremos para um problema binário que já vimos no curso de Machine Learning do projeto CITHA (por exemplo, classificar se a flor é da espécie setosa ou não). Assim, usaremos o scikit-learn para carregar e separar os dados. Copie e Teste! from sklearn.datasets import load_iris from sklearn.model_selection import train_test_split 37 CAPÍTULO 1. REDES NEURAIS DENSAS import numpy as np # Carregar dados iris = load_iris() X = iris.data y = np.where(iris.target == 0, 1, 0) # 1 se setosa, 0 caso contrário # Dividir em treino e teste X_treino, X_teste, y_treino, y_teste = train_test_split(X, y, test_size=0.3, random_state=42) print(f"Shape X_treino: {X_treino.shape}, Shape y_treino: { y_treino.shape}") print(f"Shape X_teste: {X_teste.shape}, Shape y_teste: {y_teste. shape}") Amostras divididas: 70% para treinamento, 30% para teste. 1.6.3 Treinamento do modelo Agora que temos o modelo e os dados prontos, compilamos a rede definindo a função de perda, o otimizador e a métrica de avaliação. Em seguida, treinamos com o método fit. Copie e Teste! # Compilar o modelo modelo.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy']) # Treinar o modelo historico = modelo.fit(X_treino, y_treino, epochs=50, batch_size =5, verbose=1, validation_data=(X_teste, y_teste)) Fique Alerta! A função de perda binary_crossentropy é adequada para classificação binária. O otimizador adam combina bons resultados com eficiência computacional. 1.6.4 Avaliação e métricas Após o treinamento, avaliamos o modelo no conjunto de teste. A função evaluate retorna a perda e a acurácia. Também podemos exibir previsões e calcular métricas adicionais. Copie e Teste! # Avaliação no teste loss, acc = modelo.evaluate(X_teste, y_teste, verbose=0) print(f"Acurácia no teste: {acc:.2f}") 38 CAPÍTULO 1. REDES NEURAIS DENSAS # Visualizar histórico (exemplo) import matplotlib.pyplot as plt plt.figure(figsize=(12, 5)) plt.subplot(1, 2, 1) plt.plot(historico.history['accuracy'], label='Acurácia (Treino)') plt.plot(historico.history['val_accuracy'], label='Acurácia ( Validação)') plt.title('Evolução da Acurácia') plt.xlabel('Épocas') plt.ylabel('Acurácia') plt.legend() plt.subplot(1, 2, 2) plt.plot(historico.history['loss'], label='Perda (Treino)') plt.plot(historico.history['val_loss'], label='Perda (Validação)') plt.title('Evolução da Perda') plt.xlabel('Épocas') plt.ylabel('Perda') plt.legend() plt.tight_layout() plt.show() Saída Esperada Acurácia no teste: 0.58 Discussão Figura 1.5: Evolução da perda e da acurácia durante o treinamento da rede 39 CAPÍTULO 1. REDES NEURAIS DENSAS O modelo treinado com Keras foi capaz de distinguir eficientemente a classe setosa das demais com alta precisão. Apesar da simplicidade, este exemplo introduz os conceitos essenciais para a construção de modelos mais complexos. A escolha da arquitetura, da função de ativação, dos dados e das métricas influencia diretamente o desempenho da rede. Vendo o resumo da arquitetura Note que a primeira camada (Dense(8)) tem 32 pesos (4 entradas × 8 neurônios) + 8 bias = 40 parâmetros. E a segunda camada tem 8 pesos + 1 bias = 9 parâmetros. Total: 49. Copie e Teste! modelo.summary() Saída Esperada Model: "sequential" __________________________________________________________ Layer (type) Output Shape Param # ========================================================== dense (Dense) (None, 8) 40 dense_1 (Dense) (None, 1) 9 ========================================================== Total params: 149 (600.00 B) Trainable params: 49 (196.00 B) Non-trainable params: 0 (0.00 B) Optimizer params: 100 (404.00 B) 1.6.5 Aplicando seus conhecimentos 1. Modificação da arquitetura: Altere o número de neurônios da camada oculta para 16. Recompile e treine o modelo. A acurácia final muda? Comente o impacto dessa modificação. 2. Batch size e epochs: Execute o treinamento com epochs=100 ebatch_size=10. Compare o tempo de execução e os resultados finais com os do exemplo original. O que muda? 3. Explique com suas palavras: (a) A diferença entre uma camada densa e uma convolucional. (b) Por que é importante dividir os dados em conjuntos de treino e teste? 4. Desafio prático: Substitua a função de ativação da camada oculta por tanh e reexecute o código. Interprete os resultados. 40 CAPÍTULO 1. REDES NEURAIS DENSAS Saída Esperada Figura 1.7: Comparação da Acurácia com penalidade L2 = 0.01 Agora responda: O tempo de convergência muda? Fique Alerta! A regularização L2 atua como um “freio” nos pesos do modelo, penalizando valores muito altos durante o treinamento. Quanto maior o valor de λ, mais restritivo o modelo se torna, o que pode desacelerar o aprendizado. Isso significa que o tempo de convergência pode sim mudar, especialmente para valores maiores. Observe se a curva de acurácia demora mais para subir ou se estabiliza em um patamar diferente. Teste de taxa de aprendizado: Treine o modelo com três valores distintos: 0.1,0.001 e0.0001. Copie e Teste! import numpy as np import matplotlib.pyplot as plt from sklearn.datasets import load_iris from sklearn.model_selection import train_test_split from keras.models import Sequential from keras.layers import Dense from keras.optimizers import Adam # Altere para outros valores como 0.1, 0.001 ou 0.0001 taxa_aprendizado = 0.001 47 CAPÍTULO 1. REDES NEURAIS DENSAS # Dados (classificação binária: setosa ou não) iris = load_iris() X = iris.data y = np.where(iris.target == 0, 1, 0) # 1 se setosa X_treino, X_teste, y_treino, y_teste = train_test_split(X, y, test_size=0.3, random_state=42) # Modelo com Adam e taxa customizada modelo = Sequential() modelo.add(Dense(16, activation='relu', input_shape=(4,))) modelo.add(Dense(1, activation='sigmoid')) modelo.compile(optimizer=Adam(learning_rate=taxa_aprendizado), loss='binary_crossentropy',metrics=['accuracy']) # Treinamento com validação para análise historico = modelo.fit(X_treino, y_treino,epochs=25,verbose=1, validation_split=0.2) # Avaliação no teste loss, acc = modelo.evaluate(X_teste, y_teste, verbose=1) # Gráfico da acurácia por época plt.plot(historico.history['accuracy'], label='Treino') plt.plot(historico.history['val_accuracy'], label='Validação') plt.title(f'Acurácia em {acc:.2f} com Learning Rate = { taxa_aprendizado}') plt.xlabel('Épocas') plt.ylabel('Acurácia') plt.legend() plt.grid(True) plt.show() 48 CAPÍTULO 1. REDES NEURAIS DENSAS Saída Esperada Figura 1.8: Comparação da acurácia com αem 0.001 Agora responda: Qual taxa apresentou melhor curva de perda? Alguma delas impediu o modelo de convergir? Fique Alerta! Ataxa de aprendizado controla o tamanho dos passos dados durante a otimização. Com um valor muito alto (como 0.1), o modelo pode oscilar e não convergir corretamente. Com um valor muito baixo (como 0.0001), a convergência será muito lenta, podendo parecer que o modelo ”não aprende”. Observe as curvas de acurácia e, se possível, a de perda também: a melhor taxa será aquela que apresenta melhora consistente e estável ao longo das épocas, sem grandes oscilações ou estagnação. SGD vs Adam: Compile e treine sua rede usando os dois otimizadores. Copie e Teste! import numpy as np import matplotlib.pyplot as plt from sklearn.datasets import load_iris from sklearn.model_selection import train_test_split from keras.models import Sequential from keras.layers import Dense from keras.optimizers import SGD, Adam 49 CAPÍTULO 1. REDES NEURAIS DENSAS iris = load_iris() X = iris.data y = np.where(iris.target == 0, 1, 0) # Classificação binária: setosa ou não X_treino, X_teste, y_treino, y_teste = train_test_split(X, y, test_size=0.3, random_state=42) def criar_modelo(otimizador): modelo = Sequential([Dense(16, activation='relu', input_shape =(4,)),Dense(1, activation='sigmoid')]) modelo.compile(optimizer=otimizador, loss='binary_crossentropy ', metrics=['accuracy']) return modelo # Modelo SGD modelo_sgd = criar_modelo(SGD(learning_rate=0.01)) hist_sgd = modelo_sgd.fit(X_treino, y_treino, epochs=25, verbose =1, validation_split=0.2) loss_sgd, acc_sgd = modelo_sgd.evaluate(X_teste, y_teste, verbose =1) # Modelo Adam modelo_adam = criar_modelo(Adam(learning_rate=0.001)) hist_adam = modelo_adam.fit(X_treino, y_treino, epochs=25, verbose =1, validation_split=0.2) loss_adam, acc_adam = modelo_adam.evaluate(X_teste, y_teste, verbose=1) # Plotagem do gráfico comparativo plt.figure(figsize=(10, 7)) plt.plot(hist_sgd.history['val_accuracy'], label='Validação - SGD' , color='blue') plt.plot(hist_adam.history['val_accuracy'], label='Validação - Adam', color='green') plt.plot(hist_sgd.history['accuracy'], '--', label='Treino - SGD', color='blue', alpha=0.5) plt.plot(hist_adam.history['accuracy'], '--', label='Treino - Adam ', color='green', alpha=0.5) plt.title(f'Comparação de Acurácia - SGD ({acc_sgd:.2f}) vs Adam ({acc_adam:.2f})') plt.xlabel('Épocas') plt.ylabel('Acurácia') plt.legend() plt.grid(True) plt.tight_layout() plt.show() 50 CAPÍTULO 1. REDES NEURAIS DENSAS Saída Esperada Figura 1.9: Comparação da Acurácia entre SGD e Adam Agora responda: Qual otimizador levou a uma curva mais estável? Algum deles apresentou oscilações ou desempenho inferior? Fique Alerta! Dica para análise: Observe o comportamento das curvas de acurácia ao longo das épocas. O otimizador Adam geralmente apresenta uma curva mais suave e ascendente, com convergência mais rápida, pois adapta a taxa de aprendizado durante o treinamento. Já o SGD pode gerar curvas mais instáveis, com oscilações maiores, especialmente quando a taxa de aprendizado não está bem ajustada. Compare tanto as curvas de treino quanto de validação para perceber padrões de estabilidade ou flutuações no desempenho. Diagnóstico de overfitting: Remova qualquer regularização e aumente o número de neurônios da camada oculta para 64. Copie e Teste! import matplotlib.pyplot as plt from keras.models import Sequential from keras.layers import Dense # Construção do modelo: rede densa com 64 neurônios na camada oculta (mais complexa) e sem regularização modelo = Sequential([Dense(64, activation='relu', input_shape=(4,) ),Dense(1, activation='sigmoid')]) 51 CAPÍTULO 1. REDES NEURAIS DENSAS # Compilação do modelo com otimizador Adam e função de perda para classificação binária modelo.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy']) # Treinamento do modelo com 25 épocas e validação em 20% dos dados de treino historico = modelo.fit(X_treino, y_treino, epochs=25, verbose=1, validation_split=0.2) # Avaliação do modelo no conjunto de treino e teste acc_treino = modelo.evaluate(X_treino, y_treino, verbose=1)[1] acc_teste = modelo.evaluate(X_teste, y_teste, verbose=1)[1] print(f"Acurácia no conjunto de treino: {acc_treino:.2f}") print(f"Acurácia no conjunto de teste: {acc_teste:.2f}") # Plot da acurácia de treino e validação plt.figure(figsize=(12, 5)) plt.subplot(1, 2, 1) plt.plot(historico.history['accuracy'], label='Treino') plt.plot(historico.history['val_accuracy'], label='Validação') plt.title('Acurácia durante o treinamento') plt.xlabel('Épocas') plt.ylabel('Acurácia') plt.legend() plt.grid(True) # Plot da perda de treino e validação plt.subplot(1, 2, 2) plt.plot(historico.history['loss'], label='Perda no Treino') plt.plot(historico.history['val_loss'], label='Perda na Validação' ) plt.title('Perda durante o treinamento') plt.xlabel('Épocas') plt.ylabel('Loss') plt.legend() plt.grid(True) plt.show() 52 CAPÍTULO 1. REDES NEURAIS DENSAS Saída Esperada Figura 1.10: Impacto do Aumento de Neurônios sem Regularização na Acurácia de Treino e Teste Agora responda: O desempenho no treino melhorou? A acurácia no teste piorou? Justifique com base no conceito de overfitting. Fique Alerta! Ao aumentar o número de neurônios na camada oculta sem aplicar regularização, o modelo tende a melhorar seu desempenho no conjunto de treino, pois pode memorizar melhor os dados. No entanto, essa maior capacidade pode fazer com que o modelo se ajuste demais às características específicas do treino, prejudicando sua capacidade de generalização e, consequentemente, reduzindo a acurácia no conjunto de teste. Esse comportamento é conhecido como overfitting, quando o modelo aprende ruídos e detalhes irrelevantes dos dados de treino, comprometendo seu desempenho em dados novos. Vamos discutir: Com base nos seus testes, escreva um parágrafo comparando as técnicas de regularização estudadas. Qual delas pareceu mais eficiente para seu caso? Em que situações cada uma é mais adequada? 1.7.7 Por que os resultados do treinamento mudam? Durante o treinamento de redes neurais, os pesos iniciais dos neurônios são definidos com uma semente aleatória (random seed). Esse comportamento é intencional e fundamental para o bom desempenho dos modelos de aprendizado profundo. O processo de aprendizado é guiado por um algoritmo de otimização (como o gradiente descendente), que tenta encontrar o melhor conjunto de pesos para minimizar a função de custo e como esse processo depende do ponto de partida, isto é, dos valores iniciais dos pesos, diferentes execuções podem seguir caminhos distintos no espaço de soluções. Isso pode resultar em modelos ligeiramente diferentes, com desempenhos variados, mesmo que os dados e o código permaneçam iguais. 53 CAPÍTULO 1. REDES NEURAIS DENSAS Essa aleatoriedade tem uma função estratégica: evita que a rede fique presa em mínimos locais ruins, ou seja, soluções subótimas que não representam o melhor possível que o modelo poderia alcançar. Ao iniciar com valores diferentes, o otimizador tem a chance de explorar melhor o espaço de busca e, potencialmente, encontrar soluções mais eficazes. Portanto, se ao treinar duas vezes o mesmo modelo você notar variações nos valores de perda ou acurácia, saiba que isso é esperado, especialmente em redes pequenas ou com poucos dados. 54 Capítulo 2 Redes Convolucionais (CNN) Iniciando o diálogo... Imagine que você, estudante, faz parte de uma equipe de monitoramento ambiental em sua escola. Um drone foi utilizado para tirar fotos de uma área de floresta próxima à sua comunidade. Seu desafio é analisar essas imagens para identificar locais onde houve queimadas ou desmatamento recente. Mas há dezenas de imagens, e os detalhes nem sempre são fáceis de ver a olho nu. Como ensinar um computador a reconhecer automaticamente esses sinais, como a coloração acinzentada das cinzas ou a ausência de vegetação verde? 2.1 Introdução à Visão Computacional Este desafio mostra a importância da Visão Computacional, área da inteligência artificial dedicada a permitir que máquinas interpretem imagens e vídeos. As Redes Neurais Convolucionais (CNNs) são ferramentas fundamentais nesse processo, permitindo que algoritmos aprendam automaticamente a reconhecer padrões visuais complexos. Aliás, você já tentou organizar uma coleção de fotos em seu celular? Como você as categorizaria? Pelo conteúdo visual, pela data, pelo local? Imagine ensinar isso para um computador, quais informações ele precisaria para entender o que está em cada imagem? As CNNs são amplamente utilizadas para resolver problemas práticos na Amazônia e outras regiões, essas aplicações são exemplos reais e concretos da importância da Visão Computacional, mostrando como o uso das CNNs impacta diretamente a preservação e o estudo do meio ambiente. •Detecção e monitoramento de desmatamento: Imagens de satélite são analisadas continuamente para identificar clareiras novas ou áreas degradadas. CNNs conseguem aprender padrões visuais que indicam desmatamento, mesmo em imagens com diferentes condições de iluminação e clima. •Classificação automática de espécies botânicas: Fotografias de folhas, flores e frutos podem ser usadas para treinar CNNs que identificam automaticamente a espécie da planta, ajudando a catalogar a biodiversidade sem necessidade de um especialista para cada amostra. 55 CAPÍTULO 2. REDES CONVOLUCIONAIS (CNN) •Reconhecimento de animais em câmeras armadilhadas: Para estudos da fauna, câmeras instaladas na floresta capturam milhares de imagens que precisam ser classificadas para identificar as espécies presentes. CNNs aceleram esse processo, evitando a análise manual exaustiva. •Detecção de focos de incêndio e doenças: Imagens aéreas e de drones são usadas para detectar sinais visuais de fogo, fumaça ou doenças em árvores, possibilitando ações rápidas de controle e prevenção. •Reconhecimento de padrões em rios e hidrografia: CNNs auxiliam na análise das mudanças no curso dos rios, assoreamento e alterações ambientais, importantes para o planejamento territorial e conservação. Exercício rápido Pense em outras possíveis aplicações das CNNs. Você consegue nos trazer pelo menos três? 2.1.1 Diferença entre imagens e dados tabulares Figura 2.1: Exemplo de uma típica rede neural convolucional totalmente conectada Fonte:Wikimedia Commons, 2025. Para entender por que as CNNs são especiais para imagens, precisamos compreender as características dos dados visuais em comparação aos dados tabulares tradicionais. Dados tabulares, a estrutura convencional: Em aprendizado de máquina tradicional, como em regressão ou árvores de decisão, trabalhamos com dados organizados em tabelas. Cada linha representa uma amostra, e cada coluna representa uma variável ou atributo. Esses dados geralmente são armazenados em arquivos .csv (Comma-Separated Values), amplamente utilizados por sua simplicidade e compatibilidade com diversas ferramentas. Por exemplo, uma tabela com dados de amostras de solo: Esses atributos são independentes e não possuem uma ordem espacial entre eles. A coluna ”pH”não está ”próxima”da coluna ”Umidade”, elas são variáveis distintas sem relação posicional. 56 CAPÍTULO 2. REDES CONVOLUCIONAIS (CNN) 2.2.3 Interpretação de bordas e padrões Quando aplicamos um kernel a uma imagem, é como se estivéssemos “fazendo perguntas” à imagem, procurando por partes que combinem com o padrão do filtro. Quanto maior o número gerado, mais forte é a presença desse padrão na imagem original. Por isso, os mapas de convolução (as imagens de saída da operação) mostram regiões claras onde o padrão foi detectado e regiões escuras onde não há correspondência. Na prática, esses filtros podem detectar: •Contornos de objetos, como as bordas de uma folha ou de uma letra. •Mudanças abruptas de cor ou textura, como a linha do horizonte ou o contorno de uma estrada. •Regiões salientes, ou seja, partes da imagem com contraste alto que se destacam do fundo. Fique Alerta! Em uma CNN real, os filtros não são escolhidos por nós. A rede aprende automaticamente quais são os melhores filtros para resolver o problema, como classificar imagens ou reconhecer objetos. 2.2.4 Quadro Comparativo de Filtros Clássicos Ao aplicar convoluções sobre uma imagem, diferentes filtros (ou kernels) revelam características distintas. Esses filtros operam como ”lentes”que permitem à rede convolucional enxergar aspectos específicos do padrão visual, como contornos, variações suaves ou detalhes intensificados. A escolha ou aprendizado desses filtros é o que torna as CNNs tão poderosas em tarefas de visão computacional. Na prática, alguns filtros são comumente usados para fins de análise visual e manipulação de imagens. Eles podem ser definidos manualmente para fins didáticos e servem como base para entender o que as redes aprendem automaticamente durante o treinamento. A seguir, apresentamos uma tabela com os filtros clássicos de convolução 3×3, descrevendo suas matrizes, nomes e efeitos esperados. Esses exemplos ajudam a ilustrar os tipos de padrões que podem ser destacados e como diferentes kernels respondem a variações de intensidade nos pixels vizinhos. Fique Alerta! A maioria dos filtros utilizados nas CNNs modernas são aprendidos automaticamente. No entanto, estudar filtros clássicos permite compreender o funcionamento interno e interpretar visualmente o que as redes estão destacando. 63 CAPÍTULO 2. REDES CONVOLUCIONAIS (CNN) Filtro Kernel (3×3) Efeito visual Borda horizontal      −1−1−1 0 0 0 1 1 1      Destaca bordas horizontais, ou seja, regiões da imagem onde há uma transição significativa de claro para escuro na direção vertical. Muito útil para detectar limites superiores ou inferiores de objetos. Borda vertical      −1 0 1 −1 0 1 −1 0 1      Evidencia bordas verticais, realçando transições laterais. Frequentemente aplicado na detecção de colunas, bordas de edifícios ou contornos em texto e sinalizações verticais. Nitidez (Sharpen)      0−1 0 −1 5 −1 0−1 0      Acentua detalhes da imagem ao enfatizar as diferenças entre um pixel e seus vizinhos. Aumenta o contraste local e deixa os contornos mais definidos. Ideal para melhorar a percepção de detalhes. Desfoque (Blur) 1 9     111 111 111      Promove suavização da imagem ao fazer a média dos pixels vizinhos. Utilizado para reduzir ruído, criar efeitos de fundo ou preparar a imagem para outras operações como segmentação. Realce de borda (Laplaciano)      0−1 0 −1 4 −1 0−1 0      Destaca regiões de variação abrupta em todas as direções. É um filtro isotrópico, ou seja, não depende de direção específica. Muito útil em tarefas de detecção de contornos gerais. Tabela 2.2: Filtros clássicos utilizados em convoluções 2D Esses filtros são amplamente utilizados em processamento de imagem tradicional, mas também representam a base conceitual sobre a qual as CNNs operam. Em redes convolucionais treinadas, os filtros são aprendidos automaticamente para se ajustarem aos padrões relevantes das imagens de entrada. Isso permite, por exemplo, que uma rede aprenda a detectar o contorno de folhas, texturas de pele, formas geométricas ou qualquer outro padrão visual característico dos dados fornecidos. Compreender como cada tipo de kernel afeta a imagem ajuda o estudante a interpretar as primeiras camadas de uma CNN, onde geralmente predominam filtros parecidos com os mostrados acima. Isso favorece o desenvolvimento de intuições sobre visualizações de ativação, explicabilidade e diagnóstico de modelos. 64 CAPÍTULO 2. REDES CONVOLUCIONAIS (CNN) Caso Prático Utilize o código apresentado anteriormente em 2.2.2 Convolução Manual como base para realizar novos experimentos. Substitua o kernel original por outros filtros clássicos apresentados na Tabela contida no item 2.2.4. Teste, por exemplo: •Filtro de borda vertical: realça contornos laterais; •Filtro Laplaciano: destaca transições abruptas em todas as direções; •Filtro de nitidez: acentua detalhes e contornos finos. Compare os resultados visuais com a imagem original. Qual filtro melhor evidencia as estruturas presentes? Há diferenças na detecção de bordas horizontais, verticais ou diagonais? Experimente e analise os efeitos gerados por cada matriz de convolução. 65 CAPÍTULO 2. REDES CONVOLUCIONAIS (CNN) 2.3 Padding, Stride e Pooling Nas redes convolucionais, além dos filtros que percorrem a imagem para detectar padrões, existem parâmetros fundamentais que controlam como essas operações são aplicadas: opadding e o stride (CHOLLET, 2021). Esses dois elementos influenciam diretamente no tamanho da imagem processada e na forma como as informações são mantidas ou descartadas. Outro componente essencial é o pooling, uma etapa que reduz o tamanho das imagens intermediárias, resumindo as regiões mais importantes. Essas técnicas combinadas ajudam as CNNs a funcionarem de maneira mais eficiente, veloz e, ao mesmo tempo, mais robusta frente às variações dos dados de entrada. 2.3.1 Redução de dimensão Durante o processo de convolução, os filtros ”varrem”a imagem, e em cada passo geram um valor que forma a nova imagem de saída (chamada de mapa de ativação). Entretanto, ao aplicar essa operação sem ajustes, a nova matriz tende a ser menor do que a original, especialmente nas bordas. Isso acontece porque o filtro só pode ser aplicado totalmente onde ele “cabe” dentro da imagem. Essa redução de tamanho é útil, pois permite que a rede trabalhe com menos dados, economizando memória e processamento. No entanto, é preciso cuidado: se a imagem for reduzida demais logo nas primeiras camadas, podemos perder informações valiosas antes mesmo que a rede aprenda com elas. Padding Para evitar perdas precoces de informação, utilizamos o padding. Essa técnica adiciona uma borda extra ao redor da imagem original, normalmente com valores zero, o que permite que o filtro seja aplicado até nas bordas, sem diminuir tanto o tamanho da imagem resultante. Visualmente, é como se colocássemos uma moldura preta em volta da imagem para que o filtro tenha mais espaço para se movimentar sem ”cair”fora. Conhecendo um pouco mais! Em bibliotecas como o Keras, o parâmetro padding='same' adiciona automaticamente a borda necessária para que a saída tenha o mesmo tamanho da entrada. Já padding='valid' não adiciona nada, o que pode ser útil em algumas situações, como quando queremos reduzir a imagem propositalmente. Stride Ostride define o número de posições que o filtro avança a cada movimento. Por padrão, o valor é 1, o que significa que o filtro anda “passo a passo”. Se aumentarmos o stride para 2, por exemplo, ele “pula” de dois em dois pixels, tanto na horizontal quanto na vertical. Esse comportamento é muito parecido com uma lupa: com stride=1, examinamos cada cantinho da imagem; com stride=2, fazemos uma análise mais rápida, mas menos detalhada. 66 CAPÍTULO 2. REDES CONVOLUCIONAIS (CNN) Fique Alerta! Usar stride maior reduz mais rápido o tamanho da imagem e acelera o processamento. Porém, ao pular muitos pixels, a rede pode deixar de perceber pequenos detalhes ou padrões finos. 2.3.2 Detecção de padrões invariantes Na prática, as imagens podem variar bastante: um objeto pode aparecer em diferentes posições, tamanhos, ou até com pequenas rotações. Um bom modelo de reconhecimento de imagem precisa ser capaz de identificar um mesmo padrão mesmo que ele esteja em um lugar diferente ou levemente deformado. É aí que entra o pooling. Essa etapa “condensa” os dados, extraindo o valor mais relevante de pequenas regiões da imagem. Isso reduz a dimensão e, ao mesmo tempo, ajuda a rede a focar nas partes mais importantes, mesmo que elas mudem de lugar um pouco. Exemplo: imagine que temos uma imagem 6×6. Se aplicarmos pooling com blocos 2×2e stride 2, obteremos uma imagem 3×3, ou seja, com um quarto do tamanho original. Essa compactação torna o aprendizado mais rápido e ajuda a manter os padrões essenciais, mesmo que o objeto se mova ou se transforme ligeiramente. 2.3.3 Max pooling e average pooling Opooling pode ser feito de diferentes maneiras, dependendo do que queremos extrair de cada bloco da imagem: •Max pooling: seleciona o maior valor do bloco. Isso ajuda a preservar os pontos mais intensos da imagem, como bordas bem definidas ou regiões com forte contraste. Funciona como um “destaque” automático do que mais chama atenção (GÉRON, 2023). •Average pooling: faz a média dos valores do bloco. Isso suaviza a imagem, mantendo uma representação mais generalizada e menos sensível a picos individuais. Pode ser útil para preservar um “panorama geral” da imagem (GÉRON, 2023). Conhecendo um pouco mais! Em redes modernas, o max pooling é mais comum, pois ele preserva melhor os traços fortes que as redes usam para tomar decisões. Essas técnicas combinadas, padding,stride epooling, ajudam a construir redes convolucionais mais poderosas, capazes de reconhecer padrões em diferentes posições, tamanhos e com maior eficiência. 2.3.4 Aplicando seus conhecimentos Utilize o código em Python a seguir para ajudar na solução dos exercícios abaixo: Copie e Teste! import tensorflow as tf from tensorflow.keras.layers import Conv2D 67 CAPÍTULO 2. REDES CONVOLUCIONAIS (CNN) # Imagem 8x8x1 (batch_size, height, width, channels) imagem_exemplo_sp = tf.random.uniform((1, 8, 8, 1)) # Stride e redução de imagem print("--- Testando Stride ---") filtro_stride1 = Conv2D(filters=1, kernel_size=3, strides=1, padding='valid') saida_s1 = filtro_stride1(imagem_exemplo_sp) print(f"Stride 1, Padding 'valid': Tamanho da saída: {saida_s1. shape}") filtro_stride2 = Conv2D(filters=1, kernel_size=3, strides=2, padding='valid') saida_s2 = filtro_stride2(imagem_exemplo_sp) print(f"Stride 2, Padding 'valid': Tamanho da saída: {saida_s2. shape}") # Comparando padding SAME e VALID print("\n--- Testando Padding ---") filtro_padding_valid = Conv2D(filters=1, kernel_size=3, strides=1, padding='valid') saida_pv = filtro_padding_valid(imagem_exemplo_sp) print(f"Padding 'valid', Stride 1: Tamanho da saída: {saida_pv. shape}") filtro_padding_same = Conv2D(filters=1, kernel_size=3, strides=1, padding='same') saida_ps = filtro_padding_same(imagem_exemplo_sp) print(f"Padding 'same', Stride 1: Tamanho da saída: {saida_ps. shape}") Stride e redução de imagem: Altere o valor do stride de 1 para 2 na aplicação do filtro convolucional. Observe a diferença no tamanho da saída. Compare padding SAME e VALID: Execute a mesma convolução com padding='same' epadding='valid'. Explique a diferença no tamanho da saída. Agora utilize este outro código em Python para ajudar a solucionar os exercícios abaixo: Copie e Teste! import tensorflow as tf from tensorflow.keras.layers import MaxPooling2D, AveragePooling2D import numpy as np # Imagem artificial (batch_size, height, width, channels) imagem_exemplo_pool = tf.constant([[[[1.0], [2.0], [3.0], [4.0]], [[5.0], [6.0], [7.0], [8.0]], [[9.0], [10.0], [11.0], [12.0]], [[13.0], [14.0], [15.0], [16.0]]]], dtype=tf.float32) 68 CAPÍTULO 2. REDES CONVOLUCIONAIS (CNN) print("--- Max Pooling vs Average Pooling (2x2, stride 2) ---") maxpool_layer = MaxPooling2D(pool_size=(2, 2), strides=2) maxpool_output = maxpool_layer(imagem_exemplo_pool) print("Max pooling (2x2, stride 2):\n", tf.squeeze(maxpool_output) .numpy()) avgpool_layer = AveragePooling2D(pool_size=(2, 2), strides=2) avgpool_output = avgpool_layer(imagem_exemplo_pool) print("\nAverage pooling (2x2, stride 2):\n", tf.squeeze( avgpool_output).numpy()) print("\n--- Max Pooling com Stride 1 ---") maxpool_stride1_layer = MaxPooling2D(pool_size=(2,2), strides=1) maxpool_stride1_output = maxpool_stride1_layer(imagem_exemplo_pool ) print("Max pooling (2x2, stride 1):\n", tf.squeeze( maxpool_stride1_output).numpy()) print(f"Tamanho da saída com Stride 1: {maxpool_stride1_output. shape}") Max pooling × Average pooling: Rode o código e aplique MaxPooling2D e AveragePooling2D com janela 2×2. Qual operação mantém os valores máximos? Qual suaviza os dados? Compare os resultados. Explore stride maior em pooling: Modifique o parâmetro strides para 1 no MaxPooling2D e observe como o resultado muda. A saída fica maior? O que isso significa? 2.3.5 Caso prático com imagem real Vamos agora aplicar tudo o que aprendemos em um exemplo com imagem real. Usaremos uma foto da cidade de Lábrea, localizada no interior do estado do Amazonas. O objetivo é simular como uma rede convolucional pode reduzir a resolução de uma imagem com MaxPooling, e em seguida tentar recuperar o formato original com reamostragem. Caso Prático Carregue uma imagem em tons de cinza e aplique pooling duas vezes para reduzir a resolução. Em seguida, reamostre a imagem para o tamanho original e observe visualmente a perda de detalhes. Esse processo simula como as redes extraem o essencial e descartam ruídos ou detalhes irrelevantes. Copie e Teste! import requests from PIL import Image from io import BytesIO import matplotlib.pyplot as plt import numpy as np import cv2 # OpenCV para resize 69 CAPÍTULO 2. REDES CONVOLUCIONAIS (CNN) from skimage.measure import block_reduce # Para MaxPooling manual # Baixar a imagem aérea de Lábrea url = 'https://portalamazonia.com/wp-content/uploads/2021/08/ b2ap3_large_Labrea.jpeg' cabecalhos = {'User-Agent':'Mozilla/5.0'}# Simular um navegador try: resposta = requests.get(url, headers=cabecalhos) resposta.raise_for_status() # Levanta erro para códigos HTTP ruins (4xx ou 5xx) imagem_original_pil = Image.open(BytesIO(resposta.content)) imagem_cinza_pil = imagem_original_pil.convert('L') imagem_cinza_reduzida_pil = imagem_cinza_pil.resize((256, 256) ) imagem_cinza_normalizada = np.array(imagem_cinza_reduzida_pil) / 255.0 # Aplicar MaxPooling duas vezes (reduz pela metade em cada etapa) # block_size=(2,2) e func=np.max simula MaxPooling2D com pool_size=(2,2) e strides=(2,2) imagem_pool_1x = block_reduce(imagem_cinza_normalizada, block_size=(2,2), func=np.max) imagem_pool_2x = block_reduce(imagem_pool_1x, block_size=(2,2) , func=np.max) # Reamostrar para o tamanho original (256x256) imagem_upsample = cv2.resize(imagem_pool_2x, (256, 256), interpolation=cv2.INTER_NEAREST) # Visualização fig, eixos = plt.subplots(2, 2, figsize=(10,10)) eixos[0][0].imshow(imagem_original_pil) eixos[0][0].set_title("Imagem Original (Colorida)") eixos[0][1].imshow(imagem_cinza_reduzida_pil, cmap='gray') eixos[0][1].set_title("Imagem Cinza Redimensionada (256x256)") eixos[1][0].imshow(imagem_pool_2x, cmap='gray') eixos[1][0].set_title(f"Após 2x MaxPooling (Tamanho: { imagem_pool_2x.shape})") eixos[1][1].imshow(imagem_upsample, cmap='gray') eixos[1][1].set_title("Reamostrada (Upsample para 256x256)") for linha_ax in eixos: for eixo_ax in linha_ax: eixo_ax.axis('off') plt.tight_layout() plt.show() 70 CAPÍTULO 2. REDES CONVOLUCIONAIS (CNN) except requests.exceptions.RequestException as e: print(f"Erro ao baixar a imagem: {e}") except Exception as e: print(f"Ocorreu um erro: {e}") Saída Esperada Figura 2.5: Comparação das imagens: original, após pooling e reamostragem - Lábrea-AM Fonte:portalamazonia.com, 2021. 71 CAPÍTULO 2. REDES CONVOLUCIONAIS (CNN) Fique Alerta! Você percebeu que a imagem após o pooling ainda conserva o formato geral da cidade, mas perdeu muitos detalhes finos como telhados, ruas e vegetação? Esse processo mostra como as redes CNN resumem as informações visuais, mantendo o que é mais importante para a tarefa (como detectar formas gerais), e ignorando o que pode ser ruído. Conhecendo um pouco mais! Na prática, redes convolucionais utilizam pooling para acelerar o treinamento, reduzir memória e tornar o modelo mais robusto a pequenas variações de posição e escala. 72 CAPÍTULO 2. REDES CONVOLUCIONAIS (CNN) modelo_lenet.add(MaxPooling2D(pool_size=(2,2))) # Camada Convolucional 2 + Pooling modelo_lenet.add(Conv2D(32, (5,5), activation='relu')) modelo_lenet.add(MaxPooling2D(pool_size=(2,2))) # Flatten e Camadas Densas modelo_lenet.add(Flatten()) modelo_lenet.add(Dropout(0.3)) #Camada de regularização modelo_lenet.add(Dense(120, activation='relu')) modelo_lenet.add(Dense(84, activation='relu')) modelo_lenet.add(Dense(10, activation='softmax')) # 10 classes para os dígitos 0-9 modelo_lenet.summary() modelo_lenet.compile(optimizer=Adam(learning_rate= learning_rate_teste), loss='categorical_crossentropy', metrics =['accuracy']) print("\nIniciando treinamento do modelo LeNet...") historico_lenet = modelo_lenet.fit(X_train_mnist, y_train_mnist_cat, epochs=5, batch_size=128, validation_split =0.1, verbose=1) loss_lenet, acc_lenet = modelo_lenet.evaluate(X_test_mnist, y_test_mnist_cat, verbose=0) print(f"\nAcurácia no teste MNIST: {acc_lenet*100:.2f}%") Visualize erros de classificação: Após treinar, mostre algumas imagens onde o modelo errou a previsão e analise os padrões. Copie e Teste! import matplotlib.pyplot as plt y_pred_probs_lenet = modelo_lenet.predict(X_test_mnist) y_pred_classes_lenet = np.argmax(y_pred_probs_lenet, axis=1) # Encontrar os índices dos erros erros_indices = np.where(y_pred_classes_lenet != y_test_mnist)[0] # Mostrar alguns erros plt.figure(figsize=(8, 16)) for i, idx_erro in enumerate(erros_indices[:8]): # Mostra os primeiros 8 erros plt.subplot(4, 2, i + 1) plt.imshow(X_test_mnist[idx_erro].reshape(28, 28), cmap='gray' ) plt.title(f"Previsto: {y_pred_classes_lenet[idx_erro]}, Correto: {y_test_mnist[idx_erro]}") 79 CAPÍTULO 2. REDES CONVOLUCIONAIS (CNN) plt.axis('off') plt.tight_layout() plt.show() Fique Alerta! Essas variações a seguir são comuns em experimentos reais. Ajustar camadas, funções, regularizações e taxas de aprendizado faz parte do trabalho de quem constrói redes neurais. Saída Esperada Figura 2.7: Exemplos de Dígitos MNIST Classificados Incorretamente pelo Modelo LeNet 80 CAPÍTULO 2. REDES CONVOLUCIONAIS (CNN) 2.5 O que a rede está vendo? Visualização de Filtros e Camadas As redes convolucionais (CNNs) ganharam destaque devido à sua impressionante capacidade de reconhecer objetos, padrões e até doenças em imagens. Mas surge uma dúvida comum entre iniciantes: O que exatamente a rede está enxergando? Neste item, vamos explorar ferramentas e técnicas que nos permitem “espiar” o interior de uma rede neural convolucional e entender como as informações são processadas em cada camada. Essa prática, conhecida como visualização de filtros e camadas, nos ajuda a entender como uma CNN transforma uma imagem de entrada, como uma folha de planta em uma decisão de “doente” ou “saudável”. 2.5.1 O que são mapas de ativação? Durante o processamento, cada camada convolucional aplica um conjunto de filtros sobre a imagem. Cada filtro tem a missão de detectar padrões específicos, como bordas, curvas ou manchas. O resultado é chamado de mapa de ativação (ou feature map). Conhecendo um pouco mais! Imagine um detector de calor. Ele mostra em vermelho as áreas mais quentes e em azul as frias. De forma semelhante, um mapa de ativação indica as “regiões mais relevantes” da imagem para aquele filtro específico. Esses mapas são produzidos camada por camada. As primeiras camadas geralmente destacam formas simples, como contornos, enquanto as camadas mais profundas reconhecem padrões mais abstratos, como nervuras de folhas ou formatos de letras. 2.5.2 Como podemos extrair os mapas de ativação? Para visualizar esses mapas, usamos um recurso do Keras que permite criar um modelo intermediário. Com ele, podemos acessar a saída de uma camada específica ao fornecer uma imagem de entrada, como se estivéssemos “pausando” o cérebro da rede em determinado momento. 2.5.3 Visualizando os filtros Cada filtro gera um mapa em tons de cinza, que mostra onde ele foi ativado. Por exemplo, ao usar uma imagem do número “5” do MNIST, um filtro pode se ativar mais nas curvas superiores, enquanto outro responde às linhas verticais. Conhecendo um pouco mais! Pense nos filtros como diferentes lupas: uma realça as bordas, outra destaca sombras, outra ignora o fundo e foca nos traços centrais. Juntas, elas formam uma representação rica da imagem. 81 CAPÍTULO 2. REDES CONVOLUCIONAIS (CNN) Ao exibir os mapas lado a lado, percebemos como cada filtro “enxerga” aspectos distintos da imagem, mesmo sem qualquer programação explícita. 2.5.4 O que a rede está vendo? As CNNs processam as imagens de forma hierárquica, em três níveis: •Camadas iniciais: detectam bordas, contrastes e linhas. •Camadas intermediárias: combinam essas formas em estruturas mais complexas. •Camadas finais: extraem os padrões mais significativos e tomam decisões [1, 3]. Essa estrutura reflete a forma como o ser humano enxerga: primeiro percebemos formas básicas e depois reconhecemos o todo. Fique Alerta! Se um filtro estiver ativando regiões irrelevantes ou gerando mapas vazios, pode indicar que o modelo está aprendendo padrões errados ou ainda não foi treinado adequadamente. 2.5.5 Caso Prático: Visualizando o MNIST Para ilustrar na prática, vamos utilizar um exemplo clássico: o conjunto de dados MNIST. Suponha que aplicamos uma imagem do dígito “9” a uma CNN já treinada. Ao observar os mapas de ativação, notamos que: • Alguns filtros se ativam fortemente em regiões com contornos circulares; • Outros quase não apresentam ativação; • A combinação dessas ativações forma uma espécie de “assinatura visual” única para o número. Caso Prático Parte 1: Primeiras camadas convolucionais Escolha uma imagem do MNIST (por exemplo, o dígito 9) e visualize os mapas de ativação das duas primeiras camadas convolucionais. Quais partes do dígito cada filtro destaca? O que muda ao avançar para a segunda camada? Parte 2: Camadas mais profundas e saída da rede Agora visualize os mapas de ativação da segunda camada convolucional e também a probabilidade final de classificação da rede para a mesma imagem. Como os padrões evoluem? A rede classificou corretamente? O que podemos aprender sobre o processo de decisão da rede a partir dessas informações? Copie e Teste! from tensorflow.keras.models import Model from tensorflow.keras.layers import Input # mnist, to_categorical, Conv2D, MaxPooling2D, Flatten, Dense, np, 82 CAPÍTULO 2. REDES CONVOLUCIONAIS (CNN) plt já devem ter sido importados # 1. Dados (reutilizando X_train_mnist, y_train_mnist_cat, X_test_mnist da seção 2.4.3) if 'X_train_mnist' not in locals(): # Segurança caso não tenha rodado a célula anterior print("Carregando dados MNIST novamente...") (X_train_mnist, y_train_mnist), (X_test_mnist, y_test_mnist_orig) = mnist.load_data() X_train_mnist = X_train_mnist.reshape(-1, 28, 28, 1).astype(' float32') / 255.0 X_test_mnist = X_test_mnist.reshape(-1, 28, 28, 1).astype(' float32') / 255.0 y_train_mnist_cat = to_categorical(y_train_mnist, 10) y_test_mnist_cat = to_categorical(y_test_mnist_orig, 10) # 2. Modelo funcional (para facilitar acesso às camadas) input_img = Input(shape=(28, 28, 1)) x_conv1 = Conv2D(8, (3,3), activation='relu', padding='same', name ='conv1')(input_img) # Adicionado padding x_pool1 = MaxPooling2D((2,2), name='pool1')(x_conv1) x_conv2 = Conv2D(16, (3,3), activation='relu', padding='same', name='conv2')(x_pool1) # Adicionado padding x_pool2 = MaxPooling2D((2,2), name='pool2')(x_conv2) x_flat = Flatten(name='flatten')(x_pool2) output_final = Dense(10, activation='softmax', name='output_dense' )(x_flat) modelo_viz = Model(inputs=input_img, outputs=output_final) modelo_viz.compile(optimizer='adam', loss=' categorical_crossentropy', metrics=['accuracy']) print("\nTreinando modelo para visualização...") modelo_viz.fit(X_train_mnist, y_train_mnist_cat, epochs=3, batch_size=128, verbose=1, validation_split=0.1) # Treino rápido # 3. Escolher imagem de teste (ex: dígito 9, índice pode variar) # Para encontrar um '9', por exemplo: # indice_digito_9 = np.where(y_test_mnist_orig == 9)[0] # if len(indice_digito_9) > 0: # indice_img_teste = indice_digito_9[0] # Pega o primeiro '9' # else: # indice_img_teste = 0 # Fallback indice_img_teste = 9 imagem_teste_viz = X_test_mnist[indice_img_teste].reshape(1, 28, 28, 1) label_real_viz = y_test_mnist_orig[indice_img_teste] if ' y_test_mnist_orig' in locals() else np.argmax(y_test_mnist_cat[ 83 CAPÍTULO 2. REDES CONVOLUCIONAIS (CNN) indice_img_teste]) print(f"\nVisualizando ativações para a imagem de teste (índice { indice_img_teste}, Rótulo Real: {label_real_viz})") # 4. Modelos para visualizar ativações das camadas Conv2D modelo_camada1_out = Model(inputs=modelo_viz.inputs, outputs= modelo_viz.get_layer('conv1').output) modelo_camada2_out = Model(inputs=modelo_viz.inputs, outputs= modelo_viz.get_layer('conv2').output) ativacoes1 = modelo_camada1_out.predict(imagem_teste_viz) ativacoes2 = modelo_camada2_out.predict(imagem_teste_viz) # 5. Mostrar mapas da 1ª camada convolucional (conv1 - 8 filtros) plt.figure(figsize=(10, 6.5)) plt.suptitle(f"Ativações da 1ª Camada Convolucional (conv1) para Dígito '{label_real_viz}'", fontsize=16) for iin range(ativacoes1.shape[-1]): # Itera sobre os filtros plt.subplot(2, 4, i + 1) # Assumindo 8 filtros plt.imshow(ativacoes1[0, :, :, i], cmap='viridis')# 'viridis' é uma boa alternativa a 'gray' plt.title(f'Filtro {i+1}') plt.axis('off') plt.tight_layout(rect=[0, 0, 1, 0.96]) plt.show() # 6. Mostrar mapas da 2ª camada convolucional (conv2 - 16 filtros) plt.figure(figsize=(10.5, 11)) plt.suptitle(f"Ativações da 2ª Camada Convolucional (conv2) para Dígito '{label_real_viz}'", fontsize=16) for iin range(ativacoes2.shape[-1]): # Itera sobre os filtros plt.subplot(4, 4, i + 1) # Assumindo 16 filtros plt.imshow(ativacoes2[0, :, :, i], cmap='viridis') plt.title(f'Filtro {i+1}') plt.axis('off') plt.tight_layout(rect=[0, 0, 1, 0.96]) plt.show() # 7. Prever a saída final para a imagem e mostrar saida_final_viz_probs = modelo_viz.predict(imagem_teste_viz) predito_viz_classe = np.argmax(saida_final_viz_probs[0]) fig_out, ax_out = plt.subplots(1, 2, figsize=(10, 4)) # Imagem de entrada ax_out[0].imshow(imagem_teste_viz.reshape(28,28), cmap='gray') ax_out[0].set_title(f"Imagem de Entrada (Real: {label_real_viz})") ax_out[0].axis('off') 84 CAPÍTULO 2. REDES CONVOLUCIONAIS (CNN) # Saída final (probabilidades) barras = ax_out[1].bar(range(10), saida_final_viz_probs[0], color= 'skyblue') barras[predito_viz_classe].set_color('salmon') ax_out[1].set_title(f"Saída Final - Previsto: {predito_viz_classe} ") ax_out[1].set_xlabel("Dígito") ax_out[1].set_ylabel("Probabilidade") ax_out[1].set_xticks(range(10)) ax_out[1].grid(axis='y', linestyle='--') plt.tight_layout() plt.show() Saída Esperada Figura 2.8: Ativações da 1ª camada Fique Alerta! As camadas iniciais detectam padrões simples. Camadas posteriores combinam esses padrões e a última camada converte tudo em uma decisão de classificação. Se a rede erra, você pode usar essas visualizações para entender onde o problema começou. Conhecendo um pouco mais! Você pode adaptar esse código para visualizar outras imagens e até salvar as ativações em arquivos PNG. Use indice = ... para alterar a imagem de entrada. 85 CAPÍTULO 2. REDES CONVOLUCIONAIS (CNN) Saída Esperada Figura 2.9: Ativações da 2ª camada Fique Alerta! Filtros “apagados” podem indicar que eles não estão sendo usados, isso é comum com redes rasas ou com poucos dados. Considere treinar por mais épocas. 86 CAPÍTULO 2. REDES CONVOLUCIONAIS (CNN) Saída Esperada Figura 2.10: Imagem de Entrada e saída final da rede para imagem de teste 2.5.6 O que aprendemos com as ativações? Ao visualizar as ativações internas de uma rede convolucional, conseguimos entender de forma concreta o que cada camada ”vê”e aprende. Isso traz diversos benefícios: • Permite ajustar a arquitetura da rede (número e tipo de camadas) com mais fundamento; • Facilita a identificação de gargalos ou excessos na extração de características; • Ajuda a diagnosticar erros de classificação, revelando o que pode estar confundindo o modelo; • Torna mais acessível a comunicação dos resultados, especialmente com públicos não técnicos. Conhecendo um pouco mais! Ferramentas como gradCAM,keras-vis etf-explain permitem uma análise ainda mais rica: elas evidenciam visualmente quais regiões da imagem mais influenciaram a decisão final da rede. Esses recursos reforçam um dos pilares do aprendizado profundo moderno: a interpretabilidade. Em aplicações críticas, como diagnósticos médicos, segurança pública e monitoramento ambiental, entender como e por que um modelo toma suas decisões é tão importante quanto alcançar alta acurácia. 87 Capítulo 3 Redes Recorrentes (RNN) Iniciando o diálogo... Você já tentou prever o próximo passo de uma música, entender o sentimento em uma frase, ou antecipar o clima de amanhã? Todos esses exemplos têm algo em comum: são construídos com base em sequências. Neste capítulo, vamos explorar como as máquinas também podem aprender com o tempo, lembrando de eventos passados para tomar decisões melhores no presente. Prepare-se para descobrir como as Redes Recorrentes (RNNs) conseguem “pensar” passo a passo, como fazemos quando lemos, ouvimos ou lembramos de algo importante. 3.1 Sequências e o Eixo Temporal Vivemos em um mundo onde os dados muitas vezes seguem uma ordem. Pense nas palavras que você está lendo agora: elas têm um sentido porque seguem uma sequência. Se embaralhássemos tudo, o texto perderia o significado. O mesmo acontece com muitos outros tipos de informação, e para tratá-los, precisamos de modelos que respeitem essa ordem. As Redes Neurais Recorrentes (RNN) foram criadas justamente para isso. 3.1.1 Como assim, aprender com o tempo? A maioria das redes neurais tradicionais (como as densas ou convolucionais) parte do princípio de que todas as entradas são independentes. Isso funciona bem com imagens ou tabelas, onde a posição de uma célula não muda com o tempo. Mas nem todos os problemas seguem esse padrão. Imagine as seguintes situações: •Previsão do clima: é impossível prever a temperatura de amanhã sem considerar os dias anteriores. •Tradução de textos: o significado de uma palavra pode mudar completamente dependendo das anteriores. •Diagnóstico com sensores: um único batimento cardíaco diz pouco, é o padrão ao longo do tempo que indica se está tudo bem. 88 CAPÍTULO 3. REDES RECORRENTES (RNN) entender o enredo. Uma RNN comum pode ter esquecido esse detalhe, já uma LSTM ou GRU o teria mantido na memória para fazer a conexão no momento certo. Por que isso é importante? Redes com memória de longo prazo, como LSTM e GRU, são essenciais em diversas aplicações como: •Previsão do tempo: lembrar da tendência de dias anteriores pode indicar mudanças futuras. •Tradução automática: a tradução correta de uma palavra depende do contexto de várias outras. •Reconhecimento de padrões em texto ou música: estruturas que se repetem só podem ser compreendidas se forem lembradas. •Diagnóstico médico: sintomas iniciais podem indicar um quadro clínico mesmo após vários dias. Fique Alerta! Modelos como LSTM e GRU são projetados justamente para manter essas lembranças. Se você precisa que o modelo “ligue os pontos” ao longo do tempo, essas redes são suas melhores aliadas. 95 CAPÍTULO 3. REDES RECORRENTES (RNN) 3.4 Aplicações Simples de RNN As Redes Neurais Recorrentes (RNNs) são poderosas em tarefas sequenciais, como previsão de séries temporais. A seguir, exploramos um exemplo prático que simula a previsão da temperatura diária com base nos dias anteriores. 3.4.1 Caso prático: Previsão de Temperatura Caso Prático Vamos simular uma sequência de temperaturas diárias artificiais e treinar três modelos de rede neural, SimpleRNN,LSTM eGRU, para prever a temperatura do próximo dia com base nos últimos 20 dias. Essa tarefa representa um cenário típico de série temporal. Copie e Teste! import numpy as np import matplotlib.pyplot as plt from keras.models import Sequential from keras.layers import SimpleRNN, LSTM, GRU, Dense from sklearn.metrics import mean_squared_error # Configurações iniciais np.random.seed(42) total_dias = 1000 # Total de dias simulados janela_temporal = 20 # Número de dias usados como entrada # Gerar dados simulados: função seno + ruído gaussiano temperaturas = np.sin(np.arange(total_dias) * 0.05) * 10 + 25 + np .random.normal(0, 1, total_dias) # Construção da base de dados sequencial entradas, saidas = [], [] for iin range(len(temperaturas) - janela_temporal): entradas.append(temperaturas[i:i + janela_temporal]) saidas.append(temperaturas[i + janela_temporal]) entradas = np.array(entradas).reshape(-1, janela_temporal, 1) saidas = np.array(saidas) # Divisão em treino (80%) e teste (20%) indice_limite = int(len(entradas) * 0.8) entradas_treino, entradas_teste = entradas[:indice_limite], entradas[indice_limite:] saidas_treino, saidas_teste = saidas[:indice_limite], saidas[ indice_limite:] # Função para criar o modelo def criar_modelo(tipo_rede='RNN'): modelo = Sequential() 96 CAPÍTULO 3. REDES RECORRENTES (RNN) if tipo_rede == 'RNN': modelo.add(SimpleRNN(50, input_shape=(janela_temporal, 1)) ) elif tipo_rede == 'LSTM': modelo.add(LSTM(50, input_shape=(janela_temporal, 1))) elif tipo_rede == 'GRU': modelo.add(GRU(50, input_shape=(janela_temporal, 1))) modelo.add(Dense(1)) modelo.compile(optimizer='adam', loss='mse') return modelo # Treinamento e avaliação dos modelos resultados_mse = {} plt.figure(figsize=(10, 12)) for i, tipo_rede in enumerate(['RNN','LSTM','GRU']): print(f"\nTreinando modelo {tipo_rede}...") modelo = criar_modelo(tipo_rede) modelo.fit(entradas_treino, saidas_treino, epochs=30, verbose =0) # Previsões pred_treino = modelo.predict(entradas_treino) pred_teste = modelo.predict(entradas_teste) # Cálculo dos erros (MSE) mse_treino = mean_squared_error(saidas_treino, pred_treino) mse_teste = mean_squared_error(saidas_teste, pred_teste) resultados_mse[tipo_rede] = (mse_treino, mse_teste) # Visualização dos resultados plt.subplot(3, 1, i + 1) plt.plot(saidas_teste[:200], label='Temperatura Real') plt.plot(pred_teste[:200], label=f'Predição - {tipo_rede}') plt.title(f"{tipo_rede} - MSE treino: {mse_treino:.3f}, MSE teste: {mse_teste:.3f}") plt.legend() plt.tight_layout() plt.show() # Exibir resumo dos erros print("\nResumo do Erro Quadrático Médio (MSE):") for tipo, (treino, teste) in resultados_mse.items(): print(f"{tipo}: treino = {treino:.4f}, teste = {teste:.4f}") 97 CAPÍTULO 3. REDES RECORRENTES (RNN) Saída Esperada Figura 3.1: Gráficos comparando as temperaturas reais e previstas para os três modelos: RNN, LSTM e GRU Este exemplo simula uma série temporal realista usando uma combinação de função seno com ruído aleatório, representando oscilações e variações imprevisíveis típicas de fenômenos naturais como a temperatura. Os modelos são treinados para aprender o padrão da série e prever o próximo valor com base nos 20 dias anteriores. 98 CAPÍTULO 3. REDES RECORRENTES (RNN) Fique Alerta! Modelos recorrentes como RNNs simples podem ter dificuldade com dependências de longo prazo, enquanto LSTM e GRU foram projetadas para mitigar esse problema com mecanismos internos de memória e controle de fluxo de informação. • O que acontece com a acurácia da previsão ao aumentar a janela de entrada de 20 para 30 dias? • Se você reduzir o número de neurônios na camada recorrente de 50 para 10, como isso afeta o tempo de treinamento e o desempenho? • Qual modelo teve o menor erro de teste? Isso era esperado? Por quê? • Por que é importante manter parte dos dados separados para teste? 99 CAPÍTULO 3. REDES RECORRENTES (RNN) 3.5 Implementação Leve em Keras com Dados Meteorológicos do INMET Agora que entendemos como funcionam as redes recorrentes, é hora de aplicar esse conhecimento em um experimento prático: prever a temperatura instantânea com base nas últimas horas de registros meteorológicos reais da estação do INMET. Caso Prático Neste exemplo, usaremos uma rede recorrente para prever a temperatura do próximo horário, utilizando como entrada os últimos registros de umidade relativa do ar. Vamos treinar e comparar três modelos: uma RNN simples, uma LSTM e uma GRU. Fique Alerta! Para este exemplo, foram utilizados dados reais do INMET, da estação MANACAPURU (A119), no período de 01/01/2025 a 30/04/2025. Esses dados estão disponíveis publicamente em: https://tempo.inmet.gov.br/TabelaEstacoes/A119 As informações incluem registros horários de temperatura, umidade, entre outras variáveis meteorológicas, e são valiosas para experimentos com modelos preditivos como RNN, LSTM e GRU. 3.5.1 Preparação dos dados Copie e Teste! import pandas as pd import numpy as np from sklearn.preprocessing import MinMaxScaler from sklearn.model_selection import train_test_split # Certifique-se de ter o arquivo 'dados_inmet.csv' no mesmo diretório # ou ajuste o caminho. try: df_inmet = pd.read_csv("dados_inmet.csv", sep=';', encoding=' latin1', na_values=['null','NULL']) # Limpeza e conversão de colunas (exemplo) # O livro especifica vírgula como decimal, mas pode variar no seu arquivo. cols_to_convert = ['Temp. Ins. (C)','Umi. Ins. (%)']# Adicione outras se precisar for col in cols_to_convert: if col in df_inmet.columns: if df_inmet[col].dtype == 'object':# Só converte se for string/object 100 CAPÍTULO 3. REDES RECORRENTES (RNN) df_inmet[col] = df_inmet[col].str.replace(',','.' , regex=False).astype(float) else:# Se já for numérico, apenas garante que é float df_inmet[col] = df_inmet[col].astype(float) else: print(f"Aviso: Coluna '{col}' não encontrada no CSV.") # Selecionar colunas de interesse e tratar NaNs if 'Umi. Ins. (%)' in df_inmet.columns and 'Temp. Ins. (C)' in df_inmet.columns: dados_inmet = df_inmet[['Umi. Ins. (%)','Temp. Ins. (C)' ]].dropna().values if dados_inmet.shape[0] > 0: # Verifica se há dados após dropna scaler_inmet = MinMaxScaler() dados_normalizados_inmet = scaler_inmet.fit_transform( dados_inmet) print("Dados do INMET preparados e normalizados.") else: print("ERRO: Não restaram dados após remover NaNs. Verifique o arquivo CSV.") else: print("ERRO: Colunas 'Umi. Ins. (%)' ou 'Temp. Ins. (C)' não encontradas ou não puderam ser processadas.") except FileNotFoundError: print("ERRO: Arquivo 'dados_inmet.csv' não encontrado.") except Exception as e: print(f"ERRO ao processar o arquivo CSV: {e}") 3.5.2 Construção da sequência Copie e Teste! def criar_sequencia_inmet(dados, janela=6): X_seq, y_seq = [], [] if dados is not None and len(dados) > janela : # Verifica se ' dados' não é None for iin range(len(dados) - janela): X_seq.append(dados[i:i+janela, 0]) # umidade como feature y_seq.append(dados[i+janela, 1]) # temperatura como target return np.array(X_seq), np.array(y_seq) return np.array([]), np.array([]) # Retorna arrays vazios se não houver dados 101 CAPÍTULO 3. REDES RECORRENTES (RNN) # Assegure que dados_normalizados_inmet existe e tem dados if 'dados_normalizados_inmet' in locals() and dados_normalizados_inmet.shape[0] > 0: X_inmet, y_inmet = criar_sequencia_inmet( dados_normalizados_inmet, janela=6) if X_inmet.size > 0: # Verifica se X_inmet não está vazio X_inmet = X_inmet.reshape(X_inmet.shape[0], X_inmet.shape [1], 1) X_treino_inmet, X_teste_inmet, y_treino_inmet, y_teste_inmet = train_test_split( X_inmet, y_inmet, test_size=0.3, random_state=42 # random_state para reprodutibilidade ) print(f"Shapes INMET - X_treino: {X_treino_inmet.shape}, y_treino: {y_treino_inmet.shape}") else: print("ERRO: Não foi possível criar sequências (X_inmet está vazio). Verifique a etapa anterior.") else: print("ERRO: 'dados_normalizados_inmet' não definido ou vazio. Execute a preparação de dados primeiro.") 3.5.3 Treinando modelos com RNN, LSTM e GRU Copie e Teste! # from tensorflow.keras.models import Sequential # from tensorflow.keras.layers import SimpleRNN, LSTM, GRU, Dense def treinar_modelo_inmet(tipo='RNN', X_treino_data=None, y_treino_data=None, input_shape_val=None): if X_treino_data is None or y_treino_data is None or X_treino_data.size == 0: print(f"ERRO: Dados de treino para {tipo} estão vazios ou não foram fornecidos.") return None, None modelo_inmet = Sequential() if tipo == 'RNN': modelo_inmet.add(SimpleRNN(16, input_shape=input_shape_val )) elif tipo == 'LSTM': modelo_inmet.add(LSTM(16, input_shape=input_shape_val)) elif tipo == 'GRU': modelo_inmet.add(GRU(16, input_shape=input_shape_val)) modelo_inmet.add(Dense(1)) modelo_inmet.compile(optimizer='adam', loss='mse') 102 CAPÍTULO 3. REDES RECORRENTES (RNN) print(f"Treinando modelo {tipo} com dados do INMET...") historico_inmet = modelo_inmet.fit(X_treino_data, y_treino_data, epochs=20, validation_split=0.2, verbose=0) return modelo_inmet, historico_inmet # Assegure que X_treino_inmet existe e tem dados if 'X_treino_inmet' in locals() and X_treino_inmet.size > 0: input_shape_inmet = (X_treino_inmet.shape[1], X_treino_inmet. shape[2]) else: input_shape_inmet = (6,1) # Fallback, mas o ideal é que X_treino_inmet esteja correto print("AVISO: input_shape_inmet usando fallback. Verifique os dados de treino.") 3.5.4 Comparando os resultados Copie e Teste! # import matplotlib.pyplot as plt modelos_hist_inmet = {} if 'X_treino_inmet' in locals() and X_treino_inmet.size > 0 : for tipo_m in ['RNN','LSTM','GRU']: modelo_m, hist_m = treinar_modelo_inmet(tipo_m, X_treino_inmet, y_treino_inmet, input_shape_inmet) if modelo_m is not None: # Verifica se o modelo foi treinado modelos_hist_inmet[tipo_m] = hist_m if modelos_hist_inmet: # Verifica se há históricos para plotar plt.figure(figsize=(12, 6)) for tipo_plot, hist_plot in modelos_hist_inmet.items(): plt.plot(hist_plot.history['val_loss'], label=f'{ tipo_plot} (Validação Loss)') plt.title('Comparação de Perda de Validação (MSE) - Dados INMET') plt.xlabel('Épocas') plt.ylabel('Erro Quadrático Médio (Validação)') plt.legend() plt.grid(True) plt.show() else: print("Nenhum modelo foi treinado com sucesso para plotar a comparação de perdas.") else: print("ERRO: Dados de treino do INMET não disponíveis para comparação de modelos.") 103 CAPÍTULO 3. REDES RECORRENTES (RNN) Saída Esperada Figura 3.2: Gráfico comparando a perda de validação entre as arquiteturas RNN, LSTM e GRU O Gráfico acima apresenta a comparação do erro de validação ao longo das épocas para três arquiteturas recorrentes: SimpleRNN, LSTM e GRU. Observa-se que os modelos SimpleRNN e GRU convergem rapidamente e mantêm um erro de validação significativamente baixo após cerca da 10ª época, indicando boa capacidade de modelagem do padrão temporal presente nos dados advindo da Estação: MANACAPURU A119 do INMET. A GRU, em particular, apresenta leve vantagem na estabilidade e precisão final. Por outro lado, o modelo LSTM, embora teoricamente mais poderoso para capturar dependências de longo prazo, demonstra convergência mais lenta e erro consistentemente superior. Este comportamento sugere que, para tarefas de previsão com padrões simples e curtos, como a simulação utilizada neste experimento, arquiteturas mais enxutas, como GRU ou mesmo SimpleRNN, não apenas são suficientes, como podem superar modelos mais complexos. Isso reforça a importância de alinhar a escolha do modelo à complexidade do problema, evitando o uso excessivo de recursos computacionais em cenários que não o exigem. 3.5.5 Previsão com o modelo GRU Após comparar os modelos, vamos visualizar como o modelo GRU se comporta na prática, comparando as temperaturas reais com as previstas para o conjunto de teste. Copie e Teste! if 'X_treino_inmet' in locals() and X_treino_inmet.size > 0 and ' X_teste_inmet' in locals() and X_teste_inmet.size > 0: 104 CAPÍTULO 4. EXPERIMENTO PRÁTICO •DataFrame e Embaralhamento: Os dados são organizados em um DataFrame do Pandas e, em seguida, embaralhados com dados_solo.sample(frac=1) para garantir que as amostras não estejam ordenadas por classe antes do treinamento. •Exibição Inicial: Comandos como head(),info() evalue_counts() são usados para uma inspeção inicial dos dados gerados. 4.1.2 Pré-processamento dos Dados Esta etapa prepara os dados para a rede neural, incluindo separação de features e labels, normalização e codificação dos rótulos. Copie e Teste! from sklearn.model_selection import train_test_split from sklearn.preprocessing import MinMaxScaler from tensorflow.keras.utils import to_categorical # Usando tensorflow.keras # 1. Separar features (X) e labels (y) X = dados_solo.drop('Classe_Fertilidade', axis=1) y = dados_solo['Classe_Fertilidade'] # 2. Normalizar as features scaler = MinMaxScaler() X_normalizado = scaler.fit_transform(X) X_normalizado_df = pd.DataFrame(X_normalizado, columns=X.columns) # Opcional para visualização print("\n--- Features Normalizadas (X_normalizado_df) - Primeiras 5 linhas ---") print(X_normalizado_df.head()) # 3. Codificar os labels para o formato One-Hot Encoding y_categorico = to_categorical(y) print("\n--- Labels One-Hot Encoded (y_categorico - primeiros 5) ---") print(y_categorico[:5]) # 4. Dividir os dados em conjuntos de treino e teste X_treino, X_teste, y_treino, y_teste = train_test_split( X_normalizado, y_categorico, test_size=0.2, random_state=42, stratify=y) print(f"Shape de X_treino: {X_treino.shape}") print(f"Shape de y_treino: {y_treino.shape}") print(f"Shape de X_teste: {X_teste.shape}") print(f"Shape de y_teste: {y_teste.shape}") 111 CAPÍTULO 4. EXPERIMENTO PRÁTICO Saída Esperada -- Features Normalizadas (X_normalizado_df) --- Primeiras 5 linhas --- Nitrogenio_N Fosforo_P Potassio_K 0.662224 0.738913 0.858168 0.696770 0.839052 0.988007 0.423571 0.432929 0.264191 0.201336 0.020090 0.035995 0.515544 1.000000 0.820669 pH Materia_Organica_pct Umidade_pct 0.707633 0.642898 0.702481 0.854401 0.561053 0.842712 0.452185 0.489812 0.616356 0.144179 0.017979 0.431324 0.676767 0.888988 0.653926 --- Labels One-Hot Encoded (y_categorico - primeiros 5) --- [[0. 0. 1.] [0. 0. 1.] [0. 1. 0.] [1. 0. 0.] [0. 0. 1.]] Shape de X_treino: (240, 6) Shape de y_treino: (240, 3) Shape de X_teste: (60, 6) Shape de y_teste: (60, 3) O que está acontecendo até agora? •Separação Features/Labels: Isola as colunas de características (X) da coluna alvo (y). •Normalização: MinMaxScaler transforma os valores das features para uma escala entre 0 e 1. Isso ajuda na convergência e estabilidade do treinamento da rede neural. •One-Hot Encoding: to_categorical(y) converte os rótulos numéricos das classes (0, 1, 2) para um formato vetorial binário (ex: classe 1 -> [0,1,0]), necessário para a função de perda categorical_crossentropy. •Divisão Treino/Teste: train_test_split divide os dados em 80% para treino e 20% para teste. O parâmetro stratify=y garante que a proporção das classes seja mantida em ambos os conjuntos [10]. 4.1.3 Definição da Arquitetura da Rede Neural (Keras) Aqui, construímos a estrutura da nossa rede neural densa usando Keras. 112 CAPÍTULO 4. EXPERIMENTO PRÁTICO Copie e Teste! from tensorflow.keras.models import Sequential from tensorflow.keras.layers import Dense # 1. Definir a arquitetura modelo_solo = Sequential() modelo_solo.add(Dense(32, input_shape=(X_treino.shape[1],), activation='relu')) # Camada de entrada/oculta 1 modelo_solo.add(Dense(16, activation='relu')) # Camada oculta 2 modelo_solo.add(Dense(y_treino.shape[1], activation='softmax')) # Camada de saída # 2. Compilar o modelo modelo_solo.compile(optimizer='adam', loss=' categorical_crossentropy', metrics=['accuracy']) # 3. Exibir resumo do modelo print("\n--- Resumo do Modelo ---") modelo_solo.summary() Saída Esperada --- Resumo do Modelo --- Model: "sequential_W" _________________________________________________________________ Layer (type) Output Shape Param # ================================================================= dense\_X (Dense) (None, 32) 224 dense\_Y (Dense) (None, 16) 528 dense\_Z (Dense) (None, 3) 51 ================================================================= Total params: 803 Trainable params: 803 Non-trainable params: 0 Fique Alerta! Os nomes das camadas (dense_X, dense_Y e dense_Z) e o número do modelo (”sequential_W”) podem variar a cada execução. O importante são as shapes e o número de parâmetros. O que está acontecendo até agora? •Modelo Sequencial: Sequential() cria uma pilha linear de camadas. •Camadas Densas: 113 CAPÍTULO 4. EXPERIMENTO PRÁTICO –A primeira camada Densetem 32 neurônios, recebe a entrada com input_shape definido pelo número de features, e usa ativação relu. –A segunda camada oculta tem 16 neurônios com ativação relu. –A camada de saída tem um número de neurônios igual ao número de classes (3) e usa ativação softmax, ideal para classificação multiclasse, pois gera uma distribuição de probabilidades. •Compilação: modelo_solo.compile(...) configura o processo de aprendizado com: –Otimizador adam: um algoritmo eficiente para ajustar os pesos da rede. –Função de perda categorical_crossentropy: adequada para classificação multiclasse com rótulos one-hot. –Métrica ['accuracy']: para monitorar a precisão durante o treinamento. •Resumo do Modelo: modelo_solo.summary() exibe a arquitetura da rede, incluindo o número de parâmetros treináveis em cada camada. 4.1.4 Treinamento do Modelo Com a rede definida, alimentamos os dados de treinamento para que ela aprenda os padrões. Copie e Teste! print("\n--- Iniciando o Treinamento do Modelo ---") num_epocas = 50 tamanho_lote = 10 historico_treinamento = modelo_solo.fit(X_treino,y_treino,epochs= num_epocas,batch_size=tamanho_lote,validation_data=(X_teste, y_teste), verbose=1) print("\n--- Treinamento Concluído ---") print("\nChaves disponíveis no histórico de treinamento:") print(historico_treinamento.history.keys()) Fique Alerta! A saída do treinamento será uma série de linhas mostrando a perda e a precisão para cada época, tanto para os dados de treino quanto para os de validação, além da mensagem a seguir. Saída Esperada --- Treinamento Concluído --- Chaves disponíveis no histórico de treinamento: dict_keys(['accuracy','loss','val_accuracy','val_loss']) 114 CAPÍTULO 4. EXPERIMENTO PRÁTICO O que está acontecendo até agora? •Método fit():É usado para treinar o modelo. •Parâmetros: –X_treino, y_treino: Os dados de treinamento. –epochs=50: O modelo verá o conjunto de dados de treinamento completo 50 vezes. –batch_size=10: O modelo processará 10 amostras antes de atualizar seus pesos. –validation_data=(X_teste, y_teste): Permite avaliar o modelo no conjunto de teste ao final de cada época, ajudando a monitorar a generalização e o overfitting. •Objeto history:O método fit() retorna um objeto History que contém o histórico da perda e das métricas ao longo das épocas. 4.1.5 Avaliação do Desempenho do Modelo e Visualização Após o treinamento, avaliamos o quão bem o modelo aprendeu e generaliza para novos dados. Copie e Teste! import matplotlib.pyplot as plt import numpy as np from sklearn.metrics import classification_report, confusion_matrix import seaborn as sns # Opcional, para visualização melhor da matriz # 1. Avaliar no conjunto de teste print("\n--- Avaliando o Modelo no Conjunto de Teste ---") perda_teste, precisao_teste = modelo_solo.evaluate(X_teste, y_teste, verbose=0) print(f"Perda no conjunto de teste: {perda_teste:.4f}") print(f"Precisão no conjunto de teste: {precisao_teste:.4f} (ou { precisao_teste*100:.2f}%)") # 2. Visualizar histórico de treinamento (Precisão e Perda) plt.figure(figsize=(12, 5)) plt.subplot(1, 2, 1) plt.plot(historico_treinamento.history['accuracy'], label=' Precisão (Treino)') plt.plot(historico_treinamento.history['val_accuracy'], label=' Precisão (Validação)') plt.title('Histórico de Precisão') plt.xlabel('Época') plt.ylabel('Precisão') 115 CAPÍTULO 4. EXPERIMENTO PRÁTICO plt.legend() plt.grid(True) plt.subplot(1, 2, 2) plt.plot(historico_treinamento.history['loss'], label='Perda ( Treino)') plt.plot(historico_treinamento.history['val_loss'], label='Perda ( Validação)') plt.title('Histórico de Perda') plt.xlabel('Época') plt.ylabel('Perda') plt.legend() plt.grid(True) plt.tight_layout() plt.show() # 3. Matriz de Confusão e Relatório de Classificação y_pred_prob = modelo_solo.predict(X_teste) y_pred_classes = np.argmax(y_pred_prob, axis=1) y_teste_classes = np.argmax(y_teste, axis=1) # Converter y_teste de one-hot print("\n--- Relatório de Classificação ---") print(classification_report(y_teste_classes, y_pred_classes, target_names=['Baixa F.','Média F.','Alta F.'])) mat_conf = confusion_matrix(y_teste_classes, y_pred_classes) plt.figure(figsize=(8, 6)) sns.heatmap(mat_conf, annot=True, fmt='d', cmap='Blues', xticklabels=['Baixa F.','Média F.','Alta F.'], yticklabels=['Baixa F.','Média F.','Alta F.']) plt.title('Matriz de Confusão') plt.ylabel('Classe Verdadeira') plt.xlabel('Classe Prevista') plt.show() Saída Esperada --- Avaliando o Modelo no Conjunto de Teste --- Perda no conjunto de teste: 0.0034 Precisão no conjunto de teste: 1.0000 (ou 100.00%) --- Relatório de Classificação --- precision recall f1-score support Baixa F. 1.00 1.00 1.00 20 Média F. 1.00 1.00 1.00 20 Alta F. 1.00 1.00 1.00 20 accuracy 1.00 60 macro avg 1.00 1.00 1.00 60 weighted avg 1.00 1.00 1.00 60 116 CAPÍTULO 4. EXPERIMENTO PRÁTICO Figura 4.1: Histórico de precisão e perda durante o treinamento do modelo de classificação de solo. Figura 4.2: Matriz de confusão para o modelo de classificação de solo no conjunto de teste. 117 CAPÍTULO 4. EXPERIMENTO PRÁTICO O que está acontecendo até agora? •Avaliação Final: modelo_solo.evaluate(X_teste, y_teste) calcula a perda e a precisão no conjunto de teste, fornecendo uma medida do desempenho do modelo em dados não vistos. •Visualização do Histórico: Usando matplotlib.pyplot, plotamos a precisão e a perda ao longo das épocas para os dados de treino e validação. Isso ajuda a identificar se o modelo está aprendendo adequadamente ou se está ocorrendo overfitting (quando a performance no treino melhora, mas na validação estagna ou piora). •Relatório de Classificação e Matriz de Confusão: –As previsões de probabilidade são convertidas para classes usando np.argmax. –classification_reportda sklearn.metricsfornece métricas detalhadas (precisão, recall, f1-score) para cada classe. –confusion_matrix mostra o número de classificações corretas e incorretas para cada par de classes (verdadeira vs. prevista), ajudando a entender onde o modelo comete erros. 4.1.6 Interpretação dos Resultados e Discussão Neste experimento prático, construímos e treinamos uma rede neural densa para classificar amostras de solo simuladas em três categorias de fertilidade: Baixa, Média e Alta. Utilizamos um conjunto de dados sintético representando características químicas e físicas do solo. O modelo foi desenvolvido em Keras, e seu desempenho foi avaliado por meio da precisão no conjunto de teste, análise do histórico de treinamento e uma matriz de confusão. Se o modelo alcançou uma alta precisão no conjunto de teste (por exemplo, acima de 8590%) e os gráficos de histórico mostraram boa convergência sem overfitting significativo, isso indica que a rede neural foi capaz de aprender os padrões presentes nos dados simulados para distinguir entre as classes de fertilidade. A matriz de confusão e o relatório de classificação nos dariam insights mais profundos sobre quais classes foram mais fáceis ou difíceis para o modelo prever. A capacidade de reconhecer padrões no solo e classificar sua fertilidade tem um potencial imenso para a agricultura na nossa Amazônia. Um sistema baseado em Inteligência Artificial, treinado com dados reais e locais, poderia: • Otimizar o uso de fertilizantes, economizando custos e reduzindo o impacto ambiental, alinhado ao desenvolvimento sustentável. • Apoiar a agricultura de precisão para manejo localizado e aumento da produtividade. • Ajudar na seleção de culturas adequadas e monitorar a saúde do solo. Limitações e Próximos Passos: •Dados Simulados: Solos reais, especialmente na Amazônia, são mais complexos. A coleta de dados locais de boa qualidade é essencial. •Características Limitadas: Análises mais detalhadas (micronutrientes, textura, dados de sensoriamento remoto) poderiam enriquecer o modelo. 118 CAPÍTULO 4. EXPERIMENTO PRÁTICO •Arquitetura Simples: Para dados mais complexos como imagens de solo, CNNs ou RNNs para séries temporais de sensores seriam mais adequadas. Fique Alerta! O sucesso de aplicações de Deep Learning em contextos ambientais como a análise de solo depende fortemente da qualidade e representatividade dos dados. A colaboração com especialistas da área, como agrônomos, e a adaptação às particularidades do território são fundamentais. Este experimento prático serve como uma introdução ao potencial das redes neurais para o reconhecimento de padrões no solo, inspirando a exploração de IA para um futuro mais inteligente e sustentável na Amazônia. 119 CAPÍTULO 4. EXPERIMENTO PRÁTICO 4.2 Aplicação 02: Previsão de Surtos de Pragas com Redes Neurais Recorrentes Caso Prático Neste segundo experimento prático vamos explorar como as Redes Neurais Recorrentes (RNNs), especificamente uma arquitetura LSTM, podem ser utilizadas para prever a dinâmica de populações de pragas agrícolas. A capacidade de antecipar surtos de pragas é crucial para um manejo integrado eficiente, permitindo intervenções mais precisas e a redução do uso de defensivos agrícolas. Iremos desenvolver e treinar uma rede neural recorrente do tipo LSTM para prever a população de uma praga agrícola simulada, com base em dados históricos de condições climáticas (temperatura, umidade, precipitação) e da própria contagem da praga. Este experimento ilustrará como as RNNs podem aprender padrões temporais complexos para realizar previsões. 4.2.1 Geração de Dados Simulados para Previsão de Surtos de Pragas Criaremos séries temporais para temperatura, umidade, precipitação e a contagem da praga, buscando simular interdependências plausíveis entre essas variáveis. Copie e Teste! import numpy as np import pandas as pd # Semente para reprodutibilidade np.random.seed(42) # Número de dias para simulação num_dias = 1000 # 1. Simular Temperatura Média Diária (°C) dias_ano = 365 amplitude_temp = 5 media_temp_anual = 25 temperatura = media_temp_anual + amplitude_temp * np.sin(2 * np.pi * np.arange(num_dias) / dias_ano) temperatura += np.random.normal(0, 1.5, num_dias) # 2. Simular Umidade Relativa Média Diária (%) amplitude_umid = 10 media_umid_anual = 80 umidade = media_umid_anual - amplitude_umid * np.sin(2 * np.pi * np.arange(num_dias) / dias_ano + np.pi/2) umidade += np.random.normal(0, 5, num_dias) umidade = np.clip(umidade, 40, 100) 120 CAPÍTULO 4. EXPERIMENTO PRÁTICO Copie e Teste! import matplotlib.pyplot as plt from sklearn.metrics import mean_absolute_error # numpy já foi importado # 1. Avaliar no conjunto de teste (MSE normalizado) perda_teste_normalizada = modelo_pragas_lstm.evaluate(X_teste, y_teste, verbose=0) print(f"Perda (MSE) no teste (normalizada): { perda_teste_normalizada:.4f}") # 2. Fazer previsões (normalizadas) y_pred_normalizado = modelo_pragas_lstm.predict(X_teste) # 3. Desnormalizar previsões e y_teste y_teste_reshape = y_teste.reshape(-1, 1) y_teste_desnormalizado = scaler_contagem_praga.inverse_transform( y_teste_reshape) if y_pred_normalizado.ndim == 1: y_pred_normalizado_reshape = y_pred_normalizado.reshape(-1, 1) else: y_pred_normalizado_reshape = y_pred_normalizado y_pred_desnormalizado = scaler_contagem_praga.inverse_transform( y_pred_normalizado_reshape) mae_desnormalizado = mean_absolute_error(y_teste_desnormalizado, y_pred_desnormalizado) print(f"MAE no teste (desnormalizado): {mae_desnormalizado:.2f} ( unidades da praga)") # 4. Visualizar histórico de Perda plt.figure(figsize=(10, 5)) plt.plot(historico_treinamento_pragas.history['loss'], label=' Perda (Treino)') plt.plot(historico_treinamento_pragas.history['val_loss'], label=' Perda (Validação)') plt.title('Histórico de Perda do Modelo LSTM (Pragas)') plt.xlabel('Época'); plt.ylabel('Erro Quadrático Médio (MSE)') plt.legend(); plt.grid(True) plt.show() # 5. Visualizar previsões vs. reais (desnormalizados) plt.figure(figsize=(14, 6)) num_pontos_plot = min(200, len(y_teste_desnormalizado)) plt.plot(y_teste_desnormalizado[:num_pontos_plot], label='Contagem Real da Praga', marker='.') 127 CAPÍTULO 4. EXPERIMENTO PRÁTICO plt.plot(y_pred_desnormalizado[:num_pontos_plot], label='Previsão (LSTM)', marker='x', alpha=0.7) plt.title(f'Previsão da Contagem de Pragas (Teste) - MAE: { mae_desnormalizado:.2f}') plt.xlabel(f'Amostras Temporais (Primeiros {num_pontos_plot} pontos do teste)') plt.ylabel('Contagem da Praga (Desnormalizada)') plt.legend(); plt.grid(True); plt.tight_layout() plt.show() # Comparação em tabela (primeiras 10 amostras) print("\n--- Comparação Detalhada (Primeiras 10 Amostras do Teste) ---") comparacao_df = pd.DataFrame({'Real (Desnormalizado)': y_teste_desnormalizado[:10].flatten(),'Previsto (Desnormalizado )': y_pred_desnormalizado[:10].flatten()}) print(comparacao_df.round(2)) Saída Esperada Perda (MSE) no teste (normalizada): 0.0001 MAE no teste (desnormalizado): 1.67 (unidades da praga) --- Comparação Detalhada (Primeiras 10 Amostras do Teste) --- Real (Desnormalizado) Previsto (Desnormalizado) 0 0.36 3.49 1 0.36 3.27 2 0.42 3.02 3 0.44 2.93 4 0.52 2.83 5 0.56 2.83 6 0.57 2.94 7 0.59 2.39 8 0.65 2.34 9 0.69 2.60 128 CAPÍTULO 4. EXPERIMENTO PRÁTICO Figura 4.3: Histórico da função de perda (MSE) durante o treinamento do modelo LSTM para previsão de pragas. Comparação entre dados de treino e validação. Figura 4.4: Comparação entre a contagem real da praga (desnormalizada) e as previsões do modelo LSTM (desnormalizadas) para o conjunto de teste. O que está acontecendo até agora? •Avaliação Final (MSE Normalizado):evaluate() calcula o MSE no conjunto de teste com dados normalizados. •Previsões e Desnormalização: O modelo faz previsões (predict()), que são então desnormalizadas, juntamente com os valores reais de y_teste, usando o scaler_contagem_praga salvo anteriormente. Isso permite interpretar os erros na escala original da contagem de pragas. •Cálculo do MAE Desnormalizado: O Erro Absoluto Médio é calculado sobre os dados desnormalizados, oferecendo uma medida de erro mais intuitiva. •Visualização do Histórico de Perda: O gráfico da perda de treino vs. validação ao longo das épocas ajuda a diagnosticar o aprendizado e overfitting. 129 CAPÍTULO 4. EXPERIMENTO PRÁTICO •Visualização das Previsões vs. Reais: Um gráfico compara as contagens reais e previstas (ambas desnormalizadas) para o conjunto de teste, essencial para avaliar modelos de séries temporais. Uma tabela também mostra os primeiros 10 valores para comparação direta. 4.2.6 Interpretação dos Resultados e Discussão Prática Resumo do Experimento: Neste experimento, desenvolvemos um modelo LSTM para prever a população de uma praga agrícola simulada, usando dados históricos de clima e da própria praga. Avaliamos o modelo por meio de métricas como MSE e MAE, e visualizações gráficas. Interpretando os Resultados: Um bom resultado seria caracterizado por um MAE baixo (na escala original da praga), indicando que as previsões estão próximas dos valores reais. Os gráficos de histórico de perda idealmente mostrariam convergência com pouca diferença entre treino e validação. O gráfico de previsão vs. real deveria mostrar que o modelo captura as tendências principais da população da praga. Relevância para o Contexto Amazônico e Aplicações Práticas no Controle de Pragas: A previsão de surtos de pragas é vital para a agricultura sustentável na Amazônia. Modelos preditivos podem: • Otimizar o Manejo Integrado de Pragas (MIP), permitindo intervenções pontuais. • Reduzir o uso de pesticidas, protegendo a biodiversidade e a saúde. • Aumentar a resiliência das plantações e a segurança alimentar. • Apoiar sistemas de alerta precoce para agricultores e cooperativas. Limitações e Próximos Passos: •Dados Simulados: A dinâmica real de pragas é muito mais complexa. A coleta de dados locais de alta qualidade é um desafio crucial. •Validação em Campo: Modelos precisam de validação rigorosa em condições reais com especialistas. •Aprimoramentos: Explorar mais features (estágio da cultura, dados de satélite), arquiteturas RNN mais complexas (Stacked LSTM, Bi-LSTM, Attention) e otimização de hiperparâmetros. Fique Alerta! Prever sistemas biológicos é inerentemente complexo. Modelos de Deep Learning são ferramentas de apoio e devem ser integrados com conhecimento agronômico e entomológico, adaptados às especificidades locais da Amazônia. Conhecendo um pouco mais! Pesquise sobre ”Manejo Integrado de Pragas”e ”Agricultura de Precisão”na Amazônia. As arquiteturas LSTM e GRU são fundamentais para capturar dependências de longo prazo em dados sequenciais, como explorado no Capítulo 3. 130 CAPÍTULO 4. EXPERIMENTO PRÁTICO 4.3 Aplicação 03: Diagnóstico de Doenças em Plantas com Redes Neurais Convolucionais Caso Prático Neste terceiro experimento prático, vamos focar em uma das aplicações mais impactantes da visão computacional na agricultura: o diagnóstico de doenças em plantas. Utilizaremos Redes Neurais Convolucionais (CNNs) para analisar imagens de folhas e determinar se estão saudáveis ou acometidas por alguma doença. Iremos desenvolver, treinar e avaliar uma CNN para classificar imagens de folhas de plantas como ”Saudável”ou ”Doente”, utilizando o dataset PlantVillage [4]. Além disso, exploraremos como testar o modelo treinado utilizando uma webcam para classificações em tempo real. O PlantVillage é uma base gratuita que pode ser baixada do site oficial: https://www. kaggle.com/datasets/emmarex/plantdisease. Após o download, o arquivo plantdisease.zip deve ser extraído. A pasta principal conterá várias subpastas, uma para cada tipo de folha (ex: Tomato_healthy,Tomato_Late_blight, etc.). Nosso objetivo será criar um classificador binário: identificar se uma folha está saudável ou apresenta qualquer tipo de doença. Utilizaremos o Keras para carregar as imagens com o ImageDataGenerator, treinar a rede e avaliar o desempenho. 4.3.1 Organização dos dados Você deve organizar as imagens da seguinte forma... dados/ ├── treino/ │ ├── saudavel/ │ └── doente/ ├── teste/ │ ├── saudavel/ │ └── doente/ Mas como fazer isso? Copie manualmente as imagens das pastas com nomes como: Tomato_healthy, Potato_healthy e Pepper_bell_healthy para a pasta saudavel. Todas as outras pastas representam doenças (ex: Tomato_Early_blight) e devem ir para a pasta doente. Em seguida, divida aproximadamente 80% das imagens de cada classe para a pasta treino/ e os 20% restantes para a pasta teste/. Ou... execute o código a seguir para automatizar essa tarefa de organização das imagens entre teste e treino!. O código abaixo: • Lê todas as pastas do dataset; • Separa automaticamente entre folhas saudáveis edoentes; • Divide em conjuntos de treino (80%) eteste (20%); 131 CAPÍTULO 4. EXPERIMENTO PRÁTICO • E cria a estrutura de pastas compatível com o Keras. Fique Alerta! Antes de rodar este código, certifique-se de que a pasta PlantVillage já foi descompactada corretamente no seu ambiente de trabalho. Conhecendo um pouco mais! Você pode adaptar este script para dividir em outras proporções ou usar categorias específicas. Copie e Teste! import os import shutil import random # Caminho onde as imagens foram extraídas caminho_origem = 'PlantVillage' # pasta onde o zip foi extraído caminho_destino = 'dados_folhas' # Nova pasta para esta aplicação, para não conflitar # Pastas de destino organizadas por classe pastas_destino_app3 = [ os.path.join(caminho_destino, 'treino','saudavel'), os.path.join(caminho_destino, 'treino','doente'), os.path.join(caminho_destino, 'teste','saudavel'), os.path.join(caminho_destino, 'teste','doente') ] # Criar as pastas for pasta in pastas_destino_app3: os.makedirs(pasta, exist_ok=True) # Definir quais pastas indicam folhas saudáveis pastas_saudaveis_app3 = [ 'Pepper__bell___healthy', 'Potato___healthy', 'Tomato_healthy' ] # Todas as pastas da base, é importante garantir que caminho_origem exista e contenha as subpastas do dataset if not os.path.isdir(caminho_origem): print(f"Erro: Diretório de origem '{caminho_origem}' não encontrado. Faça o download e extraia o PlantVillage dataset.") else: todas_as_pastas_app3 = os.listdir(caminho_origem) # Filtrar apenas diretórios válidos que contêm imagens pastas_validas_app3 = [p for pin todas_as_pastas_app3 if os. 132 CAPÍTULO 4. EXPERIMENTO PRÁTICO path.isdir(os.path.join(caminho_origem, p))] # Separar pastas doentes pastas_doentes_app3 = [p for pin pastas_validas_app3 if pnot in pastas_saudaveis_app3] # Função para copiar imagens def copiar_imagens_app3(lista_pastas, classe_destino_param, caminho_origem_param, caminho_destino_param): for pasta_origem_relativa in lista_pastas: pasta_completa_origem = os.path.join( caminho_origem_param, pasta_origem_relativa) if not os.path.isdir(pasta_completa_origem): print(f"Aviso: Subpasta '{pasta_completa_origem}' não encontrada ou não é um diretório.") continue imagens = [img for img in os.listdir( pasta_completa_origem) if img.lower().endswith(('.png','.jpg', '.jpeg'))] random.shuffle(imagens) qtd_treino = int(0.8 * len(imagens)) for i, imagem_nome in enumerate(imagens): origem_img = os.path.join(pasta_completa_origem, imagem_nome) tipo_conjunto = 'treino' if i < qtd_treino else ' teste' destino_final_img = os.path.join( caminho_destino_param, tipo_conjunto, classe_destino_param, imagem_nome) # Certificar que a pasta de destino da classe exista os.makedirs(os.path.dirname(destino_final_img), exist_ok=True) shutil.copy2(origem_img, destino_final_img) # Copiar as imagens saudáveis e doentes se as pastas válidas foram encontradas if 'pastas_validas_app3' in locals() and pastas_validas_app3: copiar_imagens_app3(pastas_saudaveis_app3, 'saudavel', caminho_origem, caminho_destino) copiar_imagens_app3(pastas_doentes_app3, 'doente', caminho_origem, caminho_destino) print("Organização das imagens para Aplicação 03 concluída com sucesso!") elif not os.path.isdir(caminho_origem): print("A organização não pôde ser concluída pois o diretório de origem não foi encontrado.") else: 133 CAPÍTULO 4. EXPERIMENTO PRÁTICO print("Aviso: Nenhuma pasta válida encontrada para processar em 'PlantVillage'. Verifique o conteúdo.") 4.3.2 Carregar e preparar os dados (Imagens) Com as pastas organizadas, usamos o ImageDataGenerator para carregar as imagens. Copie e Teste! from tensorflow.keras.preprocessing.image import ImageDataGenerator # Gerador com normalização (rescale) e data augmentation (opcional , mas bom para robustez) gerador_treino = ImageDataGenerator( rescale=1./255, rotation_range=20, # Rotaciona a imagem aleatoriamente width_shift_range=0.1, # Desloca horizontalmente height_shift_range=0.1, # Desloca verticalmente shear_range=0.1, # Aplica cisalhamento zoom_range=0.1, # Zoom aleatório horizontal_flip=True, # Inverte horizontalmente fill_mode='nearest' ) gerador_teste = ImageDataGenerator(rescale=1./255) # Para teste, apenas normalização # Caminho para as pastas # Usando a nova pasta definida no script de organização caminho_dados_app3 = 'dados_folhas/' IMG_HEIGHT, IMG_WIDTH = 128, 128 BATCH_SIZE = 32 # Carregar imagens para treino e teste try: treino_app3 = gerador_treino.flow_from_directory( os.path.join(caminho_dados_app3, 'treino'), target_size=(IMG_HEIGHT, IMG_WIDTH), color_mode='rgb', batch_size=BATCH_SIZE, class_mode='binary' # Saudável vs Doente ) teste_app3 = gerador_teste.flow_from_directory( os.path.join(caminho_dados_app3, 'teste'), target_size=(IMG_HEIGHT, IMG_WIDTH), 134 CAPÍTULO 4. EXPERIMENTO PRÁTICO color_mode='rgb', batch_size=BATCH_SIZE, class_mode='binary', shuffle=False # Importante para avaliação e matriz de confusão ) print("Geradores de dados criados. Verifique as classes encontradas:") print(f"Índices das classes (treino): {treino_app3. class_indices}") except FileNotFoundError: print(f"Erro: Diretório '{caminho_dados_app3}' ou suas subpastas 'treino'/'teste' não foram encontradas.") print("Certifique-se de que o script de organização de dados foi executado corretamente e criou as pastas.") Saída Esperada Found 16504 images belonging to 2 classes. Found 4134 images belonging to 2 classes. Geradores de dados criados. Verifique as classes encontradas: Índices das classes (treino): 'doente': 0, 'saudavel': 1 O que está acontecendo até agora? •Organização dos Dados (Script Python): O script fornecido automatiza a criação de uma estrutura de pastas (dados_folhas/treino/saudavel, dados_folhas/treino/doente, etc.) a partir do dataset PlantVillage original, separando as imagens em saudáveis ou doentes e dividindo-as em conjuntos de treino (80%) e teste (20%). É importante executar este script primeiro. •ImageDataGenerator: ImageDataGenerator do Keras é usado para carregar imagens diretamente dos diretórios. –rescale=1./255: Normaliza os valores dos pixels das imagens para o intervalo [0, 1]. – Data Augmentation (para treino): Parâmetros como rotation_range, width_shift_range, etc., são aplicados apenas ao gerador de treino. Eles criam versões modificadas das imagens de treino em tempo real (rotações, zooms, etc.), o que ajuda o modelo a generalizar melhor e a não memorizar as imagens exatas de treino, combatendo o overfitting [2, 1]. –target_size=(128, 128): Redimensiona todas as imagens para 128x128 pixels. –batch_size=32: As imagens serão carregadas em lotes de 32. –class_mode='binary': Indica que temos uma classificação binária (saudável ou doente). Os rótulos serão 0 ou 1. 135 CAPÍTULO 4. EXPERIMENTO PRÁTICO –shuffle=False (para teste_app3): Garante que as imagens de teste sejam processadas em ordem, o que é importante para uma avaliação consistente e para a matriz de confusão. •Verificação das Classes: É crucial verificar treino_app3.class_indices para saber qual rótulo numérico (0 ou 1) foi atribuído a ’saudavel’ e qual a ’doente’, pois isso afetará a interpretação das previsões. 4.3.3 Criar e Treinar a CNN Definimos uma arquitetura CNN simples e a treinamos com os dados preparados. Copie e Teste! from tensorflow.keras.models import Sequential from tensorflow.keras.layers import Conv2D, MaxPooling2D, Flatten, Dense, Dropout modelo_folhas = Sequential([ Conv2D(32, (3,3), activation='relu', input_shape=(IMG_HEIGHT, IMG_WIDTH, 3)), MaxPooling2D(pool_size=(2,2)), Conv2D(64, (3,3), activation='relu'), MaxPooling2D(pool_size=(2,2)), Conv2D(128, (3,3), activation='relu'), # Camada convolucional adicional MaxPooling2D(pool_size=(2,2)), Flatten(), Dropout(0.5), # Dropout mais significativo para regularização Dense(128, activation='relu'), Dense(1, activation='sigmoid')# Saída binária (1 neurônio com sigmoide) ]) modelo_folhas.compile(optimizer='adam', loss='binary_crossentropy' , metrics=['accuracy']) # Exibir resumo do modelo modelo_folhas.summary() # Treinar o modelo # Certifique-se de que 'treino_app3' e 'teste_app3' foram carregados corretamente if 'treino_app3' in locals() and 'teste_app3' in locals(): historico_folhas = modelo_folhas.fit( treino_app3, 136 CAPÍTULO 4. EXPERIMENTO PRÁTICO print("\nAponte a câmera para uma folha. Pressione 'q' para sair." ) while True: ret, frame = cap.read() if not ret: print("Erro: Não foi possível capturar o frame.") break img_processada_cam = cv2.resize(frame, (img_largura_cam, img_altura_cam)) img_array_cam = img_to_array(img_processada_cam) / 255.0 img_batch_cam = np.expand_dims(img_array_cam, axis=0) predicao_proba_cam = modelo_carregado.predict(img_batch_cam) [0][0] limiar_confianca = 0.5 if predicao_proba_cam > limiar_confianca: id_classe_prevista = 1 # Corresponde à classe 'Saudavel' no nosso exemplo de classes_map else: id_classe_prevista = 0 # Corresponde à classe 'Doente' label_previsto = classes_map.get(id_classe_prevista, " Desconhecido") # Ajustar cálculo de confiança para refletir a classe prevista confianca = predicao_proba_cam if id_classe_prevista == 1 else 1 - predicao_proba_cam texto_resultado = f"{label_previsto} ({confianca*100:.2f}%)" cor_texto = (0, 255, 0) if id_classe_prevista == 1 else (0, 0, 255) # Verde para saudável, Vermelho para doente (BGR) cv2.putText(frame, texto_resultado, (10, 30), cv2. FONT_HERSHEY_SIMPLEX, 0.9, cor_texto, 2) cv2.imshow('Classificador de Folhas - Pressione Q para sair', frame) if cv2.waitKey(1) & 0xFF == ord('q'): break cap.release() cv2.destroyAllWindows() print("Teste com a câmera finalizado.") O que está acontecendo no código de teste com a câmera? •Carregamento do Modelo: O modelo modelo_classificador_folhas.h5, previamente treinado e salvo, é carregado. 143 CAPÍTULO 4. EXPERIMENTO PRÁTICO •Definição das Classes: Um dicionário classes_map mapeia o índice da classe (0 ou 1) para o nome legível (”Doente”, ”Saudável”). Atenção: A correspondência correta (0 para Doente, 1 para Saudável, ou vice-versa) depende da saída de treino_app3.class_indices. •Captura da Webcam: cv2.VideoCapture(0) inicia a webcam, e um loop processa cada frame. •Pré-processamento do Frame: Cada frame é redimensionado, convertido para array NumPy, normalizado e suas dimensões são expandidas para o formato (1, altura, largura, canais). •Predição: modelo_carregado.predict(img_batch_cam) retorna a probabilidade da folha pertencer à classe positiva (saída da sigmoide). •Interpretação da Saída: Se a probabilidade > 0.5, é classificada como classe 1 (ex: ”Saudável”); caso contrário, classe 0 (ex: ”Doente”). A confiança da classe prevista é calculada. •Exibição do Resultado: O resultado da classificação e a confiança são escritos no frame de vídeo e exibidos. A cor do texto muda se for ”Saudável”(verde) ou ”Doente”(vermelho). •Encerramento: Pressionar ’q’ interrompe o loop, libera a câmera e fecha as janelas. Fique Alerta! O desempenho do modelo com imagens da câmera pode variar significativamente. Fatores como iluminação, ângulo, qualidade da imagem, fundo e tipos de folhas não vistos no treinamento podem afetar a precisão. Este é um ótimo exemplo da diferença entre dados curados e dados do ”mundo real”! Conhecendo um pouco mais! Para melhorar o desempenho em tempo real, considere: 1. Treinar com um dataset mais diverso e usar data augmentation de forma mais intensiva. 2. Implementar detecção de objetos para isolar a folha do fundo da imagem antes da classificação. 3. Explorar Transfer Learning com modelos pré-treinados em grandes datasets de imagens. 4.3.6 Interpretação dos Resultados e Discussão Prática Nesta aplicação, construímos e treinamos uma Rede Neural Convolucional (CNN) para classificar imagens de folhas de plantas como ”Saudável”ou ”Doente”, utilizando o dataset PlantVillage. Demonstramos também como salvar o modelo treinado e utilizá-lo para fazer classificações em tempo real com imagens de uma webcam. Interpretando os Resultados Uma alta acurácia no conjunto de teste do PlantVillage (verificada pelos gráficos de histórico e pela avaliação final) indica que a CNN aprendeu a distinguir características visuais relevantes para diferenciar folhas saudáveis de doentes dentro daquele conjunto de dados. A etapa de teste com a câmera, no entanto, provavelmente revelará desafios adicionais. Dificuldades em classificar corretamente folhas usando a webcam podem surgir devido a: 144 CAPÍTULO 4. EXPERIMENTO PRÁTICO •Variações de Iluminação e Fundo: O dataset PlantVillage possui imagens com fundo relativamente controlado. Imagens da webcam terão fundos complexos e iluminação variável. •Novas Espécies/Doenças: O modelo só conhece as espécies e doenças presentes no PlantVillage. •Qualidade da Imagem da Webcam: Resolução, foco e artefatos da câmera podem impactar. O objetivo do teste com a câmera é mais ilustrativo e demonstra o potencial, mas também as limitações de um modelo treinado em um dataset específico quando confrontado com a variabilidade do mundo real. Relevância para o Contexto Amazônico O diagnóstico rápido e preciso de doenças em plantas é crucial para a agricultura na Amazônia, onde a diversidade de cultivos e patógenos é grande. Sistemas baseados em CNNs podem: •Auxiliar Agricultores e Técnicos: Fornecer uma ferramenta de triagem inicial, ajudando a identificar problemas precocemente, mesmo em locais remotos, se o modelo for embarcado em dispositivos móveis. •Reduzir Perdas na Produção: Um diagnóstico rápido leva a tratamentos mais eficazes e oportunos. •Monitoramento Fitossanitário: Empregar drones com câmeras para capturar imagens de grandes áreas e usar CNNs para identificar focos de doenças, permitindo um controle mais direcionado. •Apoiar a Pesquisa Agronômica: Ajudar na coleta e análise de dados sobre a incidência e disseminação de doenças em diferentes condições. Soluções como esta podem fortalecer a agricultura familiar e as cooperativas, contribuindo para a segurança alimentar e a sustentabilidade econômica da região. Limitações e Próximos Passos •Generalização para o Mundo Real: O principal desafio é a generalização. Modelos treinados em datasets específicos podem não performar bem em todas as condições de campo. •Necessidade de Dados Locais: Para aplicações robustas na Amazônia, seria ideal treinar ou, pelo menos, ajustar finamente (fine-tuning) os modelos com imagens coletadas localmente, abrangendo as espécies, doenças e condições ambientais da região. •Identificação de Múltiplas Doenças e Deficiências Nutricionais: Este exemplo foi binário. Modelos mais avançados podem ser treinados para identificar tipos específicos de doenças ou até mesmo deficiências nutricionais com base em padrões visuais. •Interpretabilidade do Modelo: Técnicas como Grad-CAM podem ajudar a entender quais partes da imagem a CNN está usando para tomar sua decisão, aumentando a confiança no diagnóstico. 145 CAPÍTULO 4. EXPERIMENTO PRÁTICO Fique Alerta! A aplicação de IA para diagnóstico de doenças é uma ferramenta de apoio, não um substituto completo para a análise de especialistas (agrônomos, fitopatologistas). A validação em campo e a colaboração multidisciplinar são essenciais. Além disso, a qualidade e diversidade das imagens de treinamento são determinantes para o sucesso do modelo. Este experimento com CNNs para diagnóstico de doenças em folhas demonstra o potencial da visão computacional para resolver problemas práticos na agricultura. A capacidade de estender o modelo para testes com uma câmera em tempo real abre portas para aplicações interativas e demonstrações impactantes do poder do Deep Learning. 146 Referências Bibliográficas [1] CHOLLET, François. Deep Learning with Python. 2. ed. Shelter Island: Manning, 2021. [2] GÉRON, Aurélien. Aprenda Machine Learning com Scikit-Learn, Keras e TensorFlow. 3. ed. Sebastopol: O’Reilly Media, 2023. [3] GOODFELLOW, I.; BENGIO, Y.; COURVILLE, A. Deep learning. Cambridge: MIT Press, 2016. Disponível em: https://www.deeplearningbook.org. [4] HUGHES, David P.; SALATHÉ, Marcel. An open access repository of images on plant health to enable the development of mobile disease diagnostics. [S.l.]: arXiv, 2015. Disponível em: https://arxiv.org/pdf/1511.08060. [5] MARQUES, Bruno Torres; MARQUES, Leonardo Torres. Aprendizado de máquina: uma abordagem para descoberta de conhecimento. [S.l.]: Novas Edições Acadêmicas, 2022. [6] MITCHELL, Tom M. Machine Learning. New York: WCB/McGraw-Hill, 1997. [7] MUELLER, John Paul. Aprendizado de máquina para leigos. Rio de Janeiro: Alta Books, 2019. [8] NG, Andrew. Deep Learning Specialization. [Curso online]. [S.l.]: DeepLearning.AI; Stanford Online, 2022. Disponível em: https://www.coursera.org/ specializations/deep-learning. [9] NG, Andrew. Machine Learning Specialization. [Curso online]. [S.l.]: DeepLearning.AI; Stanford Online, 2022. Disponível em: https://www.coursera.org/ specializations/machine-learning-introduction. [10] NG, Andrew. Machine Learning Yearning: technical strategy for AI engineers, in the era of deep learning. [S.l.]: deeplearning.ai project, 2018. Disponível em: https:// info.deeplearning.ai/machine-learning-yearning-book/. [11] RASCHKA, Sebastian. Python Machine Learning: machine learning e deep learning com Python, Scikit-learn e TensorFlow 2. São Paulo: Novatec, 2021. [12] RUSSELL, Stuart; NORVIG, Peter. Artificial Intelligence: a modern approach. 3. ed. Upper Saddle River: Prentice Hall, 2010. 147