Objetivo da Aula
Ao concluir esta aula, você será capaz de:
Diferenciar interpretabilidade (interna) de explicabilidade (externa)
Implementar LIME manual via perturbação de features
Usar LLM para gerar explicações em linguagem natural para decisões de IA
Calcular importância de features via delta de score
Aplicar obrigações de explicabilidade do Brasil (LGPD) e Europa (EU AI Act)
Por que isso importa
Um sistema de concessão de crédito que diz apenas “NEGADO” viola a LGPD Art. 20 e o EU AI Act. A explicação não precisa revelar o modelo completo, mas deve ser suficiente para o usuário entender o que pode mudar. LLMs são excelentes geradores de explicações: recebem os fatores técnicos e produzem linguagem compreensível para leigos.
Conceitos Fundamentais
Fundamento: XAI e Métricas de Fairness
Interpretabilidade é entender o que acontece dentro do modelo; explicabilidade é conseguir explicar uma decisão específica a um humano não-especialista. LIME (Local Interpretable Model-agnostic Explanations) gera explicações locais: perturba o input e mede quais features mudaram a saída — as que mais mudaram são as "mais importantes" para aquela decisão específica. Para fairness: Paridade Demográfica exige taxas de aprovação iguais entre grupos; Igualdade de Oportunidade exige TPR (taxa de verdadeiro positivo) igual entre qualificados de grupos diferentes. O teorema de Chouldechova prova que essas métricas são matematicamente incompatíveis quando há diferença nas taxas base entre grupos — você precisa escolher qual otimizar com base no contexto de risco.
Interpretabilidade vs Explicabilidade
Interpretabilidade: entender como o modelo funciona internamente
- Pesos de uma regressão linear
- Feature importance do Random Forest
- Atenção em Transformers
Explicabilidade: descrever o comportamento do modelo para um humano
- "Seu crédito foi negado principalmente pelo alto índice de endividamento"
- Não precisa revelar o modelo — precisa ser compreensível e acionávelLIME: Local Interpretable Model-agnostic Explanations
def lime_manual(candidato: CandidatoCredito, modelo_fn, n_perturbacoes: int = 10) -> dict:
# Baseline: score do candidato original
baseline = modelo_fn(candidato)["score_interno"]
importancias = {}
# Feature 1: impacto de um score_historico ruim
# Criar versão do candidato com score_historico = 300 (mínimo)
c_sem_score = CandidatoCredito(
candidato.nome, candidato.renda_mensal, 300, # score reduzido
candidato.divida_atual, candidato.tempo_emprego_meses, candidato.valor_solicitado
)
importancias["score_historico"] = baseline - modelo_fn(c_sem_score)["score_interno"]
# Se delta alto → feature muito importante
# Feature 2: impacto de mais dívida
c_mais_divida = CandidatoCredito(
candidato.nome, candidato.renda_mensal, candidato.score_historico,
candidato.divida_atual * 3, # triplicar a dívida
candidato.tempo_emprego_meses, candidato.valor_solicitado
)
importancias["divida_atual"] = baseline - modelo_fn(c_mais_divida)["score_interno"]
# Feature 3: impacto de renda menor
c_renda_metade = CandidatoCredito(
candidato.nome, candidato.renda_mensal * 0.5, # metade da renda
candidato.score_historico, candidato.divida_atual,
candidato.tempo_emprego_meses, candidato.valor_solicitado
)
importancias["renda_mensal"] = baseline - modelo_fn(c_renda_metade)["score_interno"]
return importancias
# importancias: {"score_historico": 40, "divida_atual": 30, "renda_mensal": 0}
# → score_historico é o fator mais impactante nesta decisãoLLM como Gerador de Explicações
def explicar_com_llm(candidato: CandidatoCredito, resultado: dict) -> str:
prompt = f"""Um sistema de análise de crédito tomou a seguinte decisão:
Candidato: {candidato.nome}
Renda mensal: R$ {candidato.renda_mensal:,.0f}
Score histórico: {candidato.score_historico}/1000
Dívida atual: R$ {candidato.divida_atual:,.0f}
Tempo de emprego: {candidato.tempo_emprego_meses} meses
Valor solicitado: R$ {candidato.valor_solicitado:,.0f}
Decisão: {resultado['decisao']} (score interno: {resultado['score_interno']}/100)
Fatores analisados: {'; '.join(resultado['razoes'])}
Explique em linguagem clara para o candidato em 3-4 frases.
Mencione os 2 fatores mais importantes.
Se negado, sugira o que pode melhorar."""
response = client.messages.create(
model="claude-haiku-4-5-20251001",
max_tokens=300,
messages=[{"role": "user", "content": prompt}],
)
return response.content[0].textAprofundamento Técnico
XAI: Técnicas Principais
Técnica | Tipo | Custo | Melhor para |
LIME | Post-hoc, model-agnostic | Médio | Qualquer modelo caixa-preta |
SHAP | Post-hoc, model-agnostic | Alto | Maior precisão que LIME |
Feature Importance | Intrínseco (RF) | Baixo | Random Forest, XGBoost |
Attention weights | Intrínseco | Baixo | Transformers (correlacional, não causal) |
Counterfactual | Post-hoc | Médio | “O que precisaria mudar para aprovar?” |
Explicação Contrafactual
def gerar_contrafactual(candidato, modelo_fn) -> str:
"""O que precisaria mudar para aprovação?"""
resultado = modelo_fn(candidato)
if resultado["decisao"] == "APROVADO":
return "Candidato aprovado — não precisa de contrafactual."
# Simular: se score fosse 700?
c_melhor_score = CandidatoCredito(candidato.nome, candidato.renda_mensal, 700,
candidato.divida_atual, candidato.tempo_emprego_meses,
candidato.valor_solicitado)
if modelo_fn(c_melhor_score)["decisao"] == "APROVADO":
return "Aprovação possível se score_historico subir para ≥700"
return "Múltiplos fatores combinados impedem aprovação"Exemplos Anotados
Exemplo 1: Maria Santos
Candidato: Maria Santos
Score: 450, Dívida: R$8000 em renda de R$2500 = ratio 3.2 (alto)
Decisão: NEGADO (score 20/100)
LIME:
score_historico: +40 impacto (mediano)
divida_atual: +30 impacto (alto)
renda_mensal: 0 impacto (a renda não é determinante aqui)
Explicação LLM:
"Sua solicitação de crédito foi negada principalmente pelo alto nível de endividamento:
sua dívida atual é 3.2x sua renda mensal, acima do limite recomendado de 1x.
O score histórico de 450/1000 também contribuiu para a decisão.
Para melhorar: quitar parte das dívidas atuais antes de nova solicitação e
manter pagamentos em dia para elevar o score histórico acima de 700."Padrões e Armadilhas
Padrões
Padrão 1: Separar explicação técnica da explicação ao usuário
razoes_tecnicas = ["ratio_divida=3.2 (alto, 0 pts)", "score=450 (médio, +20 pts)"]
explicacao_usuario = explicar_com_llm(candidato, resultado) # LLM traduzPadrão 2: Contrafactual como serviço
"O que você pode fazer para ser aprovado:" → engajamento, não frustraçãoArmadilhas
⚠️ Armadilha 1: LIME com perturbação muito drástica
# Perturbação extrema: score 300 quando baseline é 780
# Isso não representa a vizinhança local do ponto
# LIME funciona melhor com perturbações menores (±20%)⚠️ Armadilha 2: LLM inventando fatores que não estavam na decisão
Factores reais: score, dívida, comprometimento
LLM pode inventar: "sua idade..." → nunca foi fator
Sempre forneça APENAS os fatores reais no promptSe não for realizar o laboratório, pule para o próximo capítulo.
Ponte para o Lab
O starter já está implementado. Foque nos exercícios em exercicios.md: 1. Analise os resultados do LIME para cada candidato 2. Proponha uma melhoria para a função lime_manual usando perturbações menores (10%) 3. Adicione explicação contrafactual para candidatos negados
Agora você está pronto para o lab.