Projeto de Machine Learning para prever risco de churn na rede Ford Brasil e apoiar a ficha de abordagem comercial (Dock 360) com perfil de cliente, probabilidade de churn e ação recomendada.
| Nome | RM |
|---|---|
| Abner Barbosa | 558468 |
| Eduardo Dallabella | 556803 |
| Fernando Luiz | 555201 |
| Heloísa Real | 554535 |
O Ford VinGuard identifica clientes com maior probabilidade de abandonar a rede de manutenção (churn definido como 18 meses sem serviços). A API retorna:
- Predição de churn:
churnouno_churn; - Probabilidade calibrada de churn;
- Nível de risco:
low,mediumouhigh; - Perfil previsto via clustering comportamental;
- Ação recomendada para abordagem comercial.
.
|-- data/
| |-- raw/ # Dataset real (.xlsx)
| `-- processed/ # VINs agregados e labels de cluster
|-- models/ # Modelos joblib e checksums sha256
|-- notebooks/ # Notebooks de EDA e Pipeline Completo
|-- reports/ # Gráficos e relatórios de métricas
|-- src/
| |-- api/ # API FastAPI
| `-- pipeline/ # Scripts de engenharia e treino (Real)
|-- tests/ # Testes de leakage, saúde e schema
|-- requirements.txt
`-- README.md
src/pipeline/feature_engineering_real.py: Agrega 600k+ ordens de serviço da base bruta com 175k+ VINs únicos e gera a base modelável final com 118k+ VINs após filtros de qualidade e janela temporal.src/pipeline/clustering_real.py: Executa K-Means em features comportamentais.src/pipeline/train_churn_real.py: Treina o modelo calibrado de churn.src/pipeline/mlflow_tracking.py: Registra experimentos no MLflow.src/pipeline/config.py: Centraliza constantes e lista de anti-leakage.
src/api/main.py: Ponto de entrada FastAPI.src/api/services/predictor.py: Lógica de inferência.src/api/models/schemas.py: Contratos Pydantic com exemplos reais.
O projeto é de pós-venda. As features de entrada são calculadas somente até a
data de corte (DATA_CORTE) e o target de churn usa apenas eventos futuros.
Nunca use no X informações pós-corte, futuro ou target, como
churn_futuro_18m, voltou_pos_corte, primeiro_servico_pos_corte,
ultimo_servico_pos_corte ou qtd_servicos_pos_corte.
O modelo de churn de produção espera as features comportamentais até corte:
ano_modelo, qtde_revisoes_ate_corte,
meses_desde_ultimo_servico_ate_corte,
meses_relacionamento_ate_corte, n_dealers_usados_ate_corte,
km_max_ate_corte, pct_agenda_ate_corte,
intervalo_medio_revisoes_dias_ate_corte,
dias_ate_primeira_revisao, idade_veiculo_meses_ate_corte e modelo.
Execute os scripts na ordem abaixo a partir da raiz:
python -m src.pipeline.feature_engineering_realGera data/processed/snapshots_pos_venda.csv e
data/processed/dataset_churn_pos_venda.csv.
python -m src.pipeline.clustering_realGera data/processed/segmentos_pos_venda.csv.
python -m src.pipeline.train_churn_realGera os arquivos .joblib em models/.
python -m src.pipeline.mlflow_tracking
mlflow ui --backend-store-uri ./mlrunsExecuta experimentos de churn e segmentação K-Means e registra suas métricas e artefatos. Esse passo é evidência de experimentação; não é necessário para a API em runtime.
Para evidenciar a comparação de algoritmos/configurações exigida em IA & ML:
python -m src.pipeline.mlflow_tracking --only churn-comparison \
--comparison-output reports/model_comparison_churn.csvEsse comando compara Logistic Regression balanceada, RandomForest balanceado e
RandomForest calibrado, registrando AUC-ROC, AUC-PR, precision, recall e F1 no
MLflow e na tabela reports/model_comparison_churn.csv.
As imagens abaixo são geradas pelo pipeline/notebooks e servem como prints de apoio para a banca.
Distribuição dos modelos na base de ordens de serviço:
Distribuição por tipo de serviço:
Distribuição do target de churn:
Correlação entre features comportamentais e churn:
Escolha de k por elbow/silhouette:
Visualização PCA dos clusters:
Curva Precision-Recall:
Matriz de confusão:
- Arquivos de experimentos antigos foram removidos do escopo atual.
- O tracking MLflow é apenas evidência de avaliação; a API não depende dele em runtime.
- Os notebooks de EDA geram gráficos de apoio em
reports/, mas não são passos obrigatórios do pipeline produtivo. - Evite reexecutar treino sem necessidade antes da entrega, pois isso altera modelos e checksums.
O ciclo de vida do Ford VinGuard está documentado em
docs/politica_retreino.md. A política define retreino programado trimestral,
validação da maturação de 18 meses do rótulo, gate champion/challenger,
evidências em JSON + MLflow, menor privilégio e autorização explícita.
A aderência aos requisitos de IA & Machine Learning da Sprint 3 está resumida
em docs/ia_ml_requisitos.md, incluindo compreensão do problema, preparação
dos dados, comparação de modelos/configurações, métricas e conclusão do modelo
selecionado.
O histórico de versões aprovadas e o procedimento de rollback seguro ficam em
CHANGELOG_MODELOS.md. Rollback válido deve apontar para artefato aprovado e
verificável por SHA256; backup local informal não é evidência suficiente.
Resumo operacional:
- O gatilho de retreino gera apenas artefatos candidatos isolados.
- O gate de promoção, monitoramento de drift e rollback automatizado são
planejados; não há módulo
promotion_gate.pyimplementado nesta versão. - AUC-ROC é a métrica principal de avaliação de churn. O comparador atual
ordena por average precision (coluna
auc_pr); a política futura propõe critérios adicionais para promoção, ainda não automatizados. - O Java BFF é o consumidor técnico da API de ML via
X-ML-Service-Token. - A API retorna
perfil_previsto,risk_leveleacao_recomendada. A priorização operacional é uma utilização proposta pelo consumidor da API.
Endpoints oficiais da API:
GET /healthPOST /predictPOST /predict-batch
O endpoint /predict/batch é mantido como alias de compatibilidade.
export SECRET_KEY="sua-chave-secreta-de-pelo-menos-32-caracteres"
uvicorn src.api.main:app --host 0.0.0.0 --port 8000{
"features": {
"ano_modelo": 2020,
"qtde_revisoes_ate_corte": 2,
"meses_desde_ultimo_servico_ate_corte": 14.2,
"meses_relacionamento_ate_corte": 48.0,
"n_dealers_usados_ate_corte": 1,
"km_max_ate_corte": 48200,
"pct_agenda_ate_corte": 0.65,
"intervalo_medio_revisoes_dias_ate_corte": 220.0,
"dias_ate_primeira_revisao": 180,
"idade_veiculo_meses_ate_corte": 54.0,
"modelo": "KA"
}
}/health e / são públicos. Os endpoints /predict, /predict-batch e
/predict/batch aceitam dois caminhos de autenticação:
Authorization: Bearer <jwt>: JWT de usuário/demo com roleanalystouadmin. O token gerado por/auth/demo-tokenexpira conformeACCESS_TOKEN_EXPIRE_MINUTESe deve ser usado apenas para Swagger/demo manual.X-ML-Service-Token: <token>: token fixo server-to-server para o Java BFF. Esse token não expira pela FastAPI e deve vir somente de secret/variável de ambiente. Não registre esse valor em logs, exemplos ou arquivos versionados.
Para compatibilidade técnica, a FastAPI também aceita o service token como
Authorization: Bearer <service_token>, mas o header preferido para o BFF é
X-ML-Service-Token.
Para gerar um token local usando o mesmo SECRET_KEY da API:
python scripts/create_demo_token.py --role analystPara gerar token no ambiente publicado, configure DEMO_TOKEN_SECRET no Render
e chame:
curl -X POST "https://ford-vinguard-api.onrender.com/auth/demo-token?role=analyst" \
-H "X-Demo-Token-Secret: <DEMO_TOKEN_SECRET>"Use o access_token retornado como Authorization: Bearer <token> na FastAPI
ou como header X-ML-Demo-Token no BFF Java para teste manual. O Java BFF não
deve depender desse token demo para produção.
Configure o service token fixo no ambiente da FastAPI:
ML_SERVICE_TOKEN=<service-token-fixo-gerado-fora-do-repositorio>
# fallback aceito, se você quiser usar o mesmo nome no Java e na FastAPI:
FORD_ML_SERVICE_TOKEN=<service-token-fixo-gerado-fora-do-repositorio>No Java BFF, configure o mesmo valor como FORD_ML_SERVICE_TOKEN; o BFF envia
esse segredo para a FastAPI com X-ML-Service-Token.
O projeto está preparado para deploy como Web Service Python no Render via
render.yaml.
Configuração usada:
- Build Command:
pip install --upgrade pip && pip install -r requirements-api.txt && python scripts/fetch_model_artifacts.py - Start Command:
uvicorn src.api.main:app --host 0.0.0.0 --port $PORT - Health Check Path:
/ - Python:
3.11.11
Variáveis criadas/configuradas no Render:
SECRET_KEY: gerada pelo Render, obrigatória para JWTJWT_ISSUER:ford-vinguard-apiJWT_AUDIENCE:ford-vinguard-apiACCESS_TOKEN_EXPIRE_MINUTES: validade dos JWTs de demoDEMO_TOKEN_SECRET: segredo opcional para habilitar/auth/demo-tokenML_SERVICE_TOKEN: service token fixo para Java BFF -> FastAPI MLFORD_ML_SERVICE_TOKEN: fallback aceito para o service token fixoMODELS_DIR:modelsCHURN_MODEL_FILENAME:churn_pos_venda_rf_calibrated.joblibPERFIL_MODEL_FILENAME:kmeans_segmentador_pos_venda.joblibCHURN_MODEL_URL: URL privada/pública para baixar o.joblibde churnPERFIL_MODEL_URL: URL privada/pública para baixar o.joblibde perfilCHURN_MODEL_SHA256: checksum esperado do modelo de churnPERFIL_MODEL_SHA256: checksum esperado do segmentador K-Means
Observação importante: models/ e data/ não são versionados. Por isso, o
deploy tem dois níveis:
- Sem URLs de modelo:
/sobe para liveness;/healthretorna503 degradedporque os artefatos ainda não existem. - Com
CHURN_MODEL_URLePERFIL_MODEL_URL: o scriptscripts/fetch_model_artifacts.pybaixa os.joblibparaMODELS_DIRantes do Uvicorn iniciar. Com os checksums corretos,/health,/predict,/predict-batche/predict/batchficam prontos para demo.
As URLs dos artefatos devem ser configuradas no painel do Render ou preenchidas no fluxo inicial do Blueprint. Não commitar URLs assinadas, tokens ou datasets reais no repositório.
Para evitar o build automático da Azure/Oryx, o projeto também possui um
Dockerfile explícito na raiz. A imagem roda a FastAPI na porta 8000 por
padrão e respeita a variável PORT quando a plataforma definir outro valor.
Fluxo do container:
pip install -r requirements-api.txt
python scripts/fetch_model_artifacts.py
uvicorn src.api.main:app --host 0.0.0.0 --port ${PORT:-8000}
Arquivos e pastas pesadas ou sensíveis ficam fora do contexto Docker via
.dockerignore, incluindo .env, data/, models/, notebooks/,
reports/, tests/, __pycache__ e .git.
Variáveis obrigatórias/recomendadas no Azure Container Apps:
PORT=8000
SECRET_KEY=<chave-com-pelo-menos-32-caracteres>
DEMO_TOKEN_SECRET=<segredo-para-gerar-token-demo>
ML_SERVICE_TOKEN=<service-token-fixo-do-bff>
MODELS_DIR=models
CHURN_MODEL_FILENAME=churn_pos_venda_rf_calibrated.joblib
PERFIL_MODEL_FILENAME=kmeans_segmentador_pos_venda.joblib
CHURN_MODEL_URL=<url-do-artefato-churn>
PERFIL_MODEL_URL=<url-do-artefato-perfil>
CHURN_MODEL_SHA256=<sha256-do-artefato-churn>
PERFIL_MODEL_SHA256=<sha256-do-artefato-perfil>
JWT_ISSUER=ford-vinguard-api
JWT_AUDIENCE=ford-vinguard-apiExemplo de build local:
docker build -t ford-vinguard-ml-api .Exemplo de execução local sem gravar segredos no repositório:
docker run --rm -p 8000:8000 \
-e PORT=8000 \
-e SECRET_KEY="troque-por-uma-chave-com-32-caracteres" \
-e DEMO_TOKEN_SECRET="troque-por-um-segredo-de-demo" \
-e ML_SERVICE_TOKEN="<service-token-fixo-do-bff>" \
-e MODELS_DIR=models \
-e CHURN_MODEL_FILENAME=churn_pos_venda_rf_calibrated.joblib \
-e PERFIL_MODEL_FILENAME=kmeans_segmentador_pos_venda.joblib \
-e CHURN_MODEL_URL="<url-do-artefato-churn>" \
-e PERFIL_MODEL_URL="<url-do-artefato-perfil>" \
-e CHURN_MODEL_SHA256="<sha256-do-artefato-churn>" \
-e PERFIL_MODEL_SHA256="<sha256-do-artefato-perfil>" \
-e JWT_ISSUER=ford-vinguard-api \
-e JWT_AUDIENCE=ford-vinguard-api \
ford-vinguard-ml-apiNo Azure Container Apps, configure ingress HTTP externo para a porta alvo
8000. O endpoint / valida liveness, /health valida a disponibilidade dos
modelos. A documentação Swagger não fica exposta no código atual porque o
OpenAPI público está desabilitado na aplicação FastAPI.
Não commitar .env, datasets reais, arquivos em data/raw,
data/processed, models/*.joblib, URLs assinadas ou segredos usados no Azure.
Consulte a matriz da rubrica e conclusão e
as notas da entrega acadêmica.
O CSV de comparação seleciona RandomForest_Balanced por average precision;
o pipeline de inferência permanece configurado para RandomForest_Calibrated.
Não houve promoção automática do vencedor da comparação. A avaliação usa
holdout aleatório estratificado, e não comprova generalização temporal.
Para retornar nomes dos segmentos, disponibilize também
cluster_segment_map.json ao lado do segmentador em MODELS_DIR.
O download dos dois arquivos .joblib sozinho não provisiona esse mapeamento.
Por questões de tamanho e segurança, os datasets reais e artefatos .joblib não são versionados no GitHub. Para reprodução completa, é necessário disponibilizar os arquivos em data/raw/, configurar CHURN_MODEL_URL e PERFIL_MODEL_URL com os artefatos treinados, ou executar o pipeline completo a partir dos dados brutos autorizados. Sem os artefatos de modelo, a API sobe parcialmente para inspeção via / e /health, mas os endpoints de predição dependem dos arquivos .joblib.
pytest tests/ -v






