Comment interpréter glmnet?

36

J'essaie d'adapter un modèle de régression linéaire multivarié avec environ 60 variables de prédicteur et 30 observations. J'utilise donc le package glmnet pour la régression régularisée, car p> n.

J'ai parcouru la documentation et d'autres questions, mais je ne peux toujours pas interpréter les résultats. Voici un exemple de code (avec 20 prédicteurs et 10 observations pour simplifier):

Je crée une matrice x avec num rangs = num observations et num cols = num prédicteurs et un vecteur y qui représente la variable de réponse

> x=matrix(rnorm(10*20),10,20)
> y=rnorm(10)

J'adapte un modèle de glmnet en laissant alpha par défaut (= 1 pour la pénalité de lasso)

> fit1=glmnet(x,y)
> print(fit1)

Je comprends que je reçois des prédictions différentes avec des valeurs décroissantes de lambda (c.-à-d. Une pénalité)

Call:  glmnet(x = x, y = y) 

        Df    %Dev   Lambda
  [1,]  0 0.00000 0.890700
  [2,]  1 0.06159 0.850200
  [3,]  1 0.11770 0.811500
  [4,]  1 0.16880 0.774600
   .
   .
   .
  [96,] 10 0.99740 0.010730
  [97,] 10 0.99760 0.010240
  [98,] 10 0.99780 0.009775
  [99,] 10 0.99800 0.009331
 [100,] 10 0.99820 0.008907

Maintenant, je prédis mes valeurs bêta en choisissant, par exemple, la plus petite valeur lambda donnée à partir de glmnet

> predict(fit1,type="coef", s = 0.008907)

21 x 1 sparse Matrix of class "dgCMatrix"
                  1
(Intercept) -0.08872364
V1           0.23734885
V2          -0.35472137
V3          -0.08088463
V4           .         
V5           .         
V6           .         
V7           0.31127123
V8           .         
V9           .         
V10          .         
V11          0.10636867
V12          .         
V13         -0.20328200
V14         -0.77717745
V15          .         
V16         -0.25924281
V17          .         
V18          .         
V19         -0.57989929
V20         -0.22522859

Si au lieu de cela je choisis lambda avec

cv <- cv.glmnet(x,y)
model=glmnet(x,y,lambda=cv$lambda.min)

Toutes les variables seraient (.).

Doutes et questions:

  1. Je ne suis pas sûr de savoir comment choisir lambda.
  2. Devrais-je utiliser les variables non (.) Pour s’adapter à un autre modèle? Dans mon cas, j'aimerais garder autant de variables que possible.
  3. Comment connaître la valeur p, c'est-à-dire quelles variables prédisent de manière significative la réponse?

Je m'excuse pour ma faible connaissance statistique! Et merci pour toute aide.

Alice
la source
Regardez peut-être le paquetage CRAN hdi , celui-ci fournit une inférence pour les modèles de grande dimension ...
Tom Wenseleers
Pour une explication complète des méthodes utilisées, je vous renvoie à cet article: projecteuclid.org/euclid.ss/1449670857
Tom Wenseleers le

Réponses:

40

Voici un fait peu intuitif: vous n'êtes pas censé donner à glmnet une valeur unique de lambda. De la documentation ici :

Ne fournissez pas une seule valeur pour lambda (pour les prédictions après utilisation du CV, predisez () à la place). Fournissez à la place une séquence décroissante de valeurs lambda. glmnet s'appuie sur ses démarrages rapides pour la vitesse et sur son chemin souvent plus rapide que pour calculer un seul ajustement.

cv.glmnetvous aidera à choisir lambda, comme vous l'avez mentionné dans vos exemples. Les auteurs du paquet glmnet suggèrent cv$lambda.1seau lieu decv$lambda.min , mais en pratique, j'ai eu du succès avec ce dernier.

Après avoir exécuté cv.glmnet, vous n'avez pas besoin de réexécuter glmnet! Chaque lambda de la grille ( cv$lambda) a déjà été exécuté. Cette technique s'appelle "Warm Start" et vous pouvez en lire plus ici . En reprenant l’introduction, la technique de démarrage à chaud réduit le temps d’exécution des méthodes itératives en utilisant la solution d’un problème d’optimisation différent (par exemple, glmnet avec un lambda plus grand) comme valeur de départ pour un problème d’optimisation ultérieur (par exemple, un glmnet avec un système de lambda plus petit) ).

Pour extraire le cycle souhaité cv.glmnet.fit, essayez ceci:

small.lambda.index <- which(cv$lambda == cv$lambda.min)
small.lambda.betas <- cv$glmnet.fit$beta[, small.lambda.index]

Révision (28/01/2017)

Pas besoin de pirater l'objet glmnet comme je l'ai fait ci-dessus; prendre @ conseils de alex23lemm ci - dessous et passer le s = "lambda.min", s = "lambda.1se"ou vers un autre numéro (par exemple s = .007) à la fois coefet predict. Notez que vos coefficients et vos prévisions dépendent de cette valeur définie par validation croisée. Utilisez une graine pour la reproductibilité! Et ne pas oublier que si vous ne fournissez pas "s"dans coefet predict, vous utiliserez la valeur par défaut s = "lambda.1se". Je me suis préparé à ce défaut après l'avoir constaté qu'il fonctionnait mieux dans une petite situation de données.s = "lambda.1se"tend également à fournir plus de régularisation, donc si vous travaillez avec alpha> 0, cela tendra également vers un modèle plus parcimonieux. Vous pouvez également choisir une valeur numérique de s à l'aide de plot.glmnet pour vous rendre quelque part entre les deux (n'oubliez pas d'exposer les valeurs de l'axe x!).

Ben Ogorek
la source
1
Merci! Cela aide ... avez-vous peut-être une réponse aux questions 2 et 3?
Alice
3
Ha pas de soucis. Les (.) S représentent des zéros. Depuis que vous êtes avec Lasso, vous avez indiqué vouloir une solution "clairsemée" (beaucoup de zéros). Si vous voulez qu'elles aient toutes des valeurs, définissez alpha = 0. Vous passez maintenant de la régression Lasso à Ridge. Les valeurs p pour glmnet sont conceptuellement délicates. Si vous recherchez Google "p-values ​​for lasso", par exemple, vous verrez beaucoup de recherches et de débats récents. J'ai même lu un récit (source amnésie) dans lequel l'auteur affirmait que les valeurs p n'avaient pas de sens pour les régressions biaisées telles que la régression au lasso et à la crête.
Ben Ogorek
6
Une autre façon d'extraire les coefficients associés à la valeur de lambda qui donne le minimum de cvm est la suivante:small.lambda.betas <- coef(cv, s = "lambda.min")
alex23lemm
1
@BenOgorek, excellente mise à jour! Une autre référence utile est Friedman J, Hastie T, Hoefling H, Tibshirani R. Optimisation des coordonnées Pathwise. Annales de statistiques appliquées. 2007; 2 (1): 302–332. ( arxiv.org/pdf/0708.1485.pdf )
dv_bn le
1
@erosennin, consultez l'argument lambda de cv.glmnet: "Séquence lambda facultative fournie par l'utilisateur; la valeur par défaut est NULL et glmnet choisit sa propre séquence." Vous voudrez utiliser le principe de démarrage à chaud et commencer la séquence avec des valeurs plus grandes de lambda avant de diminuer dans la plage qui vous intéresse.
Ben Ogorek
2

Q1) Je ne sais pas trop comment choisir Lambda. Q2) Devrais-je utiliser les variables non (.) Pour s’adapter à un autre modèle? Dans mon cas, j'aimerais garder autant de variables que possible.

Conformément à l'excellente réponse de @BenOgorek, vous laissez généralement l'appareillage utiliser une séquence lambda complète, puis, lors de l'extraction des coefficients optimaux, utilisez la valeur lambda.1se (contrairement à ce que vous avez fait).

Tant que vous suivez les trois mises en garde ci-dessous, ne vous opposez pas à la régularisation et ne modifiez pas le modèle: si une variable a été omise, c'est que la pénalité globale a été réduite. Les mises en garde sont les suivantes:

  1. Pour que les coefficients régularisés soient significatifs, assurez-vous d'avoir préalablement normalisé explicitement les valeurs moyenne et standard de la variable avec scale(); ne comptez pas sur glmnet(standardize=T). Pour justification, voir Est-ce que la normalisation avant Lasso est vraiment nécessaire? ; fondamentalement, une variable avec de grandes valeurs peut être injustement punie dans la régularisation.

  2. Pour être reproductible, utilisez set.seedplusieurs semences aléatoires et vérifiez la stabilité des coefficients régularisés.

  3. Si vous voulez une régularisation moins sévère, c’est-à-dire plus de variables incluses, utilisez alpha <1 (c’est-à-dire un filet élastique approprié) plutôt qu’une simple arête. Je suggère que vous balayiez l'alpha de 0 à 1. Si vous voulez le faire, pour éviter de surcharger l'hyperparamètre alpha et l'erreur de régression, vous devez utiliser la validation croisée, c'est-à-dire utiliser cv.glmnet()plutôt que simple glmnet():

.

for (alpha in c(0,.1,.3,.5,.7,.9,1)) {
  fit <- cv.glmnet(..., alpha=alpha, nfolds=...)
  # Look at the CVE at lambda.1se to find the minimum for this alpha value...
}

Si vous souhaitez automatiser une telle recherche gridsearch avec CV, vous pouvez le coder vous-même ou utiliser le paquet d’ caret au-dessus de glmnet; caret le fait bien. Pour la cv.glmnet nfoldsvaleur du paramètre, choisissez 3 (minimum) si votre jeu de données est petit, ou 5 ou 10 s'il est grand.

Q3) Comment connaître la valeur p, c’est-à-dire quelles variables prédisent de manière significative la réponse?

Non, ils n'ont pas de sens . Comme expliqué en détail dans Pourquoi est-il déconseillé d'obtenir des informations de synthèse statistique sur les coefficients de régression à partir du modèle glmnet?

Laissez simplement cv.glmnet()faire la sélection de variable automatiquement. Avec les mises en garde ci-dessus. Et bien sûr, la distribution de la variable de réponse devrait être normale (en supposant que vous utilisez family='gaussian').

smci
la source
Merci pour le commentaire très utile! J'ai aussi constaté que la standardisation des variables me semblait fonctionner plutôt que d'utiliser le glmnet (standardize = T).
Michelle
J'ai une question @ smci sur les valeurs bêta renvoyées par cvglmnet. Je comprends qu’il s’agit de valeurs bêta à chaque point de la grille de tentatives de valeurs lambda. Cependant, les valeurs bêta sont-elles renvoyées pour chaque valeur lambda (1) les valeurs des coefficients moyens des 10 plis (en supposant que j'ai utilisé 10 foisCV), (2) les valeurs bêta du pli qui a donné la meilleure précision, ou (3) les coefficients de réexécuter le modèle sur l'ensemble de données?
Michelle