Fraud Detection
Uma rede neural que sinaliza transações fraudulentas de cartão de crédito, construída sobre 1,85 milhão de compras simuladas e servida por uma API FastAPI como um pacote de modelo versionado de 6 KB.
- Python
- TensorFlow
- ONNX
- FastAPI
- Redis
- Docker
Desafio
Só 0,52% das 1,85 milhão de transações do dataset Sparkov são fraude, então um modelo que nunca sinaliza nada tem 99,5% de acurácia. O modelo precisa ser avaliado nessa classe rara e servido como um modelo de produção: versionado, validado, em contêiner e testado.
Abordagem
Dividir por tempo: treinar com 2019, validar no primeiro semestre de 2020 e guardar o conjunto de teste para uma única avaliação final. Uma rede Keras pequena, com duas camadas ReLU, tem o peso da classe fraude e o esquema de treino ajustados na validação, com três seeds cada, e o limiar de decisão é o de menor custo total, em que uma fraude perdida custa o seu valor e um alarme falso custa US$ 10. A análise de 100 fraudes perdidas e 100 alarmes falsos mostrou que os dois erros vinham de avaliar cada compra isoladamente, então o modelo 2.0.2 acrescenta cinco features das 24 horas anteriores do mesmo cartão, calculadas só com as compras anteriores. A engenharia de features é um pacote só, usado pelo treino e pela API.
Resultado
O histórico do cartão elevou o PR-AUC de 0,855 para 0,966 (três seeds) e reduziu os dois tipos de erro ao mesmo tempo: fraudes perdidas de 204 para 56 e alarmes falsos de 2.722 para 752, cortando o custo na validação pela metade. Avaliado uma única vez no teste, o modelo 2.0.2 chegou a PR-AUC de 0,959 e detectou 95,8% das fraudes com 69% de precisão. Um teste reproduz sequências de compras pela API e pelo código de treino e confere que os dois concordam exatamente. Os dados são simulados, então essas métricas não valeriam para transações reais.
Arquitetura
Decisões e trade-offs
Divisão por tempo, e o conjunto de teste avaliado uma única vez
Treinar com o passado e prever o futuro imita a produção. Todas as decisões foram tomadas na validação, o que deixa os números dela um pouco otimistas, então o teste foi avaliado uma vez, no fim, e nenhum modelo mudou depois de ver o resultado.
Peso 10 para a classe fraude, não a proporção entre as classes
Buscado na validação com três seeds: o peso 10 chegou a PR-AUC de 0,74, enquanto o padrão comum, a proporção entre as classes (176), ficou em 0,55 e fez a rede sinalizar demais.
O limiar de decisão minimiza o custo, não o número de erros
Uma fraude perdida custa ao banco o valor da compra, e um alarme falso foi estimado em US$ 10. No modelo 1.0.2, o limiar mais barato, 0,23, custou 27% menos que o padrão de 0,5.
O limiar depende desses US$ 10 estimados: com US$ 50 por alarme falso, ele passa para 0,53.
O modelo é publicado em ONNX, com o pré-processamento em JSON
O ONNX Runtime executa o modelo, então a imagem da API (377 MB) não precisa de TensorFlow nem de scikit-learn, e carregar um pacote nunca executa código serializado com pickle. Antes de publicar, o pacote é recarregado como a API o carrega e comparado com as previsões do Keras.
Um pacote de features para o treino e para a API
Uma transação é sempre transformada do mesmo jeito nos dois lugares, o que evita o training-serving skew, em que o modelo piora em produção sem aviso porque as entradas são calculadas de um jeito diferente do treino.
Histórico dos cartões no Redis, um sorted set por cartão
O histórico é uma consulta por intervalo, compartilhada por todas as réplicas e mantida entre reinícios, e o /health responde 503 quando o Redis cai, para o load balancer parar de mandar tráfego.
Um deploy novo começa vazio e vê todo cartão como tranquilo até o tráfego chegar; antes, ele precisaria ser aquecido com transações recentes.
Evidência principal
PR-AUC de 0,959 no conjunto de teste intocado, detectando 95,8% das fraudes
