Machine learning do zero · Aula 2.6 · 13 de 33 aulas prontas · 12 min
Métrica e limiar pelo custo dos erros
Em resumo
O limiar se escolhe pelo preço dos erros, não por hábito. Quando uma falha custa 10 vezes mais que um alarme falso, o limiar mais barato fica perto de 0,09.
Aquecimento Duas perguntas de aulas anteriores. Um minuto, e a aula nunca fica bloqueada.
Na aula 2.3, as curvas mostraram um cardápio de limiares, e na aula 2.4 você verificou quando os números do modelo são porcentagens reais. Agora você vai escolher um limiar desse cardápio. Não por hábito (0,5) nem por uma métrica favorita, mas pelo que cada erro realmente custa.
Escolha uma resposta e o seu grau de certeza.
Responda para liberar o experimento.
Experimento interativo
Este experimento interativo funciona em um navegador com JavaScript ativado. O texto ao redor descreve o que ele mostra.
Ver a resposta
Resposta certa: Mais perto de 0,1, porque o alarme já compensa com um risco pequeno
Teoria da decisão: o alarme compensa quando p · 10 > (1 − p) · 1, ou seja, p > 1/11 ≈ 0,09. No lab, o limiar 0,09 custa 125 e o habitual 0,5 chega a 254. O limiar mais barato na validação, 0,06, custa 111, mas só nas mesmas pontuações usadas para escolhê-lo.
Experimente
- Mantenha os preços em 10 e 1 e baixe o limiar a partir de 0,5. Onde o custo é mais baixo? Quantas falhas e quantos alarmes falsos há então?
- Ponha os dois custos em 1. Onde está agora o limiar mais barato? Compare-o com o limiar de melhor F1.
- Deixe o alarme falso mais caro que a falha. De que lado de 0,5 cai o limiar mais barato?
- Volte aos preços 10 e 1 e compare três custos: o do limiar mais barato, o da verificação honesta e o do limiar da teoria. Qual escolha é a mais barata em biópsias que não foram usadas para fazê-la?
Depois de conferir no lab, vamos dar nome ao que você vê.
A ideia em palavras simples
Cada erro tem um preço. Um tumor maligno não detectado (FN) é uma doença descoberta tarde. Um alarme falso (FP) é uma biópsia desnecessária e estresse. Quando você conhece os preços, o custo de um limiar é uma soma simples: falhas vezes o preço de uma falha mais alarmes falsos vezes o preço de um alarme. O melhor limiar é o de menor soma nos dados de validação.
Com preços 10 e 1, o limiar 0,5 dá 24 falhas e 14 alarmes falsos: custo 254. O limiar mais barato na validação é 0,06: 2 falhas e 91 alarmes, custo 111. Esse número, porém, é otimista, porque o limiar foi escolhido e avaliado nas mesmas pontuações (mais em “Por que funciona”). Para comparar, biópsia para todas custa 357, e nenhuma biópsia, 2120.
Se o modelo está calibrado, o limiar também pode ser calculado sem procurar. Uma biópsia tem probabilidade de malignidade p. Um alarme custa então em média (1 − p) · 1, e não dar o alarme, p · 10. O alarme compensa quando p · 10 > (1 − p) · 1, ou seja, quando p > 1/11 ≈ 0,09. Em geral, o limiar é c_FP / (c_FP + c_FN). É um resultado da teoria da decisão, não uma medição: supõe que os números do modelo são porcentagens reais (aula 2.4). Este modelo está bem calibrado (ECE 0,010). No lab, o limiar 0,09 custa 125, menos da metade do custo do limiar 0,5, e ninguém o ajustou nestes dados.
Jak to policzono
- Biópsias: Breast Cancer Wisconsin (Diagnostic), UCI, licença CC BY 4.0. 569 biópsias (212 malignas, 357 benignas), 10 medidas “mean” de cada uma.
- Pontuações de validação cruzada em 5 partes (
StratifiedKFold(5)no scikit-learn): cada biópsia é pontuada por um modelo que nunca a viu. - Modelo: padronização e regressão logística (C = 1). Limiares de 0,01 a 0,99 em passos de 0,01.
- Preços dos erros: uma falha 10, um alarme falso 1. É um exemplo, não uma avaliação médica.
Por que funciona
O lab e a teoria quase concordam: o limiar mais barato sai 0,06, a teoria diz 0,09. A diferença vem de que 569 biópsias são poucas, enquanto uma falha pesa tanto quanto 10 alarmes, então a curva de custo dá saltos. O fundo dela é plano e irregular: o limiar 0,13 custa o mesmo que 0,06 (111), e 0,09 custa 125. Em outros 20 cortes em partes (código abaixo), o limiar mais barato cai entre 0,04 e 0,13, em média 0,08. A teoria acerta o meio desse intervalo, e o lab mostra o quanto o fundo é largo.
Mas o limiar mais barato tem uma armadilha. O lab testa 99 limiares e escolhe o que se saiu melhor nestas 569 biópsias, em parte graças a acertos de sorte. Por isso o custo 111 é otimista: é a mesma maldição do vencedor do ajuste (aula 3.5). Uma verificação honesta: escolha o limiar em 4/5 das biópsias e calcule o custo dele no 1/5 restante, cinco vezes. Escolhido assim, o limiar custa 141, mais que o limiar da teoria 0,09 (125), que ninguém ajustou. Em outros 20 cortes, o custo honesto do limiar escolhido vai de 106 a 153 (129 em média), enquanto o limiar da teoria custa de 105 a 136 (117 em média) e é mais barato em 16 de 20. Conclusão: com um modelo calibrado, o limiar da teoria é um ponto de partida forte e barato, e ajustar o limiar nos mesmos dados exagera a economia.
A direção é sempre a mesma: quanto mais cara a falha, mais baixo o limiar, e quanto mais caro o alarme falso, mais alto. Com preços iguais, o limiar mais barato é 0,65 (custo 33). Quando o alarme custa mais que a falha, o limiar mais barato cai acima de 0,5.
Erro comum
Verdadeiro ou falso: “F1 é uma boa métrica para tudo”. Falso. F1 = 2·TP / (2·TP + FN + FP), então o F1 pesa uma falha e um alarme falso da mesma forma, mesmo quando os preços deles são diferentes. No lab, o limiar de melhor F1 é 0,65 (F1 0,918). Com preços 10 e 1, ele custa 276: mais que o habitual 0,5 (254) e quase 2,5 vezes o limiar mais barato (111). Em outros 20 cortes, o limiar de melhor F1 custa de 1,6 a 2,9 vezes o mais barato.
Mesmo com preços iguais, F1 não é o mesmo que custo. No lab, com preços 1 e 1, o limiar mais barato cai justamente onde o F1 é melhor (0,65), mas isso não é regra. O custo com preços iguais é simplesmente o número de erros, enquanto o F1 é uma fração em que os “não” corretos (TN) não contam nada. Em outros 20 cortes, os dois limiares diferem em 3.
Quando você conhece os preços dos erros, calcule o custo. Quando não conhece, pergunte antes de escolher uma métrica: escolher uma métrica também é decidir preços, só que sem dizer.
Quiz
Três perguntas para você se testar. Escolha uma resposta ou abra a resposta embaixo de cada pergunta. No teclado: as setas escolhem, Enter ou Espaço confirma.
Pontos-chave
- O limiar se escolhe pelos preços dos erros: o custo é falhas vezes o preço delas mais alarmes falsos vezes o deles, calculado na validação.
- Para um modelo calibrado, a teoria dá o limiar c_FP / (c_FP + c_FN); com preços 10 e 1, cerca de 0,09. É um ponto de partida forte: um limiar ajustado nos mesmos dados parece mais barato do que é.
- Falhas mais caras baixam o limiar, alarmes falsos mais caros o sobem.
- O F1 pesa os dois erros da mesma forma e ignora os “não” corretos; quando você conhece os preços, calcule o custo.
Mais a fundo: fórmula, código, prática
A matemática
Custo de um limiar t: K(t) = c_FN · FN(t) + c_FP · FP(t). O limiar mais barato na validação é t = argmin K(t). O custo dele K(t) calculado nos mesmos dados é otimista; o custo honesto se calcula em dados que não foram usados para escolher t*.
Uma biópsia tem probabilidade de malignidade p. Um alarme custa em média (1 − p) · c_FP, e não dar o alarme, p · c_FN. O alarme compensa quando p · c_FN > (1 − p) · c_FP, ou seja, p > c_FP / (c_FP + c_FN). Para c_FN = 10 e c_FP = 1, isso dá 1 / 11 ≈ 0,09.
F1 = 2·TP / (2·TP + FN + FP): FN e FP têm o mesmo peso 1 no denominador.
No código (Python)
O mesmo experimento no scikit-learn: pontuações de validação cruzada, o custo de cada limiar de 0,01 a 0,99, o custo honesto do limiar escolhido e depois o mesmo em outros 20 cortes em partes.
import numpy as np
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import StratifiedKFold, cross_val_predict
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import f1_score
X, y = load_breast_cancer(return_X_y=True)
X, y = X[:, :10], 1 - y # as 10 medidas "mean", como no lab; 1 = maligno
model = make_pipeline(StandardScaler(), LogisticRegression())
t = np.arange(1, 100) / 100 # limiares 0.01 ... 0.99, como no lab
def costs(p, i=slice(None), c_fn=10, c_fp=1): # uma falha custa 10, um alarme falso 1; i = quais biópsias
return np.array([c_fn * ((p[i] < s) & (y[i] == 1)).sum() + c_fp * ((p[i] >= s) & (y[i] == 0)).sum() for s in t])
def scores(cv): # cada biópsia pontuada por um modelo que nunca a viu
return cross_val_predict(model, X, y, cv=cv, method="predict_proba")[:, 1]
def held_out(p, cv): # limiar mais barato escolhido em 4/5 das biópsias, custo no 1/5 restante, cinco vezes
return sum(costs(p, b)[costs(p, a).argmin()] for a, b in cv.split(X, y))
p = scores(StratifiedKFold(5))
c, f1 = costs(p), np.array([f1_score(y, p >= s) for s in t])
print(f"cheapest threshold {t[c.argmin()]:.2f} (cost {c.min()}); theory 1/11 = {1 / 11:.2f} (cost {c[8]}); 0.5 costs {c[49]}; best F1 at {t[f1.argmax()]:.2f} costs {c[f1.argmax()]}")
print(f"picked on 4/5, priced on the other 1/5: {held_out(p, StratifiedKFold(5))}; theory 1/11: {c[8]}")
r = []
for s in range(20): # outros 20 cortes em 5 partes
cv = StratifiedKFold(5, shuffle=True, random_state=s)
q = scores(cv)
cq, fq = costs(q), np.array([f1_score(y, q >= u) for u in t])
r.append((t[cq.argmin()], cq[fq.argmax()] / cq.min(), cq[49] / cq.min(), held_out(q, cv), cq[8], costs(q, c_fn=1).argmin() != fq.argmax()))
r = np.array(r)
print(f"20 other cuts: cheapest threshold {r[:, 0].min():.2f} to {r[:, 0].max():.2f} (mean {r[:, 0].mean():.2f}); best F1 costs {r[:, 1].min():.1f} to {r[:, 1].max():.1f} times the cheapest, 0.5 costs {r[:, 2].min():.1f} to {r[:, 2].max():.1f} times")
print(f"20 other cuts, held out: the picked threshold costs {r[:, 3].min():.0f} to {r[:, 3].max():.0f} (mean {r[:, 3].mean():.0f}), theory 1/11 costs {r[:, 4].min():.0f} to {r[:, 4].max():.0f} (mean {r[:, 4].mean():.0f}); theory is cheaper in {(r[:, 4] < r[:, 3]).sum():.0f} of 20; at equal prices the cheapest and the best-F1 threshold differ in {r[:, 5].sum():.0f} of 20")
Saída
cheapest threshold 0.06 (cost 111); theory 1/11 = 0.09 (cost 125); 0.5 costs 254; best F1 at 0.65 costs 276
picked on 4/5, priced on the other 1/5: 141; theory 1/11: 125
20 other cuts: cheapest threshold 0.04 to 0.13 (mean 0.08); best F1 costs 1.6 to 2.9 times the cheapest, 0.5 costs 2.0 to 2.5 times
20 other cuts, held out: the picked threshold costs 106 to 153 (mean 129), theory 1/11 costs 105 to 136 (mean 117); theory is cheaper in 16 of 20; at equal prices the cheapest and the best-F1 threshold differ in 3 of 20
Com o corte StratifiedKFold(5), os números são os do lab: limiar mais barato 0,06 (custo 111), teoria 0,09 (125), o limiar 0,5 custa 254 e o de melhor F1, 276. Em outros 20 cortes, o limiar mais barato cai entre 0,04 e 0,13, em média 0,08, e o habitual 0,5 custa de 2,0 a 2,5 vezes o mais barato. O limiar escolhido em 4/5 das biópsias e avaliado no 1/5 restante custa 141, como no lab. Em outros 20 cortes, essa escolha custa de 106 a 153 (129 em média), o limiar da teoria de 105 a 136 (117 em média), e a teoria é mais barata em 16 de 20. Com preços iguais, o limiar mais barato e o de melhor F1 diferem em 3 de 20 cortes.
Baixar o notebook (.ipynb). Abra no Jupyter ou envie para o Google Colab; ele baixa sozinho os dados que faltarem.
Para quem pratica
- Defina os preços dos erros com as pessoas que sofrem as consequências (uma médica, uma equipe de risco) antes de ver os resultados do modelo. Senão, é fácil escolher preços que combinem com um limiar de que você já gosta.
- No scikit-learn,
TunedThresholdClassifierCVcom o seu próprioscoring(por exemplo, viamake_scorer) ajusta o limiar a um custo. Você aplica um limiar à mão assim:predict_proba(X)[:, 1] >= t. - Ajuste o limiar na validação e confira o custo dele em dados que você não usou para escolhê-lo (o teste ou uma parte separada, como no lab). Informe o custo ao lado de duas referências simples: “todas para biópsia” e “nenhuma”.
- Use a fórmula c_FP / (c_FP + c_FN) só com um modelo calibrado (aula 2.4). Quando o modelo é confiante demais, procure o limiar na curva de custo.
Isto foi útil?
Obrigado! Isso nos ajuda a melhorar.
Vá mais fundo no Atlas
Verbetes da enciclopédia sobre as ideias desta aula: