Using repeated sampling to detect mislabeled observations¶

In lecture we discussed confidence in predictions. You could use ensemble methods and assess how often a certain observation is allocated to each class - are the different models in your assemble in agreement?

We feel more confident in a prediction if there is agreement between models in the ensemble - but what if the ensemble is confident but wrong? That is, what if across your train-test splits the ensemble almost always agrees on a certain class but this is the wrong class? This could indicate that the observation is mislabeled.

By contrast, if the ensemble is uncertain - this is perhaps an observation close to the decision boundary.

In [4]:
library(conformal) # requires a lot of other packages, mainly caret for the model types
library(randomForest)
Loading required package: caret

Loading required package: ggplot2

Warning message:
"package 'ggplot2' was built under R version 4.4.3"
Loading required package: lattice

Loading required package: e1071

Warning message:
"package 'e1071' was built under R version 4.4.3"
Loading required package: grid

No methods found in package 'randomForest' for request: 'predict.randomForest' when loading 'conformal'

No methods found in package 'randomForest' for request: 'plot.randomForest' when loading 'conformal'

randomForest 4.7-1.1

Type rfNews() to see new features/changes/bug fixes.


Attaching package: 'randomForest'


The following object is masked from 'package:ggplot2':

    margin


Mislabeling¶

I train on a subset of 0s and 1s using 25 leading PC components and record the predictions and errors I make on the test set. I aggregate across the runs and assess confidence and percentage of errors.

In [2]:
library(ElemStatLearn)
library(nsprcomp)

data(zip.train)
set.seed(1000)
Numbers<-zip.train[sample(seq(1,7291),2000),] # a subset of the digits

ssn<-svd(Numbers[,-1])
plot(ssn$d,type="l",xlab="Number of components",ylab="Eigenvalues")
# most of variance explained by 50 components (of 256)
plot(ssn$u[,1:2],xlab="PC1",ylab="PC2",pch=16)   # Plotting PC1 vs PC2
#pp<-identify(ssn$u[,1:2]) # use this to mark some of the observations if use in R interactive mode
aa<-rev(sort.list(ssn$u[,1]))
bb<-rev(sort.list(ssn$u[,2]))
au<-c(sample(aa[1:10],2),sample(aa[990:1000],1))
bu<-c(sample(bb[1:10],2),sample(bb[990:1000],1)) # Here I just pick some digits at the extremes of pc1 and pc2
points(ssn$u[au,1:2],col=2,pch=16,cex=1.5)       # and highlight them in the scatter plot
points(ssn$u[bu,1:2],col=3,pch=16,cex=1.5)
#
par(mfrow=c(3,3))                                # Plotting the corresponding digits
for (bb in (1:length(au))) {
   image(t(matrix(as.numeric(Numbers[au[bb],-1]),16,16,byrow=T))[,16:1], col = gray.colors(33)) }
for (bb in (1:length(bu))) {
   image(t(matrix(as.numeric(Numbers[bu[bb],-1]),16,16,byrow=T))[,16:1], col = gray.colors(33)) }
No description has been provided for this image
No description has been provided for this image
No description has been provided for this image
In [5]:
data(zip.train)
set.seed(1000)
# repeat train-test split - keep record of predictions and errors

NN <- zip.train[zip.train[,1]<=1,] # only 0s and 1s
aa <- sample(seq(1,dim(NN)[1]),100)
NN[aa,1] <- abs(NN[aa,1]-1)        # mislabeling 100 observations on purpose
n <- dim(NN)[1]
B <- 500                           # how many runs?

PredMat  <- matrix(-1, n, length(unique(NN[,1])))     # which prediction
CountMat <- matrix(-1, n, 1)     # is the observations in the test set?              
ErrMat   <- matrix(-1, n, 1)     # how many incorrect predictions? 
#
for (bb in (1:B)) {
 it<-sample(seq(1,n),floor(n/2)) #train
 it2<-seq(1,n)[-it]              #test
 Data <- NN
 colnames(Data) <- c("class", paste0("X_", seq(1,dim(NN)[2]-1)))
 Tr.data  <- Data[it,-1]
 Tr.label <- Data[it,1]
 Test.data <- Data[it2,-1]
 Test.label <- Data[it2,1]
 trControl <- trainControl(method = "cv",  number=5, savePredictions=TRUE)
 set.seed(3)
 # number of trees - of course you can add to tune this in the inner loop as well!
 nb_trees <- 20
 model <- train(Tr.data, as.factor(Tr.label),  # using random forest, you can use other caret models as well
         method="rf",type="classification", 
         trControl=trControl,predict.all=TRUE,
         keep.forest=TRUE,norm.votes=TRUE,
         ntree=nb_trees)
 pp <- predict(model, Test.data)
 izz <- seq(1,n)[it2] 
 for (zz in 0:1) {
    izzp <- izz[pp==zz]
    PredMat[izzp,zz+1]  <- PredMat[izzp,zz+1] + 1 
 }
 CountMat[it2] <- CountMat[it2] + 1
 err <- rep(0,length(pp))
 err[pp!=Test.label] <- 1
 ErrMat[it2] <- ErrMat[it2]+err
    }
###
In [26]:
PP<-apply(PredMat,1,which.max)          # winning class across splits
PA<-apply(PredMat,1,max)/CountMat       # confidence (percentage of votes on winning class)

#
plot(PA,ErrMat/CountMat+rnorm(length(ErrMat),sd=.01),xlab="% majority votes",ylab="% misclassifications")
points(PA[aa], ErrMat[aa]/CountMat[aa]+rnorm(length(ErrMat[aa]),sd=.01),col=2)
# NOTICE - the red observations (the mislabeled ones) are all high confidence and always wrong!
#
plot(seq(1,length(ErrMat)),PA,type="s",xlab="index",ylab="percentage winning class votes") 
points(seq(1,length(ErrMat))[aa],PA[aa],col=2) # confident 
plot(seq(1,length(ErrMat)),ErrMat/CountMat,type="s")
points(seq(1,length(ErrMat))[aa],(ErrMat/CountMat)[aa],col=2,xlab="index",ylab="percentage wrong class votes")
# but wrong all the time!
No description has been provided for this image
No description has been provided for this image
No description has been provided for this image