Skip to content

Deep Learning Book

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

Deep Learning Book

Em Português, Online e Gratuito

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 Duas 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

Deep Learning Book

Capítulo 17 – Cross-Entropy Cost Function

by

Quando um jogador de tênis está aprendendo a praticar o esporte, ele geralmente passa a maior parte do tempo desenvolvendo o movimento do corpo. Apenas gradualmente ele desenvolve as tacadas, aprende a movimentar a bola com precisão para a quadra adversária e com isso vai construindo sua técnica, que se aprimora à medida que ele pratica. De maneira semelhante, até agora nos concentramos em entender o algoritmo de retropropagação (backpropagation), a base para aprender a maioria das atividades em redes neurais. A partir de agora, estudaremos um conjunto de técnicas que podem ser usadas para melhorar nossa implementação do backpropagation e, assim, melhorar a maneira como nossas redes aprendem.

As técnicas que desenvolveremos incluem: uma melhor escolha de função de custo, conhecida como função de custo de entropia cruzada (ou Cross-Entropy Cost Function); quatro métodos de “regularização” (regularização de L1 e L2, dropout e expansão artificial dos dados de treinamento), que melhoram nossas redes para generalizar além dos dados de treinamento; um método melhor para inicializar os pesos na rede; e um conjunto de heurísticas para ajudar a escolher bons hyperparâmetros para a rede. Também vamos analisar várias outras técnicas com menos profundidade. As discussões são em grande parte independentes umas das outras e, portanto, você pode avançar se quiser. Também implementaremos muitas das técnicas em nosso código e usaremos para melhorar os resultados obtidos no problema de classificação de dígitos manuscritos estudado nos capítulos anteriores.

Naturalmente, estamos cobrindo apenas algumas das muitas técnicas que foram desenvolvidas para uso em redes neurais. A filosofia é que o melhor acesso à multiplicidade de técnicas disponíveis é o estudo aprofundado de algumas das mais importantes. Dominar essas técnicas importantes não é apenas útil por si só, mas também irá aprofundar sua compreensão sobre quais problemas podem surgir quando você usa redes neurais. Isso deixará você bem preparado para aprender rapidamente outras técnicas, conforme necessário.

A Função de Custo

A maioria de nós acha desagradável estar errado. Logo depois de começar a aprender piano, minha filha fez sua primeira apresentação diante de uma platéia. Ela estava nervosa e começou a tocar a peça com uma oitava muito baixa. Ela ficou confusa e não pôde continuar até que alguém apontasse o erro. Ela ficou muito envergonhada. Ainda que desagradável, também aprendemos rapidamente quando estamos decididamente errados. Você pode apostar que a próxima vez que ela se apresentou diante de uma platéia, ela começou na oitava correta! Em contraste, aprendemos mais lentamente quando nossos erros são menos bem definidos.

Idealmente, esperamos que nossas redes neurais aprendam rapidamente com seus erros. Mas é isso que acontece na prática? Para responder a essa pergunta, vamos dar uma olhada em um exemplo simples. O exemplo envolve um neurônio com apenas uma entrada:

 

neuron

 

Nós vamos treinar esse neurônio para fazer algo ridiculamente fácil: obter a entrada 1 e gerar a saída 0. Claro, essa é uma tarefa tão trivial que poderíamos facilmente descobrir um peso apropriado e um viés (bias) de forma manual, sem usar um algoritmo de aprendizado. No entanto, vai nos ajudar a compreender melhor o processo de usar gradiente descendente para tentar aprender um peso e viés. Então, vamos dar uma olhada em como o neurônio aprende.

Para tornar as coisas definitivas, escolhemos o peso inicial como 0.6 e o ​​viés inicial como 0.9. Estas são escolhas genéricas usadas como um lugar para começar a aprender, eu não as escolhi para serem especiais de alguma forma. A saída inicial do neurônio é 0.82, então um pouco de aprendizado será necessário antes que nosso neurônio se aproxime da saída desejada 0,0.

No gráfico abaixo, podemos ver como o neurônio aprende uma saída muito mais próxima de 0.0. Durante o treinamento, o modelo está realmente computando o gradiente, e usando o gradiente para atualizar o peso e o viés, e exibir o resultado. A taxa de aprendizado é η = 0.15, o que acaba sendo lento o suficiente para que possamos acompanhar o que está acontecendo, mas rápido o suficiente para que possamos obter um aprendizado substancial em apenas alguns segundos. O custo é a função de custo quadrático, C, apresentada nos capítulos anteriores. Vou lembrá-lo da forma exata da função de custo em breve.

 

train

 

Como você pode ver, o neurônio aprende um peso e um viés que diminui o custo e dá uma saída do neurônio de cerca de 0.09 (Epoch, ou Época em português, é o número de passadas que nosso modelo faz pelos dados. A cada passada, os pesos são atualizados, o aprendizado ocorre e o custo, ou a taxa de erros, diminui). Isso não é exatamente o resultado desejado, 0.0, mas é muito bom.

Suponha, no entanto, que, em vez disso, escolhamos o peso inicial e o viés inicial como 2.0. Nesse caso, a saída inicial é 0.98, o que é muito ruim. Vamos ver como o neurônio aprende a gerar 0 neste caso:

 

train2

 

Embora este exemplo use a mesma taxa de aprendizado (η = 0.15), podemos ver que a aprendizagem começa muito mais devagar. De fato, nas primeiras 150 épocas de aprendizado, os pesos e vieses não mudam muito. Então o aprendizado entra em ação e, como em nosso primeiro exemplo, a saída do neurônio se aproxima rapidamente de 0.0.

Esse comportamento é estranho quando comparado ao aprendizado humano. Como eu disse no começo deste capítulo, muitas vezes aprendemos mais rápido quando estamos muito errados sobre algo. Mas acabamos de ver que nosso neurônio artificial tem muita dificuldade em aprender quando está muito errado – muito mais dificuldade do que quando está apenas um pouco errado. Além do mais, verifica-se que esse comportamento ocorre não apenas neste exemplo, mas em redes mais gerais. Por que aprender tão devagar? E podemos encontrar uma maneira de evitar essa desaceleração?

Para entender a origem do problema, considere que nosso neurônio aprende mudando o peso e o viés a uma taxa determinada pelas derivadas parciais da função custo, ∂C/∂w e ∂C/∂b. Então, dizer “aprender é lento” é realmente o mesmo que dizer que essas derivadas parciais são pequenas. O desafio é entender por que eles são pequenas. Para entender isso, vamos calcular as derivadas parciais. Lembre-se de que estamos usando a função de custo quadrático, que é dada por:

cost

onde a é a saída do neurônio quando a entrada de treinamento x = 1 é usada, e y = 0 é a saída desejada correspondente. Para escrever isso mais explicitamente em termos de peso e viés, lembre-se que a = σ(z), onde z = wx + b. Usando a regra da cadeia para diferenciar em relação ao peso e viés, obtemos:

 

cost2

 

onde substitui x = 1 e y = 0. Para entender o comportamento dessas expressões, vamos olhar mais de perto o termo σ ′ (z) no lado direito. Lembre-se da forma da função σ:

 

sig

 

Podemos ver neste gráfico que quando a saída do neurônio é próxima de 1, a curva fica muito plana, e então σ ′ (z) fica muito pequeno. As equações acima então nos dizem que ∂C/∂w e ∂C/∂b ficam muito pequenos. Esta é a origem da desaceleração da aprendizagem. Além do mais, como veremos mais adiante, a desaceleração do aprendizado ocorre basicamente pelo mesmo motivo em redes neurais mais genéricas, não apenas neste exemplo simples.

A Função de Custo de Entropia Cruzada

Como podemos abordar a desaceleração da aprendizagem? Acontece que podemos resolver o problema substituindo o custo quadrático por uma função de custo diferente, conhecida como entropia cruzada. Para entender a entropia cruzada, vamos nos afastar um pouco do nosso modelo super-simples. Vamos supor que estamos tentando treinar um neurônio com diversas variáveis de entrada, x1, x2,…, pesos correspondentes w1, w2,… e um viés, b:

 

neuron2

 

A saída do neurônio é, naturalmente, a = σ(z), onde z = ∑jwjxj + b é a soma ponderada das entradas. Nós definimos a função de custo de entropia cruzada para este neurônio assim:

 

entropy

 

onde n é o número total de itens de dados de treinamento, a soma é sobre todas as entradas de treinamento x, e y é a saída desejada correspondente. Não é óbvio que a expressão anterior resolva o problema de desaceleração do aprendizado. De fato, francamente, nem é óbvio que faz sentido chamar isso de uma função de custo! Antes de abordar a desaceleração da aprendizagem, vamos ver em que sentido a entropia cruzada pode ser interpretada como uma função de custo.

Duas propriedades em particular tornam razoável interpretar a entropia cruzada como uma função de custo. Primeiro, não é negativo, isto é, C > 0. Para visualizar isso, observe na fórmula anterior que: (a) todos os termos individuais na soma são negativos, já que ambos os logaritmos são de números no intervalo de 0 a 1; e (b) há um sinal de menos na frente da soma.

Segundo, se a saída real do neurônio estiver próxima da saída desejada para todas as entradas de treinamento x, então a entropia cruzada será próxima de zero. Para ver isso, suponha, por exemplo, que y = 0 e a ≈ 0 para alguma entrada x. Este é um caso quando o neurônio está fazendo um bom trabalho nessa entrada. Vemos que o primeiro termo (na fórmula acima) para o custo, desaparece, desde que y = 0, enquanto o segundo termo é apenas −ln (1 − a) ≈ 0. Uma análise semelhante é válida quando y = 1 e a ≈ 1. E assim, a contribuição para o custo será baixa, desde que a saída real esteja próxima da saída desejada.

Em suma, a entropia cruzada é positiva e tende a zero, à medida que o neurônio melhora a computação da saída desejada, y, para todas as entradas de treinamento, x.

Essas são as duas propriedades que esperamos intuitivamente para uma função de custo. De fato, ambas as propriedades também são satisfeitas pelo custo quadrático. Portanto, isso é uma boa notícia para a entropia cruzada. Mas a função custo de entropia cruzada tem o benefício de que, ao contrário do custo quadrático, evita o problema de desaceleração do aprendizado. Para ver isso, vamos calcular a derivada parcial do custo de entropia cruzada em relação aos pesos. Substituímos a = σ (z) na fórmula acima e aplicamos a regra da cadeia duas vezes, obtendo:

 

form1

 

Colocando tudo em um denominador comum e simplificando, isso se torna:

 

form2

 

Usando a definição da função sigmóide, σ (z) = 1 / (1 + ez), e um pouco de álgebra, podemos mostrar que σ (z) = σ (z) (1 − σ (z)). Vemos que os termos σ′ (z) e σ (z) (1 − σ (z)) se cancelam na equação acima, e simplificando torna-se:

 

form3

 

Esta é uma bela expressão. Ela nos diz que a taxa na qual o peso aprende é controlada por σ (z) −y, ou seja, pelo erro na saída. Quanto maior o erro, mais rápido o neurônio aprenderá. Isso é exatamente o que nós esperamos intuitivamente. Em particular, evita a lentidão de aprendizado causada pelo termo σ′ (z) na equação análoga para o custo quadrático. Quando usamos a entropia cruzada, o termo σ′ (z) é cancelado e não precisamos mais nos preocupar em ser pequeno. Este cancelamento é o milagre especial assegurado pela função de custo de entropia cruzada. Na verdade, não é realmente um milagre. Como veremos mais adiante, a entropia cruzada foi especialmente escolhida por ter apenas essa propriedade.

De maneira semelhante, podemos calcular a derivada parcial para o viés. Eu não vou passar por todos os detalhes novamente, mas você pode facilmente verificar que:

form4

 

Novamente, isso evita a lentidão de aprendizado causada pelo termo σ′ (z) na equação análoga para o custo quadrático.

Agora vamos retornar ao exemplo do início deste capítulo, e explorar o que acontece quando usamos a entropia cruzada em vez do custo quadrático. Para nos reorientarmos, começaremos com o caso em que o custo quadrático foi bom, com peso inicial de 0.6 e viés inicial de 0.9. Veja o que acontece quando substituímos o custo quadrático pela entropia cruzada:

 

train3

 

Como era de se esperar, o neurônio aprende perfeitamente bem neste caso, assim como fez anteriormente. E agora vamos olhar para o caso em que nosso neurônio ficou preso antes, com o peso e o viés ambos começando em 2.0:

 

train4

 

Sucesso! Desta vez, o neurônio aprendeu rapidamente, exatamente como esperávamos. Se você observar atentamente, pode ver que a inclinação da curva de custo era muito mais íngreme inicialmente do que a região plana inicial na curva correspondente para o custo quadrático. É essa inclinação que a entropia cruzada nos ajuda a resolver, impedindo-nos de ficar presos exatamente quando esperamos que nosso neurônio aprenda mais depressa, ou seja, quando o neurônio começa errado.

Eu não disse qual taxa de aprendizado foi usada nos exemplos que acabei de ilustrar. Anteriormente, com o custo quadrático, usamos η = 0.15. Deveríamos ter usado a mesma taxa de aprendizado nos novos exemplos? De fato, com a mudança na função de custo, não é possível dizer precisamente o que significa usar a “mesma” taxa de aprendizado; é uma comparação de maçãs e laranjas. Para ambas as funções de custo, simplesmente experimentei encontrar uma taxa de aprendizado que possibilitasse ver o que está acontecendo. Se você ainda estiver curioso, aqui está o resumo: usei η = 0.005 nos exemplos que acabei de fornecer.

Você pode contestar que a mudança na taxa de aprendizado torna os gráficos acima sem sentido. Quem se importa com a rapidez com que o neurônio aprende, quando a nossa escolha de taxa de aprendizado foi arbitrária, para começar ?! Mas essa objeção não procede. O ponto dos gráficos não é sobre a velocidade absoluta de aprendizagem. É sobre como a velocidade do aprendizado muda. Em particular, quando usamos o custo quadrático, a aprendizagem é mais lenta quando o neurônio está inequivocamente errado do que é mais tarde durante o treinamento, à medida que o neurônio se aproxima da saída correta; enquanto o aprendizado de entropia cruzada é mais rápido quando o neurônio está inequivocamente errado. Essas declarações não dependem de como a taxa de aprendizado é definida.

Estamos estudando a entropia cruzada para um único neurônio. No entanto, é fácil generalizar a entropia cruzada para redes multicamadas de muitos neurônios. Em particular, suponha que y = y1, y2,… são os valores desejados nos neurônios de saída, ou seja, os neurônios na camada final, enquanto aL1, aL2,… são os valores reais de saída. Então nós definimos a entropia cruzada por:

 

cost

 

Isso é o mesmo que nossa expressão anterior, exceto que agora nós temos o ∑j somando todos os neurônios de saída. Não vou explicitamente trabalhar com uma derivação, mas deve ser plausível que o uso da expressão anterior evite uma desaceleração na aprendizagem em muitas redes de neurônios.

A propósito, estou usando o termo “entropia cruzada” de uma maneira que confundiu alguns dos primeiros leitores, já que parece superficialmente entrar em conflito com outras fontes. Em particular, é comum definir a entropia cruzada para duas distribuições de probabilidade, pj e qj, como ∑jpjlnqj. Esta definição pode ser conectada a fórmula da entropia para um neurônio mostrada anteriormente, se tratarmos um único neurônio sigmóide como saída de uma distribuição de probabilidade que consiste na ativação a do neurônio ae seu complemento 1 − a.

No entanto, quando temos muitos neurônios sigmoides na camada final, o vetor aLj de ativações não costuma formar uma distribuição de probabilidade. Como resultado, uma definição como ∑jpjlnqj não faz sentido, já que não estamos trabalhando com distribuições de probabilidade. Em vez disso, você pode pensar na fórmula da entropia para múltiplos neurônios como um conjunto somado de entropias cruzadas por neurônio, com a ativação de cada neurônio sendo interpretada como parte de uma distribuição de probabilidade de dois elementos. Sim, eu sei que isso não é simples.

Nesse sentido, a fórmula da entropia para múltiplos neurônios é uma generalização da entropia cruzada para distribuições de probabilidade.

Quando devemos usar a entropia cruzada em vez do custo quadrático? De fato, a entropia cruzada é quase sempre a melhor escolha, desde que os neurônios de saída sejam neurônios sigmóides. Para entender por que, considere que, quando estamos configurando a rede, normalmente inicializamos os pesos e vieses usando algum tipo de aleatoriedade. Pode acontecer que essas escolhas iniciais resultem na rede sendo decisivamente errada para alguma entrada de treinamento – isto é, um neurônio de saída terá saturado próximo de 1, quando deveria ser 0, ou vice-versa. Se estamos usando o custo quadrático que irá desacelerar a aprendizagem, ele não vai parar de aprender completamente, já que os pesos continuarão aprendendo com outras entradas de treinamento, mas é obviamente indesejável.

Construir aplicações de IA é uma habilidade com demanda cada vez maior no mercado.

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

Deep Learning Book

Capítulo 16 – Algoritmo Backpropagation em Python

by

Depois de compreender como funciona o backpropagation, podemos agora entender o código usado em alguns capítulos anteriores para implementar o algoritmo (o qual vamos reproduzir aqui). 

Em nosso código nós temos os métodos update_mini_batch e backprop da classe Network. Em particular, o método update_mini_batch atualiza os pesos e bias da rede calculando o gradiente para o mini_batch atual de exemplos (dados) de treinamento:

 

metodo1

 

A maior parte do trabalho é feita pela linha:

delta_nabla_b, delta_nabla_w = self.backprop (x, y)

que usa o método backprop para descobrir as derivadas parciais ∂Cx / ∂blj e ∂Cx / ∂wljk. Isso invoca o algoritmo de backpropagation, que é uma maneira rápida de calcular o gradiente da função de custo. Portanto, update_mini_batch funciona simplesmente calculando esses gradientes para cada exemplo de treinamento no mini_batch e, em seguida, atualizando self.weights e self.biases adequadamente. Há uma pequena mudança – usamos uma abordagem ligeiramente diferente para indexar as camadas. Essa alteração é feita para aproveitar um recurso do Python, ou seja, o uso de índices de lista negativa para contar para trás a partir do final de uma lista, por exemplo, lst[-3] é a terceira última entrada em uma lista chamada lst. O código para backprop está abaixo, junto com algumas funções auxiliares, que são usadas para calcular a função σ, a derivada σ′ e a derivada da função de custo. Com essas inclusões, você deve ser capaz de entender o código de maneira independente:

 

backprop

 

Observe o método backprop. Começamos inicalizando as matrizes de pesos (nabla_w) e bias (nabla_b) com zeros. Essas  matrizes serão alimentadas com valores durante o processo de treinamento. Isso é o que a rede neural artificial efetivamente aprende. Depois de inicializar alguns objetos, temos um loop for para cada valor de b e w (que a esta altura você já sabe se trata de bias e pesos, respectivamente). Neste loop, usamos a função np.dot do Numpy para a multiplicação entre matrizes e adição do bias, colocamos o resultado na lista z e fazemos uma chamada à função de ativação Sigmóide. Ao final deste loop, teremos a lista com todas as ativações e finalizamos a passada para a frente.

Na passada para trás (Backward Pass) calculamos as derivadas e fazemos as multiplicações de matrizes mais uma vez (o funcionamento de redes neurais artificiais é baseado em um conceito elementar da Álgebra Linear, a multiplicação de matrizes). Repare que chamamos o método Transpose() para gerar a transposta da matriz e assim ajustar as dimensões antes de efetuar os cálculo. Por fim, retornamos bias e pesos.

Em que sentido backpropagation é um algoritmo rápido?

Para responder a essa pergunta, vamos considerar outra abordagem para calcular o gradiente. Imagine que é o início da pesquisa de redes neurais. Talvez seja a década de 1950 ou 1960, e você é a primeira pessoa no mundo a pensar em usar gradiente descendente para o aprendizado! Mas, para que a ideia funcione, você precisa de uma maneira de calcular o gradiente da função de custo. Você volta ao seu conhecimento de cálculo e decide se pode usar a regra da cadeia (chain rule) para calcular o gradiente. Mas depois de brincar um pouco, a álgebra parece complicada e você fica desanimado. Então você tenta encontrar outra abordagem. Você decide considerar o custo como uma função apenas dos pesos C = C(w) (voltaremos ao bias em um momento). Você numera os pesos w1, w2,… e deseja computar ∂C / ∂wj para um peso específico wj. Uma maneira óbvia de fazer isso é usar a aproximação

form

onde ϵ> 0 é um pequeno número positivo e ej é o vetor unitário na direção j. Em outras palavras, podemos estimar ∂C / ∂wj calculando o custo C para dois valores ligeiramente diferentes de wj e, em seguida, aplicando a equação. A mesma ideia nos permitirá calcular as derivadas parciais ∂C / ∂b em relação aos vieses (bias).

Essa abordagem parece muito promissora. É simples conceitualmente e extremamente fácil de implementar, usando apenas algumas linhas de código. Certamente, parece muito mais promissor do que a ideia de usar a regra da cadeia para calcular o gradiente!

Infelizmente, embora essa abordagem pareça promissora, quando você implementa o código, ele fica extremamente lento. Para entender porque, imagine que temos um milhão de pesos em nossa rede. Então, para cada peso distinto wj, precisamos computar C (w + ϵej) para calcular ∂C / ∂wj. Isso significa que, para calcular o gradiente, precisamos computar a função de custo um milhão de vezes diferentes, exigindo um milhão de passos para frente pela rede (por exemplo, treinamento). Precisamos calcular C(w) também, em um total de um milhão de vezes e em uma única passada pela rede.

O que há de inteligente no backpropagation é que ele nos permite calcular simultaneamente todas as derivadas parciais ∂C / ∂wj usando apenas uma passagem direta pela rede, seguida por uma passagem para trás pela rede. Grosso modo, o custo computacional do passe para trás é quase o mesmo que o do forward. Isso deve ser plausível, mas requer algumas análises para fazer uma declaração cuidadosa. É plausível porque o custo computacional dominante no passe para frente é multiplicado pelas matrizes de peso, enquanto no passo para trás é multiplicado pelas transpostas das matrizes de peso. Obviamente, essas operações têm um custo computacional similar. E assim, o custo total da retropropagação (backpropagation) é aproximadamente o mesmo que fazer apenas duas passagens pela rede. Compare isso com o milhão e um passe para frente que precisávamos para a abordagem que descrevi anteriormente. E assim, embora a retropropagação pareça superficialmente mais complexa do que a abordagem anterior, é na verdade muito, muito mais rápida.

Essa aceleração foi amplamente apreciada em 1986 e expandiu enormemente a gama de problemas que as redes neurais poderiam resolver. Isso, por sua vez, causou uma onda de pessoas usando redes neurais. Claro, a retropropagação não é uma panacéia. Mesmo no final da década de 1980, as pessoas enfrentavam limites, especialmente quando tentavam usar a retropropagação para treinar redes neurais profundas, ou seja, redes com muitas camadas ocultas. Mais adiante, no livro, veremos como os computadores modernos e algumas novas ideias inteligentes tornam possível usar a retropropagação para treinar redes neurais bem profundas.

Seu trabalho agora é estudar e compreender cada linha de código usada em nossa rede de amostra. Esse código é bem simples e o objetivo é mostrar a você como as coisas funcionam programaticamente. Ainda vamos treinar nossa rede, avaliar seu desempenho, otimizar algumas operações e compreender outros conceitos básicos. Temos muito mais vindo por aí! Até o próximo capítulo!

Referências:

Machine Learning

Dot Product 

How the backpropagation algorithm works

Delta Rule

An overview of gradient descent optimization algorithms

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

An overview of gradient descent optimization algorithms

Optimization: Stochastic Gradient Descent

Gradient Descent vs Stochastic Gradient Descent vs Mini-Batch Learning

Deep Learning Book

Capítulo 14 – Algoritmo Backpropagation Parte 1 – Grafos Computacionais e Chain Rule

by

No último capítulo, vimos como as redes neurais podem aprender seus pesos e bias usando o algoritmo de gradiente descendente. Houve, no entanto, uma lacuna na nossa explicação: não discutimos como calcular o gradiente da função de custo. Neste capítulo, explicaremos sobre um algoritmo usado para calcular esses gradientes, um algoritmo conhecido como backpropagation. Como esse tema é a essência do treinamento de redes neurais, vamos dividí-lo em dois capítulos. Vamos começar com Algoritmo Backpropagation Parte 1 – Grafos Computacionais e Chain Rule.

O backpropagation é indiscutivelmente o algoritmo mais importante na história das redes neurais – sem backpropagation, seria quase impossível treinar redes de aprendizagem profunda da forma que vemos hoje. O backpropagation pode ser considerado a pedra angular das redes neurais modernas e consequentemente do Deep Learning.

O algoritmo backpropagation foi originalmente introduzido na década de 1970, mas sua importância não foi totalmente apreciada até um famoso artigo de 1986 de David Rumelhart, Geoffrey Hinton e Ronald Williams. Esse artigo descreve várias redes neurais em que o backpropagation funciona muito mais rapidamente do que as abordagens anteriores de aprendizado, possibilitando o uso de redes neurais para resolver problemas que antes eram insolúveis.

O backpropagation é o algoritmo-chave que faz o treinamento de modelos profundos algo computacionalmente tratável. Para as redes neurais modernas, ele pode tornar o treinamento com gradiente descendente até dez milhões de vezes mais rápido, em relação a uma implementação ingênua. Essa é a diferença entre um modelo que leva algumas horas ou dias para treinar e e outro que poderia levar anos (sem exagero).

Além de seu uso em Deep Learning, o backpropagation é uma poderosa ferramenta computacional em muitas outras áreas, desde previsão do tempo até a análise da estabilidade numérica. De fato, o algoritmo foi reinventado pelo menos dezenas de vezes em diferentes campos. O nome geral, independente da aplicação, é “diferenciação no modo reverso”.

Fundamentalmente, backpropagation é uma técnica para calcular derivadas rapidamente (não sabe o que é derivada? Consulte o link para um excelente vídeo em português explicando esse conceito em detalhes nas referências ao final deste capítulo). E é um truque essencial, não apenas em Deep Learning, mas em uma ampla variedade de situações de computação numérica. E para compreender backpropagation de forma efetiva, vamos primeiro compreender o conceito de grafo computacional e chain rule.

Grafo Computacional

Grafos computacionais são uma boa maneira de pensar em expressões matemáticas. O conceito de grafo foi introduzido por Leonhard Euler em 1736 para tentar resolver o problema das Pontes de Konigsberg. Grafos são modelos matemáticos para resolver problemas práticos do dia a dia, com várias aplicações no mundo real tais como: circuitos elétricos, redes de distribuição, relações de parentesco entre pessoas, análise de redes sociais, logística, redes de estradas, redes de computadores e muito mais. Grafos são muito usados para modelar problemas em computação.

Um Grafo é um modelo matemático que representa relações entre objetos. Um grafo G = (V, E) consiste de um conjunto de vértices V (também chamados de nós), ligados por um conjunto de bordas ou arestas E. Para aprender sobre grafos em mais detalhes, clique aqui.

Por exemplo, considere a expressão:

 

e = (a + b) ∗ (b + 1)

 

Existem três operações: duas adições e uma multiplicação. Para facilitar a compreensão sobre isso, vamos introduzir duas variáveis intermediárias c e d para que a saída de cada função tenha uma variável. Nós agora temos:

 

c = a+b
d = b+1
e = c∗d

 

Para criar um grafo computacional, fazemos cada uma dessas operações nos nós, juntamente com as variáveis de entrada. Quando o valor de um nó é a entrada para outro nó, uma seta vai de um para outro e temos nesse caso um grafo direcionado.

 

tree-def

 

Esses tipos de grafos surgem o tempo todo em Ciência da Computação, especialmente ao falar sobre programas funcionais. Eles estão intimamente relacionados com as noções de grafos de dependência e grafos de chamadas. Eles também são a principal abstração por trás do popular framework de Deep Learning, o TensorFlow.

Podemos avaliar a expressão definindo as variáveis de entrada para determinados valores e computando os nós através do grafo. Por exemplo, vamos definir a = 2 e b = 1:

 

tree-eval

 

A expressão, nesse exemplo, é avaliada como 6.

Derivadas em Grafos Computacionais

Se alguém quiser entender derivadas em um grafo computacional, a chave é entender as derivadas nas bordas (arestas que conectam os nós no grafo). Se a afeta diretamente c, então queremos saber como isso afeta c. Se a muda um pouco, como c muda? Chamamos isso de derivada parcial de c em relação a a.

Para avaliar as derivadas parciais neste grafo, precisamos da regra da soma e da regra do produto:

 

derivada

 

Abaixo, o grafo tem a derivada em cada borda (aresta) rotulada.

 

 tree-eval-derivs

 

 

E se quisermos entender como os nós que não estão diretamente conectados afetam uns aos outros? Vamos considerar como e é afetado por a. Se mudarmos a uma velocidade de 1, c também muda a uma velocidade de 1. Por sua vez, c mudando a uma velocidade de 1 faz com que e mude a uma velocidade de 2. Então e muda a uma taxa de 1 ∗ 2 em relação a a (analise o diagrama acima para visualizar isso).

A regra geral é somar todos os caminhos possíveis de um nó para o outro, multiplicando as derivadas em cada aresta do caminho. Por exemplo, para obter a derivada de e em relação a b, obtemos:

form

Isso explica como b afeta e através de c e também como isso afeta d.

Essa regra geral de “soma sobre caminhos” é apenas uma maneira diferente de pensar sobre a regra da cadeia multivariada ou chain rule.

Fatorando os Caminhos

O problema com apenas “somar os caminhos” é que é muito fácil obter uma explosão combinatória no número de caminhos possíveis.

 

chain-def-greek

 

No diagrama acima, existem três caminhos de X a Y, e mais três caminhos de Y a Z. Se quisermos obter a derivada ∂Z/∂X somando todos os caminhos, precisamos calcular 3 ∗ 3 = 9 caminhos:

 

form2

 

O exemplo acima só tem nove caminhos, mas seria fácil o número de caminhos crescer exponencialmente à medida que o grafo se torna mais complicado. Em vez de apenas ingenuamente somar os caminhos, seria muito melhor fatorá-los:

 

form3

 

É aí que entram a “diferenciação de modo de avanço” (forward-mode differentiation ou forward pass) e a “diferenciação de modo reverso” (reverse-mode differentiation ou backpropagation). Eles são algoritmos para calcular a soma de forma eficiente fatorando os caminhos. Em vez de somar todos os caminhos explicitamente, eles calculam a mesma soma de forma mais eficiente, mesclando os caminhos juntos novamente em cada nó. De fato, os dois algoritmos tocam cada borda exatamente uma vez!

A diferenciação do modo de avanço inicia em uma entrada para o grafo e se move em direção ao final. Em cada nó, soma todos os caminhos que se alimentam. Cada um desses caminhos representa uma maneira na qual a entrada afeta esse nó. Ao adicioná-los, obtemos a maneira total em que o nó é afetado pela entrada, isso é a derivada.

 

chain-forward-greek

 

Embora você provavelmente não tenha pensado nisso em termos de grafos, a diferenciação no modo de avanço é muito parecida com o que você aprendeu implicitamente caso tenha feito alguma introdução a Cálculo.

A diferenciação no modo reverso, por outro lado, começa na saída do grafo e se move em direção ao início (ou seja, se retropropaga ou backpropagation). Em cada nó, ele mescla todos os caminhos originados nesse nó.

 

chain-backward-greek

 

A diferenciação do modo de avanço rastreia como uma entrada afeta todos os nós. A diferenciação no modo reverso rastreia como cada nó afeta uma saída. Ou seja, a diferenciação de modo de avanço aplica o operador ∂/∂X a cada nó, enquanto a diferenciação de modo reverso aplica o operador ∂Z/∂ a cada nó. Se isso parece o conceito de programação dinâmica, é porque é exatamente isso! (acesse um material sobre programação dinâmica nas referências ao final do capítulo)

Nesse ponto, você pode se perguntar porque alguém se importaria com a diferenciação no modo reverso. Parece uma maneira estranha de fazer a mesma coisa que o modo de avanço. Existe alguma vantagem? Vamos considerar nosso exemplo original novamente:

 

tree-eval-derivs

 

Podemos usar a diferenciação de modo de avanço de b para cima. Isso nos dá a derivada de cada nó em relação a b.

 

tree-forwradmode

 

Nós calculamos ∂e/∂b, a derivada de nossa saída em relação a um de nossos inputs. E se fizermos a diferenciação de modo reverso de e para baixo? Isso nos dá a derivada de e em relação a todos os nós:

tree-backprop

 

Quando digo que a diferenciação no modo reverso nos dá a derivada de e em relação a cada nó, eu realmente quero dizer cada nó. Temos tanto ∂e/∂a quanto ∂e/∂b, as derivadas de e em relação a ambas as entradas. A diferenciação no modo de avanço nos deu a derivada de nossa saída em relação a uma única entrada, mas a diferenciação no modo reverso nos dá todos eles.

Para este grafo, isso é apenas um fator de duas velocidades, mas imagine uma função com um milhão de entradas e uma saída. A diferenciação no modo de avanço exigiria que passássemos pelo grafo um milhão de vezes para obter as derivadas. Diferenciação no modo reverso pode fazer isso em uma só passada! Uma aceleração de um fator de um milhão é bem legal e explica porque conseguimos treinar um modelo de rede neural profunda em tempo razoável.

Ao treinar redes neurais, pensamos no custo (um valor que descreve o quanto uma rede neural é ruim) em função dos parâmetros (números que descrevem como a rede se comporta). Queremos calcular as derivadas do custo em relação a todos os parâmetros, para uso em descida do gradiente. Entretanto, muitas vezes, há milhões ou até dezenas de milhões de parâmetros em uma rede neural. Então, a diferenciação no modo reverso, chamada de backpropagation no contexto das redes neurais, nos dá uma velocidade enorme!

Existem casos em que a diferenciação de modo de avanço faz mais sentido? Sim, existem! Onde o modo reverso fornece as derivadas de uma saída em relação a todas as entradas, o modo de avanço nos dá as derivadas de todas as saídas em relação a uma entrada. Se tiver uma função com muitas saídas, a diferenciação no modo de avanço pode ser muito, muito mais rápida.

Agora faz sentido?

Quando aprendemos pela primeira vez o que é backpropagation, a reação é: “Oh, essa é apenas a regra da cadeia (chain rule)! Como demoramos tanto tempo para descobrir?”

Na época em que o backpropagation foi inventado, as pessoas não estavam muito focadas nas redes neurais feedforward. Também não era óbvio que as derivadas eram o caminho certo para treiná-las. Esses são apenas óbvios quando você percebe que pode calcular rapidamente derivadas. Houve uma dependência circular.

Treinar redes neurais com derivadas? Certamente você ficaria preso em mínimos locais. E obviamente seria caro computar todas essas derivadas. O fato é que só porque sabemos que essa abordagem funciona é que não começamos imediatamente a listar os motivos que provavelmente não funcionaria. Já sabemos que funciona, mas novas abordagens vem sendo propostas no avanço das pesquisas em Deep Learning e Inteligência Artificial.

Conclusão da Parte 1

O backpropagation também é útil para entender como as derivadas fluem através de um modelo. Isso pode ser extremamente útil no raciocínio sobre porque alguns modelos são difíceis de otimizar. O exemplo clássico disso é o problema do desaparecimento de gradientes em redes neurais recorrentes, que discutiremos mais diante neste livro.

Por fim, há uma lição algorítmica ampla a ser retirada dessas técnicas. Backpropagation e forward-mode differentiation usam um poderoso par de truques (linearização e programação dinâmica) para computar derivadas de forma mais eficiente do que se poderia imaginar. Se você realmente entende essas técnicas, pode usá-las para calcular com eficiência várias outras expressões interessantes envolvendo derivadas.

Mas este capítulo teve como objetivo apenas ajudá-lo a compreender o algoritmo, já que praticamente não existe documentação sobre isso em português. Falta ainda compreender como o backpropagation é aplicado no treinamento das redes neurais. Ansioso por isso? Então acompanhe o próximo capítulo!

Referências:

Me Salva! Cálculo – O que é uma derivada?

The Birth Of Graph Theory: Leonhard Euler And The Königsberg Bridge Problem

Learning representations by back-propagating errors

Chain Rule

Calculus on Computational Graphs: Backpropagation

How the backpropagation algorithm works

Dynamic programming

Nota: parte das imagens usadas neste capítulo foram extraídas no excelente post (citado nas referências acima) de Christopher Olah, pesquisador de Machine Learning do Google Brain, e com a devida autorização do autor.

Deep Learning Book

Capítulo 13 – Construindo Uma Rede Neural Com Linguagem Python

by

Ok. Chegou a hora. Vamos escrever um programa em linguagem Python que aprenda como reconhecer dígitos manuscritos, usando Stochastic Gradient Descent e o dataset de treinamento MNIST. Se você chegou até aqui sem ler os capítulos anteriores, então pare imediatamente, leia os últimos 12 capítulos e depois volte aqui! Não tenha pressa! Não existe atalho para o aprendizado!

 

******************************** Atenção ********************************

Este capítulo considera que você já tem o interpretador Python instalado no seu computador, seja ele com sistema operacional Windows, MacOS ou Linux. Recomendamos que você instale o Anaconda e que já possua conhecimentos em linguagem Python. Se esse não for seu caso, antes de ler este capítulo e executar os exemplos aqui fornecidos, acesse o curso gratuito Fundamentos de Linguagem Python Para Análise de Dados e Data Science.

Usaremos Python 3 e você deve construir os scripts no seu computador. Vamos começar!

*************************************************************************

 

Quando descrevemos o dataset MNIST anteriormente, dissemos que ele estava dividido em 60.000 imagens de treinamento e 10.000 imagens de teste. Essa é a descrição oficial do MNIST. Mas vamos dividir os dados de forma um pouco diferente. Deixaremos as imagens de teste como está, mas dividiremos o conjunto de treinamento MNIST de 60.000 imagens em duas partes: um conjunto de 50.000 imagens, que usaremos para treinar nossa rede neural e um conjunto separado de validação de 10.000 imagens. Não utilizaremos os dados de validação neste capítulo, porém mais tarde, aqui mesmo no livro, usaremos este dataset quando estivermos configurando certos hiperparâmetros da rede neural, como a taxa de aprendizado por exemplo. Embora os dados de validação não façam parte da especificação MNIST original, muitas pessoas usam o MNIST desta forma e o uso de dados de validação é comum em redes neurais. Quando eu me referir aos “dados de treinamento MNIST” de agora em diante, vou me referir ao nosso conjunto de dados de 50.000 imagens, e não ao conjunto de dados de 60.000 imagens. Fique atento!

Além dos dados MNIST, também precisamos de uma biblioteca Python chamada Numpy, para álgebra linear. Se você instalou o Anaconda, não precisa se preocupar, pois o Numpy já está instalado. Caso contrário, será necessário fazer a instalação do pacote.

Mas antes de carregar e dividir os dados, vamos compreender os principais recursos do nosso código para construção de uma rede neural. A peça central é uma classe chamada Network, que usamos para representar uma rede neural. Abaixo a classe Network e seu construtor:

 

 

Neste código, o parâmetro sizes contêm o número de neurônios nas respectivas camadas, sendo um objeto do tipo lista em Python. Então, por exemplo, se queremos criar um objeto da classe Network com 2 neurônios na primeira camada, 3 neurônios na segunda camada e 1 neurônio na camada final, aqui está o código que usamos para instanciar um objeto da classe Network::

 

rede1 = Network([2, 3, 1])

 

Os bias e pesos no objeto rede1 são todos inicializados aleatoriamente, usando a função Numpy np.random.randn para gerar distribuições gaussianas com 0 de média e desvio padrão 1. Esta inicialização aleatória dá ao nosso algoritmo de descida do gradiente estocástico um local para começar. Em capítulos posteriores, encontraremos melhores maneiras de inicializar os pesos e os bias. Observe que o código de inicialização de rede assume que a primeira camada de neurônios é uma camada de entrada e omite a definição de quaisquer bias para esses neurônios, uma vez que os bias são usados apenas para calcular as saídas de camadas posteriores.

Observe também que os bias e pesos são armazenados como listas de matrizes Numpy. Assim, por exemplo, rede1.weights[1] é uma matriz Numpy armazenando os pesos conectando a segunda e terceira camadas de neurônios. (Não é a primeira e segunda camadas, uma vez que a indexação da lista em Python começa em 0.) Uma vez que rede1.weights[1] é bastante detalhado, vamos apenas indicar essa matriz w. É uma matriz tal que wjk é o peso para a conexão entre o neurônio kth na segunda camada e o neurônio jth na terceira camada. Essa ordenação dos índices j e k pode parecer estranha – certamente teria mais sentido trocar os índices j e k? A grande vantagem de usar essa ordenação é que isso significa que o vetor de ativações da terceira camada de neurônios é:

 

Form

Equação 1

 

Onde, a é o vetor de ativações da segunda camada de neurônios. Para obter um a’ multiplicamos a pela matriz de peso w, e adicionamos o vetor b com os bias (se você leu os capítulos anteriores, isso não deve ser novidade agora). Em seguida, aplicamos a função σ de forma elementar a cada entrada no vetor wa + b. (Isto é chamado de vetorizar a função σ.)

Com tudo isso em mente, é fácil escrever código que computa a saída de uma instância de rede. Começamos definindo a função sigmoide:

 

 

Observe que quando a entrada z é um vetor ou uma matriz Numpy, Numpy aplica automaticamente a função sigmoid elementwise, ou seja, na forma vetorizada.

Em seguida, adicionamos um método feedforward à classe Network, que, dada a entrada a para a rede, retorna a saída corresponente. Basicamente o método feedforward aplica a Equação 1 mostrada acima, para cada camada:

 

 

A principal atividade que queremos que nossos objetos da classe Network façam é aprender. Para esse fim, criaremos um método SGD (Stochastic Gradient Descent). Aqui está o código. É um pouco misterioso em alguns lugares, mas vamos explicar em detalhes mais abaixo:

 

 

O training_data é uma lista de tuplas (x, y) que representam as entradas de treinamento e as correspondentes saídas desejadas. As variáveis epochs e mini_batch_size são o que você esperaria – o número de épocas para treinar e o tamanho dos mini-lotes a serem usados durante a amostragem, enquanto eta é a taxa de aprendizagem, η. Se o argumento opcional test_data for fornecido, o programa avaliará a rede após cada período de treinamento e imprimirá progresso parcial. Isso é útil para rastrear o progresso, mas retarda substancialmente as coisas.

O código funciona da seguinte forma. Em cada época, ele começa arrastando aleatoriamente os dados de treinamento e, em seguida, particiona-os em mini-lotes de tamanho apropriado. Esta é uma maneira fácil de amostragem aleatória dos dados de treinamento. Então, para cada mini_batch, aplicamos um único passo de descida do gradiente. Isso é feito pelo código self.update_mini_batch (mini_batch, eta), que atualiza os pesos e os bias da rede de acordo com uma única iteração de descida de gradiente, usando apenas os dados de treinamento em mini_batch. Aqui está o código para o método update_mini_batch:

 

 

A maior parte do trabalho é feita pela linha delta_nabla_b, delta_nabla_w = self.backprop (x, y). Isso invoca algo chamado algoritmo de backpropagation, que é uma maneira rápida de calcular o gradiente da função de custo. Portanto, update_mini_batch funciona simplesmente calculando esses gradientes para cada exemplo de treinamento no mini_batch e, em seguida, atualizando self.weights e self.biases adequadamente.

Abaixo você encontra o código para self.backprop, mas não estudaremos ele agora. Estudaremos em detalhes como funciona o backpropagation no próximo capítulo, incluindo o código para self.backprop. Por hora, basta assumir que ele se comporta conforme indicado, retornando o gradiente apropriado para o custo associado ao exemplo de treinamento x.

 

 

Além do self.backprop, o programa é auto-explicativo – todo o levantamento pesado é feito em self.SGD e self.update_mini_batch, que já discutimos. O método self.backprop faz uso de algumas funções extras para ajudar no cálculo do gradiente, nomeadamente sigmoid_prime, que calcula a derivada da função σ e self.cost_derivative.

A classe Network é em essência nosso algoritmo de rede neural. A partir dela criamos uma instância (como rede1), alimentamos com os dados de treinamento e realizamos o treinamento. Avaliamos então a performance da rede com dados de teste e repetimos todo o processo até alcançar o nível de acurácia desejado em nosso projeto. Quando o modelo final estiver pronto, usamos para realizar as previsões para as quais o modelo foi criado, apresentando a ele novos conjuntos de dados e extraindo as previsões. Perceba que este é um algoritmo de rede neural bem simples, mas que permite compreender como funcionam as redes neurais e mais tarde, aqui mesmo no livro, as redes neurais profundas ou Deep Learning.

No próximo capítulo vamos continuar trabalhando com este algoritmo e compreender como funciona o Backpropagation. Na sequência, vamos carregar os dados, treinar e testar nossa rede neural e então usá-la para reconhecer dígitos manuscritos. Até lá.

Referências:

MNIST

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

Neural Networks and Deep Learning

An overview of gradient descent optimization algorithms

Optimization: Stochastic Gradient Descent

Gradient Descent vs Stochastic Gradient Descent vs Mini-Batch Learning

Deep Learning Book

Capítulo 12 – Aprendizado Com a Descida do Gradiente

by

No capítulo anterior definimos o design para a nossa rede neural e agora podemos começar o processo de aprendizado de máquina. Neste capítulo você vai compreender o que é o Aprendizado Com a Descida do Gradiente.

A primeira coisa que precisamos é um conjunto de dados para o treinamento da rede. Usaremos o conjunto de dados MNIST, que contém dezenas de milhares de imagens digitalizadas de dígitos manuscritos, juntamente com suas classificações corretas. O nome MNIST vem do fato de que é um subconjunto modificado de dois conjuntos de dados coletados pelo NIST, o Instituto Nacional de Padrões e Tecnologia dos Estados Unidos. Aqui estão algumas imagens do MNIST:

 

 

O MNIST tem duas partes. A primeira parte contém 60.000 imagens para serem usadas como dados de treinamento. Essas imagens são amostras de manuscritos escaneados de 250 pessoas, metade dos quais funcionários do Bureau do Censo dos EUA e metade dos estudantes do ensino médio. As imagens estão em escala de cinza e 28 por 28 pixels de tamanho. A segunda parte do conjunto de dados MNIST tem 10.000 imagens a serem usadas como dados de teste, também 28 por 28 pixels em escala de cinza. Usaremos os dados do teste para avaliar o quão bem a nossa rede neural aprendeu a reconhecer os dígitos. Para fazer deste um bom teste de desempenho, os dados de teste foram retirados de um conjunto diferente de 250 pessoas em relação aos dados de treinamento originais (embora ainda seja um grupo dividido entre funcionários do Census Bureau e alunos do ensino médio). Isso nos ajuda a confiar que nosso sistema pode reconhecer dígitos de pessoas cuja escrita não viu durante o treinamento.

Usaremos a notação x para indicar uma entrada (input) de treinamento. Será conveniente considerar cada entrada de treinamento x (cada imagem) como um vetor de 784 posições (28 x 28 pixels). A imagem abaixo representa como este vetor é construído:

 

 

Cada entrada no vetor representa o valor de cinza para um único pixel na imagem. Vamos indicar a saída correspondente desejada por y = y(x), onde y é um vetor com dimensão 10. Por exemplo, se uma imagem de treinamento particular, x, representa um 3, então y(x) = (0,0,0,1,0,0,0,0,0,0)T é a saída desejada da rede . Observe que T aqui é a operação de transposição, transformando um vetor de linha em um vetor comum (coluna). Vamos deixar isso mais claro. Observe a figura abaixo:

 

 

Vamos usar os pixels de imagem correspondentes ao fluxo inteiro chamado “features”. Os rótulos são One-Hot Encoded 1-hot. O rótulo que representa a classe de saída da imagem com dígito 3 torna-se “0001000000” uma vez que temos 10 classes para os 10 dígitos possíveis, onde o primeiro índice corresponde ao dígito “0” e o último corresponde ao dígito “9”.

O que queremos é um algoritmo que nos permita encontrar pesos e bias para que a saída da rede se aproxime de y(x) para todas as entradas de treinamento x. Para quantificar o quão bem estamos alcançando esse objetivo, definimos uma função de custo:

 

Função Quadrático de Custo

 

Na fórmula acima, w indica a coleta de todos os pesos na rede, b todos os bias (viés), n é o número total de entradas de treinamento, a é o vetor de saídas da rede (quando x é entrada) e a soma é sobre todas as entradas de treinamento x. Claro, a saída a depende de x, w e b, mas para manter a notação simples, eu não indiquei explicitamente essa dependência. A notação ‖v‖ apenas indica a função de comprimento usual para um vetor v. Chamaremos C a função de custo quadrático, que também é conhecido como o erro quadrático médio ou apenas o MSE (Mean Squared Error). Inspecionando a forma da função de custo quadrático, vemos que C (w, b) não é negativo, pois cada termo na soma não é negativo. Além disso, o custo C (w, b) torna-se pequeno, isto é, C (w, b) ≈ 0, precisamente quando y(x) é aproximadamente igual à saída, a, para todas as entradas de treinamento x.

Portanto, nosso algoritmo de treinamento faz um bom trabalho se ele pode encontrar pesos e bias para que C (w, b) ≈ 0. Isso significa basicamente que nosso modelo fez as previsões corretas, ou seja, cada vez que apresentamos ao modelo uma imagem com dígito 3, ele é capaz de reconhecer que se trata do número 3.

Em contraste, o algoritmo não terá boa performance, quando C (w, b) for um valor maior que 0 – isso significaria que nosso algoritmo não está conseguindo fazer as previsões, ou seja, quando apresentado a imagem com o dígito 3, ele não é capaz de prever que se trata de um número 3. Isso ocorre, porque a diferença entre o valor real da saída e o valor previsto pelo modelo, é muito alta. Assim, o objetivo do nosso algoritmo de treinamento será minimizar o custo C(w, b) em função dos pesos e dos bias. Em outras palavras, queremos encontrar um conjunto de pesos e bias que tornem o custo o menor possível. Vamos fazer isso usando um algoritmo conhecido como Descida do Gradiente (Gradient Descent).

Mas antes, uma pergunta. Por que introduzir o custo quadrático? Afinal, não nos interessamos principalmente pelo número de imagens corretamente classificadas pela rede? Por que não tentar maximizar esse número diretamente, em vez de minimizar uma medida, como o custo quadrático? O problema com isso é que o número de imagens corretamente classificadas não é uma “smooth function” dos pesos e bias na rede. Geralmente, fazer pequenas mudanças nos pesos e bias não causará nenhuma alteração no número de imagens de treinamento classificadas corretamente. Isso torna difícil descobrir como mudar os pesos e os bias para melhorar o desempenho. Se, em vez disso, usamos uma “smooth cost function”, como o custo quadrático, revela-se fácil descobrir como fazer pequenas mudanças nos pesos e nos bias para obter uma melhoria no custo. É por isso que nos concentramos primeiro na minimização do custo quadrático e somente depois examinaremos a precisão da classificação.

Mesmo considerando que queremos usar uma “smooth cost function”, você ainda pode se perguntar por que escolhemos a função quadrática. Talvez se escolhêssemos uma função de custo diferente, obteríamos um conjunto totalmente diferente de pesos e bias? Esta é uma preocupação válida e, mais tarde, revisitaremos a função de custo e faremos algumas modificações. No entanto, a função de custo quadrático mostrada anteriormente funciona perfeitamente para entender os conceitos básicos de aprendizagem em redes neurais, então ficaremos com isso por enquanto.

Recapitulando, nosso objetivo na construção de uma rede neural é encontrar pesos e bias que minimizem a função de custo quadrático C (w, b).

Descida do Gradiente

A maioria das tarefas em Machine Learning são na verdade problemas de otimização e um dos algoritmos mais usados para isso é o Algoritmo de Descida do Gradiente. Para um iniciante, o nome Algoritmo de Descida do Gradiente pode parecer intimidante, mas espero que depois de ler o que está logo abaixo, isso deixe de ser um mistério para você.

A Descida do Gradiente é uma ferramenta padrão para otimizar funções complexas iterativamente dentro de um programa de computador. Seu objetivo é: dada alguma função arbitrária, encontrar um mínimo. Para alguns pequenos subconjuntos de funções – aqueles que são convexos – há apenas um único minumum que também acontece de ser global. Para as funções mais realistas, pode haver muitos mínimos, então a maioria dos mínimos são locais. Certifique-se de que a otimização encontre o “melhor” minimum e não fique preso em mínimos sub-otimistas (um problema comum durante o treinamento do algoritmo).

Para compreender a intuição da Descida do Gradiente, vamos simplificar um pouco as coisas. Vamos imaginar que simplesmente recebemos uma função de muitas variáveis e queremos minimizar essa função. Vamos desenvolver a técnica chamada Descida do Gradiente que pode ser usada para resolver tais problemas de minimização. Então, voltaremos para a função específica que queremos minimizar para as redes neurais.

Ok, suponhamos que estamos tentando minimizar alguma função, C(v). Esta poderia ser qualquer função de valor real de muitas variáveis, onde v = v1, v2, …. Observe que eu substitui a notação w e b por v para enfatizar que esta poderia ser qualquer função – não estamos mais pensando especificamente no contexto das redes neurais apenas. Para minimizar C (v), vamos imaginar C como uma função de apenas duas variáveis, que chamaremos v1 e v2, conforme pode ser visto na figura abaixo:

Descida do Gradiente

O que queremos é encontrar onde C atinge seu mínimo global. Fica claro, que para a função traçada no gráfico acima, podemos observar facilmente o gráfico e encontrar o mínimo. Mas uma função geral, C, pode ser uma função complicada de muitas variáveis, e geralmente não será possível apenas observar o gráfico para encontrar o mínimo.

Uma maneira de atacar o problema é usar Cálculo (especificamente Álgebra Linear) para tentar encontrar o mínimo de forma analítica. Podemos calcular derivadas e depois tentar usá-las para encontrar lugares onde C é um extremum. Isso pode funcionar quando C é uma função de apenas uma ou algumas variáveis. Mas vai se transformar em um pesadelo quando tivermos muitas outras variáveis. E para as redes neurais, muitas vezes queremos muito mais variáveis – as maiores redes neurais têm funções de custo que dependem de bilhões de pesos e bias de uma maneira extremamente complicada. Usando “apenas” Cálculo para minimizar isso, não funcionará e precisamos de algo mais! Precisamos de um algoritmo de otimização capaz de minimizar C (v).

Felizmente, há uma analogia que nos ajuda a compreender como encontrar a solução. Começamos por pensar em nossa função como uma espécie de vale e imaginamos uma bola rolando pela encosta do vale, conforme pode ser visto na figura abaixo. Nossa experiência diária nos diz que a bola acabará rolando para o fundo do vale. Talvez possamos usar essa ideia como forma de encontrar um mínimo para a função? Escolheríamos aleatoriamente um ponto de partida para uma bola (imaginária), e então simularíamos o movimento da bola enquanto ela rola até o fundo do vale. Poderíamos fazer essa simulação simplesmente por derivadas de computação da função C – essas derivadas nos diriam tudo o que precisamos saber sobre a “forma” local do vale, e, portanto, como nossa bola deve rolar.

 

Representação da Descida do Gradiente (com o objetivo de minimizar a função de custo)

 

Ou seja, a Descida do Gradiente é um algoritmo de otimização usado para encontrar os valores de parâmetros (coeficientes ou se preferir w e b – weight e bias) de uma função que minimizam uma função de custo. A Descida do Gradiente é melhor usada quando os parâmetros não podem ser calculados analiticamente (por exemplo, usando álgebra linear) e devem ser pesquisados por um algoritmo de otimização.

O procedimento começa com valores iniciais para o coeficiente ou coeficientes da função. Estes poderiam ser 0.0 ou um pequeno valor aleatório (a inicialização dos coeficiente é parte crítica do processo e diversas técnicas podem ser usadas, ficando a escolha a cargo do Engenheiro de IA ou Cientista de Dados e do problema a ser resolvido com o modelo). Poderíamos iniciar assim nossos coeficientes (valores de w e b):

 

coeficiente = 0,0

 

O custo dos coeficientes é avaliado ligando-os à função e calculando o custo.

 

custo = f (coeficiente)

 

ou

 

custo = avaliar (f (coeficiente))

 

A derivada do custo é calculada. A derivada é um conceito de Cálculo e refere-se à inclinação da função em um determinado ponto. Precisamos conhecer a inclinação para que possamos conhecer a direção (sinal) para mover os valores dos coeficientes para obter um custo menor na próxima iteração.

 

delta = derivado (custo)

 

Agora que sabemos da derivada em que direção está em declive, podemos atualizar os valores dos coeficientes. Um parâmetro de taxa de aprendizagem (alfa) deve ser especificado e controla o quanto os coeficientes podem mudar em cada atualização.

 

coeficiente = coeficiente – (alfa * delta)

 

Este processo é repetido até que o custo dos coeficientes (função de custo) seja 0,0 ou próximo o suficiente de zero, indicando que as saídas da rede estão cada vez mais próximas dos valores reais (saídas desejadas).

A Descida do Gradiente é simples, mas exige que seja calculado o gradiente da função de custo ou a função que você está otimizando, mas além disso, é muito direto. Em resumo:

Você divide seus dados em amostras e a cada amostra (sample), você passa as entradas pela rede, multiplica pelos pesos, soma, e no final você vai ter sua saÍda (a previsão da rede). Você então compara a saída da sua rede com o a resposta certa, calcula o erro, e então retroage esse erro (backpropagation), ajustando os pesos de cada neurônio de cada camada. Quando você acabar de fazer a atualização dos pesos, uma nova amostra é introduzida e ela será multiplicada pelos pesos já atualizados. Esse processo de atualizar os pesos é que é chamado de “aprendizado”.

Se você observar os algoritmos mais atuais, todos trabalham dentro de um conceito relativamente novo chamado de mini-lotes (mini-batches). Para otimizar a performance, o que se faz é passar pela rede múltiplas amostras (por exemplo 128 amostras), calcular o erro médio delas e então realizar o backpropagation e a atualização dos pesos. Do ponto de vista da atualização dos pesos, 1 amostra = 128 amostras. Esse é um conceito mais novo, necessário principalmente no treinamento de grandes modelos de Deep Learning.

Em seguida, veremos como podemos usar isso em algoritmos de aprendizado de máquina.

Batch Gradient Descent em Aprendizado de Máquina

O objetivo de todos os algoritmos supervisionados de aprendizagem de máquina é estimar uma função de destino (f) que mapeia dados de entrada (X) para as variáveis ​​de saída (Y). Isso descreve todos os problemas de classificação e regressão (aprendizagem supervisionada).

Alguns algoritmos de aprendizagem de máquina têm coeficientes que caracterizam a estimativa de algoritmos para a função alvo (f). Diferentes algoritmos têm diferentes representações e diferentes coeficientes, mas muitos deles requerem um processo de otimização para encontrar o conjunto de coeficientes que resultam na melhor estimativa da função alvo. Os exemplos comuns de algoritmos com coeficientes que podem ser otimizados usando descida do gradiente são Regressão linear e Regressão logística.

A avaliação de quão próximo um modelo de aprendizagem de máquina estima a função de destino pode ser calculada de várias maneiras, muitas vezes específicas para o algoritmo de aprendizagem de máquina. A função de custo envolve a avaliação dos coeficientes no modelo de aprendizagem de máquina calculando uma previsão para o modelo para cada instância de treinamento no conjunto de dados e comparando as previsões com os valores de saída reais e calculando uma soma ou erro médio (como a Soma de Residuais Quadrados ou SSR no caso de regressão linear).

A partir da função de custo, uma derivada pode ser calculada para cada coeficiente para que ele possa ser atualizado usando exatamente a equação de atualização descrita acima.

O custo é calculado para um algoritmo de aprendizado de máquina em todo o conjunto de dados de treinamento para cada iteração do algoritmo de descida de gradiente. Uma iteração do algoritmo é chamada de um lote e esta forma de descida do gradiente é referida como descida do gradiente em lote (Batch Gradient Descent).

A descida do gradiente em lote é a forma mais comum de descida do gradiente em Machine Learning.

Stochastic Gradient Descent em Aprendizado de Máquina

A Descida do Gradiente pode ser lenta para executar em conjuntos de dados muito grandes. Como uma iteração do algoritmo de descida do gradiente requer uma previsão para cada instância no conjunto de dados de treinamento, pode demorar muito quando você tem muitos milhões de instâncias.

Em situações em que você possui grandes quantidades de dados, você pode usar uma variação da descida do gradiente chamada Stochastic Gradient Descent.

Nesta variação, o procedimento de descida do gradiente descrito acima é executado, mas a atualização para os coeficientes é realizada para cada instância de treinamento, em vez do final do lote de instâncias.

O primeiro passo do procedimento exige que a ordem do conjunto de dados de treinamento seja randomizada. Isto é, misturar a ordem que as atualizações são feitas para os coeficientes. Como os coeficientes são atualizados após cada instância de treinamento, as atualizações serão barulhentas saltando por todo o lado, e assim o custo correspondente funcionará. Ao misturar a ordem para as atualizações dos coeficientes, ela aproveita essa caminhada aleatória e evita que ela fique “distraída” ou presa.

O procedimento de atualização para os coeficientes é o mesmo que o anterior, exceto que o custo não é somado em todos os padrões de treinamento, mas sim calculado para um padrão de treinamento.

A aprendizagem pode ser muito mais rápida com descida de gradiente estocástica para conjuntos de dados de treinamento muito grandes e muitas vezes você só precisa de um pequeno número de passagens através do conjunto de dados para alcançar um conjunto de coeficientes bom o suficiente.

Ufa, você ainda está aí? Entende agora porque Cientistas de Dados e Engenheiros de IA devem ser muito bem remunerados? Eles são os “magos” que estão ajudando a transformar o mundo com Machine Learning. E este capítulo foi apenas uma breve introdução! Voltaremos a este assunto mais a frente no livro, quando estudarmos outros algoritmos.

Tenho certeza que você está ansioso para criar e treinar sua primeira rede neural. Então, não perca o próximo capítulo!

Referências:

MNIST

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

Neural Networks and Deep Learning

An overview of gradient descent optimization algorithms

Optimization: Stochastic Gradient Descent

Gradient Descent vs Stochastic Gradient Descent vs Mini-Batch Learning

Deep Learning Book

Capítulo 11 – Design De Uma Rede Neural Para Reconhecimento de Dígitos

by

Na primeira parte deste livro online, durante os 10 primeiros capítulos, definimos e estudamos o universo das redes neurais artificias. Neste ponto você já deve ter uma boa compreensão sobre que são estes algoritmos e como podem ser usados, além da importância das redes neurais para a construção de sistemas de Inteligência Artificial. Estamos prontos para iniciar a construção de redes neurais e na sequência estudaremos as arquiteturas mais avançadas. Vamos começar definindo o Design De Uma Rede Neural Para Reconhecimento de Dígitos.

Nossa primeira tarefa será construir uma rede neural para reconhecer caligrafia, ou seja, dígitos escritos à mão que foram digitalizados em imagens no computador. Por que vamos começar com este tipo de tarefa? Porque ela permite percorrer todas as etapas e procedimentos matemáticos de uma rede neural, sendo portanto uma excelente introdução. Vamos começar?

Se você acompanha os cursos na Data Science Academy já sabe que: antes de pensar em escrever sua primeira linha de código, é preciso definir claramente o problema a ser resolvido. A tecnologia existe para resolver problemas e a definição clara do objetivo é o ponto de partida de qualquer projeto de sucesso! Neste capítulo definiremos o problema a ser resolvido, nesse caso o reconhecimento de dígitos manuscritos.

Podemos dividir o problema de reconhecer os dígitos manuscritos em dois sub-problemas. Primeiro, precisamos encontrar uma maneira de quebrar uma imagem que contenha muitos dígitos em uma sequência de imagens separadas, cada uma contendo um único dígito. Por exemplo, gostaríamos de quebrar a imagem:

 

 

em seis imagens separadas:

 

 

Nós, humanos, resolvemos esse problema de segmentação com facilidade, mas é um desafio para um programa de computador dividir corretamente a imagem. Uma vez que a imagem foi segmentada, o programa precisa classificar cada dígito individual. Então, por exemplo, gostaríamos que nosso programa reconhecesse automaticamente que o primeiro dígito acima é um 5:

 

 

Vamos nos concentrar em escrever um programa para resolver o segundo problema, isto é, classificar dígitos individuais. O problema da segmentação não é tão difícil de resolver, uma vez que você tenha uma boa maneira de classificar os dígitos individuais. Existem muitas abordagens para resolver o problema de segmentação. Uma abordagem é testar muitas maneiras diferentes de segmentar a imagem, usando o classificador de dígitos individuais para marcar cada segmentação de teste. Uma segmentação de teste obtém uma pontuação alta se o classificador de dígitos individuais estiver confiante de sua classificação em todos os segmentos e uma pontuação baixa se o classificador tiver muitos problemas em um ou mais segmentos. A ideia é que, se o classificador estiver tendo problemas em algum lugar, provavelmente está tendo problemas porque a segmentação foi escolhida incorretamente. Essa ideia e outras variações podem ser usadas para resolver o problema de segmentação. Então, em vez de se preocupar com a segmentação, nos concentraremos no desenvolvimento de uma rede neural que pode resolver o problema mais interessante e difícil, ou seja, reconhecer dígitos individuais manuscritos.

Para reconhecer dígitos individuais, usaremos uma rede neural de três camadas:

 

 

A camada de entrada da rede contém neurônios que codificam os valores dos pixels de entrada. Conforme iremos discutir no próximo capítulo, nossos dados de treinamento para a rede consistirão em muitas imagens de 28 por 28 pixels de dígitos manuscritos digitalizados e, portanto, a camada de entrada contém 28 × 28 = 784 neurônios (Nota: uma imagem nada mais é do que uma matriz, nesse caso de dimensões 28×28, que iremos converter em um vetor cujo tamanho será 784, onde cada item representa um pixel na imagem). Os pixels de entrada são de escala de cinza, com um valor de 0.0 representando branco e um valor de 1.0 representando preto. Valores intermediários representam tonalidades gradualmente escurecidas de cinza.

A segunda camada da rede é uma camada oculta. Representaremos o número de neurônios nesta camada oculta por n, e vamos experimentar diferentes valores para n. O exemplo mostrado acima ilustra uma pequena camada oculta, contendo apenas n = 15 neurônios.

A camada de saída da rede contém 10 neurônios. Se o primeiro neurônio “disparar” (for ativado), ou seja, tiver uma saída ≈ 1, então isso indicará que a rede acha que o dígito é 0. Se o segundo neurônio “disparar” (for ativado), isso indicará que a rede pensa que o dígito é um 1. E assim por diante. Em resumo, vamos numerar os neurônios de saída de 0 a 9 e descobrimos qual neurônio possui o maior valor de ativação. Se esse neurônio é, digamos, neurônio número 6, então nossa rede adivinhará que o dígito de entrada era um 6. E assim por diante para os outros neurônios de saída.

Você pode se perguntar por que usamos 10 neurônios de saída. Afinal, o objetivo da rede é nos dizer qual dígito (0,1,2, …, 9) corresponde à imagem de entrada. Uma maneira aparentemente natural de fazer isso é usar apenas 4 neurônios de saída, tratando cada neurônio como assumindo um valor binário, dependendo se a saída do neurônio está mais próxima de 0 ou 1. Quatro neurônios são suficientes para codificar a resposta, desde que 2ˆ4 = 16 é mais do que os 10 valores possíveis para o dígito de entrada. Por que nossa rede deve usar 10 neurônios em vez disso? Isso não é ineficiente? A justificativa final é empírica: podemos experimentar ambos os projetos de rede, e verifica-se que, para este problema específico, a rede com 10 neurônios de saída aprende a reconhecer dígitos melhor do que a rede com 4 neurônios de saída. Mas isso ainda deixa a pergunta por que o uso de 10 neurônios de saída funciona melhor. Existe alguma heurística que nos diga com antecedência que devemos usar a codificação de 10 saídas em vez da codificação de 4 saídas?

Entender porque fazemos isso, ajuda a pensar sobre o que a rede neural está realmente fazendo. Considere primeiro o caso em que usamos 10 neurônios de saída. Vamos nos concentrar no primeiro neurônio de saída, aquele que está tentando decidir se o dígito é ou não 0. Ele faz isso pesando evidências da camada oculta dos neurônios. O que esses neurônios ocultos estão fazendo? Bem, vamos supor que o primeiro neurônio na camada oculta detecta ou não uma imagem como a seguinte:

 

 

Isso pode ser feito pesando fortemente pixels de entrada que se sobrepõem à imagem e apenas ponderam ligeiramente as outras entradas. De forma semelhante, suponhamos que o segundo, terceiro e quarto neurônios na camada oculta detectem se as seguintes imagens estão ou não presentes:

 

 

Como você pode ter adivinhado, essas quatro imagens juntas compõem a imagem 0 que vimos na linha de dígitos mostrada anteriormente:

 

 

Então, se todos os quatro neurônios ocultos estão disparando, podemos concluir que o dígito é um 0. Claro, esse não é o único tipo de evidência que podemos usar para concluir que a imagem era um 0 – podemos legitimamente obter um 0 em muitas outras maneiras (por exemplo, através de traduções das imagens acima, ou pequenas distorções). Mas parece seguro dizer que, pelo menos neste caso, concluiríamos que a entrada era um 0.

Supondo que a rede neural funciona assim, podemos dar uma explicação plausível sobre porque é melhor ter 10 saídas da rede, em vez de 4. Se tivéssemos 4 saídas, o primeiro neurônio de saída tentaria decidir o que mais um bit significativo do dígito representa. E não existe uma maneira fácil de relacionar esse bit mais significativo com formas simples, como as mostradas acima. As formas componentes do dígito estarão intimamente relacionadas com (digamos) o bit mais significativo na saída.

Isso tudo é apenas uma heurística. Nada diz que a rede neural de três camadas tem que operar da maneira que descrevemos, com os neurônios ocultos detectando formas de componentes simples. Talvez um algoritmo de aprendizado inteligente encontre alguma atribuição de pesos que nos permita usar apenas 4 neurônios de saída. Mas, usar uma boa heurística pode economizar muito tempo na concepção de boas arquiteturas de redes neurais.

Já temos então um design para a nossa rede neural. Agora precisamos definir como será o processo de aprendizagem do algoritmo, antes de começar a codificar nossa rede em linguagem Python. Usaremos o treinamento com Gradiente Descendente, assunto do próximo capítulo, que aliás eu não perderia por nada, se fosse você, pois aí está a “magia” por trás das redes neurais. Até lá!

Para acompanhar os próximos capítulos e reproduzir os exemplos, você deve ter o Anaconda Python instalado no seu computador. Acesse o capítulo 1 do curso gratuito Fundamentos de Linguagem Python Para Análise de Dados e Data Science, para aprender como instalar o Anaconda.

Referências:

Função Sigmóide

Machine Learning

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

Pattern Recognition and Machine Learning

Understanding Activation Functions in Neural Networks

Redes Neurais, princípios e práticas

Neural Networks and Deep Learning (alguns trechos extraídos e traduzidos com autorização do autor Michael Nielsen)

Deep Learning Book

Capítulo 8 – Função de Ativação

by

Neste capítulo estudaremos um importante componente de uma rede neural artificial, a Função de Ativação. Este capítulo é uma introdução ao tema e voltaremos a ele mais adiante quando estudarmos as arquiteturas avançadas de Deep Learning. Este capítulo pode ser um pouco desafiador, pois começaremos a introduzir conceitos mais avançados, que serão muito úteis na sequência dos capítulos. Relaxe, faça a leitura e aprenda um pouco mais sobre redes neurais artificiais.

Antes de mergulhar nos detalhes das funções de ativação, vamos fazer uma pequena revisão do que são redes neurais artificiais e como funcionam. Uma rede neural é um mecanismo de aprendizado de máquina (Machine Learning) muito poderoso que imita basicamente como um cérebro humano aprende. O cérebro recebe o estímulo do mundo exterior, faz o processamento e gera o resultado. À medida que a tarefa se torna complicada, vários neurônios formam uma rede complexa, transmitindo informações entre si. Usando uma rede neural artificial, tentamos imitar um comportamento semelhante. A rede que você vê abaixo é uma rede neural artificial composta de neurônios interligados.

 

Neural Network

 

Os círculos negros na imagem acima são neurônios. Cada neurônio é caracterizado pelo peso, bias e a função de ativação. Os dados de entrada são alimentados na camada de entrada. Os neurônios fazem uma transformação linear na entrada pelos pesos e bias. A transformação não linear é feita pela função de ativação. A informação se move da camada de entrada para as camadas ocultas. As camadas ocultas fazem o processamento e enviam a saída final para a camada de saída. Este é o movimento direto da informação conhecido como propagação direta. Mas e se o resultado gerado estiver longe do valor esperado? Em uma rede neural, atualizaríamos os pesos e bias dos neurônios com base no erro. Este processo é conhecido como backpropagation. Uma vez que todos os dados passaram por este processo, os pesos e bias finais são usados para previsões.

Calma, calma, calma. Muita informação em um único parágrafo, eu sei! Vamos por partes. As entradas, os pesos e bias nós já discutimos nos capítulos anteriores. A função de ativação vamos discutir agora e a propagação direta e o backpropagation discutimos nos próximos capítulos!

Função de Ativação

Os algoritmos de aprendizagem são fantásticos. Mas como podemos elaborar esses algoritmos para uma rede neural artificial? Suponhamos que tenhamos uma rede de Perceptrons que gostaríamos de usar para aprender a resolver algum problema. Por exemplo, as entradas para a rede poderiam ser os dados de pixel de uma imagem digitalizada, escrita à mão, de um dígito. Gostaríamos que a rede aprendesse pesos e bias para que a saída da rede classifique corretamente o dígito. Para ver como a aprendizagem pode funcionar, suponha que façamos uma pequena alteração em algum peso (ou bias) na rede. O que queremos é que esta pequena mudança de peso cause apenas uma pequena alteração correspondente na saída da rede. Como veremos em um momento, esta propriedade tornará possível a aprendizagem. Esquematicamente, aqui está o que queremos (obviamente, esta rede é muito simples para fazer reconhecimento de escrita, mas fique tranquilo que veremos redes bem mais complexas).

 

Esquema

 

Se fosse verdade que uma pequena alteração em um peso (ou bias) fizesse com que tivéssemos apenas uma pequena alteração no resultado, então poderíamos usar esse fato para modificar os pesos e os valores de bias para que a nossa rede se comporte mais da maneira que queremos. Por exemplo, suponha que a rede classificasse equivocadamente uma imagem como “8” quando deveria ser um “9”. Podemos descobrir como fazer uma pequena mudança nos pesos e bias para que a rede fique um pouco mais próxima da classificação da imagem como “9”. E então, repetiríamos isso, mudando os pesos e os valores de bias repetidamente para produzir melhor e melhor resultado. A rede estaria aprendendo.

O problema é que isso não é o que acontece quando nossa rede contém apenas Perceptrons, conforme estudamos nos capítulos anteriores. De fato, uma pequena alteração nos pesos de um único Perceptron na rede pode, por vezes, fazer com que a saída desse Perceptron mude completamente, digamos de 0 a 1. Essa mudança pode então causar o comportamento do resto da rede mudar completamente de uma maneira muito complicada. Então, enquanto o seu “9” pode agora ser classificado corretamente, o comportamento da rede em todas as outras imagens provavelmente mudará completamente de maneira difícil de controlar. Talvez haja uma maneira inteligente de resolver esse problema. Sim, há. E é conhecida como função de ativação.

Podemos superar esse problema através da introdução de um componente matemático em nosso neurônio artificial, chamado função de ativação. As funções de ativação permitem que pequenas mudanças nos pesos e bias causem apenas uma pequena alteração no output. Esse é o fato crucial que permitirá que uma rede de neurônios artificiais aprenda.

Vejamos como isso funciona:

 

Função de Ativação

 

As funções de ativação são um elemento extremamente importante das redes neurais artificiais. Elas basicamente decidem se um neurônio deve ser ativado ou não. Ou seja, se a informação que o neurônio está recebendo é relevante para a informação fornecida ou deve ser ignorada. Veja na fórmula abaixo como a função de ativação é mais uma camada matemática no processamento.

 

Função de Ativação

 

A função de ativação é a transformação não linear que fazemos ao longo do sinal de entrada. Esta saída transformada é então enviada para a próxima camada de neurônios como entrada. Quando não temos a função de ativação, os pesos e bias simplesmente fazem uma transformação linear. Uma equação linear é simples de resolver, mas é limitada na sua capacidade de resolver problemas complexos. Uma rede neural sem função de ativação é essencialmente apenas um modelo de regressão linear. A função de ativação faz a transformação não-linear nos dados de entrada, tornando-o capaz de aprender e executar tarefas mais complexas. Queremos que nossas redes neurais funcionem em tarefas complicadas, como traduções de idiomas (Processamento de Linguagem Natural) e classificações de imagens (Visão Computacional). As transformações lineares nunca seriam capazes de executar tais tarefas.

As funções de ativação tornam possível a propagação posterior desde que os gradientes sejam fornecidos juntamente com o erro para atualizar os pesos e bias. Sem a função não linear diferenciável, isso não seria possível. Caso o termo gradiente não seja familiar, aguarde os próximos capítulos, quando vamos explicar este conceito em detalhes, visto que ele é a essência do processo de aprendizagem em redes neurais artificiais.

Mas não existe apenas um tipo de função de ativação. Na verdade existem vários, cada qual a ser usado em diferentes situações. Vamos a uma breve descrição dos tipos mais populares.

Tipos Populares de Funções de Ativação

A função de ativação é um componente matemático incluído na estrutura de redes neurais artificiais a fim de permitir a solução de problemas complexos. Existem diversos tipos de funções de ativação e esta é uma área de pesquisa ativa, à medida que a Inteligência Artificial evolui (não é maravilhoso estar participando desta evolução, que vai transformar completamente o mundo?). Vejamos quais são os tipos mais populares.

Função de Etapa Binária (Binary Step Function)

A primeira coisa que vem à nossa mente quando temos uma função de ativação seria um classificador baseado em limiar (threshold), ou seja, se o neurônio deve ou não ser ativado. Se o valor Y estiver acima de um valor de limite determinado, ative o neurônio senão deixa desativado. Simples! Essa seria a regra:

f(x) = 1, x>=0

f(x) = 0, x<0

A função de etapa binária é isso mesmo, extremamente simples. Ela pode ser usada ao criar um classificador binário. Quando simplesmente precisamos dizer sim ou não para uma única classe, a função de etapa seria a melhor escolha, pois ativaria o neurônio ou deixaria zero.

A função é mais teórica do que prática, pois, na maioria dos casos, classificamos os dados em várias classes do que apenas uma única classe. A função de etapa não seria capaz de fazer isso.

Além disso, o gradiente da função de etapa é zero. Isso faz com que a função de etapa não seja tão útil durante o backpropagation quando os gradientes das funções de ativação são enviados para cálculos de erro para melhorar e otimizar os resultados. O gradiente da função de etapa reduz tudo para zero e a melhoria dos modelos realmente não acontece. Lembrando, mais uma vez, que veremos em detalhes os conceitos de gradiente e backpropagation mais adiante, nos próximos capítulos!

Função Linear

Nós vimos o problema com a função step, o gradiente sendo zero, é impossível atualizar o gradiente durante a backpropagation. Em vez de uma função de passo simples, podemos tentar usar uma função linear. Podemos definir a função como:

f(x) = ax

A derivada de uma função linear é constante, isto é, não depende do valor de entrada x. Isso significa que toda vez que fazemos backpropagation, o gradiente seria o mesmo. E este é um grande problema, não estamos realmente melhorando o erro, já que o gradiente é praticamente o mesmo. E não apenas suponha que estamos tentando realizar uma tarefa complicada para a qual precisamos de múltiplas camadas em nossa rede. Agora, se cada camada tiver uma transformação linear, não importa quantas camadas nós tenhamos, a saída final não é senão uma transformação linear da entrada. Portanto, a função linear pode ser ideal para tarefas simples, onde a interpretabilidade é altamente desejada.

Sigmóide

Sigmóide é uma função de ativação amplamente utilizada. É da forma:

f (x) = 1 / (1 + e ^ -x)

Esta é uma função suave e é continuamente diferenciável. A maior vantagem sobre a função de etapa e a função linear é que não é linear. Esta é uma característica incrivelmente interessante da função sigmóide. Isto significa essencialmente que quando eu tenho vários neurônios com função sigmóide como função de ativação – a saída também não é linear. A função varia de 0 a 1 tendo um formato S.

A função essencialmente tenta empurrar os valores de Y para os extremos. Esta é uma qualidade muito desejável quando tentamos classificar os valores para uma classe específica.

A função sigmóide ainda é amplamente utilizada até hoje, mas ainda temos problemas que precisamos abordar. Com a sigmóide temos problemas quando os gradientes se tornam muito pequenos. Isso significa que o gradiente está se aproximando de zero e a rede não está realmente aprendendo.

Outro problema que a função sigmóide sofre é que os valores variam apenas de 0 a 1. Esta medida que a função sigmóide não é simétrica em torno da origem e os valores recebidos são todos positivos. Nem sempre desejamos que os valores enviados ao próximo neurônio sejam todos do mesmo sinal. Isso pode ser abordado pela ampliação da função sigmóide. Isso é exatamente o que acontece na função tanh.

Tanh

A função tanh é muito semelhante à função sigmóide. Na verdade, é apenas uma versão escalonada da função sigmóide.

Tanh (x) = 2sigmoides (2x) -1

Pode ser escrito diretamente como:

tanh (x) = 2 / (1 + e ^ (- 2x)) -1

Tanh funciona de forma semelhante à função sigmóide, mas sim simétrico em relação à origem. varia de -1 a 1.

Basicamente, soluciona o nosso problema dos valores, sendo todos do mesmo sinal. Todas as outras propriedades são as mesmas da função sigmoide. É contínuo e diferenciável em todos os pontos. A função não é linear, então podemos fazer o backpropagation facilmente nos erros.

ReLU

A função ReLU é a unidade linear rectificada. É definida como:

f(x) = max (0, x)

ReLU é a função de ativação mais amplamente utilizada ao projetar redes neurais atualmente. Primeiramente, a função ReLU é não linear, o que significa que podemos facilmente copiar os erros para trás e ter várias camadas de neurônios ativados pela função ReLU.

A principal vantagem de usar a função ReLU sobre outras funções de ativação é que ela não ativa todos os neurônios ao mesmo tempo. O que isto significa ? Se você olhar para a função ReLU e a entrada for negativa, ela será convertida em zero e o neurônio não será ativado. Isso significa que, ao mesmo tempo, apenas alguns neurônios são ativados, tornando a rede esparsa e eficiente e fácil para a computação.

Mas ReLU também pode ter problemas com os gradientes que se deslocam em direção a zero. Mas quando temos um problema, sempre podemos pensar em uma solução. Aliás, isso é o que as empresas mais procuram nos dias de hoje: “resolvedores de problemas”. Seja um e sua empregabilidade estará garantida!

Leaky ReLU

A função Leaky ReLU não passa de uma versão melhorada da função ReLU. Na função ReLU, o gradiente é 0 para x < 0, o que fez os neurônios morrerem por ativações nessa região. Leaky ReLU ajuda a resolver este problema. Em vez de definir a função Relu como 0 para x inferior a 0, definimos como um pequeno componente linear de x. Pode ser definido como:

f(x) = ax, x < 0
f(x) = x, x > = 0

O que fizemos aqui é que simplesmente substituímos a linha horizontal por uma linha não-zero, não horizontal. Aqui um é um valor pequeno como 0,01 ou algo parecido. A principal vantagem de substituir a linha horizontal é remover o gradiente zero.

Softmax

A função softmax também é um tipo de função sigmóide, mas é útil quando tentamos lidar com problemas de classificação. A função sigmóide como vimos anteriormente é capaz de lidar com apenas duas classes. O que devemos fazer quando estamos tentando lidar com várias classes? Apenas classificar sim ou não para uma única classe não ajudaria. A função softmax transforma as saídas para cada classe para valores entre 0 e 1 e também divide pela soma das saídas. Isso essencialmente dá a probabilidade de a entrada estar em uma determinada classe. Pode ser definido como:

 

Softmax

 

Digamos, por exemplo, que temos as saídas como [1.2, 0.9, 0.75], quando aplicamos a função softmax, obteríamos [0.42, 0.31, 0.27]. Então, agora podemos usá-los como probabilidades de que o valor seja de cada classe.

A função softmax é idealmente usada na camada de saída do classificador, onde realmente estamos tentando gerar as probabilidades para definir a classe de cada entrada.

Escolhendo a Função de Ativação Correta

Ufa! Muita coisa, não? E ainda não vimos as questões matemáticas envolvidas nessas funções. Mas não tenhamos pressa, não existe atalho para o aprendizado e estudaremos tudo passo a passo, item a item, no padrão dos cursos na Data Science Academy.

Agora que já vimos tantas funções de ativação, precisamos de alguma lógica/heurística para saber qual função de ativação deve ser usada em qual situação. Não há uma regra de ouro e a escolha depende do problema no qual você estiver trabalhando.

No entanto, dependendo das propriedades do problema, poderemos fazer uma melhor escolha para uma convergência fácil e rápida da rede neural.

  • Funções Sigmóide e suas combinações geralmente funcionam melhor no caso de classificadores.
  • Funções Sigmóide e Tanh às vezes são evitadas devido ao problema de Vanishing Gradient (que estudaremos no capítulo sobre redes neurais recorrentes).
  • A função ReLU é uma função de ativação geral e é usada na maioria dos casos atualmente.
  • Se encontrarmos um caso de neurônios deficientes em nossas redes, a função Leaky ReLU é a melhor escolha.
  • Tenha sempre em mente que a função ReLU deve ser usada apenas nas camadas ocultas.
  • Como regra geral, você pode começar usando a função ReLU e depois passar para outras funções de ativação no caso da ReLU não fornecer resultados ótimos.

Está começando a sentir a vibração em trabalhar com Inteligência Artificial? Então continue acompanhando, pois estamos apenas no começo! Até o próximo capítulo!

Referências:

Função Sigmóide

Machine Learning

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

Pattern Recognition and Machine Learning

Understanding Activation Functions in Neural Networks

Vanishing Gradient Problem

Redes Neurais, princípios e práticas

Neural Networks and Deep Learning (alguns trechos extraídos e traduzidos com autorização do autor Michael Nielsen)

Deep Learning Book

Capítulo 7 – O Perceptron – Parte 2

by

O Perceptron é um modelo matemático de um neurônio biológico. Enquanto nos neurônios reais o dendrito recebe sinais elétricos dos axônios de outros neurônios, no Perceptron estes sinais elétricos são representados como valores numéricos. Nas sinapses entre dendritos e axônio, os sinais elétricos são modulados em várias quantidades. Isso também é modelado no Perceptron multiplicando cada valor de entrada por um valor chamado peso. Um neurônio real dispara um sinal de saída somente quando a força total dos sinais de entrada excede um certo limiar. Nós modelamos esse fenômeno em um Perceptron calculando a soma ponderada das entradas para representar a força total dos sinais de entrada e aplicando uma função de ativação na soma para determinar sua saída. Tal como nas redes neurais biológicas, esta saída é alimentada em outros Perceptrons. Estudamos tudo isso no capítulo anterior. Agora vamos continuar nossa discussão sobre o Perceptron compreendendo mais alguns conceitos, que serão fundamentais mais a frente quando estudarmos as arquiteturas avançadas de Deep Learning.

Antes de iniciar, vamos definir dois conceitos que você vai ver com frequência daqui em diante, vetor de entrada e vetor de pesos:

Vetor de entrada –  todos os valores de entrada de cada Perceptron são coletivamente chamados de vetor de entrada desse Perceptron. Esses são seus dados de entrada.

Vetor de pesos – de forma semelhante, todos os valores de peso de cada Perceptron são coletivamente chamados de vetor de peso desse Perceptron. Iniciamos nossa rede neural artificial com valores aleatórios de pesos e durante o treinamento a rede neural aprende os valores de peso ideais. Como veremos, existem muitas formas de realizar esse processo.

Boa parte do trabalho de uma rede neural vai girar em torno das operações algébricas entre o vetor de entrada e o vetor de pesos. Em seguida, vamos adicionando outras camadas matemáticas ou estatísticas para realizar diferentes operações, de acordo com o problema que estamos tentando resolver com o modelo de rede neural. Você vai perceber que tudo não passa de Matemática, que pode ser implementada com linguagens de programação, grandes conjuntos de dados e processamento paralelo, para formar sistemas de Inteligência Artificial.

Mas o que um Perceptron pode fazer afinal?

No capítulo anterior descrevemos os Perceptrons como um método para pesar evidências a fim de tomar decisões. Outra forma em que os Perceptrons podem ser usados é para calcular as funções lógicas elementares tais como AND, OR e NAND (caso tenha dúvidas sobre as operações lógicas, consulte as referências ao final deste capítulo). Por exemplo, suponha que tenhamos um Perceptron com duas entradas, cada uma com peso -2 e um viés de 3. Aqui está o nosso Perceptron:

 

Perceptron

 

Então vemos que a entrada 00 produziria a saída 1, uma vez que (-2) * 0 + (- 2) * 0 + 3 = 3, é positivo (resultado positivo, gera saída 1 do Perceptron, lembra do capítulo anterior?). Aqui, incluímos o símbolo * para tornar as multiplicações explícitas. Cálculos similares mostram que as entradas 01 e 10 produzem a saída 1. Mas a entrada 11 produz a saída 0, uma vez que (-2) * 1 + (- 2) * 1 + 3 = -1, é negativo. E assim nosso Perceptron implementa um “portão” NAND, ou uma operação lógica binária NAND.

O exemplo NAND mostra que podemos usar Perceptrons para calcular funções lógicas simples. Na verdade, podemos usar redes de Perceptrons para calcular qualquer função lógica. A razão é que o portão NAND é universal para computação, ou seja, podemos construir qualquer computação com portões NAND.

Uma rede de Perceptrons pode ser usada para simular um circuito contendo muitos portões NAND. E como os portões NAND são universais para a computação, segue-se que os Perceptrons também são universais para a computação. Considerando que o Perceptron é o modelo mais simples de rede neural, imagine o que pode ser feito com modelos bem mais avançados! Acertou se você pensou em Inteligência Artificial.

A universalidade computacional dos Perceptrons é simultaneamente reconfortante e decepcionante. É reconfortante porque nos diz que redes de Perceptrons podem ser tão poderosas como qualquer outro dispositivo de computação. Mas também é decepcionante, porque parece que os Perceptrons são meramente um novo tipo de portão NAND. Isso não é uma grande noticia!

No entanto, a situação é melhor do que esta visão sugere. Acontece que podemos conceber algoritmos de aprendizado que podem ajustar automaticamente os pesos e os vieses de uma rede de neurônios artificiais. Este ajuste ocorre em resposta a estímulos externos, sem intervenção direta de um programador. Esses algoritmos de aprendizagem nos permitem usar neurônios artificiais de uma maneira que é radicalmente diferente dos portões lógicos convencionais. Em vez de colocar explicitamente um circuito de NAND e outros portões, nossas redes neurais podem simplesmente aprender a resolver problemas, às vezes problemas em que seriam extremamente difíceis de projetar diretamente usando um circuito convencional de lógica.

Operações Lógicas e Regiões Linearmente Separáveis

Conforme mencionado acima, um Perceptron calcula a soma ponderada dos valores de entrada. Por simplicidade, suponhamos que existem dois valores de entrada, x e y para um certo Perceptron P. Vamos definir os pesos de x e y, como sendo A e B, respectivamente. A soma ponderada pode ser representada como: A x + B y.

Uma vez que o Perceptron produz um valor não-zero somente quando a soma ponderada excede um certo limite C, pode-se escrever a saída deste Perceptron da seguinte maneira:

Regra Perceptron

Considerando que A x + B y > C e A x + B y < C são as duas regiões no plano xy separadas pela linha A x + B y + C = 0, e se considerarmos ainda a entrada (x, y) como um ponto em um plano, então o Perceptron realmente nos diz qual região no plano a que esse ponto pertence. Tais regiões, uma vez que são separadas por uma única linha, são chamadas de regiões linearmente separáveis.

Um único Perceptron consegue resolver somente funções linearmente separáveis. Em funções não linearmente separáveis, o Perceptron não consegue gerar um hiperplano, esta linha nos gráficos abaixo, para separar os dados. A questão é que no mundo real raramente os dados são linearmente separáveis, fazendo com o que o Perceptron não seja muito útil para atividades práticas (mas sendo ideal para iniciar o estudo em redes neurais artificiais). E como separamos os dados não linearmente separáveis? Continue acompanhando este livro e você irá descobrir.

Linear e Não-Linear

Mas ainda assim o Perceptron tem sua utilidade, porque resulta em algumas funções lógicas, como os operadores booleanos AND, OR e NOT, que são linearmente separáveis, isto é, eles podem ser realizadas usando um único Perceptron. Podemos ilustrar porque eles são linearmente separáveis ao traçar cada um deles em um gráfico:

 

Funções Lógicas

 

Nos gráficos acima, os dois eixos são as entradas que podem levar o valor de 0 ou 1 e os números no gráfico são a saída esperada para uma entrada específica. Usando um vetor de peso apropriado para cada caso, um único Perceptron pode executar todas essas funções.

No entanto, nem todos os operadores de lógica são linearmente separáveis. Por exemplo, o operador XOR não é linearmente separável e não pode ser alcançado por um único Perceptron. No entanto, esse problema poderia ser superado usando mais de um Perceptron organizado em redes neurais feed-forward, que veremos mais a frente nos próximos capítulos.

 

xor

 

Uma vez que é impossível desenhar uma linha para dividir as regiões contendo 1 ou 0, a função XOR não é linearmente separável, conforme pode ser visto no gráfico acima.

Agora fica mais fácil compreender porque precisamos de arquiteturas mais avançadas de redes neurais artificiais, uma vez que temos problemas complexos no mundo real, como Visão Computacional, Processamento de Linguagem Natural, Tradução, Detecção de Fraudes, Classificação e muitos outros. E veremos essas arquiteturas em detalhes. Mas antes, precisamos falar sobre um componente fundamental das redes neurais, a Função de Ativação. Não perca o próximo capítulo. Até lá.

Referências:

Operação Lógica AND

Operação Lógica OR

Operação Lógica NAND

Operação Lógica XOR

Machine Learning

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

Pattern Recognition and Machine Learning

Redes Neurais, princípios e práticas

Neural Networks and Deep Learning (alguns trechos extraídos e traduzidos com autorização do autor Michael Nielsen)

Deep Learning Book

Capítulo 6 – O Perceptron – Parte 1

by

Você sabe quais são as principais arquiteturas de redes neurais artificias? Não. Então analise cuidadosamente a imagem abaixo (excelente trabalho criado pela equipe do Asimov Institute, cujo link você encontra na seção de referências ao final deste capítulo):

Deep Learning Zoo

Incrível, não? São diversas arquiteturas, usadas para resolver diferentes tipos de problemas, como por exemplo as arquiteturas de redes neurais convolucionais usadas em problemas de Visão Computacional e as redes neurais recorrentes usadas em problemas de Processamento de Linguagem Natural. Estudaremos quase todas essas arquiteturas aqui neste livro. Sim, isso mesmo que você leu. Estamos apenas começando!! Caso queira aprender a construir modelos e projetos usando essas arquiteturas e trabalhando com linguagem Python, PyTorch e TensorFlow, clique aqui.

Embora todas essas arquiteturas sejam de redes neurais artificias, nem todas são de Deep Learning. O que caracteriza modelos de aprendizagem profunda, como o nome sugere, são redes neurais artificias com muitas camadas ocultas (ou intermediárias). Mas antes de chegarmos lá, precisamos passar pela arquitetura mais simples de uma rede neural artificial, o Perceptron. Como diz o ditado: “Toda grande caminhada começa pelo primeiro passo”.

O Modelo Perceptron foi desenvolvido nas décadas de 1950 e 1960 pelo cientista Frank Rosenblatt, inspirado em trabalhos anteriores de Warren McCulloch e Walter Pitts. Hoje, é mais comum usar outros modelos de neurônios artificiais, mas o Perceptron permite uma compreensão clara de como funciona uma rede neural em termos matemáticos, sendo uma excelente introdução.

Então, como funcionam os Perceptrons? Um Perceptron é um modelo matemático que recebe várias entradas, x1, x2, … e produz uma única saída binária:

Perceptron

No exemplo mostrado, o Perceptron possui três entradas: x1, x2, x3. Rosenblatt propôs uma regra simples para calcular a saída. Ele introduziu pesos, w1, w2, …, números reais expressando a importância das respectivas entradas para a saída. A saída do neurônio, 0 ou 1, é determinada pela soma ponderada, Σjwjxj, menor ou maior do que algum valor limiar (threshold). Assim como os pesos, o threshold é um número real que é um parâmetro do neurônio. Para colocá-lo em termos algébricos mais precisos:

Output

Esse é o modelo matemático básico. Uma maneira de pensar sobre o Perceptron é que é um dispositivo que toma decisões ao comprovar evidências. Deixe-me dar um exemplo. Não é um exemplo muito realista, mas é fácil de entender, e logo chegaremos a exemplos mais realistas. Suponha que o fim de semana esteja chegando e você ouviu falar que haverá um festival de queijo em sua cidade. Você gosta de queijo e está tentando decidir se deve ou não ir ao festival. Você pode tomar sua decisão pesando três fatores:

  • O tempo está bom?
  • Seu namorado ou namorada quer acompanhá-lo(a)?
  • O festival está perto de transporte público? (Você não possui um carro)

Podemos representar estes três fatores pelas variáveis binárias correspondentes x1, x2 e x3. Por exemplo, teríamos x1 = 1 se o tempo estiver bom e x1 = 0 se o tempo estiver ruim. Da mesma forma, x2 = 1 se seu namorado ou namorada quiser ir ao festival com você, e x2 = 0, se não. E similarmente para x3 e transporte público.

Agora, suponha que você adore queijo e está disposto a ir ao festival, mesmo que seu namorado ou namorada não esteja interessado e o festival fica em um lugar de difícil acesso e sem transporte público amplamente disponível. Além disso, você realmente detesta mau tempo, e não há como ir ao festival se o tempo estiver ruim. Você pode usar Perceptrons para modelar esse tipo de tomada de decisão.

Uma maneira de fazer isso é escolher um peso w1 = 6 para o tempo e w2 = 2 e w3 = 2 para as outras condições. O valor maior de w1 indica que o tempo é muito importante para você, muito mais do que se seu namorado ou namorada vai acompanhá-lo(a) ou se o festival é próximo do transporte público. Finalmente, suponha que você escolha um threshold de 5 para o Perceptron. Com essas escolhas, o Perceptron implementa o modelo de tomada de decisão desejado, produzindo 1 sempre que o tempo estiver bom e 0 sempre que o tempo estiver ruim. Não faz diferença para o resultado se seu namorado ou namorada quer ir, ou se o transporte público está acessível.

Variando os pesos e o limiar, podemos obter diferentes modelos de tomada de decisão. Por exemplo, suponha que escolhemos um threshold de 3. Então, o Perceptron decidirá que você deveria ir ao festival sempre que o tempo estiver bom ou quando o festival estiver perto do transporte público e seu namorado ou namorada estiver disposto a se juntar a você. Em outras palavras, seria um modelo diferente de tomada de decisão. Reduzir o threshold significa que você está mais propenso a ir ao festival.

Obviamente, o Perceptron não é um modelo completo de tomada de decisão humana! Mas o que o exemplo ilustra é como um Perceptron pode pesar diferentes tipos de evidências para tomar decisões. E deve parecer plausível que uma rede complexa de Perceptrons possa tomar decisões bastante sutis.

Rede

Nesta rede, a primeira coluna de Perceptrons – o que chamaremos de primeira camada de Perceptrons – está tomando três decisões muito simples, pesando a evidência de entrada. E quanto aos Perceptrons na segunda camada? Cada um desses Perceptrons está tomando uma decisão ponderando os resultados da primeira camada de tomada de decisão. Desta forma, um Perceptron na segunda camada pode tomar uma decisão em um nível mais complexo e mais abstrato do que os Perceptrons na primeira camada. E as decisões ainda mais complexas podem ser feitas pelos Perceptrons na terceira camada. Desta forma, uma rede de Perceptrons de várias camadas pode envolver-se em uma tomada de decisão sofisticada.

Aliás, quando definimos os Perceptrons, dissemos que um Perceptron possui apenas uma saída. Na rede acima, os Perceptrons parecem ter múltiplos resultados. Na verdade, eles ainda são de saída única. As setas de saída múltiplas são meramente uma maneira útil de indicar que a saída de um Perceptron está sendo usada como entrada para vários outros Perceptrons.

Vamos simplificar a maneira como descrevemos os Perceptrons. No limite de condição Σjwjxj > threshold podemos fazer duas mudanças de notação para simplificá-lo. A primeira mudança é escrever Σjwjxj como um produto (dot product), w⋅x≡Σjwjxj, onde w e x são vetores cujos componentes são os pesos e entradas, respectivamente. A segunda mudança é mover o threshold para o outro lado da equação e substituí-lo pelo que é conhecido como o viés (bias) do Perceptron, ou b ≡ -threshold. Usando o viés em vez do threshold, a regra Perceptron pode ser reescrita:

Fórmula Perceptron

Você pode pensar no viés como uma medida de quão fácil é obter o Perceptron para produzir um 1. Ou para colocá-lo em termos mais biológicos, o viés é uma medida de quão fácil é fazer com que o Perceptron dispare. Para um Perceptron com um viés realmente grande, é extremamente fácil para o Perceptron emitir um 1. Mas se o viés é muito negativo, então é difícil para o Perceptron emitir um 1. Obviamente, a introdução do viés é apenas uma pequena mudança em como descrevemos Perceptrons, mas veremos mais adiante que isso leva a outras simplificações de notação. Por isso, no restante do livro, não usaremos o threshold, usaremos sempre o viés.

Agora começa a ficar mais fácil compreender o conceito por trás das redes neurais artificiais e isso será muito útil quando estudarmos arquiteturas mais avançadas! Um Perceptron segue o modelo “feed-forward”, o que significa que as entradas são enviadas para o neurônio, processadas e resultam em uma saída. No diagrama abaixo, isso significa que a rede (um neurônio) lê da esquerda para a direita.

Neurônio

O processo de treinamento de um modelo Perceptron consiste em fazer com que o modelo aprenda os valores ideais de pesos e bias. Apresentamos ao modelo os dados de entrada e as possíveis saídas, treinamos o modelo e pesos e bias são aprendidos. Com o modelo treinado, podemos apresentar novos dados de entrada e o modelo será capaz de prever a saída. Veremos isso em breve quando criarmos nosso primeiro modelo usando linguagem Python.

Perceptron é uma rede neural de camada única e um Perceptron de várias camadas é chamado de Rede Neural Artificial. O Perceptron é um classificador linear (binário). Além disso, é usado na aprendizagem supervisionada e pode ser usado para classificar os dados de entrada fornecidos.

Mas o Perceptron tem ainda outras características importantes, como a representação de condicionais lógicos (and, or, xor), problemas com dados não linearmente separáveis e as funções de ativação. Mas esses são temas para o próximo capítulo. Até lá!

Referências:

Formação Engenheiro de IA

The Neural Network Zoo

Machine Learning

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

Pattern Recognition and Machine Learning

Redes Neurais, princípios e práticas

Neural Networks and Deep Learning (alguns trechos extraídos e traduzidos com autorização do autor Michael Nielsen)

Deep Learning Book

Capítulo 4 – O Neurônio, Biológico e Matemático

by

Para compreender a lógica de funcionamento das redes neurais, alguns conceitos básicos referentes ao funcionamento do cérebro humano e seus componentes, os neurônios, são de fundamental importância. A formação das conexões entre as células e algumas considerações sobre como se concebe teoricamente o funcionamento matemático, ajudam a entender as bases da aprendizagem de máquina e das redes neurais. Vejamos como funciona o neurônio biológico deixando Machine Learning de lado por um instante!

O Neurônio Biológico

O neurônio é a unidade básica do cérebro humano, sendo uma célula especializada na transmissão de informações, pois nelas estão introduzidas propriedades de excitabilidade e condução de mensagens nervosas. O neurônio é constituído por 3 partes principais: a soma ou corpo celular, do qual emanam algumas ramificações denominadas de dendritos, e por uma outra ramificação descendente da soma, porém mais extensa, chamada de axônio. Nas extremidades dos axônios estão os nervos terminais, pelos quais é realizada a transmissão das informações para outros neurônios. Esta transmissão é conhecida como sinapse.

 

Neurônio Biológico

Fig7 – Representação Simplificada do Neurônio Biológico

Nosso cérebro é formado por bilhões de neurônios. Mas eles não estão isolados. Pelo contrário, existem centenas de bilhões de conexões entre eles, formando uma enorme rede de comunicação, a rede neural. Cada neurônio possui um corpo central, diversos dendritos e um axônio. Os dendritos recebem sinais elétricos de outros neurônios através das sinapses, que constitui o processo de comunicação entre neurônios. O corpo celular processa a informação e envia para outro neurônio.

Observe que a soma e os dendritos formam a superfície de entrada do neurônio e o axônio a superfície de saída do fluxo de informação (esse fluxo de informação é importante para compreender o neurônio matemático daqui a pouco). A informação transmitida pelos neurônios na realidade são impulsos elétricos. O impulso elétrico é a mensagem que os neurônios transmitem uns aos outros, ou seja, é a propagação de um estímulo ao longo dos neurônios que pode ser qualquer sinal captado pelos receptores nervosos.

Os dendritos têm como função, receber informações, ou impulsos nervosos, oriundos de outros neurônios e conduzi-los até o corpo celular. Ali, a informação é processada e novos impulsos são gerados. Estes impulsos são transmitidos a outros neurônios, passando pelo axônio e atingindo os dendritos dos neurônios seguintes. O corpo do neurônio é responsável por coletar e combinar informações vindas de outros neurônios.

O ponto de contato entre a terminação axônica de um neurônio e o dendrito de outro é chamado sinapse. É pelas sinapses que os neurônios se unem funcionalmente, formando as redes neurais. As sinapses funcionam como válvulas, sendo capazes de controlar a transmissão de impulsos, isto é, o fluxo da informação entre os neurônios na rede neural. O efeito das sinapses é variável e é esta variação que dá ao neurônio capacidade de adaptação.

Sinais elétricos gerados nos sensores (retina ocular, papilas gustativas, etc…) caminham pelos axônios. Se esses sinais forem superiores a um limiar de disparo (threshold), seguem pelo axônio. Caso contrário, são bloqueados e não prosseguem (são considerados irrelevantes). A passagem desses sinais não é elétrica, mas química (através da substância serotonina). Se o sinal for superior a certo limite (threshold), vai em frente; caso contrário é bloqueado e não segue. Estamos falando aqui do neurônio biológico e preste bastante atenção a palavra threshold, pois ela é a essência do neurônio matemático.

Um neurônio recebe sinais através de inúmeros dendritos, os quais são ponderados e enviados para o axônio, podendo ou não seguir adiante (threshold). Na passagem por um neurônio, um sinal pode ser amplificado ou atenuado, dependendo do dendrito de origem, pois a cada condutor, está associado um peso pelo qual o sinal é multiplicado. Os pesos são o que chamamos de memória.

Cada região do cérebro é especializada em uma dada função, como processamento de sinais auditivos, sonoros, elaboração de pensamentos, desejos, etc… Esse processamento se dá através de redes particulares interligadas entre si, realizando processamento paralelo. Cada região do cérebro possui uma arquitetura de rede diferente: varia o número de neurônios, de sinapses por neurônio, valor dos thresholds e dos pesos, etc…Os valores dos pesos são estabelecidos por meio de treinamento recebido pelo cérebro durante a vida útil. É a memorização.

Inspirados no neurônio biológico, os pesquisadores desenvolveram um modelo de neurônio matemático que se tornou a base da Inteligência Artificial. A ideia era simples: “Se redes neurais formam a inteligência humana, vamos reproduzir isso e criar Inteligência Artificial”. E assim nasceu o neurônio matemático, o qual descrevemos abaixo.

O Neurônio Matemático

A partir da estrutura e funcionamento do neurônio biológico, pesquisadores tentaram simular este sistema em computador. O modelo mais bem aceito foi proposto por Warren McCulloch e Walter Pitts em 1943, o qual implementa de maneira simplificada os componentes e o funcionamento de um neurônio biológico. Em termos simples, um neurônio matemático de uma rede neural artificial é um componente que calcula a soma ponderada de vários inputs, aplica uma função e passa o resultado adiante.

Neste modelo de neurônio matemático, os impulsos elétricos provenientes de outros neurônios são representados pelos chamados sinais de entrada (a letra x nesse diagrama abaixo, que nada mais são do que os dados que alimentam seu modelo de rede neural artificial). Dentre os vários estímulos recebidos, alguns excitarão mais e outros menos o neurônio receptor e essa medida de quão excitatório é o estímulo é representada no modelo de Warren McCulloch e Walter Pitts através dos pesos sinápticos. Quanto maior o valor do peso, mais excitatório é o estímulo. Os pesos sinápticos são representados por wkn neste diagrama abaixo, onde k representa o índice do neurônio em questão e n se refere ao terminal de entrada da sinapse a qual o peso sináptico se refere.

A soma ou corpo da célula é representada por uma composição de dois módulos, o primeiro é uma junção aditiva, somatório dos estímulos (sinais de entrada) multiplicado pelo seu fator excitatório (pesos sinápticos), e posteriormente uma função de ativação, que definirá com base nas entradas e pesos sinápticos, qual será a saída do neurônio. O axônio é aqui representado pela saída (yk) obtida pela aplicação da função de ativação. Assim como no modelo biológico, o estímulo pode ser excitatório ou inibitório, representado pelo peso sináptico positivo ou negativo respectivamente.

Neurônio Matemático

Fig8 – Representação Simplificada do Neurônio Matemático

O modelo proposto possui uma natureza binária. Tanto os sinais de entrada quanto a saída, são valores binários. McCulloch acreditava que o funcionamento do sistema nervoso central possuía um carater binário, ou seja, um neurônio infuencia ou não outro neurônio, mas posteriormente mostrou-se que não era dessa forma.

O neurônio matemático é um modelo simplificado do neurônio biológico. Tais modelos inspirados a partir da análise da geração e propagação de impulsos elétricos pela membrana celular dos neurônios. O neurônio matemático recebe um ou mais sinais de entrada e devolve um único sinal de saída, que pode ser distribuído como sinal de saída da rede, ou como sinal de entrada para um ou vários outros neurônios da camada posterior (que formam a rede neural artificial). Os dendritos e axônios são representados matematicamente apenas pelas sinapses, e a intensidade da ligação é representada por uma grandeza denominada peso sináptico, simbolizada pela letra w. Quando as entradas, x são apresentadas ao neurônio, elas são multiplicadas pelos pesos sinápticos correspondentes, gerando as entradas ponderadas, ou seja, x1 que multiplica w1, etc… Isso descreve uma das bases matemáticas do funcionamento de uma rede neural artificial, a multiplicação de matrizes:

Matriz

Fig9 – Multiplicação de Matrizes Entre Sinais de Entrada x e Pesos Sinápticos w (versão simplificada)

O neurônio então totaliza todos os produtos gerando um único resultado. A esta função se denomina função de combinação. Este valor é então apresentado a uma função de ativação ou função de transferência, que tem, dentre outras, a finalidade de evitar o acréscimo progressivo dos valores de saída ao longo das camadas da rede, visto que tais funções possuem valores máximos e mínimos contidos em intervalos determinados. O uso de funções de transferência não-lineares torna a rede neural uma ferramenta poderosa. Sabe-se que uma rede perceptron de duas camadas com função de transferência não-linear como a função sigmóide (que veremos mais adiante), é denominada de aproximador universal.

Um neurônio dispara quando a soma dos impulsos que ele recebe ultrapassa o seu limiar de excitação chamado de threshold. O corpo do neurônio, por sua vez, é emulado por um mecanismo simples que faz a soma dos valores xi e wi recebidos pelo neurônio (soma ponderada) e decide se o neurônio deve ou não disparar (saída igual a 1 ou a 0) comparando a soma obtida ao limiar ou threshold do neurônio. A ativação do neurônio é obtida através da aplicação de uma “função de ativação”, que ativa a saída ou não, dependendo do valor da soma ponderada das suas entradas.

Note que este modelo matemático simplificado de um neurônio é estático, ou seja, não considera a dinâmica do neurônio natural. No neurônio biológico, os sinais são enviados em pulsos e alguns componentes dos neurônios biológicos, a exemplo do axônio, funcionam como filtros de frequência.

O modelo do neurônio matemático também pode incluir uma polarização ou bias de entrada. Esta variável é incluída ao somatório da função de ativação, com o intuito de aumentar o grau de liberdade desta função e, consequentemente, a capacidade de aproximação da rede. O valor do bias é ajustado da mesma forma que os pesos sinápticos. O bias possibilita que um neurônio apresente saída não nula ainda que todas as suas entradas sejam nulas. Por exemplo, caso não houvesse o bias e todas as entradas de um neurônio fossem nulas, então o valor da função de ativação seria nulo. Desta forma não poderíamos, por exemplo, fazer com o que o neurônio aprendesse a relação pertinente ao ”ou exclusivo” da lógica. Em resumo, temos esses componentes em um neurônio matemático:

Resumo do Neurônio

Fig10 – Representação do Neurônio Matemático

  • Sinais de entrada { X1, X2, …, Xn }: São os sinais externos normalmente normalizados para incrementar a eficiência computacional dos algoritmos de aprendizagem. São os dados que alimentam seu modelo preditivo.
  • Pesos sinápticos { W1, W2, …, Wn }: São valores para ponderar os sinais de cada entrada da rede. Esses valores são aprendidos durante o treinamento.
  • Combinador linear { Σ }: Agregar todos sinais de entrada que foram ponderados pelos respectivos pesos sinápticos a fim de produzir um potencial de ativação.
  • Limiar de ativação { Θ }: Especifica qual será o patamar apropriado para que o resultado produzido pelo combinador linear possa gerar um valor de disparo de ativação.
  • Potencial de ativação { u }: É o resultado obtido pela diferença do valor produzido entre o combinador linear e o limiar de ativação. Se o valor for positivo, ou seja, se u ≥ 0 então o neurônio produz um potencial excitatório; caso contrário, o potencial será inibitório.
  • Função de ativação { g }: Seu objetivo é limitar a saída de um neurônio em um intervalo valores.
  • Sinal de saída { y}: É o valor final de saída podendo ser usado como entrada de outros neurônios que estão sequencialmente interligados.

Os modelos baseados em redes neurais artificiais são os que mais ganharam atenção nos últimos anos por conseguirem resolver problemas de IA nos quais se conseguia pouco avanço com outras técnicas. A partir da concepção do neurônio matemático, várias arquiteturas e modelos com diferentes combinações entre esses neurônios, e aplicando diferentes técnicas matemáticas e estatísticas, surgiram e propiciaram a criação de arquiteturas avançadas de Deep Learning como Redes Neurais Convolucionais, Redes Neurais Recorrentes, Auto Encoders, Generative Adversarial Networks, Memory Networks, entre outras, que estudaremos ao longo deste livro online.

Referências:

Anatomia de um Neurônio

Bibliografia Machine Learning e IA

Deep Learning in Neural Networks: An Overview

Grokking Deep Learning

HAYKIN, S. Redes Neurais, princípios e práticas. Porto Alegre: Bookman, 2001.

JAIN, A. K, MAO, J., MOHIUDDIN, K.M. Artificial neural networks: a tutorial. IEEE Computer, v. 29, n. 3, p. 56-63, 1996.

 

Deep Learning Book

Capítulo 1 – Deep Learning e a Tempestade Perfeita

by

O interesse pela Aprendizagem de Máquina (Machine Learning) explodiu na última década. O mundo a nossa volta está passando por uma transformação e vemos uma interação cada vez maior das aplicações de computador com os seres humanos. Softwares de detecção de spam, sistemas de recomendação, marcação em fotos de redes sociais, assistentes pessoais ativados por voz, carros autônomos, smartphones com reconhecimento facial e muito mais.

E o interesse por Machine Learning se mostra ainda mais evidente pelo número cada vez maior de conferências, meetups, artigos, livros, cursos, buscas no Google e profissionais e empresas procurando compreender o que é e como usar aprendizagem de máquina, embora muitos ainda confundem o que podem fazer com o que desejam fazer. Não há como ficar indiferente a esta revolução trazida pela aprendizagem de máquina e, segundo o Gartner, até 2020 todos os softwares corporativos terão alguma funcionalidade ligada a Machine Learning.

Fundamentalmente, Machine Learning é a utilização de algoritmos para extrair informações de dados brutos e representá-los através de algum tipo de modelo matemático. Usamos então este modelo para fazer inferências a partir de outros conjuntos de dados. Existem muitos algoritmos que permitem fazer isso, mas um tipo em especial vem se destacando, as redes neurais artificiais.

As redes neurais artificiais não são necessariamente novas, existem pelo menos desde a década de 1950. Mas durante várias décadas, embora a arquitetura desses modelos tivesse evoluído, ainda faltavam ingredientes que fizessem os modelos realmente funcionar. E esses ingredientes surgiram quase ao mesmo tempo. Um deles você já deve ter ouvido: Big Data. O volume de dados, gerado em variedade e velocidade cada vez maiores, permite criar modelos e atingir altos níveis de precisão. Mas ainda falta um ingrediente. Faltava! Como processar grandes modelos de Machine Learning com grandes quantidades de dados? As CPUs não conseguiam dar conta do recado.

Foi quando os gamers e sua avidez por poder computacional e gráficos perfeitos, nos ajudaram a encontrar o segundo ingrediente: Programação Paralela em GPUs. As unidades de processamento gráfico, que permitem realizar operações matemáticas de forma paralela, principalmente operações com matrizes e vetores, elementos presentes em modelos de redes neurais artificias, formaram a tempestade perfeita, que permitiu a evolução na qual nos encontramos hoje: Big Data + Processamento Paralelo + Modelos de Aprendizagem de Máquina = Inteligência Artificial.

A unidade fundamental de uma rede neural artificial é um nó (ou neurônio matemático), que por sua vez é baseado no neurônio biológico. As conexões entre esses neurônios matemáticos também foram inspiradas em cérebros biológicos, especialmente na forma como essas conexões se desenvolvem ao longo do tempo com “treinamento”. Em meados da década de 1980 e início da década de 1990, muitos avanços importantes na arquitetura das redes neurais artificias ocorreram. No entanto, a quantidade de tempo e dados necessários para obter bons resultados retardou a adoção e, portanto, o interesse foi arrefecido, com o que ficou conhecimento como AI Winter (Inverno da IA).

No início dos anos 2000, o poder computacional expandiu exponencialmente e o mercado viu uma “explosão” de técnicas computacionais que não eram possíveis antes disso. Foi quando o aprendizado profundo (Deep Learning) emergiu do crescimento computacional explosivo dessa década como o principal mecanismo de construção de sistemas de Inteligência Artificial, ganhando muitas competições importantes de aprendizagem de máquina. O interesse por Deep Learning não para de crescer e hoje vemos o termo aprendizado profundo sendo mencionado com frequência cada vez maior e soluções comerciais surgindo a todo momento.

Este livro online, gratuito e em português, é uma iniciativa da Data Science Academy para ajudar aqueles que buscam conhecimento avançado e de qualidade em nosso idioma. Serão 100 capítulos, publicados no formato de posts. Desta forma, esperamos contribuir para o crescimento do Deep Learning e Inteligência Artificial no Brasil.

Nos acompanhe nesta incrível jornada!

Equipe DSA

www.datascienceacademy.com.br

 

Deep Learning Book

Capítulo 100 – Machine Learning – Guia Definitivo – Parte 10

by

Chegamos ao final do Deep Learning Book. Este é o centésimo e último capítulo deste livro online, em português, gratuito e agora com 100 capítulos!

Antes de mais nada nós da DSA gostaríamos de agradecer a você que acompanhou todo este trabalho realizado até aqui.

O Deep Learning Book nasceu do nosso inconformismo em ver pouco conhecimento sendo gerado em português sobre uma das tecnologias mais revolucionárias da história humana, a Inteligência Artificial. Este livro online, bem como os cursos gratuitos que oferecemos em nosso portal, fazem parte da nossa contribuição para ajudar a disseminar o conhecimento e a educação, tão importantes para a evolução do país.

Hoje o Deep Learning Book é uma referência em língua portuguesa, sendo usado aliás como referência em trabalhos de Mestrado e Doutorado, trabalhos de conclusão de curso de Graduação e Pós-Graduação e desde que foi lançado recebe um volume cada vez maior de acessos. Nosso objetivo vem sendo alcançado e estamos ajudando pessoas interessadas em aprender Inteligência Artificial. 

Para concluir este trabalho, vamos fazer uma revisão do processo de aprendizado de máquina com as 10 últimas regras do Guia Definitivo de Machine Learning.

E ao final do capítulo teremos uma surpresa para você.

Boa leitura.


Inteligência Artificial já está presente em nossas vidas. Observe a sua volta. Aplicações de filtro de spam, sistemas de reconhecimento facial no celular, chatbots de atendimento ao cliente, sistemas de recomendação e muito, muito mais.

Pelo menos desde a década de 50 que cientistas ao redor do mundo estão tentando reproduzir nas máquinas o que considera-se como inteligência, reproduzindo especialmente o sistema de aprendizado do cérebro humano.

Mas foi a partir do surgimento do Big Data, e em especial do processamento paralelo em GPUs, pouco mais de uma década atrás, que a Inteligência Artificial cresceu de forma exponencial, permitindo a criação de aplicações maravilhosas em áreas como Visão Computacional e Processamento de Linguagem Natural, notadamente as tarefas mais complexas de reproduzir em computadores.

E uma sub-área da IA, Machine Learning, teve os avanços mais incríveis, quando uma arquitetura em especial, Deep Learning, conseguiu obter resultados do estado da arte.

Mas se você acompanhou este livro com atenção deve ter percebido que muito do que fazemos em IA se resume a Matemática com programação de computadores, através do treinamento com muitos, muitos dados. Aplicando as mais diversas técnicas matemáticas e estatísticas, preparamos os dados, treinamos algoritmos via programação e modelos são criados para os mais devidos fins, resolvendo problemas de negócio, ajudando tomadores de decisão ou alimentando aplicações. E muito ainda está por vir, à medida que as empresas percebem os benefícios de aplicações baseadas em IA.

Aqui estão as 10 regras finais do Guia Definitivo de Machine Learning.

Regra 41: Precisamos de Dados

Você pode pensar: “Espere, isso é óbvio”. Você ficaria surpreso com a quantidade de pessoas que não compreendem que IA, Machine Learning ou Deep Learning não existem sem dados. Precisamos de dados históricos para que, através de algoritmos, possamos detectar padrões e então o modelo realizar suas previsões ou tarefa final.

Para compreender bem isso, basta fazer uma analogia com o aprendizado de uma criança. Como uma criança aprende a falar? Ouvindo sua família falar o tempo todo (a voz representa os dados nesse caso). Como uma criança aprende a escrever? Quando alguém a ensina através de exercícios (que nesse caso representam os dados). Ou seja, uma criança aprende à medida que é exposta a dados, que são processados através dos sentidos e criam no cérebro a memória que será usada pela criança durante toda a sua vida.

Se a sua empresa ainda não está cuidando dos dados com o devido valor, ela já está bem atrasada. Os dados são agora um ativo corporativo mais importante do que nunca e que permite o uso e benefícios de Inteligência Artificial.

Isso explica por que a engenharia de dados também cresceu muito nos últimos anos, uma vez que precisamos de mecanismos, sistemas e ferramentas para coletar, armazenar e processar os dados.

Regra 42: Os Dados Raramente Estarão Prontos Para Uso

E por isso a etapa de limpeza e pré-processamentos dados ainda é parte crucial do trabalho. São várias técnicas que devem ser usadas de acordo com o conjunto de dados.

Raramente os dados estarão no formato ideal para o processo de análise ou construção dos modelos. A criação de pipelines de dados é o que permite passar os dados por uma “linha de produção”, para que os dados cheguem ao seu destino em condições de serem usados. Para compreender isso faça uma analogia: O petróleo bruto poderia ser usado como combustível em um automóvel? Não. Logo, o petróleo passa a ser valioso quando é processado e gera como produto final o combustível que, aí sim, ajudará a resolver diversos problemas (embora também crie outros). Com os dados a ideia é a mesma.

Regra 43: Não Há Arquitetura Ideal em Machine Learning

Machine Learning está distante da perfeição. Cada arquitetura tem pontos fortes e fracos e nosso trabalho não é buscar perfeição e sim encontrar a melhor solução possível, uma aproximação, que será suficiente para resolver determinado problema de negócio.

Não desperdice seu tempo buscando o modelo perfeito. Mantenha o foco na solução do problema e tente encontrar o modelo que oferece a melhor aproximação com o menor esforço.

Regra 44: Machine Learning Não é Aplicação Pronta

Um equívoco muito comum cometido por iniciantes é achar que Machine Learning é uma aplicação pronta, linda e maravilhosa, que pode ser usada imediatamente. Não. Machine Learning cria um modelo e ainda precisamos dar um passo adiante e decidir como usar esse modelo.

O modelo pode ser usado via linha de comando em nossas máquinas, pode ser integrado em uma aplicação web, uma aplicação para smartphone, podemos criar uma API usando um serviço em nuvem. As opções são inúmeras, mas Machine Learning concentra o conhecimento para criação do modelo e não criação da uma aplicação completa.

Regra 45: Use Transfer Learning Sempre Que Possível

Quando seu conjunto de dados consiste em dados não estruturados, como imagens, texto ou áudio, é recomendável pegar carona em modelos pré-treinados existentes.

Para ajustar um classificador de imagens, você pode precisar de apenas 10 exemplos por classe, por exemplo. Frameworks como TensorFlow e PyTorch oferecem uma variedade de modelos pré-treinados. Você não precisa reinventar a roda ou gastar horas ou mesmo dias para treinar um modelo a partir do zero. Aprenda a trabalhar com Transfer Learning e mantenha o foco no seu objetivo. Seu objetivo não é criar modelos e sim resolver problemas de negócio. Aprenda a usar Transfer Learning.

Regra 46: Generalização Através de Regularização

Ao criar um modelo de Machine Learning queremos que ele seja generalizável, ou seja, depois de aprender com dados de treino o modelo deve ser capaz de fazer previsões ou extrair padrões em novos conjuntos de dados. Generalização significa que o modelo não deve aprender os detalhes dos dados de treino, mas sim a relação matemática geral nos dados.

Uma maneira de ajudar seu modelo a generalizar além do conjunto de treinamento é colocar penalidades no tamanho dos pesos w do seu modelo. Isso se chama regularização. Duas penalidades populares são a norma de Manhattan (ou norma L₁) e a norma euclidiana “padrão” (ou norma L₂).

Ao regularizar, verifique se todos os recursos são dimensionados para ordenar a unidade (sem dimensão) por padronização. Isso garante que a penalidade afete todos os pesos igualmente. As estimativas de Ball Park podem ser obtidas estudando dois casos de regressão linear que podem ser resolvidos de forma fechada, a saber, Regressão Lasso (isto é, L₁ regularização λ₁|w|₁ de pesos w) e Regressão de Ridge (L₂ regularização λ₂|w|²₂ de pesos W). Quando os recursos são centrados e não correlacionados, suas soluções podem ser expressas em termos da solução não penalizada.

A Regularização LASSO corta (ou trunca) todos os coeficientes não penalizados abaixo de λ₁. Um ponto de partida razoável pode, portanto, ser λ₁ = 0,1.

A Regularização Ridge, por outro lado, apenas diminui o tamanho para zero. Para escolher λ₂, você pode querer levar em consideração até que ponto seu sistema está sobreajustado (overfitting).

Uma nota de advertência: O Scikit-Learn usa convenções ligeiramente diferentes para os objetivos em Ridge e Lasso.

Regra 47: Qual o Volume de Dados Ideal Para Treinar Modelos de Machine Learning?

Não existe regra mágica, mas a figura abaixo apresenta um ponto de partida:

formula

Número de amostras (m), recursos (n) e parâmetros de modelo (d) formam a santíssima trindade do aprendizado de máquina. A maioria das regras de ouro pode ser amplamente trazida de volta a esta tríade.

Regra 48: Quantas Amostras Para Treinar Modelos de Machine Learning?

O desempenho normalmente escala como log m, onde m é o número de amostras e geralmente é limitado pelo ruído nos rótulos. Portanto, quando os dados de treinamento são rotulados por humanos, esse limite geralmente corresponde ao desempenho de nível humano. Logo, pode ser útil focar na qualidade dos dados, em vez da quantidade, conforme sugerido pelo movimento de IA centrada em dados.

Em geral, mais amostras são necessárias para problemas de regressão do que para problemas de classificação. 

Regra 49: Quantos Parâmetros?

Lembre-se de sua aula de álgebra linear lá no ensino médio, que para resolver um sistema linear com d graus de liberdade, você precisa de d restrições. Para regressão linear, cada amostra é uma restrição.

Portanto, para fixar os parâmetros d, você precisa de pelo menos tantas amostras — caso contrário, seu sistema é considerado subdeterminado. De forma mais geral, ao interpretar os parâmetros de um modelo como graus de liberdade, uma heurística comum é um sistema dez vezes sobredeterminado:

d ≤ m/10 

embora limites mais conservadores para redes neurais, como d ≤ m/50, também sejam sugeridos. Por sua vez, ter determinado o número de parâmetros, d, pode ajudá-lo a decidir se o número de recursos, n, precisa ser reduzido.

No entanto, é necessário cautela porque para muitos modelos, por exemplo, modelos probabilísticos, o número de restrições pode ser O(n) e independente do tamanho da amostra m.

Regra 50: Não Termina Aqui. Isso Foi Só o Começo

Existem 3 fases do aprendizado:

  • Fase 1 – Você não sabe o que não sabe.
  • Fase 2 – Você sabe o que não sabe.
  • Fase 3 – Você sabe o que sabe.

Ao finalizar a leitura deste livro esperamos que você tenha passado da Fase 1 para a Fase 2, quando o assunto é aprendizado de máquina.

Há muito ainda para aprender sobre Inteligência Artificial e este livro foi apenas o começo!

Sucesso na sua jornada!


E agora sua surpresa.

Para ajudar na sua capacitação estamos lançando a mais nova Formação DSA:

Formação Engenheiro de Inteligência Artificial

Um programa que traz para você as mais modernas técnicas de IA através de uma série de laboratórios práticos e projetos realmente incríveis em diferentes áreas de negócio.

Veja abaixo a sequência e descrição dos 6 cursos da Formação Engenheiro de IA:

1- Deep Learning Para Aplicações de IA com PyTorch e Lightning

Este é o ponto de partida da Formação. Aqui você aprenderá como construir modelos de Deep Learning, as principais arquiteturas, como pré-processar os dados, como otimizar os modelos e como realizar o deploy. Os frameworks PyTorch e Pytorch Lightning serão usados em Linguagem Python.

Este é um curso completo de Deep Learning, hoje a principal técnica de Inteligência Artificial, com o framework de maior sucesso da atualidade, o PyTorch. E vamos trazer também para você o Lightning, biblioteca que simplifica a forma como criamos modelos com o PyTorch aumentando a produtividade na construção dos modelos e a performance das aplicações de Inteligência Artificial.

Depois de mais de 5 anos capacitando milhares de alunos em Deep Learning através de diversos outros treinamentos, neste novo curso oferecido pela DSA você vai desenvolver suas habilidades em IA com o estado da arte em Deep Learning através de Estudos de Caso, Labs, Mini-Projetos e Projetos. 

2- Análise de Imagens com Inteligência Artificial

Aqui é onde a diversão começa. Você vai trabalhar com uma das principais áreas da Inteligência Artificial, a Visão Computacional. Através de labs e projetos de diferentes áreas, você aprenderá como construir modelos de IA capazes de detectar, classificar e segmentar imagens dos mais variados tipos.

Este curso traz para você modernas técnicas de Visão Computacional para análise de todo tipo de imagem, como imagens médicas, imagens de satélite, imagens de plantações agrícolas, imagens de objetos, imagens de pessoas para reconhecimento facial e você ainda vai aprender como criar seu próprio dataset de imagens customizado e como detectar Deep Fakes.

Visão Computacional é atualmente uma das principais áreas da Inteligência Artificial. Um conjunto de técnicas para carregar, manipular, tratar, processar, detectar, prever, segmentar e analisar imagens. E neste curso, de alto nível, vamos trazer as principais técnicas e ferramentas para você, incluindo Vision Transformers.

O curso é inteiramente orientado a projetos. Isso significa que a cada capítulo você vai trabalhar em um problema do mundo real com dados reais (disponíveis publicamente) e então vai desenvolver suas habilidades através de aulas teóricas que exploram os principais conceitos e aulas práticas que implementam a solução. E em um dos capítulos vamos ensinar como você cria seu próprio dataset de imagens customizado.

3- Processamento de Linguagem Natural com Transformers

O terceiro curso da Formação é sobre a área mais complexa da Inteligência Artificial. Pelo menos até o surgimento dos modelos Transformers, que estão revolucionando a forma como ensinamos o computador a fazer traduções de texto, reconhecer a voz humana ou classificar textos e mensagens por tópicos

Você deseja aplicar o Processamento de Linguagem Natural (PLN), com as mais modernas técnicas de IA, para resolver diferentes problemas nas áreas de Direito, Atendimento ao Cliente, Reconhecimento de Voz, Análise de Sentimento, Classificação de Texto e Detecção de Fake News? Então este curso é para você.

Este não é apenas um curso de PLN. Este curso traz para você o estado da arte em Inteligência Artificial com Transformers e aplicações práticas em diversas áreas com projetos completos, incluindo a Dorothy, o Bot Transformer que você irá construir para automatizar o atendimento ao cliente. Imperdível.

4- Análise e Previsão de Séries Temporais com Inteligência Artificial

No quarto curso da Formação você vai aplicar Inteligência Artificial em problemas de negócio que requerem análise ao longo do tempo. Os principais métodos clássicos para análise de séries temporais, também serão abordados no começo do curso.

A análise de séries temporais é provavelmente uma das mais importantes habilidades dentro do universo da Ciência de Dados. Afinal, o fator tempo é determinante em quase tudo que ocorre no mundo dos negócios.

E com os avanços das técnicas de Inteligência Artificial conseguimos construir modelos cada vez mais precisos, capazes de entregar respostas aos tomadores de decisão que podem fazer toda a diferença nas estratégias corporativas.

Este é um curso de alto nível que vai trazer para você modernas técnicas de IA aplicadas a análise e previsão de séries temporais e comparar essas técnicas com os métodos clássicos (que também são abordados no curso).

5- Cyber Security Data Science

No curso número 5 você vai aplicar técnicas de Data Science e Inteligência Artificial para resolver problemas na área de segurança cibernética. Um curso incrível não apenas para quem deseja aplicar análise de dados na área de segurança, mas também em problemas comuns na área de tecnologia, como configuração de rede, configuração de sistema operacional e segurança de acesso.

Além de conhecer as principais ameaças e riscos cibernéticos, você vai aprender quais são as estratégias ideais de segurança, como trabalhar com dados reais e como usar Ciência de Dados e IA para detectar anomalias, tentativas de invasão, ataques a bancos de dados e dispositivos IoT, e muito mais.

6- Infraestrutura Como Código com Terraform, AWS, Azure e Databricks

O sexto e último curso é a cereja do bolo. Você vai desenvolver as habilidades necessárias para criar a infraestrutura necessária para treinar seus modelos, fazer o deploy e publicar aplicações usando IaC (Infraestrutura Como Código) através do Terraform, ferramenta open-source que simplifica de forma considerável a maneira como criamos, usamos e então desfazemos a infraestrutura necessária para o trabalho com Data Science, Machine Learning e IA.

IaC (Infraestrutura Como Código) nasceu no universo DevOps, mas rapidamente chegou à área de dados para ajudar no trabalho de Engenheiros de Dados, Engenheiros de Machine Learning, Arquitetos de Dados, Cientistas de Dados e Engenheiros de IA.

Além do Terraform você vai trabalhar com AWS, Azure e Databricks através de diversos Labs e Projetos. O conhecimento que você irá adquirir neste curso vai colocá-lo muito a frente de outros profissionais do mercado, aumentando de forma considerável sua empregabilidade na área de dados, independente da sua função.

Não é incrível?

Mas espere, tem mais.

Assim como as demais Formações DSA você ainda recebe os Módulos Extras de Capacitação Profissional com os seguintes cursos de bônus:

  • Introdução à Lógica de Programação
  • Sistema Operacional Linux, Docker e Kubernetes
  • Governança de Dados
  • Empreendedorismo em Data Science, IA e Blockchain
  • Web Scraping e Análise de Dados
  • Soft Skills – Desenvolvendo Suas Habilidades Comportamentais
  • E-Gov Analytics
  • Machine Learning com JavaScript e Go
  • Data Science e Machine Learning com Linguagem Julia

A Formação está disponível em nosso portal. Confira:

Formação Engenheiro de IA

Obrigado

Equipe DSA

Machine Learning Guia Definitivo

Capítulo 93 – Machine Learning – Guia Definitivo – Parte 3

by

Nos 10 capítulos finais deste livro online vamos trazer um grande resumo sobre Machine Learning. O objetivo é fornecer uma visão clara do que é e como Machine Learning está sendo usado no dia a dia, um pouco de matemática, as principais regras e princípios. Queremos ainda que esses capítulos finais possam servir de material de referência para os alunos que estão buscando as certificações oferecidas pela DSA no Bootcamp de Certificação.

Serão 10 partes no total com um guia completo sobre Machine Learning. Aproveite a leitura para compreender de forma definitiva o que é uma das tecnologias mais incríveis do nosso tempo.

Agora, na Parte 3, um glossário com os 50 principais termos em Machine Learning.

Glossário

Os termos a seguir aparecerão repetidamente em projetos de Machine Learning (listamos em ordem de importância e similaridade e não em ordem alfabética):

1- Instância: linha, registro ou observação em seu conjunto de dados, sobre a qual você deseja fazer uma previsão. Por exemplo, cada instância pode ser um registro contendo informações sobre pacientes como idade, peso e altura e informação se o paciente desenvolveu diabetes. Queremos prever a ocorrência de diabetes com base nas características do paciente.

2- Rótulo (Label): uma resposta para uma tarefa de previsão, seja a resposta produzida por um sistema de aprendizado de máquina ou a resposta correta fornecida nos dados de treinamento. Por exemplo, o rótulo da instância definido no item anterior seria “diabetes”, indicando se o paciente desenvolveu ou não a doença.

3- Classe: uma categoria de um conjunto de valores de destino enumerados para um rótulo. Por exemplo, em um modelo de classificação binária que detecta spam, as duas classes são spam e não spam. Em um modelo de classificação multiclasse que identifica raças de cães, as classes seriam poodle, beagle, pug e assim por diante.

4- Recurso (Feature): uma propriedade de uma instância usada em uma tarefa de previsão. Por exemplo, uma instância com dados de pacientes teria um recurso indicando a idade.

5- Recurso Denso: um recurso em que a maioria dos valores é diferente de zero, normalmente um tensor de valores de ponto flutuante.

6- Recurso Esparso: vetor de recursos cujos valores são predominantemente zero ou vazios. Por exemplo, um vetor contendo um único valor 1 e um milhão de valores 0 é esparso. Como outro exemplo, as palavras em uma consulta de pesquisa também podem ser um recurso esparso – há muitas palavras possíveis em um determinado idioma, mas apenas algumas delas ocorrem em uma determinada consulta.

7- Dados Categóricos: recursos com um conjunto discreto de valores possíveis representando informação qualitativa. Por exemplo, considere um recurso categórico chamado estilo da casa, que possui um conjunto discreto de três valores possíveis: apartamento, casa e chalé.

8- Dados Numéricos: características representadas como números inteiros ou números reais. Por exemplo, em um modelo imobiliário, você provavelmente representaria o tamanho de uma casa (em metros quadrados) como dados numéricos. Representar um recurso como dados numéricos indica que os valores do recurso têm uma relação matemática entre si e possivelmente com o rótulo. 

9- Exemplo: uma instância (com seus recursos) e um rótulo.

10- Modelo: uma representação estatística de uma tarefa de previsão. Você treina um modelo em exemplos e depois usa o modelo para fazer previsões.

11- Baseline: um modelo usado como ponto de referência para comparar o desempenho de outro modelo (normalmente mais complexo). Por exemplo, um modelo de regressão logística pode servir como uma boa linha de base para um modelo de Deep Learning. Para um problema específico, a linha de base ajuda os Cientistas de Dados a quantificar o desempenho mínimo esperado que um novo modelo deve alcançar para que o novo modelo seja útil.

12- Aprendizado de Máquina Supervisionado: técnica para treinar um modelo a partir de dados de entrada e seus rótulos correspondentes. O aprendizado de máquina supervisionado é análogo a um aluno aprendendo um assunto estudando um conjunto de perguntas e suas respostas correspondentes. Depois de dominar o mapeamento entre perguntas e respostas, o aluno pode fornecer respostas para novas perguntas (nunca antes vistas) sobre o mesmo tema.

13- Aprendizado de Máquina Não Supervisionado: técnica para treinar um modelo para encontrar padrões em um conjunto de dados, normalmente um conjunto de dados não rotulado. O uso mais comum do aprendizado de máquina não supervisionado é agrupar dados em grupos de exemplos semelhantes. Por exemplo, um algoritmo de aprendizado de máquina não supervisionado pode agrupar músicas com base em várias propriedades da música. Os clusters resultantes podem se tornar uma entrada para outros algoritmos de aprendizado de máquina (por exemplo, para um serviço de recomendação de música). O clustering pode ser útil em domínios onde os rótulos verdadeiros são difíceis de obter. Por exemplo, em domínios como antiabuso e fraude, os clusters podem ajudar os humanos a entender melhor os dados. Outro exemplo de aprendizado de máquina não supervisionado é a análise de componentes principais (PCA). Por exemplo, a aplicação de PCA em um conjunto de dados contendo o conteúdo de milhões de carrinhos de compras pode revelar que carrinhos de compras contendo limões frequentemente também contêm antiácidos.

14- Métrica: medida da performance do seu modelo. Cada tipo de modelo pode ter uma ou mais métricas, que usamos para comparar a performance entre versões do modelo.

15- Função Objetivo: a fórmula matemática ou métrica que um modelo visa otimizar. Por exemplo, a função objetivo para regressão linear geralmente é o erro ao quadrado. Portanto, ao treinar um modelo de regressão linear, o objetivo é minimizar o erro quadrática. Em alguns casos, o objetivo é maximizar a função objetivo. Por exemplo, se a função objetivo é precisão, o objetivo é maximizar a precisão.

16- AUC (Área sob a Curva ROC): uma métrica de avaliação que considera todos os limites de classificação possíveis. A área sob a curva ROC é a probabilidade de um classificador estar mais confiante de que um exemplo positivo escolhido aleatoriamente é realmente positivo, do que um exemplo negativo escolhido aleatoriamente é positivo.

17- Acurácia: a fração de previsões que um modelo de classificação acertou. Em um modelo com acurácia de 82% dizemos que o modelo acerta 82 previsões a cada 100 previsões realizadas.

18- Precisão Média: uma métrica para resumir o desempenho de uma sequência classificada de resultados. A precisão média é calculada tomando a média dos valores de precisão para cada resultado relevante.

19- DataFrame: popular estrutura de dados para representar conjuntos de dados. Um DataFrame é análogo a uma tabela. Cada coluna do DataFrame tem um nome (um cabeçalho) e cada linha é identificada por um número.

20- Função de Ativação: uma função (por exemplo, ReLU ou sigmoid) que recebe a soma ponderada de todas as entradas da camada anterior em um modelo de rede neural e, em seguida, gera e passa um valor de saída (normalmente não linear) para a próxima camada.

21- Backpropagation: o algoritmo primário para executar gradiente descendente em redes neurais. Primeiro, os valores de saída de cada nó são calculados (e armazenados em cache) em uma passagem direta. Então, a derivada parcial do erro em relação a cada parâmetro é calculada em uma passagem para trás pelo grafo computacional, conforme estudado em detalhes neste livro online.

22- Batch: o conjunto de exemplos usados em uma iteração (ou seja, uma atualização de gradiente) do treinamento do modelo.

23- Batch Normalization: normalização da entrada ou saída das funções de ativação em uma camada oculta. A normalização em lote pode fornecer os seguintes benefícios: torna as redes neurais mais estáveis protegendo contra pesos discrepantes, habilita taxas de aprendizado mais altas e reduz o sobreajuste (overfitting).

24- Stochastic Gradient Descent (SGD): um algoritmo de descida de gradiente em que o tamanho do lote é um. Em outras palavras, o SGD conta com um único exemplo escolhido uniformemente de forma aleatória de um conjunto de dados para calcular uma estimativa do gradiente em cada etapa.

25- Overfitting: o overfiting (sobre ajuste) ocorre quando o modelo aprende os detalhes nos dados de treino. Não é isso que queremos em Machine Learning. Em aprendizado de máquina buscamos a criação de um modelo que aprende a generalização dos dados, para então fazer previsões com novos dados.

26- Bagging: um método para treinar um ensemble onde cada modelo constituinte é treinado em um subconjunto aleatório de exemplos de treinamento amostrados com substituição. Por exemplo, uma floresta aleatória (Random Forest) é uma coleção de árvores de decisão treinadas com bagging. O termo bagging é a abreviação de bootstrap aggregating.

27- Boosting: uma técnica de aprendizado de máquina que combina iterativamente um conjunto de classificadores simples e não muito precisos (referidos como classificadores “fracos”) em um classificador com alta precisão (um classificador “forte”), valorizando os exemplos que o modelo está classificando incorretamente no momento .

28- Bag of Words: uma representação das palavras em uma frase ou passagem, independentemente da ordem. Cada palavra é mapeada para um índice em um vetor esparso, onde o vetor tem um índice para cada palavra do vocabulário. Por exemplo, a frase “o cachorro pula” é mapeada em um vetor de características com valores diferentes de zero nos três índices correspondentes às palavras “o”, “cachorro” e “pula”.

29- Word Embedding: estrutura que representa cada palavra em um conjunto de palavras; ou seja, representando cada palavra como um vetor de valores de ponto flutuante entre 0,0 e 1,0. Palavras com significados semelhantes têm representações mais semelhantes do que palavras com significados diferentes. Por exemplo, cenoura, aipo e pepino teriam representações relativamente semelhantes, que seriam muito diferentes das representações de avião, óculos de sol e pasta de dente.

30- Inteligência Artificial: um programa ou modelo não humano que pode resolver tarefas sofisticadas. Por exemplo, um programa ou modelo que traduz texto ou um programa ou modelo que identifica doenças a partir de imagens radiológicas exibem Inteligência Artificial. Formalmente, o aprendizado de máquina é um subcampo da Inteligência Artificial. No entanto, nos últimos anos, algumas organizações começaram a usar os termos Inteligência Artificial e aprendizado de máquina de forma intercambiável.

31- Inteligência Artificial Geral: um mecanismo não humano que demonstra uma ampla gama de resolução de problemas, criatividade e adaptabilidade. Por exemplo, um programa que demonstra Inteligência Artificial Geral pode traduzir texto, compor sinfonias e se destacar em jogos que ainda não foram inventados.

32- Atenção: qualquer um de uma ampla gama de mecanismos de arquitetura de rede neural que agregam informações de um conjunto de entradas de maneira dependente de dados. Um mecanismo de atenção típico pode consistir em uma soma ponderada sobre um conjunto de entradas, onde o peso de cada entrada é calculado por outra parte da rede neural. São os blocos principais dos Transformers, estudados aqui mesmo neste livro online.

33- Recall: o recall é a medida do nosso modelo identificando corretamente os Verdadeiros Positivos. Assim, para todos os pacientes que realmente têm doença cardíaca, o recall nos diz quantos identificamos corretamente como tendo uma doença cardíaca.

34- Precisão: é a razão entre os Verdadeiros Positivos e todos os Positivos. Para nossa declaração de problema, essa seria a medida de pacientes que identificamos corretamente como tendo uma doença cardíaca entre todos os pacientes que realmente a têm.

35- Bias (ética/justiça): estereótipo, preconceito ou favoritismo em relação a algumas coisas, pessoas ou grupos em detrimento de outros. Esses vieses podem afetar a coleta e interpretação de dados, o design de um sistema e como os usuários interagem com um sistema.

36- Bias (matemática): uma interceptação ou deslocamento de uma origem. O bias (também conhecido como termo de viés) é referido como b ou w0 em modelos de aprendizado de máquina.

37- Classificação Binária: um tipo de tarefa de classificação que gera uma das duas classes mutuamente exclusivas. Por exemplo, um modelo de aprendizado de máquina que avalia mensagens de e-mail e gera “spam” ou “não spam” é um classificador binário.

38- BLEU (Bilingual Evaluation Understudy): uma pontuação entre 0,0 e 1,0, inclusive, indicando a qualidade de uma tradução entre duas línguas humanas (por exemplo, entre inglês e russo). Uma pontuação BLEU de 1,0 indica uma tradução perfeita; uma pontuação BLEU de 0,0 indica uma tradução terrível. Medida usada em modelos de Processamento de Linguagem Natural.

39- Normalização: o processo de conversão de um intervalo real de valores em um intervalo padrão de valores, normalmente -1 a +1 ou 0 a 1. Por exemplo, suponha que o intervalo natural de um determinado recurso seja de 800 a 6.000. Por meio de subtração e divisão, você pode normalizar esses valores no intervalo -1 a +1.

40- Normalização Z-score: uma técnica de normalização que substitui um valor de recurso bruto por um valor de ponto flutuante que representa o número de desvios padrão da média desse recurso.

41- Entropia Cruzada: uma generalização de Log Loss para problemas de classificação multiclasse. A entropia cruzada quantifica a diferença entre duas distribuições de probabilidade.

42- Matriz de Confusão: uma tabela NxN que agrega as suposições corretas e incorretas de um modelo de classificação. Um eixo de uma matriz de confusão é o rótulo que o modelo previu e o outro eixo é a verdade básica. N representa o número de classes. Por exemplo, N=2 para um modelo de classificação binária.

43- Validação: um processo usado, como parte do treinamento, para avaliar a qualidade de um modelo de aprendizado de máquina usando o conjunto de validação. Como o conjunto de validação é separado do conjunto de treinamento, a validação ajuda a garantir que o desempenho do modelo se generalize além do conjunto de treinamento.

44- Estacionaridade: uma propriedade de dados em um conjunto de dados, na qual a distribuição de dados permanece constante em uma ou mais dimensões. Mais comumente, essa dimensão é o tempo, o que significa que os dados que exibem estacionariedade não mudam com o tempo. Por exemplo, os dados que exibem estacionariedade não mudam de setembro a dezembro. Termo muito usado em análise de séries temporais.

45- Não Estacionaridade: uma propriedade de dados cujos valores mudam em uma ou mais dimensões, geralmente o tempo. Por exemplo, o número de maiôs vendidos em uma determinada loja demonstra não estacionaridade porque esse número varia com a estação. Como segundo exemplo, a quantidade de uma determinada fruta colhida em uma determinada região normalmente mostra uma acentuada não estacionariedade ao longo do tempo. Termo muito usado em análise de séries temporais.

46- Convergência: informalmente, muitas vezes se refere a um estado alcançado durante o treinamento no qual a perda de treinamento e a perda de validação mudam muito pouco ou nada a cada iteração após um certo número de iterações. Em outras palavras, um modelo atinge a convergência quando o treinamento adicional nos dados atuais não melhora o modelo. No aprendizado profundo, os valores de perda às vezes permanecem constantes por muitas iterações antes de finalmente descer, produzindo temporariamente uma falsa sensação de convergência.

47- Desbalanceamento de Classe: um problema de classificação binária em que os rótulos para as duas classes têm frequências significativamente diferentes. Por exemplo, um conjunto de dados de doenças em que 0,0001 dos exemplos tem rótulos positivos e 0,9999 têm rótulos negativos é um problema de desequilíbrio de classe, mas um preditor de jogo de futebol no qual 0,51 dos exemplos rotula um time vencedor e 0,49 rotula o outro time vencedor não é um problema de classe desequilibrada.

48- Limite (Threshold) de Classificação: um critério de valor escalar que é comparado com a pontuação prevista de um modelo para separar a classe positiva da classe negativa. Usado ao mapear resultados de regressão logística para classificação binária. Por exemplo, considere um modelo de regressão logística que determina a probabilidade de uma determinada mensagem de email ser spam. Se o limite de classificação for 0,9, os valores de regressão logística acima de 0,9 são classificados como spam e os abaixo de 0,9 são classificados como não spam.

49- Checkpoint: captura o estado dos parâmetros de um modelo em um determinado momento. Os pontos de verificação permitem exportar pesos de modelo ou realizar treinamento em várias sessões. Os pontos de verificação também permitem que o treinamento continue com erros anteriores.

50- Data Science Academy: maior portal da América Latina para o capacitação de alto nível em Data Science, IA, Blockchain, RPA e tecnologias relacionadas. 🙂

A partir do próximo capítulo veremos as regras para uso e aplicação de Machine Learning.

Machine Learning Guia Definitivo

Capítulo 92 – Machine Learning – Guia Definitivo – Parte 2

by

Nos 10 capítulos finais deste livro online vamos trazer um grande resumo sobre Machine Learning. O objetivo é fornecer uma visão clara do que é e como Machine Learning está sendo usado no dia a dia, um pouco de matemática, as principais regras e princípios. Queremos ainda que esses capítulos finais possam servir de material de referência para os alunos que estão buscando as certificações oferecidas pela DSA no Bootcamp de Certificação.

Serão 10 partes no total com um guia completo sobre Machine Learning. Aproveite a leitura para compreender de forma definitiva o que é uma das tecnologias mais incríveis do nosso tempo.

Agora na Parte 2, vejamos como Machine Learning está presente em diversos subcampos da Inteligência Artificial:

Processamento de Linguagem Natural

No Processamento de Linguagem Natural as máquinas aprendem a entender a linguagem natural, falada e escrita por humanos, em vez dos dados e números normalmente usados ​​para programar computadores. Isso permite que as máquinas reconheçam o idioma, o entendam e respondam a ele, bem como criem novos textos e traduzam idiomas. O Processamento de Linguagem Natural é a tecnologia por trás de chatbots e assistentes digitais como Siri ou Alexa.

Redes Neurais

As redes neurais são uma classe específica e comumente usada de algoritmos de aprendizado de máquina. As redes neurais artificiais são modeladas a partir do funcionamento do cérebro humano, no qual milhares ou milhões de nós de processamento são interconectados e organizados em camadas.

Em uma rede neural artificial, células ou nós são conectados, com cada célula processando entradas e produzindo uma saída que é enviada a outros neurônios. Os dados rotulados se movem pelos nós (estruturas matemáticas), ou células, com cada célula desempenhando uma função diferente. Em uma rede neural treinada para identificar se uma imagem contém um gato ou não, os diferentes nós avaliariam as informações e chegariam a uma saída que indica se uma imagem apresenta um gato. Os modelos de redes neurais atuais fazem isso muito bem.

Deep Learning

As redes de aprendizado profundo são redes neurais com muitas camadas. A rede com muitas camadas pode processar grandes quantidades de dados e determinar o “peso” de cada link na rede – por exemplo, em um sistema de reconhecimento de imagem, algumas camadas da rede neural podem detectar características individuais de um rosto, como olhos, nariz, ou boca, enquanto outra camada seria capaz de dizer se esses recursos aparecem de uma forma que indica um rosto. Estudamos Deep Learning ao longo deste livro online.

Assim como as redes neurais, o aprendizado profundo é modelado na maneira como o cérebro humano funciona e potencializa muitos usos do aprendizado de máquina, como veículos autônomos, chatbots e diagnósticos médicos.

O aprendizado profundo requer muito poder computacional, o que levanta preocupações sobre sua sustentabilidade econômica e ambiental.

Como as Empresas Estão Usando o Aprendizado de Máquina?

O aprendizado de máquina é o núcleo dos modelos de negócios de algumas empresas, como no caso do algoritmo de sugestões da Netflix ou do mecanismo de busca do Google. Outras empresas estão se engajando profundamente com o aprendizado de máquina, embora não seja sua principal proposta de negócios.

Outros ainda estão tentando determinar como usar o aprendizado de máquina de maneira eficiente. Um dos problemas mais difíceis no aprendizado de máquina é descobrir quais problemas podemos resolver com o aprendizado de máquina. Ainda há uma lacuna no entendimento.

Em um artigo de 2018, pesquisadores da Iniciativa do MIT sobre a Economia Digital delinearam uma pesquisa de 21 perguntas para determinar se uma tarefa é adequada para aprendizado de máquina. Os pesquisadores descobriram que nenhuma ocupação será intocada pelo aprendizado de máquina, mas é provável que nenhuma ocupação seja completamente dominada por ele. A maneira de desencadear o sucesso do aprendizado de máquina, descobriram os pesquisadores, era reorganizar os trabalhos em tarefas discretas, algumas que podem ser feitas por aprendizado de máquina e outras que exigem um humano.

As empresas já estão usando o aprendizado de máquina de várias maneiras, incluindo:

Algoritmos de recomendação. Os mecanismos de recomendação por trás das sugestões da Netflix e do YouTube, quais informações aparecem no seu feed do Facebook e recomendações de produtos, são alimentados pelo aprendizado de máquina. Os algoritmos estão tentando aprender nossas preferências. Eles querem aprender, como no Twitter, quais tweets queremos que eles nos mostrem, no Facebook, quais anúncios exibir e quais postagens ou conteúdos compartilhar conosco.

Análise de imagens e detecção de objetos. O aprendizado de máquina pode analisar imagens para obter informações diferentes, como aprender a identificar pessoas e diferenciá-las – embora os algoritmos de reconhecimento facial sejam controversos. Os usos comerciais para isso variam. Os fundos de hedge usam o aprendizado de máquina para analisar o número de carros em estacionamentos, o que os ajuda a saber como as empresas estão se saindo e fazer boas apostas.

Detecção de fraude. As máquinas podem analisar padrões, como por exemplo quanto alguém normalmente gasta ou onde costuma fazer compras, para identificar transações de cartão de crédito potencialmente fraudulentas, tentativas de login ou e-mails de spam.

Chatbots. Muitas empresas estão implantando chatbots, nos quais clientes interagem com uma máquina. Esses algoritmos usam aprendizado de máquina e Processamento de Linguagem Natural, com os bots aprendendo com registros de conversas anteriores para obter respostas apropriadas.

Carros autônomos. Grande parte da tecnologia por trás dos carros autônomos é baseada no aprendizado de máquina, em particular no aprendizado profundo (Deep Learning).

Imagiologia e diagnóstico médico. Programas de aprendizado de máquina podem ser treinados para examinar imagens médicas ou outras informações e procurar certos marcadores de doenças, como uma ferramenta que pode prever o risco de câncer com base em uma mamografia.

Como Machine Learning Funciona: Promessas e Desafios

Embora o aprendizado de máquina esteja alimentando a tecnologia que pode ajudar os trabalhadores ou abrir novas possibilidades para as empresas, há várias coisas que os líderes empresariais devem saber sobre o aprendizado de máquina e seus limites.

Explicabilidade

Uma área de preocupação é o que alguns especialistas chamam de explicabilidade, ou a capacidade de ser claro sobre o que os modelos de aprendizado de máquina estão fazendo e como eles tomam decisões. Entender por que um modelo faz o que faz é realmente uma questão muito difícil, e você sempre tem que se perguntar isso. Você nunca deve tratar isso como uma caixa preta, que vem apenas como um oráculo … sim, você deve usá-lo, mas então tente ter uma ideia de quais são as regras básicas que ele criou. E depois valide-as.

Isso é especialmente importante porque os sistemas podem ser enganados e prejudicados, ou simplesmente falhar em certas tarefas, mesmo aquelas que humanos podem executar facilmente. Por exemplo, ajustar os metadados nas imagens pode confundir os computadores – com alguns ajustes, uma máquina identifica uma foto de um cachorro como avestruz.

Houve um famoso caso em que um algoritmo de aprendizado de máquina examinando raios-X parecia superar os médicos. Mas descobriu-se que o algoritmo estava correlacionando os resultados com as máquinas que tiraram a imagem, não necessariamente a imagem em si. A tuberculose é mais comum em países em desenvolvimento, que tendem a ter máquinas mais antigas. O programa de aprendizado de máquina descobriu que, se o raio-X fosse feito em uma máquina mais antiga, o paciente era mais propenso a ter tuberculose. Ele completou a tarefa, mas não da maneira que os Cientistas de Dados pretendiam ou achariam útil.

A importância de explicar como um modelo está funcionando – e sua precisão – pode variar dependendo de como está sendo usado. Embora a maioria dos problemas possa ser resolvidos por meio de aprendizado de máquina, as pessoas devem assumir agora que os modelos funcionam apenas com cerca de 95% da precisão humana. Pode ser bom para o Cientista de Dados e o usuário se um algoritmo recomendando filmes for 95% preciso, mas esse nível de precisão não seria suficiente para um veículo autônomo ou um programa projetado para encontrar falhas graves em máquinas.

Viés e Resultados Não Intencionais

Máquinas são treinadas por humanos e preconceitos humanos podem ser incorporados em algoritmos – se informações tendenciosas ou dados que refletem desigualdades existentes forem alimentados a um programa de aprendizado de máquina, o programa aprenderá a replicá-lo e perpetuar formas de discriminação. Chatbots treinados sobre como as pessoas conversam no Twitter podem captar linguagem ofensiva e racista, por exemplo.

Em alguns casos, os modelos de aprendizado de máquina criam ou exacerbam problemas sociais. Por exemplo, o Facebook usa o aprendizado de máquina como uma ferramenta para mostrar aos usuários anúncios e conteúdo que os interessarão e os envolverão – o que pode levar a modelos que mostram às pessoas conteúdo extremo que causa polarização e disseminação de teorias da conspiração.

Maneiras de combater o preconceito no aprendizado de máquina, incluindo a verificação cuidadosa de dados de treinamento e apoio organizacional por trás de esforços éticos de Inteligência Artificial, como garantir que uma organização adote IA centrada no ser humano, a prática de buscar informações de pessoas de diferentes origens, experiências e estilos de vida quando projetar sistemas de IA. As iniciativas que trabalham nesta questão incluem o projeto Algorithmic Justice League e The Moral Machine.

Colocando o Aprendizado de Máquina Para Funcionar

Os executivos tendem a lutar para entender onde o aprendizado de máquina pode realmente agregar valor à empresa. O que é enigmático para uma empresa é essencial para outra e as empresas devem evitar tendências e encontrar casos de uso de negócios que funcionem para elas.

A maneira como o aprendizado de máquina funciona para a Amazon provavelmente não será traduzido em uma empresa de automóveis – embora a Amazon tenha encontrado sucesso com assistentes de voz, isso não significa que as empresas de automóveis devam priorizar a adição de assistentes aos carros. O mais provável é que a montadora encontre uma maneira de usar o aprendizado de máquina na linha da fábrica que economize ou ganhe muito dinheiro.

O campo de estudo em Machine Learning está se movendo rapidamente, e isso é incrível, mas torna difícil para os executivos tomar decisões e decidir quanto recursos devem ser investidos na tecnologia.

Também é melhor evitar olhar para o aprendizado de máquina como uma solução em busca de um problema. Algumas empresas podem acabar tentando fazer engenharia reversa do aprendizado de máquina em um uso comercial. Em vez de começar com foco em tecnologia, as empresas devem começar com foco em um problema de negócios ou necessidade do cliente que possa ser atendido com aprendizado de máquina.

Uma compreensão básica do aprendizado de máquina é importante mas, encontrar o uso certo de aprendizado de máquina depende, em última análise, de pessoas com diferentes conhecimentos trabalhando juntos. 

Continuamos no próximo capítulo!

Referências:

Machine Learning com R e Python

Machine Learning com Python e C++

Machine Learning, Explained

Deep Learning Book

Capítulo 86 – Como Funcionam os Transformadores em Processamento de Linguagem Natural – Parte 1

by

A partir de agora e nos próximos capítulos vamos compreender o funcionamento dos Transformadores, uma das técnicas mais avançadas da atualidade em Inteligência Artificial, especialmente no Processamento de Linguagem Natural.

Os Transformadores não são tão difíceis de entender. É a combinação de todos os conceitos que pode tornar a compreensão complexa, incluindo a atenção. É por isso que vamos construir lentamente todos os conceitos fundamentais. Não tenha pressa. Não existe atalho para o aprendizado.

Com Redes Neurais Recorrentes (RNNs), costumamos tratar frases sequencialmente para manter a ordem da frase no lugar (já vimos isso em capítulos anteriores deste livro). Para satisfazer esse design, cada componente RNN (camada) precisa da saída anterior (oculta). Como tal, os cálculos LSTM empilhados são executados sequencialmente.

Até que os Transformadores aparecessem! O bloco de construção fundamental de um Transformador é a auto-atenção. Para começar, precisamos superar o processamento sequencial, recorrência e LSTMs!

Como?

Simplesmente alterando a representação de entrada!

Representando a Frase de Entrada

A revolução do Transformador começou com uma pergunta simples: por que não alimentamos toda a sequência de entrada? Sem dependências entre estados ocultos! Isso pode ser legal!

Como exemplo, a frase “Hello, I love you” (“olá, eu te amo”):

tokenization

Essa etapa de processamento geralmente é chamada de tokenização e é a primeira das três etapas antes de alimentarmos a entrada no modelo. Isso vale para qualquer técnica em PLN, conforme mostramos no curso de Processamento de Linguagem Natural na DSA.

Portanto, em vez de uma sequência de elementos, agora temos um conjunto. Conjuntos são uma coleção de elementos distintos, onde a disposição dos elementos no conjunto não importa.

Em outras palavras, a ordem é irrelevante. Indicamos a entrada definida como:

token

Os elementos da sequência xi são chamados de tokens.

Após a tokenização, projetamos palavras em um espaço geométrico distribuído ou simplesmente construímos embeddings de palavras.

Word Embeddings

Em geral, um embedding é uma representação de um símbolo (palavra, caractere, frase) em um espaço distribuído de baixa dimensão de vetores de valor contínuo.

Palavras não são símbolos discretos. Elas estão fortemente correlacionadas uma com a outra. É por isso que quando as projetamos em um espaço euclidiano contínuo, podemos encontrar associações entre elas. Para compreender o que é espaço euclidiano recomendamos nosso curso de Matemática Para Data Science.

Então, dependendo da tarefa, podemos empurrar os embedding de palavras para mais longe ou mantê-los juntos.

Idealmente, um embedding captura a semântica da entrada, colocando entradas semanticamente semelhantes próximas no espaço de embedding.

Na linguagem natural, podemos encontrar significados de palavras semelhantes ou até mesmo estruturas sintáticas semelhantes (ou seja, os objetos são agrupados). Em qualquer caso, quando você os projeta no espaço 2D ou 3D, você pode identificar visualmente alguns clusters.

Para obter um exemplo prático das word embeddings, experimente brincar com este notebook fornecido pela equipe do TensorFlow.

Seguindo em frente, criaremos um truque para fornecer alguma noção de ordem no conjunto.

Codificações Posicionais

Ao converter uma sequência em um conjunto (tokenização), você perde a noção de ordem.

Você consegue encontrar a ordem das palavras (tokens) na sequência: “Hello, I love you”? Provavelmente sim! Mas e quanto a 30 palavras não ordenadas?

Lembre-se de que o aprendizado de máquina envolve escala. A rede neural certamente não consegue entender nenhuma ordem em um conjunto.

Uma vez que os Transformadores processam sequências como conjuntos, eles são, em teoria, invariantes de permutação.

Vamos ajudá-los a ter um senso de ordem alterando ligeiramente os embeddings com base na posição. Oficialmente, a codificação posicional é um conjunto de pequenas constantes, que são adicionadas ao vetor de embeddings de palavras antes da primeira camada de auto-atenção.

Portanto, se a mesma palavra aparecer em uma posição diferente, a representação real será um pouco diferente, dependendo de onde ela aparece na frase de entrada.

input-processing-tokenization-embedding

No artigo original do Transformador, os autores criaram a função senoidal para a codificação posicional. A função seno diz ao modelo para prestar atenção a um determinado comprimento de onda lambda λ.

Em nosso caso, o lambda λ será dependente da posição na frase e i é usado para distinguir entre posições ímpares e pares. Matematicamente:

formula

Isso contrasta com os modelos recorrentes, em que temos uma ordem, mas estamos lutando para prestar atenção aos tokens que não estão próximos o suficiente.

Agora podemos passar para a Parte 2. Até o próximo capítulo.

Os Transformadores estarão presentes na Formação IA Aplicada ao Direito, que será lançada em breve na DSA.

Referências:

Attention Is All You Need

Deep Learning Para Aplicações de IA com PyTorch e Lightning

Processamento de Linguagem Natural

The Illustrated Transformer

Understanding Attention In Deep Learning

How Transformers work in deep learning and NLP: an intuitive introduction

Deep Learning Book

Capítulo 85 – Transformadores – O Estado da Arte em Processamento de Linguagem Natural

by

Transformadores (Transformers) representam uma arquitetura de Deep Learning que visa resolver tarefas sequence-to-sequence enquanto lida com dependências de longo alcance com facilidade. Esse não é um conceito fácil de compreender, mas ajudaremos você!

Vamos iniciar agora uma sequência de capítulos baseados no artigo: Attention is All You Need (Atenção é tudo que você precisa). E os autores não estavam brincando quando escolheram esse título porque você vai precisar de toda a atenção à sua disposição para isso. Mas não deixe isso te assustar, pois vai valer a pena!

O famoso paper “Atenção é tudo que você precisa” de 2017 mudou a forma como pensávamos sobre a atenção. Com dados suficientes, multiplicações de matrizes, camadas lineares e normalização de camadas, podemos realizar a tradução automática de texto de última geração, desejo antigo de quem trabalha com Inteligência Artificial.

No entanto, 2020 foi definitivamente o ano dos transformadores! Da linguagem natural agora eles estão em tarefas de visão computacional. Como passamos da atenção para a auto-atenção? Por que o transformador funciona tão bem? Quais são os componentes críticos para seu sucesso?

Acompanhe essa sequência de capítulos e descubra! Vamos começar com algumas definições.

O Que é um Transformador?

O Transformer em Processamento de Linguagem Natural é uma arquitetura que visa resolver tarefas sequence-to-sequence enquanto lida com dependências de longo alcance com facilidade. Ele se baseia inteiramente na autoatenção (Self-Attention) para computar as representações de sua entrada e saída SEM usar RNNs (Redes Neurais Recorrentes) alinhadas em sequência ou convolução. ?

O Transformer é um modelo de aprendizado profundo introduzido em 2017 que utiliza o mecanismo de atenção, pesando a influência de diferentes partes dos dados de entrada. É usado principalmente no campo do Processamento de Linguagem Natural (PLN), mas pesquisas recentes também desenvolveram sua aplicação em outras tarefas, como compreensão de vídeo. 

Assim como as Redes Neurais Recorrentes (RNNs), os transformadores são projetados para lidar com dados de entrada sequenciais, como linguagem natural, para tarefas como tradução e resumo de texto. No entanto, ao contrário das RNNs, os Transformers não exigem que os dados sequenciais sejam processados ​​em ordem. Em vez disso, a operação de atenção identifica o contexto para qualquer posição na sequência de entrada. Por exemplo, se os dados de entrada forem uma frase em linguagem natural, o Transformer não precisará processar o início antes do final. Em vez disso, identifica o contexto que confere significado a uma palavra na frase. Devido a esse recurso, o Transformer permite muito mais paralelização do que RNNs e, portanto, reduz os tempos de treinamento. 

Os transformadores rapidamente se tornaram o modelo de escolha para problemas de PLN, substituindo modelos de rede neural recorrente, como a LSTM (Long-Short Term Memory). Como o modelo Transformer facilita a paralelização durante o treinamento, ele permite o treinamento em conjuntos de dados maiores do que era possível antes de ser introduzido. Isso levou ao desenvolvimento de sistemas pré-treinados, como BERT (Bidirectional Encoder Representations from Transformers) e GPT (Generative Pre-Training Transformer), que foram treinados com enormes conjuntos de dados de linguagem geral, como Wikipedia Corpus e Common Crawl, e podem ser ajustado para tarefas específicas de linguagem. 

Um Pouco de História

Antes da introdução dos Transformers, a maioria dos sistemas de PLN de última geração contava com redes neurais recorrentes (RNNs), como LSTMs e unidades recorrentes com portas (GRUs), com mecanismos de atenção adicionais. O Transformer foi construído sobre essas tecnologias de atenção sem usar uma estrutura RNN, destacando o fato de que os mecanismos de atenção por si só, sem processamento sequencial recorrente, são poderosos o suficiente para atingir o desempenho das RNNs com atenção.

Conforme vimos em capítulos anteriores deste livro, as RNNs processam tokens sequencialmente, mantendo um vetor de estado que contém uma representação dos dados vistos após cada token. Para processar o token, o modelo combina o estado que representa a frase até o token com a informação do novo token para criar um novo estado, representando a frase até o token. Teoricamente, as informações de um token podem se propagar arbitrariamente ao longo da sequência, se em todos os pontos o estado continuar a codificar informações contextuais sobre o token. Mas, na prática, esse mecanismo é imperfeito: devido em parte ao problema do desaparecimento do gradiente, o estado do modelo no final de uma frase longa geralmente não contém informações precisas e extraíveis sobre os tokens anteriores.

Este problema foi resolvido com a introdução de mecanismos de atenção. Os mecanismos de atenção permitem que um modelo olhe diretamente e se baseie no estado em qualquer ponto anterior ao longo da frase. A camada de atenção pode acessar todos os estados anteriores e pesá-los de acordo com alguma medida de relevância aprendida para o token atual, fornecendo informações mais nítidas sobre tokens relevantes distantes. Um exemplo claro da utilidade da atenção está na tradução, onde o contexto é fundamental para determinar o significado de uma palavra em uma frase. Em um sistema de tradução de inglês para francês, a primeira palavra da saída em francês provavelmente depende muito do início da entrada em inglês.

No entanto, em um modelo LSTM de codificador-decodificador clássico, para produzir a primeira palavra da saída em francês, o modelo recebe apenas o vetor de estado da última palavra em inglês. Teoricamente, esse vetor pode codificar informações sobre toda a frase em inglês, fornecendo ao modelo todo o conhecimento necessário, mas na prática essas informações muitas vezes não são bem preservadas. Se um mecanismo de atenção for introduzido, o modelo pode, em vez disso, aprender os estados dos primeiros tokens em inglês ao produzir o início da saída em francês, dando a ele um conceito muito melhor do que está traduzindo.

Quando adicionados às RNNs, os mecanismos de atenção levaram a grandes ganhos no desempenho. A introdução do Transformer trouxe à luz o fato de que os mecanismos de atenção eram poderosos em si mesmos, e que o processamento sequencial recorrente dos dados não era necessário para alcançar os ganhos de desempenho das RNNs com atenção. O Transformer usa um mecanismo de atenção sem ser uma RNN, processando todos os tokens ao mesmo tempo e calculando pesos de atenção entre eles. O fato de que os Transformers não dependem do processamento sequencial e se prestam muito facilmente à paralelização permite que os Transformers sejam treinados com mais eficiência em conjuntos de dados maiores.

Arquitetura

Como os modelos inventados antes dele, o Transformer é uma arquitetura codificador-decodificador. O codificador consiste em um conjunto de camadas de codificação que processa a entrada iterativamente uma camada após a outra e o decodificador consiste em um conjunto de camadas de decodificação que fazem a mesma coisa com a saída do codificador.

A função de cada camada do codificador é processar sua entrada para gerar codificações, contendo informações sobre quais partes das entradas são relevantes entre si. Ele passa seu conjunto de codificações para a próxima camada do codificador como entradas. Cada camada decodificadora faz o oposto, pegando todas as codificações e as processando, usando suas informações contextuais incorporadas para gerar uma sequência de saída.

Para conseguir isso, cada codificador e camada de decodificador faz uso de um mecanismo de atenção, que para cada entrada, pesa a relevância de todas as outras entradas e extrai informações delas de acordo para produzir a saída. Cada camada do decodificador também tem um mecanismo de atenção adicional que extrai informações das saídas dos decodificadores anteriores, antes que a camada do decodificador extraia informações das codificações. Ambas as camadas do codificador e do decodificador têm uma rede neural feed-forward para processamento adicional das saídas e contêm conexões residuais e etapas de normalização de camada.

Como dito no início, não é um conceito trivial e recomendamos a leitura dos capítulos anteriores deste livro se quiser realmente compreender como chegamos aos Transformadores.

Nos próximos capítulos vamos estudar os Transformadores em mais detalhes e se quiser aplicar os Transformadores na prática, eles são abordados em quase todos os cursos da Formação Engenheiro de IA.

Até o próximo capítulo.

Referências:

Processamento de Linguagem Natural com Transformers

Attention Is All You Need

The Illustrated Transformer

Understanding Attention In Deep Learning

How Transformers work in deep learning and NLP: an intuitive introduction

Deep Learning Book

Capítulo 83 – Liquid Neural Network – Rede Neural Líquida

by

A partir deste capítulo vamos estudar algumas arquiteturas de Deep Learning bem modernas e recentes que começam a ganhar destaque à medida que a Inteligência Artificial evolui. Muitas dessas arquiteturas são variações das arquiteturas estudada ao longo deste livro. Vale ressaltar, que boa parte do que vimos neste livro surgiu ou evoluiu nos últimos 5 anos. Ou seja, estamos fazendo parte dessa história. Isso não é formidável?

Vejamos o que são as Liquid Neural Networks e como elas podem revolucionar o diagnóstico médico e a direção autônoma de veículos.

Os pesquisadores do MIT desenvolveram um tipo de rede neural que aprende on-the-fly, não apenas durante sua fase de treinamento. Esses algoritmos flexíveis, chamados de redes neurais “líquidas”, mudam suas equações para se adaptar continuamente a novas entradas de dados. O avanço pode ajudar na tomada de decisões com base em fluxos de dados que mudam com o tempo, incluindo aqueles envolvidos no diagnóstico médico e na direção autônoma de veículos.

“Este é um caminho a seguir para o futuro do controle de robôs, processamento de linguagem natural, processamento de vídeo e qualquer forma de processamento de dados de série temporal”, diz Ramin Hasani, o principal autor do estudo. “O potencial é realmente significativo”.

Machine-learning

A pesquisa será apresentada na Conferência AAAI sobre Inteligência Artificial em Fevereiro/2021. Além de Hasani, um pós-doutorado no Laboratório de Ciência da Computação e Inteligência Artificial do MIT (CSAIL), os co-autores do MIT incluem Daniela Rus, diretora do CSAIL e o Professor Andrew e Erna Viterbi de Engenharia Elétrica e Ciência da Computação e aluno de PhD Alexander Amini. Outros co-autores incluem Mathias Lechner do Instituto de Ciência e Tecnologia da Áustria e Radu Grosu da Universidade de Tecnologia de Viena.

Os dados de séries temporais são onipresentes e vitais para nossa compreensão do mundo, de acordo com Hasani. “O mundo real tem tudo a ver com sequências. Mesmo nossa percepção – você não está percebendo imagens, está percebendo sequências de imagens”, diz ele. “Portanto, os dados de série temporal realmente criam nossa realidade.”

Ele aponta os aplicativos de processamento de vídeo, dados financeiros e diagnósticos médicos como exemplos de séries temporais fundamentais para a sociedade. As vicissitudes desses fluxos de dados em constante mudança podem ser imprevisíveis. Ainda assim, analisar esses dados em tempo real e usá-los para antecipar comportamento futuro, pode impulsionar o desenvolvimento de tecnologias emergentes, como carros autônomos. Então, Hasani construiu um algoritmo adequado para a tarefa.

Caso queira aprender a aplicar modelos de Machine Learning a fluxos de dados em tempo real, mostramos isso em detalhes no curso Big Data Real-Time Analytics com Python e Spark.

Hasani projetou uma rede neural que pode se adaptar à variabilidade dos sistemas do mundo real. Redes neurais são algoritmos que reconhecem padrões por meio da análise de um conjunto de exemplos de “treinamento”. Costuma-se dizer que elas imitam as vias de processamento do cérebro – definiu Hasani. “Uma rede neural artificial pode ter apenas 302 neurônios em seu sistema nervoso”, diz ele, “mas pode gerar uma dinâmica inesperadamente complexa.”

Hasani codificou sua rede neural com atenção especial ao comportamento dos neurônios que se ativam e se comunicam entre si por meio de impulsos elétricos. Nas equações que usou para estruturar sua rede neural, ele permitiu que os parâmetros mudassem ao longo do tempo com base nos resultados de um conjunto de equações diferenciais.

Essa flexibilidade é fundamental. O comportamento da maioria das redes neurais é corrigido após a fase de treinamento, o que significa que não conseguem se ajustar às mudanças no fluxo de dados de entrada. Hasani diz que a fluidez de sua rede “líquida” a torna mais resistente a imprevistos ou dados barulhentos, como se a chuva forte obscurecesse a visão de uma câmera em um carro que dirige sozinho. “Portanto, é mais robusto”, diz ele.

Há outra vantagem da flexibilidade da rede, ele acrescenta: “É mais interpretável”.

Hasani diz que sua rede líquida contorna a inescrutabilidade comum a outras redes neurais. “Apenas mudando a representação de um neurônio”, o que Hasani fez com as equações diferenciais, “você pode realmente explorar alguns graus de complexidade que não poderia explorar de outra forma.” Para o pequeno número de neurônios altamente expressivos de Hasani, é mais fácil examinar a “caixa preta” da tomada de decisão da rede e diagnosticar por que a rede fez uma certa caracterização.

“O modelo em si é mais rico em termos de expressividade”, diz Hasani. Isso pode ajudar os engenheiros a entender e melhorar o desempenho da rede.

A rede de Hasani se destacou em uma bateria de testes. Ela superou outros algoritmos de série temporal de última geração em alguns pontos percentuais ao prever com precisão valores futuros em conjuntos de dados, que vão desde a química atmosférica até os padrões de tráfego. “Em muitas aplicações vemos que o desempenho é confiavelmente alto “, diz ele. Além disso, o pequeno tamanho da rede significava permitiu que os testes fossem concluídos sem um alto custo de computação. “Todo mundo fala em aumentar a escala de sua rede”, diz Hasani. “Queremos diminuir, ter menos nós, porém mais ricos.”

Hasani planeja continuar melhorando o sistema e prepará-lo para aplicação industrial. “Temos uma rede neural comprovadamente mais expressiva, inspirada na natureza. Mas este é apenas o começo do processo”, diz ele. “A questão óbvia é como fazer para estender isso? Achamos que este tipo de rede poderia ser um elemento-chave dos sistemas de inteligência futuros.”

Esta pesquisa foi financiada, em parte, pela Boeing, a National Science Foundation, o Austrian Science Fund e Electronic Components and Systems for European Leadership.

No próximo capítulo estudamos um pouco mais essa arquitetura. Até lá.

Referências:

Why is ‘Liquid’ Neural Network From MIT a Revolutionary Innovation?

“Liquid” machine-learning system adapts to changing conditions

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:

Processamento de Linguagem Natural com Transformers

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:

Formação Machine Learning

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 Machine Learning

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 Inteligência Artificial Aplicada a Finanças, 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:

Formação Machine Learning

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

Paginação de posts

  • Previous
  • 1
  • 2
  • 3
  • Next

Capítulos Recentes

  • Capítulo 35 – A Matemática do Problema de Dissipação do Gradiente em Deep Learning
  • Capítulo 34 – O Problema da Dissipação do Gradiente
  • Capítulo 33 – Por que as Redes Neurais Profundas São Difíceis de Treinar?
  • Capítulo 32 – Como Uma Rede Neural Artificial Encontra a Aproximação de Uma Função
  • Capítulo 31 – As Redes Neurais Artificiais Podem Computar Qualquer Função?
  • Capítulo 30 – Variações do Stochastic Gradient Descent – Hessian Optimization e Momentum
  • Capítulo 29 – Definindo o Tamanho do Mini-Batch
  • Capítulo 28 – Usando Early Stopping Para Definir o Número de Épocas de Treinamento
  • Capítulo 27 – A Taxa de Aprendizado de Uma Rede Neural
  • Capítulo 26 – Como Escolher os Hiperparâmetros de Uma Rede Neural

Buscar

Podcast DSA

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