igor-millis~/research/threshold-is-a-decision
EN/PT
← todos os artigos

Um threshold é uma decisão

Por que 0,5 não é neutro, o que τ = 0,24 implica sobre o custo dos erros e como escolhê-lo sem tocar no conjunto de teste.

Um classificador como o XGBoost não entrega uma decisão. Ele entrega um escore \(\hat p(x) \in [0,1]\), e a decisão só aparece quando escolhemos uma regra:

\[ \hat y(x) = \mathbb{1}\{\hat p(x) \ge \tau\} \](1)

As bibliotecas usam \(\tau = 0{,}5\) por padrão. Esse padrão parece neutro, mas não é: ele embute uma crença específica sobre o custo dos erros. No projeto Credit Default, mover \(\tau\) de 0,50 para 0,24 elevou o recall dos inadimplentes de 35,6% para 60,1% — uma mudança maior que a de qualquer busca de hiperparâmetros.

§1A visão de custos

Seja \(c_{FP}\) o custo de sinalizar um cliente que pagaria (falso positivo) e \(c_{FN}\) o custo de deixar passar um inadimplente (falso negativo). Para um cliente com probabilidade real de inadimplência \(p\), o custo esperado de cada ação é

\[ \mathbb{E}[\text{custo} \mid \hat y = 1] = (1-p)\,c_{FP}, \qquad \mathbb{E}[\text{custo} \mid \hat y = 0] = p\,c_{FN}. \](2)

Prever “inadimplente” é a melhor ação sempre que \(p\,c_{FN} \ge (1-p)\,c_{FP}\), o que leva ao threshold ótimo de Bayes

\[ \tau^{*} = \frac{c_{FP}}{c_{FP} + c_{FN}}. \](3)

Ou seja, \(\tau = 0{,}5\) é ótimo apenas se os dois erros custam o mesmo. Invertendo (3), todo threshold implica uma razão de custos:

\[ \frac{c_{FN}}{c_{FP}} = \frac{1-\tau}{\tau}, \qquad \tau = 0{,}24 \;\Rightarrow\; \frac{c_{FN}}{c_{FP}} \approx 3{,}17. \](4)

Lido ao pé da letra: escolher 0,24 diz que deixar passar um inadimplente custa cerca de três vezes mais que um falso alarme. É uma afirmação com a qual uma equipe de crédito pode concordar ou discordar — e esse é justamente o ponto. Um threshold transforma uma premissa de negócio em um número.

Ressalva — calibração. A equação (3) vale para probabilidades. Se \(\hat p\) não for calibrado (se, entre clientes com escore 0,3, a taxa de inadimplência não for de cerca de 30%), a razão em (4) é apenas indicativa. A calibração não foi avaliada no projeto — a nota seguinte faz isso: os escores são calibrados e a razão implícita se mantém em cerca de 2,7–3,2×.

§2O que o projeto fez de fato

Não havia custos explícitos, então o threshold foi escolhido por um objetivo substituto: maximizar o F1 na validação (4.800 clientes, nunca usados na avaliação final). A busca em grade resultou em \(\tau = 0{,}24\), com F1 de validação = 0,5405. No conjunto de teste intocado (6.000 clientes), o mesmo threshold deu F1 = 0,5396 e recall = 0,6006 — quase nenhuma degradação, bom sinal de que a escolha não sofreu overfitting.

O explorador abaixo usa a curva do conjunto de teste do notebook de seleção de modelos (grade de 0,05) para mostrar todo o trade-off e a razão de custos que cada \(\tau\) implica:

xgboost (grid search) · teste · grade de 0,05n = 6.000
0,50
precisionrecallF1
0,000,250,500,751,000,10,30,50,70,9τ=0,24
—
precision
—
recall
—
F1
—
cFN/cFP implícito
Curva: notebooks/09_model_selection.ipynb (teste, n = 6.000). O threshold operacional 0,24 foi escolhido maximizando o F1 na validação (F1 = 0,5405) em notebooks/010_model_validation.ipynb. Razão de custos implícita = (1 − τ)/τ, válida só se as probabilidades forem calibradas.

§3Uma checagem pela literatura

Para um classificador calibrado, o threshold que maximiza o F1 é igual à metade do F1 máximo atingível (Lipton, Elkan & Narayanaswamy, 2014):

\[ \tau_{F1}^{*} = \frac{F1^{*}}{2}. \](5)

Com \(F1^{*} \approx 0{,}54\) na validação, (5) prevê \(\tau \approx 0{,}27\). O ótimo empírico, 0,24, fica próximo. Atualização: a nota seguinte verifica a calibração — os escores são bem calibrados e a diferença é ruído amostral: o intervalo bootstrap de 95% do threshold ótimo de F1 é [0,22; 0,33]. Isso também explica por que thresholds ótimos para F1 costumam ficar bem abaixo de 0,5: o F1 é no máximo 1, então, para um modelo calibrado, o threshold ótimo nunca passa de 0,5.

§4Conclusões

  • O threshold faz parte do modelo. Reporte, justifique e versione — por isso a API devolve o threshold em toda previsão.
  • Escolha \(\tau\) na validação, nunca no teste. O teste serve para um único número final e honesto.
  • Se os custos são conhecidos, use (3). Se não, um substituto como o F1 funciona — mas traduza o resultado de volta em razão de custos com (4) e pergunte se faz sentido.
  • Verifique a calibração antes de ler escores como probabilidades.

Referência: Z. C. Lipton, C. Elkan, B. Narayanaswamy. Thresholding Classifiers to Maximize F1 Score. ECML PKDD, 2014. · Dados: github.com/Igor-Millis/credit-default-api, notebooks 09 e 010.