Génération aléatoire de scores similaires à ceux d'un modèle de classification

8

Bonjour chers collègues

Je veux générer n scores aléatoires (avec une étiquette de classe) comme s'ils avaient été produits par un modèle de classification binaire. En détail, les propriétés suivantes sont requises:

  • chaque score est compris entre 0 et 1
  • chaque score est associé à une étiquette binaire avec des valeurs "0" ou "1" (ce dernier est de classe positive)
  • la précision globale des scores doit être par exemple de 0,1 (<- paramètre du générateur)
  • le rapport des scores avec l'étiquette "1" doit être supérieur à la précision globale dans la section supérieure et inférieur dans la section inférieure (<- la "qualité du modèle" doit également être un paramètre du générateur)
  • les scores doivent être tels que la courbe roc résultante soit lisse (et non par exemple qu'un groupe de scores avec le label "1" soit en haut et le reste des scores avec le label "1" soit en bas de la liste).

Quelqu'un at-il une idée de la façon d'aborder cela? Peut-être via la génération d'une courbe roc et ensuite générer les points de cette cure? Merci d'avance!

steffen
la source
a écrit "la précision globale des scores devrait être par exemple de 0,1" voulez-vous dire que vous voulez que le score soit une variable aléatoire avec une moyenne de 0,1? pourquoi ne générez-vous pas un problème de classification (par exemple avec des variables gaussiennes) et utilisez-vous quelque chose de simple et rapide (comme LDA) pour le résoudre? pouvez-vous nous en dire plus sur vos motivations?
Robin Girard
@Robin: Non, je parle de "précision" comme mesure de la qualité d'un modèle de classification binaire (voir par exemple en.wikipedia.org/wiki/Sensitivity_and_specificity ici). Concernant votre deuxième question: j'ai besoin des scores pour une simulation où plusieurs modèles de classification binaire sont combinés (plus dans le domaine du traitement des données que du Data Mining). Il est difficile d'expliquer la tâche en détail, mais je suis presque sûr d'avoir besoin de ces scores: D
steffen

Réponses:

4

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:

  1. Approximer une courbe ROC lisse en utilisant la fonction Logistique dans l'intervalle [0,6]
  2. 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+eXp(-kX)). 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.
  3. 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.
  4. 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) texte alternatif texte alternatif

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)")
steffen
la source
J'apprécierais que toute personne ayant plus de compétences en latex puisse modifier le calcul en 4. afin qu'il soit plus lisible.
steffen
puisque c'est exactement ce que je voulais, je vais le marquer comme "la réponse". Cependant, je suis toujours ouvert aux suggestions et idées.
steffen