Machine LearningHR AnalyticsPythonPeople Analytics

Modelo de Predição de Turnover

Modelo de ML que antecipa risco de saída voluntária com 87% de acurácia, impactando a retenção de 2.400 colaboradores.

2026·Banco BV·Concluído

Demonstração do projeto

O projeto nasceu da necessidade de reduzir o custo de rotatividade no Banco BV, que representava um impacto financeiro de R$12M/ano. Construímos um pipeline completo de machine learning — da coleta e engenharia de features até a inferência em produção — que classifica colaboradores em faixas de risco (baixo, médio, alto) com base em dados comportamentais, estruturais e de carreira, permitindo ações preventivas de retenção antes que a intenção de saída se consolide.

Impacto & Métricas

87%

Acurácia

+12pp vs baseline

0.91

AUC-ROC

30%

Redução de turnover

em 12 meses

2.400

Colaboradores impactados

R$ 3.6M

ROI estimado

Contexto e Problema

O custo de reposição de um colaborador pode chegar a 1,5x o salário anual do profissional.

Com uma taxa de turnover voluntário de 18% ao ano e um quadro de 2.400 pessoas,
o banco enfrentava uma perda estimada de R$ 12M/ano em custos diretos e indiretos.

A solução convencional de RH — ação reativa pós-pedido de demissão — mostrava-se ineficaz.
Precisávamos de um modelo preditivo e acionável.

Arquitetura da Solução

O pipeline foi estruturado em 4 camadas:

1. Ingestão de dados: coleta de 7 sistemas de RH (HRIS, LMS, pesquisa de clima,

folha de pagamento, ponto eletrônico, avaliação de desempenho, recrutamento interno)
2. Feature Engineering: 42 variáveis derivadas, incluindo scores compostos de
engajamento, velocidade de progressão de carreira e sinais comportamentais
3. Modelagem: Gradient Boosting com validação cruzada estratificada (5-fold),
otimização via Optuna para hiperparâmetros
4. Inferência e ação: score diário por colaborador com classificação em faixas de
risco, disponibilizado via dashboard para gestores

Decisões Técnicas

Por que GBM e não Random Forest?

GBM apresentou AUC-ROC 4pp superior no conjunto de validação.
A natureza sequencial do boosting captura melhor as interações entre
variáveis de carreira (ex: ausência de promoção × tempo de empresa × score de clima).

Tratamento de desbalanceamento de classes
A taxa de turnover de 18% gera desbalanceamento. Usamos SMOTE na camada de treino
combinado com ajuste do `class_weight` no GBM, obtendo F1-score de 0.82 na classe positiva.

Interpretabilidade com SHAP
Para cada colaborador em risco, o sistema gera os 5 principais fatores que
contribuíram para a classificação, permitindo ao gestor entender o contexto antes da conversa.

python·feature_engineering.py
import pandas as pd
import numpy as np
from sklearn.preprocessing import LabelEncoder

def build_features(df: pd.DataFrame) -> pd.DataFrame:
    """
    Engenharia de features para predição de turnover.
    Gera variáveis comportamentais, de carreira e estruturais.
    """
    features = df.copy()

    # Tempo na empresa (dias → anos)
    features["tenure_years"] = (
        pd.to_datetime("today") - pd.to_datetime(features["hire_date"])
    ).dt.days / 365.25

    # Velocidade de progressão de carreira
    features["promo_rate"] = (
        features["promotions_count"] / features["tenure_years"].clip(lower=0.5)
    )

    # Distância da última avaliação de desempenho (meses)
    features["months_since_review"] = (
        pd.to_datetime("today") - pd.to_datetime(features["last_review_date"])
    ).dt.days / 30

    # Score de engajamento composto
    features["engagement_score"] = (
        features["survey_score"] * 0.4
        + features["training_hours_ytd"].clip(upper=40) / 40 * 0.3
        + (1 - features["absence_rate_6m"]) * 0.3
    )

    # Sinais de desengajamento recente
    features["declining_performance"] = (
        (features["perf_score_current"] < features["perf_score_previous"]).astype(int)
    )

    return features


def encode_categoricals(df: pd.DataFrame, cat_cols: list[str]) -> pd.DataFrame:
    """Label encoding para variáveis categóricas."""
    enc = LabelEncoder()
    for col in cat_cols:
        df[col] = enc.fit_transform(df[col].astype(str))
    return df
python·train_model.py
from sklearn.ensemble import GradientBoostingClassifier
from sklearn.model_selection import StratifiedKFold, cross_val_score
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
import numpy as np

def train_turnover_model(X_train, y_train):
    """
    Treina pipeline de GBM para predição de turnover.
    Usa StratifiedKFold para preservar proporção de classes.
    """
    pipeline = Pipeline([
        ("scaler", StandardScaler()),
        ("clf", GradientBoostingClassifier(
            n_estimators=300,
            learning_rate=0.05,
            max_depth=4,
            min_samples_leaf=20,
            subsample=0.8,
            random_state=42,
        )),
    ])

    cv = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)
    scores = cross_val_score(pipeline, X_train, y_train, cv=cv, scoring="roc_auc")

    print(f"AUC-ROC CV: {scores.mean():.3f} ± {scores.std():.3f}")

    pipeline.fit(X_train, y_train)
    return pipeline
sql·risk_classification.sql
-- Classificação de risco por colaborador
-- Atualizado diariamente via job agendado

WITH predictions AS (
    SELECT
        employee_id,
        prediction_score,
        prediction_date,
        CASE
            WHEN prediction_score >= 0.70 THEN 'Alto'
            WHEN prediction_score >= 0.40 THEN 'Médio'
            ELSE 'Baixo'
        END AS risk_level
    FROM ml_predictions.turnover_scores
    WHERE prediction_date = CURRENT_DATE
),
employee_context AS (
    SELECT
        e.employee_id,
        e.full_name,
        e.department,
        e.manager_id,
        e.tenure_years,
        e.last_salary_review_date
    FROM hr.employees e
    WHERE e.active = TRUE
)
SELECT
    ec.*,
    p.prediction_score,
    p.risk_level,
    DATEDIFF(CURRENT_DATE, ec.last_salary_review_date) AS days_since_salary_review
FROM employee_context ec
INNER JOIN predictions p ON ec.employee_id = p.employee_id
ORDER BY p.prediction_score DESC;