📈 Avaliação de Modelos
Guia completo para avaliação e validação de modelos de machine learning, incluindo métricas de performance, análise de erros, interpretabilidade e validação em produção.
🎯 Visão Geral
A avaliação de modelos é crucial para garantir que o sistema de predição de atrasos seja confiável, robusto e adequado para uso em produção. Esta seção detalha todas as métricas e técnicas utilizadas.
📊 Framework de Avaliação
graph TD
A[Modelo Treinado] --> B[Métricas Básicas]
B --> C[Análise de Erros]
C --> D[Validação Cruzada]
D --> E[Testes de Robustez]
E --> F[Interpretabilidade]
F --> G[Validação de Negócio]
G --> H[Aprovação para Produção]
style A fill:#e3f2fd
style H fill:#c8e6c9
style F fill:#fff3e0
📏 Métricas de Performance
1. 🎯 Métricas Principais
Classificação Binária
from sklearn.metrics import (
accuracy_score, precision_score, recall_score, f1_score,
roc_auc_score, average_precision_score, confusion_matrix,
classification_report, roc_curve, precision_recall_curve
)
def calculate_comprehensive_metrics(y_true, y_pred, y_pred_proba):
"""Calcula todas as métricas de avaliação"""
metrics = {
# Métricas básicas
'accuracy': accuracy_score(y_true, y_pred),
'precision': precision_score(y_true, y_pred),
'recall': recall_score(y_true, y_pred),
'f1_score': f1_score(y_true, y_pred),
'specificity': recall_score(y_true, y_pred, pos_label=0),
# Métricas de ranking
'roc_auc': roc_auc_score(y_true, y_pred_proba),
'average_precision': average_precision_score(y_true, y_pred_proba),
# Matriz de confusão
'confusion_matrix': confusion_matrix(y_true, y_pred),
# Relatório detalhado
'classification_report': classification_report(y_true, y_pred, output_dict=True)
}
return metrics
Métricas Customizadas para Negócio
def business_metrics(y_true, y_pred, y_pred_proba, cost_matrix=None):
"""Métricas específicas do negócio de aviação"""
if cost_matrix is None:
# Custos estimados (em R$)
cost_matrix = {
'false_positive': 50, # Custo de preparação desnecessária
'false_negative': 200, # Custo de atraso não previsto
'true_positive': -20, # Economia por prevenção
'true_negative': 0 # Situação normal
}
# Matriz de confusão
tn, fp, fn, tp = confusion_matrix(y_true, y_pred).ravel()
# Custo total
total_cost = (
fp * cost_matrix['false_positive'] +
fn * cost_matrix['false_negative'] +
tp * cost_matrix['true_positive'] +
tn * cost_matrix['true_negative']
)
# Métricas de negócio
business_metrics = {
'total_cost': total_cost,
'cost_per_prediction': total_cost / len(y_true),
'prevention_rate': tp / (tp + fn) if (tp + fn) > 0 else 0,
'false_alarm_rate': fp / (fp + tn) if (fp + tn) > 0 else 0,
'economic_value': -total_cost, # Valor econômico (negativo do custo)
# Métricas operacionais
'delays_caught': tp,
'delays_missed': fn,
'false_alerts': fp,
'correct_non_delays': tn
}
return business_metrics
2. 📊 Visualização de Métricas
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.metrics import plot_confusion_matrix, plot_roc_curve, plot_precision_recall_curve
class ModelEvaluationVisualizer:
"""Classe para visualização de métricas de avaliação"""
def __init__(self, figsize=(15, 10)):
self.figsize = figsize
def comprehensive_evaluation_plot(self, model, X_test, y_test, model_name="Model"):
"""Cria visualização completa de avaliação"""
fig, axes = plt.subplots(2, 3, figsize=self.figsize)
fig.suptitle(f'Avaliação Completa - {model_name}', fontsize=16, fontweight='bold')
# 1. Matriz de Confusão
plot_confusion_matrix(model, X_test, y_test, ax=axes[0,0],
cmap='Blues', values_format='d')
axes[0,0].set_title('Matriz de Confusão')
# 2. ROC Curve
plot_roc_curve(model, X_test, y_test, ax=axes[0,1])
axes[0,1].set_title('Curva ROC')
axes[0,1].plot([0, 1], [0, 1], 'k--', alpha=0.6)
# 3. Precision-Recall Curve
plot_precision_recall_curve(model, X_test, y_test, ax=axes[0,2])
axes[0,2].set_title('Curva Precisão-Recall')
# 4. Distribuição de Probabilidades
y_pred_proba = model.predict_proba(X_test)[:, 1]
axes[1,0].hist(y_pred_proba[y_test == 0], bins=30, alpha=0.7,
label='Não Atrasado', color='blue')
axes[1,0].hist(y_pred_proba[y_test == 1], bins=30, alpha=0.7,
label='Atrasado', color='red')
axes[1,0].set_xlabel('Probabilidade Predita')
axes[1,0].set_ylabel('Frequência')
axes[1,0].set_title('Distribuição de Probabilidades')
axes[1,0].legend()
# 5. Feature Importance (se disponível)
if hasattr(model, 'feature_importances_'):
importances = model.feature_importances_
feature_names = X_test.columns if hasattr(X_test, 'columns') else [f'Feature_{i}' for i in range(len(importances))]
# Top 15 features
indices = np.argsort(importances)[-15:]
axes[1,1].barh(range(len(indices)), importances[indices])
axes[1,1].set_yticks(range(len(indices)))
axes[1,1].set_yticklabels([feature_names[i] for i in indices])
axes[1,1].set_xlabel('Importância')
axes[1,1].set_title('Top 15 Features Mais Importantes')
# 6. Calibração do Modelo
from sklearn.calibration import calibration_curve
fraction_of_positives, mean_predicted_value = calibration_curve(
y_test, y_pred_proba, n_bins=10
)
axes[1,2].plot(mean_predicted_value, fraction_of_positives, "s-",
label=f"{model_name}")
axes[1,2].plot([0, 1], [0, 1], "k:", label="Perfeitamente Calibrado")
axes[1,2].set_xlabel('Probabilidade Média Predita')
axes[1,2].set_ylabel('Fração de Positivos')
axes[1,2].set_title('Curva de Calibração')
axes[1,2].legend()
plt.tight_layout()
plt.show()
return fig
🔍 Análise de Erros
1. 🎯 Análise de Falsos Positivos e Negativos
def error_analysis(X_test, y_test, y_pred, y_pred_proba, feature_names=None):
"""Análise detalhada de erros do modelo"""
# Identificar tipos de erro
false_positives = (y_test == 0) & (y_pred == 1)
false_negatives = (y_test == 1) & (y_pred == 0)
true_positives = (y_test == 1) & (y_pred == 1)
true_negatives = (y_test == 0) & (y_pred == 0)
error_analysis_results = {
'false_positives': {
'count': false_positives.sum(),
'percentage': false_positives.mean() * 100,
'avg_probability': y_pred_proba[false_positives].mean(),
'samples': X_test[false_positives].head(10) if hasattr(X_test, 'head') else None
},
'false_negatives': {
'count': false_negatives.sum(),
'percentage': false_negatives.mean() * 100,
'avg_probability': y_pred_proba[false_negatives].mean(),
'samples': X_test[false_negatives].head(10) if hasattr(X_test, 'head') else None
},
'true_positives': {
'count': true_positives.sum(),
'avg_probability': y_pred_proba[true_positives].mean()
},
'true_negatives': {
'count': true_negatives.sum(),
'avg_probability': y_pred_proba[true_negatives].mean()
}
}
# Análise por faixas de probabilidade
prob_bins = np.linspace(0, 1, 11)
prob_analysis = {}
for i in range(len(prob_bins) - 1):
bin_mask = (y_pred_proba >= prob_bins[i]) & (y_pred_proba < prob_bins[i+1])
if bin_mask.sum() > 0:
prob_analysis[f"{prob_bins[i]:.1f}-{prob_bins[i+1]:.1f}"] = {
'total_predictions': bin_mask.sum(),
'actual_positive_rate': y_test[bin_mask].mean(),
'predicted_probability': y_pred_proba[bin_mask].mean()
}
error_analysis_results['probability_analysis'] = prob_analysis
return error_analysis_results
2. 📊 Análise de Segmentos
def segment_analysis(X_test, y_test, y_pred, y_pred_proba, segments):
"""Análise de performance por segmentos específicos"""
segment_results = {}
for segment_name, segment_condition in segments.items():
# Aplicar condição do segmento
if callable(segment_condition):
segment_mask = segment_condition(X_test)
else:
segment_mask = segment_condition
if segment_mask.sum() == 0:
continue
# Métricas do segmento
segment_y_test = y_test[segment_mask]
segment_y_pred = y_pred[segment_mask]
segment_y_pred_proba = y_pred_proba[segment_mask]
segment_metrics = calculate_comprehensive_metrics(
segment_y_test, segment_y_pred, segment_y_pred_proba
)
segment_results[segment_name] = {
'size': segment_mask.sum(),
'percentage': (segment_mask.sum() / len(X_test)) * 100,
'metrics': segment_metrics,
'baseline_delay_rate': segment_y_test.mean()
}
return segment_results
🔄 Validação Cruzada Avançada
1. ⏰ Validação Temporal com Walk-Forward
def walk_forward_validation(model, X, y, initial_train_size=0.6, step_size=0.1):
"""Validação walk-forward para séries temporais"""
n_samples = len(X)
results = []
# Tamanho inicial do treino
train_end = int(n_samples * initial_train_size)
while train_end + int(n_samples * step_size) <= n_samples:
# Definir janelas
test_start = train_end
test_end = min(train_end + int(n_samples * step_size), n_samples)
# Dividir dados
X_train_fold = X.iloc[:train_end]
X_test_fold = X.iloc[test_start:test_end]
y_train_fold = y.iloc[:train_end]
y_test_fold = y.iloc[test_start:test_end]
# Treinar modelo
model_fold = clone(model)
model_fold.fit(X_train_fold, y_train_fold)
# Predições
y_pred_proba_fold = model_fold.predict_proba(X_test_fold)[:, 1]
y_pred_fold = (y_pred_proba_fold > 0.5).astype(int)
# Métricas
fold_metrics = calculate_comprehensive_metrics(
y_test_fold, y_pred_fold, y_pred_proba_fold
)
results.append({
'fold': len(results) + 1,
'train_size': len(X_train_fold),
'test_size': len(X_test_fold),
'train_end': train_end,
'test_start': test_start,
'test_end': test_end,
'metrics': fold_metrics
})
# Próxima iteração
train_end = test_end
print(f"Fold {len(results)}: Train={len(X_train_fold)}, Test={len(X_test_fold)}, AUC={fold_metrics['roc_auc']:.4f}")
# Resumir resultados
avg_metrics = {}
for metric in ['accuracy', 'precision', 'recall', 'f1_score', 'roc_auc']:
values = [r['metrics'][metric] for r in results]
avg_metrics[f"{metric}_mean"] = np.mean(values)
avg_metrics[f"{metric}_std"] = np.std(values)
return results, avg_metrics
2. 🎯 Validação Estratificada por Segmentos
from sklearn.model_selection import StratifiedGroupKFold
def stratified_group_validation(model, X, y, groups, n_splits=5):
"""Validação cruzada estratificada por grupos"""
sgkf = StratifiedGroupKFold(n_splits=n_splits, shuffle=True, random_state=42)
cv_results = []
for fold, (train_idx, test_idx) in enumerate(sgkf.split(X, y, groups)):
print(f"Processando fold {fold + 1}/{n_splits}")
# Dividir dados
X_train_fold = X.iloc[train_idx]
X_test_fold = X.iloc[test_idx]
y_train_fold = y.iloc[train_idx]
y_test_fold = y.iloc[test_idx]
# Treinar modelo
model_fold = clone(model)
model_fold.fit(X_train_fold, y_train_fold)
# Predições
y_pred_proba_fold = model_fold.predict_proba(X_test_fold)[:, 1]
y_pred_fold = (y_pred_proba_fold > 0.5).astype(int)
# Métricas
fold_metrics = calculate_comprehensive_metrics(
y_test_fold, y_pred_fold, y_pred_proba_fold
)
cv_results.append({
'fold': fold + 1,
'train_groups': np.unique(groups[train_idx]),
'test_groups': np.unique(groups[test_idx]),
'metrics': fold_metrics
})
return cv_results
🛡️ Testes de Robustez
1. 🔄 Teste de Estabilidade
def stability_test(model, X_test, y_test, n_iterations=100):
"""Testa estabilidade das predições do modelo"""
predictions = []
probabilities = []
for i in range(n_iterations):
# Adicionar ruído pequeno aos dados (simular variação real)
X_test_noisy = X_test + np.random.normal(0, 0.01, X_test.shape)
# Predições
y_pred_proba = model.predict_proba(X_test_noisy)[:, 1]
y_pred = (y_pred_proba > 0.5).astype(int)
predictions.append(y_pred)
probabilities.append(y_pred_proba)
# Análise de estabilidade
predictions_array = np.array(predictions)
probabilities_array = np.array(probabilities)
stability_metrics = {
'prediction_variance': np.var(predictions_array, axis=0).mean(),
'probability_variance': np.var(probabilities_array, axis=0).mean(),
'prediction_stability': (predictions_array.std(axis=0) < 0.1).mean(),
'flip_rate': (predictions_array.std(axis=0) > 0).mean()
}
return stability_metrics
2. 🎭 Teste de Adversarial
def adversarial_test(model, X_test, y_test, epsilon=0.1):
"""Teste de robustez adversarial (para modelos lineares)"""
if not hasattr(model, 'coef_'):
print("⚠️ Teste adversarial disponível apenas para modelos lineares")
return None
# Calcular gradiente (direção de maior mudança)
gradient = model.coef_[0]
# Normalizar gradiente
gradient_norm = gradient / np.linalg.norm(gradient)
# Criar exemplos adversariais
X_adversarial = X_test + epsilon * gradient_norm
# Predições originais vs adversariais
y_pred_original = model.predict_proba(X_test)[:, 1]
y_pred_adversarial = model.predict_proba(X_adversarial)[:, 1]
# Métricas de robustez
robustness_metrics = {
'max_probability_change': np.max(np.abs(y_pred_adversarial - y_pred_original)),
'mean_probability_change': np.mean(np.abs(y_pred_adversarial - y_pred_original)),
'prediction_flip_rate': np.mean((y_pred_adversarial > 0.5) != (y_pred_original > 0.5)),
'robustness_score': 1 - np.mean(np.abs(y_pred_adversarial - y_pred_original))
}
return robustness_metrics
🔍 Interpretabilidade
1. 🌟 SHAP Values
import shap
def shap_analysis(model, X_train, X_test, feature_names=None):
"""Análise de interpretabilidade usando SHAP"""
try:
# Diferentes explicadores para diferentes tipos de modelo
if hasattr(model, 'tree_'):
explainer = shap.TreeExplainer(model)
elif hasattr(model, 'coef_'):
explainer = shap.LinearExplainer(model, X_train)
else:
explainer = shap.KernelExplainer(model.predict_proba, X_train.sample(100))
# Calcular SHAP values
shap_values = explainer.shap_values(X_test[:100]) # Primeiras 100 amostras
# Se é classificação binária, pegar valores da classe positiva
if isinstance(shap_values, list):
shap_values = shap_values[1]
# Análise dos SHAP values
feature_importance = np.abs(shap_values).mean(axis=0)
shap_results = {
'shap_values': shap_values,
'feature_importance': feature_importance,
'feature_names': feature_names or [f'Feature_{i}' for i in range(shap_values.shape[1])],
'explainer': explainer
}
return shap_results
except Exception as e:
print(f"⚠️ Erro na análise SHAP: {e}")
return None
2. 📊 Permutation Importance
from sklearn.inspection import permutation_importance
def permutation_importance_analysis(model, X_test, y_test, n_repeats=10):
"""Análise de importância por permutação"""
# Calcular importância por permutação
perm_importance = permutation_importance(
model, X_test, y_test,
n_repeats=n_repeats,
random_state=42,
scoring='roc_auc'
)
# Organizar resultados
feature_names = X_test.columns if hasattr(X_test, 'columns') else [f'Feature_{i}' for i in range(X_test.shape[1])]
importance_df = pd.DataFrame({
'feature': feature_names,
'importance_mean': perm_importance.importances_mean,
'importance_std': perm_importance.importances_std
}).sort_values('importance_mean', ascending=False)
return importance_df, perm_importance
3. 🎯 Feature Interactions
def analyze_feature_interactions(model, X_test, feature_pairs=None):
"""Análise de interações entre features"""
if feature_pairs is None:
# Selecionar pares de features mais importantes
if hasattr(model, 'feature_importances_'):
importances = model.feature_importances_
top_features_idx = np.argsort(importances)[-10:] # Top 10
feature_pairs = [(i, j) for i in top_features_idx for j in top_features_idx if i < j]
else:
print("⚠️ Modelo não suporta análise automática de interações")
return None
interaction_effects = []
for feat1_idx, feat2_idx in feature_pairs:
# Criar grid de valores para as duas features
feat1_values = np.linspace(X_test.iloc[:, feat1_idx].min(), X_test.iloc[:, feat1_idx].max(), 10)
feat2_values = np.linspace(X_test.iloc[:, feat2_idx].min(), X_test.iloc[:, feat2_idx].max(), 10)
# Calcular predições no grid
interaction_grid = np.zeros((len(feat1_values), len(feat2_values)))
for i, val1 in enumerate(feat1_values):
for j, val2 in enumerate(feat2_values):
# Criar amostra com valores específicos
X_sample = X_test.iloc[0:1].copy()
X_sample.iloc[0, feat1_idx] = val1
X_sample.iloc[0, feat2_idx] = val2
# Predição
pred_proba = model.predict_proba(X_sample)[0, 1]
interaction_grid[i, j] = pred_proba
interaction_effects.append({
'feature1_idx': feat1_idx,
'feature2_idx': feat2_idx,
'feature1_name': X_test.columns[feat1_idx] if hasattr(X_test, 'columns') else f'Feature_{feat1_idx}',
'feature2_name': X_test.columns[feat2_idx] if hasattr(X_test, 'columns') else f'Feature_{feat2_idx}',
'interaction_grid': interaction_grid,
'feature1_values': feat1_values,
'feature2_values': feat2_values
})
return interaction_effects
📊 Validação de Negócio
1. 💰 Análise de Valor Econômico
def economic_value_analysis(y_test, y_pred_proba, thresholds=None, cost_matrix=None):
"""Análise de valor econômico para diferentes thresholds"""
if thresholds is None:
thresholds = np.linspace(0.1, 0.9, 17)
if cost_matrix is None:
cost_matrix = {
'false_positive': 50,
'false_negative': 200,
'true_positive': -20,
'true_negative': 0
}
threshold_analysis = []
for threshold in thresholds:
y_pred = (y_pred_proba >= threshold).astype(int)
# Métricas básicas
metrics = calculate_comprehensive_metrics(y_test, y_pred, y_pred_proba)
# Métricas de negócio
business_metrics_result = business_metrics(y_test, y_pred, y_pred_proba, cost_matrix)
threshold_analysis.append({
'threshold': threshold,
'precision': metrics['precision'],
'recall': metrics['recall'],
'f1_score': metrics['f1_score'],
'economic_value': business_metrics_result['economic_value'],
'cost_per_prediction': business_metrics_result['cost_per_prediction'],
'prevention_rate': business_metrics_result['prevention_rate']
})
# Encontrar threshold ótimo
threshold_df = pd.DataFrame(threshold_analysis)
optimal_threshold_idx = threshold_df['economic_value'].idxmax()
optimal_threshold = threshold_df.iloc[optimal_threshold_idx]
print(f"🎯 Threshold ótimo: {optimal_threshold['threshold']:.3f}")
print(f"💰 Valor econômico: R$ {optimal_threshold['economic_value']:,.2f}")
print(f"🎯 Taxa de prevenção: {optimal_threshold['prevention_rate']:.1%}")
return threshold_df, optimal_threshold
2. 🎯 Análise Operacional
def operational_analysis(y_test, y_pred, y_pred_proba, operational_constraints=None):
"""Análise de viabilidade operacional"""
if operational_constraints is None:
operational_constraints = {
'max_daily_alerts': 100, # Máximo de alertas por dia
'min_precision': 0.70, # Precisão mínima aceitável
'min_recall': 0.60, # Recall mínimo aceitável
'response_time_limit': 300 # Limite de tempo de resposta (segundos)
}
# Simular cenário operacional
n_days = 30 # Análise para 30 dias
predictions_per_day = len(y_test) / n_days
alerts_per_day = np.sum(y_pred) / n_days
operational_metrics = {
'predictions_per_day': predictions_per_day,
'alerts_per_day': alerts_per_day,
'alert_rate': alerts_per_day / predictions_per_day,
'workload_feasible': alerts_per_day <= operational_constraints['max_daily_alerts'],
'precision_ok': precision_score(y_test, y_pred) >= operational_constraints['min_precision'],
'recall_ok': recall_score(y_test, y_pred) >= operational_constraints['min_recall']
}
# Avaliação geral
operational_metrics['operationally_viable'] = (
operational_metrics['workload_feasible'] and
operational_metrics['precision_ok'] and
operational_metrics['recall_ok']
)
print("📊 ANÁLISE OPERACIONAL")
print("=" * 40)
print(f"Predições por dia: {operational_metrics['predictions_per_day']:.1f}")
print(f"Alertas por dia: {operational_metrics['alerts_per_day']:.1f}")
print(f"Taxa de alertas: {operational_metrics['alert_rate']:.1%}")
print(f"Viável operacionalmente: {'✅' if operational_metrics['operationally_viable'] else '❌'}")
return operational_metrics
📋 Relatório Completo de Avaliação
📄 Classe Geradora de Relatório
class ModelEvaluationReport:
"""Classe para gerar relatório completo de avaliação"""
def __init__(self, model, model_name):
self.model = model
self.model_name = model_name
self.evaluation_results = {}
def full_evaluation(self, X_test, y_test, X_train=None):
"""Avaliação completa do modelo"""
print(f"🔍 Iniciando avaliação completa: {self.model_name}")
print("=" * 60)
# Predições
y_pred_proba = self.model.predict_proba(X_test)[:, 1]
y_pred = (y_pred_proba > 0.5).astype(int)
# 1. Métricas básicas
print("\n📊 1. MÉTRICAS BÁSICAS")
basic_metrics = calculate_comprehensive_metrics(y_test, y_pred, y_pred_proba)
self.evaluation_results['basic_metrics'] = basic_metrics
print(f"Accuracy: {basic_metrics['accuracy']:.4f}")
print(f"Precision: {basic_metrics['precision']:.4f}")
print(f"Recall: {basic_metrics['recall']:.4f}")
print(f"F1-Score: {basic_metrics['f1_score']:.4f}")
print(f"ROC-AUC: {basic_metrics['roc_auc']:.4f}")
# 2. Métricas de negócio
print("\n💰 2. MÉTRICAS DE NEGÓCIO")
business_metrics_result = business_metrics(y_test, y_pred, y_pred_proba)
self.evaluation_results['business_metrics'] = business_metrics_result
print(f"Valor econômico: R$ {business_metrics_result['economic_value']:,.2f}")
print(f"Taxa de prevenção: {business_metrics_result['prevention_rate']:.1%}")
print(f"Taxa de falsos alarmes: {business_metrics_result['false_alarm_rate']:.1%}")
# 3. Análise de erros
print("\n🔍 3. ANÁLISE DE ERROS")
error_analysis_result = error_analysis(X_test, y_test, y_pred, y_pred_proba)
self.evaluation_results['error_analysis'] = error_analysis_result
print(f"Falsos positivos: {error_analysis_result['false_positives']['count']} ({error_analysis_result['false_positives']['percentage']:.1f}%)")
print(f"Falsos negativos: {error_analysis_result['false_negatives']['count']} ({error_analysis_result['false_negatives']['percentage']:.1f}%)")
# 4. Teste de estabilidade
print("\n🛡️ 4. TESTE DE ESTABILIDADE")
stability_result = stability_test(self.model, X_test, y_test)
self.evaluation_results['stability'] = stability_result
print(f"Estabilidade de predição: {stability_result['prediction_stability']:.1%}")
print(f"Taxa de mudança: {stability_result['flip_rate']:.1%}")
# 5. Análise operacional
print("\n📋 5. ANÁLISE OPERACIONAL")
operational_result = operational_analysis(y_test, y_pred, y_pred_proba)
self.evaluation_results['operational'] = operational_result
# 6. Interpretabilidade (se X_train fornecido)
if X_train is not None:
print("\n🔍 6. ANÁLISE DE INTERPRETABILIDADE")
try:
shap_result = shap_analysis(self.model, X_train, X_test)
if shap_result:
self.evaluation_results['shap_analysis'] = shap_result
print("✅ Análise SHAP concluída")
except Exception as e:
print(f"⚠️ Erro na análise SHAP: {e}")
try:
perm_importance_df, perm_importance = permutation_importance_analysis(
self.model, X_test, y_test
)
self.evaluation_results['permutation_importance'] = {
'dataframe': perm_importance_df,
'raw_results': perm_importance
}
print("✅ Análise de importância por permutação concluída")
except Exception as e:
print(f"⚠️ Erro na análise de permutação: {e}")
print("\n✅ Avaliação completa finalizada!")
return self.evaluation_results
def generate_summary_report(self):
"""Gera resumo executivo da avaliação"""
if not self.evaluation_results:
print("❌ Execute full_evaluation() primeiro")
return
print(f"\n📋 RELATÓRIO EXECUTIVO - {self.model_name}")
print("=" * 60)
# Performance geral
basic = self.evaluation_results['basic_metrics']
print(f"🎯 PERFORMANCE GERAL")
print(f" ROC-AUC: {basic['roc_auc']:.3f}")
print(f" Precision: {basic['precision']:.3f}")
print(f" Recall: {basic['recall']:.3f}")
print(f" F1-Score: {basic['f1_score']:.3f}")
# Impacto de negócio
business = self.evaluation_results['business_metrics']
print(f"\n💰 IMPACTO DE NEGÓCIO")
print(f" Valor econômico: R$ {business['economic_value']:,.0f}")
print(f" Atrasos prevenidos: {business['delays_caught']}")
print(f" Atrasos perdidos: {business['delays_missed']}")
print(f" Falsos alertas: {business['false_alerts']}")
# Viabilidade operacional
operational = self.evaluation_results['operational']
print(f"\n📊 VIABILIDADE OPERACIONAL")
print(f" Alertas por dia: {operational['alerts_per_day']:.1f}")
print(f" Taxa de alertas: {operational['alert_rate']:.1%}")
print(f" Operacionalmente viável: {'✅' if operational['operationally_viable'] else '❌'}")
# Estabilidade
stability = self.evaluation_results['stability']
print(f"\n🛡️ ROBUSTEZ")
print(f" Estabilidade: {stability['prediction_stability']:.1%}")
print(f" Taxa de mudança: {stability['flip_rate']:.1%}")
# Recomendação final
print(f"\n🎯 RECOMENDAÇÃO")
# Critérios para aprovação
approval_criteria = {
'roc_auc': basic['roc_auc'] >= 0.75,
'precision': basic['precision'] >= 0.70,
'recall': basic['recall'] >= 0.60,
'economic_value': business['economic_value'] > 0,
'operational_viable': operational['operationally_viable'],
'stable': stability['prediction_stability'] >= 0.90
}
approval_score = sum(approval_criteria.values())
total_criteria = len(approval_criteria)
if approval_score >= 5:
recommendation = "✅ APROVADO para produção"
elif approval_score >= 3:
recommendation = "⚠️ APROVADO COM RESSALVAS"
else:
recommendation = "❌ NÃO APROVADO - necessita melhorias"
print(f" {recommendation}")
print(f" Critérios atendidos: {approval_score}/{total_criteria}")
return approval_criteria, recommendation
🔗 Próximos Passos
- ⚡ API - Implementação da API de predição
- 🐳 Docker - Containerização do modelo
- 🧪 Testes - Testes automatizados
📞 Referências
- 🤖 Treinamento - Desenvolvimento dos modelos
- 🏗️ Pipeline ML - Arquitetura completa
- 📊 Análise de Dados - Insights dos dados