Um Pipeline do scikit-learn conecta pré-processamento e estimador em um único objeto. A resposta direta para evitar data leakage é: divida os dados, coloque toda etapa que aprende parâmetros dentro do pipeline e passe esse objeto inteiro para a validação cruzada. Não calcule imputação, escala, seleção de atributos ou categorias no conjunto completo antes de validar.
O padrão mantém treino e previsão consistentes e simplifica a busca de hiperparâmetros. Este guia complementa pandas e machine learning com Python.
O problema que o Pipeline resolve
Considere uma tabela com idade, renda, cidade, canal e o alvo cancelou. Colunas numéricas precisam de imputação e escala; categóricas precisam de imputação e one-hot encoding. Se você ajustar essas operações antes de separar treino e teste, médias, medianas e vocabulários carregam informação do teste para o treino. A métrica deixa de representar dados realmente desconhecidos.
Um pipeline implementa a sequência fit, transform e predict. Ao chamar fit, transformadores aprendem somente com os dados recebidos. Ao chamar predict, reutilizam os parâmetros já aprendidos, sem recalculá-los. Essa distinção é a base da prevenção de leakage.
Reserve o teste antes de explorar decisões do modelo
import pandas as pd
from sklearn.model_selection import train_test_split
dados = pd.read_parquet('clientes.parquet')
X = dados.drop(columns='cancelou')
y = dados['cancelou']
X_treino, X_teste, y_treino, y_teste = train_test_split(
X,
y,
test_size=0.2,
random_state=42,
stratify=y,
)
stratify=y preserva aproximadamente a proporção das classes. Para linhas do mesmo cliente ou dispositivo, separe por grupos; para prever o futuro, divida cronologicamente. Mantenha o teste intocado até escolher o pipeline.
Pré-processamento com ColumnTransformer
ColumnTransformer aplica fluxos diferentes a grupos de colunas e concatena as saídas. Selecione colunas explicitamente para tornar o contrato auditável:
from sklearn.compose import ColumnTransformer
from sklearn.impute import SimpleImputer
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import OneHotEncoder, StandardScaler
numericas = ['idade', 'renda_mensal', 'meses_cliente']
categoricas = ['cidade', 'canal']
prep_numerico = Pipeline([
('imputar', SimpleImputer(strategy='median')),
('escalar', StandardScaler()),
])
prep_categorico = Pipeline([
('imputar', SimpleImputer(strategy='most_frequent')),
('codificar', OneHotEncoder(handle_unknown='ignore')),
])
preprocessamento = ColumnTransformer([
('num', prep_numerico, numericas),
('cat', prep_categorico, categoricas),
])
handle_unknown='ignore' permite transformar uma categoria que não apareceu no treino, produzindo zeros para aquele atributo desconhecido. Isso evita erro operacional, mas não substitui monitoramento de categorias novas. A documentação oficial de ColumnTransformer para dados heterogêneos apresenta outros seletores e combinações.
Conecte o modelo ao pré-processamento
from sklearn.linear_model import LogisticRegression
modelo = Pipeline([
('preprocessar', preprocessamento),
('classificar', LogisticRegression(
max_iter=1000,
class_weight='balanced',
)),
])
modelo.fit(X_treino, y_treino)
probabilidades = modelo.predict_proba(X_teste)[:, 1]
O objeto recebe as colunas originais tanto no treino quanto na inferência. Não chame preprocessamento.fit_transform(X) antes. Também não aplique pd.get_dummies separadamente em treino e produção, pois as colunas podem divergir. O guia oficial de Pipeline explica o encadeamento e o acesso aos parâmetros.
Validação cruzada correta
A validação cruzada estima a variação do desempenho em múltiplas divisões. Passe o pipeline completo para cross_validate. Em cada fold, o scikit-learn clona o pipeline e ajusta imputadores, codificadores, escala e modelo somente no subconjunto de treino:
from sklearn.model_selection import StratifiedKFold, cross_validate
cv = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)
metricas = cross_validate(
modelo,
X_treino,
y_treino,
cv=cv,
scoring={'roc_auc': 'roc_auc', 'f1': 'f1'},
return_train_score=True,
n_jobs=-1,
)
print(metricas['test_roc_auc'].mean())
print(metricas['test_roc_auc'].std())
Relate média e dispersão e compare treino com validação. A métrica deve refletir o custo: ROC AUC não define um limiar, e acurácia pode esconder falhas em classe rara. Para grupos, use GroupKFold; para tempo, considere TimeSeriesSplit.
Busca de hiperparâmetros sem contaminação
Parâmetros internos usam nomes unidos por dois sublinhados. A busca recebe o pipeline, não uma matriz previamente transformada:
from sklearn.model_selection import GridSearchCV
grade = {
'preprocessarnumimputarstrategy': ['mean', 'median'],
'classificarC': [0.1, 1.0, 10.0],
}
busca = GridSearchCV(
modelo,
param_grid=grade,
scoring='roc_auc',
cv=cv,
n_jobs=-1,
refit=True,
)
busca.fit(X_treino, y_treino)
melhor_modelo = busca.bestestimator
refit=True reajusta a melhor configuração em todo o conjunto de treino. Só então avalie uma vez no teste reservado. Se você usar repetidamente o teste para escolher atributos, limiar ou parâmetros, ele vira validação e sua estimativa fica otimista.
Avaliação final e persistência
from sklearn.metrics import classification_report, roc_auc_score
import joblib
proba = melhor_modelo.predict_proba(X_teste)[:, 1]
pred = melhor_modelo.predict(X_teste)
print(roc_auc_score(y_teste, proba))
print(classification_report(y_teste, pred, zero_division=0))
joblib.dump(melhor_modelo, 'modelo_cancelamento.joblib')
Persista o pipeline inteiro: ele contém transformações e estimador. Carregue artefatos somente de origem confiável: formatos baseados em pickle podem executar código. Registre versões, tipos das colunas, alvo e período dos dados. Siga práticas de código Python sustentável e use pytest para verificar amostras válidas, ausências e categorias inéditas.
Leakage que Pipeline não corrige sozinho
- Atributo posterior ao evento: “data de cancelamento” revela o alvo de cancelamento, mesmo dentro do pipeline.
- Agregação global: médias por cliente calculadas com eventos futuros contaminam a linha histórica.
- Duplicatas e entidades: o mesmo cliente em treino e validação permite memorizar padrões individuais.
- Seleção manual no teste: repetir decisões após observar a métrica final sobreajusta ao teste.
- Divisão aleatória temporal: treinar com o futuro para prever o passado produz uma avaliação irreal.
O capítulo oficial de erros comuns e práticas recomendadas detalha pré-processamento inconsistente e leakage. A prevenção também depende de entender quando cada dado estaria disponível no uso real.
Erros comuns
- Ajustar
StandardScaler, imputador ou seletor antes da divisão. - Passar apenas o classificador para a validação cruzada.
- Codificar o alvo ou incluir uma coluna derivada diretamente dele entre os atributos.
- Usar K-fold aleatório para grupos ou séries temporais.
- Otimizar hiperparâmetros e publicar a melhor pontuação da mesma validação como estimativa final.
- Salvar somente o estimador e tentar recriar o pré-processamento na aplicação.
Checklist antes de treinar
- Defina alvo, instante da previsão e quais dados existem naquele instante.
- Reserve o teste com uma estratégia adequada a classes, grupos e tempo.
- Coloque transformações aprendidas dentro de
Pipeline. - Use
ColumnTransformerpara contratos numéricos e categóricos explícitos. - Valide o pipeline completo com métricas alinhadas ao problema.
- Faça a busca apenas no treino e avalie o teste uma única vez.
- Persista o pipeline completo e registre dependências e esquema.
- Monitore categorias, distribuição, qualidade dos dados e desempenho após implantação.
Dados brutos entram no pipeline e previsões saem dele. Transformadores aprendem em cada fold, sem consultar o teste final, e o mesmo objeto atende produção. Ainda é indispensável raciocinar sobre tempo e causalidade.