Machine Learning Yearning — Lições aprendidas para criar modelos da forma correta.

Como tomar melhores decisões de Machine Learning e ajudar seu time a caminhar mais rápido e na direção correta.

Letícia Mendonça Carraro
Kompa Data & AI
23 min readNov 18, 2021

--

Fonte Imagem

Sabemos que Machine Learning está na base de diversas aplicações essenciais como web search, anti spam, speech recognition, recomendação de produtos e muito mais. Aqui na Kompa, utilizamos ativamente Machine Learning a fim de promover mais eficiência na prática da medicina e facilitar o acesso a nossos serviços de saúde. Um bom exemplo é a Karmen, nossa assistente virtual! Ela é um chatbot feito in-house com tecnologias open source, baseada nas mais recentes arquiteturas de NLP (Transformers). Uma lição que aprendemos desenvolvendo a Karmen é a necessidade de avaliar e testar a robustez e vieses de nossos modelos, de forma fácil e consistente. Muitas das lições aprendidas e aplicadas saíram do livro Machine Learning Yearning, as quais trazemos para você nesse post. Elas são aplicáveis a todo modelo/produto de dados e machine learning, e fundamentais para garantir o bom funcionamento deles. Boa leitura!

Introdução

Esse texto traz algumas das ideias abordadas no livro Machine Learning Yearning, escrito pelo Professor Andrew NG. O objetivo do livro consiste em passar estratégias de time, bem como noções sobre como montar sets de desenvolvimento de dev/test. Além disso, o livro se propõe a fornecer uma visão ampla a respeito de como esses sets tem mudado devido ao aumento do tamanho dos datasets e quais procedimentos devem ser seguidos em projetos modernos de machine learning. De nenhuma forma a leitura desse post substitui a leitura do livro, mas se você está interessado em saber um pouco mais sobre do que se trata o livro, de uma maneira mais introdutória, esse é o post para você!

Nosso objetivo — Começando com um exemplo

Logo de início, nos é proposto um exercício de imaginação! Imagine então, que nós fazemos parte de uma startup que busca fornecer uma fonte interminável de fotos de gatinhos!

Nesse cenário, decidimos que para alcançar o nosso objetivo com o nosso FotoDeGatinhosGenerator deveríamos construir um modelo utilizando uma rede neural que fosse capaz de reconhecer gatinhos em fotos, utilizando visão computacional. Parece um bom caminho a ser seguido, certo? Mas, infelizmente, após a implementação, a acurácia do nosso algoritmo se mostra bem ruim. Não podemos deixar nossos amantes de gatinhos esperando!! Como podemos então melhorar o desempenho desse modelo?

Nosso time pode ter diversas ideias, desde a mais óbvia de arrecadar mais dados: mais foto de gatinhos, coletar um dataset mais diverso, com fotos de gatinhos em diferentes posições, com configurações de câmera diferente. Podemos tentar também melhorar o algoritmo treinando o modelo por mais tempo, utilizando mais gradientes, talvez escolher outra arquitetura, são diversas escolhas…

Se escolhermos corretamente podemos melhorar muito o desempenho do FotoDeGatinhosGenerator, mas uma escolha errada pode desperdiçar semanas, meses de tempo que não temos. O objetivo desse post é deixar essa decisão mais clara e fácil.

É claro que as duas maneiras mais confiáveis de se melhorar o desempenho de um algoritmo que são bem conhecidas hoje em dia consistem em:

  1. treinar uma network com mais parâmetros, "mais potente"
  2. obter mais dados.

Essas duas soluções, apesar de clássicas, não são tão facilmente obtidas e existem limitações acerca do poder computacional necessário para obter 1e 2. Vamos, então, discutir como (e quando) aplicar e analisar essas soluções.

Datasets

Configurando dev/test sets

Ainda no exemplo da nossa Startup FotosDeGatinhoGenerator, vamos supor que nossos usuários possam fazer upload de foto de gatos no nosso app. É importante então, sabermos reconhecer gatos nessas fotos, já que as pessoas podem fazer upload de fotos de muitas coisas diferentes e queremos apenas as fotos que contém gatinhos.

Pensando nisso, nosso time consegue obter uma base de dados de fotos contendo gatinhos (exemplos positivos) e não contendo gatinhos (exemplos negativos). Como a convenção dita, decidimos dividir nosso dataset em 70/30 para treino e teste e… Uau! Nosso modelo funciona muito bem na base de teste! Hora de levar para a produção, onde temos uma surpresa desagradável: a nossa performance está horrível. O que pode ter acontecido ai?

Vamos pensar um pouco, primeiro, o tipo de fotos de gatos que temos no nosso dataset são fotos de gatos com alta qualidade, advindas da web! Já as fotos que estão sendo recebidas no nosso aplicativo, normalmente são fotos de celular, talvez tremidas, borradas, de maneira geral, possuindo qualidade inferior.

Percebemos então, que essa divisão 70/30 não é tão boa assim quando nossa base de treino é diferente dos dados com que vamos lidar realmente. Portanto, é importante definir uma forma diferente de divisão dos nossos dados:

  • Training set: onde a gente roda nosso algoritmo, onde o treino realmente acontece.
  • Dev (desenvolvimento) set: Esse a gente usa para avaliar e "tunar" os parâmetros, selecionar as features e tomar outras decisões a respeito do modelo
  • Test set: Por fim, precisamos de um set para avaliar a performance do algoritmo fora de nosso loop de treino-avaliação. Note que esse set não deve ser utilizado para tomar decisões relacionadas à qual algoritmo ou parâmetros usar

O objetivo dos sets de dev e teste é direcionar o time para as mudanças mais importantes a serem feitas no nosso sistema de machine learning. Sendo assim precisamos escolher dev e test sets que refletem os dados que esperamos possuir no futuro.

Além disso, antes de colocar nosso FotoDeGatinhosGenerator em produção, podemos tentar replicar os dados que esperamos obter no futuro, tirando fotos de gatos nós mesmos! Dessa forma, podemos atualizar nossas bases de treinamento com os dados fornecidos pelos usuários, conforme lançamos nosso aplicativo.

Já deve ter ficado claro que é muito importante que os nossos dev e test sets venham do mesmo lugar (ou seja, da mesma distribuição. Não somente isso, mas que esses sets sejam representativos da distribuição de dados do mundo real ao qual o modelo será de fato exposto). Podemos pensar ainda uma em uma outra aplicação em que esse tipo de necessidade se torna evidente: imagine que você possua uma aplicação de speech-to-text, que faça o reconhecimento de voz e que tenha usuários tanto do Sul do Brasil, quanto do Nordeste. Caso você decida utilizar apenas os dados do Nordeste para treinar seu modelo, sua performance não vai ser boa no sul e vice versa. Então se você quer performar bem nessas duas localidades, seus sets devem possuir dados de ambas e não apenas de uma.

Escolha dev e test sets que refletem os dados que você espera obter no futuro e queira ter uma boa performance.

E quanto ao tamanho do meu dataset?

O dev set deve ser grande o suficiente para eu ser capaz de detectar diferenças entre modelos. Se eu tenho um classificador X com 90% de acurácia e um Y com 90.1% então um dev set de 100 exemplos não vai conseguir detectar esse 0.1% de diferença. Para notar essa diferença precisaríamos de um dataset com 10000 exemplos.

Dependendo da aplicação pode ser interessante detectar diferenças até menores que 0.1%, por exemplo, aquelas que impactam diretamente no lucro da empresa, e para enxergar essa diferença tão pequena, é preciso ainda mais exemplos

Já o test set deve ser grande o suficiente para nos dar confiança da performance do nosso sistemas. Para pequenos datasets, de 100 até 10000 exemplos, 30% dos dados é uma convenção que funciona muito bem. Convenções a parte, é interessante que seus sets consigam expressar a mesa distribuição de dados e features da distribuição do mundo real, ao qual seu modelo precisará fazer predições. Lembra do Teorema Central do Limite? Então, ele precisa de volume para se fazer presente.

Contudo, vivemos na era de Big Data e as vezes lidamos com problemas que possuem bilhões de exemplos, nesse caso é importante notar que não há necessidade de exagerar no tamanho dos nossos dev/test sets. Basta que eles possuam tamanho suficiente para avaliar a performance dos nossos algoritmos.

Métricas

Porque é importante definir e entender métricas

Geralmente quando abordamos um problema novo, encontramos dificuldades determinando qual a melhor solução. Para chegar em possíveis soluções, é normal passar por um processo iterativo, começando com uma ideia, depois a implementando em código, e fazendo um experimento para ajudar a determinar se a ideia funciona ou não.

Quanto mais rápido passamos por esse processo, mais rápido chegamos à nossa solução desejada, por isso ter, além de dev/test sets, métricas bem definidas é importante. O processo é mais rápido e você vai para a direção certa mais rapidamente.

Suponha que, na startup FotosDeGatinhosGenerator, não temos uma métrica específica. Dessa forma, toda vez que nosso time desenvolver um novo classificador de gatos, precisaríamos incorporá-lo no app e mexer nele por algumas horas para ter uma noção se o novo classificador foi uma melhoria ou não. Além disso, se a melhoria de acurácia do classificador foi de 0.1% você pode nem ser capaz de notar! Mesmo assim, de grão em grão a galinha enche o papo, e acumulando de 0.1% em 0.1% podemos atingir grandes progressos! Dessa forma, fica evidente que é extremamente necessário definir uma métrica para detectar rapidamente se as ideias que estamos tendo vão nos ajudar ou não. Inclusive, definir métricas e o que você entende como "sucesso de experimento" é a primeira coisa a se fazer num projeto de modelagem de dados.

Definindo métricas

Estabeleça apenas uma métrica para o seu time focar em otimizar

Possuir apenas uma métrica para avaliar, como por exemplo acurácia. É muito mais fácil escolher entre modelos e comparar performances quando utilizamos apenas uma métrica. Por exemplo, caso estejamos interessados na acurácia no nosso classificador de gatinhos e temos um Classificador A que nos fornece 90% de acurácia e um Classificador B com 92% de acurácia, é evidente que o Classificador B é superior.

Já se estamos interessados na Precisão e no Recall, precisamos buscar um método de combinar as duas métricas, como por exemplo, F1 score.

Fonte: Livro

É extremamente importante que, ao comparar algoritmos, tenhamos apenas uma métrica de avaliação. Dessa forma, a nossa habilidade de tomar decisões é acelerada e melhorada, evidenciando com maior facilidade o caminho que devemos seguir.

Métricas otimizadas e satisfatórias

Ainda temos uma outra maneira de juntar métricas. Imagine que nos importamos tanto com o tempo de execução quanto com a acurácia do algoritmo. Nesse caso, o que pode ser feito é primeiro definir o que é considerada uma métrica aceitável para determinada categoria e depois comparar-se a outra.

Fonte: Livro

Estabelecendo que o tempo de execução aceitável como sendo 100ms, por exemplo, nos deixa livres para comparar os algoritmos a partir de sua acurácia.

O importante aqui é que exista apenas uma métrica que vai ser utilizada para comparar os algoritmos. Caso seja interessante utilizar mais de uma métrica na avaliação, deve-se tentar combina-las de alguma forma ou estabelecer um métrica aceitável e comparar a outra.

E quando devo mudar esses dev/test sets e métricas?

De maneira geral, as nossas primeiras escolhas são feitas de maneira rápida e no decorrer do projeto podemos perceber que as decisões que fizemos no início não foram as melhores. Dessa forma, se em qualquer momento você perceber que os seus sets de dev/test ou a métrica escolhida não estão funcionando bem, mude rapidamente!

Existem alguns motivos pelos quais as suas escolhas podem não ter sido as melhores, os principais são:

  1. Os dados nos quais você precisa ir bem é diferente daqueles dos sets de dev/test: Imagine que na FotosDeGatinhosGenerator os dev/test são compostos majoritariamente de fotos de gatos adultos, porém as fotos que os usuários estão upando são fotos de gatinhos filhotes. Nesse caso, é preciso atualizar os seus dev/test sets para que eles sejam mais representativos.
  2. Overfit no dev set: O processo de repetidamente avaliar as suas ideias no dev set pode faze com que seu algoritmo lentamente fique "overfittado" no dev set. Isso se torna perceptível quando a performance no dev set é muito melhor que no test set. Nesse caso, você precisa se um novo dev set.
  3. A métrica escolhida está medindo outra coisa que não é o que o projeto precisa otimizar: Suponha que na FotosDeGatinhosGenerator a métrica utilizada é acurácia do classificador de gatos e essa acurácia me diz que o classificador A é melhor que o classificador B. Mas você nota que o classificador A ocasionalmente deixa passar imagens desagradáveis, mesmo que ele seja mais acurado, a impressão negativa deixada por esse classificador é inaceitável. Nesse caso, a métrica escolhida está falhando em escolher o classificador que melhor se encaixa na sua aplicação, ou seja, é hora de mudar a métrica.

Análise de erro

Quando iniciamos um novo projeto, especialmente numa área que não somos experts, é difícil determinar qual a melhor direção a ser seguida. Portanto não precisamos e nem devemos tentar desenhar e construir um sistema perfeito. Ao invés disso, devemos desenvolver o sistema mais básico, o mais rápido possível. A partir daí, podemos utilizar a análise de erro para nos guiar na direção correta e melhorar nosso algoritmo.

Primeiros passos — O que é análise de erro

Imagine que uma pessoa do seu time, ao analisar os logs do FotosDeGatinhoGenerator, percebe que o classificador está identificando alguns cachorros como gatos. Existem realmente alguns cachorros que parecem com gatos. Essa pessoa, encontrou uma solução para esse problema que vai levar cerca de um mês para ser implementada e te pergunta se deve seguir em frente com isso.

Será que essa pessoa deveria colocar sua energia nisso ?

A primeira pergunta que eu devo me fazer aqui é: quanto essa mudança vai realmente melhorar o desempenho do aplicativo? E para responde-la posso pegar um dev set com cerca de 100 exemplos de erros que o Classificador de gatinhos cometeu, de todos os tipos não apenas a confusão cachorro-gatinho, e manualmente verificar quantos destes são devido à classificação incorreta de cachorros como gatos.

Agora, com os meus erros em mãos, se eu perceber que os erros gerados pela confusão entre cachorros e gatos são apenas 5% dos meus erros totais, então o máximo de erro que vou conseguir tirar do meu modelo vão ser esses 5%. Já se eu verificar que das imagens classificadas erroneamente, 50% dos meus erros são devido à classificação de cachorros como gatos, vou obter uma mudança significativa na acurácia do modelo ao melhorar esse aspecto.

Análise de erros se refere justamente a esse processo de examinar o meu dev set de exemplos que o meu modelo classificou de maneira incorreta para entender melhor as causas desses erros. Essa análise vai me ajudar a entender o que priorizar, onde é preciso mais foco e quais problemas são mais urgentes de serem resolvidos.

Eyeball e Blackbox dev sets — Uma alternativa para grandes datasets

Perceba que quando estamos analisando o nosso dev set e corrigindo os erros manualmente podemos acabar "overfittando" o modelo para os nossos dados. Esse problema pode ser melhor resolvido caso eu possua um dataset grande, com a técnica de eyeball set e blackbox set.

Essa técnica funciona da seguinte forma: Primeiro, subdivide-se o dataset em dois subsets, um que eu olho, o eyeball set, e outro que eu não olho, o blackbox set. Será através do blackbox dev set que serão feitos os ajustes dos parâmetros do modelo. Caso a performance no eyeball set seja muito melhor que no blackbox devset quer dizer que o modelo foi "overfittado" no eyeball set e para corrigir é necessário inserir mais dados no set. Explicitar esses dois sets me auxilia a perceber quando o meu processo de análise de erro manual está "overfittando" o modelo.

O tamanho do eyeball dev set deve ser grande o suficiente pra que o modelo erre o suficiente para a análise, 10 erros é pouco, 20 erros começa-se a ter uma noção, 50 erros já temos uma boa noção e com 100 erros já temos uma estimativa muito boa das fontes dos nossos erros. Já o blackbox dev set, 1000 a 10000 exemplos já é o suficiente para a maioria das aplicações.

Caso o dataset não seja grande o suficiente para dividir, usamos o dev set inteiro com eyeball set com análise de erro, seleção de modelo e tuning de hiperparâmetro.

Bias e variance

Imagine que nós tenhamos dev e test sets que venham da mesma distribuição. O próximo passo sempre deve ser conseguir mais dados, não é mesmo? Errado! Obviamente, obter mais dados não vai nos atrapalhar, mas infelizmente nem sempre vai nos ajudar tanto quanto esperado e as vezes pode ser até perda de temo correr atrás disso. Agora nosso foco é conseguir identificar quando buscar mais dados.

Existem duas grandes fontes de erros em Machine Learning: bias e variance.

Bias: Refere-se ao erro do algoritmo na base de treino

Variance: O quão pior o algoritmo tem no dev ou test set em comparação com o set de treino.

Para ficar claro, vale alguns exemplos.

Considere o FotosDeGatinhosGenerator. Idealmente ele deve ser capaz de identificar gatos em fotos como um humano. Suponha que atualmente o classificador possua a seguinte performance:

  • Erro de treino = 1%
  • Dev error = 11%

Que problema existe aqui? Aplicando a definição, nós conseguimos estimar a bias como 1% e a variance como 10% (11%-1%). Então esse modelo possui uma alta variance, isto é, o classificador possui pouco erro de treino, mas está falhando em generalizar o seu modelo. Aqui identificamos um modelo "overfittado".

Agora considere

  • Erro de treino = 15%
  • Dev error = 30%

Aqui, temos bias de 15% e a variance de 15% . Ou seja, um alto bias e uma alta variance. O modelo apresenta overfitting e underfitting ao mesmo tempo.

Por fim, imagine que você chegou aos seguintes valores com o seu modelo:

  • Erro de treino = 0.5%
  • Dev error = 1%

Olha só, esse modelo está indo muito bem, com baixo bias e baixa variance. Parabéns!!

Algo que devemos manter em mente é que, mesmo que queiramos aproximar o nosso classificador de um desempenho perfeito, temos que pensar no que consideramos um desempenho ideal. Por exemplo, podemos estar analisando nossa base de dados e perceber que 14% dos nossos dados são imagens tão borradas e ruidosas que as tornam não identificáveis. Nesse caso, o melhor que conseguiríamos obter do classificador de gatinhos seria um erro de aproximadamente 14%.

Nesse caso, caso tenhamos um classificador com as seguintes características:

  • Erro de treino = 15%
  • Dev error = 3%

Verificamos que a performance de treino já está próxima do erro ideal que seria de 14%, então não tem muito no que a melhorar no sentido de bias. Mas existe amplo espaço para eu melhorar o desempenho dela em erros relacionados a variance.

Unavoidable bias: Refere-se àquele erro que não pode-se reduzir , no exemplo citado, 14%.

Avoidable bias: É o bias que podemos corrigir, no caso do nosso exemplo é de 1%.

Tá, mas como a gente resolve esse erros?

A forma mais simples:

  • Se você tem um grande avoidable bias: aumente o tamanho do seu modelo, por exemplo adicionando mais layers e neurônios.
  • Se você tem alta variance, adicione dados ao seu training set.

Perceba entretanto, que essas soluções dependem da quantidade de dados e de poder computacional que você possui. Vamos ver outras maneiras de resolver esses problemas, mas para entende-las precisamos antes conversar um pouquinho mais sobre bias e variance.

Bias vs Variance Tradeoff

Imagine que você tem um problema para resolver e não sabe muito bem como prosseguir. Como uma primeira tentativa, você decide utilizar um modelo de regressão linear. Ao observar os resultados obtidos, você percebe que estes estão consistentes, porém consistentemente errados. Ao se questionar o motivo do comportamento do seu modelo você nota que a regressão linear assume que os dados estão numa reta, e isto que não é verdadeiro no seu projeto. Nesse caso, o modelo que você escolheu possui alto bias: ele assumiu muito sobre os dados e acabou com um problema de underfitting ao tentar simplificar de mais. Dada a falha na sua tentativa anterior, você decidiu utilizar um polinômio de grau 10000 para construir seu modelo. Contudo você percebe que ainda assim seu modelo não está com um desempenho satisfatório: os resultados estão dispersos e, apesar de as vezes seu modelo acertar, ele não está servindo muito bem para o seu propósito. Isto aconteceu pois ao construir seu modelo extremamente complexo, você acabou com uma variance altíssima e de brinde ganhou um problema de overfitting.

Observe que para diminuir o bias, precisamos aumentar a variance, e vice-versa. É assim que funciona a ligação entre Bias e Variance, eles estão intimamente conectados, e o modelo ideal é aquele que melhor harmoniza essa relação e reduz o erro. Esse problema é denominado de bias-variance tradeoff.

Agora que entendemos melhor bias e variance e temos noção da relação existente entre eles, podemos melhor entender as técnicas utilizadas para lidar-se com ambos.

Técnicas para reduzir avoidable bias

  • Aumentar o tamanho do modelo: Permite que você se ajuste melhor à sua base de treino. Se isso aumentar variance, pode-se usar regularização.
  • Modificar os features de entrada baseado no insights da análise de erro.
  • Reduzir ou eliminar regularização, tenha em mente que fazer isso vai ocasionar o aumento da variance.
  • Modificar a arquitetura do modelo

Note que adicionar mais dados pode ajudar com problemas de variance, mas não vai ter um efeito significativo no bias.

Técnicas para reduzir variance

  • Adicionar mais dados: é a maneira mais simples e confiável de se tratar variance.
  • Adicionar regularização, note que essa técnica aumenta bias.
  • Diminuir o tipo/número de input features, usando feature selection: Essa técnica pode ajudar a reduzir um pouco a variance, mas pode também aumentar o bias.É especialmente útil quando estamos lidando com datasets menores.
  • Diminuir o tamanho do modelo (número de neurônios/layers): USE COM CUIDADO. Essa técnica pode diminuir a variance e possivelmente aumentar bias, mas não é recomendada. Adicionar regularização já auxilia fortemente com a variance.
  • Modificar os features de entrada baseado no insights da análise de erro.
  • Modificar a arquitetura do modelo.

Learning Curves

Até agora a gente viu algumas maneiras de estimar que erros podem ser atribuídos para avoidable bias e para a variance. Para fazer isso, verificamos qual seria a nossa taxa de erro ótima, unavoidable bias, e computamos o erro do sets de treino e dev. Plotar learning curves vai nos ajudar na tarefa de estimar melhor erros.

Learning curves são curvas que plotam o erro do dev set em função do número de exemplos. Além disso, na curva podemos adicionar também a taxa de erro ótima que se quer atingir, o que seria considerado a performance ideal.

Analisando Learning Curves

Vamos analisar algumas learning curves e verificar que tipo de informação é possível extrair delas.

Fonte: Livro

Nesse caso é perceptível que a curva de dev error está diminuindo quanto mais dados temos, então adicionar mais dados ao dataset vai nos ajudar a chegar mais perto do comportamento ideal esperado.

Fonte: Livro

Já nessa segunda situação, a curva de erro de dev apresenta uma região plana e o aumento da quantidade de dados que oferecemos ao modelo impacta minimamente no desempenho do modelo, ou seja, adicionar mais dados não vai resolver o nosso problema e devemos buscar outra solução.

Mas além do erro no dev set também estamos interessados em saber o erro de treino, dessa forma consigo comparar a performance do meu modelo e verificar de forma visual bias e variance.

Fonte: Livro

Note que nesse caso, adicionar mais dados não vai ajudar a melhorar a performance do modelo, preciso buscar outras soluções. Além disso, perceba também que existe um grande avoidable bias, explicitado pela grande diferença entre o erro de treino e a performance esperada, e uma baixa variance, visível pela pequena distância entre o erro do dev set e o erro de treino.

Apesar das learning curves serem muito úteis e tornarem mais visual e interessante a nossa análise, elas exigem muito tempo e poder computacional, já que deve-se testar o seu modelo com diferentes volumes de dados.

Teste de verificação da otimização

Vamos voltar ao nosso FotoDeGatinhosGenerator, suponha que queremos implementar um assistente virtual gatinho que conversa com você quando estiver triste.

Fonte

Imagine que o sistema consiste em ter como entrada um áudio A, do qual obtemos uma pontuação Score_A para cada possível sentença de saída S. Podemos tentar estimar Score_A como Score_A=P(S|A). Mesmo após decidir de que maneira podemos calcular Score_A ainda temos que encontrar a frase que maximiza esse valor, ou seja, a frase com maior probabilidade de ser a frase certa.

Como a gente computa esse argmax? Talvez com um algoritmo de busca qualquer como beam search? Mas mesmos algoritmos de busca não são capazes de garantir que o valor de S que maximiza a equação vai ser encontrado.

Imagine que esse audio A tenha alguém falando “ Eu estou triste” mas a transcrição que obtemos é “ Eu estou feliz”. Temos duas possibilidades par o que pode ter acontecido de errado:

  • Problema no algoritmo de busca: Falhou em encontrar o valor de S que maximiza a equação.
  • Problema na função de pontuação: Nossas estimativas para calcular a pontuação não foram boas e falharam em reconhecer a frase corretamente.

Precisamos então definir qual foi a causa do problema para caminhar em direção a uma solução. Para fazer isso podemos utilizar o teste de verificação de otimização.

O teste funciona da seguinte forma:

  1. Chama-se de S_out a frase que eu obtive na saída do meu algoritmo e de S* a frase correta de saída. No caso do exemplo, S_out=”Eu estou feliz” e S*=”Eu estou triste”.
  2. Calcula-se e compara-se os Scores das duas frase:

Se Score(S*) > Score(S_out): O algoritmo de busca está falhando em encontrar a frase que maximiza a equação e seu foco deve ser melhora-lo.

Se Score(S*) Score(S_out): O problema está na maneira como a computação da pontuação está sendo feita e deve-se melhora-la.

Essa é uma técnica muito útil e pode ser generalizada para ser aplicada à diversas situações. Imagine que fomos capazes de evoluir tanto o FotosDeGatinhosGenerator que agora fazemos robôs de gatinhos! O primeiro passo é fazer esses robôs andarem e, para tal, implementamos um sistema de RL - Reinforcement Learning (Aprendizado por reforço). Como sabemos, RL trabalha com um sistema de recompensa. Imagine então que a gente cria esses valores de recompensa e iniciamos o treinamento do algoritmo. Mas logo percebemos que a performance do nosso algoritmo é muito ruim, os gatinhos robôs andam todos desengonçados, e apesar de fofo, não era esse o comportamento que estávamos esperando. Como podemos saber se o problema é o nosso sistema de recompensa ou o algoritmo em si? Para tal podemos tomar como base o comportamento de um gato real e compara-lo ao comportamento que seu colega robô está tendo. Tomando a trajetória que um gato normal faria T_gato e que o robô está fazendo T_out, basta observar R(T_humano) e R(T_out).

Pipelines

End-to-End learning

Vamos pensar em uma outra situação agora. Suponha que você esteja interessado em construir um sistema capaz de identificar avaliações positivas e negativas de um dado produto. Sendo assim, seu objetivo é reconhecer “Uau! Eu adorei esse produto, é um excelente produto!” como uma avaliação extremamente positiva e “Pior produto que eu já comprei, produto de baixíssima qualidade” como extremamente negativa.

Esse problema de reconhecer comentários positivos e comentários negativos é chamado de análise de sentimentos. Para construir esse sistema, você pode construir uma pipeline com dois componentes:

  1. Parser: Um sistema que classifica a classe das palavras e te ajuda a entender as palavras mais importantes do texto. Por exemplo quando estamos lidando com análise de sentimentos, é interessante identificar os substantivos e adjetivos de um texto.
  2. Classificador de sentimentos: Um algoritmo que recebe o texto anotado e faz a predição do sentimento. O parser consegue ajudar muito o algoritmo: ao atribuir um peso maior aos adjetivos, o algoritmo vai conseguir mais rapidamente identificar as palavras mais importantes como “excelente” e ignorar palavras menos significativas como “esse”.

Podemos visualizar a pipeline da seguinte forma:

A ideia de um sistema end-to-end é substituir essa pipeline por apenas um algoritmo de aprendizado, no exemplo, o algoritmo end-to-end recebe o texto original e devolve o texto analisado, sem passos a mais.

Dessa forma, algoritmos end-to-end recebem a entrada e devolvem a saída de maneira direta. Ao enfrentar problemas com abundância de dados esse tipo de solução possui uma taxa de sucesso muito alta, mas é claro que existem limitações.

Como escolher os componentes de uma pipeline

Imagine que queremos criar um carro autônomo. Nesse caso, nossa pipeline pode ser um pouco mais complicada:

Ela é composta de três componentes:

  • Detectar pessoas: detecta pedestres a partir de imagens das câmeras.
  • Detectar obstáculos: detecta carros e outros obstáculos a partir das imagens.
  • Planejar a rota do carro: Planeja a rota que o carro deve seguir de maneira evitar pedestres e outros carros.

Note que nesse caso é muito mais complicado, e não a melhor solução, um sistema end-to-end. Quando estamos interessados em construir um sistema end-to-end existem dois aspectos principais que devo levar em consideração:

Disponibilidade de dados: Não é difícil de se obter dados de pedestres e carros rotulados. Existem diversos datasets de visão computacional que possuem esses dados disponíveis. Contudo, caso você queira construir um sistema end-to-end…

… será muito mais difícil de encontrar dados de pares (Imagem, Direção de locomoção). Nesse caso, treinar um sistema end-to-end é uma tarefa muito mais trabalhosa e cara, quiça impossível.

Simplicidade das tarefas: Além de disponibilidade de dados, um outro fator a se levar em conta é o quão simples as tarefas são de serem resolvidas através de componentes individuais. Voltando ao FoosDeGatinhosGenerator, suponha que eu queira classificar uma raça específica de gatos. Posso construir a minha pipeline da seguinte forma, com uma arquitetura end-to-end:

Alternativamente, posso utilizar uma pipeline com dois componentes, no qual o primeiro componente identifica um gatinho na imagem e o segundo componente, identifica a sua raça:

Se o meu sistema recebe uma foto com mais de um gato, com a segunda pipeline, cada gatinho vai ser enviado de maneira individual para o classificador de raça. Perceba que nesse caso, os dois elementos da pipeline conseguem aprender com muito mais facilidade e com muito menos dados.

Em suma, ao decidir quais devem ser os elementos da pipeline, tente construir um pipeline em que cada elemento seja relativamente simples, que consiga aprender através de volumes pequenos de dados.

Análise de erro por partes

Vamos pensar no reconhecedor de gatos siameses, suponha que você queira melhorar a performance do sistema. Qual deve ser a parte da pipeline que você deve focar em melhorar? O primeiro passo para responder essa pergunta é entender à qual parte da pipeline devem ser atribuídos os erros.

Vamos analisar os passos que o algoritmo fez. A primeira parte, o detector de gatos, detecta e cota a parte da imagem que possui um gato. A segunda parte, o classificador de raça, decide então, se é um gato siamês ou não. Para entender qual componente da pipeline devo focar em melhorar, basta utilizar a análise de erro por partes.

A análise de erro por partes tem por objetivo atribuir os erros que estão sendo cometidos à partes específicas do sistema.

Dada uma pipeline genérica, como a mostrada acima, a análise de erros é feita da seguinte forma:

  1. Transformo manualmente a saída de A na saída perfeita, e rodo a pipeline. Caso o output obtido com essa alteração for o output coreto, quer dizer que se A fosse melhor resolveria o problema. Sendo assim, devo focar em melhorar o componente A, caso contrário, prosseguir para o segundo passo.
  2. Transformo manualmente a saída de B na saída perfeita, e rodo a pipeline. Se agora a saída obtida é a saída correta o problema está em B, então é ele que deve-se melhorar. Caso contrário, prossiga para o terceiro passo.
  3. O erro está no componente C.

Para exemplificar a análise de erros por partes, vamos voltar ao exemplo do meu classificador de raça de gatos. Suponha que ele receba de entrada a seguinte imagem:

Fonte: Livro

Com a pipeline:

Você nota que a sua pipeline não está produzindo a saída correta e decide executar a análise de erros por partes. Primeiro você passa a imagem do gatinho devidamente recortada para o seu algoritmo de classificação de raça. caso ele faça uma classificação correta, você percebe que a falha do seu sistema está na deteccção de gatos na foto. Contudo, caso a classificação ainda assim esteja errada, você pode concluir que o problema esta na classificação da raça.

Conclusões

Esse texto tinha por objetivo ser uma porta de entrada para que pudessem entender alguns conceitos básicos e extremamente importantes no desenvolvimento de algoritmos de aprendizado de máquina e eu espero que tenha conseguido atingi-lo! Lembrando que esse texto tem como base o livro Machine Learning Yearning, cuja leitura eu recomendo fortemente para qualquer profissional ou entusiasta da área de Machine Learning.

O time de IA e dados da Kompa Saúde está sempre em busca de trazer novos conhecimentos para aqueles interessados, então fique de olho que sempre tem novidade por aqui!

Se você gostou do texto não esqueça de dar claps e compartilhar com seus amigos e se não gostou, compartilhe com seus inimigos! Nos siga para ficar sabendo de primeira mão quando postarmos textos novos e tenha um ótimo dia!!

Referências

https://github.com/ajaymache/machine-learning-yearning/blob/master/full%20book/machine-learning-yearning.pdf

--

--