Introdução aos Gráficos SHAP
Hoje, o trabalho foca em reflexões conceituais com um nível de dificuldade moderado.
- Com base na documentação fornecida, complete os tipos restantes de visualizações
- Tente determinar os requisitos de dimensão para cada parâmetro nas funções de plotagem SHAP, como as necessidades de formato para o gráfico de força (shap.force_plot)
- Identifique como os dados para problemas de classificação e regressão devem ser formatados, utilizadno o dataset de crédito para classificação e o dataset de bicicletas para regressão.
# Execute o código de pré-processamento anterior
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import warnings
warnings.filterwarnings('ignore')
# Configuração de fonte para caracteres chineses
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
# Carregamento dos dados
dataset = pd.read_csv('dados.csv')
# Seleção de variáveis categóricas
recursos_discretos = dataset.select_dtypes(include=['object']).columns.tolist()
# Codificação de rótulo para 'Propriedade da Casa'
propriedade_mapping = {
'Própria': 1,
'Alugada': 2,
'Financiamento': 3,
'Hipoteca': 4
}
dataset['Propriedade da Casa'] = dataset['Propriedade da Casa'].map(propriedade_mapping)
# Codificação de rótulo para 'Anos no Emprego Atual'
anos_emprego_mapping = {
'< 1 ano': 1,
'1 ano': 2,
'2 anos': 3,
'3 anos': 4,
'4 anos': 5,
'5 anos': 6,
'6 anos': 7,
'7 anos': 8,
'8 anos': 9,
'9 anos': 10,
'10+ anos': 11
}
dataset['Anos no Emprego Atual'] = dataset['Anos no Emprego Atual'].map(anos_emprego_mapping)
# Codificação one-hot para 'Propósito'
dataset_codificado = pd.get_dummies(dataset, columns=['Propósito'])
dataset_original = pd.read_csv("dados.csv")
novos_recursos = []
for coluna in dataset_codificado.columns:
if coluna not in dataset_original.columns:
novos_recursos.append(coluna)
for recurso in novos_recursos:
dataset_codificado[recurso] = dataset_codificado[recurso].astype(int)
# Mapeamento de 'Prazo' para 0-1
prazo_mapping = {
'Curto Prazo': 0,
'Longo Prazo': 1
}
dataset_codificado['Prazo'] = dataset_codificado['Prazo'].map(prazo_mapping)
dataset_codificado.rename(columns={'Prazo': 'Longo Prazo'}, inplace=True)
recursos_continuos = dataset_codificado.select_dtypes(include=['int64', 'float64']).columns.tolist()
# Preenchimento de valores ausentes em recursos contínuos com a moda
for recurso in recursos_continuos:
moda = dataset_codificado[recurso].mode()[0]
dataset_codificado[recurso].fillna(moda, inplace=True)
# Divisão do conjunto de dados
from sklearn.model_selection import train_test_split
X = dataset_codificado.drop(['Inadimplência de Crédito'], axis=1)
y = dataset_codificado['Inadimplência de Crédito']
X_treino, X_teste, y_treino, y_teste = train_test_split(X, y, test_size=0.2, random_state=42)
# Modelo de Floresta Aleatória
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score
from sklearn.metrics import classification_report, confusion_matrix
import warnings
warnings.filterwarnings('ignore')
print("--- 1. Floresta Aleatória com parâmetros padrão ---")
import time
inicio = time.time()
modelo_rf = RandomForestClassifier(random_state=42)
modelo_rf.fit(X_treino, y_treino)
predicoes = modelo_rf.predict(X_teste)
fim = time.time()
print(f"Tempo de treino e previsão: {fim - inicio:.4f} segundos")
print("\nRelatório de classificação da Floresta Aleatória no conjunto de teste:")
print(classification_report(y_teste, predicoes))
print("Matriz de confusão da Floresta Aleatória no conjunto de teste:")
print(confusion_matrix(y_teste, predicoes))
Princípios Básicos do SHAP
Objetivo: Compreender por que modelos complexos de machine learning (especialmente modelos "caixa preta", como florestas aleatórias, árvores de boosting gradiente e redes neurais) fazem previsões específicas para entradas específicas. O SHAP fornece um método unificado para explicar as saídos dos modelos.
Conceito Central: Valores de Shapley da Teoria dos Jogos Cooperativos
O SHAP (SHapley Additive exPlanations) tem como base o conceito de Valores de Shapley da teoria dos jogos cooperativos. Imagine um jogo cooperativo:
- Jogadores: Os recursos (features) do modelo são os jogadores.
- Jogo: O objetivo é prever o valor de saída para uma amostra específica.
- Cooperação: Diferentes subconjuntos de recursos podem "cooperar" para fazer previsões.
- Recompensa/Valor: O valor obtido pela previsão de um subconjunto específico de recursos.
- Objetivo: Como justamente distribuir o resultado da previsão final (o "ganho" em relação à previsão média) para cada recurso participante (jogador)?
Abordagem do Cálculo dos Valores de Shapley (conceitualmente):
Para calcular a contribuição de um recurso específico (por exemplo, "Recurso A") para uma previsão, o SHAP considera:
- Todos os possíveis combinações de recursos (subconjuntos/coalizões): Do conjunto sem recursos ao conjunto com todos os recursos.
- Contribuição marginal do Recurso A: Para cada combinação de recursos, comparar a "previsão do conjunto que inclui o Recurso A" com a "previsão do conjunto que não inclui o Recurso A mas inclui os mesmos outros recursos". A diferença é a "contribuição marginal" do Recurso A para essa combinação específica.
- Média ponderada: O Valor de Shapley é a média ponderada das contribuições marginais do recurso em todos os possíveis subconjuntos de recursos. Os pesos garantem a justiça da distribuição.
Características Prnicipais do SHAP (Explicação Aditiva):
Uma característica importatne do SHAP é sua natureza aditiva. Isso significa:
- Valor Base (Base Value / Expected Value): É a previsão média do modelo em todo o conjunto de dados de treinamento (ou de fundo). Pode ser entendido como a previsão "padrão" sem nenhuma informação de recurso.
- Soma dos Valores SHAP: Para qualquer amostra, a soma de todos os valores SHAP mais o valor base é exatamente igual à previsão do modelo para essa amostra.
Valor da Previsão do Modelo (Amostra X) = Valor Base + SHAP(Recurso1) + SHAP(Recurso2) + ... + SHAP(RecursoN)
Por que é gerado um array valores\_shap?
Com base nos princípios acima, o SHAP precisa calcular um valor de contribuição (Valor SHAP) para cada recurso de cada amostra:
- Explicar previsões individuais: O cerne do SHAP é explicar previsões individuais.
- Contribuição dos recursos: Para essa previsão, precisamos saber se cada recurso está "empurrando" a previsão para "cima" ou para "baixo" (em relação ao valor base) e quão forte é esse empurrão.
- Numeração: O tamanho e direção desse "empurrão" é o Valor SHAP daquele recurso para aquela previsão da amostra.
Portanto:
- Para problemas de regressão:
- O modelo tem apenas uma saída.
- Para cada uma das
n\_amostras, calcula-se o Valor SHAP den\_recursosrecursos. < Isso naturalmente forma um array de formato(n\_amostras, n\_recursos).valores\_shap\[i, j\]representa a contribuição do recursojda amostraipara o valor de previsão daquela amostra.
- Para problemas de classificação:
- O modelo geralmente produz uma pontuação ou probabilidade para cada classe.
- O SHAP precisa explicar como o modelo obteve a pontuação para cada classe.
- Portanto, para cada uma das
n\_amostras, calcula-sen\_recursosValores SHAP para cada classe. - A organização mais comum é retornar uma lista cujo comprimento é igual ao número de classes. O elemento
kda lista é um array(n\_amostras, n\_recursos)que representa a contribuição de todos os recursos de todas as amostras para a previsão da classek. valores\_shap\[k\]\[i, j\]representa a contribuição do recursojda amostraipara a previsão da classekpara aquela amostra. Resumo:
O SHAP, calculando a contribuição marginal de cada recurso para uma previsão individual (em relação à previsão média), oferece um método para decompor a previsão do modelo em cada recurso. Essa decomposição é necessária para cada amostra e cada recurso (e para cada classe em problemas de classificação), resultando na estrutura do array valores\_shap que observamos.
import shap
import matplotlib.pyplot as plt
# Inicializador do explicador SHAP
explicador = shap.TreeExplainer(modelo_rf)
# Cálculo dos Valores SHAP (baseado no conjunto de teste)
# Este valores_shap é um array numpy que representa a contribuição de cada recurso para cada amostra
valores_shap = explicador.shap_values(X_teste)
# Os requisitos de dimensão dos dados serão uma das coisas mais importantes ao estudar redes neurais no futuro.
print("Formato de valores_shap:", valores_shap.shape)
print("Formato de valores_shap[0]:", valores_shap[0].shape)
print("Formato de valores_shap[:, :, 0]:", valores_shap[:, :, 0].shape)
print("Formato de X_teste:", X_teste.shape)
1. Gráfico de Importância de Recursos SHAP (Summary Plot - Bar)
# --- 1. Gráfico de Importância de Recursos SHAP (Summary Plot - Bar) ---
print("--- 1. Gráfico de Importância de Recursos SHAP (Bar) ---")
shap.summary_plot(valores_shap[:, :, 0], X_teste, plot_type="bar", show=False)
plt.title("Importância de Recursos SHAP (Gráfico de Barras)")
plt.show()
2. Gráfico de Violino de Importância de Recursos SHAP (Summary Plot - Violin)
# --- 2. Gráfico de Violino de Importância de Recursos SHAP (Summary Plot - Violin) ---
print("--- 2. Gráfico de Violino de Importância de Recursos SHAP ---")
shap.summary_plot(valores_shap[:, :, 0], X_teste, plot_type="violin", show=False, max_display=10)
plt.title("Importância de Recursos SHAP (Gráfico de Violino)")
plt.show()
# Note os parâmetros acima: plot_type pode ser "bar" ou "violin", max_display controla quantos recursos são exibidos (padrão é 20)