Pontos Principais
- Regressão logística é apropriada para classificação binária, usado quando o relacionamento entre as variáveis de entrada e saída que estamos tentando predizer é linear ou quando é importante para ser interpretada pelo modelo (por exemplo, isolando o impacto que qualquer variável de entrada tem na predição);
- Decision Tree e Random Forest são modelos não lineares que podem capturar os relacionamentos mais complexos, mas não são tão intuitivos a interpretação humana;
- É importante avaliar o desempenho do modelo apropriadamente para verificar se executará bem com novas amostras de dados;
- Produzir um modelo de aprendizado de máquinas envolve várias considerações diferentes do processo de desenvolvimento do modelo, por exemplo: como o modelo calcula as entradas assincronamente? Quais informações devem ser registradas cada vez que atingir um novo score? E como determinar o desempenho do modelo em produção?
O aprendizado de máquinas vem permitindo a criação de muitos produtos que interagimos diariamente como os assistentes "inteligentes" (tal como: o Siri da Apple e o Google Now), os mecanismos de recomendação como o da Amazon que sugerem novos produtos para comprar, os sistemas de classificação de anúncios usados pelo Google e Facebook. Mais recentemente, o aprendizado de máquinas entrou na consciência pública devido aos avanços na "aprendizagem profunda" - isto inclui a vitória que o AlphaGo teve contra Lee Sedol, jogador profissional de Go, e novos produtos com reconhecimento de imagens e tradução automatica.
Está série de artigos, fornece uma introdução para algumas técnicas poderosas, mas geralmente aplicadas no aprendizado de máquinas. Isso inclui o aprendizado profundo, mas também métodos mais tradicionais que atendem diversas necessidades de negócios. Depois de ler esta série, esperamos passar o conhecimento necessário para começar seus experimentos de aprendizado de máquinas em diversas áreas.
Este artigo do InfoQ faz parte da série "Uma introdução ao aprendizado de máquinas". É possível assinar para receber as notificações via RSS.
Está série vai explorar vários tópicos e técnicas de aprendizado de máquinas, possivelmente a área da tecnologia e ciência da computação mais comentada nos últimos anos. Aprendizado de máquinas de modo geral, já foi apresentado em alguns artigos do InfoQ (como o artigo: Começando com aprendizado de máquinas da série Trabalhando com Ciência de Dados), e neste artigo serão apresentados vários conceitos e métodos já discutidos, mas agora enfatizando com exemplos concretos, e incluindo redes neurais e aprendizado profundo.
Vamos começar detalhando um caso de uso em Python: como construir um modelo de aprendizado de máquinas para detectar fraude de cartão de crédito? (Apesar de usarmos o exemplo de detecção de fraudes, muito do que será apresentado pode ser aplicado com poucas modificações em outros problemas de classificação, tal como predição de cliques em propagandas). Entre os exemplo, serão apresentados várias ideias principais e termos de aprendizado de máquinas, incluindo regressão logística, decision tree e random forest, taxas de verdadeiro positivo e falso positivo, validação cruzada, curva ROC e AUC.
Problema: Fraude no cartão de crédito
As empresas que vendem produtos on-line inevitavelmente precisam lidar com fraude. Em uma transação fraudulenta típica, o fraudador obterá números de cartões de crédito roubados, que depois usa para comprar produtos on-line. Os fraudadores venderão esses produtos em outro lugar com desconto, embolsando o valor do produto, enquanto a empresa deve "estornar" o valor do produto. Mais detalhes sobre fraudes do cartão de crédito podem ser encontradas aqui.
Digamos que temos um negócio on-line que vem sofrendo fraude por algum tempo, e gostaríamos de usar o aprendizado de máquina para ajudar com o problema. Mais especificamente, cada vez que uma transação é feita, gostaríamos de prever se ela será ou não fraudulenta (ou seja, se o titular do cartão autorizado não foi quem faz a compra) para que possamos agir adequadamente. Esse tipo de problema de aprendizado de máquina é conhecido como classificação, pois estamos atribuindo cada pagamento recebido a uma de duas classes: fraude ou não fraude.
Para cada histórico de pagamento, temos um booleano que indica se a cobrança foi fraudulenta (fraudulent) e alguns outros atributos que achamos que podem ser indicativos de fraude, por exemplo, o valor do pagamento em dólares norte-americanos (amount), o país em que o cartão foi emitido (card_country) e o número de pagamentos feitos com o cartão em nosso negócio no último dia (card_use_24h). Assim, os dados que temos para construir o modelo preditivo são semelhantes ao seguinte CSV:
fraudulent,charge_time,amount,card_country,card_use_24h
False,2015-12-31T23:59:59Z,20484,US,0
False,2015-12-31T23:59:59Z,1211,US,0
False,2015-12-31T23:59:59Z,8396,US,1
False,2015-12-31T23:59:59Z,2359,US,0
False,2015-12-31T23:59:59Z,1480,US,3
False,2015-12-31T23:59:59Z,535,US,3
False,2015-12-31T23:59:59Z,1632,US,0
False,2015-12-31T23:59:59Z,10305,US,1
False,2015-12-31T23:59:59Z,2783,US,0
Há dois detalhes importantes que iremos ignorar em nossa discussão, mas vale a pena ter em mente, pois são tão importantes, senão mais do que os conceitos básicos de construção de modelos que estamos apresentando aqui.
Primeiro, há o problema da ciência dos dados de determinar quais características podemos considerar como indicativas de fraude. No nosso exemplo, identificamos o valor do pagamento, o país em que o cartão foi emitido e o número de vezes que o cartão foi usado no último dia como características que pensamos que podem ser úteis na previsão de fraude. Em geral, será necessário passar muito tempo olhando e obtendo dados para determinar o que é útil e o que não é.
Em segundo lugar, há o problema da infra-estrutura dos dados para calcular ou obter os valores das características: precisamos desses valores para todas as amostras históricas para treinar o modelo, mas também precisamos dos valores em tempo real à medida que os pagamentos chegam para avaliar as novas transações adequadamente. É improvável que, antes de começar a se preocupar com a fraude, você já estava registrando o número de usos do cartão de crédito nas últimas 24 horas, então se achar que essa característica é útil para detecção de fraude, será necessário calcular seu valor em produção e em lote. Dependendo da definição da característica, isso pode não ser trivial.
Esses problemas juntos são frequentemente referidos como engenharia de recursos e muitas vezes é a parte mais envolvida (e impactante) da aprendizagem de máquinas.
Regressão logística
Vamos começar com um dos modelos mais básicos, um linear. Vamos tentar encontrar os coeficientes a, b, … Z, tal que:
Para cada pagamento, vamos usar os valores de amount, card_country e card_use_24h na fórmula anterior. Se a probabilidade for maior que 0,5 vamos "prever" que o pagamento é fraudulento, caso contrário será predito como legítimo.
Discutimos anteriormente como calcular a, b, … Z, mas há dois problemas que precisam ser resolvidos:
- A probabilidade (fraude) precisa ser um número entre zero e um, mas os valores do lado direito da equação podem ficar bem grandes (em valor absoluto) dependendo dos valores de amount e card_use_24h (se o valor dessas características forem suficientemente grandes e um dos valores de a e b não for zero.
- O card_country não é um número, ele possui valor do tipo BR, US, AU, etc. Características desse tipo são chamadas de categórica e precisam ser "codificadas" apropriadamente (para um número) antes de serem usadas no treinamento do modelo.
Função Logit
Para tratar o problema (1), ao invés de modelar diretamente p = Probabilidade(fraude), modelamos o que é conhecido como o log da probabilidade da fraude, então o modelo se torna:
Se um evento tem a probabilidade p, sua probabilidade é p / (1 - p), isso ocorre porque o lado esquerdo é chamado de "log da probabilidade" ou "logit".
Dado os valores de a, b, … Z, e as características, podemos calcular a probabilidade prevista como fraude invertendo a função anterior para obter:
A probabilidade de fraude p é uma função sigmoidal da função linear L = a x amount + b x use_card_24h + … e fica parecido com isso:
Independente do valor da função linear, o sigmoid mapeia o valor para um número entre 0 e 1, que é uma probabilidade legítima.
Variáveis categórica
Para tratar o problema (2), pegamos a variável categórica card_country (que possui N valores distintos) e expandimos ela para N - 1 variáveis do tipo "dummy". Essas novas características são valores booleanos (0 ou 1) na forma de card_country = AU, card_country = GB, etc. Precisamos de N - 1 "dummies" porque os enésimo valor é implícito quando todos os N-1 dummies forem falsos. Para simplificar, digamos que card_country pode ter apenas um de três valores AU, GB e US. Então precisamos de duas variáveis dummies para codificá-lo, e o modelo que gostaríamos de obter para ajustar (fit) os valores (tal como, encontrar os valores dos coeficientes) é:
Esse tipo de modelo é conhecido como regressão logística.
Ajustando o modelo
Como determinamos os valores de a, b, c, d, e Z? Vamos começar definindo valores aleatórios para a, b, c, d, e Z. Podemos definir a probabilidade deste conjunto ser adivinhado:
Isto é, pegue cada amostra do conjunto de dados e calcule a probabilidade prevista de fraude p, dados os palpites para a, b, c, d e Z (e os valores das características para cada amostra) usando:
Para cada amostra que realmente era fraudulenta, gostaríamos que p fosse próximo de 1 e, para cada amostra que não fosse fraudulenta, gostaríamos que p fosse próximo de 0 (portanto, 1-p deve estar próximo de 1). Assim, fazemos o produto de p sobre todas as amostras fraudulentas com o produto de (1 - p) sobre todas as amostras não fraudulentas para obter a avaliação de quão boas são as suposições a, b, c, d e Z. Gostaríamos de fazer a função de semelhança o maior possível (ou seja, o mais próximo possível de 1). Começando com nosso palpite aleatório, vamos ajustar iterativamente a, b, c, d e Z, melhorando a probabilidade até descobrirmos que não podemos mais aumentá-la perturbando os coeficientes. Um método comum para fazer essa otimização é o stochastic gradient descent.
Implementação em Python
Agora vamos usar algumas ferramentas open source em Python que são padrões para colocar em prática a teoria que foi discutida. Usaremos o Pandas, que traz os dataframes para o Python como há no R, e o scikit-learn, um pacote popular de aprendizado de máquinas. Os dados dos exemplos mostrados anteriormente estão em um arquivo CSV chamado "data.csv"; podemos carregar os dados e pegar uma amostra com o código:
Podemos codificar o card_country em uma variável dummy com o código:
Agora os dados do dataframe possuem todos os dados necessários, com variáveis dummy e tudo mais, para treinar o modelo. Separamos o objetivo (a variável que tentamos predizer - nesse caso fraudulenta) e as características que o scikit trata como parâmetros diferentes.
Antes de prosseguir com o treinamento do modelo, há mais uma questão para discutir. Gostaríamos que o modelo generalizasse bem, ou seja, deveria ser preciso ao classificar os pagamentos que não vimos antes e não apenas capturar os padrões idiossincráticos nos pagamentos já conhecidos. Para garantir que não sobrecarregamos o modelo com o ruído dos dados que temos, separaremos os dados em dois conjuntos - um conjunto de treinamento que usaremos para estimar os parâmetros do modelo (a, b, c, d, e Z) e um conjunto de validação (também chamado de conjunto de testes) que usaremos para calcular as métricas de desempenho do modelo. Se um modelo for muito específico, ele terá um bom desempenho no conjunto de treinamento (pois ele terá aprendido os padrões desse conjunto), mas será mal no conjunto de validação. Existem outras abordagens como a validação cruzada (por exemplo, validação cruzada de k-fold), mas uma divisão de "teste e treino" servirá para esse exemplo.
Podemos facilmente dividir os dados em conjuntos de treino e teste com o seguinte código:
Nesse exemplo, usaremos ⅔ dos dados para treinar o modelo e ⅓ dos dados para validação. Agora o modelo está pronto para ser treinado, que nesse momento é uma trivialidade:
A função de fit realiza o procedimento de ajuste (que maximiza a função de probabilidade descrita anteriormente), e então queremos consultar o objeto retornado para os valores de a, b, c, e d (em coef_) e Z (em intercept_). O modelo final ficou assim:
Avaliando o desempenho do modelo
Uma vez que treinamos o modelo, precisamos determinar quão bom é esse modelo em prever a variável de interesse (neste caso, o booleano indicando se o pagamento é considerado fraudulento ou não). Lembre-se de que dissemos que classificamos um pagamento como fraudulento se o Probabilidade(fraude) for maior que 0,5 e o classificamos como legítimo caso contrário. Duas quantidades frequentemente usadas para medir o desempenho dado um modelo e uma política de classificação como esta são:
- a taxa de falsos positivos: a fração de todas as cobranças legítimas que são incorretamente classificadas como fraudulentas;
- a taxa de verdadeiros positivo (também conhecida como recall ou a sensitivity), a fração de todas as cobranças fraudulentas que são corretamente classificadas como fraudulentas.
Embora haja muitas medidas de desempenho do classificador, vamos nos concentrar nesses dois.
Idealmente, a taxa de falsos positivos será próxima de zero e a taxa verdadeiros positivo será próxima de 1. À medida que variamos o limiar de probabilidade em que classificamos uma cobrança como fraudulenta (que era 0,5, mas podemos escolher qualquer valor entre 0 e 1 - valores baixos significam que somos mais agressivos ao rotular os pagamentos como fraudulentos, e valores altos significam que somos mais conservadores), a taxa de falsos positivos e a taxa verdadeiros positivo traçam uma curva que depende de quão bom é o modelo. Isto é conhecido como a curva ROC e pode ser facilmente calculado com o scikit:
As variáveis fpr, tpr e thresholds contêm os dados da curva ROC completa, mas escolhemos um ponto de amostra aqui: se dissermos que uma cobrança é fraudulenta se a Probabilidade(fraude) for maior que 0,514, então a taxa de falso positivo é 0,374 e a taxa de verdadeiro positivo é 0,681. Toda curva ROC e o ponto que escolhemos estão descritos a seguir.
Quanto melhor o modelo, mais perto a curva ROC (a linha azul) irá abraçar as bordas esquerda e superior do gráfico. Observe que a curva ROC geral indica a qualidade do seu modelo, e isso pode ser capturado com um único número - o AUC ou a área sob a curva. Quanto mais próximo o AUC estiver de 1, melhor será o modelo.
É claro que, quando colocamos um modelo em produção para realizar uma ação, geralmente é necessário realizar as probabilidades geradas pelo modelo comparando-as com um limite como fizemos anteriormente, dizendo que uma carga é prevista como fraudulenta se Probabilidade(fraude) > 0,5. Assim, o desempenho do seu modelo para uma aplicação específica corresponde a um ponto na curva ROC - a curva geral apenas controla a relação entre a taxa de falsos positivos e a taxa verdadeiros positivos, ou seja, as opções de política que estão à disposição.
Decision Tree e Random Forest
O modelo anterior, a regressão logística, é um exemplo de um modelo linear de aprendizado de máquina. Imagine que cada exemplo de pagamento que temos é um ponto no espaço cujas coordenadas são os valores das características. Se tivéssemos apenas duas características, cada ponto de amostragem seria um ponto no plano x-y. Um modelo linear como a regressão logística geralmente terá um bom desempenho se pudermos separar as amostras fraudulentas das amostras não fraudulentas com uma função linear - nos dois casos, isso significa que quase todas as amostras fraudulentas estão em um lado de uma linha e quase todas as amostras não fraudulentas estão do outro lado da linha.
Geralmente, o relacionamento entre as características preditivas e a variável de saída que estamos tentando prever é não-linear. Nesse caso, devemos usar um modelo não-linear para capturar esse relacionamento. Um tipo poderoso e intuitivo de um modelo não linear é uma decision tree como a seguinte:
Em cada nó, comparamos o valor de um recurso especificado com algum limite e ramificamos para a esquerda ou para a direita, dependendo da saída da comparação. Continuamos dessa maneira (como um jogo de vinte perguntas, embora as árvores não precisem ter vinte níveis de profundidade) até chegarmos a uma folha da árvore. A folha consiste de todas as amostras no conjunto de treinamento para as quais as comparações em cada nó satisfizeram o caminho que percorremos a árvore, e a fração de amostras na folha que são fraudulentas é a probabilidade prevista de fraude que o modelo relata. Quando temos uma nova amostra a ser classificada, geramos suas características e jogamos o "jogo de vinte perguntas" até chegarmos a uma folha, e a probabilidade prevista de fraude é relatada conforme descrito.
Apesar de não apresentarmos os detalhes de como a árvore é produzida (embora, brevemente, escolhamos o recurso e o limite em cada nó para maximizar alguma noção de ganho de informação ou poder discriminatório - o gini informado na figura anterior - e seguimos de forma recursiva até atingirmos algum critério de parada pré-especificado), treinar um modelo de decision tree com o scikit é tão fácil quanto treinar uma regressão logística (ou qualquer outro modelo, na verdade):
Um problema com as decision tree é que elas podem ser facilmente adaptáveis - uma árvore muito profunda na qual cada folha tem apenas uma amostra dos dados de treinamento frequentemente captura ruído pertinente a cada amostra e não tendências gerais - mas modelos de random forest podem ajudar a resolver isso. Em uma random forest, treinamos um grande número de árvores de decisão, mas cada árvore é treinada apenas em um subconjunto dos dados que temos disponíveis e, ao construir cada árvore, consideramos apenas um subconjunto de recursos para divisão. A probabilidade prevista de fraude é então apenas a média das probabilidades produzidas por todas as decision trees. Treinar cada árvore em apenas um subconjunto dos dados, e considerando apenas um subconjunto dos recursos como candidatos divididos em cada nó, reduz a correlação entre as árvores e torna menos provável o ajuste excessivo.
Para resumir, modelos lineares como regressões logísticas são apropriados quando a relação entre as características e a variável de saída é linear ou quando for necessário isolar o impacto que determinada característica tem na previsão. Por outro lado, modelos não lineares como decision tree e random forest são mais difíceis de interpretar, mas podem capturar relacionamentos mais complexos.
Disponibilizando os modelos de aprendizado de máquinas em produção
Treinar um modelo de aprendizado de máquina, como descrito aqui, é apenas um passo no processo de usar o aprendizado de máquina para resolver um problema de negócios. Conforme descrito anteriormente, o treinamento do modelo geralmente deve ser precedido pelo trabalho de engenharia de características. E depois de ter um modelo, é necessário disponibilizá-lo em produção para agir adequadamente (bloqueando pagamentos avaliados como fraudulentos, por exemplo).
Apesar de não entrarmos em detalhes aqui, a produção pode envolver uma série de desafios - por exemplo, podemos usar o Python para o desenvolvimento de modelos, enquanto se usa um ambiente Ruby em produção. Se for esse o caso, será necessário "portar" seu modelo para Ruby, serializando-o em algum formato do Python e fazendo com que seu código Ruby de produção carregue a serialização ou use uma arquitetura orientada a serviços com chamadas de serviço de Ruby para Python.
Para um problema de natureza completamente diferente, também é desejável manter as métricas de desempenho do modelo em produção (diferentemente das métricas calculadas nos dados de validação). Dependendo de como é usado o modelo, isso pode ser difícil, porque o simples ato de usar o modelo para ditar ações podem resultar em não ter os dados para calcular essas métricas. Outros artigos desta série considerarão alguns desses problemas.
Materiais complementares
Está disponível um Jupyter notebook com todos os exemplos apresentados nesse artigo e os dados de exemplos para treinamento podem ser obtidos aqui.
Sobre o autor
Michael Manapat (@mlmanapat) (@mlmanapat) lidera o trabalho com produtos de aprendizado de máquinas na Stripe, incluindo o Stripe Radar. Antes de trabalhar na Stripe, ele foi engenheiro no Google, fez pós-doutorado e palestrou sobre Matemática aplicada em Harvard. Seu doutorado foi em Matemática no MIT.
O aprendizado de máquinas vem permitindo a criação de muitos produtos que interagimos diariamente como os assistentes "inteligentes" (tal como: o Siri da Apple e o Google Now), os mecanismos de recomendação como o da Amazon que sugerem novos produtos para comprar, os sistemas de classificação de anúncios usados pelo Google e pelo Facebook. Mais recentemente, o aprendizado de máquinas entrou na consciência pública devido aos avanços na "aprendizagem profunda" - isto inclui a vitória que o AlphaGo teve contra Lee Sedol, jogador profissional de Go, e novos produtos com reconhecimento de imagens e tradução automatica.
Está série de artigos, fornece uma introdução para algumas técnicas poderosas, mas geralmente aplicadas no aprendizado de máquinas. Isso inclui o aprendizado profundo, mas também métodos mais tradicionais que atendem diversas necessidades de negócios. Depois de ler esta série, esperamos que tenhamos passado o conhecimento necessário para que se possa começar os experimentos de aprendizado de máquinas em diversas áreas.