O Aprendizado Profundo (DL - Deep Learning) no campo do Aprendizado de Máquina (ML - Machine Learning) é uma abordagem revolucionária que tem transformado a capacidade de computadores entenderem e processarem dados complexos.
Utilizando redes neurais profundas, compostas por camadas de unidades de processamento, o DL é capaz de aprender representações de dados cada vez mais abstratas e complexas.
Isso tem levado a avanços significativos em tarefas como reconhecimento de imagens, processamento de linguagem natural (NLP - Natural Language Processing) e muito mais.
À medida que os modelos se tornam mais profundos e as técnicas de treinamento se aprimoram, o DL continua a moldar o futuro da inteligência artificial e a encontrar aplicações em uma ampla gama de setores.
O DL se concentra em treinar modelos de redes neurais artificiais para realizar tarefas complexas de aprendizado a partir de dados brutos, e tem sido uma das tecnologias mais revolucionárias na área de inteligência artificial nas últimas décadas, impulsionando avanços significativos em diversas áreas, como visão computacional, NLP, reconhecimento de fala, jogos, entre outros.
Aqui estão os principais aspectos e conceitos do DL:
As técnicas de DL são aplicadas em uma variedade de campos e setores, devido à sua capacidade de lidar com tarefas complexas e aprender representações significativas diretamente dos dados brutos.
Algumas das principais áreas de aplicação incluem:
Exemplo de visão Computacional:
No vídeo, à esquerda temos a simulação de 3 câmeras e à direita uma janela rodando em paralelo, em uma thread separada, detectando a presença de pessoas nas imagens das câmeras.
Essas são apenas algumas das inúmeras áreas onde as técnicas de DL têm sido aplicadas com sucesso. A flexibilidade e o poder das redes neurais artificiais possibilitam sua utilização em uma ampla variedade de problemas e cenários, tornando o DL uma das tecnologias mais impactantes e em constante crescimento no campo da inteligência artificial.
Existem várias bibliotecas Python especializadas em DL que oferecem ferramentas poderosas para construir, treinar e implantar modelos de redes neurais.
Algumas das principais bibliotecas são:
Essas são algumas das principais bibliotecas Python especializadas em DL. Cada uma delas tem suas características únicas e vantagens, e a escolha depende das necessidades específicas do projeto, da preferência pessoal e do suporte de hardware disponível. Muitas vezes, as bibliotecas são usadas em conjunto para aproveitar o melhor de cada uma delas.
O DL é uma poderosa abordagem de ML que tem tido um impacto significativo em várias áreas, permitindo que máquinas realizem tarefas complexas e alcancem resultados impressionantes em visão computacional, NLP e muitas outras aplicações. Seu contínuo desenvolvimento e avanços têm levado a conquistas notáveis em inteligência artificial e, por isso, o DL é amplamente adotado em projetos e pesquisas em todo o mundo.
As Redes Neurais Artificiais (ANNs - Artificial Neural Networks) representam um dos pilares do ML, sendo fundamentais para tarefas de classificação, regressão e muito mais. Inspiradas pelo funcionamento do cérebro humano, ANNs consistem em camadas de neurônios interconectados, que processam informações e aprendem a partir de dados. No entanto, a capacidade de ANNs de modelar relações em dados tabulares e sequenciais é limitada.
Para lidar com dados de imagem, as Redes Neurais Convolucionais (CNNs - Convolutional Neural Networks) surgiram como uma revolução. Com suas camadas convolucionais, as CNNs extraem características espaciais de imagens, tornando-as ideais para tarefas de visão computacional, como reconhecimento de objetos e segmentação de imagens. Elas são altamente eficazes na captura de padrões visuais complexos.
Enquanto ANNs e CNNs brilham em tarefas relacionadas a dados tabulares e imagens, as Redes Neurais Recorrentes (RNNs - Recurrent Neural Networks) são projetadas para lidar com dados sequenciais, como texto e séries temporais. As RNNs têm memória interna e podem capturar dependências temporais em dados, tornando-os adequados para tarefas de NLP, tradução automática e previsão de séries temporais.
As ANNs formam a base do ML e são amplamente aplicadas em muitas tarefas, as CNNs são a escolha principal para tarefas de visão computacional, enquanto as RNNs são cruciais para tarefas envolvendo dados sequenciais. A combinação dessas arquiteturas permite abordar uma ampla gama de problemas de ML, tornando as redes neurais uma ferramenta versátil na era da inteligência artificial.
As GANs (Redes Generativas Adversariais) são uma arquitetura especial de rede neural usada para a geração de dados, como imagens, e têm características únicas em comparação com ANNs, CNNs e RNNs
As Redes Neurais Artificiais (ANN - Artificial Neural Networks) são modelos matemáticos inspirados no funcionamento do cérebro humano, compostos por unidades interconectadas denominadas neurônios artificiais, ou "nós", organizadas em camadas e projetadas para resolver problemas de ML e tarefas de reconhecimento de padrões.
Estes modelos servem de base para várias técnicas de DL e têm sido usadas com sucesso em diversas aplicações.
Cada neurônio recebe entradas ponderadas, aplica uma função de ativação não linear a essas entradas e produz uma saída.
A conexão entre neurônios é representada por pesos, que são ajustados durante o processo de treinamento para que a rede possa aprender a mapear as entradas para as saídas corretas.
Cada neurônio recebe entradas, realiza operações matemáticas nelas e gera uma saída que é transmitida para outros neurônios na rede.
Essa estrutura em camadas é o que permite às ANNs aprenderem a partir dos dados e realizar tarefas complexas, como classificação, regressão, NLP, visão computacional, entre outros.
As ANNs são organizadas com camadas distintas:
O processo de treinamento de uma ANN envolve alimentar o modelo com um conjunto de dados de treinamento conhecidos e ajustar os pesos das conexões entre os neurônios para minimizar o erro entre as previsões da rede e as saídas corretas. Isso é geralmente feito usando algoritmos de otimização, como o gradiente descendente, que ajustam os pesos para que a rede possa aprender a representar corretamente os padrões nos dados de treinamento.
As ANNs têm sido aplicadas em uma ampla variedade de tarefas, como classificação de imagens, reconhecimento de fala, NLP, previsão de séries temporais, entre outras. Com o avanço das técnicas de DL, as ANNs têm se mostrado particularmente eficazes em tarefas que envolvem grandes volumes de dados e problemas complexos.
Um dos desafios do uso de ANNs é a necessidade de grandes quantidades de dados de treinamento e poder computacional para ajustar os milhões de pesos da rede. No entanto, com os avanços tecnológicos e o acesso a GPUs e TPUs, as ANNs têm se mostrado cada vez mais poderosas em uma variedade de aplicações, tornando-se uma das ferramentas mais importantes e impactantes no campo do ML e da Inteligência Artificial.
A seguir, apresentamos alguns conceitos importantes relacionados às Redes Neurais Artificiais:
As ANNs são uma das ferramentas mais poderosas do ML moderno e têm alcançado resultados impressionantes em uma variedade de domínios.
No entanto, treinar redes neurais profundas pode exigir grandes quantidades de dados e poder computacional, mas com os avanços em hardware e algoritmos, as ANNs continuam sendo objeto de pesquisa intensiva e são amplamente utilizadas em problemas complexos de aprendizado.
As Redes Neurais Convolucionais (CNNs - Convolutional Neural Networks) são um tipo especializado de ANNs que se destacam em tarefas de visão computacional, como classificação de imagens, detecção de objetos e segmentação semântica.
Elas foram inspiradas pela organização do córtex visual no cérebro humano e têm demonstrado um desempenho impressionante em diversas aplicações.
As CNNs são projetadas para trabalhar com dados que têm uma estrutura espacial, como imagens, e têm a capacidade de aprender representações hierárquicas e invariantes da translação de características dos dados.
Isso é possível devido a duas operações principais que as CNNs empregam: convolução e agrupamento (pooling).
Conceitos das CNNs:
Camadas nas CNNs:
Podemos observar como funciona uma CNN na prática com o diagrama abaixo:
Na primeira etapa, entrada, a rede irá receber uma imagem já pré-processada, geralmente a imagem é representada em 3 camadas RGB ou em tons de cinza.
Em seguida, na etapa de aprendizado de características (feature learning), estas serão aprendidas pelos processos de convolução e agrupamento.
O processo de convolução consiste em utilizar um filtro, também chamado de kernel, para percorrer várias pequenas matrizes na imagem e identificar suas características mais importantes.
Quanto ao agrupamento (pooling), que pode ser interpretado como uma camada simplificadora da camada anterior, atua reduzindo o tamanho dos mapas de características gerados pelas camadas de convolução, para que o processo seja otimizado no decorrer da rede.
Na última etapa, classificação, a rede usará um Perceptron de Múltiplas Camadas (MLP - Multi-Layer Perceptron), ou uma camada totalmente conectada, para realizar a classificação baseando-se nas características obtidas na etapa de aprendizagem de características.
Perceptrons de múltiplas camadas são uma classe de redes neurais artificiais que consistem em múltiplas camadas de neurônios interconectados, incluindo uma camada de entrada, uma ou mais camadas ocultas e uma camada de saída. Cada neurônio em uma camada está conectado a todos os neurônios na camada seguinte, tornando os MLPs capazes de aprender representações complexas de dados. São amplamente utilizados para tarefas de classificação, regressão e outras tarefas de aprendizado supervisionado, devido à sua capacidade de modelar relações não lineares nos dados. O treinamento de MLPs geralmente envolve algoritmos de retropropagação, que ajustam os pesos das conexões para minimizar uma função de perda, permitindo que o modelo faça previsões precisas.
Aqui está um exemplo de como as classes de camadas do Keras podem ser usadas em uma arquitetura de CNN:
from tensorflow.keras.layers import Conv2
from tensorflow.keras.layers import MaxPooling2D
from tensorflow.keras.layers import Flatten
from tensorflow.keras.layers import Dense
from tensorflow.keras.models import Sequential
from tensorflow.keras.datasets import mnist
(x_treino, y_treino), (x_teste, y_teste) = mnist.load_data()
modelo = Sequential()
modelo.add(Conv2(32, (3, 3), activation='relu', input_shape=(28, 28, 1)))
modelo.add(MaxPooling2D((2, 2)))
modelo.add(Flatten())
modelo.add(Dense(128, activation='relu'))
modelo.add(Dense(10, activation='softmax'))
modelo.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy'])
modelo.fit(x_treino, y_treino, epochs=10)
modelo.evaluate(x_teste, y_teste)
modelo.predict(x_teste)
As CNNs têm tido um impacto significativo em tarefas de visão computacional e se mostraram altamente eficazes em reconhecimento de padrões em imagens.
Além disso, suas arquiteturas têm sido adaptadas para outros tipos de dados estruturados, como séries temporais e dados de áudio.
Devido ao seu poder de representação e aprendizado de características relevantes, as CNNs são amplamente utilizadas em uma variedade de aplicações, desde carros autônomos até diagnóstico médico e muito mais.
As Redes Neurais Recorrentes (RNNs - Recurrent Neural Networks) são um tipo especializado de ANN projetadas para lidar com dados sequenciais, como séries temporais, texto e fala. A principal característica das RNNs é a capacidade de lidar com sequências de comprimentos variáveis, mantendo uma memória interna que permite que elas considerem informações contextuais de eventos anteriores ao processar o próximo elemento da sequência.
Diferentemente das CNNs, que são altamente eficazes para dados com estrutura espacial, as RNNs são projetadas para dados com dependências temporais e são especialmente adequadas para problemas que envolvem previsão, classificação ou geração de sequências.
Conceitos das RNNs:
Camadas nas CNNs:
Aqui está um exemplo de como as classes de camadas do Keras podem ser usadas em uma arquitetura de CNN:
from keras.models import Sequential
from keras.layers import SimpleRNN, Embedding, Dense
# Crie um modelo de RNN sequencial
modelo = Sequential()
# Adicione uma camada de embedding para representar palavras em vetores contínuos
modelo.add(Embedding(input_dim=1000, output_dim=128, input_length=20))
# Adicione uma camada recorrente SimpleRNN com 64 unidades
modelo.add(SimpleRNN(64, activation='relu'))
# Camada de saída com ativação softmax para classificação
modelo.add(Dense(10, activation='softmax'))
As RNNs têm sido amplamente utilizadas em tarefas de NLP, como tradução automática, geração de texto e análise de sentimento, bem como em outras tarefas que envolvem sequências temporais, como previsão de séries temporais, reconhecimento de fala, entre outras. Com as variantes mais avançadas, como as LSTMs e GRUs, as RNNs são capazes de aprender dependências temporais de longo prazo e lidar com sequências mais extensas e complexas.
As Redes Neurais Generativas (GANs - Generative Adversarial Networks) são uma arquitetura especial de rede neural usada para a geração de dados, como imagens, e têm características únicas em comparação com ANNs, CNNs e RNNs.
Consistem em duas redes neurais, o Gerador e o Discriminador, que competem em um jogo adversarial. O Gerador cria dados sintéticos, enquanto o Discriminador tenta distinguir entre dados reais e sintéticos.
Aspectos mencionados em relação às GANs:
As GANs são uma arquitetura especializada para a geração de dados realistas, envolvendo uma competição adversarial entre o Gerador e o Discriminador. Elas podem incorporar elementos de CNNs para processamento de imagem, mas não são projetadas para lidar com sequências temporais.
O treinamento de GANs pode ser desafiador e requer atenção especial aos problemas de gradiente. Seu foco principal é o aprendizado de características para a geração de dados, resultando em aplicações emocionantes em áreas como visão computacional e geração de conteúdo.
Alguns conceitos importantes sobre os neurônios, gradientes e aprendizado de características.
Os neurônios em cada camada são responsáveis por aprender representações dos dados e transmitir essas representações às camadas subsequentes.
O número de neurônios em cada camada de uma rede neural é uma parte fundamental do projeto da rede e tem um impacto significativo no seu desempenho e capacidade de aprendizado, afetando a capacidade da rede de aprender representações complexas dos dados.
Camadas com mais neurônios podem aprender representações mais ricas, mas também podem aumentar a complexidade do modelo.
É importante ajustar esse hiperparâmetro com base na natureza da tarefa e na quantidade de dados disponíveis para o treinamento. Experimentar diferentes tamanhos de camada pode ser necessário para encontrar a configuração ideal para um problema específico.
Vamos explorar como isso funciona.
O número de neurônios em uma camada é também chamado de "tamanho" ou "largura" da camada. É uma das principais decisões de hiperparâmetros ao projetar uma rede neural.
O número de neurônios em uma camada pode variar de acordo com a tarefa. Em geral, uma camada com mais neurônios é capaz de aprender representações mais complexas, mas também pode levar a um aumento na complexidade do modelo e na necessidade de mais dados de treinamento.
Cada neurônio em uma camada é associado a uma função de ativação, que determina como o neurônio responde a entradas. Funções comuns incluem ReLU (Unidade Linear Retificada), sigmoid e tangente hiperbólica.
Durante o treinamento, as informações fluem da camada de entrada para as camadas ocultas e, finalmente, para a camada de saída. A passagem de informações ocorre da seguinte maneira:
As entradas são multiplicadas pelos pesos sinápticos associados a cada neurônio na camada.
Os resultados são somados e, em seguida, a função de ativação é aplicada a essa soma para produzir a saída do neurônio.
As saídas dos neurônios em uma camada se tornam as entradas para a camada seguinte. Esse processo de propagação de informações é repetido em todas as camadas da rede até a camada de saída.
As camadas intermediárias, chamadas camadas ocultas, são responsáveis por aprender representações intermediárias dos dados. Quanto mais camadas ocultas a rede possui, mais níveis de abstração ela pode aprender.
O número de neurônios em cada camada oculta determina a dimensionalidade dessas representações intermediárias. Camadas com mais neurônios podem aprender representações mais ricas, enquanto camadas com menos neurônios podem aprender representações mais simples.
Durante o treinamento, a rede ajusta os pesos sinápticos (parâmetros) de cada neurônio para minimizar a função de perda. Isso envolve a propagação do erro retroativamente, ou seja, a retropropagação, para ajustar os pesos em todas as camadas.
Os gradientes se referem às derivadas parciais de uma função de perda em relação aos parâmetros do modelo. Essas derivadas indicam como a função de perda muda à medida que os parâmetros do modelo são ajustados, e são fundamentais para o processo de treinamento de redes neurais.
Para entender melhor o papel dos gradientes no Aprendizado Profundo, aqui estão alguns pontos-chave:
Os gradientes desempenham um papel essencial no treinamento de redes neurais. Eles guiam o processo de ajuste dos parâmetros do modelo para minimizar a função de perda, permitindo que a rede aprenda a fazer previsões precisas em tarefas de aprendizado supervisionado.
O retropropagação (backpropagation) é um algoritmo fundamental para o treinamento de redes neurais artificiais, especialmente em modelos de aprendizado supervisionado. Ele é usado para calcular os gradientes dos pesos da rede em relação à função de perda, permitindo assim a atualização dos pesos durante o processo de treinamento.
A ideia central da retropropagação é realizar uma propagação do erro da saída da rede para as camadas anteriores, calculando os gradientes das funções de ativação em relação aos pesos.
Esse processo acontece em duas fases: a fase de passagem direta (forward pass) e a fase de passagem reversa (backward pass).
O retropropagação é essencial para o treinamento de redes neurais profundas, onde há várias camadas ocultas, pois permite que a informação do erro seja propagada eficientemente através da rede, ajustando os pesos para que o modelo possa aprender a representar os padrões e relacionamentos presentes nos dados de treinamento.
É importante mencionar que o sucesso da retropropagação está ligado ao uso de funções de ativação não lineares nas camadas ocultas da rede. Sem as funções não lineares, a rede seria apenas uma combinação linear de operações, tornando-a incapaz de aprender representações complexas e abstratas dos dados.
O Aprendizado de Características (FL - Features Learning) é uma etapa essencial em tarefas de ML e DL, agindo no processo de identificar e criar representações ou características significativas e informativas a partir dos dados brutos, tornando-os mais adequados para a tarefa específica em questão.
As características são representações que descrevem diferentes aspectos dos dados, e a qualidade dessas características pode ter um grande impacto no desempenho dos modelos de ML. Quando as características são bem escolhidas e representam de forma eficiente as informações importantes nos dados, os modelos tendem a ter um desempenho melhor e mais robusto.
Existem duas abordagens principais para o FL:
O FL é uma etapa crítica no processo de preparação dos dados para a modelagem de ML e DL. A escolha ou aprendizado de características adequadas pode fazer uma grande diferença no desempenho dos modelos, permitindo que eles capturem com eficácia os padrões e informações importantes presentes nos dados e tornando-os mais robustos e eficientes em suas tarefas.
As três arquiteturas de redes neurais - ANNs, CNNs e RNNs - têm a capacidade de realizar FL, embora a maneira como o fazem possa variar:
As ANNs podem realizar FL, mas geralmente dependem da pré-processamento dos dados de entrada. O FL é frequentemente feita antes de alimentar os dados à rede, no estágio de pré-processamento dos dados. Isso pode incluir redimensionamento, normalização, codificação de variáveis categóricas e extração de características antes de alimentar os dados à camada de entrada da rede.
As CNNs são especialmente eficazes em realizar FL diretamente dos dados de imagem. Elas podem aprender automaticamente a extrair características relevantes das imagens, como bordas, texturas e padrões complexos. O FL ocorre nas camadas de convolução e agrupamento (pooling) de uma CNN. As camadas de convolução aplicam filtros para extrair características locais, enquanto as camadas de pooling reduzem a dimensionalidade e mantêm características relevantes.
As RNNs não são tradicionalmente usadas para o FL, mas podem ser usadas para processar sequências de dados e aprender representações temporais. A principal função das RNNs é modelar dependências temporais em sequências. Elas não realizam FL no sentido convencional, mas podem aprender representações de sequências durante o treinamento.
Todas as três arquiteturas podem contribuir para o FL, mas a maneira como o fazem varia. As CNNs são especialmente poderosas na extração automática de características de imagens, enquanto as ANNs geralmente dependem de pré-processamento de dados. As RNNs são mais focadas em modelar dependências temporais em sequências de dados, embora também possam aprender representações de sequências.
A Transferência de Aprendizado (LT - Learning Transfer) é uma técnica essencial em ML e DL que permite aproveitar conhecimentos adquiridos em uma tarefa ou domínio para melhorar o desempenho em outra tarefa ou domínio relacionado. É uma abordagem poderosa que ajuda a economizar tempo e recursos no treinamento de modelos, especialmente quando há limitações de dados disponíveis.
O princípio básico da LT é que um modelo que tenha sido treinado para uma tarefa específica pode aprender representações de características gerais que são úteis para outras tarefas relacionadas.
Essas representações gerais são aprendidas nas camadas mais profundas da rede neural, capturando informações abstratas e complexas dos dados.
A aplicação da LT geralmente envolve as seguintes etapas:
A LT oferece várias vantagens:
A LT tem aplicações em diversas áreas, incluindo:
E muitas outras tarefas de ML.
A LT é uma técnica poderosa que permite aproveitar o conhecimento adquirido em tarefas de origem para melhorar o desempenho em tarefas de destino relacionadas.
É especialmente útil quando há poucos dados rotulados disponíveis para a tarefa de destino, tornando-se uma estratégia valiosa para o sucesso de muitos projetos de ML e DL.
O Aprendizado Supervisionado (SL - Supervised Learning) é uma das principais categorias de técnicas de ML. Nesse tipo de aprendizado, o modelo é treinado utilizando um conjunto de dados rotulados, ou seja, dados nos quais as saídas desejadas ou rótulos estão disponíveis para cada exemplo de entrada.
O processo de treinamento do modelo envolve ajustar os parâmetros do modelo para que ele seja capaz de mapear corretamente as entradas para as saídas corretas. O objetivo do SL é fazer com que o modelo generalize bem para novos dados não vistos durante o treinamento, sendo capaz de fazer previsões precisas para novas amostras.
Algumas das principais tarefas de SL incluem:
O Aprendizado Não Supervisionado (UL - Unsupervised Learning) é outra categoria de técnicas de ML. Nesse tipo de aprendizado, o modelo é treinado em um conjunto de dados não rotulados, ou seja, os rótulos ou saídas desejadas não estão disponíveis para o modelo durante o treinamento.
O objetivo do UL é encontrar padrões e estruturas ocultas nos dados de entrada sem a necessidade de rótulos prévios. Isso pode ser útil para descobrir grupos (clusters) de amostras semelhantes ou para reduzir a dimensionalidade dos dados para visualização ou compressão.
Algumas das principais tarefas de UL incluem:
É importante ressaltar que, em algumas situações, é possível combinar técnicas de Aprendizado Supervisionado e Não Supervisionado, como na aprendizagem semi-supervisionada, onde uma parte dos dados é rotulada e outra parte não é. O Aprendizado Supervisionado e o Aprendizado Não Supervisionado são complementares e têm aplicações em diversas áreas, permitindo que os modelos aprendam a partir de diferentes tipos de dados e atendam a diferentes objetivos.
Em DL, as camadas e arquiteturas referem-se à estrutura e organização das redes neurais artificiais (ANNs) ou outros modelos de DL. Vamos detalhar cada um desses conceitos:
Uma rede neural é composta por várias camadas de neurônios interconectados. Cada camada possui um conjunto de neurônios que recebem entradas, realizam um processamento e produzem saídas. As camadas são organizadas em uma sequência, onde a saída de uma camada serve como entrada para a próxima.
As principais camadas em uma rede neural são:
A arquitetura de uma rede neural se refere à organização e configuração das camadas e neurônios na rede. Existem várias arquiteturas de redes neurais, cada uma com suas características e usos específicos. Algumas das arquiteturas mais comuns incluem:
Essas são apenas algumas das arquiteturas comuns de redes neurais. Existem muitas outras arquiteturas e variações que são utilizadas em diferentes tarefas e aplicações de DL. A escolha da arquitetura adequada é crucial para alcançar o desempenho desejado em uma determinada tarefa de ML.
Frameworks e bibliotecas são ferramentas essenciais no campo do ML e DL, pois oferecem uma infraestrutura e um conjunto de funcionalidades que facilitam o desenvolvimento, treinamento e implantação de modelos.
Um Framework é um conjunto abrangente de ferramentas, bibliotecas e APIs que fornecem uma estrutura de trabalho para desenvolver aplicativos e modelos de ML. Os frameworks de ML e DL geralmente incluem uma variedade de módulos que abrangem desde a manipulação de dados até a implementação de algoritmos complexos de ML.
Alguns dos frameworks mais populares e amplamente utilizados incluem:
As bibliotecas são conjuntos de funções e algoritmos que são projetados para realizar tarefas específicas em ML e DL. Diferentemente dos frameworks, as bibliotecas podem ser mais focadas em tarefas específicas e podem ser usadas de forma independente ou em conjunto com um framework.
Alguns exemplos de bibliotecas populares em ML e DL incluem:
Essas são apenas algumas das muitas frameworks e bibliotecas disponíveis em ML e DL, e sua escolha dependerá do tipo de tarefa que está sendo realizada e das preferências do desenvolvedor. A combinação certa pode facilitar significativamente o processo de desenvolvimento de modelos de ML e torná-lo mais eficiente e produtivo.
O hardware diferenciado desempenha um papel fundamental na viabilidade e eficácia do DL no ML.
A complexidade computacional das redes neurais profundas requer poder de processamento massivo, e as GPUs (Unidades de Processamento Gráfico) e, em alguns casos, TPUs (Unidades de Processamento de Tensor), oferecem aceleração significativa para treinamento e inferência de modelos.
Essas arquiteturas especializadas permitem a paralelização eficiente de cálculos, acelerando o processo de experimentação e desenvolvimento de modelos.
Além disso, a escalabilidade do hardware diferenciado é crucial para lidar com conjuntos de dados cada vez maiores.
A busca contínua por inovações em hardware, como chips específicos para tarefas de IA, destaca a importância de um ecossistema de hardware robusto para impulsionar o campo do DL e suas aplicações em uma variedade de setores, desde a medicina até a indústria automobilística.
O desenvolvimento de hardware e a melhoria contínua das tecnologias são fundamentais para superar os desafios em ML e DL, permitindo a criação e implantação eficiente de modelos cada vez mais poderosos e sofisticados.
O DL, apesar de suas conquistas notáveis, enfrenta desafios significativos. Um deles é a necessidade de grandes volumes de dados para treinar modelos complexos, o que nem sempre está disponível, especialmente em domínios específicos.
Além disso, a complexidade desses modelos pode tornar o treinamento demorado e exigir hardware poderoso, aumentando os custos computacionais.
A interpretabilidade também é um desafio, uma vez que redes profundas muitas vezes operam como "caixas-pretas" difíceis de entender.
Lidar com o sobreajuste (Overfitting), a transferência de aprendizado e a adaptação a mudanças de conceito são outras questões críticas.
A ética e a privacidade também surgem como preocupações, já que modelos podem perpetuar preconceitos e lidar com dados sensíveis.
Superar esses desafios é fundamental para aproveitar todo o potencial do DL de forma eficaz e responsável.
O Keras é uma biblioteca de Aprendizado Profundo (DL - Deep Learning) versátil e amigável que facilita a criação e o treinamento de redes neurais, de código aberto e alto nível.
Sua flexibilidade, simplicidade, facilidade de uso e integração com bibliotecas populares, sendo executada sobre o TensorFlow, o Theano e o CNTK, a tornaram uma escolha popular para muitos projetos de aprendizado de máquina (ML) e DL, permitindo que os desenvolvedores construam, treinem e avaliem redes neurais de maneira eficaz.
As redes neurais comportam diferentes arquiteturas usando modelos sequencial ou funcional e dispondo de camadas de inicio, ocultas e fim próprias para uso com as diferentes arquiteturas.
Aqui estão alguns pontos importantes sobre o Keras:
Na construção de redes neurais com o Keras, os desenvolvedores têm à disposição dois principais paradigmas de modelagem: Modelos Sequenciais e Modelos Funcionais.
Os Modelos Sequenciais são ideais para tarefas em que a informação flui de maneira linear de uma camada para a próxima, proporcionando uma abordagem simples e direta na construção de redes.
Aqui está um exemplo de criação de um Modelo Sequencial em Keras:
from keras.models import Sequential
from keras.layers import Dense
modelo = Sequential()
modelo.add(Dense(32, input_shape=(784,)))
modelo.add(Dense(64, activation='relu'))
modelo.add(Dense(10, activation='softmax'))
Neste exemplo, criamos um Modelo Sequencial com três camadas, onde a primeira camada tem 32 unidades, a segunda tem 64 unidades com ativação ReLU e a terceira tem 10 unidades com ativação softmax para classificação.
Por outro lado, Modelos Funcionais oferecem uma flexibilidade excepcional, permitindo a criação de arquiteturas complexas e personalizadas, com múltiplas entradas e saídas, conexões não lineares entre camadas, e compartilhamento de camadas.
Aqui está um exemplo de criação de um Modelo Funcional em Keras:
from keras.layers import Input, Dense
from keras.models import Model
# Definindo as entradas
dados_entrada = Input(shape=(784,))
# Criando camadas e conectando-as
escondido_1 = Dense(32, activation='relu')(dados_entrada)
escondido_2 = Dense(64, activation='relu')(escondido_1)
output = Dense(10, activation='softmax')(escondido_2)
# Criando o modelo
modelo = Model(inputs=dados_entrada, outputs=output)
Neste exemplo, começamos definindo as entradas, criando camadas independentes e conectando-as para construir uma arquitetura personalizada. Isso é útil quando você precisa criar redes mais complexas, como redes siamesas, redes com múltiplas entradas ou saídas, ou qualquer arquitetura não linear.
Ambos os modelos desempenham um papel crucial no desenvolvimento de redes neurais profundas, adaptando-se a uma variedade de tarefas e requisitos de modelagem.
Em resumo, Modelos Sequenciais são ideais para arquiteturas de rede neural com fluxo linear de camadas, enquanto Modelos Funcionais oferecem flexibilidade para criar arquiteturas complexas e personalizadas, com conexões não lineares entre camadas, múltiplas entradas e saídas, entre outros. A escolha entre eles depende da natureza do seu problema e da arquitetura da rede que você deseja construir.
Redes neurais são modelos de aprendizado de máquina fortemente influenciados pela estrutura e funcionamento do cérebro humano. Elas são compostas por camadas de unidades interconectadas, chamadas de neurônios artificiais ou unidades de processamento. Essas redes são projetadas para processar informações e aprender a partir de dados, tornando-as poderosas para uma ampla gama de tarefas de aprendizado de máquina, como classificação, regressão, processamento de linguagem natural e visão computacional.
O funcionamento de uma rede neural envolve a passagem de dados através das camadas, onde cada neurônio realiza cálculos ponderados e aplica funções de ativação. Durante o treinamento, as conexões entre os neurônios, chamadas de pesos, são ajustadas para minimizar o erro entre as previsões do modelo e os dados de treinamento. Esse processo de ajuste de pesos é realizado por algoritmos de otimização, como o gradiente descendente.
Redes neurais podem variar em complexidade, desde simples redes feedforward com uma única camada oculta até arquiteturas profundas com muitas camadas e milhões de neurônios. O sucesso das redes neurais em tarefas complexas é atribuído, em parte, ao seu poder de representação e à capacidade de aprender características relevantes diretamente dos dados.
As Redes Neurais Convolucionais (CNNs - Convolutional Neural Networks) são amplamente utilizadas para tarefas de visão computacional, como reconhecimento de imagens, detecção de objetos e segmentação de imagens.
São algoritmos de DL que recebem imagens como entrada, impondo importância às características (features), baseado-se nos pesos (weights) e vieses (biases) definidos pelos aspectos da rede, para que o classificador possa distinguir as imagens.
A arquitetura de uma CNN é baseada nos padrões dos neurônios de um cérebro humano, onde neurônios respondem individualmente à estímulos em uma área específica do campo visual chamada de campo receptor, e um conjunto desses campos interpretam a imagem.
O Keras oferece suporte completo para a criação e treinamento de CNNs.
Aqui estão alguns recursos e camadas específicas do Keras para CNNs:
As Redes Neurais Recorrentes (RNNs - Recurrent Neural Networks) são amplamente utilizadas em redes neurais profundas, em tarefas que envolvem dados sequenciais, como séries temporais, processamento de linguagem natural (NLP), classificação e emparelhamento de texto, verificação de rosto, etc.
O Keras suporta RNNs e oferece camadas específicas para trabalhar com sequências.
Alguns recursos relacionados a RNNs no Keras incluem:
Além de CNNs e RNNs, o Keras suporta uma ampla variedade de outras arquiteturas de redes neurais e tipos de modelos, incluindo:
Essas são apenas algumas das arquiteturas de redes neurais que o Keras suporta. A biblioteca é altamente versátil e permite a construção de uma ampla variedade de modelos para diferentes tipos de tarefas de aprendizado de máquina e deep learning.
Conv2D é uma camada de convolução bidimensional usada em redes neurais convolucionais (CNNs) para processar dados de imagem e extrair características locais.
Através dos parâmetros são definidos os números de filtros, o tamanho do filtro e a função de ativação.
A camada Conv2D realiza uma operação de convolução bidimensional na imagem de entrada.
Os passos para construir a camada Conv2D são:
Os mapas de características resultantes da convolução sao chamados de maps, e são usados como entrada para outras camadas.
As camadas Conv2D são fundamentais para CNNs, permitindo que a rede detecte bordas, texturas, padrões e recursos mais complexos em imagens, desempenhando um papel crítico na extração de características relevantes e na capacidade da rede de realizar tarefas de visão computacional, como classificação de objetos, detecção de rostos e segmentação de imagens.
A camada MaxPooling2D no Keras é uma camada de pooling bidimensional usada em redes neurais convolucionais (CNNs) para reduzir a dimensionalidade dos mapas de características e, ao mesmo tempo, preservar as características mais proeminentes.
Através dos parâmetros são definidos o tamanho da janela de pooling, o passo com que a janela de pooling se move e a maneira de lidar com os limites dos mapas de características.
A camada MaxPooling2D realiza uma operação de pooling bidimensional na saída da camada de convolução.
Aqui estão os passos principais:
O principal objetivo da camada MaxPooling2D é reduzir a dimensionalidade dos mapas de características, diminuindo o número de unidades (neurônios) em cada mapa.
Isso é benéfico por várias razões:
As camadas MaxPooling2D são frequentemente intercaladas com camadas de convolução em CNNs para criar arquiteturas eficazes na extração de características em imagens. Elas ajudam a consolidar as informações relevantes enquanto reduzem a dimensionalidade, o que é fundamental para a tarefa de processamento de imagens.
Em resumo, a camada MaxPooling2D é uma etapa importante em redes convolucionais para reduzir a dimensionalidade e manter as características mais relevantes. Ela desempenha um papel fundamental na eficácia e eficiência das CNNs, permitindo a extração de características e reduzindo o risco de overfitting.
a camada da classe Dense é uma das mais comuns usadas em redes neurais artificiais, especialmente em redes neurais feedforward, onde cada neurônio ou unidade em uma camada está conectado a cada neurônio na camada subsequente. A camada Dense é uma camada totalmente conectada, o que significa que cada neurônio na camada atual está conectado a todos os neurônios na camada seguinte.
Aqui estão alguns dos principais aspectos da camada Dense:
Neurônios ou Unidades (Units): A camada Dense é definida com um número de neurônios ou unidades. Cada neurônio na camada representa uma unidade computacional que recebe entradas, executa uma combinação linear das entradas ponderadas e aplica uma função de ativação.
Função de Ativação: Você pode especificar uma função de ativação para os neurônios na camada Dense. Funções de ativação comuns incluem ReLU (Rectified Linear Unit), sigmoid e softmax. A função de ativação determina a saída do neurônio com base na soma ponderada das entradas.
Pesos e Viés (Weights and Bias): Cada conexão entre os neurônios na camada Dense possui um peso. Os pesos são ajustados durante o treinamento da rede para aprender padrões nos dados. Além disso, cada neurônio tem um valor de viés (bias) que é somado à soma ponderada das entradas antes de aplicar a função de ativação.
Operação de Camada Oculta ou de Saída: A camada Dense pode ser usada como uma camada oculta ou como a camada de saída de uma rede neural, dependendo da arquitetura da rede. Em camadas de saída, a função de ativação geralmente é ajustada de acordo com o tipo de problema (por exemplo, softmax para classificação multiclasse).
Flexibilidade na Arquitetura: Você pode adicionar várias camadas Dense em sequência para construir arquiteturas de redes neurais mais complexas. Isso permite que você projete redes com várias camadas e tamanhos variados de unidades.
Aqui está um exemplo de como criar uma camada Dense em uma rede neural usando Keras:
from tensorflow.keras.layers import Dense
# Cria uma camada Dense com 64 neurônios e função de ativação ReLU
dense_layer = Dense(units=64, activation='relu', input_shape=(input_dim,))
Neste exemplo, estamos criando uma camada Dense com 64 neurônios e função de ativação ReLU.
A entrada da camada é especificada pela dimensão input_dim.
Essa camada pode ser adicionada a um modelo sequencial ou funcional para construir a arquitetura da rede.
A camada Dense é uma parte fundamental na construção de redes neurais e é usada em muitos tipos de arquiteturas, como redes feedforward, redes convolucionais e redes recorrentes.
Ela desempenha um papel crucial no aprendizado e na representação de dados em redes neurais.
A função de ativação ReLU (Rectified Linear Unit) é uma das funções de ativação mais amplamente utilizadas em redes neurais artificiais.
A ReLU é uma função não linear que introduz a não linearidade em uma rede neural, o que a torna capaz de aprender relações complexas nos dados.
Aqui estão algumas características importantes da função de ativação ReLU:
A função de ativação ReLU é uma escolha popular em redes neurais devido à sua simplicidade, eficiência computacional e capacidade de mitigar problemas de gradiente. No entanto, ao usá-la, é importante estar ciente das suas limitações e considerar variações da ReLU, dependendo do problema e das necessidades do modelo.
A função de ativação Softmax é usada comumente em problemas de classificação multiclasse em redes neurais.
Ela converte um vetor de números reais em uma distribuição de probabilidade, onde a soma de todas as saídas é igual a 1.
A função Softmax é fundamental para atribuir probabilidades a cada classe em um problema de classificação, tornando-a uma escolha popular para a camada de saída em muitas arquiteturas de redes neurais.
Aqui estão alguns pontos-chave sobre a função Softmax:
A função Softmax desempenha um papel fundamental no aprendizado supervisionado, onde a classificação de dados em várias classes é uma tarefa comum. Ela converte as saídas do modelo em probabilidades que podem ser usadas para tomar decisões sobre a classe prevista. A normalização garantida pela Softmax facilita a interpretação das previsões e a compreensão da confiança do modelo nas suas escolhas.
Flatten é uma camada especial que é frequentemente usada em CNNs para transformar tensores multidimensionais em um vetor unidimensional. Essa transformação é útil quando você deseja conectar camadas totalmente conectadas (como camadas Dense) a camadas de convolução em sua arquitetura.
Aqui estão os principais pontos relacionados à classe da camada Flatten:
Aqui está um exemplo de como você pode usar a camada Flatten em uma arquitetura de rede neural usando Keras:
from tensorflow.keras.layers import Flatten
modelo = Sequential()
modelo.add(Flatten(input_shape=(28, 28))) # Converte uma imagem 28x28 em um vetor 1D de 784 elementos
modelo.add(Dense(128, activation='relu')) # Camada Dense com 128 neuronios após a camada Flatten
Neste exemplo, a camada Flatten é usada para converter imagens 28x28 em vetores unidimensionais antes de conectar a camada densa.
A camada Flatten é uma etapa comum em redes neurais convolucionais (CNNs), onde é frequentemente usada para transformar os dados da camada de convolução em um formato que pode ser alimentado em camadas densas para a tomada de decisões finais. Ela é uma parte essencial do pré-processamento de dados em redes neurais profundas, permitindo que você trabalhe com uma variedade de formatos de dados de entrada.
from tensorflow.keras.layers import Flatten
camada_achatada = Flatten()
A camada Dropout é uma técnica de regularização usada em redes neurais para reduzir o sobreajuste (overfitting).
Age desativando aleatoriamente um subconjunto de unidades (neurônios) em cada passagem durante o treinamento.
Isso evita que as unidades se tornem muito dependentes umas das outras e ajuda a melhorar a generalização do modelo.
O hiperparâmetro-chave é a taxa de dropout, que controla a fração de unidades a serem desativadas em cada passagem.
Por exemplo, uma taxa de dropout de 0,5 significa que metade das unidades é desativada aleatoriamente em cada etapa de treinamento.
from keras.layers import Dropout
modelo.add(Dense(128, activation='relu'))
modelo.add(Dropout(0.5))
A camada BatchNormalization é usada para normalizar a ativação de cada neurônio em uma camada, tornando o treinamento da rede mais estável, sendo especialmente útil em redes profundas.
A normalização é realizada em lotes de dados de treinamento, podendo atuar como um regularizador, tornando o modelo mais robusto, ajudando a evitar problemas como gradiente desvanecente (vanishing gradient), e acelerando o treinamento.
É comum adicionar uma camada BatchNormalization após a ativação em camadas densas ou convolucionais.
from keras.layers import BatchNormalization
modelo.add(Dense(128, activation='relu'))
modelo.add(BatchNormalization())
A camada Activation não é uma camada no sentido tradicional, mas é usada para aplicar funções de ativação aos resultados de camadas anteriores.
As funções de ativação introduzem não-linearidade nas redes neurais, permitindo que elas capturem relações complexas nos dados.
Algumas funções de ativação comuns incluem ReLU (Rectified Linear Unit), Sigmoid, Tanh e Softmax, dependendo do tipo de tarefa e camada.
Por exemplo, a função ReLU é comumente usada em camadas ocultas para introduzir não-linearidade.
from keras.layers import Activation
modelo.add(Dense(128))
modelo.add(Activation('relu'))
São apresentados 4 exemplos usando as bibliotecas Keras.
São exemplos para classificar spams, imagens de cães, gatos e outras categorias e imagens de dígitos.
Este código primeiro carrega os dados de treinamento e teste do conjunto de dados Reuters.
O conjunto de dados Reuters é um conjunto de dados com aproximadamente 10 mil mensagens de notícias classificadas em 46 categorias diferentes.
O código então normaliza os dados dividindo cada caractere por 255. Em seguida, o código cria um modelo de rede neural.
Este modelo é uma rede neural simples com duas camadas ocultas, cada uma com 128 neurônios.
A camada de saída tem 10 neurônios, um para cada classe no conjunto de dados. O código então compila o modelo, definindo o otimizador, a função de perda e as métricas.
Em seguida, o código treina o modelo, alimentando os dados de treinamento.
Por fim, o código avalia o desempenho do modelo, alimentando os dados de teste.
O código imprime a acurácia do modelo, que é a porcentagem de mensagens de notícias que o modelo classificou corretamente.
import numpy as np # álgebra linear
import pandas as pd # processamento de dados, CSV
from keras.datasets import reuters # conjuntos de dados e alvos
Carregar e dividir os dados de treinamento e teste.
(dados_treino, alvos_treino), (dados_teste, alvos_teste) = reuters.load_data(num_words=10000)
Usando o backend TensorFlow.
Dê uma olhada nos dados.
Conjuntos de dados e alvos de treino.
Dados de treino:
print('*** Dados do conjunto de treino')
print(f'Dimensões: {dados_treino.shape}')
print()
print(dados_treino)
Alvos de treino:
print('*** Alvos do conjunto de treino')
print(f'Dimensões: {alvos_treino.shape}')
print()
print(alvos_treino)
Conjuntos de dados e alvos de teste.
Dados de teste:
print('*** Dados do conjunto de teste')
print(f'Dimensões: {dados_teste.shape}')
print()
print(dados_teste)
Alvos de teste:
print('*** Alvos do conjunto de teste')
print(f'Dimensões: {alvos_teste.shape}')
print()
print(alvos_teste)
Cada exemplo é uma lista de inteiros (índices de palavras).
Podemos transformá-lo novamente em palavras usando o método get_word_index(), que retorna um dicionário de palavras e seus respectivos índices.
No exemplo a seguir a variável indice_palavra é um dicionário de palavras e seus respectivos índices.
indice_palavra = reuters.get_word_index()
print("*** Tamanho dicionário de palavras/índices:", len(indice_palavra))
print(list(índice_palavra.items())[:10]) # 10 primeiros elementos
A variável indice_palavra_reversa é um dicionário de índices e as respectivas palavras, criado a partir da inversão das colunas palavra/indice dos itens de em indice_palavra para indice/palavra nos itens de indice_palavra_reversa.
indice_palavra_reversa = dict([(valor,chave) for (chave, valor) in indice_palavra.items()])
print(list(indice_palavra_reversa.items())[-10:]) # 10 primeiros elementos
Os valores numéricos utilizados em dados_treino podem assim ser usados para representar palavras, encontradas no dicionário da variável indice_palavra_reversa.
noticias_decodificadas = ' '.join([indice_palavra_reversa.get(i - 3, '?') for i in dados_treino[0]])
print(noticias_decodificadas)
O rótulo associado a um exemplo é um número inteiro entre 0 e 45 (índice de tópico).
Vamos vetorizar os dados de treinamento usando o tensor inteiro e o train_label usando a codificação one-hot.
A codificação one-hot (one-hot encoding), também chamada de codificação categórica, é um formato amplamente utilizado para dados categóricos.
def vetorizar_sequencias(sequencias, dimension=10000):
resultados = np.zeros((len(sequencias),dimension))
for i,sequencia in enumerate(sequencias):
resultados[i,sequencia] = 1
return resultados
dados_treino = vetorizar_sequencias(dados_treino)
dados_teste = vetorizar_sequencias(dados_teste)
print('*** Amostra de dados de treino:')
print(dados_treino[0])
print()
print('*** Amostra de dados de teste:')
print(dados_teste[0])
from keras.utils import to_categorical
rotulos_treino_one_hot = to_categorical(alvos_treino)
rotulos_teste_one_hot = to_categorical(alvos_teste)
print(f'uma amostra de rótulos one-hot: {rotulos_treino_one_hot[0]}')
Na construção das pilhas de camadas Dense, cada camada só pode acessar as informações da saída da camada anterior.
Se uma das camadas deixar "cair" alguma informação sobre a classificação, cada camada poderá se tornar um gargalo de informações.
É por isso que usaremos camadas maiores.
from keras import models
from keras import layers
modelo = models.Sequential()
modelo.add(layers.Dense(64, activation='relu', input_shape=(10000,)))
modelo.add(layers.Dense(64, activation='relu'))
modelo.add(layers.Dense(46, activation='softmax'))
modelo.compile(
optimizer='rmsprop',
loss='categorical_crossentropy',
metrics=['accuracy'])
Vamos dividir dados_treino em dados_treino_parciais e valores_dados.
Vamos colocar 1000 amostras de dados_treino em valores_dados.
valores_dados = dados_treino[:1000]
dados_treino_parciais = dados_treino[1000:]
valores_alvos = rotulos_treino_one_hot[:1000]
alvos_treino_parciais = rotulos_treino_one_hot[1000:]
history = modelo.fit(
dados_treino_parciais,
alvos_treino_parciais,
epochs=20,
batch_size=512,
validation_data=(valores_dados,valores_alvos))
import matplotlib.pyplot as plt
perdas = history.history['loss']
valores_perda = history.history['val_loss']
episodios = range(1,len(perdas) + 1)
plt.clf()
plt.plot(episodios, perdas, 'r', label='Perdas de treinamento')
plt.plot(episodios, valores_perda, 'b', label='Perdas de validação')
plt.title('Perda de treinamento versus perda de validação')
plt.xlabel('Episódios')
plt.ylabel('Perdas')
plt.legend()
plt.show()
Traçando a precisão do treinamento e da validação.
acc = history.history['accuracy']
val_acc = history.history['val_accuracy']
plt.clf()
plt.plot(episodios, acc, 'r', label='Acurácia do treinamento')
plt.plot(episodios, val_acc, 'b', label='Acurácia de validação')
plt.xlabel('Episódios')
plt.ylabel('Acurácia')
plt.legend()
plt.show()
Recalculando:
modelo = models.Sequential()
modelo.add(layers.Dense(64, activation='relu', input_shape=(10000,)))
modelo.add(layers.Dense(64, activation='relu'))
modelo.add(layers.Dense(46, activation='softmax'))
modelo.compile(
optimizer='rmsprop',
loss='categorical_crossentropy',
metrics=['accuracy'])
modelo.fit(dados_treino_parciais,
alvos_treino_parciais,
epochs=9,
batch_size=512,
validation_data=(valores_dados,valores_alvos))
Avalie o modelo.
resultados = modelo.evaluate(dados_teste, rotulos_teste_one_hot)
Imprima os resultados.
print(resultados)
A perda é uma medida que indica o quão bem o modelo está performando na tarefa.
Neste caso, a perda é relativamente alta, indicando que o modelo está tendo dificuldades em fazer previsões precisas.
Uma perda de aproximadamente 1 é considerada relativamente alta para muitos problemas de classificação.
Isso pode sugerir que o modelo está tendo dificuldades em se ajustar aos dados de treinamento e está cometendo erros significativos.
A acurácia (ou precisão) é uma métrica que expressa a porcentagem de classificações corretas em relação ao total de amostras no conjunto de teste.
No exemplo, a precisão é de cerca de 78%, o que significa que o modelo está classificando corretamente aproximadamente 78% das amostras no conjunto de teste.
A interpretação dos resultados depende do contexto da tarefa e das expectativas de desempenho. Neste caso, os valores sugerem que o modelo não está performando tão bem quanto em alguns outros cenários.
A perda relativamente alta pode indicar problemas com o treinamento do modelo, como sobreajuste (ajuste excessivo, overfitting) ou subajuste (underfitting), que podem ser resolvidos ajustando os hiperparâmetros do modelo.
A precisão de 78% também indica que o modelo está cometendo erros em cerca de 22% das previsões, o que pode ou não ser aceitável, dependendo da natureza da tarefa.
Portanto, seria aconselhável realizar uma análise mais aprofundada do modelo, ajustar hiperparâmetros, explorar diferentes arquiteturas de rede ou realizar outras ações para melhorar o desempenho, caso seja necessário.
Modelos com camadas intermediárias com menos de 46 unidades ocultas.
modelo = models.Sequential()
modelo.add(layers.Dense(64, activation='relu', input_shape=(10000,)))
modelo.add(layers.Dense(4, activation='relu'))
modelo.add(layers.Dense(46, activation='softmax'))
modelo.compile(optimizer='rmsprop',
loss='categorical_crossentropy',
metrics=['accuracy'])
modelo.fit(dados_treino_parciais,
alvos_treino_parciais,
epochs=20,
batch_size=128,
validation_data=(valores_dados, valores_alvos))
resultados = modelo.evaluate(dados_teste, rotulos_teste_one_hot)
Imprima os resultados.
print(resultados)
Utilizando mais neurônios que o necessário em uma camada Dense.
modelo = models.Sequential()
modelo.add(layers.Dense(64, activation='relu', input_shape=(10000,)))
modelo.add(layers.Dense(128, activation='relu'))
modelo.add(layers.Dense(46, activation='softmax'))
modelo.compile(
optimizer='rmsprop',
loss='categorical_crossentropy',
metrics=['accuracy'])
modelo.fit(
dados_treino_parciais,
alvos_treino_parciais,
epochs=20,
batch_size=128,
validation_data=(valores_dados, valores_alvos))
resultados = modelo.evaluate(dados_teste, rotulos_teste_one_hot)
print(resultados)
Com apenas duas camadas Dense.
modelo = models.Sequential()
modelo.add(layers.Dense(64, activation='relu', input_shape=(10000,)))
modelo.add(layers.Dense(46, activation='softmax'))
modelo.compile(
optimizer='rmsprop',
loss='categorical_crossentropy',
metrics=['accuracy'])
modelo.fit(
dados_treino_parciais,
alvos_treino_parciais,
epochs=20,
batch_size=128,
validation_data=(valores_dados, valores_alvos))
resultados = modelo.evaluate(dados_teste, rotulos_teste_one_hot)
Imprima os resultados:
print(resultados)
Este código primeiro carrega os dados de treinamento e teste do conjunto de dados CIFAR-10, de imagens de 10 classes diferentes: pássaros, gatos, cães, sapos, cavalos, cervos, carros, aviões, navios e caminhões.
Em seguida, o código cria um modelo de rede neural.
Este modelo é uma rede neural simples com duas camadas ocultas, cada uma com 128 neurônios.
A camada de saída tem 10 neurônios, um para cada classe no conjunto de dados.
O código então compila o modelo, definindo o otimizador, a função de perda e as métricas.
Em seguida, o código treina o modelo, alimentando os dados de treinamento.
Por fim, o código avalia o desempenho do modelo, alimentando os dados de teste.
O código então normaliza os dados dividindo cada pixel por 255. Isto é feito para que todos os dados estejam na mesma escala.
O código imprime a acurácia do modelo, que é a porcentagem de imagens que o modelo classificou corretamente.
import keras
from keras.models import Sequential
from keras.layers import Dense, Flatten
from keras.datasets import cifar10
Carrege os dados de treinamento e teste.
(dados_treino, alvos_treino), (dados_teste, alvos_teste) = cifar10.load_data()
Visualize os dados.
Primeiro, os conjuntos de dados e alvos de treino.
Dados de treino:
print('*** Dados do conjunto de treino')
print(f'Tipo: {type(dados_treino)}')
print(f'Dims: {dados_treino.shape}')
print()
print(dados_treino)
*** Dados do conjunto de treino
Tipo: <class 'numpy.ndarray'>
Dims: (50000, 32, 32, 3)
[[[[ 59 62 63] [ 43 46 45] [ 50 48 43]
...
[158 132 108] [152 125 102] [148 124 103]]
[[ 16 20 20] [ 0 0 0] [ 18 8 0]
...
[123 88 55] [119 83 50] [122 87 57]]
[[ 25 24 21] [ 16 7 0] [ 49 27 8]
...
[118 84 50] [120 84 50] [109 73 42]]
...
[[208 170 96] [201 153 34] [198 161 26]
...
[160 133 70] [ 56 31 7] [ 53 34 20]]
[[180 139 96] [173 123 42] [186 144 30]
...
[184 148 94] [ 97 62 34] [ 83 53 34]]
[[177 144 116] [168 129 94] [179 142 87]
...
[216 184 140] [151 118 84] [123 92 72]]]
[[[154 177 187] [126 137 136] [105 104 95]
...
[ 91 95 71] [ 87 90 71] [ 79 81 70]]
[[140 160 169] [145 153 154] [125 125 118]
...
[ 96 99 78] [ 77 80 62] [ 71 73 61]]
[[140 155 164] [139 146 149] [115 115 112]
...
[ 79 82 64] [ 68 70 55] [ 67 69 55]]
...
[[175 167 166] [156 154 160] [154 160 170]
...
[ 42 34 36] [ 61 53 57] [ 93 83 91]]
[[165 154 128] [156 152 130] [159 161 142]
...
[103 93 96] [123 114 120] [131 121 131]]
[[163 148 120] [158 148 122] [163 156 133]
...
[143 133 139] [143 134 142] [143 133 144]]]
[[[255 255 255] [253 253 253] [253 253 253]
...
[253 253 253] [253 253 253] [253 253 253]]
[[255 255 255] [255 255 255] [255 255 255]
...
[255 255 255] [255 255 255] [255 255 255]]
[[255 255 255] [254 254 254] [254 254 254]
...
[254 254 254] [254 254 254] [254 254 254]]
...
[[113 120 112] [111 118 111] [105 112 106]
...
[ 72 81 80] [ 72 80 79] [ 72 80 79]]
[[111 118 110] [104 111 104] [ 99 106 98]
...
[ 68 75 73] [ 70 76 75] [ 78 84 82]]
[[106 113 105] [ 99 106 98] [ 95 102 94]
...
[ 78 85 83] [ 79 85 83] [ 80 86 84]]]
...
[[[ 35 178 235] [ 40 176 239] [ 42 176 241]
...
[ 99 177 219] [ 79 147 197] [ 89 148 189]]
[[ 57 182 234] [ 44 184 250] [ 50 183 240]
...
[156 182 200] [141 177 206] [116 149 175]]
[[ 98 197 237] [ 64 189 252] [ 69 192 245]
...
[188 195 206] [119 135 147] [ 61 79 90]]
...
[[ 73 79 77] [ 53 63 68] [ 54 68 80]
...
[ 17 40 64] [ 21 36 51] [ 33 48 49]]
[[ 61 68 75] [ 55 70 86] [ 57 79 103]
...
[ 24 48 72] [ 17 35 53] [ 7 23 32]]
[[ 44 56 73] [ 46 66 88] [ 49 77 105]
...
[ 27 52 77] [ 21 43 66] [ 12 31 50]]]
[[[189 211 240] [186 208 236] [185 207 235]
...
[175 195 224] [172 194 222] [169 194 220]]
[[194 210 239] [191 207 236] [190 206 235]
...
[173 192 220] [171 191 218] [167 190 216]]
[[208 219 244] [205 216 240] [204 215 239]
...
[175 191 217] [172 190 216] [169 191 215]]
...
[[207 199 181] [203 195 175] [203 196 173]
...
[135 132 127] [162 158 150] [168 163 151]]
[[198 190 170] [189 181 159] [180 172 147]
...
[178 171 160] [175 169 156] [175 169 154]]
[[198 189 173] [189 181 162] [178 170 149]
...
[195 184 169] [196 189 171] [195 190 171]]]
[[[229 229 239] [236 237 247] [234 236 247]
...
[217 219 233] [221 223 234] [222 223 233]]
[[222 221 229] [239 239 249] [233 234 246]
...
[223 223 236] [227 228 238] [210 211 220]]
[[213 206 211] [234 232 239] [231 233 244]
...
[220 220 232] [220 219 232] [202 203 215]]
...
[[150 143 135] [140 135 127] [132 127 120]
...
[224 222 218] [230 228 225] [241 241 238]]
[[137 132 126] [130 127 120] [125 121 115]
...
[181 180 178] [202 201 198] [212 211 207]]
[[122 119 114] [118 116 110] [120 116 111]
...
[179 177 173] [164 164 162] [163 163 161]]]]
Imprima apenas o primeiro elemento do conjunto de dados, para formar ideia do formato das imagens.
primeiro_dado_treino = dados_treino[0]
print(f'Tipo: {type(primeiro_dado_treino)}')
print(f'Dims: {primeiro_dado_treino.shape}')
print()
print(primeiro_dado_treino)
Tipo: <class 'numpy.ndarray'>
Dims: (32, 32, 3)
[[[ 59 62 63] [ 43 46 45] [ 50 48 43]
...
[158 132 108] [152 125 102] [148 124 103]]
[[ 16 20 20] [ 0 0 0] [ 18 8 0]
...
[123 88 55] [119 83 50] [122 87 57]]
[[ 25 24 21] [ 16 7 0] [ 49 27 8]
...
[118 84 50] [120 84 50] [109 73 42]]
...
[[208 170 96] [201 153 34] [198 161 26]
...
[160 133 70] [ 56 31 7] [ 53 34 20]]
[[180 139 96] [173 123 42] [186 144 30]
...
[184 148 94] [ 97 62 34] [ 83 53 34]]
[[177 144 116] [168 129 94] [179 142 87]
...
[216 184 140] [151 118 84] [123 92 72]]]
Alvos de treino:
Os alvos são um conjunto de números inteiros entre 0 e 9.
print('*** Alvos do conjunto de treino')
print(f'Tipo: {type(alvos_treino)}')
print(f'Dims: {alvos_treino.shape}')
print()
print(alvos_treino)
*** Alvos do conjunto de treino
Tipo: <class 'numpy.ndarray'>
Dims: (50000, 1)
[[6] [9] [9] ... [9] [1] [1]]
Imprima apenas o primeiro elemento do conjunto de alvos, para formar ideia do formato das etiquetas.
primeiro_alvo_treino = alvos_treino[0]
print(f'Tipo: {type(primeiro_alvo_treino)}')
print(f'Dims: {primeiro_alvo_treino.shape}')
print()
print(primeiro_alvo_treino)
*** Alvos do conjunto de treino
Tipo: <class 'numpy.ndarray'>
Dims: (1, )
[6]
O conjunto de dados CIFAR-10 do Keras não fornece métodos integrados para recuperar diretamente os rótulos originais das classes em formato de texto.
No entanto, você pode facilmente criar uma lista de textos com os rótulos das classes com base no conhecimento das classes do conjunto de dados.
Os rótulos das classes no CIFAR-10 são as seguintes:
Você pode criar manualmente uma lista de strings correspondente a essas classes da seguinte maneira:
rotulos_classes = [
"Avião", "Automóvel", "Pássaro", "Gato", "Cervo",
"Cachorro", "Sapo", "Cavalo", "Navio", "Caminhão"
]
Agora, você pode usar a lista rotulos_classes para mapear os rótulos das classes em formato numérico para os rótulos originais em formato de texto.
Por exemplo, rotulos_classes[0] retorna "Avião", rotulos_classes[1] retorna "Automóvel" e assim por diante.
Esta abordagem é simples e eficaz para associar rótulos originais às classes no conjunto de dados CIFAR-10.
Os conjuntos de dados e alvos de teste são semelhantes aos conjuntos de dados e alvos de treino, com valores diferentes.
Crie o modelo Sequential e adicione camadas Flatten, Dense (ReLU) e Dense (Softmax).
modelo = Sequential()
modelo.add(Flatten(input_shape=(32, 32, 3)))
modelo.add(Dense(128, activation='relu'))
modelo.add(Dense(10, activation='softmax'))
Compile o modelo.
modelo.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics=['accuracy'])
Treine o modelo.
modelo.fit(dados_treino, alvos_treino, epochs=10)
Epoch 1/10
1563/1563 [==============================] - 10s 6ms/step - loss: 9.4726 - accuracy: 0.1012
Epoch 2/10
1563/1563 [==============================] - 9s 6ms/step - loss: 2.3027 - accuracy: 0.0992
Epoch 3/10
1563/1563 [==============================] - 9s 6ms/step - loss: 2.3026 - accuracy: 0.0987
Epoch 4/10
1563/1563 [==============================] - 10s 6ms/step - loss: 2.3027 - accuracy: 0.0994
Epoch 5/10
1563/1563 [==============================] - 9s 6ms/step - loss: 2.3028 - accuracy: 0.0979
Epoch 6/10
1563/1563 [==============================] - 9s 6ms/step - loss: 2.3027 - accuracy: 0.0981
Epoch 7/10
1563/1563 [==============================] - 9s 6ms/step - loss: 2.3028 - accuracy: 0.0993
Epoch 8/10
1563/1563 [==============================] - 10s 6ms/step - loss: 2.3028 - accuracy: 0.0990
Epoch 9/10
1563/1563 [==============================] - 10s 6ms/step - loss: 2.3028 - accuracy: 0.0999
Epoch 10/10
1563/1563 [==============================] - 10s 6ms/step - loss: 2.3028 - accuracy: 0.0975
Durante o treinamento são apresentados os cálculos dos 10 episódios, observe o tempo de treinamento, a perda (loss) e a acurácia (accuracy).
A perda em todos os episódios é alta e a acurácia é baixa, em torno de 0.1 (10%).
Avalie o desempenho do modelo com os dados de teste.
escore = modelo.evaluate(dados_teste, alvos_teste)
Imprima a acurácia.
print(f'Acurácia: {escore[1]:.4f}')
print(f'Perda: {escore[0]:.4f}')
Acurácia: 0.1000
Perda: 2.3027
A acurância do conjunto de teste é baixa, em torno de 0.1 (10%), a taxa de perda alta, pelo que o modelo está sobreajustado ou subajustado.
Vamos normalizar os dados de entrada para que os dados de entrada sejam valores entre 0 e 1, visando que a rede neural aprenda melhor.
Normalize os dados.
Como os valores de pixels estão entre 0 e 255, dividir os valores de entrada por 255 normalizará os dados de entrada para valores entre 0 (0/255) e 1 (255/255).
dados_treino = dados_treino / 255.0
dados_teste = dados_teste / 255.0
Imprima apenas o primeiro elemento do conjunto de dados de treino normalizado, para formar ideia do formato normalizado das imagens.
print('*** Dados do conjunto de treino')
print(dados_treino[0])
*** Dados do conjunto de treino normalizado
[[[0.23 0.24 0.24] [0.16 0.18 0.17] [0.19 0.18 0.16]
...
[0.61 0.51 0.42] [0.59 0.49 0.4 ] [0.58 0.48 0.40]]
[[0.06 0.07 0.07] [0. 0. 0. ] [0.07 0.03 0. ]
...
[0.48 0.34 0.21] [0.46 0.32 0.19] [0.47 0.34 0.22]]
[[0.09 0.09 0.08] [0.06 0.02 0. ] [0.19 0.10 0.03]
...
[0.46 0.32 0.19] [0.47 0.32 0.19] [0.42 0.28 0.16]]
...
[[0.81 0.66 0.37] [0.78 0.6 0.13] [0.77 0.63 0.10]
...
[0.62 0.52 0.27] [0.21 0.12 0.02] [0.20 0.13 0.07]]
[[0.70 0.54 0.37] [0.67 0.48 0.16] [0.72 0.56 0.11]
...
[0.72 0.58 0.36] [0.38 0.24 0.13] [0.32 0.20 0.13]]
[[0.69 0.56 0.45] [0.65 0.50 0.36] [0.70 0.55 0.34]
...
[0.84 0.72 0.54] [0.59 0.46 0.32] [0.48 0.36 0.28]]]
Crie o modelo.
modelo = Sequential()
modelo.add(Flatten(input_shape=(32, 32, 3)))
modelo.add(Dense(128, activation='relu'))
modelo.add(Dense(10, activation='softmax'))
Compile o modelo.
modelo.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics=['accuracy'])
Treine o modelo.
modelo.fit(dados_treino, alvos_treino, epochs=10)
Epoch 1/10
1563/1563 [==============================] - 11s 6ms/step - loss: 1.8943 - accuracy: 0.3264
Epoch 2/10
1563/1563 [==============================] - 9s 6ms/step - loss: 1.7427 - accuracy: 0.3784
Epoch 3/10
1563/1563 [==============================] - 10s 6ms/step - loss: 1.6873 - accuracy: 0.3981
Epoch 4/10
1563/1563 [==============================] - 9s 6ms/step - loss: 1.6514 - accuracy: 0.4114
Epoch 5/10
1563/1563 [==============================] - 9s 6ms/step - loss: 1.6278 - accuracy: 0.4225
Epoch 6/10
1563/1563 [==============================] - 9s 6ms/step - loss: 1.6050 - accuracy: 0.4291
Epoch 7/10
1563/1563 [==============================] - 9s 6ms/step - loss: 1.5846 - accuracy: 0.4364
Epoch 8/10
1563/1563 [==============================] - 9s 6ms/step - loss: 1.5734 - accuracy: 0.4404
Epoch 9/10
1563/1563 [==============================] - 9s 6ms/step - loss: 1.5594 - accuracy: 0.4443
Epoch 10/10
1563/1563 [==============================] - 10s 6ms/step - loss: 1.5526 - accuracy: 0.4467
Observe o desempenho do modelo. Durante o treinamento são apresentados os cálculos dos 10 episódios, observe os valores de perda (loss) e de acurácia (accuracy).
Embora os valores de acurácia e de perda tenham sido significativamente melhores com valores normalizados, em relação aos valores originais, a perda em todos os episódios é alta e a acurácia é baixa, em torno de 0.44 (44%).
Isso significa que o modelo consegue acertar apenas 44% das imagens, menos da metade.
Avalie o desempenho do modelo com os dados de teste.
escore = modelo.evaluate(dados_teste, alvos_teste)
Imprima a acurácia.
print(f'Acurácia: {escore[1]:.4f}')
print(f'Perda: {escore[0]:.4f}')
Acurácia: 0.4307
Perda: 1.6005
A acurância do conjunto de teste normalizado é melhor que a acurácia do conjunto de teste original, sem normalização, em torno de 0.43 (43%), e a perda alta, demonstrando que o modelo está sobre/subajustado.
Este é apenas um exemplo simples de código.
Para obter melhores resultados, você precisará usar um conjunto de dados maior e mais diversificado, treinar seu modelo por mais épocas e usar uma arquitetura de rede neural mais complexa.
No artigo acadêmico An Image is Worth 16x16 Words: Transformers for Image Recognition at Scale, os autores mencionam que os Tranformadores de Visão (ViT - Vision Transformers) são ávidos por dados.
Portanto, pré-treinar um ViT em um conjunto de dados de grande porte como JFT300M e ajustá-lo em conjuntos de dados de tamanho médio (como ImageNet) é a única maneira de superar os modelos de rede neural convolucional de última geração.
JFT-300M é um conjunto de dados interno do Google usado para treinar modelos de classificação de imagens, rotuladas usando-se um algoritmo com uma mistura complexa de sinais brutos da web, conexões entre páginas da web e feedback do usuário, resultando em mais de um bilhão de rótulos para 300 milhões de imagens (uma única imagem pode ter vários rótulos). Dos bilhões de rótulos de imagens, aproximadamente 375 milhões são selecionados por meio de um algoritmo que visa maximizar a precisão dos rótulos das imagens selecionadas.
A camada de autoatenção do ViT carece de viés indutivo de localidade (a noção de que os pixels da imagem são correlacionados localmente e que seus mapas de correlação são invariantes à tradução). Esta é a razão pela qual os ViTs precisam de mais dados. Por outro lado, as CNNs analisam as imagens através de janelas deslizantes espaciais, o que as ajuda a obter melhores resultados com conjuntos de dados menores.
As camadas de autoatenção (self-attention), são componentes fundamentais em arquiteturas de modelos como o Transformador (Transformer) e também são usadas nas Redes Neurais Convolucionais (CNNs) de ViT, desempenhando um papel crucial na extração de informações contextuais dos dados e permitindo que o modelo se concentre em partes relevantes da entrada durante o processamento.
Em um contexto de ViT, essas camadas de autoatenção são particularmente interessantes porque permitem que o modelo lide com imagens de forma eficaz, capturando as relações entre pixels ou correções (patches) de maneira eficaz e permitindo que o modelo aprenda a atenção em patchs de maneira semelhante à sua aplicação em dados sequenciais, como texto. Isso permite que as ViTs capturem relações espaciais e semânticas em imagens.
As camadas de autoatenção funcionam da seguinte forma:
No artigo acadêmico Vision Transformer for Small-Size Datasets, os autores se propuseram a resolver o problema do viés indutivo de localidade em ViTs.
As ideias principais são:
Este exemplo implementa as ideias do artigo. Grande parte deste exemplo é inspirada na classificação de imagens com ViT.
Observação: Este exemplo requer o TensorFlow 2.6 ou superior, bem como os complementos do TensorFlow, que podem ser instalados usando o seguinte comando:
pip install -qq -U tensorflow-addons
Importe as bibliotecas necessárias.
import math
import numpy as np
import tensorflow as tf
from tensorflow import keras
import tensorflow_addons as tfa
import matplotlib.pyplot as plt
from tensorflow.keras import layers
Configure a semente para reproduzibilidade.
SEED = 42
keras.utils.set_random_seed(SEED)
Prepare os dados com o número de classes e dimensões de entrada.
NUM_CLASSES = 100
DIMENSOES_ENTRADA = (32, 32, 3)
Carregue os conjuntos de dados e alvos de treino e teste, usando o dataset CIFAR-100.
(dados_treino, alvos_treino), (dados_teste, alvos_teste) = keras.datasets.cifar100.load_data()
Imprima as dimensões dos dados.
print(f"dados_treino shape: {dados_treino.shape} - alvos_treino shape: {alvos_treino.shape}")
print(f"dados_teste shape: {dados_teste.shape} - alvos_teste shape: {alvos_teste.shape}")
Os hiperparâmetros cumprimem diferentes papéis, sinta-se à vontade para ajustar os hiperparâmetros.
# Dados
TAM_BUFFER = 512
TAM_LOTE = 256
# Aumento de dados
TAM_IMAGEM = 72
TAM_CORRECAO = 6
NUM_CORRECOES = (TAM_IMAGEM // TAM_CORRECAO) ** 2
# Otimizador
TAXA_APRENDIZADO = 0.001
PESO_DECAIMENTO = 0.0001
# Treinamento
EPISODIOS = 50
# Arquitetura
NORMA_CAMADA_EPS = 1e-6
CAMADAS_TRANFORMACAO = 8
DIM_PROJECAO = 64
NUM_CABECAS = 4
UNIDADES_TRANSFORMADORAS = [
DIM_PROJECAO * 2,
DIM_PROJECAO,
]
UNIDADES_CABECA_MLP = [2048, 1024]
Um trecho do jornal:
"De acordo com o DeiT, várias técnicas são necessárias para treinar ViTs com eficácia.
Assim, aplicamos aumentos de dados como CutMix, Mixup, Auto Augment, Repeated Augment a todos os modelos."
Neste exemplo, focaremos apenas na novidade da abordagem e não na reprodução dos resultados do artigo.
Por esta razão, não utilizamos os esquemas de aumento de dados mencionados.
Sinta-se à vontade para adicionar ou remover o pipeline de aumento.
aumento_dados = keras.Sequential(
[
layers.Normalization(),
layers.Resizing(TAM_IMAGEM, TAM_IMAGEM),
layers.RandomFlip("horizontal"),
layers.RandomRotation(factor=0.02),
layers.RandomZoom(height_factor=0.2, width_factor=0.2),
],
name="data_augmentation",
)
# Calcule a média e a variância dos dados de treinamento para normalização.
aumento_dados.layers[0].adapt(dados_treino)
Em um pipeline ViT, as imagens de entrada são divididas em patchs que são então projetados linearmente em tokens.
A tokenização de correção por deslocamento (SPT - Shift Patch Tokenization) é introduzida para combater o baixo campo receptivo dos ViTs.
As etapas para SPT são as seguintes:
class TokenizacaoCorrecaoDeslocamento(layers.Layer):
def __init__(
self,
tamanho_imagem = TAM_IMAGEM,
tamanho_correcao = TAM_CORRECAO,
num_correcoes = NUM_CORRECOES,
dim_projecao = DIM_PROJECAO,
vanilla = False,
**kwargs,
):
super().__init__(**kwargs)
self.vanilla = vanilla # Sinalizador para mudar para extrator de correção Vanilla.
self.tamanho_imagem = tamanho_imagem
self.tamanho_correcao = tamanho_correcao
self.half_patch = tamanho_correcao // 2
self.correcoes_achatadas = layers.Reshape((num_correcoes, -1))
self.projecao = layers.Dense(units=dim_projecao)
self.normas_camada = layers.LayerNormalization(epsilon=NORMA_CAMADA_EPS)
# Amortecedor de corte por deslocamento
def crop_shift_pad(self, imagens, mode):
# Construir as imagens diagonais deslocadas
if mode == "left-up":
altura_corte = self.half_patch
largura_corte = self.half_patch
altura_deslocamento = 0
largura_deslocamento = 0
elif mode == "left-down":
altura_corte = 0
largura_corte = self.half_patch
altura_deslocamento = self.half_patch
largura_deslocamento = 0
elif mode == "right-up":
altura_corte = self.half_patch
largura_corte = 0
altura_deslocamento = 0
largura_deslocamento = self.half_patch
else:
altura_corte = 0
largura_corte = 0
altura_deslocamento = self.half_patch
largura_deslocamento = self.half_patch
# Corte as imagens deslocadas e preencha-as
corte = tf.image.crop_to_bounding_box(
imagens,
offset_height = altura_corte,
offset_width = largura_corte,
target_height = self.tamanho_imagem - self.half_patch,
target_width = self.tamanho_imagem - self.half_patch,
)
amortecedor_deslocamento = tf.image.pad_to_bounding_box(
corte,
offset_height = altura_deslocamento,
offset_width = largura_deslocamento,
target_height = self.tamanho_imagem,
target_width = self.tamanho_imagem,
)
return amortecedor_deslocamento
def call(self, imagens):
if not self.vanilla:
# Concatenar as imagens deslocadas com a imagem original
imagens = tf.concat(
[
imagens,
self.crop_shift_pad(imagens, mode="left-up"),
self.crop_shift_pad(imagens, mode="left-down"),
self.crop_shift_pad(imagens, mode="right-up"),
self.crop_shift_pad(imagens, mode="right-down"),
],
axis=-1,
)
# Corrija as correcoes achatando e projetando as imagens
correcoes = tf.image.extract_patches(
images = imagens,
sizes = [1, self.tamanho_correcao, self.tamanho_correcao, 1],
strides = [1, self.tamanho_correcao, self.tamanho_correcao, 1],
rates = [1, 1, 1, 1],
padding = "VALID",
)
correcoes_planas = self.correcoes_achatadas(correcoes)
if not self.vanilla:
# Normalize a camada dos <i>patchs</i> planos e projete-as linearmente
tokens = self.normas_camada(correcoes_planas)
tokens = self.projecao(tokens)
else:
# Projete linearmente os <i>patchs</i> planos
tokens = self.projecao(correcoes_planas)
return (tokens, correcoes)
O gráfico a seguir mostra as correções projetadas.
# Obtenha uma imagem aleatória do conjunto de dados de treinamento e redimensione a imagem
imagem = dados_treino[np.random.choice(range(dados_treino.shape[0]))]
imagem_redimensionada = tf.image.resize(
tf.convert_to_tensor([imagem]), size=(TAM_IMAGEM, TAM_IMAGEM)
)
# Corretor Vanilla (<i>Vanilla Patch Maker</i>): Isso pega uma imagem e a divide em correcoes como no artigo ViT original
(token, correcao) = TokenizacaoCorrecaoDeslocamento(vanilla=True)(imagem_redimensionada / 255.0)
(token, correcao) = (token[0], correcao[0])
# Desloque a imagem em direções diagonais.
n = correcao.shape[0]
contagem = 1
plt.figure(figsize=(4, 4))
for linha in range(n):
for coluna in range(n):
plt.subplot(n, n, contagem)
contagem = contagem + 1
imagem = tf.reshape(correcao[linha][coluna], (TAM_CORRECAO, TAM_CORRECAO, 3))
plt.imshow(imagem)
plt.axis("off")
plt.show()
Esta camada pega a imagem e a desloca diagonalmente e depois extrai correções das imagens concatenadas.
(token, correcao) = TokenizacaoCorrecaoDeslocamento(vanilla=False)(imagem_redimensionada / 255.0)
(token, correcao) = (token[0], correcao[0])
n = correcao.shape[0]
imagens_deslocadas = ["ORIGINAL", "ESQUERDA-SUPERIOR", "ESQUERDA-INFERIOR", "DIREITA_SUPERIOR", "DIREITA-INFERIOR"]
for indice, nome in enumerate(imagens_deslocadas):
print(nome)
contagem = 1
plt.figure(figsize=(4, 4))
for linha in range(n):
for coluna in range(n):
plt.subplot(n, n, contagem)
contagem = contagem + 1
imagem = tf.reshape(correcao[linha][coluna], (TAM_CORRECAO, TAM_CORRECAO, 5 * 3))
plt.imshow(imagem[..., 3 * indice : 3 * indice + 3])
plt.axis("off")
plt.show()
ORIGINAL
Esta camada aceita correções projetadas e então adiciona informações posicionais a eles.
class CodificadorCorrecao(layers.Layer):
def __init__(
self, num_correcoes=NUM_CORRECOES, dim_projecao=DIM_PROJECAO, **kwargs
):
super().__init__(**kwargs)
self.num_correcoes = num_correcoes
self.incorporacao_posicao = layers.Embedding(
input_dim=num_correcoes, output_dim=dim_projecao
)
self.posicoes = tf.range(start=0, limit=self.num_correcoes, delta=1)
def call(self, correcoes_codificadas):
posicoes_codificadas = self.incorporacao_posicao(self.posicoes)
correcoes_codificadas = correcoes_codificadas + posicoes_codificadas
return correcoes_codificadas
A equação de atenção regular é indicada abaixo.
atenção(Q,K,V) = softmax(QK^T/sqrt(dk))V
Na autoatenção, consulta, chave e valor vêm da mesma entrada.
O produto escalar resultaria em grandes relações de auto-token, em vez de relações entre tokens.
Isso também significa que o softmax oferece probabilidades mais altas às relações auto-token do que às relações inter-token.
Para combater isso, os autores propõem mascarar a diagonal do produto escalar. Dessa forma, forçamos o módulo de atenção a prestar mais atenção às relações entre tokens.
O fator de escala é uma constante no módulo de atenção regular.
Isto atua como um 'termo de temperatura' que pode modular a função softmax.
Os autores sugerem um termo de temperatura que pode ser aprendido em vez de uma constante.
As duas dicas acima tornam-se a Autoatenção da Localidade.
Subclassificamos layers.MultiHeadAttention e implementamos a temperatura treinável.
A máscara de atenção é construída posteriormente.
class AtencaoMultiCabecaLSA(tf.keras.layers.MultiHeadAttention):
def __init__(self, **kwargs):
super().__init__(**kwargs)
# O termo de temperatura treinável. O valor inicial
# é a raiz quadrada da dimensão principal.
self.tau = tf.Variable(math.sqrt(float(self._key_dim)), trainable=True)
def _compute_attention(self, consulta, chave, valor, attention_mask=None, training=None):
consulta = tf.multiply(consulta, 1.0 / self.tau)
escores_atencao = tf.einsum(self._dot_product_equation, chave, consulta)
escores_atencao = self._masked_softmax(escores_atencao, attention_mask)
abandono_escores_atencao = self._dropout_layer(
escores_atencao, training=training
)
saida_atencao = tf.einsum(
self._combine_equation, abandono_escores_atencao, valor
)
return saida_atencao, escores_atencao
Implemente o Perceptron Multi-Camadas (MLP - Multi-Layer Perceptron).
def mlp(x, unidades_escondidas, taxa_abandono):
for unidades in unidades_escondidas:
x = layers.Dense(unidades, activation=tf.nn.gelu)(x)
x = layers.Dropout(taxa_abandono)(x)
return x
# Construir a máscara de atenção diagonal
mascara_atencao_diagonal = 1 - tf.eye(NUM_CORRECOES)
mascara_atencao_diagonal = tf.cast([mascara_atencao_diagonal], dtype=tf.int8)
def criar_classificador_vit(vanilla=False):
entradas = layers.Input(shape=DIMENSOES_ENTRADA)
# Dados aumentados
aumentado = aumento_dados(entradas)
# Criar patches.
(tokens, _) = TokenizacaoCorrecaoDeslocamento(vanilla=vanilla)(aumentado)
# Codificar correções.
correcoes_codificadas = CodificadorCorrecao()(tokens)
# Crie múltiplas camadas do bloco Transformer.
for _ in range(CAMADAS_TRANFORMACAO):
# Normalização de camada 1
x1 = layers.LayerNormalization(epsilon=1e-6)(correcoes_codificadas)
# Crie uma camada de atenção com várias cabeças.
if not vanilla:
saida_atencao = AtencaoMultiCabecaLSA(
num_heads=NUM_CABECAS, key_dim=DIM_PROJECAO, dropout=0.1)(
x1, x1, attention_mask=mascara_atencao_diagonal)
else:
saida_atencao = layers.MultiHeadAttention(
num_heads=NUM_CABECAS, key_dim=DIM_PROJECAO, dropout=0.1)(
x1, x1)
# Pular conexão 1
x2 = layers.Add()([saida_atencao, correcoes_codificadas])
# Normalização de camada 2
x3 = layers.LayerNormalization(epsilon=1e-6)(x2)
# MLP - Multi-Layer Perceptron (Perceptron Multi-Camada)
x3 = mlp(x3, unidades_escondidas=UNIDADES_TRANSFORMADORAS, taxa_abandono=0.1)
# Pular conexão 2
correcoes_codificadas = layers.Add()([x3, x2])
# Crie um tensor [tamanho do lote, dimensão de projeção].
representacao = layers.LayerNormalization(epsilon=1e-6)(correcoes_codificadas)
representacao = layers.Flatten()(representacao)
representacao = layers.Dropout(0.5)(representacao)
# Adicionar MLP
caracteristicas = mlp(representacao, unidades_escondidas=UNIDADES_CABECA_MLP, taxa_abandono=0.5)
# Classificar as saídas.
saidas = layers.Dense(NUM_CLASSES)(caracteristicas)
# Criar o modelo Keras
modelo = keras.Model(entradas=entradas, outputs=saidas)
return modelo
class coseno_aquecimento(keras.optimizers.schedules.LearningRateSchedule):
def __init__(
self, taxa_base_aprendizado, total_passos, taxa_aprendizado_aquecimento, passos_aquecimento
):
super().__init__()
self.taxa_base_aprendizado = taxa_base_aprendizado
self.total_passos = total_passos
self.taxa_aprendizado_aquecimento = taxa_aprendizado_aquecimento
self.passos_aquecimento = passos_aquecimento
self.pi = tf.constant(np.pi)
def __call__(self, step):
if self.total_passos < self.passos_aquecimento:
raise ValueError("Total_steps deve ser maior ou igual a passos_aquecimento.")
cos_lr_recozido = tf.cos(
self.pi
* (tf.cast(step, tf.float32) - self.passos_aquecimento)
/ float(self.total_passos - self.passos_aquecimento)
)
taxa_aprendizado = 0.5 * self.taxa_base_aprendizado * (1 + cos_lr_recozido)
if self.passos_aquecimento > 0:
if self.taxa_base_aprendizado < self.taxa_aprendizado_aquecimento:
raise ValueError(
"taxa_base_aprendizado deve ser maior ou igual a "
"taxa_aprendizado_aquecimento."
)
slope = (self.taxa_base_aprendizado - self.taxa_aprendizado_aquecimento) / self.passos_aquecimento
taxa_aquecimento = slope * tf.cast(step, tf.float32) + self.taxa_aprendizado_aquecimento
taxa_aprendizado = tf.where(step < self.passos_aquecimento, taxa_aquecimento, taxa_aprendizado)
return tf.where(step > self.total_passos, 0.0, taxa_aprendizado, name="learning_rate")
A função executar_experimento() recebe um modelo e executa o treinamento.
def executar_experimento(modelo):
total_passos = int((len(dados_treino) / TAM_LOTE) * EPISODIOS)
percentagem_episodio_aquecimento = 0.10
passos_aquecimento = int(total_passos * percentagem_episodio_aquecimento)
scheduled_lrs = coseno_aquecimento(
learning_rate_base = TAXA_APRENDIZADO,
warmup_steps = total_passos,
warmup_learning_rate = 0.0,
warmup_steps = passos_aquecimento,
)
otimizador = tfa.optimizers.AdamW(
learning_rate = TAXA_APRENDIZADO,
weight_decay = PESO_DECAIMENTO
)
modelo.compile(
optimizer = otimizador,
loss = keras.losses.SparseCategoricalCrossentropy(from_logits=True),
metrics = [
keras.metrics.SparseCategoricalAccuracy(name="acuracia"),
keras.metrics.SparseTopKCategoricalAccuracy(5, name="acuracia-top-5"),
],
)
historia = modelo.fit(
x=dados_treino,
y=alvos_treino,
batch_size=TAM_LOTE,
epochs=EPISODIOS,
validation_split=0.1,
)
_, acuracia, acuracia_top_5 = modelo.evaluate(dados_teste, alvos_teste, batch_size=TAM_LOTE)
print(f"Acurácia do teste: {round(acuracia * 100, 2)}%")
print(f"Acurácia do teste top-5: {round(acuracia_top_5 * 100, 2)}%")
return historia
vit = criar_classificador_vit(vanilla=True)
historia = executar_experimento(vit)
Epoch 1/50
176/176 [==============================] - 22s 83ms/step - loss: 4.4912 - acuracia: 0.0427 acuracia-top-5: 0.1549 - val_loss: 3.9409 - val_acuracia: 0.1030 - val_acuracia-top-5: 0.3036
Epoch 2/50
176/176 [==============================] - 14s 77ms/step - loss: 3.9749 - acuracia: 0.0897 acuracia-top-5: 0.2802 - val_loss: 3.5721 - val_acuracia: 0.1550 - val_acuracia-top-5: 0.4058
Epoch 3/50
176/176 [==============================] - 14s 77ms/step - loss: 3.7129 - acuracia: 0.1282 acuracia-top-5: 0.3601 - val_loss: 3.3235 - val_acuracia: 0.2022 - val_acuracia-top-5: 0.4788
Epoch 4/50
176/176 [==============================] - 14s 77ms/step - loss: 3.5518 - acuracia: 0.1544 acuracia-top-5: 0.4078 - val_loss: 3.2432 - val_acuracia: 0.2132 - val_acuracia-top-5: 0.5056
Epoch 5/50
176/176 [==============================] - 14s 77ms/step - loss: 3.4098 - acuracia: 0.1828 acuracia-top-5: 0.4471 - val_loss: 3.0910 - val_acuracia: 0.2462 - val_acuracia-top-5: 0.5376
Epoch 6/50
176/176 [==============================] - 14s 77ms/step - loss: 3.2835 - acuracia: 0.2037 acuracia-top-5: 0.4838 - val_loss: 2.9803 - val_acuracia: 0.2704 - val_acuracia-top-5: 0.5606
Epoch 7/50
176/176 [==============================] - 14s 77ms/step - loss: 3.1756 - acuracia: 0.2205 acuracia-top-5: 0.5113 - val_loss: 2.8608 - val_acuracia: 0.2802 - val_acuracia-top-5: 0.5908
Epoch 8/50
176/176 [==============================] - 14s 77ms/step - loss: 3.0585 - acuracia: 0.2439 acuracia-top-5: 0.5432 - val_loss: 2.8055 - val_acuracia: 0.2960 - val_acuracia-top-5: 0.6144
Epoch 9/50
176/176 [==============================] - 14s 77ms/step - loss: 2.9457 - acuracia: 0.2654 acuracia-top-5: 0.5697 - val_loss: 2.7034 - val_acuracia: 0.3210 - val_acuracia-top-5: 0.6242
Epoch 10/50
176/176 [==============================] - 14s 77ms/step - loss: 2.8458 - acuracia: 0.2863 acuracia-top-5: 0.5918 - val_loss: 2.5899 - val_acuracia: 0.3416 - val_acuracia-top-5: 0.6500
Epoch 11/50
176/176 [==============================] - 14s 77ms/step - loss: 2.7530 - acuracia: 0.3052 acuracia-top-5: 0.6191 - val_loss: 2.5275 - val_acuracia: 0.3526 - val_acuracia-top-5: 0.6660
Epoch 12/50
176/176 [==============================] - 14s 77ms/step - loss: 2.6561 - acuracia: 0.3250 acuracia-top-5: 0.6355 - val_loss: 2.5111 - val_acuracia: 0.3544 - val_acuracia-top-5: 0.6554
Epoch 13/50
176/176 [==============================] - 14s 77ms/step - loss: 2.5833 - acuracia: 0.3398 acuracia-top-5: 0.6538 - val_loss: 2.3931 - val_acuracia: 0.3792 - val_acuracia-top-5: 0.6888
Epoch 14/50
176/176 [==============================] - 14s 77ms/step - loss: 2.4988 - acuracia: 0.3594 acuracia-top-5: 0.6724 - val_loss: 2.3695 - val_acuracia: 0.3868 - val_acuracia-top-5: 0.6958
Epoch 15/50
176/176 [==============================] - 14s 77ms/step - loss: 2.4342 - acuracia: 0.3706 acuracia-top-5: 0.6877 - val_loss: 2.3076 - val_acuracia: 0.4072 - val_acuracia-top-5: 0.7074
Epoch 16/50
176/176 [==============================] - 14s 77ms/step - loss: 2.3654 - acuracia: 0.3841 acuracia-top-5: 0.7024 - val_loss: 2.2346 - val_acuracia: 0.4202 - val_acuracia-top-5: 0.7174
Epoch 17/50
176/176 [==============================] - 14s 77ms/step - loss: 2.3062 - acuracia: 0.3967 acuracia-top-5: 0.7130 - val_loss: 2.2277 - val_acuracia: 0.4206 - val_acuracia-top-5: 0.7190
Epoch 18/50
176/176 [==============================] - 14s 77ms/step - loss: 2.2415 - acuracia: 0.4100 acuracia-top-5: 0.7271 - val_loss: 2.1605 - val_acuracia: 0.4398 - val_acuracia-top-5: 0.7366
Epoch 19/50
176/176 [==============================] - 14s 77ms/step - loss: 2.1802 - acuracia: 0.4240 acuracia-top-5: 0.7386 - val_loss: 2.1533 - val_acuracia: 0.4428 - val_acuracia-top-5: 0.7382
Epoch 20/50
176/176 [==============================] - 14s 77ms/step - loss: 2.1264 - acuracia: 0.4357 acuracia-top-5: 0.7486 - val_loss: 2.1395 - val_acuracia: 0.4428 - val_acuracia-top-5: 0.7404
Epoch 21/50
176/176 [==============================] - 14s 77ms/step - loss: 2.0856 - acuracia: 0.4442 acuracia-top-5: 0.7564 - val_loss: 2.1025 - val_acuracia: 0.4512 - val_acuracia-top-5: 0.7448
Epoch 22/50
176/176 [==============================] - 14s 77ms/step - loss: 2.0320 - acuracia: 0.4566 acuracia-top-5: 0.7668 - val_loss: 2.0677 - val_acuracia: 0.4600 - val_acuracia-top-5: 0.7534
Epoch 23/50
176/176 [==============================] - 14s 77ms/step - loss: 1.9903 - acuracia: 0.4666 acuracia-top-5: 0.7761 - val_loss: 2.0273 - val_acuracia: 0.4650 - val_acuracia-top-5: 0.7610
Epoch 24/50
176/176 [==============================] - 14s 77ms/step - loss: 1.9398 - acuracia: 0.4772 acuracia-top-5: 0.7877 - val_loss: 2.0253 - val_acuracia: 0.4694 - val_acuracia-top-5: 0.7636
Epoch 25/50
176/176 [==============================] - 14s 78ms/step - loss: 1.9027 - acuracia: 0.4865 acuracia-top-5: 0.7933 - val_loss: 2.0584 - val_acuracia: 0.4606 - val_acuracia-top-5: 0.7520
Epoch 26/50
176/176 [==============================] - 14s 77ms/step - loss: 1.8529 - acuracia: 0.4964 acuracia-top-5: 0.8010 - val_loss: 2.0128 - val_acuracia: 0.4752 - val_acuracia-top-5: 0.7654
Epoch 27/50
176/176 [==============================] - 14s 77ms/step - loss: 1.8161 - acuracia: 0.5047 acuracia-top-5: 0.8111 - val_loss: 1.9630 - val_acuracia: 0.4898 - val_acuracia-top-5: 0.7746
Epoch 28/50
176/176 [==============================] - 13s 77ms/step - loss: 1.7792 - acuracia: 0.5136 acuracia-top-5: 0.8140 - val_loss: 1.9931 - val_acuracia: 0.4780 - val_acuracia-top-5: 0.7640
Epoch 29/50
176/176 [==============================] - 14s 77ms/step - loss: 1.7268 - acuracia: 0.5211 acuracia-top-5: 0.8250 - val_loss: 1.9748 - val_acuracia: 0.4854 - val_acuracia-top-5: 0.7708
Epoch 30/50
176/176 [==============================] - 14s 77ms/step - loss: 1.7115 - acuracia: 0.5298 acuracia-top-5: 0.8265 - val_loss: 1.9669 - val_acuracia: 0.4884 - val_acuracia-top-5: 0.7796
Epoch 31/50
176/176 [==============================] - 14s 77ms/step - loss: 1.6795 - acuracia: 0.5361 acuracia-top-5: 0.8329 - val_loss: 1.9428 - val_acuracia: 0.4972 - val_acuracia-top-5: 0.7852
Epoch 32/50
176/176 [==============================] - 14s 77ms/step - loss: 1.6411 - acuracia: 0.5448 acuracia-top-5: 0.8412 - val_loss: 1.9318 - val_acuracia: 0.4952 - val_acuracia-top-5: 0.7864
Epoch 33/50
176/176 [==============================] - 14s 77ms/step - loss: 1.6015 - acuracia: 0.5547 acuracia-top-5: 0.8466 - val_loss: 1.9233 - val_acuracia: 0.4996 - val_acuracia-top-5: 0.7882
Epoch 34/50
176/176 [==============================] - 14s 77ms/step - loss: 1.5651 - acuracia: 0.5655 acuracia-top-5: 0.8525 - val_loss: 1.9285 - val_acuracia: 0.5082 - val_acuracia-top-5: 0.7888
Epoch 35/50
176/176 [==============================] - 14s 77ms/step - loss: 1.5437 - acuracia: 0.5672 acuracia-top-5: 0.8570 - val_loss: 1.9268 - val_acuracia: 0.5028 - val_acuracia-top-5: 0.7842
Epoch 36/50
176/176 [==============================] - 14s 77ms/step - loss: 1.5103 - acuracia: 0.5748 acuracia-top-5: 0.8620 - val_loss: 1.9262 - val_acuracia: 0.5014 - val_acuracia-top-5: 0.7890
Epoch 37/50
176/176 [==============================] - 14s 77ms/step - loss: 1.4784 - acuracia: 0.5822 acuracia-top-5: 0.8690 - val_loss: 1.8698 - val_acuracia: 0.5130 - val_acuracia-top-5: 0.7948
Epoch 38/50
176/176 [==============================] - 14s 77ms/step - loss: 1.4449 - acuracia: 0.5922 acuracia-top-5: 0.8728 - val_loss: 1.8734 - val_acuracia: 0.5136 - val_acuracia-top-5: 0.7980
Epoch 39/50
176/176 [==============================] - 14s 77ms/step - loss: 1.4312 - acuracia: 0.5928 acuracia-top-5: 0.8755 - val_loss: 1.8736 - val_acuracia: 0.5150 - val_acuracia-top-5: 0.7956
Epoch 40/50
176/176 [==============================] - 14s 77ms/step - loss: 1.3996 - acuracia: 0.5999 acuracia-top-5: 0.8808 - val_loss: 1.8718 - val_acuracia: 0.5178 - val_acuracia-top-5: 0.7970
Epoch 41/50
176/176 [==============================] - 14s 77ms/step - loss: 1.3859 - acuracia: 0.6075 acuracia-top-5: 0.8817 - val_loss: 1.9097 - val_acuracia: 0.5084 - val_acuracia-top-5: 0.7884
Epoch 42/50
176/176 [==============================] - 14s 77ms/step - loss: 1.3586 - acuracia: 0.6119 acuracia-top-5: 0.8860 - val_loss: 1.8620 - val_acuracia: 0.5148 - val_acuracia-top-5: 0.8010
Epoch 43/50
176/176 [==============================] - 14s 77ms/step - loss: 1.3384 - acuracia: 0.6154 acuracia-top-5: 0.8911 - val_loss: 1.8509 - val_acuracia: 0.5202 - val_acuracia-top-5: 0.8014
Epoch 44/50
176/176 [==============================] - 14s 78ms/step - loss: 1.3090 - acuracia: 0.6236 acuracia-top-5: 0.8954 - val_loss: 1.8607 - val_acuracia: 0.5242 - val_acuracia-top-5: 0.8020
Epoch 45/50
176/176 [==============================] - 14s 78ms/step - loss: 1.2873 - acuracia: 0.6292 acuracia-top-5: 0.8964 - val_loss: 1.8729 - val_acuracia: 0.5208 - val_acuracia-top-5: 0.8056
Epoch 46/50
176/176 [==============================] - 14s 77ms/step - loss: 1.2658 - acuracia: 0.6367 acuracia-top-5: 0.9007 - val_loss: 1.8573 - val_acuracia: 0.5278 - val_acuracia-top-5: 0.8066
Epoch 47/50
176/176 [==============================] - 14s 77ms/step - loss: 1.2628 - acuracia: 0.6346 acuracia-top-5: 0.9023 - val_loss: 1.8240 - val_acuracia: 0.5292 - val_acuracia-top-5: 0.8112
Epoch 48/50
176/176 [==============================] - 14s 78ms/step - loss: 1.2396 - acuracia: 0.6431 acuracia-top-5: 0.9057 - val_loss: 1.8342 - val_acuracia: 0.5362 - val_acuracia-top-5: 0.8096
Epoch 49/50
176/176 [==============================] - 14s 77ms/step - loss: 1.2163 - acuracia: 0.6464 acuracia-top-5: 0.9081 - val_loss: 1.8836 - val_acuracia: 0.5246 - val_acuracia-top-5: 0.8044
Epoch 50/50
176/176 [==============================] - 14s 77ms/step - loss: 1.1919 - acuracia: 0.6541 acuracia-top-5: 0.9122 - val_loss: 1.8513 - val_acuracia: 0.5336 - val_acuracia-top-5: 0.8048
40/40 [==============================] - 1s 26ms/step - loss: 1.8172 - acuracia: 0.5310 acuracia-top-5: 0.8053
Acurácia do teste: 53.1%
Acurácia do teste top-5: 80.53%
Execute experimentos com Tokenização de Patchs por Deslocamento (SPT - Shifted Patch Tokenization) e autoatenção de localidade modificada ViT.
vit_sl = criar_classificador_vit(vanilla=False)
historia = executar_experimento(vit_sl)
Epoch 1/50
176/176 [==============================] - 23s 90ms/step - loss: 4.4889 - acuracia: 0.0450 acuracia-top-5: 0.1559 - val_loss: 3.9364 - val_acuracia: 0.1128 - val_acuracia-top-5: 0.3184
Epoch 2/50
176/176 [==============================] - 15s 85ms/step - loss: 3.9806 - acuracia: 0.0924 acuracia-top-5: 0.2798 - val_loss: 3.6392 - val_acuracia: 0.1576 - val_acuracia-top-5: 0.4034
Epoch 3/50
176/176 [==============================] - 15s 84ms/step - loss: 3.7713 - acuracia: 0.1253 acuracia-top-5: 0.3448 - val_loss: 3.3892 - val_acuracia: 0.1918 - val_acuracia-top-5: 0.4622
Epoch 4/50
176/176 [==============================] - 15s 85ms/step - loss: 3.6297 - acuracia: 0.1460 acuracia-top-5: 0.3859 - val_loss: 3.2856 - val_acuracia: 0.2194 - val_acuracia-top-5: 0.4970
Epoch 5/50
176/176 [==============================] - 15s 85ms/step - loss: 3.4955 - acuracia: 0.1706 acuracia-top-5: 0.4239 - val_loss: 3.1359 - val_acuracia: 0.2412 - val_acuracia-top-5: 0.5308
Epoch 6/50
176/176 [==============================] - 15s 85ms/step - loss: 3.3781 - acuracia: 0.1908 acuracia-top-5: 0.4565 - val_loss: 3.0535 - val_acuracia: 0.2620 - val_acuracia-top-5: 0.5652
Epoch 7/50
176/176 [==============================] - 15s 85ms/step - loss: 3.2540 - acuracia: 0.2123 acuracia-top-5: 0.4895 - val_loss: 2.9165 - val_acuracia: 0.2782 - val_acuracia-top-5: 0.5800
Epoch 8/50
176/176 [==============================] - 15s 85ms/step - loss: 3.1442 - acuracia: 0.2318 acuracia-top-5: 0.5197 - val_loss: 2.8592 - val_acuracia: 0.2984 - val_acuracia-top-5: 0.6090
Epoch 9/50
176/176 [==============================] - 15s 85ms/step - loss: 3.0348 - acuracia: 0.2504 acuracia-top-5: 0.5440 - val_loss: 2.7378 - val_acuracia: 0.3146 - val_acuracia-top-5: 0.6294
Epoch 10/50
176/176 [==============================] - 15s 84ms/step - loss: 2.9311 - acuracia: 0.2681 acuracia-top-5: 0.5704 - val_loss: 2.6274 - val_acuracia: 0.3362 - val_acuracia-top-5: 0.6446
Epoch 11/50
176/176 [==============================] - 15s 85ms/step - loss: 2.8214 - acuracia: 0.2925 acuracia-top-5: 0.5986 - val_loss: 2.5557 - val_acuracia: 0.3458 - val_acuracia-top-5: 0.6616
Epoch 12/50
176/176 [==============================] - 15s 85ms/step - loss: 2.7244 - acuracia: 0.3100 acuracia-top-5: 0.6168 - val_loss: 2.4763 - val_acuracia: 0.3564 - val_acuracia-top-5: 0.6804
Epoch 13/50
176/176 [==============================] - 15s 85ms/step - loss: 2.6476 - acuracia: 0.3255 acuracia-top-5: 0.6358 - val_loss: 2.3946 - val_acuracia: 0.3678 - val_acuracia-top-5: 0.6940
Epoch 14/50
176/176 [==============================] - 15s 85ms/step - loss: 2.5518 - acuracia: 0.3436 acuracia-top-5: 0.6584 - val_loss: 2.3362 - val_acuracia: 0.3856 - val_acuracia-top-5: 0.7038
Epoch 15/50
176/176 [==============================] - 15s 85ms/step - loss: 2.4620 - acuracia: 0.3632 acuracia-top-5: 0.6776 - val_loss: 2.2690 - val_acuracia: 0.4006 - val_acuracia-top-5: 0.7222
Epoch 16/50
176/176 [==============================] - 15s 85ms/step - loss: 2.4010 - acuracia: 0.3749 acuracia-top-5: 0.6908 - val_loss: 2.1937 - val_acuracia: 0.4216 - val_acuracia-top-5: 0.7338
Epoch 17/50
176/176 [==============================] - 15s 85ms/step - loss: 2.3330 - acuracia: 0.3911 acuracia-top-5: 0.7041 - val_loss: 2.1519 - val_acuracia: 0.4286 - val_acuracia-top-5: 0.7370
Epoch 18/50
176/176 [==============================] - 15s 85ms/step - loss: 2.2600 - acuracia: 0.4069 acuracia-top-5: 0.7171 - val_loss: 2.1212 - val_acuracia: 0.4356 - val_acuracia-top-5: 0.7460
Epoch 19/50
176/176 [==============================] - 15s 85ms/step - loss: 2.1967 - acuracia: 0.4169 acuracia-top-5: 0.7320 - val_loss: 2.0748 - val_acuracia: 0.4470 - val_acuracia-top-5: 0.7580
Epoch 20/50
176/176 [==============================] - 15s 85ms/step - loss: 2.1397 - acuracia: 0.4302 acuracia-top-5: 0.7450 - val_loss: 2.1152 - val_acuracia: 0.4362 - val_acuracia-top-5: 0.7416
Epoch 21/50
176/176 [==============================] - 15s 85ms/step - loss: 2.0929 - acuracia: 0.4396 acuracia-top-5: 0.7524 - val_loss: 2.0044 - val_acuracia: 0.4652 - val_acuracia-top-5: 0.7680
Epoch 22/50
176/176 [==============================] - 15s 85ms/step - loss: 2.0423 - acuracia: 0.4521 acuracia-top-5: 0.7639 - val_loss: 2.0628 - val_acuracia: 0.4488 - val_acuracia-top-5: 0.7544
Epoch 23/50
176/176 [==============================] - 15s 85ms/step - loss: 1.9771 - acuracia: 0.4661 acuracia-top-5: 0.7750 - val_loss: 1.9380 - val_acuracia: 0.4740 - val_acuracia-top-5: 0.7836
Epoch 24/50
176/176 [==============================] - 15s 84ms/step - loss: 1.9323 - acuracia: 0.4752 acuracia-top-5: 0.7848 - val_loss: 1.9461 - val_acuracia: 0.4732 - val_acuracia-top-5: 0.7768
Epoch 25/50
176/176 [==============================] - 15s 85ms/step - loss: 1.8913 - acuracia: 0.4844 acuracia-top-5: 0.7914 - val_loss: 1.9230 - val_acuracia: 0.4768 - val_acuracia-top-5: 0.7886
Epoch 26/50
176/176 [==============================] - 15s 84ms/step - loss: 1.8520 - acuracia: 0.4950 acuracia-top-5: 0.7999 - val_loss: 1.9159 - val_acuracia: 0.4808 - val_acuracia-top-5: 0.7900
Epoch 27/50
176/176 [==============================] - 15s 85ms/step - loss: 1.8175 - acuracia: 0.5046 acuracia-top-5: 0.8076 - val_loss: 1.8977 - val_acuracia: 0.4896 - val_acuracia-top-5: 0.7876
Epoch 28/50
176/176 [==============================] - 15s 85ms/step - loss: 1.7692 - acuracia: 0.5133 acuracia-top-5: 0.8146 - val_loss: 1.8632 - val_acuracia: 0.4940 - val_acuracia-top-5: 0.7920
Epoch 29/50
176/176 [==============================] - 15s 85ms/step - loss: 1.7375 - acuracia: 0.5193 acuracia-top-5: 0.8206 - val_loss: 1.8686 - val_acuracia: 0.4926 - val_acuracia-top-5: 0.7952
Epoch 30/50
176/176 [==============================] - 15s 85ms/step - loss: 1.6952 - acuracia: 0.5308 acuracia-top-5: 0.8280 - val_loss: 1.8265 - val_acuracia: 0.5024 - val_acuracia-top-5: 0.7996
Epoch 31/50
176/176 [==============================] - 15s 85ms/step - loss: 1.6631 - acuracia: 0.5379 acuracia-top-5: 0.8348 - val_loss: 1.8665 - val_acuracia: 0.4942 - val_acuracia-top-5: 0.7854
Epoch 32/50
176/176 [==============================] - 15s 85ms/step - loss: 1.6329 - acuracia: 0.5466 acuracia-top-5: 0.8401 - val_loss: 1.8364 - val_acuracia: 0.5090 - val_acuracia-top-5: 0.7996
Epoch 33/50
176/176 [==============================] - 15s 85ms/step - loss: 1.5960 - acuracia: 0.5537 acuracia-top-5: 0.8465 - val_loss: 1.8171 - val_acuracia: 0.5136 - val_acuracia-top-5: 0.8034
Epoch 34/50
176/176 [==============================] - 15s 85ms/step - loss: 1.5815 - acuracia: 0.5578 acuracia-top-5: 0.8476 - val_loss: 1.8020 - val_acuracia: 0.5128 - val_acuracia-top-5: 0.8042
Epoch 35/50
176/176 [==============================] - 15s 85ms/step - loss: 1.5432 - acuracia: 0.5667 acuracia-top-5: 0.8566 - val_loss: 1.8173 - val_acuracia: 0.5142 - val_acuracia-top-5: 0.8080
Epoch 36/50
176/176 [==============================] - 15s 85ms/step - loss: 1.5110 - acuracia: 0.5768 acuracia-top-5: 0.8594 - val_loss: 1.8168 - val_acuracia: 0.5124 - val_acuracia-top-5: 0.8066
Epoch 37/50
176/176 [==============================] - 15s 85ms/step - loss: 1.4890 - acuracia: 0.5816 acuracia-top-5: 0.8641 - val_loss: 1.7861 - val_acuracia: 0.5274 - val_acuracia-top-5: 0.8120
Epoch 38/50
176/176 [==============================] - 15s 85ms/step - loss: 1.4672 - acuracia: 0.5849 acuracia-top-5: 0.8660 - val_loss: 1.7695 - val_acuracia: 0.5222 - val_acuracia-top-5: 0.8106
Epoch 39/50
176/176 [==============================] - 15s 85ms/step - loss: 1.4323 - acuracia: 0.5939 acuracia-top-5: 0.8721 - val_loss: 1.7653 - val_acuracia: 0.5250 - val_acuracia-top-5: 0.8164
Epoch 40/50
176/176 [==============================] - 15s 85ms/step - loss: 1.4192 - acuracia: 0.5975 acuracia-top-5: 0.8754 - val_loss: 1.7727 - val_acuracia: 0.5298 - val_acuracia-top-5: 0.8154
Epoch 41/50
176/176 [==============================] - 15s 85ms/step - loss: 1.3897 - acuracia: 0.6055 acuracia-top-5: 0.8805 - val_loss: 1.7535 - val_acuracia: 0.5328 - val_acuracia-top-5: 0.8122
Epoch 42/50
176/176 [==============================] - 15s 85ms/step - loss: 1.3702 - acuracia: 0.6087 acuracia-top-5: 0.8828 - val_loss: 1.7746 - val_acuracia: 0.5316 - val_acuracia-top-5: 0.8116
Epoch 43/50
176/176 [==============================] - 15s 85ms/step - loss: 1.3338 - acuracia: 0.6185 acuracia-top-5: 0.8894 - val_loss: 1.7606 - val_acuracia: 0.5342 - val_acuracia-top-5: 0.8176
Epoch 44/50
176/176 [==============================] - 15s 85ms/step - loss: 1.3171 - acuracia: 0.6200 acuracia-top-5: 0.8920 - val_loss: 1.7490 - val_acuracia: 0.5364 - val_acuracia-top-5: 0.8164
Epoch 45/50
176/176 [==============================] - 15s 85ms/step - loss: 1.3056 - acuracia: 0.6276 acuracia-top-5: 0.8932 - val_loss: 1.7535 - val_acuracia: 0.5388 - val_acuracia-top-5: 0.8156
Epoch 46/50
176/176 [==============================] - 15s 85ms/step - loss: 1.2876 - acuracia: 0.6289 acuracia-top-5: 0.8952 - val_loss: 1.7546 - val_acuracia: 0.5320 - val_acuracia-top-5: 0.8154
Epoch 47/50
176/176 [==============================] - 15s 85ms/step - loss: 1.2764 - acuracia: 0.6350 acuracia-top-5: 0.8970 - val_loss: 1.7177 - val_acuracia: 0.5382 - val_acuracia-top-5: 0.8200
Epoch 48/50
176/176 [==============================] - 15s 85ms/step - loss: 1.2543 - acuracia: 0.6407 acuracia-top-5: 0.9001 - val_loss: 1.7330 - val_acuracia: 0.5438 - val_acuracia-top-5: 0.8198
Epoch 49/50
176/176 [==============================] - 15s 84ms/step - loss: 1.2191 - acuracia: 0.6470 acuracia-top-5: 0.9042 - val_loss: 1.7316 - val_acuracia: 0.5436 - val_acuracia-top-5: 0.8196
Epoch 50/50
176/176 [==============================] - 15s 85ms/step - loss: 1.2186 - acuracia: 0.6457 acuracia-top-5: 0.9066 - val_loss: 1.7201 - val_acuracia: 0.5486 - val_acuracia-top-5: 0.8218
40/40 [==============================] - 1s 30ms/step - loss: 1.6760 - acuracia: 0.5611 acuracia-top-5: 0.8227
Acurácia do teste: 56.11%
Acurácia do teste top-5: 82.27%
Notas Finais
Com a ajuda de Shifted Patch Tokenization e Locality Self Attention, obtivemos um modelo de 65% de acurácia no CIFAR100.
As ideias sobre estas técnicas são muito intuitivas e fáceis de implementar.
Os autores também abordam diferentes estratégias de mudança para SPT no suplemento do artigo.
Foi utilizada a plataforma de GPUs de Jarvislabs.ai para aceleração do algorítmo.
Este código primeiro carrega os dados de treinamento e teste do conjunto de dados MNIST.
O conjunto de dados MNIST é um conjunto de imagens de dígitos manuscritos.
O código normaliza os dados dividindo cada pixel por 255. Isto é feito para que todos os dados estejam na mesma escala.
Em seguida, o código cria um modelo de rede neural. Este modelo é uma rede neural simples com duas camadas ocultas, cada uma com 128 neurônios.
A camada de saída tem 10 neurônios, um para cada classe no conjunto de dados. O código então compila o modelo, definindo o otimizador, a função de perda e as métricas.
Em seguida, o código treina o modelo, alimentando os dados de treinamento.
Por fim, o código avalia o desempenho do modelo, alimentando os dados de teste.
O código imprime a acurácia do modelo, que é a porcentagem de imagens que o modelo classificou corretamente.
import tensorflow as tf
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Flatten
from tensorflow.keras.datasets import mnist
A classe Sequential é usada para criar uma sequência linear de camadas em uma rede neural.
A camada Dense é uma camada de neurônios completamente conectados, e a camada Flatten é usada para aplanar os dados de entrada.
Carrega as imagens de gatos e cães:
(dados_treino, alvos_treino), (dados_teste, alvos_teste) = mnist.load_data()
Dê uma olhada nos dados.
Conjuntos de dados e alvos de treino.
Dados de treino:
print('*** Dados do conjunto de treino')
print(f'Tipo: {type(dados_treino)}')
print(f'Dims: {dados_treino.shape}')
print()
print(dados_treino)
Imprima apenas o primeiro elemento do conjunto de dados, para formar ideia do formato das imagens.
primeiro_dado_treino = dados_treino[0]
print(f'Tipo: {type(primeiro_dado_treino)}')
print(f'Dims: {primeiro_dado_treino.shape}')
print()
print(primeiro_dado_treino)
Alvos de treino:
print('*** Alvos do conjunto de treino')
print(f'Tipo: {type(alvos_treino)}')
print(f'Dims: {alvos_treino.shape}')
print()
print(alvos_treino)
Os alvos são um conjunto de números inteiros entre 0 e 9.
Imprima apenas o primeiro elemento do conjunto de alvos, para formar ideia do formato das etiquetas.
primeiro_alvo_treino = alvos_treino[0]
print(f'Tipo: {type(primeiro_alvo_treino)}')
print(f'Dims: {primeiro_alvo_treino.shape}')
print()
print(primeiro_alvo_treino)
Conjuntos de dados e alvos de teste.
Dados de teste:
print('*** Dados do conjunto de teste')
print(f'Dimensões: {dados_teste.shape}')
print()
print(dados_teste)
Alvos de teste:
print('*** Alvos do conjunto de teste')
print(f'Dimensões: {alvos_teste.shape}')
print()
print(alvos_teste)
Normalize os dados de pixel das imagens dividindo-os por 255.0. Isso coloca os valores dos pixels no intervalo de 0 a 1.
dados_treino = dados_treino / 255.0
dados_teste = dados_teste / 255.0
Imprima apenas os primeiros elementos dos conjuntos de dados de treino, para formar ideia do formato normalizado das imagens.
print('*** Dados do conjunto de treino')
print(dados_treino[0])
Crie o modelo usando a classe Sequential.
modelo = Sequential()
modelo.add(Flatten(input_shape=(28, 28)))
modelo.add(Dense(128, activation='relu'))
modelo.add(Dense(10, activation='softmax'))
Compile o modelo.
Defina o otimizador como 'adam', a função de perda como 'sparse_categorical_crossentropy' (adequada para tarefas de classificação multiclasse) e a métrica 'accuracy' de avaliação da precisão.
modelo.compile(
optimizer='adam',
loss='sparse_categorical_crossentropy',
metrics=['accuracy'])
Treine o modelo.
Use os dados de treinamento (dados_treino e alvos_treino) por 10 episódios (épocas), ajustando os pesos da rede neural para minimizar a função de perda.
modelo.fit(dados_treino, alvos_treino, epochs=10)
Avalie o desempenho do modelo.
Use o conjunto de dados de teste (dados_teste e alvos_teste), retorna a pontuação, que inclui a perda e a precisão no conjunto de teste.
escore = modelo.evaluate(dados_teste, alvos_teste)
Imprima a acurácia do modelo, que é a porcentagem de imagens que o modelo classificou corretamente.
print(f'Acurácia ... : {escore[1]:.4f}')
print(f'Perda....... : {escore[0]:.4f}')
No exemplo fornecido, um modelo de rede neural foi treinado para realizar a tarefa de classificação de dígitos manuscritos usando o conjunto de dados MNIST.
Após o treinamento, foram obtidos dois valores essenciais: a perda (loss) e a precisão (accuracy).
A interpretação desses valores depende do contexto da tarefa específica, e eles são cruciais para avaliar o desempenho do modelo.
A perda é uma medida que indica o quão bem o modelo está performando na tarefa. Neste caso, a perda apresentou um valor de aproximadamente 0.08, o que é considerado baixo.
A perda é uma métrica relativa que deve ser interpretada em comparação com outros modelos ou referências específicas.
Em um cenário de classificação de dígitos MNIST, uma perda baixa é um sinal positivo, indicando que o modelo está fazendo previsões precisas.
Além disso, a acurácia ( ou precisão), que é uma métrica que expressa a porcentagem de classificações corretas em relação ao total de amostras no conjunto de teste, também foi calculada.
No exemplo, a precisão foi de cerca de 97.83%, o que é considerado uma precisão muito boa para a tarefa de classificação de dígitos MNIST.
No entanto, assim como a perda, a precisão é relativa ao contexto da tarefa.
Os valores de perda e precisão obtidos no exemplo são indicativos de um bom desempenho do modelo para a tarefa em questão, que é a classificação de dígitos manuscritos do MNIST.
No entanto, é fundamental considerar o contexto específico da tarefa e as expectativas de desempenho ao interpretar esses valores.
Em algumas tarefas, uma precisão de quase 98% e uma perda de 0.08 podem ser consideradas excelentes, enquanto em outras, esses valores podem ser insuficientes.
Portanto, a avaliação do desempenho do modelo deve sempre ser adaptada às necessidades e exigências do problema em questão.
Este projeto tem objetivo de abrir simulações de câmera suando vídeos para detecção de objetos em imagens em tempo real.
O esquema abaixo mostra os passos a seguir:
As passos são:
Estes passos são detalhados a seguir com os códigos Python do projeto.
São utilizadas as seguintes bibliotecas:
Essas bibliotecas são todas ferramentas valiosas para cientistas de dados e engenheiros. Podem ser usadas para realizar uma variedade de tarefas relacionadas a computação científica, aprendizado de máquina, processamento de imagens e sistema operacional.
Para instalar o Numpy, OpenCV e o TensorFlow, e suas dependências, use o pip:
pip show numpy
pip show opencv
pip show threading
pip show tensorflow
pip show tensorflow-object-detection-api
pip show protobuf
Para instalar o Numpy, OpenCV e o TensorFlow, e suas dependências, se estiverem faltantes, use o argumento install do pip:
pip install numpy
pip install opencv
pip install threading
pip install tensorflow
pip install tensorflow-object-detection-api
pip install protobuf
Se necessário, para instalar os módulos, execute o comando:
pip install tensorflow numpy cv2 threading
Remova da instalação os módulos que não são necessários.
Código para importar as bibliotecas:
# 1 Importar bibliotecas
import tensorflow as tf
import numpy as np
from object_detection.utils import label_map_util
from object_detection.utils import visualization_utils as vis_util
import cv2
import threading
import os
A seguir são declaradas variáveis globais.
# 2 Declarar as variáveis globais
continuar = True
detectar = True
desenhar_deteccoes = True
redimensionar = False
cinza = False
---------------------------------
--- class DetectorObjetos ---
---------------------------------
A classe DetectorObjetos é responsável por detectar objetos em um frame.
Ela é inicializada com o caminho do modelo pré-treinado e a configuração do TensorFlow.
O método 'detectar_01()' recebe um frame e retorna os atributos calculados, usados para desenhar as detecções na janela de exibição.
O método 'desenhar_deteccoes()' é chamado a cada frame detectado, para desenhar as detecções na janela de exibição.
O método 'encerrar' é chamado automaticamente quando o objeto é destruído.
Ele fecha a janela de exibição do frame com as detecções.
O destrutor é chamado automaticamente quando o programa é encerrado.
Declarar a classe DetectorObjetos responsável por detectar objetos em um frame:
class DetectorObjetos:
Passo 3.1: Declarar o método __init__ construtor da classe detectora de objetos
def __init__(
self, root_path, model_path, label_path, max_detections=2,
desenhar_deteccoes=True):
#
self.root_path = root_path
self.model_path = model_path
self.label_path = label_path
self.max_detections = max_detections
self.desenhar_deteccoes = desenhar_deteccoes
self.deteccao_pronta = False
self.frame_deteccao = None
self.caixas = None
self.classes = None
self.escores = None
self.iniciar()
Passo 3.2: Declarar o método 'iniciar', chamado no construtor, que carrega o modelo pré-treinado e a configuração do TensorFlow e cria a janela de exibição do frame com as detecções.
def iniciar(self):
self.carregar_modelo_pre_treinado()
self.criar_janela()
Passo 3.3: Declarar o método 'encerrar' é chamado no destrutor, que fecha a janela de exibição do frame com as detecções. O destrutor é chamado automaticamente quando o objeto é destruído. Por exemplo, quando o programa é encerrado.
def encerrar(self):
self.fechar_janela()
Passo 3.4: Declarar o método 'criar_janela' cria a janela de exibição do frame com as detecções.
def criar_janela(self):
cv2.namedWindow('DETECTOR', cv2.WINDOW_NORMAL)
Passo 3.5: Declarar o método 'reposicionar_janela' move a janela de exibição do frame com as detecções.
def reposicionar_janela(self,x,y):
cv2.moveWindow('DETECTOR', x, y)
Passo 3.6: Declarar o método 'fechar_janela' fecha a janela de exibição do frame com as detecções.
def fechar_janela(self):
cv2.destroyWindow('DETECTOR')
Passo 3.7: Carrega o modelo pré-treinado e a configuração do TensorFlow:
def carregar_modelo_pre_treinado(self):
self.detector_grafico = tf.Graph()
with self.detector_grafico.as_default():
od_graph_def = tf.compat.v1.GraphDef()
with tf.compat.v2.io.gfile.GFile(self.model_path,'rb') as f:
serialized_graph = f.read()
od_graph_def.ParseFromString(serialized_graph)
tf.import_graph_def(od_graph_def, name='')
label_map = label_map_util.load_labelmap(self.label_path)
categories = label_map_util.convert_label_map_to_categories(label_map, max_num_classes=90, use_display_name=True)
self.category_index = label_map_util.create_category_index(categories)
Passo 3.8: Executa a detecção de objetos no frame.
Essas linhas de código estão relacionadas à detecção de objetos em imagens usando um modelo de rede neural convolucional.
Cada uma das variáveis do tipo tensor retorna um array multidimensional de valores que representam diferentes aspectos da detecção de objetos.
Aqui está o que cada um desses tensores retorna:
def executar_deteccao(self,frame):
# O frame da câmera é copiado para o frame de detecção
# que será alterado para conter os retângulos de detecção
self.frame_deteccao = frame.copy()
dg = self.detector_grafico
with dg.as_default():
with tf.compat.v1.Session(graph=dg) as sess:
# Redimensiona o frame para a detecção de objetos
frame_expanded = sess.run(tf.expand_dims(self.frame_deteccao, axis=0))
# Obtém os tensores de saída do modelo
# image_tensor: é um tensor que contém a imagem de entrada que
# será usada para a detecção de objetos.
image_tensor = dg.get_tensor_by_name('image_tensor:0')
# caixas_deteccao: é um tensor que contém as coordenadas das
# caixas delimitadoras (bounding boxes)
# que cercam os objetos detectados na imagem de entrada.
caixas_deteccao = dg.get_tensor_by_name('detection_boxes:0')
# escores_deteccao: é um tensor que contém os escores de
# confiança (probabilidades) associados a cada caixa delimitadora.
escores_deteccao = dg.get_tensor_by_name('detection_scores:0')
# classes_deteccao: é um tensor que contém as classes dos objetos
# detectados nas caixas delimitadoras correspondentes.
classes_deteccao = dg.get_tensor_by_name('detection_classes:0')
# num_deteccoes: é um tensor que contém o número de
# detecções de objetos encontrados na imagem.
num_deteccoes = dg.get_tensor_by_name('num_detections:0')
# Executa a detecção de objetos e retorna os valores dos tensores
# caixas_deteccao, escores_deteccao, classes_deteccao e num_deteccoes
# para os atributos caixas, escores, classes e num, respectivamente.
(self.caixas, self.escores, self.classes, self.num) = sess.run(
[caixas_deteccao, escores_deteccao, classes_deteccao, num_deteccoes],
feed_dict={image_tensor: frame_expanded})
Passo 3.9: Desenha as caixas delimitadoras das classes detectadas no frame_deteccao
def desenhar_retangulos(self):
if self.desenhar_deteccoes:
vis_util.visualize_boxes_and_labels_on_image_array(
self.frame_deteccao,
self.caixas,
self.classes.astype(np.int32),
self.escores,
self.category_index,
use_normalized_coordinates=True,
line_thickness=2,
max_boxes_to_draw=self.max_detections)
Passo 3.10: O método 'detector_01()' detecta objetos no frame redimensiona os tensores de saída para remover as dimensões adicionais filtra as detecções com pontuações acima de um limiar mínimo desenha as caixas delimitadoras das classes detectadas no frame_deteccao.
def detector_01(self,frame):
self.executar_deteccao(frame)
self.redimensionar_tensores_01()
self.desenhar_retangulos()
Passo 3.11: Remove dimensões adicionais dos tensores de saída e redimensiona os valores dos tensores para o tamanho do frame.
def redimensionar_tensores_01(self):
self.caixas = np.squeeze(self.caixas)
self.escores = np.squeeze(self.escores)
self.classes = np.squeeze(self.classes).astype(np.int32)
# Filtra as detecções com pontuações acima de um limiar mínimo
self.limiar = 0.5
self.caixas = self.caixas[self.escores > self.limiar]
self.classes = self.classes[self.escores > self.limiar]
self.escores = self.escores[self.escores > self.limiar]
Passo 3.12: Filtra os resultados da detecção de objetos
def detector_02(self):
self.executar_deteccao()
self.redimensionar_tensores_02()
self.desenhar_retangulos()
Passo 3.13: Remove dimensões adicionais dos tensores de saída e redimensiona os valores dos tensores para o tamanho do frame
def redimensionar_tensores_02(self):
# altura e largura do frame
height, width = self.frame_deteccao.shape[:2]
self.caixas = np.squeeze(self.caixas)
self.caixas[:, 0] *= height
self.caixas[:, 1] *= width
self.caixas[:, 2] *= height
self.caixas[:, 3] *= width
# Converte os valores dos tensores para inteiros
self.caixas = self.caixas.astype(np.int32)
self.escores = np.squeeze(self.escores).astype(np.float32)
self.classes = np.squeeze(self.classes).astype(np.int32)
# Filtra os resultados da detecção de objetos
self.caixas = self.caixas[:self.max_detections]
self.escores = self.escores[:self.max_detections]
self.classes = self.classes[:self.max_detections]
------------------------
--- class Camera ---
------------------------
class Camera:
A classe Camera controla uma câmera individual (webcam, arquivo de vídeo, etc).
Ela fornece acesso a imagens de vídeo em tempo real, bem como a localização de objetos detectados em cada quadro de vídeo.
A classe Camera é usada pelo programa principal para detectar objetos em um vídeo.
A classe Camera é uma classe abstrata. Ela não pode ser instanciada diretamente.
Em vez disso, você deve usar uma das subclasses concretas: WebcamCamera ou VideoCamera.
Passo 4.1: Método __init__ construtor da classe, inicializa a câmera
# 4.1 Método __init__ construtor da classe
def __init__(self, cameras, id_camera, root_path, nome_video, redimensionar=(), cinza=False):
# Atributos da classe
self.cameras = cameras
self.largura_maxima = cameras.largura_maxima
self.altura_maxima = cameras.altura_maxima
self.largura_borda = cameras.largura_borda
self.altura_titulo = cameras.altura_titulo
self.id_camera = id_camera
self.redimensionar = redimensionar
self.cinza = cinza
self.root_path = root_path
self.nome_video = nome_video
self.video_path = os.path.join(root_path, nome_video)
self.iniciar()
Passo 4.2: O método iniciar() inicializa a câmera, abre o arquivo de vídeo e cria a janela de vídeo.
def iniciar(self):
self.abrir_camera()
self.criar_janela()
Passo 4.3: O método encerrar() fecha a câmera, fecha a janela de vídeo e fecha o arquivo de vídeo.
def encerrar(self):
self.fechar_janela()
self.fechar_camera()
Passo 4.4: O método abrir_camera() abre o arquivo de vídeo e cria a janela de vídeo, e define o total de frames do arquivo de vídeo.0
def abrir_camera(self):
self.cap = cv2.VideoCapture(self.video_path)
self.total_frames = int(self.cap.get(cv2.CAP_PROP_FRAME_COUNT))
self.largura_cliente=int(self.cap.get(cv2.CAP_PROP_FRAME_WIDTH))
self.altura_cliente=int(self.cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
if self.largura_maxima:
self.altura_cliente = int(self.altura_cliente * self.largura_maxima / self.largura_cliente)
self.largura_cliente = self.largura_maxima
elif self.altura_maxima:
self.largura_cliente = int(self.largura_cliente * self.altura_maxima // self.altura_cliente)
self.altura_cliente = self.altura_maxima
self.largura_janela = self.largura_cliente + (2 * self.largura_borda)
self.altura_janela = self.altura_cliente + ((2 * self.largura_borda) + self.altura_titulo)
Passo 4.5: O método fechar_camera() fecha a câmera e fecha o arquivo de vídeo.
def fechar_camera(self):
self.cap.release()
Passo 4.6: Cria a janela de vídeo.
def criar_janela(self):
cv2.namedWindow(self.id_camera, cv2.WINDOW_NORMAL)
cv2.resizeWindow(self.id_camera,self.largura_cliente, self.altura_cliente)
Passo 4.7: Move a janela de vídeo
def reposicionar_janela(self, x, y):
cv2.moveWindow(self.id_camera, x, y)
Passo 4.8: Fecha a janela de vídeo
def fechar_janela(self):
cv2.destroyWindow(self.id_camera)
Passo 4.9: Lê um frame do arquivo de vídeo e retorna ao primeiro frame caso chegue ao fim
def ler_frame(self):
ret, self.frame = self.cap.read()
if not ret:
self.cap.set(cv2.CAP_PROP_POS_FRAMES, 0)
ret, self.frame = self.cap.read()
if ret: self.tratamentos_frame()
return ret
Passo 4.10: Tratamento dos frames do arquivo de vídeo, redimensiona o frame e converte o frame para escala de cinza
def tratamentos_frame(self):
if self.redimensionar:
self.frame = cv2.resize(self.frame,(self.redimensionar))
if self.cinza:
self.frame = cv2.cvtColor(self.frame, cv2.COLOR_BGR2GRAY)
self.frame = cv2.cvtColor(self.frame, cv2.COLOR_GRAY2RGB)
class Cameras:
Passo 5.1: Método __init__
def __init__(self, largura_maxima=0, altura_maxima=0, largura_borda=2, altura_titulo=30):
self.largura_maxima = largura_maxima
self.altura_maxima = altura_maxima
self.largura_borda = largura_borda
self.altura_titulo = altura_titulo
self.lista_cameras: list[Camera] = []
Passo 5.2: Método __iter__()
def __iter__(self):
return iter(self.lista_cameras)
Passo 5.3: Método adicionar()
def adicionar(self, pasta, nomes_cameras):
for nome_camera in nomes_cameras:
camera = Camera(cameras,str(len(self.lista_cameras)+1),pasta,nome_camera)
self.lista_cameras.append(camera)
Passo 5.4: Método reposicionar_janelas()
def reposicionar_janelas(self, x_inicial, y_inicial):
x = x_inicial
y = y_inicial
max_y = 0
linha = 0
for camera in cameras.lista_cameras:
camera.reposicionar_janela(x,y)
x += camera.largura_cliente
if camera.altura_cliente > max_y:
max_y = camera.altura_janela
linha += 1
if linha == 4:
x = x_inicial
y += max_y
max_y = 0
linha = 0
if detectar:
detector_objetos.reposicionar_janela(x,y)
Passo 5.5: Método iniciar()
def iniciar(self):
for camera in self.cameras:
camera.iniciar()
Passo 5.6: Método encerrar()
def encerrar(self):
for camera in cameras.lista_cameras:
camera.encerrar()
Passo 6.1: Define o caminho para o diretório raiz do projeto.
ROOT_PATH = './'
cameras = Cameras(altura_maxima=400)
cameras.adicionar(ROOT_PATH,[
#'video-1-360.mp4',
#'video-ran.mp4',
#'video-mulher-flores.mp4',
#'video-pessoas-3.1280x720.mp4',
'video-2-720.mp4'
])
Passo 6.2: Inicia a detecção
if detectar:
OD_PATH = ROOT_PATH # "c:/cieda/object_detection/"
# 6.2.1 Define o caminho para os arquivos de modelo pré-treinado e configuração
MODEL_PATH = OD_PATH + 'object_detection/ssd_mobilenet_v1_coco_11_06_2017/frozen_inference_graph.pb'
LABEL_PATH = OD_PATH + 'object_detection/data/mscoco_label_map.pbtxt'
# 6.2.2 Define o máximo de detecções a serem exibidas
MAX_DETECTIONS = 10
# 6.2.3 Instancia e inicia o objeto detector
detector_objetos = DetectorObjetos(
ROOT_PATH, MODEL_PATH, LABEL_PATH, MAX_DETECTIONS,
desenhar_deteccoes)
def thread_funcao_detector_objetos():
global continuar, detector_objetos
while continuar:
if detector_objetos.iniciar_deteccao:
detector_objetos.iniciar_deteccao = False
detector_objetos.detector_01(cameras.camera_atual.frame)
detector_objetos.deteccao_pronta = True
deteccao_liberada = True
detector_objetos.iniciar_deteccao = False
thread_deteccao_objetos = threading.Thread(target=thread_funcao_detector_objetos, args=())
thread_deteccao_objetos.start()
else:
thread_deteccao_objetos = None
Passo 6.3: Iniciar loop
cameras.reposicionar_janelas(100,100)
# 6.3 Loop infinito para processar cada frame do vídeo
num_camera = 0
while True:
camera = cameras.lista_cameras[num_camera]
cameras.camera_atual = camera
# Lê frame com verificação se a leitura do frame foi bem sucedida
if camera.ler_frame():
# Exibe o frame com as detecções
cv2.imshow(camera.id_camera,camera.frame)
if detectar:
if deteccao_liberada:
deteccao_liberada = False
detector_objetos.iniciar_deteccao = True
if detector_objetos.deteccao_pronta:
cv2.imshow('DETECTOR',detector_objetos.frame_deteccao)
detector_objetos.deteccao_pronta = False
deteccao_liberada = True
num_camera += 1
if num_camera == len(cameras.lista_cameras): num_camera = 0
# Aguarda a tecla 'q' ser pressionada para encerrar o programa
if cv2.waitKey(1) == ord('q'):
break
Passo 6.4: Fecha a janela, encerra o detector, as câmeras e o programa
continuar = False
if detectar: detector_objetos.encerrar()
cameras.encerrar()