Un certain temps s'est écoulé et je pense que je pourrais avoir une solution à portée de main. Je décrirai brièvement mon approche pour vous donner une idée générale. Le code devrait être suffisant pour comprendre les détails. J'aime joindre du code ici, mais c'est beaucoup et stackexchange ne facilite pas la tâche. Je suis bien sûr heureux de répondre à vos commentaires, et j'apprécie également toute critique.
Le code se trouve ci-dessous.
La stratégie:
- Approximer une courbe ROC lisse en utilisant la fonction Logistique dans l'intervalle [0,6]
- En ajoutant un paramètre k, on peut influencer la forme de la courbe pour l'adapter à la qualité de modèle souhaitée, mesurée par l'ASC (Area Under Curve). La fonction résultante estFk( x ) =1( 1 + e x p ( - k ∗ x ) ). Si k-> 0, AUC approche 0,5 (pas d'optimisation), si k -> Inf, AUC approche 1 (modèle optimal). Comme approche pratique, k devrait être dans l'intervalle [0,0001,100]. Par certains calculs de base, on peut créer une fonction pour mapper k à AUC et vice versa.
- Maintenant, étant donné que vous avez une courbe roc qui correspond à l'ASC souhaitée, déterminez un score par échantillon à partir de [0,1] uniformément. Cela représente le fpr ( taux de faux positifs ) sur la courbe ROC. Par souci de simplicité, le score est alors calculé comme 1-fpr.
- L'étiquette est maintenant déterminée par échantillonnage à partir d'une distribution de Bernoulli avec p calculé en utilisant la pente de la courbe ROC à ce fpr et la précision globale souhaitée des scores. En détail: poids (label = "1"): = pente (fpr) multipliée par globalPrecision, poids (label = "0"): = 1 multiplié par (1-globalPrecision). Normalisez les poids afin qu'ils totalisent 1 pour déterminer p et 1-p.
Voici un exemple de courbe ROC pour AUC = 0,6 et précision globale = 0,1 (également dans le code ci-dessous)
Remarques:
- l'AUC résultante n'est pas exactement la même que l'ASC d'entrée, en fait, il y a une petite erreur (environ 0,02). Cette erreur provient de la façon dont l'étiquette d'un score est déterminée. Une amélioration pourrait être d'ajouter un paramètre pour contrôler la taille de l'erreur.
- le score est fixé à 1-fpr. C'est arbitraire car la courbe ROC ne se soucie pas de l'apparence des scores tant qu'ils peuvent être triés.
code:
# This function creates a set of random scores together with a binary label
# n = sampleSize
# basePrecision = ratio of positives in the sample (also called overall Precision on stats.stackexchange)
# auc = Area Under Curve i.e. the quality of the simulated model. Must be in [0.5,1].
#
binaryModelScores <- function(n,basePrecision=0.1,auc=0.6){
# determine parameter of logistic function
k <- calculateK(auc)
res <- data.frame("score"=rep(-1,n),"label"=rep(-1,n))
randUniform = runif(n,0,1)
runIndex <- 1
for(fpRate in randUniform){
tpRate <- roc(fpRate,k)
# slope
slope <- derivRoc(fpRate,k)
labSampleWeights <- c((1-basePrecision)*1,basePrecision*slope)
labSampleWeights <- labSampleWeights/sum(labSampleWeights)
res[runIndex,1] <- 1-fpRate # score
res[runIndex,2] <- sample(c(0,1),1,prob=labSampleWeights) # label
runIndex<-runIndex+1
}
res
}
# min-max-normalization of x (fpr): [0,6] -> [0,1]
transformX <- function(x){
(x-0)/(6-0) * (1-0)+0
}
# inverse min-max-normalization of x (fpr): [0,1] -> [0,6]
invTransformX <- function(invx){
(invx-0)/(1-0) *(6-0) + 0
}
# min-max-normalization of y (tpr): [0.5,logistic(6,k)] -> [0,1]
transformY <- function(y,k){
(y-0.5)/(logistic(6,k)-0.5)*(1-0)+0
}
# logistic function
logistic <- function(x,k){
1/(1+exp(-k*x))
}
# integral of logistic function
intLogistic <- function(x,k){
1/k*log(1+exp(k*x))
}
# derivative of logistic function
derivLogistic <- function(x,k){
numerator <- k*exp(-k*x)
denominator <- (1+exp(-k*x))^2
numerator/denominator
}
# roc-function, mapping fpr to tpr
roc <- function(x,k){
transformY(logistic(invTransformX(x),k),k)
}
# derivative of the roc-function
derivRoc <- function(x,k){
scalFactor <- 6 / (logistic(6,k)-0.5)
derivLogistic(invTransformX(x),k) * scalFactor
}
# calculate the AUC for a given k
calculateAUC <- function(k){
((intLogistic(6,k)-intLogistic(0,k))-(0.5*6))/((logistic(6,k)-0.5)*6)
}
# calculate k for a given auc
calculateK <- function(auc){
f <- function(k){
return(calculateAUC(k)-auc)
}
if(f(0.0001) > 0){
return(0.0001)
}else{
return(uniroot(f,c(0.0001,100))$root)
}
}
# Example
require(ROCR)
x <- seq(0,1,by=0.01)
k <- calculateK(0.6)
plot(x,roc(x,k),type="l",xlab="fpr",ylab="tpr",main=paste("ROC-Curve for AUC=",0.6," <=> k=",k))
dat <- binaryModelScores(1000,basePrecision=0.1,auc=0.6)
pred <- prediction(dat$score,as.factor(dat$label))
performance(pred,measure="auc")@y.values[[1]]
perf <- performance(pred, measure = "tpr", x.measure = "fpr")
plot(perf,main="approximated ROC-Curve (random generated scores)")