Voltar ao blog
12 de maio de 20264 min de leitura

Distribuições de Hipóteses: Modelando Incerteza em vez de Respostas Absolutas

pythonaimath

A maioria dos sistemas de IA trata conhecimento como binário: sabe ou não sabe, verdadeiro ou falso. Mas pergunte a qualquer especialista "qual a melhor linguagem de programação?" e a resposta será "depende". Não existe uma resposta certa, existem múltiplas hipóteses com probabilidades diferentes dependendo do contexto. Como modelar isso computacionalmente sem perder a nuance?

A estrutura que uso é uma HypothesisDistribution: um conjunto de até 8 hipóteses competindo entre si, cada uma com sua probabilidade, atualizadas bayesianamente conforme evidências chegam. Não é "Python é a melhor linguagem com 70% de certeza". É "60% Python, 25% JavaScript, 15% Go para backend". A distribuição inteira é a resposta, não apenas o dominante.

Guia de tópicos:

  • Por Que Múltiplas Hipóteses em vez de Uma Resposta
  • A Estrutura: Max 8 Hipóteses Competindo
  • O Papel do "Desconhecido"
  • Atualização Bayesiana Simplificada
  • Limite de Hipóteses e Evicção
  • Exemplo Prático em Python
  • Considerações Finais

Por Que Múltiplas Hipóteses em vez de Uma Resposta

Se o sistema recebe "a capital do Brasil é Brasília" de uma fonte e "a capital do Brasil é Rio de Janeiro" de outra (talvez um texto histórico pré-1960), o que fazer? Com fato único, você escolhe um e descarta o outro, perdendo informação. Com distribuição, mantém ambos: {Brasília: 0.7, Rio: 0.3}. A próxima evidência resolve naturalmente.

Isso é especialmente importante para domínios onde não existe resposta única: "melhor framework web", "causa da inflação", "linguagem mais rápida". Manter a distribuição preserva a nuance que um fato binário destruiria. E permite que o sistema responda "existem opiniões divergentes sobre isso" em vez de escolher um lado arbitrariamente.

A Estrutura: Max 8 Hipóteses Competindo

É um dicionário simples: {hipótese: probabilidade}. As probabilidades sempre somam 1.0 (normalização obrigatória). O dominante é a hipótese com maior probabilidade. A entropia mede quão "espalhada" está a distribuição.

O limite de 8 hipóteses é um trade-off entre expressividade e eficiência. Na prática, raramente uma crença tem mais de 4-5 alternativas relevantes. O limite evita que distribuições cresçam indefinidamente com hipóteses de probabilidade ínfima que nunca serão relevantes.

O Papel do "Desconhecido"

Quando a primeira evidência chega para um par (sujeito, verbo) novo, o sistema automaticamente cria uma hipótese "desconhecido" com probabilidade complementar. Se a evidência diz (Brasil, capital, Brasília) com likelihood 0.8, a distribuição fica {Brasília: 0.8, desconhecido: 0.2}.

Isso é epistemologicamente importante: uma única fonte não deveria gerar certeza absoluta. O "desconhecido" representa "talvez exista outra resposta que eu ainda não vi". Conforme mais evidências confirmam, o desconhecido vai para quase zero. Mas nunca chega a zero absoluto (MIN_PROB = 0.02), mantendo sempre uma porta aberta para correção futura.

Atualização Bayesiana Simplificada

Quando nova evidência chega: para cada hipótese, multiplica probabilidade anterior pela verossimilhança da evidência dado aquela hipótese. A hipótese favorecida recebe likelihood alto, as outras recebem (1 - likelihood) / (n - 1). Depois normaliza.

O efeito: evidência forte (likelihood 0.9) concentra rapidamente. Evidência fraca (0.55) muda pouco. Múltiplas fracas na mesma direção convergem tanto quanto uma forte. É acúmulo bayesiano natural.

Limite de Hipóteses e Evicção

Quando uma nova hipótese chega e o limite de 8 é atingido, a mais fraca é removida. Sua probabilidade é redistribuída na normalização. Isso é aceitável porque hipóteses com probabilidade < 0.02 já são praticamente irrelevantes para qualquer decisão.

Probabilidades nunca vão abaixo de 0.02 (MIN_PROB) e likelihood de entrada é clampada entre 0.01 e 0.99. Nenhuma evidência gera certeza absoluta. Isso permite correção futura: mesmo uma crença com 0.98 pode ser revertida com evidência contrária suficiente.

Exemplo Prático em Python

import numpy as np

class HypothesisDistribution:
    """Distribution over competing hypotheses with Bayesian updates."""

    MAX_HYPS = 8
    MIN_PROB = 0.02

    def __init__(self):
        self.hyps: dict[str, float] = {}

    def update(self, obj: str, likelihood: float) -> str:
        """Update with evidence favoring 'obj'. Returns status string."""
        likelihood = float(np.clip(likelihood, 0.01, 0.99))

        # Add hypothesis if new
        if obj not in self.hyps:
            if len(self.hyps) >= self.MAX_HYPS:
                # Evict weakest
                weakest = min(self.hyps, key=self.hyps.get)
                del self.hyps[weakest]
            self.hyps[obj] = max(1.0 - sum(self.hyps.values()), 0.05)

        # Epistemic humility: single evidence shouldn't mean certainty
        if len(self.hyps) == 1:
            self.hyps["_unknown"] = 1.0 - likelihood

        # Bayesian update
        def p_ev(h):
            return likelihood if h == obj else (1-likelihood)/max(len(self.hyps)-1, 1)

        unnorm = {h: p_ev(h) * p for h, p in self.hyps.items()}
        total = sum(unnorm.values())
        self.hyps = {h: v/total for h, v in unnorm.items() if v/total >= self.MIN_PROB}

        # Renormalize
        t = sum(self.hyps.values())
        self.hyps = {h: p/t for h, p in self.hyps.items()}

        dom, conf = self.dominant()
        return f"dominant='{dom}' p={conf:.3f} H={self.entropy():.3f}"

    def dominant(self) -> tuple[str, float]:
        if not self.hyps:
            return ("unknown", 0.0)
        best = max(self.hyps, key=self.hyps.get)
        return best, self.hyps[best]

    def entropy(self) -> float:
        probs = np.array(list(self.hyps.values()))
        probs = probs[probs > 0]
        return float(-np.sum(probs * np.log2(probs + 1e-12)))

    def show(self):
        for h, p in sorted(self.hyps.items(), key=lambda x: -x[1]):
            bar = "█" * int(p * 40)
            print(f"    {h:20s} {bar} {p:.3f}")


# Demo: competing hypotheses converging
print("=== Best backend language (subjective, multiple valid answers) ===\n")
dist = HypothesisDistribution()

opinions = [
    ("python", 0.6, "Dev 1 prefers Python"),
    ("go", 0.7, "Dev 2 strongly prefers Go"),
    ("python", 0.65, "Dev 3 also likes Python"),
    ("javascript", 0.5, "Dev 4 suggests Node.js"),
    ("python", 0.7, "Dev 5 confirms Python"),
    ("go", 0.6, "Dev 6 likes Go too"),
]

for obj, lk, source in opinions:
    result = dist.update(obj, lk)
    print(f"  {source} → {result}")
    dist.show()
    print()

print(f"Final: {dist.dominant()}")
print(f"The distribution preserves the nuance: Python leads but Go is a strong alternative.")

Considerações Finais

Modelar conhecimento com distribuições de hipóteses em vez de fatos absolutos é mais trabalho, mas produz um sistema fundamentalmente mais honesto. Ele nunca está "errado de forma irrecuperável" (qualquer crença pode ser corrigida). Ele nunca está "certo demais" (sempre mantém dúvida residual). E ele preserva nuance em vez de forçar respostas binárias.

O padrão é aplicável a qualquer sistema que lida com informação incerta: recomendação, diagnóstico, classificação. Em vez de forçar uma resposta, mantenha a distribuição e deixe a evidência convergir naturalmente.


Links indicativos: