Back-End

27 mar, 2017

Mastermind: utilizando a engenharia do Uber para combater fraudes em tempo real

Publicidade

Onde há dinheiro há fraude. Para ajudar a combater fraudes em uma escala tão grande, a equipe da plataforma de prevenção de fraudes do Uber construiu o Mastermind, um motor de regras que pode detectar formas de fraude altamente evoluídas em uma fração de segundo. Neste artigo, explicamos como o Mastermind funciona e por que escolhemos um motor de regras.

Como não querermos interromper a experiência tranquila do Uber para os usuários e motoristas parceiros, nosso algoritmo deve deduzir a habilidade do usuário de pagar e detectar qualquer fraude em uma fração de segundo. O escopo de prevenção de fraude cobre fraudes de pagamento, roubo de contas, conspiração entre motorista e usuário e abuso de promoções. O Mastermind nos ajuda a cumprir esse acordo, tudo enquanto mantém a experiência tranquila do cliente.

Por que um motor de regras dita as regras

Para entender por que utilizamos um motor de regras, vamos dar uma olhada no panorama de prevenção de fraudes do Uber antes do Mastermind. Lógicas que lembram regras estavam espalhadas pelo código do Uber, dividindo nossos esforços para a prevenção de fraudes. Uma equipe utilizava lógica do tipo regra para aceitar ou rejeitar o cadastro de motoristas, outra equipe tinha regras para tratar as viagens e outra tinha outras regras para tratar alguma outra coisa, isso acontecia em várias equipes no Uber.

Outro problema era que os analistas não podiam escrever as regras eles próprios. Eles tinham que recorrer à ajuda de quaisquer engenheiros que fossem responsáveis pelos códigos que continham lógicas relevantes. Os engenheiros tinham que gastar tempo escrevendo ou mudando os códigos, o que se tornou muito entediante. Um engenheiro precisava de aproximadamente uma hora para escrever e aplicar uma regra. O risco de erros também aumentava, pois detalhes importantes poderiam se perder nas comunicações entre o engenheiro e o analista.

O sistema era cheio de ineficiência. Estava claro que precisávamos de uma maneira de os analistas escreverem, testarem e aplicarem as regras sozinhos o mais rápido possível e com quase nenhum envolvimento dos engenheiros.

A princípio, pode parecer que o aprendizado de máquina seja a melhor maneira de resolver esse problema, em vez de um motor de regras. Atividades fraudulentas são tão variáveis e com um escopo tão grande que é impossível que as regras capturem todos os cenários. Prevenção de fraudes requer uma tomada de decisão inteligente, e estamos utilizando aprendizado de máquinas extensamente, então por que construímos um motor de regras?

Primeiro, os fraudadores agem mais rápido do que podemos ensinar uma máquina. Segundo, existem casos em que a heurística é mais precisa do que dados treinados. Terceiro, nosso sistema de fraudes atualmente engloba aproximadamente duas dúzias de eventos (ex.: login, cadastro e solicitação de corrida), e um pequeno time como o nosso não consegue construir um modelo de aprendizado de máquina para cada um deles. Em vez disso, construímos modelos somente para os eventos complicados e importantes, como solicitação de corridas, e utilizamos regras para todo o resto.

Construindo o Mastermind

Em 2015, a engenharia da Uber tinha algumas opções em relação à implementação do Mastermind:

  1. Modificar um motor de regras existente para os propósitos do Uber: Existem muitas opções disponíveis de motores de regras open source. No entanto, eles normalmente têm lógica limitada e não incorporam uma linguagem para programação de forma livre. O último foi o impeditivo para nós. Queríamos que os analistas fossem capazes de escrever as regras que quisessem, e para isso necessitariam da uma linguagem de forma livre.
  2. Reciclar um serviço existente no Uber para ser um motor de regras: Alguns serviços internos já se pareciam com um sistema baseado em regras. Mas nenhum deles possuía a lógica complexa que seria necessária para combater formas avançadas de fraude.
  3. Construir um motor de regras do zero: Poderíamos fazer algo que acomodasse a lógica complexa e uma linguagem de forma livre. Nossa equipe de vários engenheiros seria responsável por desde o design da arquitetura até a manutenção do sistema, mas conheceríamos esse sistema muito bem.

Dada a variedade de atividades fraudulentas que uma grande plataforma internacional como o Uber atrai, não poderíamos nos contentar com a primeira e a segunda alternativas, e escolhemos construir um novo sistema do zero com a prevenção de fraudes do Uber sendo a intenção original da aplicação.

Dois anos mais tarde, no início de 2017, temos um motor de regras avançado que não somente executa das regras, mas também:

  • Mapeia diferentes tipos e níveis de riscos  para diferentes ações;
  • Permite que os analistas escrevam, testem e implementem as regras em minutos.

Por exemplo, se o Mastermind determinar que um usuário tem risco moderado de atividades fraudulentas, ele reduz a velocidade da solicitação de uma corrida para permitir que sistema tenha mais tempo de analisar o usuário. Em um caso extremo, se o Mastermind determinar que um usuário tem um alto risco, ele pode rejeitar a solicitação e bani-lo.

O Mastermind também é incrivelmente fácil para os analistas utilizarem. Eles podem escrever, testar e implementar uma regra em minutos. Dados um problema, um conjunto de funcionalidades de usuário e um modelo de negócio, os analistas podem escrever uma regra e testá-la no Mastermind utilizando o modo Evaluate. O estado de “Evaluate” significa que a regra está sendo executada, mas não vai afetar o usuário; estamos apenas armazenando dados para analisar posteriormente. Isso permite que o analista veja como uma regra é acionada e como ela atua de forma geral. Assim que eles sabem que a regra funciona, eles podem mudar seu estado para “Active” e executá-la em produção. Os engenheiros não precisam se envolver, e o processo é otimizado.

Iteragindo com o Mastermind

Tivemos que construir duas versões do Mastermind antes que pudéssemos concretizar nossos objetivos traçados. A primeira versão levou dois meses para ser construída e nos serviu por pouco mais de um ano. Nessa versão, as regras eram configuradas como dicionários Python. O design não era perfeito – os analistas levavam quase uma hora para implementar, testar e aplicar uma regra – mas ainda era muito mais rápido do que os engenheiros escreverem um novo código todas as vezes, já que nesse modelo as funcionalidades, as declarações e as ações que faziam uma regra podiam ser reutilizadas.

A segunda versão do Mastermind tinha que ter um desempenho melhor que a primeira, mas também ser inteiramente compatível com ela. Para conseguir isso, nós utilizamos o teste de sombra. Migramos todas as regras existentes para o novo sistema e direcionamos o tráfego de produção para ambas as versões do Mastermind. A nova versão na verdade não tomava decisões, mas registrava os resultados das decisões que teria tomado. Em seguida, comparávamos os resultados de cada sistema para ver se eram os mesmos. Assim que tivemos certeza de que a segunda versão fazia previsões corretas, tiramos a primeira versão do ar. Acabamos levando quatro meses para construir a segunda versão do Mastermind e outros dois meses para a migração, mas agora está muito mais rápido criar e aplicar uma regra do que na primeira versão.

Fluxo de eventos do Mastermind

Aqui está como o Mastermind se encaixa em nosso sistema maior:

Os analistas criam as regras no fron-end. As regras são então armazenadas em um banco de dados. O front-end pode validar a sintaxe da regra, e o autor pode testar a regra entrando valores das funcionalidades. O Mastermind atualiza as regras do banco de dados para a memória periodicamente. Leva aproximadamente um minuto para uma mudança do front-end chegar à produção, uma grande melhoria em relação à hora que levava na primeira versão do Mastermind.

A segunda versão não somente armazena e executa as regras; ela também fornece um framework para buscar recursos (ex.: quantidade de solicitações de corridas na última hora), executa modelos de aprendizado de máquina e realiza ações. O fluxo de evento típico para uma solicitação de avaliação parece o seguinte:

  1. Uma solicitação chega.
  2. O Mastermind envia a informação da solicitação para nosso serviço de agregação de recursos.
  3. O serviço de agregação de recursos busca recursos em tempo real de diferentes serviços e recursos pré-calculados do banco de dados em paralelo.
  4. Assim que o Mastermind recebe todos os recursos, ele os envia para o serviço de aprendizado de máquina.
  5. O serviço de aprendizado de máquina executa um ou mais modelos e devolve os resultados de risco.
  6. O Mastermind adiciona os resultados de risco aos recursos, executa as regras e envia as ações.

Algumas ações são síncronas, como quando o Mastermind rejeita uma solicitação e a devolve para o solicitante. Algumas solicitações são assíncronas, como banir o usuário. O Mastermind chama o serviço de ações de fraude para fazer isso.

Vamos ver mais detalhadamente como o fluxo de controle de regras do Mastermind funciona.

O fluxo de controle

Começamos a construir o Mastermind analisando todas as regras codificadas espalhadas pelo código do Uber. Descobrimos alguns padrões de lógica úteis, os quais abstraímos para construir nosso atual fluxo de controle de regras (rule control flow – RFC). O RFC consiste em uma série de pontos de verificação. Cada ponto de verificação contém um conjunto de regras, cada regra contém um conjunto de atributos, aqui está um exemplo:

Checkpoint(1)
 Rule(1): Property(1) -> Predicates(1, 2) -> Actions(1, 2)
 Rule(2): Property(2) -> Predicates(2, 3) -> Actions(2,3)
Run all rules => Output Actions(1,2,3)

Checkpoint(2)
 Rule(3): Property(3) -> Predicates(1, 4) -> Actions(4,5)
 Rule(4): Property(4) -> Predicates(4, 5) -> Actions(3)
Run all rules => Output Actions(3,4,5)

Elementos do RFC

Vamos dividir os elementos do RFC:

Pontos de controle: são os eventos dos usuários onde o Mastermind realiza a verificação de fraudes (exemplo: cadastro de usuário). Cada ponto de controle contém uma ou mais regras, e cada regra é independente.

Regras: São as condicionais que contêm três blocos: propriedades, declarações e ações.

  • Propriedade: controla o estado da regra em diferentes cidades ou países. Por exemplo, uma regra pode ter um estado de “Active/Inactive”, dependendo de a regra estar sendo executada em uma localidade. Uma regra pode também ter o estado de “Evaluate” para propósito de testes, como mencionado antes. Propriedade também guarda qualquer constante associada à localização.
  • Declarações: são as expressões lógicas. Todas as declarações precisam retornar “Verdadeiro” para acionar uma ação. As declarações são escritas em nossa linguagem de domínio específico “Predicate-eval” que veremos na próxima sessão.
  • Ações: são as saídas quando uma regra é acionada (ex.: banimento de usuário ou solicitação de informações adicionais). Uma regra pode acionar várias ações. Nesses casos, as ações são agregadas, e um conjunto final de ações é o resultado. Se múltiplas ações rejeitarem a solicitação com múltiplas mensagens de erro do lado mobile, somente a primeira é recebida.

Note que as declarações e as ações podem ser reutilizadas entre as regras, enquanto cada propriedade pertence à uma regra.

Aqui está o exemplo de uma regra:

Propriedade: city=’Daresbury’, status=’Active’, SPEC={‘threshold’: 10}

Declaração_A: ‘name != “Charles Dodgson”’

Declaração_B: ‘num_days_since_jabberwock_sighted > SPEC[“threshold”] and domain(email) == “lewiscarroll.org”’

Ação_A: Reject trip request

Ação_B: Add to blacklist

Aqui, SPEC é uma constante associada à localidade Daresbury. Determinamos o SPEC para uma propriedade, então SPEC [Threshold] será determinado como 10 para Daresbury. Domain (e-mail) é uma função definida pelo usuário em Email.

Mastermind fala “Predicate-eval”

Com esse fluxo de controle de regras, como construímos o Mastermind para que nossos analistas pudessem escrever uma regra e aplicá-la em minutos?

Como dissemos antes, uma de nossas prioridades na fase de design era tornar mais fácil para os analistas escreverem regras de forma livre sem a ajuda dos engenheiros. Para fazer isso, precisávamos que o Mastermind entendesse uma linguagem de forma livre (que agora chamamos de “Predicate-eval”), na qual os analistas poderiam facilmente escrever. Isso reduziria drasticamente o tempo que levava para escrever, testar e implementar uma regra. Tendo dito isso, nós realmente não queríamos construir uma linguagem de programação do zero, considerando o quão difícil e demorado seria.

Após muita busca sem resultados, nós finalmente encontramos módulo Python abstract syntax tree (AST). O AST é uma árvore que representa a estrutura de um programa. Por exemplo, utilizando o AST, você pode escrever um programa em Python que receba outro programa como entrada.

O AST nos permitiria construir a base do Python e modificar/personalizar a linguagem para nosso propósito sem termos que escrever uma nova linguagem. Nós utilizamos isso para construir a “Predicate-eval”, uma linguagem de forma livre para os analistas escreverem e avaliarem suas regras.

Construir a “Predicate-eval” baseada em Python se adequa bem aos nossos propósitos porque:

  • Os usuários alvos da  Predicate-eval são analistas (embora os engenheiros a utilizem às vezes), então as sintaxes devem ser diretas.
  • Python é uma linguagem dinâmica, que permite analisar e avaliar uma string de expressões em tempo de execução. Isso dá poder aos usuários para escrever as regras no front-end, e disponibilizá-las sem reiniciar os servidores.
  • A engenharia do Uber tem muitos engenheiros Python, então não existe curva de aprendizado para eles se adaptarem à Predicate-eval.

Com o Python, poderíamos simplesmente utilizar a função embutida eval(). No entanto, ela introduz à uma brecha de segurança. Em vez disso, utilizamos uma abordagem de lista branca e o módulo AST para decompor uma expressão em uma árvore de sintaxe abstrata. Definimos os encarregados pelos nós na árvore para nos protegermos ainda mais. Operações perigosas que poderiam ser usadas para ganhar acesso às classes do banco de dados (ex.: acesso aos atributos) não são implementadas e, portanto, são impossíveis.

Com essa abordagem , não somente tornamos a “Predicate-eval” mais segura, mas também mais confiável para utilizar. Você sabia que a expressão None<1 tem avaliação de verdadeira em Python? Considere esta regra: if feature < 1, then ban the user. Se acontecer de o recurso estar faltando por algum motivo, então o usuário seria banido por engano. Predicate-eval não permite esse tipo de avaliação insegura.

Também adicionamos algumas funções definidas pelo usuário na Predicate-eval, como lower() e upper() para strings. Dessa maneira, os analistas não precisam importar essas funções.

Como a Predicate-eval foi construída com Python, o Mastermind também precisa ser escrito em Python. Com o Python GIL, não conseguimos acessar vários núcleos e não podemos executar regras em paralelo. Então nós fizemos algumas otimizações, como cachear as árvores de sintaxe abstrata na memória. Agora, executar 300 regras complexas leva apenas 30 milissegundos.

Próximos passos

O maior problema que enfrentamos é que a maioria das regras são efetivas por várias semanas; então os fraudadores se adaptam, e as regras acabam com mais falsos-positivos. Então, precisamos continuamente melhorar o Mastermind para automaticamente avaliar a efetividade das regras através do tempo. Também planejamos enviar casos para revisão manual se a confiança na regra não está alta o suficiente.

Outro problema atual é que as declarações somente podem ser combinadas com lógica “E”, nos limitando em termos de critérios que podemos verificar. Para aumentar a flexibilidade e tornar os componentes mais reutilizáveis, podemos permitir que as declarações sejam utilizadas como variáveis, para que possam ser aninhadas em outras declarações. A sintaxe aninhada se pareceria com isto: Declaração_B = (Declaração_A == Verdadeiro) ou (recurso < 3).

Conclusão

O Mastermind nos permite reagir mais rápido e aumenta a produtividade de nossos analistas de fraudes. Na primeira versão do Mastermind, um analista levava um ano e meio para implementar 300 regras. Na segunda versão, os analistas conseguiram implementar 200 novas regras em apenas três meses depois que lançamos o front-end. Se você quiser nos ajudar a detectar e prevenir fraudes mais rápido que antes, veja nossas vagas para engenharia de fraudes.

***