Classificação: Por que Regressão Não Basta
Semana 3 começa um assunto novo: classificação. E antes de mais nada, um aviso de conteúdo: esse tema vai ficando mais denso rápido (regressão logística, fronteira de decisão, função de custo nova), então daqui pra frente eu vou puxar mais pra intuição e pro código explicado linha a linha do que pra matemática pesada. Se você quiser a derivação completa, ela existe e eu linko quando fizer sentido, mas o objetivo aqui é você entender o que o código faz e por quê, não decorar fórmula.
Regressão prevê número, classificação decide categoria
Regressão prevê um valor contínuo: preço de casa, tamanho de tumor em milímetros, qualquer coisa que pode ser 152.37. Classificação decide entre categorias: e-mail é spam ou não, tumor é benigno ou maligno. O caso mais simples, classificação binária, tem só duas opções, geralmente codificadas como 0 (negativo) e 1 (positivo).
x_train = np.array([0., 1, 2, 3, 4, 5])
y_train = np.array([0, 0, 0, 1, 1, 1])
Aqui x_train é o tamanho de um tumor (numa escala qualquer) e y_train é 0 pra benigno, 1 pra maligno. Repara que y só tem dois valores possíveis, nunca 0.3 ou 2.7, isso é o que diferencia esse problema de regressão.
A tentação: já sei regressão, por que não uso ela aqui?
A pergunta é honesta: se eu já sei ajustar uma reta a um conjunto de pontos, por que não ajusto uma reta em cima desse y 0/1 e uso um corte no meio (> 0.5 vira 1, < 0.5 vira 0)? Essa pergunta já apareceu, com bastante profundidade, lá na playlist de Reconhecimento de Padrões: o professor daquela matéria reaproveitou a própria classe de regressão linear pra classificar câncer de mama, e funcionou razoavelmente bem, até ele mostrar por que mínimos quadrados carrega um viés estrutural pra esse tipo de problema (o Bishop explica o porquê: mínimos quadrados assume ruído gaussiano, rótulo binário não tem ruído gaussiano). Aquele post fechou com um gancho explícito pra regressão logística, que ainda não tinha chegado na matéria. Pois chegou, só que numa playlist diferente: essa aqui.
O curso do Andrew Ng mostra o mesmo problema de um jeito mais visual, sem precisar do Bishop: adiciona um ponto extremo no dado e observa a reta se distorcer toda.
Testando ao vivo: a reta se distorce com um ponto extremo
O notebook original usa um widget clicável do Jupyter (plt_one_addpt_onclick) que só roda dentro do ambiente do curso, então reconstruí a mesma ideia como um componente interativo pro navegador: ajusto os mínimos quadrados de verdade, ao vivo, a cada clique.
w = 0.257 · b = -0.143 · acurácia: 100%
Sem os pontos extras, a reta encontrada (w≈0.257, b≈-0.143) já separa os seis pacientes perfeitamente, o corte em 0.5 cai bem no meio. Clica em "Adicionar tumores grandes": dois pacientes malignos novos aparecem lá longe, em x=10 e x=11. A reta, que tem que passar perto de todos os pontos, se achata pra tentar alcançar os novos, e o paciente em x=3 (que continua sendo maligno) passa a cair do lado errado da fronteira. Ele não mudou de lugar, a reta que se moveu por baixo dele. Isso é exatamente o mesmo problema estrutural que o Bishop explica: pontos "certos demais" longe da fronteira ainda puxam o ajuste, porque contribuem erro quadrático mesmo sem precisar.
O que fica dessa aula
O curso resume assim: regressão linear é insuficiente pra classificação. O lab seguinte já entra na correção.
Fechando
| O que eu já sabia | O que essa aula assentou |
|---|---|
| Regressão prevê número contínuo | Classificação decide entre categorias, e o rótulo nunca é um valor intermediário |
Um > 0.5 transforma número em decisão | Esse truque quebra quando um ponto longe da fronteira desloca a reta inteira, mesmo pontos "óbvios" continuam puxando o ajuste |
| Já vi essa fragilidade explicada pelo Bishop | Aqui vi ela de um jeito mais visual, bastando um ponto extremo pra quebrar o modelo na cara |
Aplicação Prática
Reaproveito o dataset de câncer de mama real (load_breast_cancer, 569 pacientes, o mesmo já usado na playlist de Reconhecimento de Padrões), com a mesma variável mais correlacionada de lá, worst concave points, normalizada por z-score usando só estatística do treino.
w, b = fit_linear_1d(X_train_z, y_train) # mínimos quadrados, fechado
print(f"w={w:.4f} b={b:.4f}")
Saída:
w=0.3812 b=0.3714, acurácia de treino 0.9099, acurácia de teste 0.9298.
A acurácia até que não é ruim (esse dataset é bem separável nessa variável, sem outlier chamativo pra quebrar tudo feito no exemplo de brinquedo acima). Mas repara no que a saída crua da regressão faz pros pacientes com tumor mais extremo:
scores = w * X_train_z.flatten() + b
print(scores.min(), scores.max())
Saída:
-0.2956até1.4043.
1.4043 não quer dizer nada como probabilidade de câncer, probabilidade não passa de 1. A reta não tem teto: ela extrapola livremente pra qualquer número, positivo ou negativo, por mais absurdo que seja como resposta pra "qual a chance desse tumor ser maligno?". É exatamente essa falta de teto que o próximo lab resolve.