mozak.tech Engenharia de IA Corporativa 67%

Parte III — Governança, Conformidade e Responsabilidade na IA

3.2 — Interpretabilidade e Explicação de Decisões de Modelos (XAI)

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ável

LIME: 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ão

LLM 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].text

Aprofundamento 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 traduz

Padrão 2: Contrafactual como serviço

"O que você pode fazer para ser aprovado:" → engajamento, não frustração

Armadilhas

⚠️ 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 prompt
⚗ Laboratório prático — mozak.tech
Se 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.