Phase 01 - Lesson 07
Teorema de Bayes
This lesson includes a graded coding exercise that runs in your browser, unlocked with lifetime access.
Probabilidade é sobre o que você espera. O teorema de Bayes é sobre o que você aprende.
Tipo: Build Linguagem: Python Pré-requisitos: Fase 1, Lição 06 (Fundamentos de Probabilidade) Tempo: ~75 minutos
Objetivos de Aprendizagem
- Aplicar o teorema de Bayes para calcular probabilidades a posteriori a partir de priors, verossimilhanças é evidência
- Construir um classificador de texto Naive Bayes do zero com suavização de Laplace e cálculo em espaço logarítmico
- Comparar a estimação MLE e MAP e explicar como MAP corresponde à regularização L2
- Implementar a atualização bayesiana sequencial usando priors conjugados Beta-Binomial para testes A/B
O Problema
Um teste médico é 99% preciso. Você testa positivo. Quais são as chances de você realmente ter a doença?
A maioria das pessoas diz 99%. A resposta real depende de quão rara é a doença. Se 1 em 10.000 pessoas a tem, um resultado positivo só te dá cerca de 1% de chance de estar doente. Os outros 99% dos resultados positivos são alarmes falsos de pessoas saudáveis.
Isso não é uma pegadinha. É o teorema de Bayes. Todo filtro de spam, todo diagnóstico médico, todo modelo de machine learning que quantifica incerteza usa exatamente esse raciocínio. Você começa com uma crença. Você vê evidência. Você atualiza.
Se você construir sistemas de ML sem entender isso, vai interpretar mal as saídas dos modelos, definir limiares ruins e entregar previsões excessivamente confiantes.
O Conceito
Da probabilidade conjunta ao Bayes
Você já sabe da Lição 06 que a probabilidade condicional é:
P(A|B) = P(A e B) / P(B)
E simetricamente:
P(B|A) = P(A e B) / P(A)
Ambas as expressões compartilham o mesmo numerador: P(A e B). Iguale-as e reorganize:
P(A e B) = P(A|B) * P(B) = P(B|A) * P(A)
Portanto:
P(A|B) = P(B|A) * P(A) / P(B)
Esse é o teorema de Bayes. Quatro quantidades, uma equação.
As quatro partes
| Parte | Nome | O que significa |
|---|---|---|
| P(A|B) | Posterior | Sua crença atualizada sobre A depois de ver a evidência B |
| P(B|A) | Verossimilhança | Quão provável é a evidência B se A for verdadeiro |
| P(A) | Prior | Sua crença sobre A antes de ver qualquer evidência |
| P(B) | Evidência | Probabilidade total de observar B sob todas as possibilidades |
O termo da evidência P(B) atua como um normalizador. Você pode expandi-lo usando a lei da probabilidade total:
P(B) = P(B|A) * P(A) + P(B|nao A) * P(nao A)
Exemplo do teste médico
Uma doença afeta 1 em 10.000 pessoas. O teste é 99% preciso (detecta 99% das pessoas doentes, dá falsos positivos 1% das vezes).
P(doente) = 0.0001 (prior: a doenca e rara)
P(positivo|doente) = 0.99 (verossimilhanca: o teste detecta)
P(positivo|saudavel) = 0.01 (taxa de falso positivo)
P(positivo) = P(positivo|doente) * P(doente) + P(positivo|saudavel) * P(saudavel)
= 0.99 * 0.0001 + 0.01 * 0.9999
= 0.000099 + 0.009999
= 0.010098
P(doente|positivo) = P(positivo|doente) * P(doente) / P(positivo)
= 0.99 * 0.0001 / 0.010098
= 0.0098
= 0.98%
Menos de 1%. O prior domina. Quando uma condição é rara, até testes precisos produzem em sua maioria falsos positivos. É por isso que médicos pedem testes de confirmação.
Exemplo do filtro de spam
Você recebe um e-mail contendo a palavra "loteria". É spam?
P(spam) = 0.3 (30% dos e-mails sao spam)
P("loteria"|spam) = 0.05 (5% dos e-mails de spam contem "loteria")
P("loteria"|nao spam) = 0.001 (0.1% dos e-mails legitimos contem "loteria")
P("loteria") = 0.05 * 0.3 + 0.001 * 0.7
= 0.015 + 0.0007
= 0.0157
P(spam|"loteria") = 0.05 * 0.3 / 0.0157
= 0.955
= 95.5%
Uma palavra muda a probabilidade de 30% para 95.5%. Um filtro de spam real aplica Bayes a centenas de palavras simultaneamente.
Naive Bayes: a suposição de independência
O Naive Bayes estende isso para múltiplas features assumindo que todas as features são condicionalmente independentes dada a classe:
P(classe | feature_1, feature_2, ..., feature_n)
= P(classe) * P(feature_1|classe) * P(feature_2|classe) * ... * P(feature_n|classe)
/ P(feature_1, feature_2, ..., feature_n)
A parte "naive" (ingênua) é a suposição de independência. Em texto, ocorrências de palavras não são independentes ("New" e "York" são correlacionadas). Mas a suposição funciona surpreendentemente bem na prática porque o classificador só precisa ordenar as classes, não produzir probabilidades calibradas.
Como o denominador é o mesmo para todas as classes, você pode ignorá-lo e apenas comparar os numeradores:
score(classe) = P(classe) * produto de P(feature_i | classe)
Escolha a classe com o maior score.
Estimação por máxima verossimilhança (MLE)
Como você obtém P(feature|classe) a partir dos dados de treinamento? Conte.
P("free"|spam) = (numero de e-mails de spam contendo "free") / (total de e-mails de spam)
Isto é MLE: escolher os valores de parâmetro que tornam os dados observados mais prováveis. Você está maximizando a função de verossimilhança, que para contagens discretas se reduz a frequência relativa.
Problema: se uma palavra nunca aparece em spam durante o treinamento, o MLE lhe dá a probabilidade zero. Uma única palavra não vista mata todo o produto. Corrija isso com a suavização de Laplace:
P(palavra|classe) = (count(palavra, classe) + 1) / (total_de_palavras_na_classe + tamanho_do_vocabulario)
Adicionar 1 a cada contagem garante que nenhuma probabilidade seja nunca zero.
Máximo a posteriori (MAP)
O MLE pergunta: quais parâmetros maximizam P(dados|parâmetros)?
O MAP pergunta: quais parâmetros maximizam P(parâmetros|dados)?
Pelo teorema de Bayes:
P(parametros|dados) proporcional a P(dados|parametros) * P(parametros)
O MAP adiciona um prior sobre os próprios parâmetros. Se você acredita que os parâmetros devem ser pequenos, você codifica isso como um prior que penaliza valores grandes. Isso é idêntico à regularização L2 em ML. A penalidade "ridge" na regressão ridge é literalmente um prior Gaussiano sobre os pesos.
| Estimação | Otimiza | Equivalente em ML |
|---|---|---|
| MLE | P(dados|params) | Treinamento sem regularização |
| MAP | P(dados|params) * P(params) | Regularização L2 / L1 |
Bayesiano vs frequentista: a diferença prática
Os frequentistas tratam os parâmetros como incognitas fixas. Eles perguntam: "Se eu repetisse este experimento muitas vezes, o que aconteceria?"
Os bayesianos tratam os parâmetros como distribuições. Eles perguntam: "Dado o que eu observei, no que acredito sobre os parâmetros?"
Para construir sistemas de ML, a diferença prática:
| Aspecto | Frequentista | Bayesiano |
|---|---|---|
| Saída | Estimativa pontual | Distribuição sobre valores |
| Incerteza | Intervalos de confiança (sobre o procedimento) | Intervalos de credibilidade (sobre o parâmetro) |
| Poucos dados | Pode sofrer overfitting | O prior atua como regularização |
| Computação | Geralmente mais rápida | Frequentemente requer amostragem (MCMC) |
A maior parte do ML em produção é frequentista (SGD, estimativas pontuais). Os métodos bayesianos brilham quando você precisa de incerteza calibrada (decisões médicas, sistemas críticos de segurança) ou quando os dados são escassos (few-shot learning, partida a frio).
Por que o pensamento bayesiano importa para ML
A conexão é mais profunda do que uma analogia:
Priors são regularização. Um prior Gaussiano sobre os pesos é regularização L2. Um prior de Laplace é L1. Toda vez que você adiciona um termo de regularização, você está fazendo uma afirmação bayesiana sobre quais valores de parâmetro você espera.
Posteriores são incerteza. Uma única probabilidade prevista não te diz nada sobre quão confiante o modelo está nessa estimativa. Os métodos bayesianos te dão uma distribuição: "Acho que P(spam) está entre 0.8 e 0.95."
Atualizacoes de Bayes são aprendizado online. O posterior de hoje vira o prior de amanhã. Quando seu modelo vê novos dados, ele atualiza suas crenças incrementalmente em vez de retreinar do zero.
Comparação de modelos é bayesiana. O critério de informação bayesiano (BIC), a verossimilhança marginal e os fatores de Bayes usam raciocínio bayesiano para escolher entre modelos sem overfitting.
Construa
Passo 1: Função do teorema de Bayes
def bayes(prior, likelihood, false_positive_rate):
evidence = likelihood * prior + false_positive_rate * (1 - prior)
posterior = likelihood * prior / evidence
return posterior
result = bayes(prior=0.0001, likelihood=0.99, false_positive_rate=0.01)
print(f"P(sick|positive) = {result:.4f}")
Passo 2: Classificador Naive Bayes
import math
from collections import defaultdict
class NaiveBayes:
def __init__(self, smoothing=1.0):
self.smoothing = smoothing
self.class_counts = defaultdict(int)
self.word_counts = defaultdict(lambda: defaultdict(int))
self.class_word_totals = defaultdict(int)
self.vocab = set()
def train(self, documents, labels):
for doc, label in zip(documents, labels):
self.class_counts[label] += 1
words = doc.lower().split()
for word in words:
self.word_counts[label][word] += 1
self.class_word_totals[label] += 1
self.vocab.add(word)
def predict(self, document):
words = document.lower().split()
total_docs = sum(self.class_counts.values())
vocab_size = len(self.vocab)
best_class = None
best_score = float("-inf")
for cls in self.class_counts:
score = math.log(self.class_counts[cls] / total_docs)
for word in words:
count = self.word_counts[cls].get(word, 0)
total = self.class_word_totals[cls]
score += math.log((count + self.smoothing) / (total + self.smoothing * vocab_size))
if score > best_score:
best_score = score
best_class = cls
return best_class
As log-probabilidades evitam o underflow. Multiplicar muitas probabilidades pequenas produz números pequenos demais para ponto flutuante. Somar log-probabilidades é numericamente estável e matematicamente equivalente.
Passo 3: Treinar com dados de spam
train_docs = [
"win free money now",
"free lottery ticket winner",
"claim your prize today free",
"urgent offer free cash",
"congratulations you won free",
"meeting tomorrow at noon",
"project update attached",
"can we schedule a call",
"quarterly report review",
"lunch on thursday sounds good",
"team standup notes attached",
"please review the pull request",
]
train_labels = [
"spam", "spam", "spam", "spam", "spam",
"ham", "ham", "ham", "ham", "ham", "ham", "ham",
]
classifier = NaiveBayes()
classifier.train(train_docs, train_labels)
test_messages = [
"free money waiting for you",
"meeting rescheduled to friday",
"you won a free prize",
"please review the attached report",
]
for msg in test_messages:
print(f" '{msg}' -> {classifier.predict(msg)}")
Passo 4: Inspecionar as probabilidades aprendidas
def show_top_words(classifier, cls, n=5):
vocab_size = len(classifier.vocab)
total = classifier.class_word_totals[cls]
probs = {}
for word in classifier.vocab:
count = classifier.word_counts[cls].get(word, 0)
probs[word] = (count + classifier.smoothing) / (total + classifier.smoothing * vocab_size)
sorted_words = sorted(probs.items(), key=lambda x: x[1], reverse=True)
for word, prob in sorted_words[:n]:
print(f" {word}: {prob:.4f}")
print("\nTop spam words:")
show_top_words(classifier, "spam")
print("\nTop ham words:")
show_top_words(classifier, "ham")
Use
O Scikit-learn fornece implementações de naive Bayes prontas para produção:
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.naive_bayes import MultinomialNB
from sklearn.metrics import classification_report
vectorizer = CountVectorizer()
X_train = vectorizer.fit_transform(train_docs)
clf = MultinomialNB()
clf.fit(X_train, train_labels)
X_test = vectorizer.transform(test_messages)
predictions = clf.predict(X_test)
for msg, pred in zip(test_messages, predictions):
print(f" '{msg}' -> {pred}")
O mesmo algoritmo. O CountVectorizer cuida da tokenização e construção do vocabulário. O MultinomialNB cuida da suavização e das log-probabilidades internamente. Sua versão feita do zero faz a mesma coisa em 40 linhas.
Entregue
A classe NaiveBayes construída aqui demonstra o pipeline completo: tokenização, estimação de probabilidade com suavização de Laplace, previsão em espaço logarítmico. O código em code/bayes.py roda de ponta a ponta sem dependências além da biblioteca padrão do Python.
Priors Conjugados
Quando o prior e o posterior pertencem à mesma família de distribuições, o prior é chamado de "conjugado". Isso torna a atualização bayesiana algebricamente limpa -- você obtém um posterior em forma fechada sem integração numérica.
| Verossimilhança | Prior Conjugado | Posterior | Exemplo |
|---|---|---|---|
| Bernoulli | Beta(a, b) | Beta(a + sucessos, b + falhas) | Estimação de viés de moeda |
| Normal (variância conhecida) | Normal(mu_0, sigma_0) | Normal(média ponderada, variância menor) | Calibração de sensor |
| Poisson | Gamma(a, b) | Gamma(a + soma das contagens, b + n) | Modelagem de taxas de chegada |
| Multinomial | Dirichlet(alpha) | Dirichlet(alpha + contagens) | Modelagem de tópicos, modelos de linguagem |
Por que isso importa: sem priors conjugados, você precisa de amostragem de Monte Carlo ou inferência variacional para aproximar o posterior. Com priors conjugados, você só atualiza dois números.
A distribuição Beta é o prior conjugado mais comum na prática. Beta(a, b) representa sua crença sobre um parâmetro de probabilidade. A média é a/(a+b). Quanto maior a+b, mais concentrada (confiante) é a distribuição.
Casos especiais do prior Beta:
- Beta(1, 1) = uniforme. Você não tem opinião sobre o parâmetro.
- Beta(10, 10) = com pico em 0.5. Você acredita fortemente que o parâmetro está perto de 0.5.
- Beta(1, 10) = enviesado em direção a 0. Você acredita que o parâmetro é pequeno.
A regra de atualização é absurdamente simples:
Prior: Beta(a, b)
Dados: s sucessos, f falhas
Posterior: Beta(a + s, b + f)
Sem integrais. Sem amostragem. Apenas adição.
Atualização Bayesiana Sequencial
A inferência bayesiana é naturalmente sequencial. O posterior de hoje vira o prior de amanhã. É assim que sistemas reais aprendem incrementalmente sem reprocessar todos os dados históricos.
Exemplo concreto: estimar se uma moeda é justa.
Dia 1: Ainda sem dados. Comece com Beta(1, 1) -- um prior uniforme. Você não tem opinião.
- Média do prior: 0.5
- O prior é plano em [0, 1]
Dia 2: Observe 7 caras, 3 coroas. Posterior = Beta(1 + 7, 1 + 3) = Beta(8, 4)
- Média do posterior: 8/12 = 0.667
- A evidência sugere que a moeda está enviesada para caras
Dia 3: Observé mais 5 caras, mais 5 coroas. Use o posterior de ontem como o prior de hoje. Posterior = Beta(8 + 5, 4 + 5) = Beta(13, 9)
- Média do posterior: 13/22 = 0.591
- Os novos dados equilibrados puxaram a estimativa de volta em direção a 0.5
graph LR
A["Prior<br/>Beta(1,1)<br/>mean = 0.50"] -->|"7H, 3T"| B["Posterior 1<br/>Beta(8,4)<br/>mean = 0.67"]
B -->|"becomes prior"| C["Prior 2<br/>Beta(8,4)"]
C -->|"5H, 5T"| D["Posterior 2<br/>Beta(13,9)<br/>mean = 0.59"]
A ordem das observações não importa. Beta(1,1) atualizado com todas as 12 caras e 8 coroas de uma vez resulta em Beta(13, 9) -- o mesmo resultado. A atualização sequencial e a atualização em lote são matematicamente equivalentes. Mas a atualização sequencial permite tomar decisões a cada passo sem armazenar dados brutos.
Esta é a base do aprendizado online em sistemas de ML em produção. A amostragem de Thompson para bandidos, sistemas de recomendação incrementais e detectores de anomalias em streaming usam esse padrão.
Conexão com Testes A/B
O teste A/B é inferência bayesiana disfarçada.
Cenário: você está testando duas cores de botão. Variante A (azul) e variante B (verde). Você quer saber qual recebe mais cliques.
O teste A/B bayesiano:
- Prior. Comece com Beta(1, 1) para ambas as variantes. Sem preferência prévia.
- Dados. Variante A: 50 cliques em 1000 visualizações. Variante B: 65 cliques em 1000 visualizações.
- Posteriores.
- A: Beta(1 + 50, 1 + 950) = Beta(51, 951). Média = 0.051
- B: Beta(1 + 65, 1 + 935) = Beta(66, 936). Média = 0.066
- Decisão. Calcule P(B > A) -- a probabilidade de que a taxa de conversão verdadeira de B seja maior que a de A.
Calcular P(B > A) analiticamente é difícil. Mas o Monte Carlo torna isso trivial:
1. Amostre 100.000 amostras de Beta(51, 951) -> samples_A
2. Amostre 100.000 amostras de Beta(66, 936) -> samples_B
3. P(B > A) = fracao das amostras onde B > A
Se P(B > A) > 0.95, você entrega a variante B. Se estiver entre 0.05 e 0.95, você continua coletando dados. Se P(B > A) < 0.05, você entrega a variante A.
Vantagens sobre o teste A/B frequentista:
- Você obtém uma afirmação direta de probabilidade: "há 97% de chance de B ser melhor"
- Sem confusão de p-valor. Sem o jargão de "não rejeitar a hipótese nula".
- Você pode verificar resultados a qualquer momento sem inflar as taxas de falso positivo (sem o "problema de espiar")
- Você pode incorporar conhecimento previo (por exemplo, testes anteriores sugerem que as taxas de conversão geralmente são de 3-8%)
| Aspecto | A/B Frequentista | A/B Bayesiano |
|---|---|---|
| Saída | p-valor | P(B > A) |
| Interpretação | "Quão surpreendentes são esses dados se A=B?" | "Quão provável é que B seja melhor que A?" |
| Parada antecipada | Infla os falsos positivos | Segura em qualquer ponto (dado um prior bem escolhido e um modelo corretamente especificado) |
| Conhecimento previo | Não usado | Codificado como prior Beta |
| Regra de decisão | p < 0.05 | P(B > A) > limiar |
Exercícios
Testes múltiplos. Um paciente testa positivo duas vezes em testes independentes (ambos 99% precisos, prevalência da doença 1 em 10.000). Qual é P(doente) após os dois testes? Use o posterior do primeiro teste como o prior do segundo.
Impacto da suavização. Rode o classificador de spam com valores de suavização de 0.01, 0.1, 1.0 e 10.0. Como mudam as probabilidades das palavras mais frequentes? O que acontece com suavização=0 e uma palavra que aparece apenas em ham?
Adicione features. Estenda a classe NaiveBayes para também usar o comprimento da mensagem (curta/longa) como uma feature ao lado das contagens de palavras. Estime P(curta|spam) e P(curta|ham) a partir dos dados de treinamento e incorpore-as no score de previsão.
MAP à mão. Dados os dados observados (7 caras em 10 lançamentos de moeda), calcule a estimativa MAP do viés usando um prior Beta(2,2). Compare-a com a estimativa MLE (7/10).
Termos-Chave
| Termo | O que as pessoas dizem | O que realmente significa |
|---|---|---|
| Prior | "Meu palpite inicial" | P(hipótese) antes de observar a evidência. Em ML: o termo de regularização. |
| Verossimilhança | "Quão bem os dados se ajustam" | P(evidência|hipótese). Quão provável são os dados observados sob uma hipótese específica. |
| Posterior | "Minha crença atualizada" | P(hipótese|evidência). O prior multiplicado pela verossimilhança, depois normalizado. |
| Evidência | "A constante de normalização" | P(dados) sobre todas as hipóteses. Garante que o posterior some 1. |
| Naive Bayes | "Aquele classificador de texto simples" | Um classificador que assume que as features são independentes dada a classe. Funciona bem apesar da suposição falsa. |
| Suavização de Laplace | "Suavização add-one" | Adicionar uma pequena contagem a cada feature para evitar probabilidades zero de dados não vistos. |
| MLE | "Só use as frequências" | Escolher parâmetros que maximizam P(dados|parâmetros). Sem prior. Pode sofrer overfitting com poucos dados. |
| MAP | "MLE com um prior" | Escolher parâmetros que maximizam P(dados|parâmetros) * P(parâmetros). Equivalente a MLE regularizado. |
| Log-probabilidade | "Trabalhar em espaço logarítmico" | Usar log(P) em vez de P para evitar o underflow de ponto flutuante ao multiplicar muitos números pequenos. |
| Falso positivo | "Um alarme errado" | O teste diz positivo, mas o estado verdadeiro é negativo. Causa a falácia da taxa de base. |
Leitura Adicional
- 3Blue1Brown: Bayes' theorem - explicação visual com o exemplo do teste médico
- Stanford CS229: Generative Learning Algorithms - naive Bayes e sua conexão com modelos discriminativos
- Think Bayes - livro gratuito, estatística bayesiana com código Python
- scikit-learn Naive Bayes - implementações de produção e quando usar cada variante