mlr-org / mlr3learners

Recommended learners for mlr3
https://mlr3learners.mlr-org.com
GNU Lesser General Public License v3.0
89 stars 14 forks source link

cv.glmnet weights are inverted #281

Closed tdhock closed 4 months ago

tdhock commented 7 months ago

Hi! First of all, thanks for maintaining mlr3, which I find very useful. I noticed that the weights from mlr3learners::LearnerClassifCVGlmnet are the opposite/negative of the weights from plain cv.glmnet. I expected they should be the same, or at least this difference should be documented, so is this a bug? Here is some code that can be run to see the difference:

N <- 100
x <- cbind(rnorm(N)+0:1, rnorm(N))
y <- factor(rep(c("A","B"),l=N))
library(glmnet)
orig.fit <- cv.glmnet(x, y, family="binomial")
cv_glmnet <- mlr3learners::LearnerClassifCVGlmnet$new()
sim_task <- mlr3::TaskClassif$new(
  "simulated", data.frame(x,y), target="y")
cv_glmnet$train(sim_task)
coef(cv_glmnet$model)
coef(orig.fit)
sessionInfo()

The result that I got on my system is shown below:

(base) tdhock@maude-MacBookPro:~/R$ R --vanilla < mlr3-glmnet-weight-issue.R

R version 4.3.2 (2023-10-31) -- "Eye Holes"
Copyright (C) 2023 The R Foundation for Statistical Computing
Platform: x86_64-pc-linux-gnu (64-bit)

R is free software and comes with ABSOLUTELY NO WARRANTY.
You are welcome to redistribute it under certain conditions.
Type 'license()' or 'licence()' for distribution details.

  Natural language support but running in an English locale

R is a collaborative project with many contributors.
Type 'contributors()' for more information and
'citation()' on how to cite R or R packages in publications.

Type 'demo()' for some demos, 'help()' for on-line help, or
'help.start()' for an HTML browser interface to help.
Type 'q()' to quit R.

> N <- 100
> x <- cbind(rnorm(N)+0:1, rnorm(N))
> y <- factor(rep(c("A","B"),l=N))
> library(glmnet)
Loading required package: Matrix
Loaded glmnet 4.1-8
> orig.fit <- cv.glmnet(x, y, family="binomial")
> cv_glmnet <- mlr3learners::LearnerClassifCVGlmnet$new()
> sim_task <- mlr3::TaskClassif$new(
+   "simulated", data.frame(x,y), target="y")
> cv_glmnet$train(sim_task)
> coef(cv_glmnet$model)
3 x 1 sparse Matrix of class "dgCMatrix"
                    s1
(Intercept)  0.0861301
X1          -0.2009159
X2           .        
> coef(orig.fit)
3 x 1 sparse Matrix of class "dgCMatrix"
                    s1
(Intercept) -0.0861301
V1           0.2009159
V2           .        
> sessionInfo()
R version 4.3.2 (2023-10-31)
Platform: x86_64-pc-linux-gnu (64-bit)
Running under: Ubuntu 18.04.6 LTS

Matrix products: default
BLAS:   /usr/lib/x86_64-linux-gnu/blas/libblas.so.3.7.1 
LAPACK: /usr/lib/x86_64-linux-gnu/lapack/liblapack.so.3.7.1

locale:
 [1] LC_CTYPE=en_US.UTF-8       LC_NUMERIC=C              
 [3] LC_TIME=fr_FR.UTF-8        LC_COLLATE=en_US.UTF-8    
 [5] LC_MONETARY=fr_FR.UTF-8    LC_MESSAGES=en_US.UTF-8   
 [7] LC_PAPER=fr_FR.UTF-8       LC_NAME=C                 
 [9] LC_ADDRESS=C               LC_TELEPHONE=C            
[11] LC_MEASUREMENT=fr_FR.UTF-8 LC_IDENTIFICATION=C       

time zone: America/Phoenix
tzcode source: system (glibc)

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
[1] glmnet_4.1-8   Matrix_1.6-1.1

loaded via a namespace (and not attached):
 [1] crayon_1.5.2         data.table_1.14.10   listenv_0.9.0       
 [4] backports_1.4.1      paradox_0.11.1       mlr3misc_0.13.0     
 [7] grid_4.3.2           mlr3_0.17.0          foreach_1.5.2       
[10] palmerpenguins_0.1.1 compiler_4.3.2       codetools_0.2-19    
[13] Rcpp_1.0.11          future_1.33.0        lattice_0.21-9      
[16] digest_0.6.33        R6_2.5.1             parallelly_1.36.0   
[19] parallel_4.3.2       splines_4.3.2        shape_1.4.6         
[22] checkmate_2.3.1      uuid_1.1-1           tools_4.3.2         
[25] mlr3learners_0.5.7   iterators_1.0.14     globals_0.16.2      
[28] lgr_0.4.4            survival_3.5-7      
> 

the important part of the output above is the coef method, which shows that the signs are inverted.

tdhock commented 7 months ago

this may be fixed by removing swap_levels? https://github.com/mlr-org/mlr3learners/blob/main/R/LearnerClassifCVGlmnet.R#L105

sebffischer commented 4 months ago

Hey @tdhock, thanks for bringing this up. This seems to be intentional to keep it consistent with the behavior of glmnet:

https://github.com/mlr-org/mlr3learners/commit/fbb2275aa0b0f56eaba183a28b307d92999e6df1

Also it seems to be documented under the section Internal Encoding.

tdhock commented 4 months ago

This seems to be intentional to keep it consistent with the behavior of glmnet:

You mean glm, right? I guess that is reasonable, but as a user of glmnet, I would expect mlr3 output to match glmnet output (not glm, which I don't use very much, and I was not aware of the alternative convention).

sebffischer commented 4 months ago

Yes right, glm.