Visualização da calibração da probabilidade prevista de um modelo

23

Suponha que eu tenha um modelo preditivo que produz, para cada instância, uma probabilidade para cada classe. Agora reconheço que existem muitas maneiras de avaliar esse modelo se eu quiser usar essas probabilidades para classificação (precisão, recall, etc.). Também reconheço que uma curva ROC e a área sob ela podem ser usadas para determinar quão bem o modelo diferencia as classes. Não é disso que estou perguntando.

Estou interessado em avaliar a calibração do modelo. Eu sei que uma regra de pontuação como a pontuação Brier pode ser útil para esta tarefa. Tudo bem, e provavelmente incorporarei algo nesse sentido, mas não tenho certeza de quão intuitivas serão essas métricas para o leigo. Estou procurando algo mais visual. Quero que a pessoa que interpreta os resultados possa ver se, quando o modelo prevê algo com 70% de probabilidade de acontecer, isso realmente acontece ~ 70% das vezes, etc.

Ouvi falar (mas nunca usei) de plotagens de QQ e, a princípio, pensei que era isso que estava procurando. No entanto, parece que isso realmente significa comparar duas distribuições de probabilidade . Isso não é diretamente o que eu tenho. Tenho, por várias instâncias, minha probabilidade prevista e, em seguida, se o evento realmente ocorreu:

Index    P(Heads)    Actual Result
    1          .4            Heads
    2          .3            Tails
    3          .7            Heads
    4         .65            Tails
  ...         ...              ...

Então, um enredo de QQ é realmente o que eu quero ou estou procurando outra coisa? Se um gráfico de QQ é o que eu deveria estar usando, qual é a maneira correta de transformar meus dados em distribuições de probabilidade?

Eu imagino que eu poderia classificar as duas colunas pela probabilidade prevista e depois criar alguns compartimentos. Esse é o tipo de coisa que devo fazer ou estou pensando em algum lugar? Eu estou familiarizado com várias técnicas de discretização, mas existe uma maneira específica de discretizar em caixas que é padrão para esse tipo de coisa?

Michael McGowan
fonte

Respostas:

19

Seu pensamento é bom.

John Tukey recomendou a divisão por metades: divida os dados em metades superior e inferior, divida essas metades e, em seguida, divida as metades extremas recursivamente. Comparado ao bin de largura igual, isso permite a inspeção visual do comportamento da cauda sem dedicar muitos elementos gráficos à maior parte dos dados (no meio).

Aqui está um exemplo (usando R) da abordagem de Tukey. (Não é exatamente o mesmo: ele implementou mletterum pouco diferente.)

Primeiro, vamos criar algumas previsões e alguns resultados compatíveis com essas previsões:

set.seed(17)
prediction <- rbeta(500, 3/2, 5/2)
actual <- rbinom(length(prediction), 1, prediction)
plot(prediction, actual, col="Gray", cex=0.8)

actual0 01mletterrn

mletter <- function(r,n) {
    lower <-  2 + floor(log(r/(n+1))/log(2))
    upper <- -1 - floor(log((n+1-r)/(n+1))/log(2))
    i <- 2*r > n
    lower[i] <- upper[i]
    lower
}

Usando isso, classificamos as previsões e os resultados e calculamos a média de cada uma delas. Ao longo do caminho, calculamos as populações de caixas:

classes <- mletter(rank(prediction), length(prediction))
pgroups <- split(prediction, classes)
agroups <- split(actual, classes)
bincounts <- unlist(lapply(pgroups, length)) # Bin populations
x <- unlist(lapply(pgroups, mean))           # Mean predicted values by bin
y <- unlist(lapply(agroups, mean))           # Mean outcome by bin

Para simbolizar o gráfico efetivamente, devemos tornar as áreas dos símbolos proporcionais à contagem de posições. Também pode ser útil variar um pouco as cores dos símbolos, de onde:

binprop <- bincounts / max(bincounts)
colors <- -log(binprop)/log(2)
colors <- colors - min(colors)
colors <- hsv(colors / (max(colors)+1))

Com isso em mãos, agora aprimoramos o gráfico anterior:

abline(0,1, lty=1, col="Gray")                           # Reference curve
points(x,y, pch=19, cex = 3 * sqrt(binprop), col=colors) # Solid colored circles
points(x,y, pch=1, cex = 3 * sqrt(binprop))              # Circle outlines

Figura

Como exemplo de uma previsão ruim, vamos alterar os dados:

set.seed(17)
prediction <- rbeta(500, 5/2, 1)
actual <- rbinom(length(prediction), 1, 1/2 + 4*(prediction-1/2)^3)

Repetir a análise produz esse gráfico no qual os desvios são claros:

Figura 2

Esse modelo tende a ser super-otimista (o resultado médio para previsões na faixa de 50% a 90% é muito baixo). Nos poucos casos em que a previsão é baixa (menos de 30%), o modelo é muito pessimista.

whuber
fonte
(+1) Muito bom, obrigado. Acho que as cores podem distrair um pouco o objetivo, mas o resto foi uma boa idéia e uma explicação muito boa.
Michael McGowan
Michael, descobri que era necessária alguma cor para ajudar a ver os círculos muito pequenos que aparecem em cada extremidade. Uma cor constante conseguiria isso, é claro. Apenas substitua col=colorspela cor desejada, como col="Red".
whuber
+1, isso é muito bom. No entanto, não entendo muito bem por que a linha de referência é uma linha simples e reta de 45 graus, em vez da linha de regressão logística adequada ou de uma loess? Eu acho que essas seriam referências mais apropriadas contra as quais julgar a qualidade das previsões.
gung - Restabelece Monica
pp±[0 0,1]×[0 0,1]
whuber
p(1-p)/npn
4

Outra opção é a regressão isotônica. É semelhante à resposta do whuber, exceto que os compartimentos são gerados dinamicamente, em vez de serem divididos ao meio, com a exigência de que os resultados estejam aumentando estritamente.

Esse uso primário da regressão isotônica é recalibrar suas probabilidades se elas forem mal calibradas, mas também pode ser usado para visualização. Basicamente, se a linha de regressão isotônica seguir aproximadamente a linha Y = X, suas probabilidades serão calibradas corretamente.

Regressão isotônica em probabilidades

Esta é a regressão isotônica aplicada ao problema mostrado por Whuber.

import numpy as np
import matplotlib.pyplot as plt
from sklearn.isotonic import IsotonicRegression

prediction = np.random.beta(3.0/2.0, 5.0/2.0, size=500)
actual = np.random.binomial(1,prediction, len(prediction))
plt.scatter(prediction, actual,  facecolors='none', edgecolors=[0.3,0.3,0.3], label='Data')

ir = IsotonicRegression()
isotonic = ir.fit_transform(prediction, actual)
plt.plot(prediction, isotonic,'ok', label='Isotonic Fit')

plt.xlabel('Prediction')
plt.ylabel('Actual')
plt.plot([0,1],[0,1], '--k', label='y=x line')
plt.legend(loc = 'center left')

http://fa.bianp.net/blog/2013/isotonic-regression/

http://stat.wikia.com/wiki/Isotonic_regression

Bscan
fonte