Skip to content

Deep Learning Book

  • Início
  • Capítulos
  • Cursos Online
  • Contato
  • Índice

Deep Learning Book

Em Português, Online e Gratuito

Deep Learning Book

Capítulo 76 – O Que é BERT (Bidirectional Encoder Representations from Transformers)?

by

BERT (Bidirectional Encoder Representations from Transformers) é o algoritmo de aprendizado profundo (Deep Learning) do Google para PLN (Processamento de Linguagem Natural). Ajuda computadores e máquinas a entender a linguagem como nós, humanos, fazemos. Simplificando, o BERT pode ajudar o Google a entender melhor o significado das palavras nas consultas no mecanismo de busca.

Por exemplo, nas frases “quinze para as seis” e “nove para as seis”, a preposição “para” é interpretada de uma maneira diferente pelos humanos em comparação com um motor de busca que a trata como um só. O BERT permite que os mecanismos de busca entendam essas diferenças para fornecer resultados de pesquisa mais relevantes aos usuários.

Desenvolvido no ano de 2018, o BERT é um modelo pré-treinado para Processamento de Linguagem Natural, de código aberto (open-source). Agora, ele pode ser usado por qualquer pessoa para treinar seus sistemas de processamento de linguagem. Para facilitar as melhores consultas de pesquisa, ele é construído em representações contextuais de pré-treinamento, como o Transformer, ULMFiT, o transformador OpenAI, Semi-Supervised Sequence Learning e Elmo.

Um ponto importante de diferença entre o BERT e outros modelos de PLN é que é a primeira tentativa do Google de um modelo pré-treinado que é profundamente bidirecional e faz pouco uso de qualquer outra coisa além de um corpus de texto simples. Como é um modelo de código aberto, qualquer pessoa com conhecimento sólido de algoritmos de aprendizado de máquina pode usá-lo para desenvolver um modelo de PLN sem ter que integrar conjuntos de dados diferentes para treinamento de modelo, economizando recursos e dinheiro.

Outro diferencial importante em relação ao BERT é que ele foi pré-treinado em um corpus gigantesco de texto que ultrapassa 33 milhões de itens.

O Que é Uma Rede Neural?

Conforme vimos nos capítulos deste livro, algoritmos projetados para redes neurais funcionam identificando ou reconhecendo padrões. Prever tendências globais no domínio econômico, classificar o conteúdo da imagem e identificar a caligrafia são algumas das aplicações comuns das redes neurais no mundo real. Elas empregam conjuntos de dados para reconhecimento de padrões. Na verdade, o BERT foi pré-treinado na Wikipedia que ultrapassa 2500 milhões de palavras.

O Que é Processamento de Linguagem Natural?

O Processamento de Linguagem Natural (PLN) é um ramo da Inteligência Artificial projetado para ajudar as máquinas a entender o processo natural de comunicação dos seres humanos.

Você digita uma palavra na caixa de pesquisa do Google e uma série de sugestões aparecem. Você se comunica com chatbots de uma empresa. Todas essas comunicações são possibilitadas via PLN.

Exemplos de avanços possibilitados por PLN incluem ferramentas de escuta social, chatbots e sugestões de palavras em seu smartphone. Embora PLN não seja uma novidade para os mecanismos de pesquisa, o BERT representa um avanço no processamento de linguagem natural por meio do treinamento bidirecional.

Como Funciona o BERT?

Vamos estudar o funcionamento do BERT em mais detalhes no capítulo seguinte, mas o BERT treina os modelos de linguagem com base no conjunto completo de palavras em uma consulta ou frase conhecida como treinamento bidirecional, enquanto os modelos de PLN tradicionais treinam os modelos de linguagem na ordem da sequência de palavras (da direita para a esquerda ou da esquerda para a direita). O BERT facilita os modelos de linguagem a discernir o contexto das palavras com base nas palavras circundantes, em vez de palavras que o seguem ou precedem.

O Google chama esse processo de “profundamente bidirecional” e com razão, pela simples razão de que o verdadeiro significado do que as palavras estão comunicando só é possível por meio de uma análise profunda da rede neural.

Por exemplo, seria difícil para uma máquina diferenciar entre a palavra “banco”, como sendo uma agência bancária, da palavra “banco”, como sendo o local para sentar em uma praça, por exemplo. O modelo contextual funciona mapeando uma representação distinta de toda a frase para entender melhor seus contextos.

O BERT Substituiu o Algoritmo RankBrain?

RankBrain foi o primeiro algoritmo baseado em IA do Google a compreender as consultas de pesquisa e o contexto de uma palavra em uma frase. Ele usa aprendizado de máquina para fornecer os resultados de pesquisa mais relevantes para as consultas do usuário. Corresponde às consultas e ao conteúdo das páginas da web para entender melhor o contexto das palavras em uma frase. É importante compreender que o BERT não foi introduzido como um substituto do RankBrain. Na verdade, ele adiciona mais poder para que os pontos mais precisos do que o usuário está solicitando ou desejando sejam melhor compreendidos e processados. No entanto, se o Google precisa entender melhor o contexto de uma palavra, o BERT certamente se sairá melhor. O Google pode usar vários métodos para entender uma única consulta, incluindo RankBrain e BERT.

No próximo capítulo veremos um pouco dos detalhes técnicos do BERT. Até lá.

Referências:

Formação Processamento de Linguagem Natural 4.0

A Primer in BERTology: What we know about how BERT works
Deep Learning Book

Capítulo 70 – Deep Q-Network e Processos de Decisão de Markov

by

Nos capítulos anteriores estudamos os fundamentos do Aprendizado Por Reforço. Agora podemos subir mais alguns degraus e estudar uma evolução dessa fascinante técnica de aprendizado de máquina que une Deep Learning e Aprendizado Por Reforço: Deep Q-Network.

Acompanhe a leitura com atenção e na próxima aula traremos uma aplicação prática desse modelo de aprendizagem profunda.

O Processo

O processo de Q-Learning cria uma matriz (tabela) exata para o agente a qual ele “consulta” para maximizar sua recompensa a longo prazo durante seu aprendizado. Embora essa abordagem não seja errada por si só, é prática apenas para ambientes muito pequenos e rapidamente perde a viabilidade quando o número de estados e ações no ambiente aumenta.

A solução para o problema acima vem da constatação de que os valores na matriz têm apenas importância relativa, ou seja, os valores têm importância apenas em relação aos outros valores. Assim, esse pensamento nos leva a Deep Q-Network, que usa uma rede neural profunda para aproximar os valores. Essa aproximação de valores não prejudica desde que a importância relativa seja preservada. Ou seja, substituímos a Q Table no processo Q-Learning por um modelo de Deep Learning para o aprendizado dos valores Q. Por isso Deep Q-Network também é chamada de Deep Q-Learning.

A etapa básica de trabalho da Deep Q-Network é que o estado inicial seja alimentado na rede neural e retorne o valor Q de todas as ações possíveis, como na saída.

A diferença entre Q-Learning e Deep Q-Learning pode ser ilustrada da seguinte maneira:

 

Q-learning Usa uma Q Matrix (ou Q Table)

q-learning

 

Deep Q-learning substitui a Q Matrix por uma rede neural profunda (Deep Learning):

Deep-Q-Learning

 

Vamos compreender as vantagens em usar Dep Q-Learning.

Definindo o Mundo

Uma tarefa de aprendizado por reforço é o treinamento de um agente que interage com seu ambiente. O agente faz a transição entre diferentes cenários do ambiente, chamados de estados, executando ações. As ações, em troca, geram recompensas, que podem ser positivas, negativas ou zero. O único objetivo do agente é maximizar a recompensa total que ele recebe sobre um episódio, que é tudo o que acontece entre um estado inicial e um estado terminal. Por isso, reforçamos o agente para executar determinadas ações, oferecendo-lhe recompensas positivas, e afastando-o de outras ações, fornecendo-lhe recompensas negativas. É assim que um agente aprende a desenvolver uma estratégia ou política.

Tome o Super Mario como exemplo: Mario é o agente que interage com o mundo (o ambiente). Os estados são exatamente o que vemos na tela, e um episódio é um nível: o estado inicial é como o nível começa e o estado terminal é como o nível termina, se o concluímos ou perecemos enquanto tentávamos. As ações avançam, retrocedem, saltam, etc. As recompensas são dadas dependendo do resultado das ações: quando Mario coleta moedas ou bônus, recebe uma recompensa positiva e, quando cai ou é atingido por um inimigo, recebe uma recompensa negativa . Quando Mario apenas anda em frente, a recompensa que recebe é zero, como se dissesse “você não fez nada de especial”.

 

super-mario

 

Mas há um problema aqui: para poder receber recompensas, algumas ações “não especiais” são necessárias – é preciso caminhar em direção às moedas antes de poder recebê-las. Portanto, um agente deve aprender a lidar com recompensas adiadas, aprendendo a vinculá-las às ações que realmente as causaram. Essa é a característica mais fascinante no aprendizado por reforço.

Processos de Decisão de Markov

Cada estado em que o agente se encontra é uma conseqüência direta do estado anterior e da ação escolhida. O estado anterior também é uma consequência direta do que veio antes dele, e assim por diante até chegarmos ao estado terminal. Cada uma dessas etapas, e sua ordem, retém informações sobre o estado atual – e, portanto, têm efeito direto sobre qual ação o agente deve escolher a seguir. Mas há um problema óbvio aqui: quanto mais avançamos, mais informações o agente precisa para salvar e processar a cada passo necessário. Isso pode facilmente chegar ao ponto em que é simplesmente inviável realizar cálculos.

Para resolver isso, assumimos que todos os estados são Markov; isto é – assumimos que qualquer estado depende unicamente do estado que veio antes dele e da transição desse estado para o atual (a ação executada e a recompensa dada). Vamos ver um exemplo – veja estes dois jogos Tic Tac Toe (o famoso Jogo da Velha):

 

tic

 

Ambos os jogos atingiram o mesmo estado, mas de maneiras diferentes. Ainda assim, em ambos os casos, o jogador azul deve capturar a célula superior direita, ou ele perderá. Tudo o que precisávamos para determinar esse estado era o último estado, nada mais.

É importante lembrar que, ao usar a suposição de Markov, dados estão sendo perdidos – em jogos complexos como Chess ou Go, a ordem dos movimentos pode ter algumas informações implícitas sobre a estratégia ou o modo de pensar do oponente. Ainda assim, a suposição de Markov é fundamental quando se tenta calcular estratégias de longo prazo.

A Equação de Bellman

Vamos seguir em frente e desenvolver nossa primeira estratégia. Considere o caso mais simples: suponha que já sabemos qual é a recompensa esperada para cada ação em cada etapa. Como vamos escolher uma ação neste caso? Simplesmente – escolheremos a sequência de ações que eventualmente gerará a maior recompensa. Essa recompensa cumulativa que receberemos é frequentemente referida como Q Value (uma abreviação de Quality Value) e podemos formalizar matematicamente nossa estratégia como:

bellman

A equação acima afirma que o valor Q produzido por estar no estado s e selecionar a ação a é a recompensa imediata recebida, r (s, a), mais o valor Q mais alto possível do estado s’ (que é o estado em que chegamos depois de executar a ação a dos estados). Receberemos o valor Q mais alto de s’ escolhendo a ação que maximiza o valor Q. Também apresentamos γ, geralmente chamado de fator de desconto, que controla a importância das recompensas de longo prazo versus as imediatas.

Essa equação é conhecida como Equação de Bellman, e sua página na Wikipedia fornece uma explicação abrangente de sua derivação matemática. Essa equação elegante é bastante poderosa e é muito útil devido a duas características importantes:

  1. Enquanto ainda mantemos as suposições dos estados de Markov, a natureza recursiva da Equação de Bellman permite que as recompensas dos estados futuros se propaguem para estados passados ​​longínquos.
  2. Não é necessário saber realmente quais são os verdadeiros valores Q quando começamos; Desde a sua recursividade, podemos adivinhar algo, e eventualmente convergirá para os valores reais.

Q-Learning

Agora temos uma estratégia básica – em qualquer estado, execute a ação que acabará gerando a maior recompensa acumulada. Algoritmos como esse são chamados gananciosos, por uma razão óbvia.

Como implementaríamos isso para resolver os desafios da vida real? Uma maneira é desenhar uma tabela para armazenar todas as combinações possíveis de ação e estado e usá-la para salvar os valores Q. Podemos então atualizá-la usando a Equação de Bellman como regra de atualização:

 

q-learning-table

Mas nosso algoritmo ganancioso tem um problema sério: se você continuar selecionando as mesmas melhores ações, nunca tentará algo novo e poderá perder uma abordagem mais gratificante só porque nunca tentou (exatamente como acontece com nós seres humanos).

Para resolver isso, usamos uma abordagem ε-gananciosa: para alguns 0 < ε <1, escolhemos a ação gananciosa (usando nossa tabela) com uma probabilidade p = 1-ε, ou uma ação aleatória com probabilidade p = ε. Assim, damos ao agente a chance de explorar novas oportunidades.

Esse algoritmo é conhecido como Q-Learning e resume o que estudamos nos capítulos anteriores.

Deep Q-Networks

O que acontece quando o número de estados e ações se torna muito grande? Na verdade, isso não é tão raro – mesmo um jogo simples como o Tic Tac Toe tem centenas de estados diferentes (tente calcular isso) e não se esqueça de multiplicarmos esse número por 9, que é o número de ações possíveis. Então, como vamos resolver problemas realmente complexos?

Uma solução possível é a Deep Q-Network! Combinamos Q Learning e Deep Learning, o que gera Deep Q-Networks. A ideia é simples: substituiremos a tabela de valores Q por uma rede neural que tente aproximar os valores Q. É geralmente referido como o aproximador ou a função de aproximação e indicado como Q (s, a; θ), em que θ representa os pesos treináveis da rede.

Agora, só faz sentido usar a Equação de Bellman como a função de custo – mas o que exatamente minimizaremos? Vamos dar uma outra olhada:

form1

O sinal “=” marca a atribuição, mas existe alguma condição que também satisfaça uma igualdade? Bem, sim – quando o valor Q atingiu seu valor convergente e final. E esse é exatamente o nosso objetivo – para minimizar a diferença entre o lado esquerdo e o lado direito – e bingo!

 

cost

 

Isso parece familiar? Provavelmente – é a função erro do quadrado médio (função usada em regressão linear, um dos modelos mais básicos em Machine Learning), onde o valor Q atual é a previsão (y) e as recompensas imediatas e futuras são o destino (y’):

 

mse

 

É por isso que Q (s’, a; θ) é geralmente referido como Q-target (a variável target em modelo tradicional de Machine Learning).

Seguindo em frente, precisamos treinar a rede. No Aprendizado por Reforço, o conjunto de treinamento é criado à medida que avançamos; pedimos ao agente para tentar selecionar a melhor ação usando a rede atual – e registramos o estado, a ação, a recompensa e o próximo estado em que ele terminou. Decidimos o tamanho de um lote b e, toda vez que novos registros de b foram gravados, selecionamos b registros aleatoriamente (!!) na memória e treinamos a rede. Os buffers de memória usados geralmente são chamados de Experience Replay. Existem vários tipos de tais memórias – uma muito comum é um buffer de memória cíclico. Isso garante que o agente continue treinando sobre seu novo comportamento, em vez de coisas que podem não ser mais relevantes.

As coisas estão ficando reais, então vamos falar sobre arquitetura: se imitar uma tabela, a rede deve receber como entrada o estado e a ação e deve gerar um valor Q:

 

magic

 

Embora correta, essa arquitetura é muito ineficiente do ponto de vista técnico. Observe que a função de custo requer o valor Q máximo futuro, portanto, precisaremos de várias previsões de rede para um único cálculo de custo. Então, em vez disso, podemos usar a seguinte arquitetura:

 

black

 

Aqui, fornecemos à rede apenas os estados como entrada e recebemos valores Q para todas as ações possíveis de uma só vez. Muito melhor. E é praticamente isso. Parabéns! Você acabou de aprender sobre um dos modelos mais avançados em Inteligência Artificial. Se vem acompanhando o livro capítulo a capítulo até aqui não deve ter sido tão difícil. 

Antes de encerrarmos, aqui está algo extra:

Alguns parágrafos atrás, comparamos a função de custo da Deep Q-Network com o erro quadrado médio. Mas o MSE compara as previsões y aos rótulos verdadeiros y’- e os rótulos verdadeiros são constantes durante todo o procedimento de treinamento. Obviamente, esse não é o caso na Deep Q-Network: y e y’ são previstos pela própria rede e, portanto, podem variar a cada iteração. O impacto é claro.

Por isso usamos a Double Deep Q-Network, que usa rótulos semi-constantes durante o treinamento. Mantemos duas cópias da rede Q, mas apenas uma está sendo atualizada – a outra permanece imóvel. De vez em quando, porém, substituímos a rede constante por uma cópia da Q Network treinada, daí a razão pela qual chamamos de “semi-constante”. 

Aqui na Data Science Academy acreditamos que a melhor maneira de entender novos conceitos é praticando e por isso nossos cursos tem um perfil prático, com projetos voltados ao mercado de trabalho e ao que as empresas precisam!

Na próxima traremos um exemplo prático da Deep Q-Network. Até lá.

Referências:

Customizando Redes Neurais com Funções de Ativação Alternativas

A Beginner’s Guide to Deep Reinforcement Learning

Reinforcement Learning: What is, Algorithms, Applications, Example

What is reinforcement learning? The complete guide

Qrash Course: Reinforcement Learning 101 & Deep Q Networks in 10 Minutes

Reinforcement Learning algorithms — an intuitive overview

Reinforcement learning

Reinforcement Learning, Second Edition

Applications of Reinforcement Learning in Real World

Practical Recommendations for Gradient-Based Training of Deep Architectures

Gradient-Based Learning Applied to Document Recognition

Neural Networks & The Backpropagation Algorithm, Explained

Recurrent neural network based language model

The Elements of Statistical Learning: Data Mining, Inference, and Prediction, Second Edition

Gradient Descent For Machine Learning

Pattern Recognition and Machine Learning

Deep Learning Book

Capítulo 68 – Algoritmo de Agente Baseado em IA com Reinforcement Learning – Q-Learning

by

O Q-learning é um algoritmo de Aprendizado Por Reforço que busca encontrar a melhor ação a ser tomada, dado o estado atual. É considerado off-policy porque a função q-learning aprende com ações que estão fora da política atual, como executar ações aleatórias. Mais especificamente, o q-learning busca aprender uma política que maximize a recompensa total.

O ‘q’ no q-learning significa qualidade. A qualidade, neste caso, representa a utilidade de uma determinada ação para obter alguma recompensa futura.

Esse é um dos algoritmos mais usados em Aprendizado Por Reforço, o qual vamos estudar agora. É amplamente empregado em robótica, automação de games e robôs investidores na bolsa de valores.

Algoritmo Q-Learning

Q-learning é um algoritmo de aprendizado baseado em valor. Os algoritmos baseados em valor atualizam a função de valor com base em uma equação (particularmente a equação de Bellman). Enquanto o outro tipo, baseado em políticas, estima a função de valor com uma política gananciosa obtida a partir do último aprimoramento da política.

Funções de valor são funções de par de ação de estado que estimam quão boa será uma ação específica em um determinado estado ou qual o retorno esperado para essa ação.

O Q-Learning é um algoritmo off-policy (pode atualizar as funções de valor estimado usando ações hipotéticas, aquelas que ainda não foram tentadas) para o aprendizado da diferença temporal (método para estimar as funções de valor). Pode-se provar que, com treinamento suficiente, o Q-learning converge com a probabilidade 1 para uma aproximação da função de valor da ação para uma política de destino arbitrária. O Q-Learning aprende a política ideal, mesmo quando as ações são selecionadas de acordo com uma política mais exploratória ou até aleatória. O Q-learning pode ser implementado da seguinte maneira:

q-learning

Onde:

  • s: é o estado anterior.
  • a: é a ação anterior.
  • Q (): é o algoritmo Q-learning.
  • s ‘: é o estado atual.
  • alfa: é a taxa de aprendizado, definida geralmente entre 0 e 1. A configuração para 0 significa que os valores Q nunca são atualizados; portanto, nada é aprendido. Definir alfa como um valor alto, como 0,9, significa que o aprendizado pode ocorrer rapidamente.
  • gama: é o fator de desconto, também definido entre 0 e 1. Isso modela o fato de que recompensas futuras valem menos que recompensas imediatas.
  • max: é a recompensa máxima que é alcançável no estado após o atual (a recompensa por executar a ação ideal posteriormente).

O algoritmo pode ser interpretado como:

  • Inicialize a tabela de valores Q, Q (s, a).
  • Observe o estado atual, s.
  • Escolha uma ação, a, para esse estado com base na política de seleção.
  • Tome a ação e observe a recompensa, r, bem como o novo estado, s ‘.
  • Atualize o valor Q para o estado usando a recompensa observada e a recompensa máxima possível para o próximo estado.
  • Defina o estado para o novo estado e repita o processo até que um estado terminal seja alcançado.

Aqui o algoritmo:

algoritmo

A próxima etapa para o agente é interagir com o ambiente e fazer atualizações nos pares de estado e ação em nossa tabela q: Q [estado, ação].

Explore ou Exploit

Um agente interage com o ambiente com 1 de 2 maneiras possíveis.

A primeira é usar a tabela q como referência e visualizar todas as ações possíveis para um determinado estado. O agente seleciona a ação com base no valor máximo dessas ações. Isso é conhecido como Exploit, pois usamos as informações que temos disponíveis para tomar uma decisão.

A segunda maneira é agir aleatoriamente. Isso é chamado de Explore. Em vez de selecionar ações com base na recompensa máxima futura, selecionamos uma ação aleatoriamente. A ação aleatória é importante porque permite ao agente explorar e descobrir novos estados que, de outra forma, não podem ser selecionados durante o processo de Exploit. Você pode equilibrar a Exploit / Explore usando o parâmetro épsilon (ε) e definindo o valor de quantas vezes deseja fazer Exploit versus Explore. 

Quer criar um agente inteligente baseado em Aprendizado Por Reforço usando o algoritmo Q-Learning em Python?

Então não perca o próximo capítulo.

Referências:

Formação Linguagem Python Para Data Science 4.0

Customizando Redes Neurais com Funções de Ativação Alternativas

A Beginner’s Guide to Deep Reinforcement Learning

Reinforcement Learning: What is, Algorithms, Applications, Example

What is reinforcement learning? The complete guide

Reinforcement Learning algorithms — an intuitive overview

Reinforcement learning

Reinforcement Learning, Second Edition

Applications of Reinforcement Learning in Real World

Practical Recommendations for Gradient-Based Training of Deep Architectures

Gradient-Based Learning Applied to Document Recognition

Neural Networks & The Backpropagation Algorithm, Explained

Recurrent neural network based language model

The Elements of Statistical Learning: Data Mining, Inference, and Prediction, Second Edition

Gradient Descent For Machine Learning

Pattern Recognition and Machine Learning

Deep Learning Book

Capítulo 67 – Algoritmo de Agente Baseado em IA com Reinforcement Learning – Parte 2

by

Vamos continuar nosso estudo sobre o Algoritmo de Agente Baseado em IA com Reinforcement Learning e compreender mais alguns detalhes importantes.

Aprendizado por Reforço (Reinforcement Learning) refere-se a um tipo de método de Aprendizado de Máquina (Machine Learning) no qual o agente recebe uma recompensa atrasada na próxima etapa para avaliar sua ação anterior. Recentemente, à medida que o algoritmo evolui com a combinação de redes neurais artificiais, ele é capaz de resolver tarefas mais complexas.

Embora exista um grande número de algoritmos de Aprendizado por Reforço, não parece haver uma comparação abrangente entre cada um deles. Quais algoritmos seriam aplicados a uma tarefa específica? Este capítulo tem como objetivo resolver esse problema discutindo brevemente a configuração da Aprendizado por Reforço e fornecendo uma introdução para alguns dos algoritmos conhecidos.

Componentes do Aprendizado Por Reforço

Vamos relembrar os componentes do Aprendizado Por Reforço:

Ação (A): Todos os movimentos possíveis que o agente pode executar

Estado (S): Situação atual retornada pelo ambiente.

Recompensa (R): Um retorno imediato é enviado do ambiente para avaliar a última ação.

Política (π): A estratégia que o agente emprega para determinar a próxima ação com base no estado atual.

Valor (V): O retorno esperado a longo prazo com desconto, em oposição à recompensa de curto prazo R. Vπ (s) é definido como o retorno esperado a longo prazo da atual política de desdobramento de estados π.

Valor Q ou Valor da Ação (Q): O valor Q é semelhante ao Valor, exceto pelo fato de ser necessário um parâmetro extra, a ação atual a. Qπ (s, a) refere-se ao retorno a longo prazo do estado atual s, realizando uma ação sob a política π.

Agora vamos compreender quais são os principais tipos de algoritmos usados em Aprendizado Por Reforço.

Model-free v.s. Model-based

O modelo representa a simulação da dinâmica do ambiente. Ou seja, o modelo aprende a probabilidade de transição T (s1 | (s0, a)) a partir do par de estado atual s0 e a ação a para o próximo estado s1. Se a probabilidade de transição for detectada com êxito, o agente saberá qual a probabilidade de entrar em um estado específico, dado o estado e a ação atuais.

No entanto, algoritmos baseados em modelo se tornam impraticáveis à medida que o espaço de estado e o espaço de ação aumentam (S * S * A, para uma configuração tabular).

Por outro lado, algoritmos sem modelo (Model-free) dependem de tentativa e erro para atualizar seu conhecimento. Como resultado, ele não requer espaço para armazenar toda a combinação de estados e ações. Em geral, os algoritmos desse tipo obtém resultados bem superiores, mas são mais complexos de programar.

On-policy v.s. Off-policy

Um agente on-policy aprende o valor com base em sua ação atual a derivada da política atual, enquanto sua contraparte off-policy o aprende com base na ação a* obtida de outra política. No Q-learning, essa política é a política gananciosa (falaremos mais sobre isso em Q-learning).

Abordagens Para Um Algoritmo de Aprendizado por Reforço

Existem três abordagens para implementar um algoritmo de Aprendizado por Reforço (Reinforcement Learning):

Baseado em Valor

Em um método de Aprendizado por Reforço baseado em valor, você deve tentar maximizar uma função de valor V (s). Nesse método, o agente espera um retorno a longo prazo dos estados atuais sob a política π.

Baseado em Políticas

Em um método de Aprendizado por Reforço baseado em política, você tenta criar uma política em que a ação executada em todos os estados ajude o agente a obter a recompensa máxima no futuro. Esse método tem dois sub-tipos:

  • Determinístico: Para qualquer estado, a mesma ação é produzida pela política π.
  • Estocástico: Toda ação tem uma certa probabilidade.

Baseado em Modelo

Neste método de Aprendizado por Reforço, você precisa criar um modelo virtual para cada ambiente. O agente aprende a atuar nesse ambiente específico.

Modelos de Aprendizagem de Reforço

Além escolher uma ou mais das abordagens acima mencionadas, precisamos definir o modelo de Aprendizado por Reforço. Existem três modelos de aprendizagem importantes amplamente usados em Reinforcement Learning:

Processo de Decisão de Markov

Nesse processo de aprendizagem do agente, os seguintes parâmetros são usados para obter uma solução:

  • Conjunto de ações – A
  • Conjunto de estados – S
  • Recompensa – R
  • Política – n
  • Valor – V

A abordagem matemática para mapear uma solução no Aprendizado por Reforço é reconhecido como um Processo de Decisão de Markov (MDP).

MDP

Q-Learning

Q-Learning é um método baseado em valor de fornecer informações para informar qual ação um agente deve executar.

Vamos entender esse método pelo seguinte exemplo simples, no qual o agente inteligente deve aprender a chegar à porta de saída (porta 5):

  • Há cinco quartos em um prédio que são conectados por portas.
  • Cada quarto é numerado de 0 a 4.
  • A parte externa do edifício pode ser uma grande área externa (5).
  • As portas número 1 e 4 levam ao prédio a partir da sala 5.

q-learning

Em seguida, você precisa associar um valor de recompensa a cada porta:

  1. As portas que levam diretamente ao objetivo recebem uma recompensa de 100.
  2. Portas que não estão diretamente conectadas à sala de destino não oferecem recompensa.
  3. Como as portas são de mão dupla, duas setas são atribuídas para cada quarto.
  4. Cada seta na imagem acima contém um valor de recompensa instantâneo.

Explicação:

Nesta imagem, você pode ver que a sala representa um estado. O movimento do agente de uma sala para outra representa uma ação. Um estado é descrito como um nó, enquanto as setas mostram a ação.

q-learning2

Por exemplo, um agente deve aprender a passar da porta 2 a porta 5. Aqui seriam as opções:

Estado inicial = estado 2
Estado 2-> estado 3
Estado 3 -> estado (2,1,4)
Estado 4-> estado (0,5,3)
Estado 1-> estado (5,3)
Estado 0-> estado 4

Como são várias possibilidades, nosso algoritmo deve recompensar aquelas que levam ao destino da forma mais rápida e penalizar aquelas que não levam. O agente então vai experimentando as possibilidades e criando uma tabela com o que traz recompensa e o que não traz. Se o aprendizado for bem sucedido o agente aprenderá o melhor conjunto de ações que leva ao destino.

Deep Q Network (DQN)

Embora o Q-learning seja um algoritmo muito poderoso, sua principal fraqueza é a falta de generalidade. Se você visualizar o Q-learning como números de atualização em uma matriz bidimensional (Espaço de Ação * Espaço de Estado), ele se parecerá com a programação dinâmica. Isso indica que, para os estados que o agente de Q-learning não viu antes, não tem ideia de qual ação executar. Em outras palavras, o agente de Q-learning não tem a capacidade de estimar valor para estados invisíveis. Para lidar com esse problema, o DQN se livra da matriz bidimensional introduzindo a Rede Neural Artificial Profunda (Deep Learning).

O DQN utiliza uma rede neural para estimar a função de valor Q. A entrada para a rede é a corrente, enquanto a saída é o valor Q correspondente a cada ação.

atari

Em 2013, o DeepMind aplicou o DQN ao jogo Atari, conforme ilustrado na figura acima. A entrada é a imagem bruta da situação atual do jogo, que passa por várias camadas, incluindo a camada convolucional e a camada totalmente conectada. A saída é o valor Q para cada uma das ações que o agente pode executar.

Deep Deterministic Policy Gradient (DDPG)

Embora o DQN tenha alcançado grande sucesso em problemas dimensionais mais altos, como o jogo Atari, o espaço de ação ainda é discreto. No entanto, muitas tarefas de interesse, especialmente tarefas de controle físico, o espaço de ação é contínuo. Se você discretizar muito bem o espaço de ação, acabará tendo um espaço de ação muito grande. Por exemplo, suponha que o grau de sistema aleatório livre seja 10. Para cada grau, você divide o espaço em 4 partes. Você acaba tendo 4¹⁰ = 1048576 ações. Também é extremamente difícil convergir para um espaço de ação tão grande.

O DDPG conta com a arquitetura ator-crítico com dois elementos de mesmo nome, ator e crítico. Um ator é usado para ajustar o parâmetro ? para a função de política, ou seja, decidir a melhor ação para um estado específico.

O DDPG também empresta as ideias de repetição da experiência e separação da rede de destino, do DQN. Um problema do DDPG é que ele raramente realiza exploração de ações. Uma solução para isso é adicionar ruído no espaço de parâmetros ou no espaço de ação.

Como o objetivo deste livro é abordar principalmente Deep Learning, nos próximos capítulos estudaremos os algoritmos que empregam Deep Learning, o que chamamos de Deep Reinforcement Learning, talvez um dos métodos de IA mais avançados da atualidade. O método jé é estudado na DSA no curso de Engenharia Financeira com Inteligência Artificial, para otimização de portfólios financeiros e robôs investidores baseados em IA.

Estudar e aprender tem um efeito interessante: nos fazem mais humildes. Quando uma pessoa é muito arrogante, ela em geral é vazia em termos de conteúdo, pois arrogância é sinal de ignorância, falta de conhecimento. Quanto mais aprendemos, mais humildes ficamos, pois percebemos que o aprendizado é um ato contínuo e que temos muito, muito a aprender. Passamos a ver o mundo com outros olhos. Por isso estudar é transformador.

Até o próximo capítulo.

Referências:

Customizando Redes Neurais com Funções de Ativação Alternativas

A Beginner’s Guide to Deep Reinforcement Learning

Reinforcement Learning: What is, Algorithms, Applications, Example

What is reinforcement learning? The complete guide

Reinforcement Learning algorithms — an intuitive overview

Reinforcement learning

Reinforcement Learning, Second Edition

Applications of Reinforcement Learning in Real World

Practical Recommendations for Gradient-Based Training of Deep Architectures

Gradient-Based Learning Applied to Document Recognition

Neural Networks & The Backpropagation Algorithm, Explained

Recurrent neural network based language model

The Elements of Statistical Learning: Data Mining, Inference, and Prediction, Second Edition

Gradient Descent For Machine Learning

Pattern Recognition and Machine Learning

Deep Learning Book

Capítulo 66 – Algoritmo de Agente Baseado em IA com Reinforcement Learning – Parte 1

by

O Aprendizado por Reforço (Reinforcement Learning) é uma abordagem computacional para entender e automatizar aprendizado direcionado e tomada de decisão. Distingue-se de outras abordagens por sua ênfase na aprendizagem de um agente a partir da interação direta com seu ambiente, sem exigir supervisão ou modelos completos do ambiente.

Em nossa opinião, a Aprendizado por Reforço é o primeiro campo a abordar seriamente problemas computacionais que surgem ao aprender com a interação com um ambiente para atingir objetivos de longo prazo. É um dos métodos mais avançados em Inteligência Artificial.

O Aprendizado por Reforço usa a estrutura formal dos Processos de Decisão de Markov para definir a interação entre um agente de aprendizagem e seu ambiente em termos de estados, ações e recompensas. Essa estrutura pretende ser uma maneira simples de representar características essenciais do problema de Inteligência Artificial. Esses recursos incluem uma sensação de causa e efeito, um senso de incerteza e não-determinismo, e a existência de metas explícitas. Algo muito similar ao aprendizado humano.

Os conceitos de valor e função de valor são essenciais para a maior parte dos métodos de Aprendizado por Reforço. Assumimos a posição de que a função de valor é importante para a pesquisa eficiente no espaço das políticas. O uso de funções de valor distingue os métodos de Aprendizado por Reforço dos métodos evolutivos que buscam diretamente no espaço de políticas orientado por avaliações de políticas inteiras.

Para ilustrar a ideia geral do Aprendizado por Reforço, vamos descrever um algoritmo (conjunto de etapas) que implementa o que seria um agente baseado em IA usando essa técnica. Passaremos por cada detalhe do algoritmo, inclusive alguns detalhes matemáticos. A construção de um algoritmo é a primeira parte do trabalho de construção de uma aplicação de Inteligência Artificial. Vamos usar o contexto de um jogo, para que o exemplo fique ainda mais claro. Mas o processo descrito aqui não é muito diferente de um agente de carro autônomo, por exemplo.

Muitos se preocupam apenas com a programação, sem compreender que existe um raciocínio lógico antes disso, que determina como deve ser a solução a ser implementada. Esse raciocínio lógico pode ser descrito em etapas, o que chamados de algoritmo. Isso é o que queremos praticar com você agora.

Algoritmo de um Agente Baseado em IA

Considere o famoso Jogo da Velha.

jogo_da_velha

Dois jogadores revezam-se jogando em um tabuleiro de três por três, conforme a figura acima. Um jogador joga Xs e o outro OS até que um jogador ganhe colocando três marcas em uma fileira, horizontal, vertical ou diagonal. Se o tabuleiro estiver totalmente preenchido e nenhum jogador conseguir três marcas, então o jogo fica empatado.

Vamos assumir que estamos jogando contra um jogador imperfeito, aquele cujo jogo às vezes está incorreto e nos permite vencer. Por enquanto, consideremos empates e perdas igualmente ruins para nós. Como podemos construir um agente de IA que encontrará as imperfeições no jogo de seu oponente e aprenderá a maximizar sua chances de ganhar?

Embora este seja um problema simples, ele não pode ser facilmente resolvido de maneira satisfatória. através de técnicas clássicas. Por exemplo, a solução clássica “minimax” da Teoria dos Jogos, não seria ideal aqui porque assume uma maneira particular de jogar pelo oponente. Por exemplo, um jogador minimax nunca chegaria a um estado de jogo do qual poderia perder, mesmo que, de fato, sempre tenha saído desse estado por causa de jogo incorreto do oponente.

Métodos clássicos de otimização para problemas de decisão sequenciais, como programação dinâmica, poderiam calcular uma solução ideal para qualquer oponente, mas exige como entrada, uma especificação completa desse oponente, incluindo as probabilidades com as quais o oponente faz cada jogada em cada estado do tabuleiro. Vamos supor que esta informação não está disponível a priori para esse problema, pois não existe para a grande maioria dos problemas de interesse prático.

Por outro lado, essas informações podem ser estimadas a partir da experiência, neste caso, jogando muitos jogos contra o oponente. O melhor que se pode fazer sobre esse problema é primeiro aprender um modelo do comportamento do oponente, até algum nível de confiança e, em seguida, aplicar a programação dinâmica para calcular uma solução ideal o modelo aproximado do oponente. No final, isso não é tão diferente de alguns dos métodos de aprendizado por reforço.

Um método evolutivo aplicado a esse problema pesquisaria diretamente o espaço de políticas possíveis para alguém com alta probabilidade de vencer o oponente. Aqui, uma política é uma regra que informa ao agente o que fazer para cada estado do jogo – todas as configurações possíveis de Xs e Os no tabuleiro três por três. Para cada política considerada, uma estimativa de sua probabilidade de vitória seria obtida jogando algum número de jogos contra o oponente. Essa avaliação direcionaria então quais políticas devem ser consideradas a seguir.

Um método evolutivo típico escalaria no espaço de políticas, gerando e avaliando sucessivamente políticas na tentativa de obter melhorias incrementais. Ou, talvez, um algoritmo genético possa ser usado para manter e avaliar uma população de políticas. Literalmente centenas de diferentes métodos de otimização podem ser aplicados.

Uma função de valor poderia ser um método eficaz nesse cenário. Aqui está como o problema do jogo da velha seria tratado com um método que faz uso de uma função de valor.

Primeiro, montaríamos uma tabela de números, uma para cada estado possível do jogo. Cada número será a estimativa mais recente da probabilidade de ganharmos nesse estado. Tratamos essa estimativa como o valor do estado, e toda a tabela é a função de valor aprendido. O estado A tem um valor mais alto que o estado B ou é considerado “melhor” do que o estado B, se a estimativa atual da probabilidade de ganharmos em A for maior do que em B. Supondo que sempre jogamos Xs, então para todos os estados com três Xs seguidos a probabilidade de ganhar é 1, porque já vencemos.

Da mesma forma, para todos os estados com três Os seguidos ou preenchidos, a probabilidade correta é 0, pois não podemos ganhar com eles. Definimos os valores iniciais de todos os outros estados para 0,5, representando que temos 50% de chance de ganhar.

Em seguida, jogamos muitos jogos contra o oponente. Para selecionar nossos movimentos, examinamos os estados que resultariam de cada um de nossos movimentos possíveis (um para cada espaço em branco no tabuleiro) e procuramos seus valores atuais na tabela. Na maioria das vezes nos movemos avidamente, selecionando o movimento que leva ao estado com maior valor, ou seja, com o maior valor de probabilidade estimada de vitória. Ocasionalmente, no entanto, selecionamos um movimento aleatoriamente. Estes são chamados movimentos exploratórios porque nos levam a ter uma experiência que, de outra forma, jamais poderíamos ter. Uma sequência de movimentos feitos e considerados durante um jogo podem ser diagramados como na figura abaixo:

agente

Poderíamos usar Deep Learning para realizar parte do aprendizado do processo descrito anteriormente e alimentar o agente com o resultado desse aprendizado. Isso é o que chamamos de Deep Reinforcement Learning.

Reinforcement Learning e Deep Reinforcement Learning são estudados na prática em Engenharia Financeira com Inteligência Artificial. Muitos robôs investidores usam Aprendizado por Reforço como técnica principal.

Continuaremos na parte 2. Acompanhe o próximo capítulo.

Referências:

Customizando Redes Neurais com Funções de Ativação Alternativas

A Beginner’s Guide to Deep Reinforcement Learning

What is reinforcement learning? The complete guide

Reinforcement learning

Reinforcement Learning, Second Edition

Applications of Reinforcement Learning in Real World

Practical Recommendations for Gradient-Based Training of Deep Architectures

Gradient-Based Learning Applied to Document Recognition

Neural Networks & The Backpropagation Algorithm, Explained

Recurrent neural network based language model

The Elements of Statistical Learning: Data Mining, Inference, and Prediction, Second Edition

Gradient Descent For Machine Learning

Pattern Recognition and Machine Learning

Deep Learning Book

Capítulo 64 – Componentes do Aprendizado Por Reforço (Reinforcement Learning)

by

O Aprendizado Por Reforço pode ser entendido através de seus componentes: agente, ambiente, estados, ações e recompensas, todos os quais explicaremos neste capítulo. Letras maiúsculas indicarão conjuntos de objetos e letras minúsculas indicarão um objeto específico. Por exemplo: A são todas as ações possíveis, enquanto a é uma ação específica contida no conjunto.

Agente: um agente executa ações; por exemplo, um drone fazendo uma entrega ou Super Mario navegando em um videogame. O algoritmo é o agente. Pode ser útil considerar que, na vida, o agente é você.

Ação (A): A é o conjunto de todos os movimentos possíveis que o agente pode fazer. Uma ação é quase autoexplicativa, mas deve-se notar que os agentes geralmente escolhem de uma lista de ações possíveis e discretas. Nos videogames, a lista pode incluir correr para a direita ou para a esquerda, pular alto ou baixo, agachar-se ou ficar parado. Nos mercados de ações, a lista pode incluir a compra, venda ou manutenção de qualquer um de uma matriz de ativos financeiros e seus derivativos. Ao lidar com drones, as alternativas incluiriam muitas velocidades e acelerações diferentes no espaço 3D.

Fator de desconto: O fator de desconto é multiplicado por recompensas futuras, conforme descoberto pelo agente, a fim de amortecer o efeito dessas recompensas na escolha de ação do agente. Por quê? Ele foi projetado para fazer com que as recompensas futuras valham menos que as recompensas imediatas; isto é, aplica um tipo de hedonismo de curto prazo no agente. Geralmente expressa com a letra grega minúscula gama: γ. Se γ for 0,8 e houver uma recompensa de 10 pontos após três etapas de tempo, o valor atual dessa recompensa será de 0,8³ x 10. Um fator de desconto de 1 faria as recompensas futuras valerem tanto quanto as recompensas imediatas. Estamos lutando contra o imediatismo aqui. O agente deve escolher as ações que levam à melhor solução global possível, não apenas a melhor solução imediata.

Ambiente: O mundo pelo qual o agente se move e que responde ao agente. O ambiente toma o estado atual e a ação do agente como entrada e retorna como saída a recompensa do agente e seu próximo estado. Se você é o agente, o ambiente pode ser as leis da física e as regras da sociedade que processam suas ações e determinam as consequências delas.

Estado (S): Um estado é uma situação concreta e imediata em que o agente se encontra; ou seja, um local e momento específico, uma configuração instantânea que coloca o agente em relação a outras coisas importantes, como ferramentas, obstáculos, inimigos ou prêmios. Pode ser a situação atual retornada pelo ambiente ou qualquer situação futura. Você já esteve no lugar errado na hora errada? Isso é um estado.

Recompensa (R): Uma recompensa é o feedback pelo qual medimos o sucesso ou o fracasso das ações de um agente em um determinado estado. Por exemplo, em um videogame, quando Mario toca uma moeda, ele ganha pontos. A partir de qualquer estado, um agente envia a saída na forma de ações para o ambiente, e o ambiente retorna o novo estado do agente (que resultou da ação no estado anterior), bem como recompensas, se houver. As recompensas podem ser imediatas ou atrasadas. Eles avaliam efetivamente a ação do agente.

Política (π): A política é a estratégia que o agente emprega para determinar a próxima ação com base no estado atual. Ele mapeia estados para ações, as ações que prometem a maior recompensa.

Valor (V): O retorno esperado a longo prazo com desconto, em oposição à recompensa de curto prazo R. Vπ (s) é definido como o retorno esperado a longo prazo do estado atual sob a política π. Descontamos as recompensas ou diminuímos seu valor estimado, quanto mais futuro elas ocorrerem. E lembre-se de Keynes: “A longo prazo, estamos todos mortos”. É por isso que você desconta recompensas futuras. É útil distinguir.

Valor Q ou Valor da Ação (Q): O valor Q é semelhante ao Valor, exceto pelo fato de ser necessário um parâmetro extra, a ação atual a. Qπ (s, a) refere-se ao retorno a longo prazo de uma ação que executa uma ação sob política π do estado atual s. Q mapeia pares de ação e estado para recompensas. Observe a diferença entre Q e política.

Trajetória: Uma sequência de estados e ações que influenciam esses estados. A vida de um agente é apenas uma bola lançada alta e arqueando-se no espaço-tempo sem ser perturbada, como os humanos no mundo moderno.

Distinções Principais: Recompensa é um sinal imediato recebido em um determinado estado, enquanto valor é a soma de todas as recompensas que você pode antecipar desse estado. Valor é uma expectativa de longo prazo, enquanto recompensa é um prazer imediato. O valor é comer salada de espinafre no jantar, antecipando uma vida longa e saudável; recompensa é comer hamburguer com batata frita e refrigerante para o jantar e comprometer sua saúde futura. Eles diferem em seus horizontes temporais. Assim, você pode ter estados em que o valor e a recompensa divergem: você pode receber uma recompensa baixa e imediata (espinafre), mesmo quando se move para uma posição com grande potencial de valor a longo prazo; ou você pode receber uma alta recompensa imediata (hamburguer com batata frita e refrigerante) que leva à diminuição das perspectivas ao longo do tempo. É por isso que a função de valor, em vez de recompensas imediatas, é o que o aprendizado por reforço procura prever e controlar.

Portanto, ambientes são funções que transformam uma ação executada no estado atual no próximo estado e uma recompensa; agentes são funções que transformam o novo estado e recompensam na próxima ação. Podemos conhecer e definir a função do agente, mas na maioria das situações em que é útil e interessante aplicar o aprendizado por reforço, não sabemos a função do ambiente. É uma caixa preta onde só vemos as entradas e saídas. É como o relacionamento da maioria das pessoas com a tecnologia: sabemos o que faz, mas não sabemos como funciona. O aprendizado por reforço representa a tentativa de um agente de aproximar a função do ambiente, para que possamos enviar ações para o ambiente de caixa preta que maximize as recompensas que ele distribui.

 

simple_RL_schema

 

No loop de feedback acima, os subscritos indicam as etapas de tempo t e t + 1, cada uma das quais se refere a estados diferentes: o estado no momento t e o estado no momento t + 1. Diferente de outras formas de aprendizado de máquina – como aprendizado supervisionado e não supervisionado – o aprendizado por reforço só pode ser pensado sequencialmente em termos de pares de ação de estado que ocorrem um após o outro.

O aprendizado por reforço julga as ações pelos resultados que elas produzem. É orientado a objetivos, e seu objetivo é aprender sequências de ações que levarão um agente a atingir seu objetivo ou maximizar sua função objetivo. aqui estão alguns exemplos:

Nos videogames, o objetivo é terminar o jogo com mais pontos, para que cada ponto adicional obtido ao longo do jogo afete o comportamento subsequente do agente; ou seja, o agente pode aprender que deve atirar em navios de guerra, tocar em moedas ou desviar de meteoros para maximizar sua pontuação.

No mundo real, o objetivo pode ser que um robô viaje do ponto A ao ponto B, e cada centímetro que o robô é capaz de se aproximar do ponto B pode ser contado como pontos. Aqui está um exemplo de uma função objetiva para o aprendizado por reforço; ou seja, a maneira como define seu objetivo:

reinforcementlearning_objective

Estamos somando a função de recompensa r sobre t, que significa etapas de tempo. Portanto, essa função objetivo calcula toda a recompensa que poderíamos obter executando, digamos, um jogo. Aqui, x é o estado em um determinado momento, a é a ação executada nesse estado e r é a função de recompensa para x e a.

Outro exemplo é a otimização de portfólios financeiros (uma das principais aplicações atuais da Aprendizagem Por Reforço). Dado o volume de movimentação de diversos ativos financeiros, o agente procura a melhor combinação possível de investimentos que garanta o maior retorno financeiro no longo prazo. Essa é a tecnologia por trás dos Robôs Investidores e que ensinamos em detalhes em Engenharia Financeira com Inteligência Artificial.

Quer aprender um pouco da Matemática desta incrível técnica de aprendizagem de máquina e como ela se relaciona com Deep Learning? Então acompanhe os próximos capítulos.

Referências:

Customizando Redes Neurais com Funções de Ativação Alternativas

A Beginner’s Guide to Deep Reinforcement Learning

What is reinforcement learning? The complete guide

Reinforcement learning

Applications of Reinforcement Learning in Real World

Practical Recommendations for Gradient-Based Training of Deep Architectures

Gradient-Based Learning Applied to Document Recognition

Neural Networks & The Backpropagation Algorithm, Explained

Recurrent neural network based language model

The Elements of Statistical Learning: Data Mining, Inference, and Prediction, Second Edition

Gradient Descent For Machine Learning

Pattern Recognition and Machine Learning

Deep Learning Book

Capítulo 63 – Aplicações da Aprendizagem Por Reforço no Mundo Real

by

Enquanto as Redes Neurais Convolucionais (CNNs) e as Redes Neurais Recorrentes (RNNs) estão se tornando cada vez mais importantes para as empresas devido às suas aplicações em Visão Computacional e Processamento de Linguagem Natural, o Aprendizado por Reforço como uma estrutura para a neurociência computacional de um modelo para o processo de tomada de decisão parece estar subvalorizado. A Aprendizagem Por Reforço não deve ser negligenciada no espaço da pesquisa corporativa, dados seus enormes potenciais em auxiliar na tomada de decisões.

O Aprendizado por Reforço (ou Reinforcement Learning – RL), conhecido como modelo de aprendizado semi-supervisionado em Machine Learning, é uma técnica para permitir que um agente tome ações e interaja com um ambiente, a fim de maximizar as recompensas totais. Aprendizado por Reforço é geralmente modelado como um Processo de Decisão de Markov (MDP) e estudaremos isso em mais detalhes nos capítulos seguintes.

Mas aqui vai uma explicação bem simples e objetiva para ajudar a compreender o conceito:

Imagine que um bebê recebe um controle remoto da TV em sua casa (ambiente). Em termos simples, o bebê (agente) primeiro observará e construirá sua própria representação do ambiente (estado). Em seguida, o bebê curioso executará certas ações, como pressionar o controle remoto (ação) e observará a resposta da TV (próximo estado). Como uma TV não responde e é monótona, o bebê não gosta dela (recebendo uma recompensa negativa) e realiza menos ações que levarão a esse resultado (atualização da política) e vice-versa. O bebê repetirá o processo até encontrar uma política (o que fazer em diferentes circunstâncias) com a qual está satisfeito (maximizar as recompensas totais (com desconto)). Isso é o Aprendizado por Reforço. Lembre-se que em Machine Learning o que fazemos é tentar reproduzir o aprendizado humano através de diferentes técnicas.

O estudo da Aprendizagem Por Reforço é construir uma estrutura matemática para resolver problemas. Por exemplo, para encontrar uma boa política, poderíamos usar métodos baseados em valores, como Q-learning, para medir o quão boa é uma ação em um determinado estado ou métodos baseados em políticas para descobrir diretamente quais ações executar em diferentes estados sem saber quão boas as ações são.

No entanto, os problemas que enfrentamos no mundo real podem ser extremamente complicados de várias maneiras diferentes e, portanto, um algoritmo típico de Aprendizagem Por Reforço não tem nenhuma pista para resolver. Por exemplo, o espaço de estado é muito grande no jogo do GO, o ambiente não pode ser totalmente observado no jogo de pôquer e há muitos agentes interagindo entre si no mundo real. Os pesquisadores inventaram métodos para resolver alguns dos problemas usando redes neurais profundas para modelar as políticas desejadas, funções de valor ou mesmo os modelos de transição, que, portanto, são chamados de Aprendizado por Reforço Profundo (Deep Reinforcement Learning). 

Mas abordaremos tudo isso em detalhes mais a frente. Por hora, vejamos algumas das principais aplicações da Aprendizagem Por Reforço.

Gerenciamento de Recursos em Clusters de Computadores

Projetar algoritmos para alocar recursos limitados a diferentes tarefas é desafiador e requer heurísticas geradas por humanos. O artigo “Gerenciamento de recursos com aprendizado por reforço profundo” explica como usar o RL para aprender automaticamente a alocar e programar recursos de computador para jobs em espera, com o objetivo de minimizar a desaceleração média do job (tarefa).

O espaço de estados foi formulado como a alocação de recursos atual e o perfil de recursos dos jobs. Para o espaço de ação, eles usaram um truque para permitir que o agente escolha mais de uma ação a cada etapa do tempo. A recompensa era a soma de (-1 / duração do job) em todos os jobs no sistema. Em seguida, eles combinaram o algoritmo REINFORCE e o valor da linha de base para calcular os gradientes da política e encontrar os melhores parâmetros de política que fornecem a distribuição de probabilidade das ações para minimizar o objetivo. 

Controle de Semáforo

No artigo “Sistema multiagente baseado em aprendizado por reforço para controle de sinais de tráfego de rede”, os pesquisadores tentaram projetar um controlador de semáforo para resolver o problema de congestionamento. Testados apenas em ambiente simulado, seus métodos mostraram resultados superiores aos métodos tradicionais e lançaram uma luz sobre os possíveis usos da RL de múltiplos agentes no projeto de sistemas de tráfego.

Cinco agentes foram colocados na rede de tráfego de cinco cruzamentos, com um agente RL no cruzamento central para controlar a sinalização de tráfego. O estado foi definido como um vetor de oito dimensões, com cada elemento representando o fluxo de tráfego relativo de cada faixa. Oito opções estavam disponíveis para o agente, cada uma representando uma combinação de fases, e a função de recompensa foi definida como redução no atraso em comparação com o passo anterior. Os autores usaram o DQN para aprender o valor Q dos pares {state, action}.

Robótica

Há um incrível trabalho na aplicação de RL em robótica. Recomendamos a leitura desse paper com o resultado de uma pesquisa de RL em robótica. Neste outro trabalho, os pesquisadores treinaram um robô para aprender políticas para mapear imagens de vídeo brutas para as ações do robô. As imagens RGB foram alimentadas em uma CNN e as saídas foram os torques do motor. O componente RL era a pesquisa de política guiada para gerar dados de treinamento provenientes de sua própria distribuição de estado.

Configuração de Sistemas Web

Existem mais de 100 parâmetros configuráveis ​​em um Sistema Web e o processo de ajuste dos parâmetros requer um operador qualificado e vários testes de rastreamento e erro. O artigo “Uma abordagem de aprendizado por reforço à auto-configuração do sistema Web on-line” mostrou a primeira tentativa no domínio sobre como fazer a reconfiguração autônoma de parâmetros em sistemas web multicamada em ambientes dinâmicos baseados em VM.

O processo de reconfiguração pode ser formulado como um MDP finito. O espaço de estado era a configuração do sistema, o espaço de ação era {aumentar, diminuir, manter} para cada parâmetro e a recompensa era definida como a diferença entre o tempo de resposta pretendido e o tempo de resposta medido. Os autores usaram o algoritmo de Q-learning para executar a tarefa.

Embora os autores tenham usado alguma outra técnica, como a inicialização de políticas, para remediar o grande espaço de estados e a complexidade computacional do problema, em vez das combinações potenciais de RL e rede neural, acredita-se que o trabalho pioneiro tenha preparado o caminho para futuras pesquisas nessa área. .

Química

O RL também pode ser aplicado na otimização de reações químicas. Pesquisadores mostraram que seu modelo superou um algoritmo de última geração e generalizou para mecanismos subjacentes diferentes no artigo “Otimizando reações químicas com aprendizado de reforço profundo”.

Combinado com o LSTM para modelar a função de política, o agente RL otimizou a reação química com o processo de decisão de Markov (MDP) caracterizado por {S, A, P, R}, em que S era o conjunto de condições experimentais (como temperatura, pH, etc), A foi o conjunto de todas as ações possíveis que podem alterar as condições experimentais, P foi a probabilidade de transição da condição atual da experiência para a próxima condição e R foi a recompensa que é uma função do estado.

A aplicação é excelente para demonstrar como a RL pode reduzir o trabalho demorado e de tentativa e erro em um ambiente relativamente estável.

Recomendações Personalizadas

O trabalho de recomendações de notícias sempre enfrentou vários desafios, incluindo a dinâmica de mudança rápida das notícias, os usuários que se cansam facilmente e a Taxa de Cliques que não pode refletir a taxa de retenção de usuários. Guanjie et al. aplicaram RL no sistema de recomendação de notícias em um documento intitulado “DRN: Uma Estrutura de Aprendizado de Reforço Profundo para Recomendação de Notícias” para combater os problemas.

Na prática, eles construíram quatro categorias de recursos, a saber: A) recursos do usuário, B) recursos de contexto como os recursos de estado do ambiente, C) recursos de notícias do usuário e D) recursos de notícias como os recursos de ação. Os quatro recursos foram inseridos na Deep Q-Network (DQN) para calcular o valor Q. Uma lista de notícias foi escolhida para recomendar com base no valor Q, e o clique do usuário nas notícias foi parte da recompensa que o agente da RL recebeu. 

Os autores também empregaram outras técnicas para resolver outros problemas desafiadores, incluindo repetição de memória, modelos de sobrevivência, Dueling Bandit Gradient Descent e assim por diante. 

Leilões e Publicidade

Pesquisadores do Alibaba Group publicaram um artigo “Leilões em tempo real com aprendizado de reforço de agentes múltiplos em publicidade gráfica” e afirmaram que sua solução distribuída de agentes múltiplos baseada em cluster (DCMAB) alcançou resultados promissores e, portanto, planejam realizar uma teste ao vivo na plataforma Taobao.

De um modo geral, a plataforma de anúncios Taobao é um local para os comerciantes fazerem um lance para exibir anúncios para os clientes. Isso pode ser um problema de vários agentes, porque os comerciantes fazem lances uns contra os outros e suas ações são inter-relacionadas. No artigo, comerciantes e clientes foram agrupados em diferentes grupos para reduzir a complexidade computacional. O espaço de estado dos agentes indicava o status de custo-receita dos agentes, o espaço de ação era o lance (contínuo) e a recompensa era a receita causada pelo cluster de clientes.

Talvez você esteja curioso por saber como seria o algoritmo para resolver esse tipo de problema. Aqui está o algoritmo criado pelos pesquisadores (mais detalhes no paper da pesquisa, no link anterior):

Jogos

A RL é tão conhecida hoje em dia porque é o algoritmo convencional usado para resolver jogos diferentes e às vezes alcançar um desempenho super-humano.

O mais famoso deve ser AlphaGo e AlphaGo Zero. O AlphaGo, treinado com inúmeros jogos humanos, já alcançou um desempenho super-humano usando a rede de valor e a pesquisa de árvores Monte Carlo (MCTS) em sua rede de políticas. No entanto, os pesquisadores tentaram uma abordagem mais pura da RL – treinando-a do zero. Os pesquisadores deixaram o novo agente, AlphaGo Zero, jogar sozinho e finalmente derrotar o AlphaGo 100-0.

Aprendizagem Profunda (Deep Learning)

Mais e mais tentativas de combinar RL e outras arquiteturas de aprendizado profundo podem ser vistas recentemente e mostraram resultados impressionantes.

Um dos trabalhos mais influentes da RL é o trabalho pioneiro da Deepmind para combinar a CNN com a RL. Ao fazer isso, o agente tem a capacidade de “ver” o ambiente por meio de sensores de alta dimensão e depois aprender a interagir com ele.

RL e RNN são outras combinações usadas pelas pessoas para tentar novas ideias. RNN é um tipo de rede neural que possui “memórias”. Quando combinada com a RL, a RNN oferece aos agentes a capacidade de memorizar as coisas. Por exemplo, combinaram LSTM com RL para criar uma profunda rede Q recorrente (DRQN) para jogar jogos do Atari 2600. Também usaram RNN e RL para resolver problemas de otimização de reações químicas.

O Deepmind mostrou como usar modelos generativos e RL para gerar programas. No modelo, o agente treinado adversamente usou o sinal como recompensa para melhorar as ações, em vez de propagar os gradientes para o espaço de entrada como no treinamento da GAN. Incrível, não?

O Que Você Precisa Saber Antes de Aplicar a RL ao seu Problema?

Existem várias coisas necessárias para que a RL possa ser aplicada.

Entendendo seu problema: Você não precisa necessariamente usar RL no seu problema e, às vezes, simplesmente não pode usá-lo. Convém verificar se o seu problema possui algumas das seguintes características antes de decidir usar a RL:

  • a) tentativa e erro (você pode aprender a fazer melhor recebendo comentários do ambiente);
  • b) recompensas atrasadas;
  • c) pode ser modelado como MDP;
  • d) seu problema é um problema de controle.

Um ambiente simulado: Muitas iterações são necessárias antes que um algoritmo RL funcione. Tenho certeza de que você não quer ver um agente de RL tentando coisas diferentes em um carro autônomo em uma rodovia, certo? Portanto, é necessário um ambiente simulado que possa refletir corretamente o mundo real.

MDP: Seu mundo precisa formular seu problema em um MDP. Você precisa projetar o espaço de estado, o espaço de ação, a função de recompensa e assim por diante. Seu agente fará o que é recompensado sob as restrições. Você pode não obter os resultados desejados se projetar as coisas de maneira diferente.

Algoritmos: Existem diferentes algoritmos de RL que você pode escolher e perguntas a serem feitas. Você deseja descobrir diretamente a política ou deseja aprender a função de valor? Você quer ir livre de modelo ou baseado em modelo? Você precisa combinar outros tipos de rede ou métodos neurais profundos para resolver seus problemas?

Intuições de Outras Disciplinas

RL tem uma relação muito estreita com psicologia, biologia e neurociência. Se você pensar bem, o que um agente de RL faz é apenas tentativa e erro: aprende o quão bom ou ruim suas ações são baseadas nas recompensas que recebe do ambiente. E é exatamente assim que o ser humano aprende a tomar uma decisão. Além disso, o problema de exploração, o problema de cessão de recompensa/penalidade, as tentativas de modelar o ambiente também são algo que enfrentamos em nossa vida cotidiana.

A teoria da economia também pode lançar alguma luz sobre RL. Em particular, a análise da aprendizagem de reforço multi-agente (MARL) pode ser entendida a partir das perspectivas da teoria dos jogos, que é uma área de pesquisa desenvolvida por John Nash para entender as interações de agentes em um sistema. Além da teoria dos jogos, o MARL, Processo de Decisão Markov Parcialmente Observável (POMDP) ​​também pode ser útil para entender outros tópicos econômicos, como estrutura de mercado (por exemplo, monopólio, oligopólio, etc.), externalidade e assimetria de informação. 

O que a Aprendizagem Por Reforço Pode Alcançar no Futuro?

A Aprendizagem Por Reforço seria influente e impactante das seguintes maneiras:

Assistência humana: Talvez seja exagero dizer que a RL pode um dia evoluir para inteligência geral artificial (AGI), mas a RL certamente tem o potencial de ajudar e trabalhar com pessoas. Imagine um robô ou um assistente virtual trabalhando com você e levando suas ações em consideração para executar ações a fim de alcançar um objetivo comum. Não seria ótimo?

Entendendo as consequências de diferentes estratégias: A vida é incrível, porque o tempo não volta e as coisas acontecem apenas uma vez. No entanto, às vezes gostaríamos de saber como as coisas poderiam ser diferentes (pelo menos a curto prazo) se eu adotasse uma ação diferente. A Croácia teria uma chance maior de ganhar a Copa do Mundo de 2018 se o treinador usasse outra estratégia? Obviamente, para conseguir isso, precisaríamos modelar perfeitamente o ambiente, as funções de transição e assim por diante e também analisar as interações entre os agentes.

As possibilidade são muitas e por isso Inteligência Artificial é um dos temas mais quentes do momento e os profissionais atentos a isso, ficarão com as melhores oportunidades. Continuaremos no próximo capítulo!

Referências:

Customizando Redes Neurais com Funções de Ativação Alternativas

What is reinforcement learning? The complete guide

Reinforcement learning

Applications of Reinforcement Learning in Real World

Practical Recommendations for Gradient-Based Training of Deep Architectures

Gradient-Based Learning Applied to Document Recognition

Neural Networks & The Backpropagation Algorithm, Explained

Recurrent neural network based language model

The Elements of Statistical Learning: Data Mining, Inference, and Prediction, Second Edition

Gradient Descent For Machine Learning

Pattern Recognition and Machine Learning

Deep Learning Book

Capítulo 62 – O Que é Aprendizagem Por Reforço?

by

Todas as arquiteturas de Deep Learning que estudamos até aqui neste livro podem ser classificadas em duas categorias de aprendizagem de máquina (você já sabe que Deep Learning é sub-categoria de Machine Learning, que por sua vez é uma sub-categoria de Inteligência Artificial): 

  • Aprendizagem Supervisionada – quando apresentamos ao algoritmo dados de entrada e as respectivas saídas.
  • Aprendizagem Não Supervisionada – quando apresentamos somente os dados de entrada e o algoritmo descobre as saídas.

Mas existe uma terceira categoria de aprendizagem, chamada de Aprendizagem Por Reforço (ou Reinforcement Learning), muito usada em Games e Robótica e que vem obtendo resultados cada vez melhores. A Aprendizagem Por Reforço é a principal técnica por trás do AlphaGo e está muito bem retratada no documentário do mesmo nome: AlphaGo.

Serão diversos capítulos dedicados a esta técnica e à sua extensão, o Deep Reinforcement Learning. Vamos começar definindo o que é Aprendizagem Por Reforço.

O Que é Aprendizagem Por Reforço?

A Aprendizagem Por Reforço é o treinamento de modelos de aprendizado de máquina para tomar uma sequência de decisões. O agente aprende a atingir uma meta em um ambiente incerto e potencialmente complexo. No aprendizado por reforço, o sistema de inteligência artificial enfrenta uma situação. O computador utiliza tentativa e erro para encontrar uma solução para o problema. Para que a máquina faça o que o programador deseja, a inteligência artificial recebe recompensas ou penalidades pelas ações que executa. Seu objetivo é maximizar a recompensa total.

Embora o Cientista de Dados ou o Engenheiro de IA defina a política de recompensa – isto é, as regras do jogo – ele não dá ao modelo nenhuma dica ou sugestão de como resolver o jogo. Cabe ao modelo descobrir como executar a tarefa para maximizar a recompensa, começando com testes totalmente aleatórios e terminando com táticas sofisticadas. Ao alavancar o poder da pesquisa e de muitas tentativas, o aprendizado por reforço é atualmente a maneira mais eficaz de sugerir a criatividade da máquina. Ao contrário dos seres humanos, a inteligência artificial pode reunir experiência de milhares de jogos paralelos se um algoritmo de aprendizado por reforço for executado em uma infraestrutura de computador suficientemente poderosa.

Exemplo:

O problema é o seguinte: Temos um agente e uma recompensa, com muitos obstáculos no meio, como nesta imagem abaixo. O agente deve encontrar o melhor caminho possível para alcançar a recompensa e quando encontrar um obstáculo, deve ser penalizado (pois ele deve escolher o caminho sem obstáculos). Com a Aprendizagem Por Reforço, podemos treinar o agente para encontrar o melhor caminho.

rl

Desafios do Aprendizado Por Reforço

O principal desafio do aprendizado por reforço está na preparação do ambiente de simulação, que depende muito da tarefa a ser executada. Quando o modelo é treinado em jogos de Xadrez, Go ou Atari, a preparação do ambiente de simulação é relativamente simples. Quando se trata de construir um modelo capaz de dirigir um carro autônomo, a construção de um simulador realista é crucial antes de deixar o carro andar na rua. O modelo precisa descobrir como frear ou evitar uma colisão em um ambiente seguro. Transferir o modelo do ambiente de treinamento para o mundo real é onde as coisas ficam complicadas.

Escalar e ajustar a rede neural que controla o agente é outro desafio. Não há como se comunicar com a rede a não ser através do sistema de recompensas e penalidades. Isso pode levar a um esquecimento catastrófico, em que a aquisição de novos conhecimentos faz com que alguns dos antigos sejam apagados da rede. Ou seja, precisamos guardar o aprendizado na “memória” do agente.

Outro desafio é alcançar um ótimo local – ou seja, o agente executa a tarefa como está, mas não da maneira ideal ou necessária. Um “saltador” pulando como um canguru em vez de fazer o que se esperava dele com pequenos saltos é um ótimo exemplo. Por fim, existem agentes que otimizarão o prêmio sem executar a tarefa para a qual foram projetados. 

O Que Distingue o Aprendizado Por Reforço do Aprendizado Profundo e do Aprendizado de Máquina?

De fato, não há uma divisão clara entre aprendizado de máquina, aprendizado profundo e aprendizado por reforço. É como uma relação paralelogramo – retângulo – quadrado, em que o aprendizado de máquina é a categoria mais ampla e o aprendizado por reforço é o mais estreito.

Da mesma forma, o aprendizado por reforço é uma aplicação especializada de técnicas de Deep Learning e Machine Learning, projetada para resolver problemas de uma maneira específica.

Embora as ideias pareçam divergir, não há uma divisão acentuada entre esses subtipos. Além disso, eles se mesclam nos projetos, pois os modelos são projetados para não se ater ao “tipo puro”, mas para executar a tarefa da maneira mais eficaz possível. Portanto, “o que distingue precisamente o aprendizado de máquina, o aprendizado profundo e o aprendizado por reforço” é, na verdade, uma pergunta difícil de responder. Mas vamos definir cada um deles!

Aprendizado de Máquina é uma forma de IA na qual os computadores têm a capacidade de melhorar progressivamente o desempenho de uma tarefa específica com dados, sem serem diretamente programados. Essa é a definição de Arthur Lee Samuel. Ele cunhou o termo “aprendizado de máquina”, do qual existem dois tipos, aprendizado de máquina supervisionado e não supervisionado.

O aprendizado de máquina supervisionado acontece quando um programador pode fornecer um rótulo para cada entrada de treinamento no sistema de aprendizado de máquina.

O aprendizado não supervisionado ocorre quando o modelo é fornecido apenas com os dados de entrada, mas sem rótulos explícitos. Ele precisa pesquisar os dados e encontrar a estrutura ou os relacionamentos ocultos. O Cientista de Dados pode não saber qual é a estrutura ou o que o modelo de aprendizado de máquina irá encontrar.

O Aprendizado Profundo consiste em várias camadas de redes neurais, projetadas para executar tarefas mais sofisticadas. A construção de modelos de aprendizado profundo foi inspirada no design do cérebro humano, mas simplificada. Os modelos de aprendizado profundo consistem em algumas camadas de rede neural que são, em princípio, responsáveis por aprender gradualmente recursos mais abstratos sobre dados específicos.

Embora as soluções de aprendizado profundo sejam capazes de fornecer resultados maravilhosos, em termos de escala, elas não são páreo para o cérebro humano. Cada camada usa o resultado de uma anterior como entrada e toda a rede é treinada como um todo. O conceito central de criar uma rede neural artificial não é novo, mas apenas recentemente o hardware moderno forneceu energia computacional suficiente para treinar efetivamente essas redes, expondo um número suficiente de exemplos. A adoção estendida trouxe estruturas como TensorFlow, Keras e PyTorch, as quais tornaram a construção de modelos de aprendizado de máquina muito mais conveniente.

O Aprendizado Por Reforço, como declarado acima, emprega um sistema de recompensas e penalidades para obrigar o computador a resolver um problema sozinho. O envolvimento humano é limitado à mudança do ambiente e ao ajuste do sistema de recompensas e penalidades. Como o computador maximiza a recompensa, ele está propenso a procurar maneiras inesperadas de fazê-lo. O envolvimento humano é focado em impedir que ele explore o sistema e motive a máquina a executar a tarefa da maneira esperada. O aprendizado por reforço é útil quando não existe uma “maneira adequada” de executar uma tarefa, mas existem regras que o modelo deve seguir para desempenhar corretamente suas tarefas. Abaixo a performance de um agente sendo treinado em um jogo clássico do Atari.

Performance inicial do agente:

breakout_0

Após 15 minutos de treinamento:

breakout_1

Após 30 minutos de treinamento:

breakout_2

Em particular, se a inteligência artificial vai dirigir um carro ou aprender a jogar alguns clássicos do Atari, pode ser considerado um marco intermediário significativo. Uma aplicação potencial do aprendizado por reforço em veículos autônomos é uma das aplicações mais trabalhadas nos dias de hoje em todo mundo. Um desenvolvedor é incapaz de prever todas as situações futuras da estrada, portanto, deixar o modelo treinar-se com um sistema de penalidades e recompensas em um ambiente variado é possivelmente a maneira mais eficaz da IA ampliar a experiência que possui e coleta e assim aprender a conduzir um veículo autônomo sem que seja explicitamente programada para isso.

Continuaremos no próximo capítulo!

Referências:

Engenharia Financeira com Inteligência Artificial

Customizando Redes Neurais com Funções de Ativação Alternativas

What is reinforcement learning? The complete guide

Reinforcement learning

Practical Recommendations for Gradient-Based Training of Deep Architectures

Gradient-Based Learning Applied to Document Recognition

Neural Networks & The Backpropagation Algorithm, Explained

Recurrent neural network based language model

The Elements of Statistical Learning: Data Mining, Inference, and Prediction, Second Edition

Gradient Descent For Machine Learning

Pattern Recognition and Machine Learning

Deep Learning Book

Capítulo 61 – A Matemática dos Variational Autoencoders (VAEs)

by

No capítulo anterior, fornecemos a seguinte visão geral intuitiva: Os VAEs são Autoencoders que codificam entradas como distribuições em vez de pontos e cuja “organização” do espaço latente é regularizada restringindo as distribuições retornadas pelo codificador a estarem próximas de um gaussiano padrão. Neste capítulo, forneceremos uma visão matemática dos VAEs que nos permitirá justificar o termo de regularização com mais rigor. Para isso, definiremos uma estrutura probabilística clara e usaremos, em particular, a técnica de inferência variacional.

Estrutura Probabilística e Premissas

Vamos começar definindo um modelo gráfico probabilístico para descrever nossos dados. Denotamos por x a variável que representa nossos dados e assumimos que x é gerado a partir de uma variável latente z (a representação codificada) que não é diretamente observada. Assim, para cada ponto de dados, é assumido o seguinte processo generativo de duas etapas:

  • Primeiro, uma representação latente z é amostrada da distribuição anterior p(z).
  • Segundo, os dados x são amostrados da distribuição de probabilidade condicional definido por p(x | z).

Com esse modelo probabilístico em mente, podemos redefinir nossas noções de codificador e decodificador. De fato, ao contrário de um Autoencoder simples que considera codificador e decodificador determinístico, consideraremos agora versões probabilísticas desses dois objetos. O “decodificador probabilístico” é definido naturalmente por p(x | z), que descreve a distribuição da variável decodificada dada a codificada, enquanto o “codificador probabilístico” é definido por p(z | x), que descreve a distribuição de a variável codificada, dada a decodificada.

Neste ponto, já podemos notar que a regularização do espaço latente que nos faltava em Autoencoders simples aparece naturalmente aqui na definição do processo de geração de dados: presume-se que representações codificadas z no espaço latente sigam a distribuição anterior p(z). Caso contrário, também podemos lembrar o conhecido teorema de Bayes, que faz a ligação entre o anterior p(z), a probabilidade p(x | z) e o posterior p(z | x):

form1

Vamos agora assumir que p(z) é uma distribuição gaussiana padrão e que p(x | z) é uma distribuição gaussiana cuja média é definida por uma função determinística f da variável de z e cuja matriz de covariância tem a forma de uma constante positiva c que multiplica a matriz de identidade I. Supõe-se que a função f pertence a uma família de funções denotadas F que é deixada não especificada no momento e que será escolhida posteriormente. Assim, temos:

form2

Vamos considerar, por enquanto, que f está bem definido e fixo. Em teoria, como conhecemos p(z) e p(x | z), podemos usar o Teorema de Bayes para calcular p(z | x): este é um problema clássico de inferência bayesiana. No entanto, esse tipo de computação geralmente é intratável (por causa da integral no denominador) e requer o uso de técnicas de aproximação, como inferência variacional.

Nota: Aqui podemos mencionar que p(z) e p(x | z) são ambas distribuições gaussianas, implicando que p(z | x) também deve seguir uma distribuição gaussiana. Em teoria, poderíamos “apenas” tentar expressar a média e a matriz de covariância de p(z | x) com relação às médias e às matrizes de covariância de p(z) e p(x | z). No entanto, na prática, esses valores dependem da função f que pode ser complexa e que não está definida por enquanto (mesmo que tenhamos assumido o contrário). Além disso, o uso de uma técnica de aproximação como inferência variacional torna a abordagem bastante geral e mais robusta a algumas mudanças na hipótese do modelo.

Formulação de Inferência Variacional

Em Estatística, a inferência variacional é uma técnica para aproximar distribuições complexas. A ideia é definir uma família de distribuição parametrizada (por exemplo, a família de Gaussianos, cujos parâmetros são a média e a covariância) e procurar a melhor aproximação de nossa distribuição de destino entre essa família. O melhor elemento da família é aquele que minimiza uma determinada medição de erro de aproximação (na maioria das vezes a divergência de Kullback-Leibler entre aproximação e alvo) e é encontrada por descida do gradiente sobre os parâmetros que descrevem a família. Para mais detalhes, você encontra um artigo sobe isso na seção de referências ao final do capítulo.

Aqui vamos aproximar p(z | x) por uma distribuição gaussiana q_x(z) cuja média e covariância são definidas por duas funções, g e h, do parâmetro x. Essas duas funções devem pertencer, respectivamente, às famílias de funções G e H que serão especificadas mais tarde, mas que devem ser parametrizadas. Assim, podemos denotar:

form3

Portanto, definimos dessa maneira uma família de candidatos à inferência variacional e precisamos agora encontrar a melhor aproximação entre essa família otimizando as funções g e h (de fato, seus parâmetros) para minimizar a divergência de Kullback-Leibler entre a aproximação e o alvo p(z | x). Em outras palavras, estamos procurando a aproximação ideal g* e h* de modo que:

 

form4

 

Na penúltima equação, podemos observar a troca existente – ao aproximar-se do p(z | x) posterior – entre maximizar a probabilidade das “observações” (maximização da probabilidade logarítmica esperada para o primeiro termo) e permanecer próximo à distribuição anterior (minimização da divergência de KL entre q_x(z) e p(z), para o segundo termo). Essa troca é natural para o problema de inferência bayesiana e expressa o equilíbrio que precisa ser encontrado entre a confiança que temos nos dados e a confiança que temos no passado.

Até agora, assumimos a função f conhecida e fixa e mostramos que, com tais premissas, podemos aproximar o p(z | x) posterior usando a técnica de inferência variacional. No entanto, na prática, essa função f, que define o decodificador, não é conhecida e também precisa ser escolhida. Para fazer isso, lembre-se de que nosso objetivo inicial é encontrar um esquema de codificação e decodificação com desempenho, cujo espaço latente seja regular o suficiente para ser usado para fins generativos. Se a regularidade é regida principalmente pela distribuição anterior assumida no espaço latente, o desempenho do esquema geral de codificação / decodificação depende muito da escolha da função f.

De fato, como p(z | x) pode ser aproximado (por inferência variacional) de p(z) e p(x | z) e como p(z) é um gaussiano padrão simples, as duas únicas alavancas que temos à nossa disposição em nosso modelo para fazer otimizações são o parâmetro c (que define a variância da probabilidade) e a função f (que define a média da probabilidade).

Portanto, vamos considerar que, como discutimos anteriormente, podemos obter para qualquer função f em F (cada uma definindo um decodificador probabilístico diferente p(x | z)) a melhor aproximação de p(z | x), denotada q*_x(z) Apesar de sua natureza probabilística, estamos procurando um esquema de codificação-decodificação o mais eficiente possível e, em seguida, queremos escolher a função f que maximize a probabilidade logarítmica esperada de x dado z quando z é amostrado de q*_x(z) Em outras palavras, para uma dada entrada x, queremos maximizar a probabilidade de ter x̂ = x quando amostramos z da distribuição q*_x(z) e depois amostramos x̂ da distribuição p(x | z). Assim, procuramos o f* ideal para que:

form5

onde q*_x (z) depende da função f e é obtido como descrito anteriormente. Reunindo todas as peças, estamos procurando as aproximações f*,  g* e h* ideais para que:

 

form6

 

Podemos identificar nesta função objetivo os elementos introduzidos na descrição intuitiva dos VAEs dados no capítulo anterior: o erro de reconstrução entre x e f(z) e o termo de regularização dado pela divergência KL entre q_x(z) e p(z) ) (que é um gaussiano padrão). Também podemos notar a constante c que regula o equilíbrio entre os dois termos anteriores. Quanto maior c for, mais assumimos uma alta variação em torno de f(z) para o decodificador probabilístico em nosso modelo e, portanto, mais favorecemos o termo de regularização sobre o termo de reconstrução (e o oposto se c for baixo).

Trazendo Redes Neurais Para o Modelo

Até o momento, definimos um modelo probabilístico que depende de três funções, f, g e h, e expressamos, usando inferência variacional, o problema de otimização a ser resolvido para obter f*, g* e h* que ofereçam o melhor esquema de codificação-decodificação com este modelo. Como não podemos otimizar facilmente todo o espaço de funções, restringimos o domínio de otimização e decidimos expressar f, g e h como redes neurais. Assim, F, G e H correspondem, respectivamente, às famílias de funções definidas pelas arquiteturas de rede e a otimização é feita sobre os parâmetros dessas redes. 

Na prática, g e h não são definidos por duas redes completamente independentes, mas compartilham uma parte de sua arquitetura e seus pesos, de modo que temos:

 

form7

 

Como isso define a matriz de covariância de q_x(z), h(x) deve ser uma matriz quadrada. Entretanto, para simplificar o cálculo e reduzir o número de parâmetros, assumimos que nossa aproximação de p(z | x), q_x(z), é uma distribuição gaussiana multidimensional com matriz de covariância diagonal (assunção de independência de variáveis). Com essa suposição, h(x) é simplesmente o vetor dos elementos diagonais da matriz de covariância e, então, tem o mesmo tamanho de g(x). No entanto, reduzimos dessa maneira a família de distribuições que consideramos para inferência variacional e, portanto, a aproximação de p(z | x) obtida pode ser menos precisa.

form8

Ao contrário da parte do codificador que modela p(z | x) e para a qual consideramos um gaussiano com média e covariância que são funções de x(g e h), nosso modelo assume para p(x | z) um gaussiano com covariância. A função f da variável z que define a média desse gaussiano é modelada por uma rede neural e pode ser representada da seguinte forma:

form9

A arquitetura geral é então obtida concatenando o codificador e as partes do decodificador. No entanto, ainda precisamos ter muito cuidado com a maneira como coletamos amostras da distribuição retornada pelo codificador durante o treinamento. O processo de amostragem deve ser expresso de forma a permitir que o erro seja retropropagado pela rede. Um truque simples, chamado truque de reparametrização, é usado para tornar possível a descida do gradiente, apesar da amostragem aleatória que ocorre na metade da arquitetura e consiste em usar o fato de que se z é uma variável aleatória após uma distribuição gaussiana com média g(x) e com covariância h(x), pode ser expresso como:

form10

form11

Finalmente, a função objetivo da arquitetura de Autoencoder Variacional obtida dessa maneira é dada pela última equação da subseção anterior, na qual a expectativa teórica é substituída por uma aproximação de Monte-Carlo mais ou menos precisa que consiste, na maioria das vezes, em um sorteio único. Assim, considerando essa aproximação e denotando C = 1 / (2c), recuperamos a função de perda derivada intuitivamente na seção anterior, composta por um termo de reconstrução, um termo de regularização e uma constante para definir os pesos relativos desses dois termos.

form12

Autoencoders Variacionais (VAEs) são Autoencoders que resolvem o problema da irregularidade do espaço latente, fazendo com que o codificador retorne uma distribuição sobre o espaço latente em vez de um único ponto e adicionando à função de perda um termo de regularização sobre a distribuição retornada para garantir uma melhor organização do espaço latente assumindo um modelo probabilístico simples para descrever nossos dados, a função de perda bastante intuitiva dos VAEs, composta por um termo de reconstrução e um termo de regularização, pode ser cuidadosamente derivada, usando em particular a técnica estatística de inferência variacional (daí o nome Autoencoder “Variacional”).

Para concluir, podemos destacar que, durante os últimos anos, as GANs se beneficiaram de muito mais contribuições científicas do que os VAEs. Entre outras razões, o maior interesse demonstrado pela comunidade por GANs pode ser parcialmente explicado pelo maior grau de complexidade na base teórica dos VAEs (modelo probabilístico e inferência variacional) em comparação à simplicidade do conceito de treinamento adversário que rege os GANs. Com este capítulo, esperamos que tenhamos compartilhado intuições valiosas e fortes fundamentos teóricos para tornar os VAEs mais acessíveis aos recém-chegados. No entanto, agora que discutimos em profundidade os dois, resta uma pergunta … qual arquitetura você achou mais interessante, GANs ou VAEs?

No próximo capítulo começamos a estudar a Aprendizagem Por Reforço! Até lá.

Referências:

Customizando Redes Neurais com Funções de Ativação Alternativas

Autoencoders – Unsupervised Learning

Bayesian inference problem, MCMC and variational inference

Understanding Variational Autoencoders (VAEs)

Deep inside: Autoencoders

Deep Learning — Different Types of Autoencoders

Contractive Auto-Encoders – Explicit Invariance During Feature Extraction

Stacked Denoising Autoencoders: Learning Useful Representations in a Deep Network with a Local Denoising Criterion

Introduction to Autoencoders

Practical Recommendations for Gradient-Based Training of Deep Architectures

Gradient-Based Learning Applied to Document Recognition

Neural Networks & The Backpropagation Algorithm, Explained

Recurrent neural network based language model

The Elements of Statistical Learning: Data Mining, Inference, and Prediction, Second Edition

Gradient Descent For Machine Learning

Deep Learning Book

Capítulo 60 – Variational Autoencoders (VAEs) – Definição, Redução de Dimensionalidade, Espaço Latente e Regularização

by

Nos últimos anos, modelos generativos baseados em aprendizado profundo ganharam cada vez mais interesse devido a (e implicando) algumas melhorias surpreendentes em Inteligência Artificial. Contando com uma enorme quantidade de dados, arquiteturas de rede bem projetadas e técnicas de treinamento inteligentes, os modelos generativos profundos demonstraram uma capacidade incrível de produzir peças de conteúdo altamente realistas de vários tipos, como imagens, textos e sons. Entre esses modelos, duas famílias principais se destacam e merecem uma atenção especial: Redes Adversárias Generativas (GANs) e Autoencoders Variacionais (VAEs). O primeiro já estudamos e agora estudaremos o segundo.

Em um capítulo anterior, discutimos em profundidade as Redes Adversárias Generativas (GANs) e mostramos, em particular, como o treinamento antagônico pode opor duas redes, um gerador e um discriminador, para pressionar ambas a melhorar iteração após iteração. Apresentamos agora, neste capítulo, outro tipo de modelo generativo profundo: Autoencoders Variacionais (VAEs – Variational Autoencoders).

Em resumo, um VAE é um Autoencoder cuja distribuição de codificações é regularizada durante o treinamento, a fim de garantir que seu espaço latente tenha boas propriedades, o que nos permite gerar novos dados. Além disso, o termo “variacional” vem da estreita relação que existe entre a regularização e o método de inferência variacional em Estatística. 

Mas o conceito por trás dos VAEs também pode levantar muitas questões. Qual é o espaço latente e por que regularizá-lo? Como gerar novos dados a partir dos VAEs? Qual é a ligação entre VAEs e inferência variacional? Para descrever os VAEs da melhor maneira possível, tentaremos responder a todas essas perguntas (e muitas outras!) e fornecer a você o máximo de informação e conhecimento possível (variando de intuições básicas a detalhes matemáticos mais avançados). Assim, o objetivo deste capítulo não é apenas discutir as noções fundamentais dos Variational Autoencoders (VAEs), mas também construir passo a passo e começar desde o início o raciocínio que leva a essas noções (como sempre fazemos em nossos cursos na Data Science Academy). E vamos começar com o conceito de redução de dimensionalidade.

O Que é Redução de Dimensionalidade?

No aprendizado de máquina, a redução de dimensionalidade é o processo de redução do número de recursos (atributos) que descrevem alguns dados. Essa redução é feita por seleção (apenas alguns recursos existentes são conservados) ou por extração (um número reduzido de novos recursos é criado com base nos recursos antigos) e pode ser útil em muitas situações que exigem dados de baixa dimensionalidade (visualização de dados, armazenamento, computação pesada, etc…). Embora existam muitos métodos diferentes de redução de dimensionalidade, podemos definir uma estrutura global que seja compatível com a maioria desses métodos.

Primeiro, vamos codificar o processo que produz a representação de “novos recursos” a partir da representação de “recursos antigos” (por seleção ou por extração) e decodificar o processo inverso. A redução de dimensionalidade pode então ser interpretada como compactação de dados, onde o codificador compacta os dados (do espaço inicial para o espaço codificado, também chamado de espaço latente) enquanto o decodificador os descompacta. Obviamente, dependendo da distribuição inicial dos dados, da dimensão do espaço latente e da definição do codificador, essa compactação pode ser perdida, o que significa que uma parte da informação é perdida durante o processo de codificação e não pode ser recuperada durante a decodificação.

reduc

O principal objetivo de um método de redução de dimensionalidade é encontrar o melhor par codificador / decodificador entre uma determinada família. Em outras palavras, para um determinado conjunto de codificadores e decodificadores possíveis, estamos procurando o par que mantém o máximo de informações ao codificar e, portanto, tem o mínimo de erro de reconstrução ao decodificar. Se denotarmos respectivamente E e D as famílias de codificadores e decodificadores que estamos considerando, o problema da redução de dimensionalidade pode ser escrito:

form1

Onde:

form2

define a medida do erro de reconstrução entre os dados de entrada x e os dados codificados e decodificados d (e (x)). 

Análise de Componentes Principais (PCA) e Autoencoders

Um dos primeiros métodos que vêm à mente quando se fala em redução de dimensionalidade é a análise de componentes principais (PCA). Para mostrar como ele se encaixa na estrutura que acabamos de descrever e criar o link para os Autoencoders, vamos dar uma visão geral de como o PCA funciona, deixando a maioria dos detalhes de lado (caso queira estudar o PCA em detalhes e na prática, há um capítulo inteiro dedicado a esta técnica em Matemática e Estatística Aplicada Para Data Science, Machine Learning e IA) .

A ideia do PCA é construir novos recursos independentes, que são combinações lineares dos novos recursos antigos e, de modo que as projeções dos dados no subespaço definido por esses novos recursos sejam o mais próximo possível dos dados iniciais (em termos de distância euclidiana). Em outras palavras, o PCA está procurando o melhor subespaço linear do espaço inicial (descrito por uma base ortogonal de novos recursos), de modo que o erro de aproximar os dados por suas projeções nesse subespaço seja o menor possível.

Traduzido em nossa estrutura global, procuramos um codificador na família E das matrizes n_e por n_d (transformação linear) cujas linhas são ortonormais (independência de recursos) e pelo decodificador associado entre a família D de matrizes n_d por n_e. Pode-se mostrar que os autovetores unitários correspondentes aos n_e maiores autovalores (em norma) da matriz de características de covariância são ortogonais (ou podem ser escolhidos assim) e definem o melhor subespaço da dimensão n_e para projetar dados com erro mínimo de aproximação. Assim, esses n_e autovetores podem ser escolhidos como nossos novos recursos e, portanto, o problema de redução de dimensão pode ser expresso como um problema de autovalor / autovetor. Além disso, também pode ser mostrado que, nesse caso, a matriz decodificadora é a transposta da matriz codificadora (se esses conceitos de Matemática parecem estranhos a você recomendamos Matemática e Estatística Aplicada Para Data Science, Machine Learning e IA).

Os Autoencoders são em essência, redes neurais para redução de dimensionalidade. A ideia geral dos Autoencoders é bastante simples e consiste em definir um codificador e um decodificador como redes neurais e aprender o melhor esquema de codificação-decodificação usando um processo de otimização iterativo. Assim, a cada iteração, alimentamos a arquitetura do autoencoder (o codificador seguido pelo decodificador) com alguns dados, comparamos a saída decodificada com os dados iniciais e retropropagamos o erro na arquitetura para atualizar os pesos das redes.

Assim, intuitivamente, a arquitetura geral do autoencoder (codificador + decodificador) cria um gargalo de dados que garante que apenas a parte estruturada principal da informação possa passar e ser reconstruída. Observando nossa estrutura geral, a família E dos codificadores considerados é definida pela arquitetura da rede do codificador, a família D dos decodificadores considerados é definida pela arquitetura da rede do decodificador e a busca do codificador e decodificador que minimiza o erro de reconstrução é feita por descida do gradiente sobre os parâmetros dessas redes.

autoencoder

Vamos primeiro supor que nossas arquiteturas de codificador e decodificador tenham apenas uma camada sem não linearidade (autoencoder linear). Esse codificador e decodificador são transformações lineares simples que podem ser expressas como matrizes. Em tal situação, podemos ver um vínculo claro com o PCA no sentido de que, assim como o PCA, estamos procurando o melhor subespaço linear para projetar dados com o mínimo de perda de informações possível ao fazê-lo. As matrizes de codificação e decodificação obtidas com o PCA definem naturalmente uma das soluções que gostaríamos de alcançar por descida do gradiente, mas devemos destacar que essa não é a única. De fato, várias bases podem ser escolhidas para descrever o mesmo subespaço ideal e, portanto, vários pares de codificador / decodificador podem fornecer o erro de reconstrução ideal. Além disso, para autoencoders lineares e, ao contrário do PCA, os novos recursos não precisam ser independentes (sem restrições de ortogonalidade nas redes neurais).

Agora, vamos supor que o codificador e o decodificador sejam profundos e não lineares. Nesse caso, quanto mais complexa a arquitetura, mais o autoencoder pode prosseguir para uma alta redução de dimensionalidade, mantendo baixa a perda de reconstrução. Intuitivamente, se nosso codificador e nosso decodificador tiver graus de liberdade suficientes, podemos reduzir qualquer dimensionalidade inicial para 1. De fato, um codificador com “poder infinito” poderia teoricamente pegar nossos N pontos de dados iniciais e codificá-los como 1, 2, 3, … até N (ou mais geralmente, como N inteiro no eixo real) e o decodificador associado pode fazer a transformação reversa, sem perda durante o processo.

Aqui, porém, devemos ter duas coisas em mente. Primeiro, uma importante redução de dimensionalidade sem perda de reconstrução costuma ter um preço: a falta de estruturas interpretáveis ​​e exploráveis ​​no espaço latente (falta de regularidade). Segundo, na maioria das vezes, o objetivo final da redução da dimensionalidade não é apenas reduzir o número de dimensões dos dados, mas reduzir esse número de dimensões, mantendo a maior parte das informações da estrutura de dados nas representações reduzidas. Por essas duas razões, a dimensão do espaço latente e a “profundidade” dos autoencoders (que definem o grau e a qualidade da compressão) devem ser cuidadosamente controladas e ajustadas, dependendo do objetivo final da redução da dimensionalidade.

Variational Autoencoders (VAEs)

No capítulo 58 introduzimos o conceito de Autoencoders, que são arquiteturas de codificador-decodificador que podem ser treinadas por descida do gradiente. Vamos agora fazer o link com o problema de geração de conteúdo, ver as limitações dos Autoencoders em sua forma atual para esse problema e introduzir os Autoencoders Variacionais.

Limitações de Autoencoders para Geração de Conteúdo

Nesse ponto, uma pergunta natural que vem à mente é “qual é o link entre os Autoencoders e a geração de conteúdo?”. De fato, uma vez que o Autoencoder foi treinado, temos um codificador e um decodificador, mas ainda não há uma maneira real de produzir qualquer novo conteúdo. À primeira vista, poderíamos ficar tentados a pensar que, se o espaço latente for regular o suficiente (bem “organizado” pelo codificador durante o processo de treinamento), poderíamos pegar um ponto aleatoriamente nesse espaço latente e decodificá-lo para obter um novo conteúdo. O decodificador agiria mais ou menos como o gerador de uma Rede Adversária Generativa .

vae

No entanto, a regularidade do espaço latente para Autoencoders é um ponto difícil que depende da distribuição dos dados no espaço inicial, da dimensão do espaço latente e da arquitetura do codificador. Portanto, é bastante difícil (se não impossível) garantir, a priori, que o codificador organize o espaço latente de maneira inteligente, compatível com o processo generativo que acabamos de descrever.

Para ilustrar esse ponto, vamos considerar o exemplo no qual descrevemos um codificador e um decodificador suficientemente poderosos para colocar N dados de treinamento inicial no eixo real (cada ponto de dados sendo codificado como um valor real) e decodificá-los sem nenhum perda de reconstrução. Nesse caso, o alto grau de liberdade do Autoencoder que possibilita a codificação e decodificação sem perda de informações (apesar da baixa dimensionalidade do espaço latente) leva a uma super adaptação severa, o que implica que alguns pontos do espaço latente fornecerão conteúdo sem sentido uma vez decodificado. Se esse exemplo unidimensional tiver sido voluntariamente escolhido para ser extremo, podemos notar que o problema da regularidade espacial latente dos Autoencoders é muito mais geral do que isso e merece uma atenção especial.

vae2

Ao pensar nisso por um minuto, essa falta de estrutura entre os dados codificados no espaço latente é bastante normal. De fato, o Autoencoder é treinado apenas para codificar e decodificar com o mínimo de perdas possível, independentemente da organização do espaço latente. Portanto, se não tomarmos cuidado com a definição da arquitetura, é natural que, durante o treinamento, a rede aproveite todas as possibilidades de sobreajuste para realizar sua tarefa da melhor forma possível … a menos que a regularizemos explicitamente!

Definição de Autoencoders Variacionais

Portanto, para poder usar o decodificador de nosso autoencoder para fins generativos, precisamos ter certeza de que o espaço latente é regular o suficiente. Uma solução possível para obter essa regularidade é introduzir regularização explícita durante o processo de treinamento. Assim, como mencionamos brevemente na introdução deste capítulo, um autoencoder variacional pode ser definido como um autoencoder cujo treinamento é regularizado para evitar sobreajuste e garantir que o espaço latente tenha boas propriedades que possibilitem processos generativos.

Assim como um autoencoder padrão, um autoencoder variacional é uma arquitetura composta por um codificador e um decodificador, treinada para minimizar o erro de reconstrução entre os dados decodificados e os dados iniciais. No entanto, para introduzir alguma regularização do espaço latente, procedemos a uma ligeira modificação do processo de codificação / decodificação: em vez de codificar uma entrada como um único ponto, a codificamos como uma distribuição no espaço latente. O modelo é treinado da seguinte maneira:

  • Primeiro, a entrada é codificada como distribuição no espaço latente.
  • Segundo, um ponto do espaço latente é amostrado a partir dessa distribuição.
  • Terceiro, o ponto amostrado é decodificado e o erro de reconstrução pode ser calculado.
  • Finalmente, o erro de reconstrução é retropropagado pela rede.

vae3

Na prática, as distribuições codificadas são escolhidas para serem normais, de modo que o codificador possa ser treinado para retornar a média e a matriz de covariância que descrevem esses gaussianos. A razão pela qual uma entrada é codificada como uma distribuição com alguma variância em vez de um único ponto é que torna possível expressar muito naturalmente a regularização do espaço latente: as distribuições retornadas pelo codificador são impostas para estarem próximas a uma distribuição normal padrão. Veremos na próxima subseção que garantimos dessa maneira uma regularização local e global do espaço latente (local por causa do controle de variância e global por causa do controle médio).

Assim, a função de perda que é minimizada ao treinar um VAE é composta de um “termo de reconstrução” (na camada final), que tende a tornar o esquema de codificação-decodificação o mais eficiente possível e um “termo de regularização” (no camada latente), que tende a regularizar a organização do espaço latente, tornando as distribuições retornadas pelo codificador próximas a uma distribuição normal padrão. Esse termo de regularização é expresso como a divergência de Kulback-Leibler entre a distribuição retornada e uma gaussiana padrão e será mais justificado na próxima seção. Podemos notar que a divergência de Kullback-Leibler entre duas distribuições gaussianas tem uma forma fechada que pode ser expressa diretamente em termos das médias e das matrizes de covariância das duas distribuições.

vae4

Intuições Sobre a Regularização

A regularidade que se espera do espaço latente para possibilitar o processo generativo pode ser expressa por meio de duas propriedades principais: continuidade (dois pontos de fechamento no espaço latente não devem fornecer dois conteúdos completamente diferentes uma vez decodificados) e integridade (para uma distribuição escolhida , um ponto amostrado no espaço latente deve fornecer conteúdo “significativo” depois de decodificado).

O único fato de que os VAEs codificam entradas como distribuições, em vez de pontos simples, não é suficiente para garantir continuidade e integridade. Sem um termo de regularização bem definido, o modelo pode aprender, a fim de minimizar seu erro de reconstrução, “ignorar” o fato de que as distribuições são retornadas e se comportam quase como os autoencoders clássicos (levando ao super ajuste). Para fazer isso, o codificador pode retornar distribuições com pequenas variações (que tendem a ser distribuições pontuais) ou retornar distribuições com meios muito diferentes (que ficariam muito distantes um do outro no espaço latente). Nos dois casos, as distribuições são usadas da maneira errada (cancelando o benefício esperado) e a continuidade e / ou a integridade não são satisfeitas.

Portanto, para evitar esses efeitos, precisamos regularizar a matriz de covariância e a média das distribuições retornadas pelo codificador. Na prática, essa regularização é feita impondo distribuições para estar perto de uma distribuição normal padrão (centralizada e reduzida). Dessa forma, exigimos que as matrizes de covariância estejam próximas da identidade, impedindo distribuições pontuais e com a média próxima de 0, impedindo que as distribuições codificadas estejam muito distantes umas das outras.

Com esse termo de regularização, impedimos que o modelo codifique dados distantes no espaço latente e incentivamos o máximo possível as distribuições retornadas a “se sobrepor”, satisfazendo dessa maneira as condições de continuidade e integridade esperadas. Naturalmente, como em qualquer termo de regularização, isso tem o preço de um erro de reconstrução mais alto nos dados de treinamento. A troca entre o erro de reconstrução e a divergência de KL pode, no entanto, ser ajustada e veremos no próximo capítulo como a expressão emerge naturalmente de nossa derivação formal.

Para concluir, podemos observar que a continuidade e a integridade obtidas com a regularização tendem a criar um “gradiente” sobre as informações codificadas no espaço latente. Por exemplo, um ponto do espaço latente que estaria a meio caminho entre as médias de duas distribuições codificadas provenientes de diferentes dados de treinamento deve ser decodificado em algo que esteja em algum lugar entre os dados que deram a primeira distribuição e os dados que deram a segunda distribuição como pode ser amostrado pelo autoencoder em ambos os casos.

Essa é uma das arquiteturas mais avançadas de Deep Learning e para uma melhor compreensão, precisamos da nossa amiga, a Matemática. No próximo capítulo traremos um pouco da Matemática por trás dos VAEs. Até lá.

Referências:

Customizando Redes Neurais com Funções de Ativação Alternativas

Autoencoders – Unsupervised Learning

Understanding Variational Autoencoders (VAEs)

Deep inside: Autoencoders

Deep Learning — Different Types of Autoencoders

Contractive Auto-Encoders – Explicit Invariance During Feature Extraction

Stacked Denoising Autoencoders: Learning Useful Representations in a Deep Network with a Local Denoising Criterion

Introduction to Autoencoders

Practical Recommendations for Gradient-Based Training of Deep Architectures

Gradient-Based Learning Applied to Document Recognition

Neural Networks & The Backpropagation Algorithm, Explained

Recurrent neural network based language model

The Elements of Statistical Learning: Data Mining, Inference, and Prediction, Second Edition

Gradient Descent For Machine Learning

Pattern Recognition and Machine Learning

Deep Learning Book

Capítulo 58 – Introdução aos Autoencoders

by

Até aqui neste livro descrevemos arquiteturas de redes neurais profundas (Deep Learning) usadas em aprendizagem supervisionada, na qual rotulamos exemplos de treinamento, fornecendo ao algoritmo dados de entrada (X) e de saída (Y). Agora, suponha que tenhamos apenas um conjunto de exemplos de treinamento não rotulados {x1, x2, x3,…}, onde x (i) ∈ℜn. Uma rede neural Autoencoder é um algoritmo de aprendizado não supervisionado que aplica backpropagation, definindo os valores de destino como iguais às entradas. Ou seja, usa y (i) = x (i). Estudaremos essa arquitetura de Deep Learning a partir de agora.

O que são Autoencoders?

Os Autoencoders são uma técnica de aprendizado não supervisionado, na qual usamos as redes neurais para a tarefa de aprendizado de representação. Especificamente, projetaremos uma arquitetura de rede neural de modo a impor um gargalo na rede que força uma representação de conhecimento compactada da entrada original. Se os recursos de entrada fossem independentes um do outro, essa compressão e reconstrução subsequente seriam uma tarefa muito difícil. No entanto, se houver algum tipo de estrutura nos dados (ou seja, correlações entre os recursos de entrada), essa estrutura poderá ser aprendida e consequentemente aproveitada ao forçar a entrada através do gargalo da rede.

Autoencoders (AE) são redes neurais que visam copiar suas entradas para suas saídas. Eles trabalham compactando a entrada em uma representação de espaço latente e, em seguida, reconstruindo a saída dessa representação. Esse tipo de rede é composto de duas partes:

Codificador (Encoder): é a parte da rede que compacta a entrada em uma representação de espaço latente (codificando a entrada). Pode ser representado por uma função de codificação h = f (x).

Decodificador (Decoder): Esta parte tem como objetivo reconstruir a entrada da representação do espaço latente. Pode ser representado por uma função de decodificação r = g (h).

 

mushroom_encoder

 

O Autoencoder tenta aprender uma função h W, b (x) ≈ x. Em outras palavras, ele está tentando aprender uma aproximação com a função de identidade, de modo a gerar x’ semelhante a x. A função de identidade parece uma função particularmente trivial para tentar aprender; mas colocando restrições na rede, como limitando o número de unidades ocultas, podemos descobrir uma estrutura interessante sobre os dados. Como um exemplo concreto, suponha que as entradas x sejam os valores de intensidade de pixel de uma imagem 10 × 10 (100 pixels), portanto n = 100 e haja s = 50 unidades ocultas na camada L2. Observe que também temos y∈ℜ100. Como existem apenas 50 unidades ocultas, a rede é forçada a aprender uma representação “compactada” da entrada. Ou seja, dado apenas o vetor de ativações de unidades ocultas, ele deve tentar ”reconstruir” a entrada de 100 pixels x. Se a entrada fosse completamente aleatória – digamos, cada xi provém de um ID Gaussiano independente dos outros recursos, essa tarefa de compactação seria muito difícil. Mas se houver estrutura nos dados, por exemplo, se alguns dos recursos de entrada estiverem correlacionados, esse algoritmo poderá descobrir algumas dessas correlações. De fato, esse Autoencoder simples geralmente acaba aprendendo uma representação de baixa dimensão muito semelhante aos PCAs (Principals Component Analysis).

Nosso argumento acima se baseava no número de unidades ocultas s sendo pequenas. Mas mesmo quando o número de unidades ocultas é grande (talvez até maior que o número de pixels de entrada), ainda podemos descobrir uma estrutura interessante, impondo outras restrições à rede. Em particular, se impusermos uma restrição de “esparsidade” nas unidades ocultas, o Autoencoder ainda descobrirá uma estrutura interessante nos dados, mesmo que o número de unidades ocultas seja grande.

 

autoencoders

 

Por que copiar a entrada para a saída?

Se o único objetivo dos Autoencoders fosse copiar a entrada para a saída, eles seriam inúteis. De fato, esperamos que, treinando o Autoencoder para copiar a entrada para a saída, a representação latente h tenha propriedades úteis.

Isso pode ser conseguido criando restrições na tarefa de cópia. Uma maneira de obter recursos úteis do Autoencoder é restringir h a ter dimensões menores que x; nesse caso, o Autoencoder é chamado de incompleto. Ao treinar uma representação incompleta, forçamos o Autoencoder a aprender os recursos mais importantes dos dados de treinamento. Se for dada muita capacidade ao Autoencoder, ele poderá aprender a executar a tarefa de cópia sem extrair nenhuma informação útil sobre a distribuição dos dados. Isso também pode ocorrer se a dimensão da representação latente for a mesma que a entrada e, no caso de excesso de conclusão, em que a dimensão da representação latente for maior que a entrada.

Nesses casos, mesmo um codificador linear e um decodificador linear podem aprender a copiar a entrada na saída sem aprender nada útil sobre a distribuição de dados. Idealmente, alguém poderia treinar qualquer arquitetura de Autoencoder com sucesso, escolhendo a dimensão do código e a capacidade do codificador e decodificador com base na complexidade da distribuição a ser modelada.

Para que são usados ​​os Autoencoders?

Atualmente, o denoising de dados (remoção de ruídos) e a redução de dimensionalidade para visualização de dados são considerados duas principais aplicações práticas interessantes de Autoencoders. Com restrições de dimensionalidade e esparsidade apropriadas, os Autoencoders podem aprender projeções de dados mais interessantes que o PCA ou outras técnicas básicas.

Os Autoencoders aprendem automaticamente a partir de exemplos de dados. Isso significa que é fácil treinar instâncias especializadas do algoritmo que terão bom desempenho em um tipo específico de entrada e que não requer nenhuma nova engenharia de recursos, apenas os dados de treinamento apropriados.

Outra aplicação dos Autoencoders é como tarefa preliminar ao reconhecimento de imagens com CNNs (Redes Neurais Convolucionais). Observando a imagem acima você percebe que a saída do Autoencoder é a imagem do número 4 muito mais suave. Ou seja, aplicamos os Autoencoders para remover ruído dos dados (denoising) e depois usamos a saída dos Autoencoders para treinar um modelo CNN.

Os Autoencoders são treinados para preservar o máximo de informações possível quando uma entrada é passada pelo codificador e depois pelo decodificador, mas também são treinados para fazer com que a nova representação tenha várias propriedades agradáveis. Diferentes tipos de Autoencoders visam atingir diferentes tipos de propriedades. 

Referências:

Customizando Redes Neurais com Funções de Ativação Alternativas

Autoencoders – Unsupervised Learning

Deep inside: Autoencoders

Introduction to Autoencoders

Practical Recommendations for Gradient-Based Training of Deep Architectures

Gradient-Based Learning Applied to Document Recognition

Neural Networks & The Backpropagation Algorithm, Explained

Recurrent neural network based language model

The Elements of Statistical Learning: Data Mining, Inference, and Prediction, Second Edition

Gradient Descent For Machine Learning

Pattern Recognition and Machine Learning

Deep Learning Book

Capítulo 57 – Os Detalhes Matemáticos das GANs (Generative Adversarial Networks)

by

Neste capítulo vamos concluir nosso estudo das GANs com os detalhes matemáticos, antes de avançar para outra arquitetura de Deep Learning que estudaremos na sequência. Estamos considerando que você leu os capítulos anteriores sobre GANs.

A modelagem de redes neurais requer essencialmente definir duas coisas: uma arquitetura e uma função de perda. Já descrevemos a arquitetura de redes adversárias generativas. Consiste em duas redes:

  1. Uma rede generativa G que recebe uma entrada aleatória z com densidade p_z e retorna uma saída x_g = G (z) que deve seguir (após o treinamento) a distribuição de probabilidade alvo.
  2. Uma rede discriminativa D que recebe uma entrada x que pode ser uma entrada “verdadeira” (x_t, cuja densidade é denotada p_t) ou uma entrada “gerada” (x_g, cuja densidade p_g é a densidade induzida pela densidade p_z através de G) e que retorna a probabilidade D (x) de x para ser um dado “verdadeiro”.

Vamos agora examinar mais de perto a função de perda “teórica” das GANs. Se enviarmos ao discriminador dados “verdadeiros” e “gerados” nas mesmas proporções, o erro absoluto esperado do discriminador poderá ser expresso como:

 

form1

 

O objetivo do gerador é enganar o discriminador cujo objetivo é ser capaz de distinguir entre dados verdadeiros e dados gerados. Portanto, ao treinar o gerador, queremos maximizar esse erro enquanto tentamos minimizá-lo para o discriminador. Isso nos dá a fórmula abaixo:

form2

Para qualquer gerador G (juntamente com a densidade de probabilidade induzida p_g), o melhor discriminador possível é aquele que minimiza a integral abaixo:

 

form3

 

Para minimizar (em relação a D) essa integral, podemos minimizar a função dentro da integral para cada valor de x. Em seguida, definimos o melhor discriminador possível para um determinado gerador:

form4

(de fato, um dos melhores porque x valores tais que p_t (x) = p_g (x) podem ser manipulados de outra maneira, mas isso não importa para o que segue). Em seguida, pesquisamos G que maximiza:

 

form5

 

Parece complexo? É menos do que parece. Novamente, para maximizar (em relação a G) essa integral, podemos maximizar a função dentro da integral para cada valor de x. Como a densidade p_t é independente do gerador G, não podemos fazer melhor do que definir G de modo que:

form7

Obviamente, como p_g é uma densidade de probabilidade que deve se integrar a 1, necessariamente temos o melhor G:

 

form8

 

Assim, mostramos que, em um caso ideal com gerador e discriminador de capacidade ilimitada, o ponto ideal do cenário adversário é tal que o gerador produz a mesma densidade que a densidade real e o discriminador não pode fazer melhor do que ser verdadeiro em um caso a cada dois, exatamente como a intuição nos disse. Por fim, observe também que G maximiza:

 

form6

 

Na fórmula acima vemos que G deseja maximizar a probabilidade esperada de o discriminador estar errado.

Conclusão Sobre as GANs

As GANs são bem recentes e possuem uma ideia inovadora sobre como treinar uma arquitetura de rede neural. Muitos estudos e aplicações vem sendo feitos em todo mundo e já existem até algumas aplicações comerciais usando essa arquitetura de Deep Learning. 

Pontos mais importantes sobre as GANs:

  • Computadores podem basicamente gerar variáveis ​​pseudo-aleatórias simples (por exemplo, eles podem gerar variáveis ​​que seguem muito de perto uma distribuição uniforme).
  • Existem maneiras diferentes de gerar variáveis ​​aleatórias mais complexas, incluindo a noção de “método de transformação” que consiste em expressar uma variável aleatória em função de algumas variáveis ​​aleatórias mais simples.
  • No aprendizado de máquina, os modelos generativos tentam gerar dados de uma determinada distribuição de probabilidade (complexa). Modelos generativos de aprendizado profundo são modelados como redes neurais (funções muito complexas) que recebem como entrada uma variável aleatória simples e retornam uma variável aleatória que segue a distribuição direcionada (como “método de transformação”).
  • Essas redes generativas podem ser treinadas “diretamente” (comparando a distribuição dos dados gerados com a verdadeira distribuição): esta é a ideia das redes correspondentes generativas
  • Essas redes generativas também podem ser treinadas “indiretamente” (tentando enganar outra rede treinada ao mesmo tempo para distinguir dados “gerados” de dados “verdadeiros”): essa é a ideia das redes adversas generativas.
  • Mesmo que o “hype” que rodeia os GANs seja talvez um pouco exagerado, podemos dizer que a ideia de treinamento antagônico sugerida por Ian Goodfellow e seus co-autores é realmente ótima. Essa maneira de distorcer a função de perda, passando de uma comparação direta para uma indireta, é realmente algo que pode ser muito inspirador para futuros trabalhos na área de aprendizado profundo.

Referências:

Customizando Redes Neurais com Funções de Ativação Alternativas

A Beginner’s Guide to Generative Adversarial Networks (GANs)

A Leap into the Future: Generative Adversarial Networks

Understanding Generative Adversarial Networks (GANs)

How A.I. Is Creating Building Blocks to Reshape Music and Art

Train longer, generalize better: closing the generalization gap in large batch training of neural networks

Practical Recommendations for Gradient-Based Training of Deep Architectures

Gradient-Based Learning Applied to Document Recognition

Neural Networks & The Backpropagation Algorithm, Explained

Neural Networks and Deep Learning

Recurrent neural network based language model

The Elements of Statistical Learning: Data Mining, Inference, and Prediction, Second Edition

Gradient Descent For Machine Learning

Deep Learning Book

Capítulo 56 – Modelos Generativos – O Diferencial das GANs (Generative Adversarial Networks)

by

Neste capítulo discutiremos o funcionamento dos modelos generativos, o principal diferencial nas GANs (Generative Adversarial Networks). Estamos considerando que você leu o capítulo anterior.

Suponha que estamos interessados ​​em gerar imagens quadradas em preto e branco de cães com um tamanho de n por n pixels. Podemos remodelar cada dado como um vetor dimensional N = n x n (empilhando colunas umas sobre as outras), de modo que uma imagem de cachorro possa ser representada por um vetor (quem sabe conseguimos criar um modelo capaz de diferenciar um Muffin de um Chihuahua).

 

dogs

 

No entanto, isso não significa que todos os vetores representem um cão que foi moldado de volta a um quadrado! Portanto, podemos dizer que os vetores dimensionais N que efetivamente geram algo que se parece com um cachorro são distribuídos de acordo com uma distribuição de probabilidade muito específica em todo o espaço vetorial dimensional N (alguns pontos desse espaço provavelmente representam cães, enquanto é improvável para alguns outros). No mesmo espírito, existe, nesse espaço vetorial dimensional N, distribuições de probabilidade para imagens de gatos, pássaros e assim por diante.

Então, o problema de gerar uma nova imagem do cão é equivalente ao problema de gerar um novo vetor após a “distribuição de probabilidade do cão” no espaço vetorial dimensional N. De fato, estamos enfrentando um problema de gerar uma variável aleatória com relação a uma distribuição de probabilidade específica (lembra do que discutimos no capítulo anterior?).

Neste ponto, podemos mencionar duas coisas importantes. Primeiro, a “distribuição de probabilidade canina” que mencionamos é uma distribuição muito complexa em um espaço muito grande. Segundo, mesmo se pudermos assumir a existência de tal distribuição subjacente (na verdade existem imagens que se parecem com cachorro e outras que não), obviamente não sabemos como expressar explicitamente essa distribuição. Os dois pontos anteriores dificultam bastante o processo de geração de variáveis ​​aleatórias a partir dessa distribuição. Vamos tentar resolver esses dois problemas a seguir.

Nosso primeiro problema ao tentar gerar nossa nova imagem de cachorro é que a “distribuição de probabilidade do cachorro” no espaço vetorial dimensional N é muito complexa e não sabemos como gerar diretamente variáveis ​​aleatórias complexas. No entanto, como sabemos muito bem como gerar N variáveis ​​aleatórias uniformes não correlacionadas, poderíamos fazer uso do método de transformação. Para fazer isso, precisamos expressar nossa variável aleatória N dimensional como resultado de uma função muito complexa aplicada a uma variável aleatória N dimensional simples!

Aqui, podemos enfatizar o fato de que encontrar a função de transformação não é tão simples quanto tomar a inversa de forma fechada da Função de Distribuição Cumulativa (que obviamente não sabemos) como fizemos ao descrever o método de transformação inversa no capítulo anterior. A função de transformação não pode ser expressa explicitamente e, então, precisamos aprender com os dados (essa é uma das razões pelas quais treinamos um modelo de Machine Learning).

Em seguida, a ideia é modelar a função de transformação por uma rede neural que tome como entrada uma variável aleatória uniforme N dimensional simples e que retorne como saída outra variável aleatória N dimensional que deve seguir, após o treinamento, a “distribuição de probabilidade canina” correta . Depois que a arquitetura da rede foi projetada, ainda precisamos treiná-la. Nas próximas duas seções, discutiremos duas maneiras de treinar essas redes generativas, incluindo a ideia de treinamento antagônico por trás das GANs!

 

model1

 

Redes de Correspondência Generativa (Generative Matching Networks)

Obs: a denominação de “redes de correspondência generativa” não é padrão. No entanto, podemos encontrar na literatura, por exemplo, “Redes de correspondência de momentos generativos” ou também “Redes de correspondência de recursos generativos”. Só queremos aqui usar uma denominação um pouco mais geral para o que descrevemos abaixo.

Treinando Modelos Generativos

Até agora, mostramos que nosso problema de gerar uma nova imagem de cachorro pode ser reformulado em um problema de gerar um vetor aleatório no espaço vetorial dimensional N que segue a “distribuição de probabilidade canina” e sugerimos o uso de um método de transformação , com uma rede neural para modelar a função de transformação.

Agora, ainda precisamos treinar (otimizar) a rede para expressar a função de transformação correta. Para isso, podemos sugerir dois métodos diferentes de treinamento: um direto e um indireto. O método de treinamento direto consiste em comparar as distribuições de probabilidade verdadeira e gerada e retropropagar a diferença (o erro) através da rede. Essa é a ideia que governa as redes de correspondência generativa (GMNs – Generative Matching Networks)).

Para o método de treinamento indireto, não comparamos diretamente as distribuições verdadeiras e geradas. Em vez disso, treinamos a rede generativa, fazendo com que essas duas distribuições passem por um ajuste escolhido de forma que o processo de otimização da rede generativa em relação à tarefa ajustada imponha que a distribuição gerada esteja próxima da verdadeira distribuição. Essa última ideia é a que está por trás das Redes Adversárias Generativas (GANs) que apresentaremos na próxima seção. Mas, por enquanto, vamos começar com o método direto e as GMNs.

Comparando duas distribuições de probabilidade com base em amostras

Como mencionado, a ideia das GMNs é treinar a rede generativa comparando diretamente a distribuição gerada com a verdadeira. No entanto, não sabemos como expressar explicitamente a verdadeira “distribuição de probabilidade de cães” e também podemos dizer que a distribuição gerada é complexa demais para ser expressa explicitamente. Portanto, comparações baseadas em expressões explícitas não são possíveis. Logo, se tivermos uma maneira de comparar distribuições de probabilidade com base em amostras, podemos usá-la para treinar a rede. De fato, temos uma amostra de dados verdadeiros e podemos, a cada iteração do processo de treinamento, produzir uma amostra de dados gerados.

Embora, em teoria, qualquer distância (ou medida de similaridade) capaz de comparar efetivamente duas distribuições baseadas em amostras possa ser usada, podemos mencionar, em particular, a abordagem da Discrepância Média Máxima (MMD). O MMD define uma distância entre duas distribuições de probabilidade que podem ser calculadas (estimadas) com base em amostras dessas distribuições. Embora não esteja totalmente fora do escopo deste capítulo, decidimos não gastar muito mais tempo descrevendo o MMD. No entanto, caso você queira mais detalhes sobre isso, recomendamos esses 3 papers abaixo:

Learning features to compare distributions

A Kernel Method for the Two-Sample-Problem

A Kernel Two-Sample Test

Retropropagação do erro de correspondência de distribuição

Assim, uma vez que definimos uma maneira de comparar duas distribuições com base em amostras, podemos definir o processo de treinamento da rede generativa em GMNs. Dada uma variável aleatória com distribuição de probabilidade uniforme como entrada, queremos que a distribuição de probabilidade da saída gerada seja a “distribuição de probabilidade do cão”. A ideia das GMNs é otimizar a rede repetindo as seguintes etapas:

  • Gerar algumas entradas uniformes.
  • Fazer essas entradas passarem pela rede e coletar as saídas geradas.
  • Comparar a verdadeira “distribuição de probabilidade de cães” e a gerada com base nas amostras disponíveis (por exemplo, calculando a distância MMD entre a amostra de imagens reais de cães e a amostra de imagens geradas).
  • Usar retropropagação para fazer uma etapa de descida de gradiente para diminuir a distância (por exemplo, MMD) entre distribuições verdadeiras e geradas.

Conforme descrito acima, ao seguir estas etapas, aplicamos uma descida do gradiente na rede com uma função de perda que é a distância entre as distribuições verdadeiras e as distribuições geradas na iteração atual.

O método de treinamento “indireto”

A abordagem “direta” apresentada acima compara diretamente a distribuição gerada com a verdadeira ao treinar a rede generativa. A brilhante ideia que governa as GANs consiste em substituir essa comparação direta por uma indireta que assume a forma de uma tarefa ajustada sobre essas duas distribuições.

O treinamento da rede generativa é então realizado com relação a essa tarefa, de modo que força a distribuição gerada a se aproximar cada vez mais da distribuição verdadeira.

A tarefa ajustada das GANs é uma tarefa de discriminação entre amostras verdadeiras e geradas. Ou poderíamos dizer uma tarefa de “não discriminação”, pois queremos que a discriminação falhe o máximo possível. Portanto, em uma arquitetura GAN, temos um discriminador, que coleta amostras de dados verdadeiros e gerados e tenta classificá-los da melhor maneira possível, e um gerador treinado para enganar o discriminador o máximo possível. Vamos ver em um exemplo simples porque as abordagens diretas e indiretas que mencionamos devem, em teoria, levar ao mesmo gerador ideal.

O caso ideal: gerador e discriminador perfeitos

Para entender melhor por que treinar um gerador para enganar um discriminador levará ao mesmo resultado que treinar diretamente o gerador para corresponder à distribuição de destino, vamos dar um exemplo unidimensional simples. Esquecemos, por enquanto, como o gerador e o discriminador são representados e os consideramos como noções abstratas (que serão especificadas na próxima subseção). Além disso, ambos são supostos “perfeitos” (com capacidades infinitas) no sentido de que não são limitados por nenhum tipo de modelo (parametrizado).

Suponha que tenhamos uma distribuição verdadeira, por exemplo, um gaussiano unidimensional e que desejemos um gerador que faça amostras dessa distribuição de probabilidade. O que chamamos de método de treinamento “direto” consistiria em ajustar iterativamente o gerador (iterações de descida de gradiente) para corrigir a diferença / erro medido entre distribuições verdadeiras e geradas. Por fim, supondo que o processo de otimização seja perfeito, devemos terminar com a distribuição gerada que corresponda exatamente à verdadeira distribuição.

 

norm

 

Para a abordagem “indireta”, devemos considerar também um discriminador. Presumimos por enquanto que esse discriminador é um tipo de oráculo que sabe exatamente quais são as distribuições verdadeira e gerada e que é capaz, com base nessas informações, de prever uma classe (“verdadeira” ou “gerada”) para qualquer ponto. Se as duas distribuições estiverem distantes, o discriminador será capaz de classificar facilmente e com um alto nível de confiança a maioria dos pontos que apresentamos. Se queremos enganar o discriminador, precisamos aproximar a distribuição gerada da verdadeira. O discriminador terá mais dificuldade em prever a classe quando as duas distribuições serão iguais em todos os pontos: nesse caso, para cada ponto, haverá chances iguais de ser “verdadeiro” ou “gerado” e, em seguida, o discriminador poderá ” fazer melhor do que ser verdadeiro em um caso em dois em média.

 

norm2

 

Nesse ponto, parece legítimo se perguntar se esse método indireto é realmente uma boa ideia. De fato, parece ser mais complicado (temos que otimizar o gerador com base em uma tarefa ajustada, em vez de diretamente nas distribuições) e requer um discriminador que consideramos aqui como um determinado oráculo, mas que, na realidade, não é conhecido. Nem perfeito. Para o primeiro ponto, a dificuldade de comparar diretamente duas distribuições de probabilidade com base em amostras contrabalança a aparente maior complexidade do método indireto. Para o segundo ponto, é óbvio que o discriminador não é conhecido. No entanto, pode ser aprendido!

A aproximação: redes neurais adversárias

Vamos agora descrever a forma específica que assume o gerador e o discriminador na arquitetura das GANs. O gerador é uma rede neural que modela uma função de transformação. Ele assume como entrada uma variável aleatória simples e deve retornar, uma vez treinada, uma variável aleatória que segue a distribuição de destino. Como é muito complicado e desconhecido, decidimos modelar o discriminador com outra rede neural. Essa rede neural modela uma função discriminativa. Ele toma como entrada um ponto (no nosso exemplo de cachorro, um vetor dimensional N) e retorna como saída a probabilidade desse ponto ser “verdadeiro”.

Observe que o fato de impormos agora um modelo parametrizado para expressar tanto o gerador quanto o discriminador (em vez das versões idealizadas na subseção anterior) não tem, na prática, um grande impacto no argumento / intuição teórica acima: apenas trabalhamos em alguns espaços parametrizados em vez de espaços completos ideais e, portanto, os pontos ideais que devemos alcançar no caso ideal podem ser vistos como “arredondados” pela capacidade de precisão dos modelos parametrizados.

Uma vez definidas, as duas redes podem ser treinadas em conjunto (ao mesmo tempo) com objetivos opostos:

  • O objetivo do gerador é enganar o discriminador; portanto, a rede neural generativa é treinada para maximizar o erro de classificação final (entre dados verdadeiros e gerados).
  • O objetivo do discriminador é detectar dados falsos gerados, para que a rede neural discriminativa seja treinada para minimizar o erro de classificação final.

Portanto, a cada iteração do processo de treinamento, os pesos da rede generativa são atualizados para aumentar o erro de classificação (subida do gradiente de erro sobre os parâmetros do gerador), enquanto os pesos da rede discriminativa são atualizados para diminuir esse erro (descida do gradiente de erro sobre os parâmetros do discriminador).

Esses objetivos opostos e a noção implícita de treinamento antagônico das duas redes explicam o nome de “redes adversárias”: ambas as redes tentam se derrotar e, ao fazê-lo, estão cada vez melhor. A competição entre elas faz com que essas duas redes “progridam” com relação a seus respectivos objetivos. Do ponto de vista da teoria dos jogos, podemos pensar nessa configuração como um jogo minimax para dois jogadores, em que o estado de equilíbrio corresponde à situação em que o gerador produz dados a partir da distribuição exata e onde o discriminador prediz “verdadeiro” ou “gerado” com probabilidade 1/2 para qualquer ponto que receber.

Os modelos GAN estão entre os mais avançados em Deep Learning e a ideia por trás da sua concepção é simples e brilhante.

Agora sim, estamos prontos para compreender a Matemática que faz tudo isso acontecer!

Está gostando do Deep Learning Book? Então ajude a continuarmos esse trabalho e compartilhe o Deep Learning Book entre seus amigos e rede de contatos.

Até o próximo capítulo!

Referências:

Customizando Redes Neurais com Funções de Ativação Alternativas

A Beginner’s Guide to Generative Adversarial Networks (GANs)

A Leap into the Future: Generative Adversarial Networks

Understanding Generative Adversarial Networks (GANs)

How A.I. Is Creating Building Blocks to Reshape Music and Art

Train longer, generalize better: closing the generalization gap in large batch training of neural networks

Practical Recommendations for Gradient-Based Training of Deep Architectures

Gradient-Based Learning Applied to Document Recognition

Neural Networks & The Backpropagation Algorithm, Explained

Neural Networks and Deep Learning

Recurrent neural network based language model

The Elements of Statistical Learning: Data Mining, Inference, and Prediction, Second Edition

Gradient Descent For Machine Learning

Pattern Recognition and Machine Learning

Deep Learning Book

Capítulo 55 – Geração de Variáveis Aleatórias – Uma das Bases dos Modelos Generativos em GANs (Generative Adversarial Networks)

by

Neste capítulo discutiremos o processo de geração de variáveis aleatórias a partir de uma determinada distribuição, tema importante para compreender o funcionamento dos modelos generativos das GANs e nos próximos capítulos veremos os detalhes de funcionamento dos modelos generativos e os detalhes matemáticos das GANs. Estamos considerando que você leu o capítulo anterior.

Variáveis ​​aleatórias uniformes podem ser geradas pseudo-aleatoriamente

Vamos começar discutindo o processo de geração de variáveis ​​aleatórias. Veremos alguns métodos existentes e, mais especificamente, o método de transformação inversa que permite gerar variáveis ​​aleatórias complexas a partir de variáveis ​​aleatórias uniformes simples. Embora tudo isso possa parecer um pouco distante do nosso assunto, GANs, veremos no próximo capítulo o vínculo profundo que existe com os modelos generativos. 

Uma variável aleatória é uma variável quantitativa, cujo resultado (valor) depende de fatores aleatórios. Um exemplo de uma variável aleatória é o resultado do lançamento de um dado que pode resultar em qualquer número entre 1 e 6. Embora possamos conhecer os seus possíveis resultados, o resultado em si depende de fatores de sorte (álea). Uma variável aleatória pode ser uma medição de um parâmetro que pode gerar valores diferentes. O conceito de variável aleatória é essencial em Estatística e em outros métodos quantitativos para a representação de fenômenos incertos. Este conceito é estudado em detalhes no curso Análise Estatística Para Data Science com Linguagem R.

As variáveis aleatórias podem ser classificadas em variáveis aleatórias discretas, contínuas e mistas.

Os computadores são fundamentalmente determinísticos. Portanto, é teoricamente impossível gerar números realmente aleatórios (a pergunta “o que realmente é aleatoriedade?” é algo difícil de responder). No entanto, é possível definir algoritmos que geram sequências de números cujas propriedades estão muito próximas das propriedades das sequências teóricas de números aleatórios. Em particular, um computador é capaz, usando um gerador de números pseudo-aleatórios, de gerar uma sequência de números que segue aproximadamente uma distribuição aleatória uniforme entre 0 e 1. O caso uniforme é muito simples, no qual variáveis ​​aleatórias mais complexas podem ser construídas.

Variáveis ​​aleatórias expressas como resultado de uma operação ou processo

Existem diferentes técnicas que visam gerar variáveis ​​aleatórias mais complexas. Entre elas, podemos encontrar, por exemplo, método de transformação inversa, amostragem por rejeição, algoritmo Metropolis-Hastings entre outros. Todos esses métodos se baseiam em diferentes truques matemáticos que consistem principalmente em representar a variável aleatória que queremos gerar como resultado de uma operação (sobre variáveis ​​aleatórias mais simples) ou de um processo.

A amostragem por rejeição expressa a variável aleatória como resultado de um processo que consiste em amostrar não da distribuição complexa, mas de uma distribuição simples bem conhecida e para aceitar ou rejeitar o valor amostrado, dependendo de alguma condição. Repetindo esse processo até que o valor amostrado seja aceito, podemos mostrar que, com a condição correta de aceitação, o valor que será efetivamente amostrado seguirá a distribuição correta.

No algoritmo Metropolis-Hastings, a ideia é encontrar uma Cadeia de Markov (MC – Markov Chain) de modo que a distribuição estacionária dessa MC corresponda à distribuição da qual gostaríamos de amostrar nossa variável aleatória. Uma vez encontrada essa MC, podemos simular uma trajetória longa o suficiente sobre ela para considerar que atingimos um estado estacionário e, em seguida, o último valor que obtemos dessa maneira pode ser considerado como extraído da distribuição de interesse.

Não iremos mais adiante nos detalhes da amostragem por rejeição e do Metropolis-Hastings, porque esses métodos não são os que nos levarão à noção por trás das GANs. No entanto, vamos nos concentrar um pouco mais no método de transformação inversa.

O método de transformação inversa

A ideia do método de transformação inversa é simplesmente representar nossa “complexa” variável aleatória como resultado de uma função aplicada a um variável aleatória uniforme, que nós sabemos como gerar.

Consideramos abaixo um exemplo unidimensional. Seja X uma variável aleatória complexa da qual queremos amostrar e U seja uma variável aleatória uniforme sobre [0,1] que sabemos como amostrar. Lembramos que uma variável aleatória é totalmente definida por sua Função de Distribuição Cumulativa (CDF). O CDF de uma variável aleatória é uma função do domínio de definição da variável aleatória até o intervalo [0,1] e definido, em uma dimensão, de modo que:

 

form1

 

No caso particular de nossa variável aleatória uniforme U, temos:

 

form2

 

Por uma questão de simplicidade, vamos supor aqui que a função CDF_X é inversível e seu inverso é indicado por:

 

form3

 

(o método pode ser facilmente estendido ao caso não inversível usando o inverso generalizado da função, mas não é realmente o ponto principal em que queremos focar aqui). Então, se definirmos:

 

form4

 

temos:

 

form5

 

Como podemos ver, Y e X têm o mesmo CDF e depois definem a mesma variável aleatória. Assim, definindo Y como acima (em função de uma variável aleatória uniforme), conseguimos definir uma variável aleatória com a distribuição alvo.

Para resumir, o método de transformação inversa é uma maneira de gerar uma variável aleatória que segue uma determinada distribuição, fazendo uma variável aleatória uniforme passar por uma “função de transformação” bem projetada (CDF inverso). Essa noção de “método de transformação inversa” pode, de fato, ser estendida à noção de “método de transformação” que consiste, de maneira mais geral, em gerar variáveis ​​aleatórias em função de algumas variáveis ​​aleatórias mais simples (não necessariamente uniformes e, em seguida, a função de transformação é não mais o CDF inverso). Conceitualmente, o objetivo da “função de transformação” é deformar / remodelar a distribuição de probabilidade inicial: a função de transformação começa de onde a distribuição inicial é muito alta em comparação com a distribuição de destino e a coloca onde é muito baixa. Foi exatamente isso que pensou o criador do modelo GAN e muitos consideram o conceito como uma espécie de “hack” na teoria estatística, o que gerou o modelo GAN.

Observe a ilustração do método de transformação inversa abaixo. Em azul: a distribuição uniforme em [0,1]. Em laranja: a distribuição gaussiana (normal) padrão. Em cinza: o mapeamento da distribuição uniforme para a gaussiana (CDF inverso).

 

dist

 

Compreendeu o conceito? Isso é o que está por trás dos modelos generativos nas GANs, que veremos no próximo capítulo!

Referências:

Customizando Redes Neurais com Funções de Ativação Alternativas

A Beginner’s Guide to Generative Adversarial Networks (GANs)

A Leap into the Future: Generative Adversarial Networks

Understanding Generative Adversarial Networks (GANs)

How A.I. Is Creating Building Blocks to Reshape Music and Art

Train longer, generalize better: closing the generalization gap in large batch training of neural networks

Practical Recommendations for Gradient-Based Training of Deep Architectures

Gradient-Based Learning Applied to Document Recognition

Neural Networks & The Backpropagation Algorithm, Explained

Neural Networks and Deep Learning

Recurrent neural network based language model

The Elements of Statistical Learning: Data Mining, Inference, and Prediction, Second Edition

Gradient Descent For Machine Learning

Pattern Recognition and Machine Learning

Deep Learning Book

Capítulo 53 – Matemática na GRU, Dissipação e Clipping do Gradiente

by

A capacidade da rede GRU de manter dependências ou memória de longo prazo decorre dos cálculos na célula na GRU para produzir o estado oculto. As LSTMs têm dois estados diferentes passados entre as células – o estado da célula e o estado oculto, que carregam a memória de longo e curto prazo, respectivamente – as GRUs têm apenas um estado oculto transferido entre as etapas do tempo. Esse estado oculto é capaz de manter as dependências de longo e curto prazo ao mesmo tempo, devido aos mecanismos de restrição (portões) e cálculos pelos quais o estado oculto e os dados de entrada passam.

 

gruxlstm

 

A célula GRU contém apenas dois portões: o portão de atualização e o portão de redefinição. Assim como os portões das LSTMs, os portões na GRU são treinados para filtrar seletivamente qualquer informação irrelevante, mantendo o que é útil. Esses portões são essencialmente vetores contendo valores entre 0 e 1 que serão multiplicados com os dados de entrada e/ou estado oculto. Um valor 0 nos vetores indica que os dados correspondentes no estado de entrada ou oculto não são importantes e, portanto, retornarão como zero. Por outro lado, um valor 1 no vetor significa que os dados correspondentes são importantes e serão usados.

Usaremos os termos gate e vetor de forma intercambiável para o restante deste capítulo, pois eles se referem à mesma coisa.

A estrutura de uma unidade GRU é mostrada abaixo.

 

 

gru1

 

 

Embora a estrutura possa parecer bastante complicada devido ao grande número de conexões, o mecanismo por trás dela pode ser dividido em três etapas principais.

Reset Gate

Na primeira etapa, criamos o portão de redefinição (reset gate). Essa porta é derivada e calculada usando o estado oculto da etapa anterior e os dados de entrada na etapa atual.

Matematicamente, isso é conseguido multiplicando o estado oculto anterior e a entrada atual com seus respectivos pesos e somando-os antes de passar a soma através de uma função sigmóide. A função sigmoide transformará os valores entre 0 e 1, permitindo que o portão filtre entre as informações menos importantes e mais importantes nas etapas subsequentes. A fórmula matemática é representada abaixo:

form1

Quando toda a rede é treinada através de backpropagation, os pesos na equação serão atualizados de forma que o vetor aprenda a reter apenas os recursos úteis.

O estado oculto anterior será primeiro multiplicado por um peso treinável e passará por uma multiplicação por elementos (produto Hadamard) com o vetor de redefinição. Esta operação decidirá quais informações serão mantidas nas etapas anteriores, juntamente com as novas entradas. Ao mesmo tempo, a entrada atual também será multiplicada por um peso treinável antes de ser somada com o produto do vetor de redefinição e do estado oculto anterior acima. Por fim, uma função tanh de ativação não linear será aplicada ao resultado final para obter r na equação abaixo.

form2

Update Gate

Em seguida, temos o portão de atualização (update gate). Assim como o reset gate, o update gate é calculado usando o estado oculto anterior e os dados de entrada atuais.

Os vetores Update e Reset gate são criados usando a mesma fórmula, mas os pesos multiplicados pela entrada e pelo estado oculto são exclusivos para cada portão, o que significa que os vetores finais para cada portão são diferentes. Isso permite que os portões sirvam a seus propósitos específicos.

form3

O vetor Update será submetido a multiplicação por elementos com o estado oculto anterior para obter u em nossa equação abaixo, que será usada para calcular nossa saída final posteriormente.

O vetor Update também será usado em outra operação posteriormente ao obter nossa saída final. O objetivo do update gate aqui é ajudar o modelo a determinar quanto das informações passadas armazenadas no estado oculto anterior precisam ser retidas para o futuro.

Combinando as Saídas

Na última etapa, reutilizaremos o portal Update e obteremos o estado oculto atualizado.

Desta vez, pegaremos a versão inversa em elementos do mesmo vetor Update (1 – Update gate) e faremos uma multiplicação em elementos com a nossa saída do reset gate, r. O objetivo desta operação é o gate Update determinar qual parte das novas informações deve ser armazenada no estado oculto.

Por fim, o resultado das operações acima será resumido com a nossa saída do portão Update na etapa anterior, u. Isso nos dará nosso novo e atualizado estado oculto.

form5

Podemos usar esse novo estado oculto como nossa saída para esse intervalo de tempo, passando-o por uma camada de ativação linear.

Solução do Problema de Dissipação/Explosão do Gradiente

Vimos os portões em ação. Sabemos como eles transformam nossos dados. Agora, vamos revisar seu papel geral no gerenciamento da memória da rede e falar sobre como eles resolvem o problema de dissipação/explosão do gradiente.

Como vimos nos mecanismos acima, o Reset Gate é responsável por decidir quais partes do estado oculto anterior devem ser combinadas com a entrada atual para propor um novo estado oculto.

E o Update Gate é responsável por determinar quanto do estado oculto anterior deve ser retido e qual parte do novo estado oculto proposto (derivado do Reset Gate) deve ser adicionado ao estado oculto final. Quando o Update Gate é multiplicado pela primeira vez com o estado oculto anterior, a rede escolhe quais partes do estado oculto anterior ele manterá em sua memória enquanto descarta o restante. Posteriormente, ele corrige as partes ausentes das informações quando usa o inverso do gate Update para filtrar o novo estado oculto proposto a partir do Reset Gate.

Isso permite que a rede retenha dependências de longo prazo. O Update Gate pode optar por manter a maioria das memórias anteriores no estado oculto se os valores do vetor Update estiverem próximos de 1 sem recalcular ou alterar todo o estado oculto.

O problema de dissipação/explosão do gradiente ocorre durante a propagação de retorno (backpropagation) ao treinar a RNN, especialmente se a RNN estiver processando longas sequências ou tiver várias camadas. O erro do gradiente calculado durante o treinamento é usado para atualizar o peso da rede na direção certa e na magnitude certa. No entanto, esse gradiente é calculado com a regra da cadeia (chain rule), começando no final da rede. Portanto, durante o backpropagation, os gradientes sofrerão continuamente multiplicações de matrizes e encolherão ou explodirão exponencialmente por sequências longas. Ter um gradiente muito pequeno significa que o modelo não atualiza seus pesos de maneira eficaz, enquanto gradientes extremamente grandes fazem com que o modelo seja instável.

Os portões nas LSTM e GRUs ajudam a resolver esse problema devido ao componente aditivo dos portões de atualização. Enquanto as RNNs tradicionais sempre substituem todo o conteúdo do estado oculto a cada etapa, as LSTMs e GRUs mantêm a maior parte do estado oculto existente enquanto adicionam novo conteúdo sobre ele. Isso permite que os erros dos gradientes sejam propagados de volta sem desaparecer ou explodir muito rapidamente devido às operações de adição.

Clipping (Recorte) do Gradiente

Embora LSTMs e GRUs sejam as correções mais usadas para o problema acima, outra solução para o problema de explosão de gradientes é o clipping do gradiente. 

O clipping do gradiente é mais comum em redes neurais recorrentes. Quando os gradientes estão sendo propagados no tempo, eles podem desaparecer porque são continuamente multiplicados por números menores que um. Isso é chamado de problema de dissipação do gradiente, podendo ser resolvido por LSTMs e GRUs e, se você estiver usando uma rede profunda feed-forward, isso é resolvido por conexões residuais. Por outro lado, você também pode ter  explosão dos gradientes. É quando eles se tornam exponencialmente grandes por serem multiplicados por números maiores que 1. O clipping do gradiente cortará os gradientes entre dois números para impedir que eles fiquem muito grandes.

O clipping define um valor limite definido nos gradientes, o que significa que, mesmo se um gradiente aumentar além do valor predefinido durante o treinamento, seu valor ainda será limitado ao limite definido. Dessa forma, a direção do gradiente permanece inalterada e apenas a magnitude do gradiente é alterada.

Até o próximo capítulo!

Referências:

Customizando Redes Neurais com Funções de Ativação Alternativas

Understanding GRU Networks

Illustrated Guide to LSTM’s and GRU’s: A step by step explanation

Gated Recurrent Unit (GRU) 

A Recursive Recurrent Neural Network for Statistical Machine Translation

Sequence to Sequence Learning with Neural Networks

Recurrent Neural Networks Cheatsheet

On the difficulty of training recurrent neural networks

A Beginner’s Guide to LSTMs and Recurrent Neural Networks

Long Short-Term Memory (LSTM): Concept

Recurrent Neural Networks Tutorial, Part 1 – Introduction to RNNs

Recurrent Neural Networks Tutorial, Part 3 – Backpropagation Through Time and Vanishing Gradients

Train longer, generalize better: closing the generalization gap in large batch training of neural networks

Practical Recommendations for Gradient-Based Training of Deep Architectures

Gradient-Based Learning Applied to Document Recognition

Neural Networks & The Backpropagation Algorithm, Explained

Neural Networks and Deep Learning

Recurrent neural network based language model

The Elements of Statistical Learning: Data Mining, Inference, and Prediction, Second Edition

Gradient Descent For Machine Learning

Pattern Recognition and Machine Learning

Deep Learning Book

Capítulo 50 – A Matemática da Dissipação do Gradiente e Aplicações das RNNs

by

No Capítulo 34 nós discutimos sobre o problema da dissipação do gradiente e a dificuldade em treinar as redes neurais artificiais. Com as RNNs esse problema é ainda mais acentuado e por isso vamos agora estudar A Matemática da Dissipação do Gradiente e Aplicações das RNNs e compreender matematicamente porque o problema acontece.

Mencionamos anteriormente que as RNNs têm dificuldades em aprender dependências de longo alcance – interações entre palavras que estão separadas por vários passos, por exemplo. Isso é problemático porque o significado de uma frase em português é geralmente determinado por palavras que não são muito próximas: “O homem que usava uma peruca entrou no bar”. A frase é realmente sobre um homem entrando em um bar, não sobre a peruca. Mas é improvável que uma RNN simples seja capaz de capturar essas informações. Para entender porque, vamos dar uma olhada mais de perto no gradiente que calculamos no capítulo anterior:

 

form1

 

Observe que:

form2

 

é uma regra de cadeia em si! Por exemplo:

 

form3

 

Observe também que, como estamos tomando a derivada de uma função vetorial em relação a um vetor, o resultado é uma matriz (chamada de matriz jacobiana) cujos elementos são todos derivadas pointwise. Podemos reescrever o gradiente acima da seguinte forma:

 

form4

 

Acontece (esse paper explica isso em detalhes) que a norma (na álgebra linear, análise funcional e áreas relacionadas da matemática, uma norma é uma função que atribui um comprimento ou tamanho estritamente positivo a cada vetor em um espaço vetorial – exceto para o vetor zero, ao qual é atribuído um comprimento de zero), que você pode pensar como um valor absoluto, da matriz jacobiana acima tem um limite superior de 1. Isso porque a nossa função de ativação tanh (ou sigmóide) mapeia todos os valores em um intervalo entre -1 e 1, e a derivada é limitada por 1 (1/4 no caso de sigmoide) também:

 

tanh

 

Você pode ver que as funções tanh e sigmoid têm derivadas de 0 em ambas as extremidades, onde se aproximam de uma linha plana. Quando isso acontece, dizemos que os neurônios correspondentes estão saturados. Eles têm um gradiente nulo e conduzem outros gradientes nas camadas anteriores para 0. Assim, com valores pequenos nas multiplicações de matriz e múltiplas matrizes (t-k em particular) os valores de gradiente estão diminuindo exponencialmente rápido, desaparecendo completamente após alguns passos de tempo.

Contribuições gradientes de etapas “longínquas” se tornam zero e o estado nessas etapas não contribui para o que a rede está aprendendo: a rede acaba não aprendendo dependências de longo alcance. Dissipações do gradiente não são exclusivos das RNNs e também acontecem em Redes Neurais Profundas Feedforward. Mas as RNNs tendem a ser muito profundas (tão profundas quanto a duração da sentença, em um problema de Processamento de Linguagem Natural por exemplo), o que torna o problema muito mais comum.

É fácil imaginar que, dependendo de nossas funções de ativação e parâmetros de rede, poderíamos obter explosão em vez de dissipação de gradientes, se os valores da matriz Jacobiana forem grandes. Na verdade, isso é chamado de problema de explosão do gradiente. A razão pela qual as dissipações do gradiente receberam mais atenção do que as explosões é dupla. Por um lado, explosão de gradientes são óbvias. Seus gradientes se tornarão NaN (não um número) e seu programa falhará.

Em segundo lugar, recortar os gradientes em um limiar pré-definido é uma solução muito simples e eficaz para evitar a explosão dos gradientes. As dissipações dos gradientes são mais problemáticas porque não são óbvias quando ocorrem ou é mais complicado lidar com elas.

Felizmente, existem algumas maneiras de combater o problema da dissipação do gradiente. A inicialização adequada da matriz W pode reduzir o efeito do problema. Ou seja, aplicamos regularização. Uma solução mais interessante é usar as funções de ativação ReLU em vez de tanh ou sigmóide. A derivada ReLU é uma constante de 0 ou 1, por isso não é tão provável que sofra de dissipação do gradiente.

Uma solução ainda mais popular é usar as arquiteturas Long Short-Term Memory (LSTM) ou Gated Recurrent Unit (GRU). As LSTMs foram propostas pela primeira vez em 1997 e são os modelos talvez mais amplamente usados em Processamento de Linguagem Natural atualmente. As GRUs, propostas pela primeira vez em 2014, são versões simplificadas das LSTMs. Ambas as arquiteturas RNN foram explicitamente projetadas para lidar com dissipação do gradiente e aprender eficientemente dependências de longo alcance. Vamos cobrir as duas arquiteturas nos próximos capítulos.

Mas o que podemos fazer com as RNNs?

As RNNs mostraram grande sucesso em muitas tarefas de Processamento de Linguagem Natural. Neste ponto, devo mencionar que os tipos de RNN mais usados são as LSTMs, que são muito melhores na captura de dependências de longo prazo do que as RNNs em sua arquitetura padrão. Mas não se preocupe, as LSTMs são essencialmente a mesma coisa que as RNNs, mas apenas têm uma maneira diferente de computar o estado oculto. Cobriremos as LSTMs com mais detalhes no próximo capítulo. Aqui estão alguns exemplos de aplicações de RNNs em Processamento de Linguagem Natural (o que não é uma lista definitiva).

Modelagem de Linguagem e Geração de Texto

Dada uma sequência de palavras, queremos prever a probabilidade de cada palavra dada às palavras anteriores. Os Modelos de Linguagem nos permitem medir a probabilidade de uma sentença, que é uma entrada importante para a Tradução Automática (já que as sentenças de alta probabilidade estão normalmente corretas). Um efeito colateral de poder prever a próxima palavra é que obtemos um modelo generativo, que nos permite gerar um novo texto por amostragem a partir das probabilidades de saída. E dependendo de quais são nossos dados de treinamento, podemos gerar todos os tipos de coisas.

Em Modelagem de Linguagem, nossa entrada é tipicamente uma sequência de palavras (codificadas como vetores únicos), e nossa saída é a sequência de palavras previstas. Ao treinar a rede, definimos ot = x{t + 1}, pois queremos que a saída na etapa t seja a próxima palavra real.

Machine Translation

A tradução automática é semelhante à modelagem de linguagem, pois nossa entrada é uma sequência de palavras em nosso idioma de origem (por exemplo português). Queremos produzir uma sequência de palavras em nosso idioma de destino (por exemplo, inglês). A principal diferença é que nossa saída só é iniciada depois de termos visto a entrada completa, porque a primeira palavra de nossas sentenças traduzidas pode exigir informações capturadas da sequência de entrada completa.

Reconhecimento de Fala

Dada uma sequência de entrada de sinais acústicos de uma onda sonora, podemos prever uma sequência de segmentos fonéticos juntamente com suas probabilidades.

Gerar Descrições de Imagens

Juntamente com as redes neurais convolucionais, as RNNs foram usados como parte de um modelo para gerar descrições de imagens não rotuladas. É incrível como isso parece funcionar. O modelo combinado alinha as palavras geradas com os recursos encontrados nas imagens.

Continuamos no próximo capítulo.

Referências:

Matemática e Estatística Aplicada Para Data Science, Machine Learning e IA

A Recursive Recurrent Neural Network for Statistical Machine Translation

Sequence to Sequence Learning with Neural Networks

Recurrent Neural Networks Cheatsheet

On the difficulty of training recurrent neural networks

A Beginner’s Guide to LSTMs and Recurrent Neural Networks

Recurrent Neural Networks Tutorial, Part 1 – Introduction to RNNs

Recurrent Neural Networks Tutorial, Part 3 – Backpropagation Through Time and Vanishing Gradients

Train longer, generalize better: closing the generalization gap in large batch training of neural networks

Practical Recommendations for Gradient-Based Training of Deep Architectures

Gradient-Based Learning Applied to Document Recognition

Neural Networks & The Backpropagation Algorithm, Explained

Neural Networks and Deep Learning

Recurrent neural network based language model

The Elements of Statistical Learning: Data Mining, Inference, and Prediction, Second Edition

Gradient Descent For Machine Learning

Pattern Recognition and Machine Learning

Deep Learning Book

Capítulo 49 – A Matemática do Backpropagation Through Time (BPTT)

by

Estudamos no capítulo anterior as Redes Neurais Recorrentes. Mas para que elas funcionem, o algoritmo de treinamento precisa de um pequeno ajuste, uma vez que esse tipo de rede possui o que podemos chamar de “memória” durante seu treinamento. E o que faz isso acontecer é A Matemática do Backpropagation Through Time (BPTT), assunto deste capítulo do Deep Learning Book.

Um Pouco Mais Sobre as RNNs

A ideia por trás dos RNNs é fazer uso de informações sequenciais. Em uma rede neural tradicional, assumimos que todas as entradas (e saídas) são independentes umas das outras. Mas para muitas tarefas isso é uma ideia muito ruim. Se você quiser prever a próxima palavra em uma frase, é melhor saber quais palavras vieram antes dela. As RNNs são chamadas de recorrentes porque executam a mesma tarefa para todos os elementos de uma sequência, com a saída sendo dependente dos cálculos anteriores. Outra maneira de pensar sobre RNNs é que elas têm uma “memória” que captura informações sobre o que foi calculado até agora. Em teoria, RNNs podem fazer uso de informações em sequências arbitrariamente longas, mas, na prática, limitam-se a olhar para trás apenas alguns passos (mais sobre isso adiante). Aqui está o que uma RNN típica parece:

rnn

O diagrama acima mostra uma RNN sendo “desenrolada” ou “desdobrada” (termo unfolded em inglês) em uma rede completa. Ao desenrolar, simplesmente queremos dizer que escrevemos a rede para a sequência completa. Por exemplo, se a sequência que nos interessa é uma sentença de 5 palavras, a rede seria desdobrada em uma rede neural de 5 camadas, uma camada para cada palavra. As fórmulas que governam o cálculo que acontece em uma RNN são as seguintes:

1. xt é a entrada no passo de tempo t. Por exemplo, x1 poderia ser um vetor one-hot correspondente à segunda palavra de uma sentença.

2. st é o estado oculto no passo de tempo t. É a “memória” da rede. O termo st é calculado com base no estado oculto anterior e a entrada na etapa atual através da fórmula: st = f(Uxt + Wst-1). A função geralmente é uma não-linearidade, como tanh ou ReLU. Já s -1, que é necessário para calcular o primeiro estado oculto, é tipicamente inicializado com zero.

3. ot é a saída na etapa t. Por exemplo, se quiséssemos prever a próxima palavra em uma frase, seria um vetor de probabilidades em todo o nosso vocabulário. ot = softmax(Vst).

Há algumas coisas a serem observadas aqui:

Você pode pensar no estado oculto st como a memória da rede. st captura informações sobre o que aconteceu em todas as etapas de tempo anteriores. A saída na etapa ot é calculada exclusivamente com base na memória no tempo t. É um pouco mais complicado na prática, porque normalmente não é possível capturar informações de muitas etapas de tempo anteriores.

Ao contrário de uma rede neural profunda tradicional, que usa parâmetros diferentes em cada camada, uma RNN compartilha os mesmos parâmetros (U, V, W acima) em todas as etapas. Isso reflete o fato de que estamos executando a mesma tarefa em cada etapa, apenas com entradas diferentes. Isso reduz muito o número total de parâmetros que precisamos aprender.

O diagrama acima tem saídas em cada etapa de tempo, mas dependendo da tarefa, isso pode não ser necessário. Por exemplo, ao prever o sentimento de uma frase, podemos nos preocupar apenas com a saída final, não com o sentimento após cada palavra. Da mesma forma, podemos não precisar de entradas em cada etapa de tempo. A principal característica de uma RNN é seu estado oculto, que captura algumas informações sobre uma sequência.

E o Backpropagation?

Lembre-se, o objetivo das redes neurais recorrentes é classificar com precisão uma entrada sequencial (por exemplo, dada uma frase, prever o sentimento ou mesmo a próxima palavra). Contamos com a retropropagação (backpropagation) do erro e o gradiente descendente para fazê-lo.

A retropropagação em redes feedforward retrocede do erro final através das saídas, pesos e entradas de cada camada oculta, atribuindo a esses pesos a responsabilidade por uma parte do erro calculando suas derivadas parciais – ∂E / ∂w, ou a relação entre suas taxas de mudança. Essas derivações são então usadas por nossa regra de aprendizado, gradiente descendente, para ajustar os pesos para cima ou para baixo, qualquer que seja a direção que diminua o erro. Já estudamos isso nos capítulos anteriores aqui do Deep Learning Book.

As redes recorrentes dependem de uma extensão da retropropagação, chamada Backpropagation Through Time, ou BPTT. O tempo, neste caso, é simplesmente expresso por uma série ordenada e bem definida de cálculos, ligando um passo de tempo ao seguinte, o que significa que toda a retropropagação precisa funcionar.

Redes neurais, sejam elas recorrentes ou não, são simplesmente funções compostas aninhadas como f(g(h(x))). A adição de um elemento de tempo apenas estende a série de funções para as quais calculamos derivadas com a regra da cadeia (chain rule). Matemática pura!

A Matemática do Backpropagation Through Time (BPTT)

Por falar em Matemática, vamos compreender o BPTT através de fórmulas e alguns gráficos. Para as devidas referências, sempre consulte as notas ao final do capítulo.

Considere que estamos criando uma rede neural recorrente que seja capaz de prever a próxima palavra em um texto, o que pode ser útil em aplicações de IA para criar petições e assim ajudar advogados a automatizar o trabalho. Algo que já é feito pelo ROSS, o Robô Advogado.

Vamos começar com a equação básica de uma Rede Neural Recorrente:

form1

Também definimos nossa perda, ou erro, como a perda de entropia cruzada, dada por:

form2

Aqui, yt é a palavra correta no momento do passo t, e y^t é nossa previsão. Normalmente, tratamos a sequência completa (sentença) como um exemplo de treinamento, portanto, o erro total é apenas a soma dos erros em cada etapa de tempo (palavra).

rnn-bptt1

Lembre-se de que nosso objetivo é calcular os gradientes do erro em relação aos nossos parâmetros U, V e W e, em seguida, aprender bons parâmetros usando o Gradiente Descendente Estocástico. Assim como resumimos os erros, também somamos os gradientes em cada etapa de tempo para um exemplo de treinamento:

 

form3

 

Para calcular esses gradientes, usamos a regra de diferenciação da cadeia. Esse é o algoritmo de retropropagação quando aplicado para trás a partir do erro. Usaremos o E_3 como exemplo, apenas para trabalhar com números concretos.

 

form4

 

Acima, z3 = Vs3 e a última linha é o produto externo de dois vetores. Não se preocupe se você não seguir os passos acima, nós pulamos vários passos e você pode tentar calcular essas derivadas você mesmo (bom exercício!). O ponto que estou tentando transmitir é que

 

latex

 

depende apenas dos valores no momento atual, yˆ3, y3, s3. Se você tem estes valores, calculando o gradiente para V é uma multiplicação de matriz simples. Mas a história é diferente para W (e para U):

 

latex (1)

 

Para entender porque, escrevemos a regra da cadeia, como acima:

 

form5

 

Agora, note que s3 = tanh(Uxt + Ws2) depende de s2, que depende de W e s1, e assim por diante. Então, se pegarmos a derivada em relação a W, não podemos simplesmente tratar s2 como uma constante! Precisamos aplicar a regra da cadeia novamente e o que realmente temos é o seguinte:

 

form6

 

Somamos as contribuições de cada passo de tempo para o gradiente. Em outras palavras, como W é usado em todas as etapas até a saída que nos interessa, precisamos retroceder gradientes na rede de t = 3 até t = 0:

rnn-bptt-with-gradients

Observe que isso é exatamente o mesmo que o algoritmo de retropropagação padrão que usamos nas Redes Neurais Profundas da Feedforward. A principal diferença é que resumimos os gradientes para W em cada etapa de tempo. Em um rede neural tradicional, não compartilhamos parâmetros entre camadas, portanto, não precisamos somar nada. Mas, na minha opinião, o BPTT é apenas um nome sofisticado para retropropagação padrão em uma RNN “desenrolada”. Assim como com Backpropagation, você pode definir um vetor delta que você repassa, por exemplo:

 

form7

 

Aqui um exemplo de como seria a implementação do BPTT em Python:

bptt

 

Isso também deve lhe dar uma ideia do motivo pelo qual as RNNs são difíceis de treinar: as sequências (frases) podem ser bastante longas, talvez 20 palavras ou mais, e, portanto, você precisa retroceder através de várias camadas. Na prática, muitas pessoas truncam a retropropagação em poucos passos. Mas isso é assunto para o próximo capítulo! Até lá.

Referências:

Formação Inteligência Artificial Para o Direito 4.0

Recurrent Neural Networks Cheatsheet

A Beginner’s Guide to LSTMs and Recurrent Neural Networks

Recurrent Neural Networks Tutorial, Part 1 – Introduction to RNNs

Recurrent Neural Networks Tutorial, Part 3 – Backpropagation Through Time and Vanishing Gradients

Train longer, generalize better: closing the generalization gap in large batch training of neural networks

Practical Recommendations for Gradient-Based Training of Deep Architectures

Gradient-Based Learning Applied to Document Recognition

Neural Networks & The Backpropagation Algorithm, Explained

Neural Networks and Deep Learning

The Elements of Statistical Learning: Data Mining, Inference, and Prediction, Second Edition

Gradient Descent For Machine Learning

Pattern Recognition and Machine Learning

Deep Learning Book

Capítulo 34 – O Problema da Dissipação do Gradiente

by

Então, por que as redes neurais profundas são difíceis de treinar?

Para responder a essa pergunta, primeiro revisitemos o caso de uma rede com apenas uma camada oculta. Como de costume, usaremos o problema de classificação de dígitos MNIST o mesmo já estudado nos capítulos anteriores.

A partir de um shell do Python, nós carregamos os dados MNIST:

 

mnist

 

Montamos nossa rede:

 

mnist

 

Esta rede possui 784 neurônios na camada de entrada, correspondendo a 28 × 28 = 784 pixels na imagem de entrada. Utilizamos 30 neurônios ocultos, assim como 10 neurônios de saída, correspondentes às 10 classificações possíveis para os dígitos MNIST (‘0’, ‘1’, ‘2’,…, ‘9’).

Vamos tentar treinar nossa rede por 30 épocas completas, usando mini-lotes de 10 exemplos de treinamento por vez, uma taxa de aprendizado η = 0.1 e um parâmetro de regularização λ = 5.0. À medida que treinarmos, monitoramos a precisão da classificação no conjunto de dados validation_data. Podemos executar o script test.py com todos os comandos. Via prompt de comando ou terminal, digitamos: python test.py (o treinamento pode levar muitos minutos dependendo da velocidade do computador).

 

treinamento

 

Ao final do treinamento, obtemos uma precisão de classificação de 96,48% (aproximadamente), comparável a nossos resultados anteriores com uma configuração semelhante. Agora, vamos adicionar outra camada oculta, também com 30 neurônios, e tentar treinar com os mesmos hiperparâmetros. Usamos:

 

net = network2.Network([784, 30, 30, 10])

 

Isto dá uma melhor precisão de classificação 96,90%. Isso é encorajador: um pouco mais de profundidade está ajudando. Vamos adicionar outra camada oculta de 30 neurônios.

 

net = network2.Network([784, 30, 30, 30, 10])

 

Isso não ajuda em nada. Na verdade, o resultado cai para 96,57%, próximo à nossa rede original. E suponha que inserimos mais uma camada oculta.

 

net = network2.Network([784, 30, 30, 30, 30, 10])

 

Esse comportamento parece estranho. Intuitivamente, camadas ocultas extras devem tornar a rede capaz de aprender funções de classificação mais complexas e, assim, fazer uma melhor classificação. Certamente, as coisas não devem piorar, já que as camadas extras podem, no pior dos casos, simplesmente não fazer nada. Mas não é isso que está acontecendo.

Então, o que está acontecendo? Vamos supor que as camadas ocultas extras realmente possam ajudar em princípio e o problema é que nosso algoritmo de aprendizado não está encontrando os pesos e vieses corretos. Gostaríamos de descobrir o que está errado em nosso algoritmo de aprendizado e como fazer melhor.

Para entender melhor o que está errado, vamos visualizar como a rede aprende. Abaixo, traçamos parte de uma rede [784,30,30,10], ou seja, uma rede com duas camadas ocultas, cada uma contendo 30 neurônios ocultos. Cada neurônio no diagrama tem uma pequena barra nele, representando a rapidez com que o neurônio está mudando à medida que a rede aprende. Uma barra grande significa que o peso e o viés do neurônio estão mudando rapidamente, enquanto uma barra pequena significa que os pesos e o viés estão mudando lentamente. Mais precisamente, as barras indicam o gradiente ∂C / ∂b para cada neurônio, ou seja, a taxa de mudança do custo em relação ao viés do neurônio.

Nos capítulos anteriores, vimos que essa quantidade de gradiente controlava não apenas a rapidez com que o viés muda durante o aprendizado, mas também a rapidez com que os pesos inseridos no neurônio também mudam. Não se preocupe se você não se lembrar dos detalhes: a única coisa a ter em mente é simplesmente que essas barras mostram a rapidez com que os pesos e os vieses de cada neurônio mudam conforme a rede aprende.

Para manter o diagrama simples, mostrei apenas os seis principais neurônios nas duas camadas ocultas. Eu omiti os neurônios de entrada, pois eles não têm pesos nem viés para aprender. Eu também omiti os neurônios de saída, já que estamos fazendo comparações por camadas, e faz mais sentido comparar camadas com o mesmo número de neurônios. Os resultados foram plotados no início do treinamento, ou seja, imediatamente após a inicialização da rede. Aqui estão eles:

 

rede

 

A rede foi inicializada aleatoriamente e, portanto, não é surpreendente que haja muita variação na rapidez com que os neurônios aprendem. Ainda assim, uma coisa que vale ressaltar é que as barras na segunda camada oculta são em sua maioria muito maiores que as barras na primeira camada oculta. Como resultado, os neurônios da segunda camada oculta aprendem um pouco mais rápido que os neurônios da primeira camada oculta. Isso é meramente uma coincidência, ou os neurônios da segunda camada oculta provavelmente aprenderão mais rápido do que os neurônios na primeira camada oculta em geral?

Para determinar se esse é o caso, é útil ter uma maneira global de comparar a velocidade de aprendizado na primeira e segunda camadas ocultas. Para fazer isso, vamos indicar o gradiente como δlj = ∂C / ∂blj, ou seja, o gradiente para o neurônio jth na camada lth.

Podemos pensar no gradiente δ1 como um vetor cujas entradas determinam a rapidez com que a primeira camada oculta aprende, e δ2 como um vetor cujas entradas determinam a rapidez com que a segunda camada oculta aprende. Em seguida, usaremos os comprimentos desses vetores como medidas globais da velocidade na qual as camadas estão aprendendo. Assim, por exemplo, o comprimento “δ1” mede a velocidade na qual a primeira camada oculta está aprendendo, enquanto o comprimento “δ2” mede a velocidade na qual a segunda camada oculta está aprendendo.

Com essas definições, e na mesma configuração que foi plotada acima, encontramos δδ1 = 0.07… e δδ2 = 0.31…. Isso confirma nossa suspeita anterior: os neurônios na segunda camada oculta realmente estão aprendendo muito mais rápido que os neurônios da primeira camada oculta.

O que acontece se adicionarmos mais camadas ocultas? Se tivermos três camadas ocultas, em uma rede [784,30,30,30,10], então as respectivas velocidades de aprendizado serão 0,012, 0,060 e 0,283. Novamente, as camadas ocultas anteriores estão aprendendo muito mais lentamente que as camadas ocultas posteriores. Suponha que adicionemos mais uma camada com 30 neurônios ocultos. Nesse caso, as respectivas velocidades de aprendizado são 0,003, 0,017, 0,070 e 0,285. O padrão é válido: as camadas iniciais aprendem mais lentamente que as camadas posteriores.

Temos observado a velocidade de aprendizado no início do treinamento, ou seja, logo após as redes serem inicializadas. Como a velocidade do aprendizado muda à medida que treinamos nossas redes? Vamos voltar para ver a rede com apenas duas camadas ocultas. A velocidade de aprendizado muda da seguinte forma:

 

training_speed_2_layers

 

Para gerar esses resultados, usamos a descida do gradiente em lote com apenas 1.000 imagens de treinamento, treinadas em mais de 500 épocas. Isso é um pouco diferente do que normalmente treinamos nos capítulos anteriores, mas acontece que o uso de gradiente estocástico em mini-lote dá resultados muito mais ruidosos (embora muito similares, quando você mede o ruído). Usar os parâmetros que escolhemos é uma maneira fácil de suavizar os resultados, para que possamos ver o que está acontecendo.

Em qualquer caso, como você pode ver, as duas camadas começam a aprender em velocidades muito diferentes (como já sabemos). A velocidade em ambas as camadas cai muito rapidamente, antes de se recuperar. Mas, apesar de tudo, a primeira camada oculta aprende muito mais lentamente do que a segunda camada oculta.

E quanto a redes mais complexas? Aqui estão os resultados de uma experiência semelhante, mas desta vez com três camadas ocultas (uma rede [784,30,30,30,10]):

 

training_speed_3_layers

 

Mais uma vez, as primeiras camadas ocultas aprendem muito mais lentamente do que as camadas ocultas posteriores. Finalmente, vamos adicionar uma quarta camada oculta (uma rede [784,30,30,30,30,10]) e ver o que acontece quando treinamos:

 

training_speed_4_layers

 

Mais uma vez, as primeiras camadas ocultas aprendem muito mais lentamente do que as camadas ocultas posteriores. Nesse caso, a primeira camada oculta está aprendendo aproximadamente 100 vezes mais lenta que a camada oculta final. Natural que estivéssemos tendo problemas para treinar essas redes antes!

Temos aqui uma observação importante: em pelo menos algumas redes neurais profundas, o gradiente tende a diminuir à medida que nos movemos para trás através das camadas ocultas. Isso significa que os neurônios nas camadas anteriores aprendem muito mais lentamente que os neurônios nas camadas posteriores. E, embora tenhamos visto isso em apenas uma única rede, há razões fundamentais pelas quais isso acontece em muitas redes neurais. O fenômeno é conhecido como O Problema da Dissipação do Gradiente ou The Vanishing Gradient Problem. Esse é um problema muito comum e ainda mais evidente em Redes Neurais Recorrentes, usadas em aplicações de Processamento de Linguagem Natural.

Por que o problema de dissipação do gradiente ocorre? Existem maneiras de evitar isso? E como devemos lidar com isso no treinamento de redes neurais profundas? Na verdade, aprenderemos rapidamente que não é inevitável, embora a alternativa também não seja muito atraente: às vezes, o gradiente fica muito maior nas camadas anteriores! Este problema é chamado de explosão do gradiente, e não é uma notícia muito melhor do que o problema da dissipação do gradiente. Geralmente, verifica-se que o gradiente em redes neurais profundas é instável, tendendo a explodir ou a desaparecer nas camadas anteriores. Essa instabilidade é um problema fundamental para o aprendizado baseado em gradiente em redes neurais profundas. É algo que precisamos entender e, se possível, tomar medidas para resolver.

Momentaneamente se afastando das redes neurais, imagine que estamos tentando minimizar numericamente uma função f(x) de uma única variável. Não seria uma boa notícia se a derivada f′(x) fosse pequena? Isso não significaria que já estávamos perto de um extremo? De forma semelhante, o pequeno gradiente nas primeiras camadas de uma rede profunda pode significar que não precisamos fazer muito ajuste dos pesos e vieses?

Claro, isso não é o caso. Lembre-se de que inicializamos aleatoriamente o peso e os vieses na rede. É extremamente improvável que nossos pesos e vieses iniciais façam um bom trabalho em qualquer coisa que desejamos que nossa rede faça. Para ser concreto, considere a primeira camada de pesos em uma rede [784,30,30,30,10] para o problema MNIST.

A inicialização aleatória significa que a primeira camada elimina a maior parte das informações sobre a imagem de entrada. Mesmo que as camadas posteriores tenham sido extensivamente treinadas, elas ainda acharão extremamente difícil identificar a imagem de entrada, simplesmente porque elas não possuem informações suficientes. E assim, não é possível que não seja preciso aprender muito na primeira camada. Se vamos treinar redes profundas, precisamos descobrir como resolver o problema da dissipação do gradiente.

Se eu fosse você, não perderia o próximo capítulo com uma explicação matemática para esse importante fenômeno no treinamento de redes neurais profundas (Deep Learning).

Referências:

Orquestração de Fluxos de Dados com Apache Airflow

Practical Recommendations for Gradient-Based Training of Deep Architectures

Gradient-Based Learning Applied to Document Recognition

Neural Networks & The Backpropagation Algorithm, Explained

Neural Networks and Deep Learning (material usado com autorização do autor)

Machine Learning

The Elements of Statistical Learning: Data Mining, Inference, and Prediction, Second Edition

Gradient Descent For Machine Learning

Pattern Recognition and Machine Learning

Deep Learning Book

Capítulo 32 – Como Uma Rede Neural Artificial Encontra a Aproximação de Uma Função

by

Este é um capítulo muito importante para compreender como as redes neurais realmente funcionam e Como Uma Rede Neural Artificial Encontra a Aproximação de Uma Função. Acompanhe a explicação passo a passo analisando cada um dos gráficos apresentados.

Mas antes de explicar porque o teorema da universalidade é verdadeiro, quero mencionar duas advertências a esta declaração informal: “uma rede neural pode computar qualquer função”, que vimos no capítulo anterior.

Primeiro, isso não significa que uma rede possa ser usada para calcular exatamente qualquer função. Em vez disso, podemos obter uma aproximação que seja tão boa quanto desejamos. Aumentando o número de neurônios ocultos, podemos melhorar a aproximação. Por exemplo, anteriormente ilustramos uma rede computando alguma função f(x) usando três neurônios ocultos. Para a maioria das funções, apenas uma aproximação de baixa qualidade será possível usando três neurônios ocultos. Ao aumentar o número de neurônios ocultos (digamos, para cinco), podemos obter uma melhor aproximação:

rede

E podemos melhorar ainda mais aumentando o número de neurônios ocultos.

Para tornar esta afirmação mais precisa, suponha que tenhamos uma função f(x) que gostaríamos de computar com alguma precisão desejada ϵ > 0. A garantia é que usando neurônios ocultos suficientes sempre podemos encontrar uma rede neural cuja saída g(x) satisfaça | g(x) − f(x) | < ϵ, para todas as entradas x. Em outras palavras, a aproximação será boa dentro da precisão desejada para cada entrada possível.

A segunda ressalva é que a classe de funções que podem ser aproximadas da maneira descrita são as funções contínuas. Se uma função é descontínua, isto é, faz saltos bruscos e repentinos, então, em geral, não será possível aproximar usando uma rede neural. Isso não é surpreendente, já que nossas redes neurais calculam funções contínuas de sua entrada.

No entanto, mesmo que a função que realmente gostaríamos de computar fosse descontínua, muitas vezes a aproximação contínua é boa o suficiente. Se é assim, então podemos usar uma rede neural. Na prática, isso geralmente não é uma limitação importante.

Em suma, uma afirmação mais precisa do teorema da universalidade é que redes neurais com uma única camada oculta podem ser usadas para aproximar qualquer função contínua a qualquer precisão desejada. Neste e no próximo capítulo, vamos provar uma versão desse resultado.

Universalidade Com Uma Entrada e Uma Saída

Para entender por que o teorema da universalidade é verdadeiro, vamos começar entendendo como construir uma rede neural que se aproxima de uma função com apenas uma entrada e uma saída:

rede2

Este é o cerne do problema da universalidade. Uma vez que entendemos esse caso especial, é realmente fácil estender para funções com muitas entradas e muitas saídas (tema do próximo capítulo).

Para construir um insight sobre como construir uma rede para calcular f, vamos começar com uma rede contendo apenas uma camada oculta, com dois neurônios ocultos e uma camada de saída contendo um único neurônio de saída:

rede

Para ter uma ideia de como funcionam os componentes da rede, vamos nos concentrar no neurônio oculto superior. No diagrama abaixo, aumentando o valor de w, podemos ver imediatamente como a função computada pelo neurônio oculto superior muda:

rede

Como aprendemos anteriormente no livro, o que está sendo computado pelo neurônio oculto é σ(wx + b), onde σ(z) ≡ 1 / (1 + e^-z) é a função sigmóide. Até agora, fizemos uso frequente dessa forma algébrica. Mas, para a prova da universalidade, obteremos mais discernimento ignorando inteiramente a álgebra e, em vez disso, manipulando e observando a forma mostrada no gráfico. Isso não apenas nos dará uma ideia melhor do que está acontecendo, mas também nos dará uma prova de universalidade que se aplica a outras funções de ativação que não a função sigmóide.

Para começar esta prova, podemos aumentar o bias, b, no diagrama acima. Você verá que, conforme o bias aumenta, o gráfico se move para a esquerda, mas sua forma não muda.

Em seguida, podemos diminuir o viés (bias). Você verá que conforme o viés diminui, o gráfico se move para a direita, mas, novamente, sua forma não muda. Em seguida, diminuímos o peso para cerca de 2 ou 3. Você verá que à medida que diminui o peso, a curva se alarga. Talvez seja necessário alterar o bias também, para manter a curva no quadro.

Finalmente, aumentamos o peso acima de w = 100. A curva fica mais íngreme, até que, eventualmente, ela começa a parecer uma função de passo (Step Function). A imagem a seguir mostra como deve ser resultado:

rede3

Podemos simplificar um pouco nossa análise aumentando o peso para que a saída realmente seja uma Step Function, para uma aproximação muito boa. Abaixo eu plotei a saída do neurônio oculto superior quando o peso é w = 999. 

rede4

Na verdade, é um pouco mais fácil trabalhar com funções step do que com funções gerais sigmóides. A razão é que, na camada de saída, somamos contribuições de todos os neurônios ocultos. É fácil analisar a soma de várias funções step, mas é mais difícil pensar sobre o que acontece quando você adiciona um monte de curvas em forma de sigmóide.

E assim torna as coisas muito mais fáceis de assumir que nossos neurônios ocultos estão emitindo funções step. Mais concretamente, fazemos isso fixando o peso w como sendo um valor muito grande e, em seguida, definindo a posição da etapa modificando o bias. É claro que tratar a saída como uma função step é uma aproximação, mas é uma aproximação muito boa e, por enquanto, vamos tratá-la como exata. Voltarei mais tarde para discutir o impacto dos desvios dessa aproximação.

Em que valor de x a etapa ocorre? Em outras palavras, como a posição da etapa depende do peso e do viés?

Para responder a essa pergunta, podemos modificar o peso e o viés no diagrama acima. Você consegue descobrir como a posição da etapa depende de w e b. Com um pouco de trabalho, você deve ser capaz de se convencer de que a posição da etapa é proporcional a b e inversamente proporcional a w.

Na verdade, a etapa está na posição s = −b / w, como você pode ver modificando o peso e o bias no diagrama a seguir:

rede5

Isso simplificará muito nossas vidas para descrever os neurônios ocultos usando apenas um único parâmetro, s, que é a posição do passo, s = −b / w. 

rede6

Como mencionado acima, nós implicitamente definimos o peso w na entrada como um valor grande – grande o suficiente para que a função de passo seja uma boa aproximação. Podemos facilmente converter um neurônio parametrizado dessa maneira de volta ao modelo convencional, escolhendo o viés b = −ws.

Até agora, nos concentramos na saída apenas do neurônio oculto superior. Vamos dar uma olhada no comportamento de toda a rede. Em particular, vamos supor que os neurônios ocultos estejam computando funções de passos parametrizadas pelos pontos de degrau s1 (neurônio superior) e s2 (neurônio de baixo). E eles terão os respectivos pesos de saída w1 e w2. Aqui está a rede:

rede7

O que está sendo plotado à direita é a saída ponderada w1a1 + w2a2 da camada oculta. Aqui, a1 e a2 são as saídas dos neurônios ocultos superior e inferior, respectivamente. Essas saídas são frequentemente conhecidas como ativações dos neurônios.

Podemos aumentar ou diminuir o ponto de passo s1 do neurônio oculto superior e isso nos dá uma ideia de como isso altera a saída ponderada da camada oculta. Vale a pena entender o que acontece quando o s1 passa do s2. Você verá que o gráfico muda de forma quando isso acontece, já que nos movemos de uma situação em que o neurônio oculto superior é o primeiro a ser ativado para uma situação em que o neurônio oculto na parte inferior é o primeiro a ser ativado.

Da mesma forma, podemos manipular o ponto de passo s2 do neurônio oculto na parte inferior e ter uma ideia de como isso altera a saída combinada dos neurônios ocultos.

Finalmente, podemos definir w1 como 0.8 e w2 como −0.8. Você recebe uma função “bump”, que começa no ponto s1, termina no ponto s2 e tem a altura 0.8. Por exemplo, a saída ponderada pode ser assim:

rede8

Claro, podemos redimensionar o bump para ter qualquer altura. Vamos usar um único parâmetro, h, para indicar a altura. Para reduzir a confusão, também removerei as notações “s1 = …” e “w1 = …”.

rede9

Podemos alterar o valor de h para cima e para baixo, para ver como a altura do bump muda. 

Você notará, a propósito, que estamos usando nossos neurônios de uma forma que pode ser pensada não apenas em termos gráficos, mas em termos de programação mais convencionais, como uma espécie de declaração if-then-else, por exemplo:

code

Na maior parte eu vou ficar com o ponto de vista gráfico. Mas, no que se segue, às vezes você pode achar útil trocar pontos de vista e pensar sobre as coisas em termos de se-então-senão (uma das bases da programação convencional).

Podemos usar o nosso truque de fazer bump para obter dois solavancos, colando dois pares de neurônios ocultos na mesma rede:

rede11

Eu suprimi os pesos aqui, simplesmente escrevendo os valores h para cada par de neurônios ocultos. 

De maneira mais geral, podemos usar essa ideia para obter o máximo de picos que quisermos, de qualquer altura. Em particular, podemos dividir o intervalo [0,1] em um número grande, N, de subintervalos, e usar N pares de neurônios ocultos para configurar picos de qualquer altura desejada. Vamos ver como isso funciona para N = 5. Desculpa pela a complexidade do diagrama abaixo (eu poderia esconder a complexidade abstraindo mais, mas acho que vale a pena colocar um pouco de complexidade, para obter uma ideia mais concreta de como essas redes funciona):

rede

Você pode ver que existem cinco pares de neurônios ocultos. Os pontos escalonados para os respectivos pares de neurônios são 0,1 / 5, depois 1 / 5,2 / 5 e assim por diante, para 4 / 5,5 / 5. Esses valores são fixos – eles fazem com que tenhamos cinco saliências uniformemente espaçadas no gráfico.

Cada par de neurônios tem um valor de h associado a ele. Lembre-se, as conexões saídas dos neurônios têm pesos h e −h (não marcados). Ao alterar os pesos de saída, estamos realmente projetando a função!

Conforme alteramos as alturas, é possível ver a mudança correspondente nos valores h. E há também uma mudança nos pesos de saída correspondentes, que são + h e −h.

Em outras palavras, podemos manipular diretamente a função que aparece no gráfico à direita e ver isso refletido nos valores h à esquerda. 

Mas aqui consideramos uma entrada e uma saída, o que é bem simples. Com múltiplas entradas o conceito é basicamente o mesmo, mas iremos discutir as particularidades nos próximos capítulos, quando mergulharmos nas redes neurais profundas. Até lá.

Referências:

Construção e Deploy de Agentes de IA

Practical Recommendations for Gradient-Based Training of Deep Architectures

Gradient-Based Learning Applied to Document Recognition

Neural Networks & The Backpropagation Algorithm, Explained

Neural Networks and Deep Learning (material utilizado com autorização do autor)

The Elements of Statistical Learning: Data Mining, Inference, and Prediction, Second Edition

Gradient Descent For Machine Learning

Pattern Recognition and Machine Learning

Deep Learning Book

Capítulo 30 – Variações do Stochastic Gradient Descent – Hessian Optimization e Momentum

by

Cada técnica mostrada até aqui é valiosa e deve ser dominada por aqueles que pretendem trabalhar com redes neurais artificiais e aplicações de Inteligência Artificial, mas essa não é a única razão pela qual nós as explicamos.

O ponto principal é familiarizar você com alguns dos problemas que podem ocorrer nas redes neurais e com um estilo de análise que pode ajudar a superar esses problemas. De certo modo, aprendemos a pensar sobre redes neurais.

Agora neste capítulo, esquematizamos brevemente algumas outras técnicas. Esses esboços são menos aprofundados do que as discussões anteriores, mas devem transmitir algum sentimento pela diversidade de técnicas disponíveis para uso em redes neurais. Lembrando que você sempre pode estudar todas essas técnicas em detalhes nos cursos da Formação Engenheiro de Inteligência Artificial.

Variações do Stochastic Gradient Descent

A descida de gradiente estocástico pela retropropagação tem nos servido bem no ataque ao problema de classificação de dígitos do dataset MNIST. No entanto, existem muitas outras abordagens para otimizar a função de custo e, às vezes, essas outras abordagens oferecem desempenho superior ao gradiente estocástico em mini-lote. Neste capítulo discutiremos duas dessas abordagens, Hessian Optimization e Momentum.

Hessian Optimization

Para iniciar nossa discussão, ajuda a colocar as redes neurais de lado por um tempo. Em vez disso, vamos apenas considerar o problema abstrato de minimizar uma função de custo C que é uma função de muitas variáveis, w = w1, w2,…, então C = C(w). Pelo teorema de Taylor, a função custo pode ser aproximada perto de um ponto w por:

 

form1

Fórmula 1

 

Podemos reescrever isso de forma mais compacta:

form2

Fórmula 2

 

onde ∇C é o vetor gradiente usual e H é uma matriz conhecida como Matriz Hessiana. Suponha que nós aproximemos C descartando os termos de ordem superior representados por … acima:

 

Fórmula 3

 

Usando o cálculo, podemos mostrar que a expressão do lado direito pode ser minimizada escolhendo:

 

form4

Fórmula 4

 

Considerando que a Fórmula 3 é uma boa expressão aproximada para a função custo, então esperamos que a mudança do ponto w para form6 deva diminuir significativamente a função custo. Isso sugere um algoritmo possível para minimizar o custo:

  • Escolha um ponto de partida, w.
  • Atualize w para um novo ponto w ′ = w − H ^ − 1 ∇C, onde o Hessian H e ∇C são calculados em w.
  • Atualize w′ para um novo ponto w′′ = w′ − H′ ^ − 1 ∇′C, onde o Hessian H′ e ∇′C são calculados em w′.
    …

Na prática, a Fórmula 3 é apenas uma aproximação e é melhor dar passos menores. Fazemos isso alterando repetidamente w por uma quantidade form8 onde η é conhecido como taxa de aprendizado.

Essa abordagem para minimizar uma função de custo é conhecida como Hessian Technique ou Hessian Optimization. Existem resultados teóricos e empíricos mostrando que os métodos de Hessian convergem em um mínimo em menos etapas do que a descida de gradiente padrão. Em particular, ao incorporar informações sobre mudanças de segunda ordem na função de custo, é possível que a abordagem Hessiana evite muitas patologias que podem ocorrer na descida de gradiente. Além disso, há versões do algoritmo de retropropagação que podem ser usadas para computar o Hessian.

Se a Hessian Optimization é tão bom, por que não a estamos usando em nossas redes neurais? Infelizmente, embora tenha muitas propriedades desejáveis, tem uma propriedade muito indesejável: é muito difícil de aplicar na prática. Parte do problema é o tamanho da matriz Hessiana. Suponha que você tenha uma rede neural com 107 pesos e vieses. Em seguida, a matriz Hessiana correspondente conterá 107 × 107 = 1014 entradas. Isso é um número grande de entradas! E isso torna a computação H ^ − 1 ∇C extremamente difícil na prática. No entanto, isso não significa que não seja útil entender. De fato, há muitas variações na descida de gradiente que são inspiradas pela Hessian Optimization, mas que evitam o problema com matrizes excessivamente grandes. Vamos dar uma olhada em uma dessas técnicas, a descida do gradiente baseada em Momentum.

Momentum

Intuitivamente, a vantagem da Hessian Optimization é que ela incorpora não apenas informações sobre o gradiente, mas também informações sobre como o gradiente está mudando. A descida do gradiente baseada no Momentum baseia-se em uma intuição similar, mas evita grandes matrizes de derivadas secundárias. Para entender a técnica de Momentum, pense em nossa imagem original de descida do gradiente, na qual consideramos uma bola rolando em um vale (veja figura abaixo). Observamos que a descida do gradiente é, apesar de seu nome, apenas vagamente semelhante a uma bola caindo no fundo de um vale.

A técnica de Momentum modifica a descida do gradiente de duas maneiras que a tornam mais semelhante à imagem física. Primeiro, introduz uma noção de “velocidade” para os parâmetros que estamos tentando otimizar. O gradiente atua para alterar a velocidade, não (diretamente) a “posição”, da mesma maneira que as forças físicas alteram a velocidade, afetando apenas indiretamente a posição. Em segundo lugar, o método Momentum introduz um tipo de termo de fricção, que tende a reduzir gradualmente a velocidade.

Vamos dar uma descrição matemática mais precisa. Introduzimos variáveis de velocidade v = v1, v2,…, uma para cada variável wj correspondente. Então nós substituímos a regra de atualização de descida de gradiente w → w′ = w − η∇C por:

 

form5

 

Nessas equações, μ é um hiperparâmetro que controla a quantidade de amortecimento ou atrito no sistema. Para entender o significado das equações, é útil considerar primeiro o caso onde μ = 1, o que corresponde a nenhum atrito. Quando esse é o caso, a inspeção das equações mostra que a “força” ∇C está agora modificando a velocidade, v, e a velocidade está controlando a taxa de variação de w. Intuitivamente, nós aumentamos a velocidade adicionando repetidamente termos de gradiente a ela. Isso significa que se o gradiente estiver na (aproximadamente) mesma direção através de várias rodadas de aprendizado, poderemos desenvolver um pouco de vapor movendo-se nessa direção. Pense, por exemplo, no que acontece se estivermos nos movendo diretamente por um declive:

gradient

 

A cada passo a velocidade se torna maior no declive, então nos movemos mais e mais rapidamente para o fundo do vale. Isso pode permitir que a técnica de Momentum funcione muito mais rapidamente do que a descida de gradiente padrão. Claro, um problema é que, uma vez que chegarmos ao fundo do vale, vamos ultrapassar. Ou, se o gradiente deve mudar rapidamente, então podemos nos encontrar indo na direção errada. Essa é a razão para o hiperparâmetro µ nas equações acima.

Eu disse anteriormente que μ controla a quantidade de atrito no sistema; para ser um pouco mais preciso, você deve pensar em 1 − μ como a quantidade de atrito no sistema. Quando μ = 1, como vimos, não há atrito e a velocidade é completamente controlada pelo gradiente ∇C. Em contraste, quando μ = 0 há muito atrito, a velocidade não pode se acumular e as equações acima reduzem à equação usual para o gradiente descendente, w → w ′ = w − η∇C. Na prática, usar um valor intermediário entre 0 e 1 pode nos dar muito do benefício de ser capaz de aumentar a velocidade, mas sem causar overshooting. Podemos escolher um valor para μ usando os dados de validação retidos, da mesma maneira que selecionamos η e λ. 

Evitei nomear o hiperparâmetro μ até agora. A razão é que o nome padrão para μ é mal escolhido: é chamado de coeficiente de momentum. Isso é potencialmente confuso, já que μ não é de maneira alguma a noção de momento da física. Pelo contrário, está muito mais relacionado ao atrito. No entanto, o termo coeficiente de momentum é amplamente utilizado, por isso continuaremos a usá-lo.

Uma coisa boa sobre a técnica do Momentum é que não é preciso quase nenhum trabalho para modificar uma implementação de descida de gradiente para incorporar o Momentum. Ainda podemos usar a retropropagação para calcular os gradientes, assim como antes, e usar ideias como a amostragem de mini-lotes estocasticamente escolhidos. Desta forma, podemos obter algumas das vantagens da Hessian Optimization, usando informações sobre como o gradiente está mudando, mas sem as desvantagens e com apenas pequenas modificações no nosso código. Na prática, a técnica do Momentum é comumente usada e, muitas vezes, acelera o aprendizado.

Até o próximo capítulo!

Referências:

Deep Learning Para Aplicações de Inteligência Artificial com Python e C++

Practical Recommendations for Gradient-Based Training of Deep Architectures

Gradient-Based Learning Applied to Document Recognition

Neural Networks & The Backpropagation Algorithm, Explained

Neural Networks and Deep Learning

Machine Learning

The Elements of Statistical Learning: Data Mining, Inference, and Prediction, Second Edition

Gradient Descent For Machine Learning

Pattern Recognition and Machine Learning

Deep Learning Book

Capítulo 28 – Usando Early Stopping Para Definir o Número de Épocas de Treinamento

by

Ao treinar redes neurais, várias decisões precisam ser tomadas em relação às configurações (hiperparâmetros) usadas, a fim de obter um bom desempenho. Um desses hiperparâmetros é o número de épocas de treinamento: ou seja, quantas passagens completas do conjunto de dados (épocas) devem ser usadas? Se usarmos poucas épocas, poderemos ter problemas de underfitting (ou seja, não aprender tudo o que pudermos com os dados de treinamento); se usarmos muitas épocas, podemos ter o problema oposto, overfitting (“aprender demais”, ou seja, ajustar o “ruído” nos dados de treinamento, e não o sinal).

Usamos o Early Stopping (“Parada Antecipada” ou “Parada Precoce”) exatamente para tentar definir manualmente esse valor. Também pode ser considerado um tipo de método de regularização (como L1/L2 weight decay e dropout estudados anteriormente aqui no livro), pois pode impedir o overfitting da rede neural. A imagem abaixo ajuda a definir claramente o que é o Early Stopping:

 

earlystopping

 

Ao treinar uma rede neural, geralmente se está interessado em obter uma rede com desempenho ideal de generalização. No entanto, todas as arquiteturas de rede neural padrão, como o perceptron multicamada totalmente conectado, são propensas a overfitting. Enquanto a rede parece melhorar, isto é, o erro no conjunto de treinamento diminui, em algum momento durante o treinamento na verdade começa a piorar novamente, ou seja, o erro em exemplos invisíveis aumenta.

Normalmente, o erro de generalização é estimado pelo erro de validação, isto é, o erro médio em um conjunto de validação, um conjunto fixo de exemplos que não são do conjunto de treino. Existem basicamente duas maneiras de combater o overfitting: reduzindo o número de dimensões do espaço de parâmetros ou reduzindo o tamanho efetivo de cada dimensão. Técnicas para reduzir o número de parâmetros são aprendizagem construtiva gananciosa, poda ou compartilhamento de peso. Técnicas para reduzir o tamanho de cada dimensão de parâmetro são a regularização, como weight decay ou dropout, ou a Parada Precoce (Early Stopping). A parada precoce é amplamente usada porque é simples de entender e implementar e foi relatada como sendo superior aos métodos de regularização em muitos casos.

Usar Early Stopping significa que, no final de cada época, devemos calcular a precisão da classificação nos dados de validação. Quando a precisão parar de melhorar, terminamos o treinamento. Isso torna a configuração do número de épocas muito simples. Em particular, isso significa que não precisamos nos preocupar em descobrir explicitamente como o número de épocas depende dos outros hiperparâmetros, pois isso é feito automaticamente. Além disso, a Parada Antecipada também impede automaticamente o overfitting. Isto é, obviamente, uma coisa boa, embora nos estágios iniciais da experimentação possa ser útil desligar a Parada Antecipada, para que você possa ver quaisquer sinais de overfitting e usá-los para definir sua abordagem de regularização.

Para implementar a Parada Antecipada, precisamos dizer com mais precisão o que significa que a precisão da classificação parou de melhorar. Como já vimos, a precisão pode se mover um pouco, mesmo quando a tendência geral é melhorar. Se pararmos pela primeira vez, a precisão diminui, então quase certamente pararemos quando houver mais melhorias a serem feitas. Uma regra melhor é terminar se a melhor precisão de classificação não melhorar por algum tempo. Suponha, por exemplo, que estamos trabalhando com o dataset MNIST. Poderíamos optar por terminar se a precisão da classificação não melhorou durante as últimas dez épocas. Isso garante que não paremos cedo demais, em resposta à má sorte no treinamento, mas também que não estamos esperando para sempre uma melhoria que nunca acontece.

Esta regra de “parar o treinamento se não melhorar em dez épocas” é boa para a exploração inicial do MNIST. No entanto, as redes podem às vezes estabilizar-se perto de uma determinada precisão de classificação por algum tempo, apenas para começar a melhorar novamente. Se você está tentando obter um desempenho realmente bom, a regra de “parar o treinamento se não melhorar em dez épocas” pode ser muito agressiva. Nesse caso, sugerimos usar essa regra para a experimentação inicial e, gradualmente, adotar regras mais brandas, conforme entender melhor a maneira como sua rede treina: sem melhoria em vinte épocas, sem melhoria em cinquenta épocas e assim por diante. Claro, isso introduz um novo hiperparâmetro para otimizar! Na prática, no entanto, geralmente é fácil definir esse hiperparâmetro para obter bons resultados. Da mesma forma, para problemas diferentes do MNIST, a regra de não-melhoria-em-dez pode ser agressiva demais ou não ser agressiva o suficiente, dependendo dos detalhes do problema. No entanto, com um pouco de experimentação, geralmente é fácil encontrar uma boa estratégia para o Early Stopping. Isso faz parte do trabalho do Cientista de Dados ou do Engenheiro de Inteligência Artificial.

Até aqui, nós não usamos o Early Stopping em nossos experimentos MNIST. A razão é que temos feito muitas comparações entre diferentes abordagens de aprendizado. Para tais comparações, é útil usar o mesmo número de épocas em cada caso. No entanto, vale a pena modificar o network2.py para implementar o Early Stopping, e deixaremos isso como tarefa para você. Se precisar de ajuda, o Early Stopping é estudado em detalhes aqui.

Até o próximo capítulo!

Referências:

Formação Linguagem Python Para Data Science

Practical Recommendations for Gradient-Based Training of Deep Architectures

Gradient-Based Learning Applied to Document Recognition

Neural Networks & The Backpropagation Algorithm, Explained

Neural Networks and Deep Learning

Machine Learning

The Elements of Statistical Learning: Data Mining, Inference, and Prediction, Second Edition

Gradient Descent For Machine Learning

Pattern Recognition and Machine Learning

Deep Learning Book

Capítulo 27 – A Taxa de Aprendizado de Uma Rede Neural

by

Vamos continuar a discussão do capítulo anterior sobre a escolha dos hiperparâmetros de um modelo de rede neural, estudando um dos mais importantes, a taxa de aprendizado.

Suponha que executemos três redes neurais artificiais sendo treinadas com o dataset MNIST com três taxas de aprendizado diferentes, η = 0.025, η = 0.25 e η = 2.5, respectivamente. Vamos definir os outros hiperparâmetros de acordo com as experiências nos capítulos anteriores, executando mais de 30 epochs, com um tamanho de mini-lote de 10 e com λ = 5.0. Também voltaremos a usar todas as 50.000 imagens de treinamento. Aqui está um gráfico mostrando o comportamento do custo de treinamento enquanto treinamos:

multiple_eta

 

Com η = 0.025, o custo diminui suavemente até a época final. Com η = 0.25 o custo inicialmente diminui, mas após cerca de 20 épocas ele está próximo da saturação, e daí em diante a maioria das mudanças são meramente pequenas e aparentemente oscilações aleatórias. Finalmente, com η = 2.5, o custo faz grandes oscilações desde o início. Para entender o motivo das oscilações, lembre-se de que a descida estocástica do gradiente supostamente nos levará gradualmente a um vale da função de custo (conforme explicado aqui):

taxa de aprendizado

No entanto, se η for muito grande, os passos serão tão grandes que poderão, na verdade, ultrapassar o mínimo, fazendo com que o algoritmo simplesmente fique perdido durante o treinamento. Isso é provavelmente o que está causando a oscilação do custo quando η = 2.5. Quando escolhemos η = 0.25, os passos iniciais nos levam a um mínimo da função de custo, e é só quando chegamos perto desse mínimo que começamos a sofrer com o problema de overshooting. E quando escolhemos η = 0.025, não sofremos este problema durante as primeiras 30 épocas.

Claro, escolher η tão pequeno cria outro problema, que reduz a velocidade da descida estocástica do gradiente, aumentando o tempo total de treinamento. Uma abordagem ainda melhor seria começar com η = 0.25, treinar por 20 épocas e então mudar para η = 0.025. Discutiremos essas tabelas de taxas de aprendizado variáveis posteriormente. Por enquanto, porém, vamos nos ater a descobrir como encontrar um único valor bom para a taxa de aprendizado, η.

Com esta imagem em mente, podemos definir η da seguinte maneira. Primeiro, estimamos o valor limite para η no qual o custo nos dados de treinamento começa imediatamente a diminuir, em vez de oscilar ou aumentar. Essa estimativa não tem que ser muito precisa. Você pode estimar a ordem de magnitude começando com η = 0.01. Se o custo diminuir durante as primeiras épocas, então você deve sucessivamente tentar η = 0.1, 1.0,… até encontrar um valor para η onde o custo oscile ou aumente durante as primeiras poucas épocas (isso faz parte do trabalho de um Engenheiro de IA).

Alternativamente, se o custo oscilar ou aumentar durante as primeiras épocas, quando η = 0.01, então tente η = 0.001 ,0.0001,… até encontrar um valor para η onde o custo diminui durante as primeiras poucas épocas. Seguindo este procedimento, obteremos uma estimativa da ordem de magnitude para o valor limite de η. Você pode, opcionalmente, refinar sua estimativa, para escolher o maior valor de η no qual o custo diminui durante as primeiras poucas épocas, digamos η = 0.5 ou η = 0.2 (não há necessidade de que isso seja super-preciso). Isso nos dá uma estimativa para o valor limite de η. E claro, documente tudo!!!!

Obviamente, o valor real de η que você usa não deve ser maior que o valor limite. De fato, se o valor de η permanecer utilizável ao longo de muitas épocas, então você provavelmente desejará usar um valor para η que seja menor, digamos, um fator de dois abaixo do limite. Essa escolha normalmente permitirá que você treine por muitas épocas, sem causar muita lentidão no aprendizado.

No caso dos dados MNIST, seguir esta estratégia leva a uma estimativa de 0.1 para a ordem de magnitude do valor limite de η. Depois de um pouco mais de refinamento, obtemos um valor limite η = 0.5. Seguindo a prescrição acima, isso sugere usar η = 0.25 como nosso valor para a taxa de aprendizado. De fato, eu descobri que usar η = 0.5 funcionava bem o suficiente em 30 épocas que, na maioria das vezes, eu não me preocupava em usar um valor menor de η.

Tudo isso parece bastante simples. No entanto, usar o custo de treinamento para escolher η parece contradizer o que dissemos anteriormente, que escolheríamos os hiperparâmetros avaliando o desempenho usando nossos dados de validação. Na verdade, usaremos a precisão de validação para escolher o hiperparâmetro de regularização, o tamanho do mini-lote e os parâmetros de rede, como o número de camadas e neurônios ocultos, e assim por diante (estudaremos isso nos próximos capítulos).

Por que as coisas diferem para a taxa de aprendizado? Francamente, essa escolha é uma preferência estética pessoal e talvez seja um tanto idiossincrática. O raciocínio é que os outros hiperparâmetros são destinados a melhorar a precisão final da classificação no conjunto de testes, e por isso faz sentido selecioná-los com base na precisão da validação. No entanto, a taxa de aprendizado é apenas para influenciar a precisão final da classificação. Sua finalidade principal é realmente controlar o tamanho da etapa na descida do gradiente e monitorar o custo do treinamento é a melhor maneira de detectar se o tamanho da etapa é muito grande. Com isso dito, essa é uma preferência pessoal. No início, durante o aprendizado, o custo do treinamento geralmente diminui apenas se a precisão da validação melhorar e assim, na prática, é improvável que faça muita diferença em qual critério você usa.

No próximo capítulo tem mais. Até lá!

Referências:

Practical Recommendations for Gradient-Based Training of Deep Architectures

Gradient-Based Learning Applied to Document Recognition

Neural Networks & The Backpropagation Algorithm, Explained

Neural Networks and Deep Learning

Machine Learning

The Elements of Statistical Learning: Data Mining, Inference, and Prediction, Second Edition

Gradient Descent For Machine Learning

Pattern Recognition and Machine Learning

Deep Learning Book

Capítulo 26 – Como Escolher os Hiperparâmetros de Uma Rede Neural

by

Até agora não explicamos como foram escolhidos os valores dos hiperparâmetros como a taxa de aprendizado, η, o parâmetro de regularização, λ e assim por diante. Fornecemos valores que funcionaram muito bem, mas, na prática, quando você está usando redes neurais para resolver um problema, pode ser difícil encontrar bons parâmetros. Neste capítulo, começamos nosso estudo sobre Como Escolher os Hiperparâmetros de Uma Rede Neural. Vamos começar?

Imagine, por exemplo, que acabamos de ser apresentados ao dataset MNIST e começamos a trabalhar nele, sem saber nada sobre quais hiperparâmetros usar. Vamos supor que, por sorte, em nossos primeiros experimentos, escolhemos muitos dos hiperparâmetros da mesma forma como foi feito nos capítulos anteriores: 30 neurônios ocultos, um tamanho de mini-lote de 10, treinando por 30 épocas usando a entropia cruzada. Mas escolhemos uma taxa de aprendizado η = 10.0 e o parâmetro de regularização λ = 1000.0. Aqui está um exemplo de execução da rede:

 

net1

 

Nossas precisões de classificação não são melhores do que o acaso! Nossa rede está agindo como um gerador de ruído aleatório!

“Bem, isso é fácil de consertar”, você pode dizer, “apenas diminua a taxa de aprendizado e os hiperparâmetros de regularização”. Infelizmente, você não sabe a priori quais são os hiperparâmetros que você precisa ajustar. Talvez o verdadeiro problema seja que nossa rede de neurônios ocultos nunca funcionará bem, não importa como os outros hiperparâmetros sejam escolhidos? Talvez realmente precisemos de pelo menos 100 neurônios ocultos? Ou 300 neurônios ocultos? Ou várias camadas ocultas? Ou uma abordagem diferente para codificar a saída? Talvez nossa rede esteja aprendendo, mas precisamos treinar em mais épocas? Talvez os mini-lotes sejam pequenos demais? Talvez seja melhor voltarmos para a função de custo quadrático? Talvez precisemos tentar uma abordagem diferente para inicializar o peso? E assim por diante. 

É fácil sentir-se perdido com tantas escolhas e combinações possíveis para os hiperparâmetros. Isso pode ser particularmente frustrante se sua rede for muito grande ou usar muitos dados de treinamento, pois você pode treinar por horas, dias ou semanas, apenas para não obter resultados. Se a situação persistir, prejudicará sua confiança. Talvez as redes neurais sejam a abordagem errada para o seu problema? Talvez você devesse largar o emprego e trabalhar com a apicultura?

Nos próximos capítulos, explicaremos algumas heurísticas que podem ser usadas para definir os hiperparâmetros em uma rede neural. O objetivo é ajudá-lo a desenvolver um fluxo que permita que você faça um bom trabalho definindo hiperparâmetros. Claro, não vamos cobrir tudo sobre otimização de hiperparâmetros. Esse é um assunto enorme, e não é, de qualquer forma, um problema que já está completamente resolvido, nem existe um acordo universal entre os profissionais sobre as estratégias corretas a serem usadas. Há sempre mais um truque que você pode tentar para obter um pouco mais de desempenho da sua rede. Mas temos algumas heurísticas com as quais podemos começar.

Compreendendo a Situação – Estratégia Geral

Ao usar redes neurais para atacar um novo problema, o primeiro desafio é obter qualquer aprendizado não-trivial, ou seja, para que a rede obtenha resultados melhores que o acaso. Isso pode ser surpreendentemente difícil, especialmente ao confrontar uma nova classe de problemas. Vejamos algumas estratégias que você pode usar se tiver esse tipo de problema.

Suponha, por exemplo, que você esteja atacando o MNIST pela primeira vez. Você começa entusiasmado, mas fica um pouco desanimado quando sua primeira rede falha completamente, como no exemplo acima. O caminho a percorrer é reduzir o tamanho do problema. Livre-se de todas as imagens de treinamento e validação, exceto imagens de 0s ou 1s. Em seguida, tente treinar uma rede para distinguir 0s de 1s. Não só isso é um problema inerentemente mais fácil do que distinguir todos os dez dígitos, como também reduz a quantidade de dados de treinamento em 80%, acelerando o treinamento por um fator de 5. Isso permite experimentações muito mais rápidas e, portanto, fornece uma visão mais rápida sobre como construir uma boa rede.

Você pode acelerar ainda mais a experimentação, desmembrando sua rede na rede mais simples, provavelmente fazendo aprendizado significativo. Se você acredita que uma rede [784, 10] provavelmente faz uma classificação melhor que o acaso com o dataset de dígitos MNIST, então comece sua experimentação com essa rede. Vai ser muito mais rápido do que treinar uma rede [784, 30, 10], e você pode “falhar” mais rápido (este é um conceito muito comum nos EUA: “fail fast”, ou seja, cometa falhas o mais rápido possível e aprenda com elas. Não se preocupe em tentar atingir a perfeição, pois você não vai conseguir de qualquer forma).

Você pode acelerar mais na experimentação aumentando a frequência de monitoramento. No network2.py, monitoramos o desempenho no final de cada época de treinamento. Com 50.000 imagens por época, isso significa esperar um pouco – cerca de dez segundos por época, no meu laptop, ao treinar uma rede [784, 30, 10] – antes de obter feedback sobre o quanto a rede está aprendendo.

É claro que dez segundos não são muito longos, mas se você quiser testar dezenas de opções de hiperparâmetros, é irritante, e se você quiser testar centenas ou milhares de opções, isso começa a ficar debilitante. Podemos obter feedback mais rapidamente, monitorando a precisão da validação com mais frequência, digamos, a cada 1.000 imagens de treinamento. Além disso, em vez de usar o conjunto completo de 10.000 imagens de validação para monitorar o desempenho, podemos obter uma estimativa muito mais rápida usando apenas 100 imagens de validação.

Tudo o que importa é que a rede veja imagens suficientes para aprender de verdade e obter uma boa estimativa aproximada de desempenho. Claro, nosso programa network2.py atualmente não faz esse tipo de monitoramento. Mas, como um clímax para obter um efeito semelhante para fins de ilustração, vamos reduzir nossos dados de treinamento para apenas as primeiras 1.000 imagens de treinamento MNIST. Vamos tentar e ver o que acontece. (Para manter o código abaixo simples, não implementei a ideia de usar apenas imagens 0 e 1. Claro, isso pode ser feito com um pouco mais de trabalho).

 

net2

 

Ainda estamos recebendo puro ruído! Mas há uma grande vitória: agora estamos obtendo feedback em uma fração de segundo, em vez de uma vez a cada dez segundos. Isso significa que você pode experimentar mais rapidamente outras opções de hiperparâmetros, ou até mesmo conduzir experimentos testando muitas opções diferentes de hiperparâmetros quase simultaneamente.

No exemplo acima, eu deixamos λ como λ = 1000.0, como usamos anteriormente. Mas como mudamos o número de exemplos de treinamento, deveríamos realmente mudar λ para manter weight decay o mesmo. Isso significa mudar λ para 20.0. Se fizermos isso, então é o que acontece:

 

net3

 

Ah! Nós temos um sinal. Não é um sinal muito bom, mas um sinal, no entanto. Isso é algo que podemos construir, modificando os hiperparâmetros para tentar melhorar ainda mais. Talvez nós achemos que nossa taxa de aprendizado precisa ser maior. (Como você talvez perceba, é um palpite bobo, por razões que discutiremos em breve, mas chegaremos lá. Não existe atalho para o aprendizado). Então, para testar nosso palpite, tentamos alterar η até 100.0:

 

net4

 

Isso não é bom, pois sugere que nosso palpite estava errado e o problema não era que a taxa de aprendizado fosse muito baixa. Então, em vez disso, tentamos alterar η para η = 1.0:

 

net5

 

Agora ficou melhor! E assim podemos continuar, ajustando individualmente cada hiperparâmetro, melhorando gradualmente o desempenho. Uma vez feita a exploração para encontrar um valor melhor para η, seguimos para encontrar um bom valor para λ. Em seguida, experimente uma arquitetura mais complexa, digamos uma rede com 10 neurônios ocultos e ajuste os valores para η e λ novamente. Depois, aumente para 20 neurônios ocultos e então, ajuste outros hiperparâmetros um pouco mais e assim por diante, em cada estágio avaliando o desempenho usando nossos dados de validação e usando essas avaliações para encontrar melhores hiperparâmetros. Ao fazer isso, normalmente leva mais tempo para testemunhar o impacto devido a modificações dos hiperparâmetros, e assim podemos diminuir gradualmente a frequência de monitoramento.

Tudo isso parece muito promissor como uma estratégia ampla. No entanto, quero voltar a esse estágio inicial de encontrar hiperparâmetros que permitem que uma rede aprenda qualquer coisa. De fato, mesmo a discussão acima transmite uma perspectiva muito positiva. Pode ser extremamente frustrante trabalhar com uma rede que não está aprendendo nada.

Você pode ajustar os hiperparâmetros por dias e ainda não obter uma resposta significativa. Por isso, gostaria de enfatizar novamente que, durante os primeiros estágios, você deve se certificar de que pode obter um feedback rápido dos experimentos. Intuitivamente, pode parecer que simplificar o problema e a arquitetura apenas irá atrasá-lo. Na verdade, isso acelera as coisas, pois você encontra muito mais rapidamente uma rede com um sinal significativo. Uma vez que você tenha recebido tal sinal, muitas vezes você pode obter melhorias rápidas aprimorando os hiperparâmetros. Assim como em tudo na vida, começar pode ser a coisa mais difícil a se fazer.

Ok, essa é a estratégia geral. Vamos agora olhar algumas recomendações específicas para definir hiperparâmetros. Vou me concentrar na taxa de aprendizado, η, no parâmetro de regularização L2, λ e no tamanho do mini-lote. No entanto, muitas das observações também se aplicam a outros hiperparâmetros, incluindo aqueles associados à arquitetura de rede, outras formas de regularização e alguns hiperparâmetros que encontraremos mais adiante aqui no Deep Learning Book, como o coeficiente momentum.

Oh não! O capítulo acabou! Fique tranquilo, continuamos no próximo. Até lá!

Referências:

Machine Learning Para Advogados

Practical Recommendations for Gradient-Based Training of Deep Architectures

Gradient-Based Learning Applied to Document Recognition

Neural Networks & The Backpropagation Algorithm, Explained

Neural Networks and Deep Learning

Machine Learning

The Elements of Statistical Learning: Data Mining, Inference, and Prediction, Second Edition

Gradient Descent For Machine Learning

Pattern Recognition and Machine Learning

Deep Learning Book

Capítulo 24 – Expandir Artificialmente os Dados de Treinamento

by

Vimos anteriormente que a precisão da classificação com o dataset MNIST caiu para porcentagens em torno de 80%, quando usamos apenas 1.000 imagens de treinamento. Não é de surpreender que isso aconteça, uma vez que menos dados de treinamento significam que nossa rede será exposta a menos variações na forma como os seres humanos escrevem dígitos. Vamos tentar treinar nossa rede de 30 neurônios ocultos com uma variedade de diferentes tamanhos de conjuntos de dados de treinamento, para ver como o desempenho varia.

Nós treinaremos usando um tamanho de mini-lote de 10, uma taxa de aprendizado η = 0,5, um parâmetro de regularização λ = 5.0 e a função de custo de entropia cruzada. Treinaremos por 30 épocas quando o conjunto completo de dados de treinamento for usado e aumentaremos o número de épocas proporcionalmente quando conjuntos de treinamento menores forem usados. Para garantir que o fator de decaimento do peso (weight decay factor) permaneça o mesmo nos conjuntos de treinamento, usaremos um parâmetro de regularização de λ = 5.0 quando o conjunto de dados de treinamento completo for usado, e reduziremos proporcionalmente quando conjuntos de treinamento menores forem usados. Observe esse gráfico:

 

more_data

 

Como você pode ver analisando o gráfico acima, as precisões de classificação melhoram consideravelmente à medida que usamos mais dados de treinamento. Presumivelmente, essa melhoria continuaria se houvesse mais dados disponíveis. É claro que, olhando para o gráfico acima, parece que estamos chegando perto da saturação. Suponha, no entanto, que refizemos o gráfico com o tamanho do conjunto de treinamento plotado logaritmicamente:

 

more_data_log

 

Parece claro que o gráfico ainda está subindo em direção aos 100% de precisão. Isso sugere que, se usássemos muito mais dados de treinamento – digamos, milhões ou até bilhões de amostras de dígitos manuscritos, em vez de apenas 50.000, provavelmente teríamos um desempenho consideravelmente melhor, mesmo nessa rede muito pequena.

Obter mais dados de treinamento é uma ótima ideia. Infelizmente, pode ser caro e nem sempre é possível na prática. No entanto, há outra técnica que pode funcionar quase tão bem, que é expandir artificialmente os dados de treinamento. Suponha, por exemplo, que tomemos uma imagem de treinamento MNIST, o dígito 5:

 

more_data_5

 

e rotacionamos por um pequeno ângulo, digamos 15 graus:

 

more_data_rotated_5

 

Ainda é reconhecivelmente o mesmo dígito. E ainda no nível do pixel é bem diferente de qualquer imagem atualmente nos dados de treinamento MNIST. É possível que adicionar essa imagem aos dados de treinamento possa ajudar nossa rede a aprender mais sobre como classificar os dígitos. Além do mais, obviamente, não estamos limitados a adicionar apenas uma imagem. Podemos expandir nossos dados de treinamento fazendo muitas rotações pequenas de todas as imagens de treinamento MNIST e, em seguida, usando os dados de treinamento expandidos para melhorar o desempenho de nossa rede.

Essa técnica é muito poderosa e tem sido amplamente usada. Vejamos alguns dos resultados de um artigo que aplicou diversas variações da técnica ao MNIST. Uma das arquiteturas de redes neurais que eles consideraram foi similar às que estamos usando, uma rede feedforward com 800 neurônios ocultos e usando a função de custo de entropia cruzada. Executando a rede com os dados de treinamento MNIST padrão, eles obtiveram uma precisão de classificação de 98,4% em seu conjunto de testes. Eles então expandiram os dados de treinamento, usando não apenas rotações, como descrevi acima, mas também traduzindo e distorcendo as imagens. Ao treinar no conjunto de dados expandido, aumentaram a precisão de sua rede para 98,9%.

Eles também experimentaram o que chamaram de “distorções elásticas”, um tipo especial de distorção de imagem destinada a emular as oscilações aleatórias encontradas nos músculos da mão. Usando as distorções elásticas para expandir os dados, eles alcançaram uma precisão ainda maior, 99,3%. Efetivamente, eles estavam ampliando a experiência de sua rede, expondo-a ao tipo de variações encontradas na caligrafia real. Caso queira aprender sobre estas técnicas, elas são estudadas em detalhes em Inteligência Artificial Para Visão Computacional.

Variações sobre essa técnica podem ser usadas para melhorar o desempenho em muitas tarefas de aprendizado, não apenas no reconhecimento de manuscrito. O princípio geral é expandir os dados de treinamento aplicando operações que reflitam a variação do mundo real. Não é difícil pensar em maneiras de fazer isso. Suponha, por exemplo, que você esteja construindo uma rede neural para fazer o reconhecimento de fala. Nós humanos podemos reconhecer a fala mesmo na presença de distorções como ruído de fundo e assim você pode expandir seus dados adicionando ruído de fundo. Também podemos reconhecer a fala se ela estiver acelerada ou desacelerada. Então, essa é outra maneira de expandir os dados de treinamento. Essas técnicas nem sempre são usadas – por exemplo, em vez de expandir os dados de treinamento adicionando ruído, pode ser mais eficiente limpar a entrada para a rede aplicando primeiro um filtro de redução de ruído. Ainda assim, vale a pena manter a ideia de expandir os dados de treinamento e buscar oportunidades para aplicar a técnica.

Agora você compreende melhor o poder do Big Data, pois com mais dados, em maior variedade e gerados em alta velocidade, conseguimos chegar a resultados nunca antes vistos em Inteligência Artificial. Vamos ver novamente como a precisão da nossa rede neural varia com o tamanho do conjunto de treinamento:

 

more_data_log2

 

Suponha que, em vez de usar uma rede neural, usemos alguma outra técnica de aprendizado de máquina para classificar os dígitos. Por exemplo, vamos tentar usar as máquinas de vetores de suporte (SVMs). Não se preocupe se você não estiver familiarizado com SVMs, não precisamos entender seus detalhes (caso queira aprender sobre SVMs, elas são estudadas em detalhes em Machine Learning). Vamos usar o SVM fornecido pela biblioteca scikit-learn. Veja como o desempenho do SVM varia em função do tamanho do conjunto de treinamento. Eu tracei os resultados da rede neural também, para facilitar a comparação:

 

more_data_comparison

 

Provavelmente, a primeira coisa que chama a atenção sobre esse gráfico é que nossa rede neural supera o SVM para cada tamanho de conjunto de treinamento. Isso é bom, embora tenhamos usado as configurações prontas do SVM do scikit-learn, enquanto fizemos um bom trabalho customizando nossa rede neural. Um fato sutil, porém interessante, sobre o gráfico é que, se treinarmos o SVM usando 50.000 imagens, ele terá melhor desempenho (94,48% de precisão) do que a nossa rede neural quando treinado usando 5.000 imagens (precisão de 93,24%). Em outras palavras, mais dados de treinamento podem, às vezes, compensar diferenças no algoritmo de aprendizado de máquina usado.

Algo ainda mais interessante pode ocorrer. Suponha que estamos tentando resolver um problema usando dois algoritmos de aprendizado de máquina, algoritmo A e algoritmo B. Às vezes acontece que o algoritmo A superará o algoritmo B com um conjunto de dados de treinamento, enquanto o algoritmo B superará o algoritmo A com um conjunto diferente de dados de treinamento. Não vemos isso acima – seria necessário que os dois gráficos se cruzassem – mas a resposta correta à pergunta “O algoritmo A é melhor que o algoritmo B?” seria: “Qual o tamanho do conjunto de dados de treinamento que você está usando?”

Tudo isso é uma precaução a ter em mente, tanto ao fazer o desenvolvimento quanto ao ler artigos de pesquisa. Muitos artigos concentram-se em encontrar novos truques para obter melhor desempenho em conjuntos de dados de referência padrão. “Nossa técnica XPTO nos deu uma melhoria de X por cento no benchmark padrão Y” é uma forma canônica de alegação de pesquisa. Tais alegações são, com frequência, genuinamente interessantes, mas devem ser entendidas como aplicáveis ​​apenas no contexto do conjunto de dados de treinamento específico usado. Imagine uma história alternativa na qual as pessoas que originalmente criaram o conjunto de dados de referência tinham uma concessão de pesquisa maior. Eles podem ter usado o dinheiro extra para coletar mais dados de treinamento. É perfeitamente possível que o “aprimoramento” devido à técnica de “XPTO” desapareça em um conjunto maior de dados. Em outras palavras, a suposta melhoria pode ser apenas um acidente da história.

A mensagem a ser retirada, especialmente em aplicações práticas, é que o que queremos é melhores algoritmos e melhores dados de treinamento. Não há problema em procurar algoritmos melhores, mas certifique-se de não estar se concentrando apenas em melhores algoritmos, excluindo a busca por mais ou melhores dados de treinamento.

Com isso concluímos nosso mergulho no overfitting e na regularização. Claro, voltaremos novamente ao assunto. Como já mencionamos várias vezes, o overfitting é um grande problema nas redes neurais, especialmente à medida que os computadores se tornam mais poderosos e temos a capacidade de treinar redes maiores. Como resultado, há uma necessidade premente de desenvolver técnicas poderosas de regularização para reduzir o overfitting, e esta é uma área extremamente ativa de pesquisa.

No próximo capítulo vamos tratar de um outro importante assunto: a inicialização de pesos. Até lá.

Referências:

Best practices for convolutional neural networks applied to visual document analysis

Neural Networks & The Backpropagation Algorithm, Explained

Scaling to very very large corpora for natural language disambiguation

Gradient-Based Learning Applied to Document Recognition

Machine Learning

Neural Networks and Deep Learning

The Elements of Statistical Learning: Data Mining, Inference, and Prediction, Second Edition

Gradient Descent For Machine Learning

Pattern Recognition and Machine Learning

Understanding Activation Functions in Neural Networks

Redes Neurais, princípios e práticas

ImageNet Classification with Deep Convolutional Neural Networks

Improving neural networks by preventing co-adaptation of feature detectors

Deep Learning Book

Capítulo 23 – Como Funciona o Dropout?

by

Dropout é uma técnica radicalmente diferente para regularização. Ao contrário da Regularização L1 e L2, o Dropout não depende da modificação da função de custo. Em vez disso, no Dropout, modificamos a própria rede. Deixe-me descrever a mecânica básica de Como Funciona o Dropout? antes de entender porque ele funciona e quais são os resultados. Suponha que estamos tentando treinar uma rede neural:

 

rede

 

Em particular, suponha que tenhamos uma entrada de treinamento x e a saída desejada correspondente y. Normalmente, nós treinamos pela propagação direta de x através da rede, e depois retrocedemos (retropropagação) para determinar a contribuição do erro para o gradiente. Com o Dropout, esse processo é modificado. Começamos por eliminar aleatoriamente (e temporariamente) alguns dos neurônios ocultos na rede, deixando os neurônios de entrada e saída intocados. Depois de fazer isso, terminaremos com uma rede da seguinte forma (observe as linhas tracejadas na figura abaixo). Note os neurônios que foram temporariamente eliminados:

 

rede2

 

Nós encaminhamos para frente a entrada x através da rede modificada, e depois retropropagamos o resultado, também através da rede modificada. Depois de fazer isso em um mini-lote de exemplos, atualizamos os pesos e vieses apropriados. Em seguida, repetimos o processo, primeiro restaurando os neurônios removidos, depois escolhendo um novo subconjunto aleatório de neurônios ocultos para excluir, estimando o gradiente para um mini-lote diferente e atualizando os pesos e vieses na rede.

Ao repetir esse processo várias vezes, nossa rede aprenderá um conjunto de pesos e vieses. Naturalmente, esses pesos e vieses terão sido aprendidos sob condições em que parte dos neurônios ocultos foram descartados. Quando realmente executamos a rede completa, isso significa que mais neurônios ocultos estarão ativos. Para compensar isso, reduzimos pela metade os pesos que saem dos neurônios ocultos.

Esse procedimento de desistência pode parecer estranho e ad-hoc. Por que esperamos que ajude com a regularização? Para explicar o que está acontecendo, gostaria que você parasse brevemente de pensar sobre o Dropout e, em vez disso, imagine o treinamento de redes neurais no modo padrão (sem Dropout). Em particular, imagine que treinamos várias redes neurais diferentes, todas usando os mesmos dados de treinamento.

É claro que as redes podem não começar idênticas e, como resultado, após o treinamento, elas podem, às vezes, dar resultados diferentes. Quando isso acontece, podemos usar algum tipo de esquema de média ou votação para decidir qual saída aceitar. Por exemplo, se nós treinamos cinco redes, e três delas estão classificando um dígito como um “3”, então provavelmente é um “3”. As outras duas redes provavelmente estão cometendo um erro. Este tipo de esquema de média é frequentemente encontrado como uma maneira poderosa (embora requeira mais capacidade computacional) de reduzir o overfitting. A razão é que as diferentes redes podem se sobrepor de diferentes maneiras e a média pode ajudar a eliminar esse tipo de overfitting.

O que isso tem a ver com o Dropout? Heuristicamente, quando abandonamos diferentes conjuntos de neurônios, é como se estivéssemos treinando redes neurais diferentes. E assim, o procedimento de eliminação é como calcular a média dos efeitos de um grande número de redes diferentes. As diferentes redes se adaptarão de diferentes maneiras, e assim, esperançosamente, o efeito líquido do Dropout será reduzir o overfitting.

Uma explicação heurística relacionada ao Dropout é dada em um dos primeiros artigos a usar a técnica: “Esta técnica reduz co-adaptações complexas de neurônios, já que um neurônio não pode confiar na presença de outros neurônios em particular. É, portanto, forçado a aprenda recursos mais robustos que são úteis em conjunto com muitos subconjuntos aleatórios diferentes dos outros neurônios”. Em outras palavras, se pensarmos em nossa rede como um modelo que está fazendo previsões, então podemos pensar no Dropout como uma forma de garantir que o modelo seja robusto para a perda de qualquer evidência individual. Nesse ponto, é um pouco semelhante à Regularização L1 e L2, que tendem a reduzir os pesos e, assim, tornar a rede mais robusta para perder qualquer conexão individual na rede.

Naturalmente, a verdadeira medida do Dropout é que ele foi muito bem sucedido em melhorar o desempenho das redes neurais. O artigo original, introduzindo a técnica, aplicou-a a muitas tarefas diferentes. Para nós, é de particular interesse que eles aplicaram o Dropout na classificação de dígitos MNIST, usando uma rede neural feedforward “vanilla” ao longo de linhas similares àquelas que estamos considerando. O documento observou que o melhor resultado que alguém alcançou até aquele ponto usando tal arquitetura foi a precisão de classificação de 98,4% no conjunto de testes. Eles melhoraram isso para 98,7% de precisão usando uma combinação de Dropout e uma forma modificada de Regularização L2. Da mesma forma, resultados impressionantes foram obtidos para muitas outras tarefas, incluindo problemas de reconhecimento de imagem e fala e processamento de linguagem natural. O Dropout tem sido especialmente útil no treinamento de redes grandes e profundas, nas quais o problema do overfitting é frequentemente agudo.

Até o próximo capítulo!

Referências:

Formação Engenheiro de Inteligência Artificial

Gradient-Based Learning Applied to Document Recognition

Neural Networks & The Backpropagation Algorithm, Explained

Neural Networks and Deep Learning

Machine Learning

The Elements of Statistical Learning: Data Mining, Inference, and Prediction, Second Edition

Gradient Descent For Machine Learning

Pattern Recognition and Machine Learning

Understanding Activation Functions in Neural Networks

Redes Neurais, princípios e práticas

ImageNet Classification with Deep Convolutional Neural Networks

Improving neural networks by preventing co-adaptation of feature detectors

Deep Learning Book

Capítulo 22 – Regularização L1

by

Existem muitas técnicas de regularização além da Regularização L2 que vimos no capítulo anterior. De fato, tantas técnicas foram desenvolvidas que é difícil resumir todas elas. Neste e nos próximos dois capítulos, vamos descrever brevemente três outras abordagens para reduzir o overfitting: Regularização L1, Dropout e aumento artificial do tamanho do conjunto de treinamento. Não aprofundaremos tanto nessas técnicas como fizemos com a Regularização L2. Em vez disso, o objetivo é familiarizar você com as ideias principais e apreciar a diversidade de técnicas de regularização disponíveis.

Regularização L1

Nesta abordagem, modificamos a função de custo não regularizada, adicionando a soma dos valores absolutos dos pesos:

 

form1

Equação 1

 

Intuitivamente, isso é semelhante à Regularização L2, penalizando grandes pesos e tendendo a fazer com que a rede prefira pequenos pesos. Naturalmente, o termo de Regularização L1 não é o mesmo que o termo de Regularização L2 e, portanto, não devemos esperar obter exatamente o mesmo comportamento. Vamos tentar entender como o comportamento de uma rede treinada usando a Regularização L1 difere de uma rede treinada usando a Regularização L2.

Para fazer isso, vejamos as derivadas parciais da função de custo. A partir da fórmula anterior obtemos:

 

form2

Equação 2

 

onde sgn(w) é o sinal de w, isto é, +1 se w é positivo e −1 se w é negativo. Usando essa expressão, podemos facilmente modificar a retropropagação (backpropagation) para fazer a descida de gradiente estocástica usando a Regularização L1. A regra de atualização resultante para uma rede regularizada L1 é:

 

form3

Equação 3 – Regra de atualização L1

 

onde, como de costume, podemos estimar ∂C0/∂w usando uma média de mini-lote, se desejarmos. Compare isso com a regra de atualização para a Regularização L2:

 

form4

Equação 4 – Regra de atualização L2

 

Em ambas as expressões, o efeito da regularização é diminuir os pesos. Isso está de acordo com a nossa intuição de que ambos os tipos de regularização penalizam grandes pesos. Mas a maneira como os pesos diminuem é diferente. Na Regularização L1, os pesos diminuem em uma quantidade constante para 0. Na Regularização L2, os pesos diminuem em um valor proporcional a w. E assim, quando um peso específico tem uma grande magnitude, a Regularização L1 reduz o peso muito menos do que a Regularização L2. Em contraste, quando |w| é pequena, a Regularização L1 reduz o peso muito mais do que a Regularização L2. O resultado é que a Regularização L1 tende a concentrar o peso da rede em um número relativamente pequeno de conexões de alta importância, enquanto os outros pesos são direcionados para zero.

Mas há ainda um pequeno detalhe na discussão acima. A derivada parcial ∂C/∂w não é definida quando w = 0. A razão é que a função |w| tem um “canto” agudo em w = 0 e, portanto, não é diferenciável nesse ponto. Tudo bem, no entanto. O que faremos é aplicar a regra usual (não regularizada) para descida de gradiente estocástica quando w = 0. Isso ajuda a resolver a questão – intuitivamente, o efeito da regularização é diminuir os pesos e, obviamente, não pode reduzir um peso que já é 0. Para colocá-lo com mais precisão, usaremos as Equações (2) e (3) com a convenção que sgn(0) = 0. Isso dá uma regra legal e compacta para se fazer uma descida gradiente estocástica com Regularização L1.

Agora vamos para o Dropout, no próximo capítulo!

Referências:

Formação Engenheiro de IA

Gradient-Based Learning Applied to Document Recognition

Neural Networks & The Backpropagation Algorithm, Explained

Neural Networks and Deep Learning

Machine Learning

The Elements of Statistical Learning: Data Mining, Inference, and Prediction, Second Edition

Gradient Descent For Machine Learning

Pattern Recognition and Machine Learning

Understanding Activation Functions in Neural Networks

Redes Neurais, princípios e práticas

Deep Learning Book

Capítulo 21 – Afinal, Por Que a Regularização Ajuda a Reduzir o Overfitting?

by

Vimos no capítulo anterior que a regularização ajuda a reduzir o overfitting. Isso é encorajador, mas, infelizmente, não é óbvio porque a regularização ajuda a resolver o overfitting! Uma história padrão que as pessoas contam para explicar o que está acontecendo segue mais ou menos esse raciocínio: pesos menores são, em certo sentido, de menor complexidade e, portanto, fornecem uma explicação mais simples e mais poderosa para os dados e devem, normalmente, ser preferidos. É uma história bastante concisa e contém vários elementos que talvez pareçam dúbios ou mistificadores. Vamos descompactar essa explicação e examiná-la criticamente. Afinal, Por Que a Regularização Ajuda a Reduzir o Overfitting?

Para fazer isso, vamos supor que temos um conjunto de dados simples para o qual desejamos construir um modelo:

graph

Implicitamente, estamos estudando algum fenômeno do mundo real aqui, com x e y representando dados desse fenômeno. Nosso objetivo é construir um modelo que nos permita prever y como uma função de x (isso é o que fazemos em Machine Learning). Poderíamos tentar usar redes neurais para construir esse modelo, mas vou fazer algo ainda mais simples: vou tentar modelar y como um polinômio em x. Estou fazendo isso em vez de usar redes neurais porque usar polinômios tornará as coisas particularmente transparentes. Uma vez que tenhamos entendido o caso polinomial, vamos traduzir para redes neurais.

Há dez pontos no gráfico acima, o que significa que podemos encontrar um único polinômio de 9ª ordem y = a0x9 + a1x8 +… + a9 que se ajusta exatamente aos dados. Aqui está o gráfico desse polinômio:

graph2

Isso fornece um ajuste exato. Mas também podemos obter um bom ajuste usando o modelo linear y = 2x:

graph3

Qual destes é o melhor modelo? E qual modelo é mais provável de generalizar bem a outros exemplos do mesmo fenômeno do mundo real?

Essas são questões difíceis. De fato, não podemos determinar com certeza a resposta para qualquer uma das perguntas acima, sem muito mais informações sobre o fenômeno do mundo real que estamos analisando (é onde entra a experiência do Cientista de Dados sobre áreas de negócio). Mas vamos considerar duas possibilidades: (1) o polinômio de 9ª ordem é, de fato, o modelo que realmente descreve o fenômeno do mundo real, e o modelo, portanto, generalizará perfeitamente; (2) o modelo correto é y = 2x, mas há um pequeno ruído adicional devido a, digamos, erros de medição, e é por isso que o modelo não é um ajuste exato.

Não é possível a priori dizer qual dessas duas possibilidades está correta (ou, na verdade, se alguma terceira possibilidade é válida). Logicamente, qualquer uma poderia ser verdade. E não é uma diferença trivial. É verdade que nos dados fornecidos há apenas uma pequena diferença entre os dois modelos. Mas suponha que queremos predizer o valor de y correspondendo a um grande valor de x, muito maior do que qualquer um mostrado nos gráficos acima. Se tentarmos fazer isso, haverá uma diferença dramática entre as previsões dos dois modelos, já que o modelo polinomial de 9ª ordem passa a ser dominado pelo termo x9, enquanto o modelo linear permanece, bem, linear.

Um ponto de vista é dizer que, na ciência, devemos seguir a explicação mais simples, a menos que sejamos obrigados a fazer o contrário. Quando encontramos um modelo simples que parece explicar muitos dados, somos tentados a gritar “Eureka!” Afinal, parece improvável que uma explicação simples ocorra apenas por coincidência. Em vez disso, suspeitamos que o modelo deve estar expressando alguma verdade subjacente sobre o fenômeno. No caso em questão, o modelo y = 2x + ruído parece muito mais simples que y = a0x9 + a1x8 +…. Seria surpreendente se essa simplicidade tivesse ocorrido por acaso, e então suspeitamos que y = 2x + ruído expressa alguma verdade subjacente. Nesse ponto de vista, o modelo de 9ª ordem está realmente aprendendo apenas os efeitos do ruído local. E assim, enquanto o modelo de 9ª ordem funciona perfeitamente para esses pontos de dados particulares, o modelo não conseguirá generalizar para outros pontos de dados, e o modelo linear terá maior poder preditivo.

Vamos ver o que esse ponto de vista significa para redes neurais. Suponha que nossa rede tenha, na maioria das vezes, pequenos pesos, como tenderá a acontecer em uma rede regularizada. O tamanho menor dos pesos significa que o comportamento da rede não mudará muito se alterarmos algumas entradas aleatórias aqui e ali. Isso dificulta que uma rede regularizada aprenda os efeitos do ruído local nos dados. Pense nisso como uma maneira de fazer com que as evidências não importem muito para a saída da rede. Em vez disso, uma rede regularizada aprende a responder a tipos de evidências que são vistas com frequência em todo o conjunto de treinamento. Por outro lado, uma rede com grandes pesos pode alterar bastante seu comportamento em resposta a pequenas alterações na entrada. Assim, uma rede não regularizada pode usar grandes pesos para aprender um modelo complexo que contém muitas informações sobre o ruído nos dados de treinamento. Em suma, as redes regularizadas são levadas a construir modelos relativamente simples baseados em padrões vistos frequentemente nos dados de treinamento e são resistentes às peculiaridades de aprendizagem do ruído nos dados de treinamento. A esperança é que isso forçará nossas redes a aprender de verdade sobre o fenômeno em questão e a generalizar melhor o que aprendem.

Com isso dito, e mantendo a necessidade de cautela em mente, é um fato empírico que as redes neurais regularizadas geralmente generalizam melhor do que as redes não regularizadas. A verdade é que ninguém ainda desenvolveu uma explicação teórica inteiramente convincente para explicar porque a regularização ajuda a generalizar as redes. De fato, os pesquisadores continuam a escrever artigos nos quais tentam abordagens diferentes à regularização, comparam-nas para ver qual funciona melhor e tentam entender por que diferentes abordagens funcionam melhor ou pior. Embora muitas vezes ajude, não temos uma compreensão sistemática inteiramente satisfatória do que está acontecendo, apenas heurísticas incompletas e regras gerais.

Há um conjunto mais profundo de questões aqui, questões que vão para o coração da ciência. É a questão de como generalizamos. A regularização pode nos dar uma varinha mágica computacional que ajuda nossas redes a generalizar melhor, mas não nos dá uma compreensão baseada em princípios de como a generalização funciona, nem de qual é a melhor abordagem.

Isso é particularmente irritante porque na vida cotidiana, nós humanos generalizamos bem. Mostradas apenas algumas imagens de um elefante, uma criança aprenderá rapidamente a reconhecer outros elefantes. É claro que eles podem ocasionalmente cometer erros, talvez confundindo um rinoceronte com um elefante, mas em geral esse processo funciona notavelmente com precisão. Então nós temos um sistema – o cérebro humano – com um grande número de parâmetros livres. E depois de ser mostrado apenas uma ou algumas imagens de treinamento, o sistema aprende a generalizar para outras imagens. Nossos cérebros estão, em certo sentido, se regularizando incrivelmente bem! Como fazemos isso? Neste ponto não sabemos. Espero que nos próximos anos desenvolvamos técnicas mais poderosas de regularização em redes neurais artificiais, técnicas que permitirão que as redes neurais generalizem bem, mesmo a partir de pequenos conjuntos de dados.

De fato, nossas redes já generalizam melhor do que se poderia esperar a priori. Uma rede com 100 neurônios ocultos tem quase 80.000 parâmetros. Temos apenas 50.000 imagens em nossos dados de treinamento. É como tentar encaixar um polinômio de grau 80.000 em 50.000 pontos de dados. Consequentemente, nossa rede deve se ajustar muito bem. E, no entanto, como vimos anteriormente, essa rede realmente faz um ótimo trabalho generalizando. Por que esse é o caso? Não é bem entendido. Foi conjecturado que “a dinâmica do aprendizado de gradiente descendente em redes multicamadas tem um efeito de ‘autorregulação'”. Isso é excepcionalmente bom, mas também é um tanto inquietante que não entendemos porque exatamente isso ocorre e por isso muitas vezes modelos de redes neurais profundas são chamados de “caixa preta”. Enquanto isso, adotaremos a abordagem pragmática e usaremos a regularização sempre que pudermos. Nossas redes neurais serão melhores assim.

Deixe-me concluir esta seção voltando a um detalhe que deixei inexplicado antes: o fato de que a regularização L2 não restringe os vieses. É claro que seria fácil modificar o procedimento de regularização para regularizar os vieses. Empiricamente, fazendo isso muitas vezes não muda muito os resultados, então, em certa medida, é apenas uma convenção se regularizar os vieses ou não. No entanto, vale a pena notar que ter um grande viés não torna um neurônio sensível às suas entradas da mesma maneira que ter pesos grandes. Portanto, não precisamos nos preocupar com grandes vieses que permitem que nossa rede aprenda o ruído em nossos dados de treinamento. Ao mesmo tempo, permitir grandes vieses dá às nossas redes mais flexibilidade no comportamento – em particular, grandes vieses facilitam a saturação dos neurônios, o que às vezes é desejável. Por essas razões, geralmente não incluímos termos de viés quando regularizamos a rede neural.

Você já percebeu que regularização é um assunto importante quando tratamos de redes neurais. Nos próximos capítulos estudaremos mais duas técnicas de regularização: Regularização L1 e Dropout! Não perca!

Referências:

Gradient-Based Learning Applied to Document Recognition

Neural Networks & The Backpropagation Algorithm, Explained

Neural Networks and Deep Learning

Machine Learning

The Elements of Statistical Learning: Data Mining, Inference, and Prediction, Second Edition

Gradient Descent For Machine Learning

Pattern Recognition and Machine Learning

Understanding Activation Functions in Neural Networks

Redes Neurais, princípios e práticas

Deep Learning Book

Capítulo 20 – Overfitting e Regularização – Parte 2

by

Aumentar a quantidade de dados de treinamento é uma maneira de reduzir o overfitting. Mas existem outras maneiras de reduzir a extensão de ocorrência do overfitting? Uma abordagem possível é reduzir o tamanho da nossa rede. No entanto, redes grandes têm o potencial de serem mais poderosas do que redes pequenas e essa é uma opção que só adotaríamos com relutância.

Felizmente, existem outras técnicas que podem reduzir o overfitting, mesmo quando temos uma rede de tamanho fixo e dados de treinamento em quantidade limitada. Essas técnicas são conhecidos como técnicas de regularização. Neste capítulo descrevemos uma das técnicas de regularização mais comumente usadas, uma técnica às vezes conhecida como decaimento de peso (weight decay) ou Regularização L2. A ideia da Regularização L2 é adicionar um termo extra à função de custo, um termo chamado termo de regularização. Aqui está a entropia cruzada regularizada:

 

form

Equação 1

 

O primeiro termo é apenas a expressão usual para a entropia cruzada. Mas adicionamos um segundo termo, a soma dos quadrados de todos os pesos da rede. Isto é escalonado por um fator λ / 2n, onde λ > 0 é conhecido como o parâmetro de regularização e n é, como de costume, o tamanho do nosso conjunto de treinamento. Vou discutir mais tarde como λ é escolhido. É importante notar também que o termo de regularização não inclui os vieses. Eu também voltarei a isso mais frente.

Claro, é possível regularizar outras funções de custo, como o custo quadrático. Isso pode ser feito de maneira semelhante:

 

form2

Equação 2

 

Em ambos os casos, podemos escrever a função de custo regularizada como:

 

form3

Equação 3

 

onde C0 é a função de custo original e não regularizada.

Intuitivamente, o efeito da regularização é fazer com que a rede prefira aprender pequenos pesos, sendo todas as outras coisas iguais. Pesos grandes só serão permitidos se melhorarem consideravelmente a primeira parte da função de custo. Dito de outra forma, a regularização pode ser vista como uma forma de se comprometer entre encontrar pequenos pesos e minimizar a função de custo original. A importância relativa dos dois elementos do compromisso depende do valor de λ: quando λ é pequeno, preferimos minimizar a função de custo original, mas quando λ é grande, preferimos pesos pequenos.

Agora, não é de todo óbvio porque fazer este tipo de compromisso deve ajudar a reduzir o overfitting! Mas acontece que sim, reduz. Abordaremos a questão de porque isso ajuda na redução do overfitting no próximo capítulo, mas primeiro vamos trabalhar em um exemplo mostrando como a regularização reduz o overfitting.

Para construir um exemplo, primeiro precisamos descobrir como aplicar nosso algoritmo de aprendizado de descida de gradiente estocástico em uma rede neural regularizada. Em particular, precisamos saber como calcular as derivadas parciais ∂C/∂w e ∂C/∂b para todos os pesos e vieses na rede. Tomando as derivadas parciais da Equação 3 acima, temos:

 

form4

Equação 4

 

Os termos ∂C0/∂w e ∂C0/∂b podem ser calculados usando backpropagation, conforme descrito nos capítulos anteriores. E assim vemos que é fácil calcular o gradiente da função de custo regularizada, pois basta usar backpropagation, como de costume, e depois adicionar (λ/n).w à derivada parcial de todos os termos de peso. As derivadas parciais em relação aos vieses são inalteradas e, portanto, a regra de aprendizado de descida de gradiente para os vieses não muda da regra usual:

 

form5

Equação 5

 

A regra de aprendizado para os pesos se torna:

 

form6

Equação 6

 

Isto é exatamente o mesmo que a regra usual de aprendizado de descida de gradiente, exceto pelo fato de primeiro redimensionarmos o peso w por um fator 1 − (ηλ/n). Esse reescalonamento é, às vezes, chamado de redução de peso, uma vez que diminui os pesos. À primeira vista, parece que isso significa que os pesos estão sendo direcionados para zero, mas isso não é bem isso, uma vez que o outro termo pode levar os pesos a aumentar, se isso causar uma diminuição na função de custo não regularizada.

Ok, é assim que a descida de gradiente funciona. E quanto à descida de gradiente estocástica? Bem, assim como na descida de gradiente estocástica não-regularizada, podemos estimar ∂C0/∂w pela média de um mini-lote de m exemplos de treinamento. Assim, a regra de aprendizagem regularizada para a descida de gradiente estocástica torna-se:

 

form7

Equação 7

 

onde a soma é sobre exemplos de treinamento x no mini-lote, e Cx é o custo (não-regularizado) para cada exemplo de treinamento. Isto é exatamente o mesmo que a regra usual para descida de gradiente estocástico, exceto pelo fator de decaimento de peso de 1 − (ηλ/n). Finalmente, e por completo, deixe-me declarar a regra de aprendizagem regularizada para os vieses. Isto é, naturalmente, exatamente o mesmo que no caso não regularizado:

 

form8

Equação 8

 

onde a soma é sobre exemplos de treinamento x no mini-lote.

Vamos ver como a regularização altera o desempenho da nossa rede neural. Usaremos uma rede com 30 neurônios ocultos, um tamanho de mini-lote de 10, uma taxa de aprendizado de 0,5 e a função de custo de entropia cruzada. No entanto, desta vez vamos usar um parâmetro de regularização de λ = 0,1. Note que no código, usamos o nome da variável lmbda, porque lambda é uma palavra reservada em Python, com um significado não relacionado ao que estamos fazendo aqui (caso tenha dúvidas sobre as palavras reservadas em Python, acesse o curso gratuito de Python em nosso portal).

Eu também usei o test_data novamente, não o validation_data. Estritamente falando, devemos usar o validation_data, por todas as razões que discutimos anteriormente. Mas decidi usar o test_data porque ele torna os resultados mais diretamente comparáveis com nossos resultados anteriores e não regularizados. Você pode facilmente alterar o código para usar o validation_data e você verá que ele terá resultados semelhantes.

python

O custo com os dados de treinamento diminui durante todo o tempo, da mesma forma que no caso anterior, não regularizado no capítulo anterior:

 

regularized1

 

Mas desta vez a precisão no test_data continua a aumentar durante as 400 épocas:

 

regularized2

 

Claramente, o uso da regularização suprimiu o overfitting. Além do mais, a precisão é consideravelmente maior, com uma precisão de classificação de pico de 87.1%, em comparação com o pico de 82.27% obtido no caso não regularizado. De fato, quase certamente poderíamos obter resultados consideravelmente melhores, continuando a treinar mais de 400 épocas. Parece que, empiricamente, a regularização está fazendo com que nossa rede generalize melhor e reduza consideravelmente os efeitos do overfitting.

O que acontece se sairmos do ambiente artificial de ter apenas 1.000 imagens de treinamento e retornar ao conjunto completo de treinamento de 50.000 imagens? É claro, já vimos que o overfitting é muito menos problemático com as 50.000 imagens. A regularização ajuda ainda mais? Vamos manter os hiperparâmetros iguais ao exemplo anterior – 30 épocas, taxa de aprendizado de 0,5, tamanho de mini-lote de 10. No entanto, precisamos modificar o parâmetro de regularização. A razão é porque o tamanho n do conjunto de treinamento mudou de n = 1.000 para n = 50.000, e isso muda o fator de decaimento de peso 1 − (ηλ/n). Se continuássemos a usar λ = 0,1, isso significaria muito menos perda de peso e, portanto, muito menos efeito de regularização. Nós compensamos mudando para λ = 5.0.

Ok, vamos treinar nossa rede, parando primeiro para reinicializar os pesos:

python2

Obtemos os resultados:

regularized_full

Há muitas boas notícias aqui. Primeiro, nossa precisão de classificação nos dados de teste aumentou de 95.49%, quando não foi regularizada, para 96.49%. Isso é uma grande melhoria. Em segundo lugar, podemos ver que a diferença entre os resultados nos dados de treinamento e teste é muito menor do que antes, com um percentual abaixo de zero. Essa ainda é uma lacuna significativa, mas obviamente fizemos um progresso substancial para reduzir o overfitting.

Finalmente, vamos ver qual a precisão da classificação de teste que obtemos quando usamos 100 neurônios ocultos e um parâmetro de regularização de λ = 5.0. Eu não vou passar por uma análise detalhada de overfitting aqui, isso é puramente por diversão, só para ver a precisão que podemos obter quando usamos nossos novos truques: a função de custo de entropia cruzada e a Regularização L2.

python3

O resultado final é uma precisão de classificação de 97.92% nos dados de validação. É um grande salto do caso dos 30 neurônios ocultos. Na verdade, ajustando um pouco mais, para executar por 60 épocas com η = 0.1 e λ = 5.0, quebramos a barreira de 98%, alcançando uma precisão de classificação de 98.04% nos dados de validação. Nada mal para o que acaba sendo 152 linhas de código!

Descrevi a regularização como uma forma de reduzir o overfitting e aumentar as precisões de classificação. Na verdade, esse não é o único benefício. Empiricamente, ao executar várias execuções de nossas redes com o dataset MNIST, mas com diferentes inicializações de peso (aleatórias), descobrimos que as execuções não-regularizadas ocasionalmente ficarão “presas”, aparentemente capturadas em mínimos locais da função de custo. O resultado é que diferentes execuções às vezes fornecem resultados bastante diferentes. Por outro lado, as execuções regularizadas forneceram resultados muito mais facilmente replicáveis.

Por que isso está acontecendo? Heuristicamente, se a função de custo for desregularizada, o comprimento do vetor de peso provavelmente crescerá, todas as outras coisas sendo iguais. Com o tempo, isso pode levar o vetor de peso a ser realmente muito grande. Isso pode fazer com que o vetor de peso fique preso apontando mais ou menos na mesma direção, já que as mudanças devido a descida do gradiente fazem apenas pequenas alterações na direção, quando o comprimento é longo. Acredito que esse fenômeno esteja dificultando o nosso algoritmo de aprendizado para explorar adequadamente o espaço de pesos e, consequentemente, mais difícil encontrar bons mínimos da função de custo.

Ainda não acabamos sobre regularização. Mais sobre isso no próximo capítulo! Até lá!

Referências:

Dot Product 

Neural Networks & The Backpropagation Algorithm, Explained

Neural Networks and Deep Learning

Machine Learning

The Elements of Statistical Learning: Data Mining, Inference, and Prediction, Second Edition

Gradient Descent For Machine Learning

Pattern Recognition and Machine Learning

Understanding Activation Functions in Neural Networks

Redes Neurais, princípios e práticas

Deep Learning Book

Capítulo 19 – Overfitting e Regularização – Parte 1

by

O físico Enrico Fermi, ganhador do Prêmio Nobel de Física em 1938, foi questionado sobre sua opinião em relação a um modelo matemático que alguns colegas haviam proposto como a solução para um importante problema de física não resolvido. O modelo teve excelente performance no experimento, mas Fermi estava cético. Ele perguntou quantos parâmetros livres poderiam ser definidos no modelo. “Quatro” foi a resposta. Fermi respondeu: “Eu lembro que meu amigo Johnny Von Neumann costumava dizer: com quatro parâmetros eu posso encaixar um elefante, e com cinco eu posso fazê-lo mexer seu tronco” *. Com isso, ele quis dizer que não se deve ficar impressionado quando um modelo complexo se ajusta bem a um conjunto de dados. Com parâmetros suficientes, você pode ajustar qualquer conjunto de dados.

(* A citação vem de um artigo de Freeman Dyson, que é uma das pessoas que propôs o modelo. O artigo “Um elefante de quatro parâmetros” ou “A four-parameter elephant” pode ser encontrado aqui.)

O ponto, claro, é que modelos com um grande número de parâmetros podem descrever uma variedade incrivelmente ampla de fenômenos. Mesmo que tal modelo esteja de acordo com os dados disponíveis, isso não o torna um bom modelo. Isso pode significar apenas que há liberdade suficiente no modelo que pode descrever quase qualquer conjunto de dados de tamanho determinado, sem capturar nenhuma percepção genuína do fenômeno em questão. Quando isso acontece, o modelo funcionará bem para os dados existentes, mas não conseguirá generalizar para novas situações. O verdadeiro teste de um modelo é sua capacidade de fazer previsões em situações que não foram expostas antes.

Fermi e von Neumann suspeitavam de modelos com quatro parâmetros. Nossa rede de 30 neurônios ocultos para classificação de dígitos MNIST possui quase 24.000 parâmetros! Nossa rede de 100 neurônios ocultos tem cerca de 80.000 parâmetros e redes neurais profundas de última geração às vezes contêm milhões ou até bilhões de parâmetros. Devemos confiar nos resultados?

Vamos aguçar este problema construindo uma situação em que a nossa rede faz um mau trabalho ao generalizar para novas situações. Usaremos nossa rede de 30 neurônios ocultos, com seus 23.860 parâmetros. Mas não treinamos a rede usando todas as imagens de treinamento de 50.000 dígitos MNIST. Em vez disso, usaremos apenas as primeiras 1.000 imagens de treinamento. Usar esse conjunto restrito tornará o problema com a generalização muito mais evidente. Vamos treinar usando a função de custo de entropia cruzada, com uma taxa de aprendizado de η = 0,5 e um tamanho de mini-lote de 10. No entanto, vamos treinar por 400 épocas, pois não estamos usando muitos exemplos de treinamento. Vamos usar network2 para ver como a função de custo muda:

 

network

 

Usando os resultados, podemos traçar a maneira como o custo muda à medida que a rede aprende (o script overfitting.py contém o código que gera esse resultado):

overfitting1

Isso parece encorajador, mostrando uma redução suave no custo, exatamente como esperamos. Note que eu só mostrei as épocas de treinamento de 200 a 399. Isso nos dá uma boa visão dos últimos estágios do aprendizado, que, como veremos, é onde está a ação interessante.

Vamos agora ver como a precisão da classificação nos dados de teste muda com o tempo:

overfitting2

Mais uma vez, eu ampliei um pouco. Nas primeiras 200 épocas (não mostradas), a precisão sobe para pouco menos de 82%. O aprendizado então diminui gradualmente. Finalmente, por volta da época 280, a precisão da classificação praticamente pára de melhorar. As épocas posteriores meramente vêem pequenas flutuações estocásticas perto do valor da precisão na época 280. Compare isso com o gráfico anterior, em que o custo associado aos dados de treinamento continua a cair suavemente. Se olharmos apenas para esse custo, parece que nosso modelo ainda está ficando “melhor”. Mas os resultados da precisão do teste mostram que a melhoria é uma ilusão. Assim como o modelo que Fermi não gostava, o que nossa rede aprende após a época 280 não mais se generaliza para os dados de teste. E assim não é um aprendizado útil. Dizemos que a rede está super adaptando ou com sobreajuste ou ainda com overfitting, a partir da época 280.

Você pode se perguntar se o problema aqui é que eu estou olhando para o custo dos dados de treinamento, ao contrário da precisão da classificação nos dados de teste. Em outras palavras, talvez o problema seja que estamos fazendo uma comparação de maçãs e laranjas. O que aconteceria se comparássemos o custo dos dados de treinamento com o custo dos dados de teste, estaríamos comparando medidas semelhantes? Ou talvez pudéssemos comparar a precisão da classificação tanto nos dados de treinamento quanto nos dados de teste? Na verdade, essencialmente o mesmo fenômeno aparece, não importa como fazemos a comparação. Os detalhes mudam, no entanto. Por exemplo, vamos analisar o custo nos dados de teste:

overfitting3

Podemos ver que o custo nos dados de teste melhora até a época 15, mas depois disso ele realmente começa a piorar, mesmo que o custo nos dados de treinamento continue melhorando. Este é outro sinal de que nosso modelo está super adaptando (overfitting). No entanto, coloca um enigma, que é se devemos considerar a época 15 ou a época 280 como o ponto em que o overfitting está dominando a aprendizagem? Do ponto de vista prático, o que realmente nos importa é melhorar a precisão da classificação nos dados de teste, enquanto o custo dos dados de teste não é mais do que um proxy para a precisão da classificação. E assim faz mais sentido considerar a época 280 como o ponto além do qual o overfitting está dominando o aprendizado em nossa rede neural.

Outro sinal de overfitting pode ser visto na precisão da classificação nos dados de treinamento:

overfitting4

A precisão aumenta até 100%. Ou seja, nossa rede classifica corretamente todas as 1.000 imagens de treinamento! Enquanto isso, nossa precisão de teste atinge apenas 82,27%. Portanto, nossa rede realmente está aprendendo sobre as peculiaridades do conjunto de treinamento, não apenas reconhecendo os dígitos em geral. É quase como se nossa rede estivesse apenas memorizando o conjunto de treinamento, sem entender os dígitos suficientemente bem para generalizar o conjunto de testes.

Overfitting é um grande problema em redes neurais. Isso é especialmente verdadeiro em redes modernas, que geralmente têm um grande número de pesos e vieses. Para treinar de forma eficaz, precisamos de uma maneira de detectar quando o overfitting está acontecendo. E precisamos aplicar técnicas para reduzir os efeitos do overfitting (por todo esse trabalho e conhecimento necessário, Cientistas de Dados devem ser muito bem remunerados).

A maneira óbvia de detectar overfitting é usar a abordagem acima, mantendo o controle da precisão nos dados de teste conforme nossos treinos da rede. Se percebermos que a precisão nos dados de teste não está mais melhorando, devemos parar de treinar. É claro que, estritamente falando, isso não é necessariamente um sinal de overfitting. Pode ser que a precisão nos dados de teste e os dados de treinamento parem de melhorar ao mesmo tempo. Ainda assim, a adoção dessa estratégia impedirá o overfitting.

Na verdade, usaremos uma variação dessa estratégia. Lembre-se de que, quando carregamos os dados MNIST, carregamos em três conjuntos de dados:

 

code

 

Até agora, usamos o training_data e test_data e ignoramos o validation_data. O validation_data contém 10.000 imagens de dígitos, imagens que são diferentes das 50.000 imagens no conjunto de treinamento MNIST e das 10.000 imagens no conjunto de teste MNIST. Em vez de usar o test_data para evitar overfitting, usaremos o validation_data. Para fazer isso, usaremos praticamente a mesma estratégia descrita acima para o test_data. Ou seja, calcularemos a precisão da classificação nos dados de validação no final de cada época. Quando a precisão da classificação nos dados de validação estiver saturada, paramos de treinar. Essa estratégia é chamada de parada antecipada (Early-Stopping). É claro que, na prática, não sabemos imediatamente quando a precisão está saturada. Em vez disso, continuamos treinando até termos certeza de que a precisão está saturada.

Por que usar o validation_data para evitar overfitting, em vez de test_data? Na verdade, isso faz parte de uma estratégia mais geral, que é usar o validation_data para avaliar diferentes opções de avaliação de hiperparâmetros, como o número de épocas para treinamento, a taxa de aprendizado, a melhor arquitetura de rede e assim por diante. Usamos essas avaliações para encontrar e definir bons valores para os hiperparâmetros. De fato, embora eu não tenha mencionado isso até agora, isto é, em parte, como chegamos às escolhas de hiperparâmetros feitas anteriormente neste livro. (Mais sobre isso depois.)

Claro, isso não responde de forma alguma à pergunta de por que estamos usando o validation_data para evitar overfitting, em vez de test_data. Para entender o porquê, considere que, ao definir os hiperparâmetros, é provável que tentemos muitas opções diferentes para os hiperparâmetros. Se definirmos os hiperparâmetros com base nas avaliações do test_data, será possível acabarmos super adequando nossos hiperparâmetros ao test_data. Ou seja, podemos acabar encontrando hiperparâmetros que se encaixam em peculiaridades particulares dos dados de teste, mas onde o desempenho da rede não se generalizará para outros conjuntos de dados. Protegemos contra isso descobrindo os hiperparâmetros usando o validation_data. Então, uma vez que tenhamos os hiperparâmetros que queremos, fazemos uma avaliação final da precisão usando o test_data. Isso nos dá confiança de que nossos resultados nos dados de teste são uma medida real de quão bem nossa rede neural se generaliza. Para colocar de outra forma, você pode pensar nos dados de validação como um tipo de dados de treinamento que nos ajuda a aprender bons parâmetros. Essa abordagem para encontrar bons hiperparâmetros é às vezes conhecida como o método “hold out”, uma vez que os dados de validação são mantidos separados ou “mantidos” a partir dos dados de treinamento.

Agora, na prática, mesmo depois de avaliar o desempenho nos dados de teste, podemos mudar nossa opinião e tentar outra abordagem – talvez uma arquitetura de rede diferente – que envolva a descoberta de um novo conjunto de hiperparâmetros. Se fizermos isso, não há perigo de acabarmos com o test_data também? Precisamos de uma regressão potencialmente infinita de conjuntos de dados, para que possamos ter certeza de que nossos resultados serão generalizados? Abordar essa preocupação é um problema profundo e difícil. Mas para nossos objetivos práticos, não vamos nos preocupar muito com essa questão. Em vez disso, vamos nos concentrar no método básico de retenção, com base nos dados training_data, validation_data e test_data, conforme descrito acima.

Vimos que o overfitting ocorre quando estamos usando apenas 1.000 imagens de treinamento. O que acontece quando usamos o conjunto completo de treinamento de 50.000 imagens? Manteremos todos os outros parâmetros iguais (30 neurônios ocultos, taxa de aprendizado de 0,5, tamanho de mini-lote de 10), mas treinamos usando todas as 50.000 imagens por 30 épocas. Aqui está um gráfico mostrando os resultados da precisão de classificação nos dados de treinamento e nos dados de teste. Observe que usei os dados de teste aqui, em vez dos dados de validação, para tornar os resultados mais diretamente comparáveis aos gráficos anteriores.

overfitting_full

Como você pode ver, a precisão nos dados de teste e treinamento permanece muito mais próxima do que quando estávamos usando 1.000 exemplos de treinamento. Em particular, a melhor precisão de classificação de 97,86% nos dados de treinamento é apenas 2,53% maior do que os 95,33% nos dados de teste. Isso é comparado com a diferença de 17,73% que tivemos anteriormente! Overfitting ainda está acontecendo, mas foi bastante reduzido. Nossa rede está se generalizando muito melhor dos dados de treinamento para os dados de teste. Em geral, uma das melhores maneiras de reduzir o overfitting é aumentar o volume (tamanho) dos dados de treinamento. Com dados de treinamento suficientes, é difícil até mesmo uma rede muito grande sofrer de overfitting. Infelizmente, os dados de treinamento podem ser caros ou difíceis de adquirir, por isso nem sempre é uma opção prática.

Aumentar a quantidade de dados de treinamento é uma maneira de reduzir o overfitting. Mas existem outras maneiras de reduzir a extensão do overfitting? Uma abordagem possível é reduzir o tamanho da nossa rede. No entanto, redes grandes têm o potencial de serem mais poderosas do que redes pequenas e, portanto, essa é uma opção que só adotamos em último caso.

Felizmente, existem outras técnicas que podem reduzir o overfitting, mesmo quando temos uma rede fixa e dados de treinamento fixos. Estas técnicas são conhecidas como técnicas de regularização e serão assunto do próximo capítulo.

Até lá!

Referências:

Formação Engenheiro de IA

Dot Product 

Neural Networks & The Backpropagation Algorithm, Explained

Neural Networks and Deep Learning

Machine Learning

The Elements of Statistical Learning: Data Mining, Inference, and Prediction, Second Edition

Gradient Descent For Machine Learning

Pattern Recognition and Machine Learning

Understanding Activation Functions in Neural Networks

Redes Neurais, princípios e práticas

Deep Learning Book

Capítulo 18 – Entropia Cruzada Para Quantificar a Diferença Entre Distribuições de Probabilidade

by

A Cross-Entropy (ou entropia cruzada, se você preferir o termo em português) é fácil de implementar como parte de um programa que aprende usando gradiente descendente e backpropagation. Faremos isso nos próximos capítulos quando treinarmos uma rede completa, desenvolvendo uma versão melhorada do nosso programa anterior para classificar os dígitos manuscritos do dataset MNIST. O novo programa é chamado de network2.py e incorpora não apenas a entropia cruzada, mas também várias outras técnicas que estudaremos mais adiante. Agora, vejamos como usar a Entropia Cruzada Para Quantificar a Diferença Entre Duas Distribuições de Probabilidade.

Por enquanto, vamos ver como nosso novo programa classifica os dígitos MNIST. Usaremos uma rede com 30 neurônios ocultos, e usaremos um tamanho de mini-lote de 10. Definimos a taxa de aprendizado para η = 0,5 e nós treinamos por 30 épocas. A interface para o network2.py será um pouco diferente do network.py, mas ainda deve estar claro o que está acontecendo. Nos próximos capítulos apresentamos o código completo.

 

corss-entropy

 

Perceba que o comando net.large_weight_initializer() é usado para inicializar os pesos e vieses da mesma maneira que já descrevemos anteriormente. Precisamos executar este comando porque mais adiante vamos alterar o peso padrão para inicialização em nossas redes. O resultado da execução da sequência de comandos acima é uma rede com 95,49% de precisão.

Vejamos também o caso em que usamos 100 neurônios ocultos, a entropia cruzada, e mantemos os parâmetros da mesma forma. Neste caso, obtemos uma precisão de 96,82%. Essa é uma melhoria substancial em relação aos resultados que obtivemos nos capítulos anteriores, onde a precisão de classificação foi de 96,59%, usando o custo quadrático. Isso pode parecer uma pequena mudança, mas considere que a taxa de erro caiu de 3,41% para 3,18%. Ou seja, eliminamos cerca de um em quatorze dos erros originais. Isso é uma melhoria bastante útil.

É encorajador que o custo de entropia cruzada nos dê resultados semelhantes ou melhores do que o custo quadrático. No entanto, esses resultados não provam conclusivamente que a entropia cruzada é uma escolha melhor. A razão é que nós colocamos apenas um pequeno esforço na escolha de hyperparâmetros como taxa de aprendizado, tamanho de mini-lote e assim por diante. Para que a melhoria seja realmente convincente, precisaríamos fazer um trabalho completo de otimização desses hyperparâmetros. Ainda assim, os resultados são encorajadores e reforçam nosso argumento teórico anterior de que a entropia cruzada é uma escolha melhor do que o custo quadrático.

Isso, a propósito, é parte de um padrão geral que veremos nos próximos capítulos e, na verdade, em grande parte do restante do livro. Vamos desenvolver uma nova técnica, vamos experimentá-la e obteremos resultados “aprimorados”. É claro que é bom vermos essas melhorias, mas a interpretação de tais melhorias é sempre problemática. Elas só são verdadeiramente convincentes se virmos uma melhoria depois de nos esforçarmos para otimizar todos os outros hyperparâmetros. Isso é uma grande quantidade de trabalho, exigindo muito poder de computação, e normalmente não vamos fazer uma investigação tão exaustiva. Em vez disso, procederemos com base em testes informais como os realizados até aqui.

Até agora, discutimos a entropia cruzada de forma bem detalhada. Por que tanto esforço quando a entropia cruzada nos dá apenas uma pequena melhora em nossos resultados com o dataset MNIST? Mais adiante veremos outras técnicas, notadamente a regularização, que trazem melhorias muito maiores. Então, por que tanto foco na entropia cruzada? Parte da razão é que a entropia cruzada é uma função de custo amplamente utilizada e, portanto, vale a pena compreendê-la bem. Mas a razão mais importante é que a saturação dos neurônios é um problema importante nas redes neurais, um problema ao qual voltaremos repetidamente ao longo do livro. Por isso discutimos a entropia cruzada em extensão pois é um bom laboratório para começar a entender a saturação dos neurônios e como ela pode ser abordada.

O que significa a entropia cruzada? De onde isso vem?

Nossa discussão sobre a entropia cruzada se concentrou na análise algébrica e na implementação prática. Isso é útil, mas deixa questões conceituais mais amplas não respondidas, como: o que significa a entropia cruzada? Existe alguma maneira intuitiva de pensar sobre a entropia cruzada? E quanto ao significado intuitivo da entropia cruzada? Como devemos pensar sobre isso?

Explicar isso em profundidade nos levaria mais longe do que queremos ir neste livro. No entanto, vale ressaltar que existe uma maneira padrão de interpretar a entropia cruzada que vem do campo da teoria da informação. Vejamos.

Já sabemos que para treinar uma rede neural, você precisa encontrar o erro entre as saídas calculadas e as saídas alvo desejadas. A medida de erro mais comum é chamada de erro quadrático médio (ou Mean Square Error). No entanto, existem alguns resultados de pesquisa que sugerem o uso de uma medida diferente, denominada erro de entropia cruzada, como método preferível em relação ao erro quadrático médio.

A medida de entropia cruzada tem sido utilizada como alternativa ao erro quadrático médio. A entropia cruzada pode ser usada como uma medida de erro quando as saídas de uma rede podem ser pensadas como representando hipóteses independentes (por exemplo, cada nó significa um conceito diferente) e as ativações dos nós podem ser entendidas como representando a probabilidade (ou a confiança) que cada uma das hipóteses pode ser verdadeira. Nesse caso, o vetor de saída representa uma distribuição de probabilidade, e nossa medida de erro – entropia cruzada – indica a distância entre o que a rede acredita que essa distribuição deve ser e o que realmente deveria ser. Existe também uma razão prática para usar a entropia cruzada. Pode ser mais útil em problemas nos quais os alvos são 0 e 1. A entropia cruzada tende a permitir que erros alterem pesos mesmo quando houver nós saturados (o que significa que suas derivadas são próximas de 0). Vamos compreender melhor isso:

A entropia cruzada é comumente usada para quantificar a diferença entre duas distribuições de probabilidade. Geralmente, a distribuição “verdadeira” (dos dados usados para treinamento) é expressa em termos de uma distribuição One-Hot.

Por exemplo, suponha que para uma instância de treinamento específica (uma única linha no seu dataset), a classe seja B (de 3 possíveis possibilidades: A, B e C). A distribuição única para esta instância de treinamento é, portanto:

 

Pr(Class A)  Pr(Class B)  Pr(Class C)

0.0          1.0          0.0

 

Você pode interpretar a distribuição acima da seguinte forma: a instância de treinamento tem 0% de probabilidade de ser classe A, 100% de probabilidade de ser classe B e 0% de probabilidade de ser a classe C.

Agora, suponha que seu algoritmo de aprendizado de máquina tenha previsto a seguinte distribuição de probabilidade:

 

Pr(Class A)  Pr(Class B)  Pr(Class C)     

0.228          0.619           0.153

 

Quão próxima é a distribuição prevista da distribuição verdadeira? É isso que determina o erro de entropia cruzada. A entropia cruzada é representada por esta fórmula:

 

cross-entropy

 

A soma é sobre as três classes A, B e C. Se você completar o cálculo, você achará que a perda é 0.479. Então, é assim que “longe” está a sua previsão da distribuição verdadeira.

A entropia cruzada é uma das muitas funções de perda possíveis. Essas funções de perda são tipicamente escritas como J(theta) e podem ser usadas dentro da descida do gradiente, que é uma estrutura iterativa para mover os parâmetros (ou coeficientes) para os valores ótimos. A entropia cruzada descreve a perda entre duas distribuições de probabilidade.

Ao usar uma rede neural para realizar classificação e predição, geralmente é melhor usar o erro de entropia cruzada do que o erro de classificação e um pouco melhor usar o erro de entropia cruzada do que o erro quadrático médio para avaliar a qualidade da rede neural. É importante deixar claro que estamos lidando apenas com uma rede neural que é usada para classificar os dados, como a previsão da concessão de crédito (sim ou não), ou ainda outras classificações como idade, sexo ou dígitos no dataset MNIST e assim por diante. Não estamos lidando com uma rede neural que faz regressão, onde o valor a ser previsto é numérico.

Até o próximo capítulo!

Referências:

Dot Product 

Neural Networks & The Backpropagation Algorithm, Explained

Derivada

Machine Learning

The Elements of Statistical Learning: Data Mining, Inference, and Prediction, Second Edition

Gradient Descent For Machine Learning

Pattern Recognition and Machine Learning

Understanding Activation Functions in Neural Networks

Redes Neurais, princípios e práticas

Paginação de posts

  • Previous
  • 1
  • 2
  • 3
  • Next

Capítulos Recentes

  • Capítulo 90 – Como Funcionam os Transformadores em Processamento de Linguagem Natural – Parte 5
  • Capítulo 89 – Como Funcionam os Transformadores em Processamento de Linguagem Natural – Parte 4
  • Capítulo 88 – Como Funcionam os Transformadores em Processamento de Linguagem Natural – Parte 3
  • Capítulo 87 – Como Funcionam os Transformadores em Processamento de Linguagem Natural – Parte 2
  • Capítulo 86 – Como Funcionam os Transformadores em Processamento de Linguagem Natural – Parte 1
  • Capítulo 85 – Transformadores – O Estado da Arte em Processamento de Linguagem Natural
  • Capítulo 84 – CLIP (Contrastive Language Image Pre-training): Conectando Texto e Imagens
  • Capítulo 83 – Liquid Neural Network – Rede Neural Líquida
  • Capítulo 82 – GPT-3 – Características e Limitações
  • Capítulo 81 – Modelos de Linguagem Como Tarefas Não Supervisionadas (GPT-2)

Buscar

Podcast DSA

© 2025 Data Science Academy - www.datascienceacademy.com.br