3 Régressions pénalisées (ou sous contraintes)
Nous considérons toujours le modèle linéaire
\[Y=\beta_0+\beta_1X_1+\dots+\beta_dX_d+\varepsilon\] Lorsque \(d\) est grand ou que les variables sont linéairement dépendantes, les estimateurs des moindres carrées peuvent être mis en défaut. Les méthodes pénalisées ou sous contraintes consistent alors à restreindre l’espace sur lequel on minimise ce critère. On va alors chercher le vecteur \(\beta\) qui minimise
\[\sum_{i=1}^n \left(y_i-\beta_0-\sum_{j=1}^dx_{ij}\beta_j\right)^2\quad\text{sous la contrainte }\quad\sum_{j=1}^d\beta_j^2\leq t\] ou de façon équivalente (dans le sens où il existe une équivalence entre \(t\) et \(\lambda\))
\[\sum_{i=1}^n \left(y_i-\beta_0-\sum_{j=1}^dx_{ij}\beta_j\right)^2+\lambda\sum_{j=1}^d\beta_j^2.\] Les estimateurs obtenus sont les estimateurs ridge. Les estimateurs lasso s’obtiennent en remplaçant la contrainte ou la pénalité par une norme 1 (\(\sum_{j=1}^d|\beta_j|\)). Nous présentons dans cette partie les étapes principales qui permettent de faire ce type de régression avec R. Le package le plus souvent utilisé est glmnet.
3.1 Ridge et lasso avec glmnet
On considère le jeu de données ozone.txt où on cherche à expliquer la concentration maximale en ozone relevée sur une journée (variable maxO3) par d’autres variables essentiellement météorologiques.
ozone <- read.table("data/ozone.txt")
head(ozone) maxO3 T9 T12 T15 Ne9 Ne12 Ne15 Vx9 Vx12 Vx15 maxO3v
20010601 87 15.6 18.5 18.4 4 4 8 0.6946 -1.7101 -0.6946 84
20010602 82 17.0 18.4 17.7 5 5 7 -4.3301 -4.0000 -3.0000 87
20010603 92 15.3 17.6 19.5 2 5 4 2.9544 1.8794 0.5209 82
20010604 114 16.2 19.7 22.5 1 1 0 0.9848 0.3473 -0.1736 92
20010605 94 17.4 20.5 20.4 8 8 7 -0.5000 -2.9544 -4.3301 114
20010606 80 17.7 19.8 18.3 6 6 7 -5.6382 -5.0000 -6.0000 94
vent pluie
20010601 Nord Sec
20010602 Nord Sec
20010603 Est Sec
20010604 Nord Sec
20010605 Ouest Sec
20010606 Ouest Pluie
Contrairement à la plupart des autres package R qui permettent de faire de l’apprentissage, le package glmnet n’autorise pas l’utilisation de formules : il faut spécifier explicitement la matrice des \(X\) et le vecteur des \(Y\). On peut obtenir la matrice des \(X\) et notamment le codage des variables qualitatives avec la fonction model.matrix:
ozone.X <- model.matrix(maxO3~.,data=ozone)[,-1]
ozone.Y <- ozone$maxO3Charger le package
glmnetet à l’aide de la fonctionglmnetcalculer les estimateursridgeetlasso.library(glmnet) mod.R <- glmnet(ozone.X,ozone.Y,alpha=0) mod.L <- glmnet(ozone.X,ozone.Y,alpha=1)Analyser les sorties qui se trouvent dans les arguments
lambdaetbetadeglmnet.La fonction
glmnetcalcule tous les estimateurs pour une grille de valeurs delambdaspécifiée ici :mod.R$lambda |> head()[1] 22007.27 20052.20 18270.82 16647.69 15168.76 13821.21On peut récupérer les valeurs de
betaassociées à chaque valeur de la grille avecmod.R$beta[,1]T9 T12 T15 Ne9 Ne12 6.376767e-36 5.523924e-36 4.867402e-36 -6.821464e-36 -7.994984e-36 Ne15 Vx9 Vx12 Vx15 maxO3v -5.839057e-36 5.706014e-36 4.387350e-36 3.970583e-36 6.892387e-37 ventNord ventOuest ventSud pluieSec -5.830507e-36 -1.022483e-35 1.519222e-35 2.772246e-35Visualiser les chemins de régularisation des estimateurs
ridgeetlasso. On pourra utiliser la fonctionplot.plot(mod.R,label=TRUE)
plot(mod.L,label=TRUE)
plot(mod.R,xvar="lambda",label=TRUE)
plot(mod.L,xvar="lambda",label=TRUE)
Sélectionner les paramètres de régularisation à l’aide de la fonction
cv.glmnet. On pourra notamment faire unplotde l’objet et expliquer le graphe obtenu.Commençons par ridge :
ridgeCV <- cv.glmnet(ozone.X,ozone.Y,alpha=0) plot(ridgeCV)
On visualise les erreurs quadratiques calculées par validation croisée 10 blocs en fonction de
lambda(échelle logarithmique). Deux traites verticaux sont représentés :celui de gauche correspond à la valeur de `lambda` qui minimise l’erreur quadratique ;
celui de droite correspond à la plus grande valeur de `lambda` telle que l’erreur ne dépasse pas l’erreur minimale + 1 écart-type estimé de cette erreur.
D’un point de vu pratique, cela signifie que l’utilisateur peut choisir n’importe quelle valeur de
lambdaentre les deux traits verticaux. Si on veut diminuer la complexité du modèle on choisira la valeur de droite. On peut obtenir ces deux valeurs avecridgeCV$lambda.min[1] 9.750588ridgeCV$lambda.1se[1] 43.20116On peut faire de même pour le lasso :
lassoCV <- cv.glmnet(ozone.X,ozone.Y,alpha=1) plot(lassoCV)
On souhaite prédire la variable cible pour de nouveaux individus, par exemple les 25ème et 50ème individus du jeu de données. Calculer les valeurs prédites pour ces deux individus.
Une première approche pourrait consister à réajuster le modèle sur toutes les données pour la valeur de
lambdasélectionnée. Cette étape est en réalité déjà effectuée par la fonctioncv.glmnet. Il suffit par conséquent d’appliquer la fonctionpredictà l’objet obtenu aveccv.glmneten spécifiant la valeur delambdasouhaitée. Par exemple pour ridge :predict(ridgeCV,newx = ozone.X[50:51,],s="lambda.min")lambda.min 20010723 90.10981 20010724 96.74374predict(ridgeCV,newx = ozone.X[50:51,],s="lambda.1se")lambda.1se 20010723 93.23058 20010724 96.21185On peut faire de même pour le lasso :
predict(lassoCV,newx = ozone.X[50:51,],s="lambda.min")lambda.min 20010723 87.18766 20010724 98.11351predict(lassoCV,newx = ozone.X[50:51,],s="lambda.1se")lambda.1se 20010723 87.44713 20010724 95.61077A l’aide d’une validation croisée, comparer les performances des estimateurs MCO, ridge et lasso. On pourra utiliser les données
ozone_complet.txtqui contiennent plus d’individus et de variables.ozone1 <- read.table("data/ozone_complet.txt",sep=";") |> na.omit() ozone1.X <- model.matrix(maxO3~.,data=ozone1)[,-1] ozone1.Y <- ozone1$maxO3On crée une fonction qui calcule les erreurs quadratiques par validations croisée des 3 procédures d’estimation.
cv.ridge.lasso <- function(data,form){ set.seed(1234) data.X <- model.matrix(form,data=data)[,-1] data.Y <- data$maxO3 blocs <- caret::createFolds(1:nrow(data),k=10) prev <- matrix(0,ncol=3,nrow=nrow(data)) |> as.data.frame() names(prev) <- c("lin","ridge","lasso") for (k in 1:10){ app <- data[-blocs[[k]],] test <- data[blocs[[k]],] app.X <- data.X[-blocs[[k]],] app.Y <- data.Y[-blocs[[k]]] test.X <- data.X[blocs[[k]],] test.Y <- data.Y[blocs[[k]]] ridge <- cv.glmnet(app.X,app.Y,alpha=0) lasso <- cv.glmnet(app.X,app.Y,alpha=1) lin <- lm(form,data=app) prev[blocs[[k]],] <- tibble(lin=predict(lin,newdata=test), ridge=as.vector(predict(ridge,newx=test.X)), lasso=as.vector(predict(lasso,newx=test.X))) } err <- prev |> mutate(obs=data$maxO3) |> summarise_at(1:3,~mean((obs-.)^2)) return(err) }cv.ridge.lasso(ozone1,form=formula(maxO3~.))lin ridge lasso 1 184.3755 192.4984 191.5436On remarque que les approches régularisées n’apportent rien par rapport aux estimateurs MCO ici. Ceci peut s’expliquer par le fait que le nombre de variables n’est pas très important.
Refaire la question précédente en considérant toutes les interactions d’ordre 2.
cv.ridge.lasso(ozone1,form=formula(maxO3~.^2))lin ridge lasso 1 185.0517 168.7122 166.0982Les méthodes régularisées permettent ici de diminuer les erreurs quadratiques de manière intéressante. Cela vient certainement du fait du nombre de variables explicatives qui est beaucoup plus important lorsqu’on prend en compte toutes les interactions d’ordre 2, nous en avons en effet 253 :
ozone2.X <- model.matrix(maxO3~.^2,data=ozone1)[,-1] dim(ozone2.X)[1] 1366 253
3.2 Reconstruction d’un signal
Le fichier signal.csv contient un signal que l’on peut représenter par une fonction \(m:\mathbb R\to\mathbb R\). On le visualise
signal <- read_csv("data/signal.csv")
ggplot(signal)+aes(x=x,y=y)+geom_line()
Plaçons nous dans le cas où on ne dispose que d’une version bruitée de ce signal. La courbe n’est pas observée mais on dispose d’un échantillon \((x_i,y_i),i=1,\dots,n\) généré selon le modèle
\[y_i=m(x_i)+\varepsilon_i.\]
Le fichier ech_signal.csv contient \(n=60\) observations issues de ce modèle. On représente les données et la courbe
donnees <- read_csv("data/ech_signal.csv")
ggplot(signal)+aes(x=x,y=y)+geom_line()+
geom_point(data=donnees,aes(x=X,y=Y))
Nous cherchons dans cette partie à reconstruire le signal à partir de l’échantillon. Bien entendu, vu la forme du signal, un modèle linéaire de la forme \[
y_i=\beta_0+\beta_1x_i+\varepsilon_i
\] n’est pas approprié. De nombreuses approches en traitement du signal proposent d’utiliser une base ou dictionnaire représentée par une collection de fonctions \(\{\psi_j(x)\}_{j=1,\dots,K}\) et de décomposer le signal dans cette base :
\[m(x)\approx \sum_{j=1}^K \beta_j\psi_j(x).\]
Pour un dictionnaire donné, on peut alors considérer un modèle linéaire
\[ y_i=\sum_{j=1}^K \beta_j\psi_j(x_i)+\varepsilon_i. \tag{3.1}\]
Le problème est toujours d’estimer les paramètres \(\beta_j\) mais les variables sont maintenant définies par les élements du dictionnaire. Il existe différents types de dictionnaire, dans cet exercice nous proposons de considérer la base de Fourier définie par
\[\psi_0(x)=1,\quad \psi_{2j-1}(x)=\cos(2j\pi x)\quad\text{et}\quad \psi_{2j}(x)=\sin(2j\pi x),\quad j=1,\dots,K.\]
Écrire une fonction R qui admet en entrée :
- une grille de valeurs de
x(un vecteur) - une valeur de
K(un entier positif)
et qui renvoie en sortie une matrice qui contiennent les valeurs du dictionnaire pour chaque valeur de
x. Cette matrice devra donc contenir2Kcolonnes et le nombre de lignes sera égal à la longueur du vecteurx.mat.dict <- function(K,x){ res <- matrix(0,nrow=length(x),ncol=2*K) |> as_tibble() for (j in 1:K){ res[,2*j-1] <- cos(2*j*pi*x) res[,2*j] <- sin(2*j*pi*x) } return(res) }- une grille de valeurs de
On fixe
K=25. Calculer les estimateurs des moindres carrés du modèle (Équation 3.1).Il suffit d’ajuster le modèle linéaire où les variables explicatives sont données par le dictionnaire :
D25 <- mat.dict(25,donnees$X) |> mutate(Y=donnees$Y) mod.lin <- lm(Y~.,data=D25)Représenter le signal estimé. Commenter le graphe.
S25 <- mat.dict(25,signal$x) prev.MCO <- predict(mod.lin,newdata = S25) signal1 <- signal |> mutate(MCO=prev.MCO) |> rename(signal=y) signal2 <- signal1 |> pivot_longer(-x,names_to="meth",values_to="y") ggplot(signal2)+aes(x=x,y=y)+geom_line(aes(color=meth))+ scale_y_continuous(limits = c(-2,2))+geom_point(data=donnees,aes(x=X,y=Y))
Le signal estimé a tendance à surajuster les données. Cela vient du fait que on estime 51 paramètres avec seulement 60 observations.
Calculer les estimateurs lasso et représenter le signal issu de ces estimateurs.
On regarde tout d’abord le
chemin de régularisationdes estimateurs lassoX.25 <- model.matrix(Y~.,data=D25)[,-1] lasso1 <- glmnet(X.25,D25$Y,alpha=1) plot(lasso1)
Il semble que quelques coefficients quittent la valeur 0 bien avant les autres. On effectue maintenant la validation croisée pour sélectionner le paramètre \(\lambda\).
lasso.cv <- cv.glmnet(X.25,D25$Y,alpha=1) plot(lasso.cv)
On calcule les prévisions et on trace le signal.
prev.lasso <- as.vector(predict(lasso.cv,newx=as.matrix(S25))) signal1$lasso <- prev.lasso signal2 <- signal1 |> pivot_longer(-x,names_to="meth",values_to="y") ggplot(signal2)+aes(x=x,y=y)+geom_line(aes(color=meth))+ scale_y_continuous(limits = c(-2,2))+geom_point(data=donnees,aes(x=X,y=Y))
L’algorithme lasso a permis de corriger le problème de sur-apprentissage.
Identifier les coefficients lasso sélectionnés qui ne sont pas nuls.
v.sel <- which(coef(lasso.cv)!=0) v.sel[1] 1 2 4 5 6 8 21 28 30 36 37 38 40Ajouter les signaux ajustés par les algorithme Ridge, PCR et PLS. Comparer les performances.
On commence par la
régression ridge:set.seed(1234) ridge.cv <- cv.glmnet(X.25,D25$Y,alpha=0) plot(ridge.cv) # on est en bord de grille
ridge.cv <- cv.glmnet(X.25,D25$Y,alpha=0,lambda = exp(seq(-4,0,length=100))) plot(ridge.cv)
prev.ridge <- as.vector(predict(ridge.cv,newx=as.matrix(S25)))On effectue la
PCR:library(pls) pcr.fit <- pcr(Y~.,data=D25,validation="CV") ncomp.pcr <- which.min(pcr.fit$validation$PRESS) ncomp.pcr[1] 39prev.pcr <- predict(pcr.fit,newdata=S25,ncomp=ncomp.pcr)Puis la
PLS:pls.fit <- plsr(Y~.,data=D25,validation="CV") ncomp.pls <- which.min(pls.fit$validation$PRESS) ncomp.pls[1] 5prev.pls <- predict(pls.fit,newdata=S25,ncomp=ncomp.pls)On trace les signaux :
signal1 <- signal1 |> mutate(ridge=prev.ridge, pcr=prev.pcr, pls=prev.pls) signal2 <- signal1 |> pivot_longer(-x,names_to="meth",values_to="y") ggplot(signal2)+aes(x=x,y=y)+geom_line(aes(color=meth))+ scale_y_continuous(limits = c(-2,2))+geom_point(data=donnees,aes(x=X,y=Y))
On peut également obtenir les erreurs quadratiques (puisqu’on connait la vraie courbe)
signal1 |> summarise_at(-(1:2),~mean((.-signal)^2)) |> round(3)# A tibble: 1 × 5 MCO lasso ridge pcr pls <dbl> <dbl> <dbl> <dbl> <dbl> 1 598. 0.014 0.089 0.07 0.067
3.3 Régression logistique pénalisée
On considère le jeu de données sur la détection d’images publicitaires disponible ici https://archive.ics.uci.edu/ml/datasets/internet+advertisements.
ad.data <- read_delim("data/ad_data.txt",delim=",",
col_names = FALSE,na=c("?"),trim_ws = TRUE) |>
rename("Y"=last_col()) |>
mutate(Y=fct(Y))La variable à expliquer est
summary(ad.data$Y) ad. nonad.
459 2820
Cette variable est binaire. On considère une régression logistique pour expliquer cette variable. Le nombre de variables explicatives étant important, comparer les algorithmes du maximum de vraisemblance aux algorithmes de type ridge/lasso en faisant une validation croisée 10 blocs. On pourra utiliser comme critère de comparaison l’erreur de classification, la courbe ROC et l’AUC. Il faudra également prendre des décisions pertinentes vis-à-vis des données manquantes…
On commence par regarder les données manquantes :
sum(is.na(ad.data))[1] 2729
var.na <- apply(is.na(ad.data),2,any)
names(ad.data)[var.na][1] "X1" "X2" "X3" "X4"
ind.na <- apply(is.na(ad.data),1,any)
sum(ind.na)[1] 920
On remarque que 920 individus ont au moins une donnée manquante alors que seules les 4 premières variables ont des données manquantes, on choisit donc de supprimer ces 4 variables.
ad.data1 <- ad.data[,var.na==FALSE]
dim(ad.data1)[1] 3279 1555
sum(is.na(ad.data1))[1] 0
On construit les matrices des variables explicatives pour les méthodes lasso et ridge (glmnet veut les variables explicatives sous forme de matrices).
X.ad <- model.matrix(Y~.,data=ad.data1)[,-1]
Y.ad <- ad.data1$YAvant de faire la validation croisée, nous présentons la syntaxe de l’algorithme lasso. Comme pour la régression, on utilise la fonction cv.glmnet, il faut simplement ajouter l’argument family="binomial" :
set.seed(1234)
lasso.cv <- cv.glmnet(X.ad,Y.ad,family="binomial",alpha=1)
plot(lasso.cv)
Par défaut le critère utilisé pour la classification binaire est celui de la déviance. On peut utiliser d’autres critères comme l’erreur de classification ou l’auc en modifiant l’argument type.measure. On gardera la déviance dans la suite. On peut maintenant faire la validation croisée 10 blocs pour calculer les prévisions des 3 algorithmes.
set.seed(5678)
blocs <- caret::createFolds(1:nrow(ad.data1),k=10)
score <- matrix(0,ncol=3,nrow=nrow(ad.data1)) |> as.data.frame()
names(score) <- c("MV","ridge","lasso")
for (k in 1:10){
print(k)
app <- ad.data1[-blocs[[k]],]
test <- ad.data1[blocs[[k]],]
app.X <- X.ad[-blocs[[k]],]
app.Y <- Y.ad[-blocs[[k]]]
test.X <- X.ad[blocs[[k]],]
test.Y <- Y.ad[blocs[[k]]]
ridge <- cv.glmnet(app.X,app.Y,family="binomial",alpha=0)
lasso <- cv.glmnet(app.X,app.Y,family="binomial",alpha=1)
MV <- glm(Y~.,data=app,family="binomial")
score[blocs[[k]],] <- tibble(MV=predict(MV,newdata=test,type="response"),
ridge=as.vector(predict(ridge,newx=test.X,type="response")),
lasso=as.vector(predict(lasso,newx=test.X,type="response")))
}Le tibble score contient, pour chaque individu, les prévisions des probabilités a posteriori \[\mathbf P(Y=\text{nonad.}|X=x_i),\quad i=1,\dots,n.\]
On peut déduire de ce tableau les critères souhaités :
les
courbes ROC:library(tidymodels) score1 <- score |> mutate(obs=ad.data1$Y) |> pivot_longer(-obs,names_to="Methode",values_to="score") score1 |> group_by(Methode)|> roc_curve(obs,score,event_level = "second") |> autoplot()
les
AUC:score1 |> group_by(Methode) |> roc_auc(obs,score,event_level = "second") |> arrange(desc(.estimate))# A tibble: 3 × 4 Methode .metric .estimator .estimate <chr> <chr> <chr> <dbl> 1 ridge roc_auc binary 0.981 2 lasso roc_auc binary 0.945 3 MV roc_auc binary 0.756les
accuracy:score1 |> mutate(prev=fct_recode(as.character(round(score)),"ad."="0","nonad."="1")) |> group_by(Methode) |> accuracy(obs,prev) |> arrange(desc(.estimate))# A tibble: 3 × 4 Methode .metric .estimator .estimate <chr> <chr> <chr> <dbl> 1 lasso accuracy binary 0.970 2 ridge accuracy binary 0.970 3 MV accuracy binary 0.847
On remarque que les méthodes pénalisées sont nettement meilleures que l’approche classique par maximum de vraisemblance sur cet exemple.
On peut également faire le travail dans un environnement tidymodels :
# 1 recette pour normaliser
rec_norm <-
recipe(Y ~ ., data = ad.data1) |>
step_normalize()
# Definition du lasso et du ridge
lasso_spec <-
logistic_reg(penalty=tune(),mixture=1) |>
set_mode("classification") |>
set_engine("glmnet")
ridge_spec <-
logistic_reg(penalty=tune(),mixture=0) |>
set_mode("classification") |>
set_engine("glmnet")
MV_spec <-
logistic_reg(penalty=NULL,mixture=NULL) |>
set_mode("classification") |>
set_engine("glm")
# agregation des algorithme
wflow <- workflow_set(
preproc = list(norm=rec_norm),
models=list(lasso=lasso_spec,
ridge=ridge_spec)
#MV_spec)
)
# definition des blocs
set.seed(12345)
blocs <- vfold_cv(ad.data1,v=10,repeats = 5)
results <- wflow |>
workflow_map(fn="tune_grid",
resamples=blocs,
grid=50,
metrics=metric_set(kap,f_meas,bal_accuracy,accuracy,roc_auc),
seed = 321,
control=control_grid(event_level = "first",save_pred = FALSE)) #On peut visualiser les résultats avec :
results |>
rank_results(select_best = TRUE) |>
select(wflow_id,.metric,mean) |>
mutate(mean=round(mean,3)) |>
pivot_wider(names_from = .metric,values_from = mean)# A tibble: 2 × 6
wflow_id accuracy bal_accuracy f_meas kap roc_auc
<chr> <dbl> <dbl> <dbl> <dbl> <dbl>
1 norm_ridge 0.971 0.91 0.89 0.874 0.98
2 norm_lasso 0.971 0.915 0.889 0.872 0.962
results |>
rank_results(select_best = TRUE) |>
ggplot()+
aes(x=wflow_id,ymin=mean-std_err,ymax=mean+std_err,y=mean)+
geom_errorbar()+
facet_wrap(~.metric,scales = "free_y")+
scale_x_discrete(guide = guide_axis(angle = 90),name=NULL)+ylab("")
On peut maintenant choisir l’algorithme, par exemple ridge :
(best_result <-
results |>
extract_workflow_set_result("norm_ridge") |>
select_best(metric="roc_auc"))# A tibble: 1 × 2
penalty .config
<dbl> <chr>
1 0.0457 Preprocessor1_Model44
(final <-
results |>
extract_workflow("norm_ridge") |>
finalize_workflow(best_result) |>
fit(data=ad.data1))══ Workflow [trained] ══════════════════════════════════════════════════════════
Preprocessor: Recipe
Model: logistic_reg()
── Preprocessor ────────────────────────────────────────────────────────────────
1 Recipe Step
• step_normalize()
── Model ───────────────────────────────────────────────────────────────────────
Call: glmnet::glmnet(x = maybe_matrix(x), y = y, family = "binomial", alpha = ~0)
Df %Dev Lambda
1 1554 0.00 200.700
2 1554 2.26 182.800
3 1554 2.48 166.600
4 1554 2.71 151.800
5 1554 2.97 138.300
6 1554 3.24 126.000
7 1554 3.55 114.800
8 1554 3.88 104.600
9 1554 4.24 95.340
10 1554 4.63 86.870
11 1554 5.05 79.150
12 1554 5.51 72.120
13 1554 6.00 65.710
14 1554 6.52 59.870
15 1554 7.10 54.550
16 1554 7.71 49.710
17 1554 8.38 45.290
18 1554 9.09 41.270
19 1554 9.85 37.600
20 1554 10.66 34.260
21 1554 11.51 31.220
22 1554 12.42 28.440
23 1554 13.39 25.920
24 1554 14.40 23.620
25 1554 15.45 21.520
26 1554 16.56 19.610
27 1554 17.71 17.860
28 1554 18.90 16.280
29 1554 20.13 14.830
30 1554 21.39 13.510
31 1554 22.69 12.310
32 1554 24.01 11.220
33 1554 25.36 10.220
34 1554 26.72 9.314
35 1554 28.10 8.487
36 1554 29.49 7.733
37 1554 30.89 7.046
38 1554 32.29 6.420
39 1554 33.70 5.850
40 1554 35.11 5.330
41 1554 36.51 4.857
42 1554 37.91 4.425
43 1554 39.30 4.032
44 1554 40.69 3.674
45 1554 42.07 3.347
46 1554 43.43 3.050
...
and 54 more lines.
3.4 Exercices
Exercice 3.1 (Estimateurs ridge pour le modèle linéaire) On considère le modèle de régression
\[ Y_i=\beta_1x_{i1}+\dots+\beta_px_{ip}+\varepsilon_i \] où les \(\varepsilon_i\) sont i.i.d de loi \(\mathcal N(0,\sigma^2)\). Pour \(\lambda\geq 0\), on note \(\hat\beta_R(\lambda)\) l’estimateur ridge défini par
\[ \hat\beta_R(\lambda)=\mathop{\mathrm{argmin}}_\beta\sum_{i=1}^n\left(y_i-\sum_{j=1}^px_{ij}\beta_j\right)^2+\lambda\sum_{j=1}^p\beta_j^2. \]
Exprimer \(\hat\beta_R(\lambda)\) en fonction de \(\mathbb X\), \(\mathbb Y\) et \(\lambda\).
Le critère à minimiser se réécrit \[\mathcal C(\beta)=(\mathbb Y-\mathbb X\beta)^t (\mathbb Y-\mathbb X\beta)+\lambda\beta^t\beta.\] L’estimateur ridge est donc solution de \[-2\mathbb X^t\mathbb Y+2\mathbb X^t\mathbb X\beta+2\lambda\beta=0,\] d’où \[\hat\beta_R(\lambda)=(\mathbb X^t\mathbb X+\lambda I)^{-1}\mathbb X^t\mathbb Y.\]
Étudier le biais et la variance de \(\hat\beta_R(\lambda)\) en fonction de \(\lambda\). On pourra également faire la comparaison avec l’estimateur des MCO.
Comme \(\mathbb Y=\mathbb X\beta+\varepsilon\), on obtient
\[ \begin{aligned} \mathbf E[\hat\beta_R(\lambda)]-\beta & =(\mathbb X^t\mathbb X+\lambda I)^{-1}\mathbb X^t\mathbb X\beta-\beta \\ & =\left[(\mathbb X^t\mathbb X+\lambda I)^{-1}(\mathbb X^t\mathbb X-(\mathbb X^t\mathbb X+\lambda I))\right]\beta \\ & = -\lambda(\mathbb X^t\mathbb X+\lambda I)^{-1}\beta. \end{aligned} \]
De même, on obtient pour la variance \[ \mathbf V(\hat\beta_R(\lambda))=\sigma^2(\mathbb X^t\mathbb X+\lambda\mathbb I)^{-1}\mathbb X^t\mathbb X(\mathbb X^t\mathbb X+\lambda\mathbb I)^{-1}. \] La variance diminue lorsque \(\lambda\) augmente, mais on remarque une augmentation du bais par rapport à l’estimateur des moindres carrés (et réciproquement lorsque \(\lambda\) diminue).
On suppose que la matrice \(\mathbb X\) est orthogonale. Exprimer les estimateurs \(\hat\beta_{R,j}(\lambda)\) en fonction des estimateurs des MCO \(\hat\beta_j, j=1,\dots,p\). Interpréter.
Si \(\mathbb X\) est orthogonale, alors \[\hat\beta_R(\lambda)=\frac{1}{1+\lambda}\mathbb X^t\mathbb Y=\frac{\hat\beta_{MCO}}{1+\lambda}.\]
Exercice 3.2 (Estimateurs lasso dans le cas orthogonal) Cet exercice est inspiré de Giraud (2015). On rappelle qu’une fonction \(F:\mathbb R^n\to\mathbb R\) est convexe si \(\forall x,y\in\mathbb R^n\), \(\forall\lambda\in[0,1]\) on a \[F(\lambda x+(1-\lambda) y)\leq \lambda F(x)+(1-\lambda)F(y).\] On définit la sous-différentielle d’une fonction convexe \(F\) par \[\partial F(x)=\{w\in\mathbb R^n:F(y)\geq F(x)+\langle w,y-x\rangle\textrm{ pour tout }y\in\mathbb R^n\}.\] On admettra que les minima d’une fonction convexe \(F:\mathbb R^n\to\mathbb R\) sont caractérisés par \[x^\star\in\mathop{\mathrm{argmin}}_{x\in\mathbb R^n}F(x)\Longleftrightarrow 0\in \partial F(x^\star)\] et que \(\partial F(x)=\{\nabla F(x)\}\) lorsque \(F\) est différentiable en \(x\).
Montrer que pour \(x\in\mathbb R\) \[ \partial |x|=\left\{ \begin{array}{ll} \textrm{signe}(x) & \textrm{si } x\neq 0 \\ \left[-1;1\right] & \textrm{sinon,} \end{array}\right. \] où \(\text{signe}(x)=\mathbf 1_{x>0}-\mathbf 1_{x\leq 0}\).
\(x\mapsto|x|\) est dérivable partout sauf en 0 donc \(\partial |x|=\textrm{signe}(x)1\) si \(x\neq 0\). De plus, si \(x=0\) \[\partial|x|=\{w\in\mathbb R:|y|\geq \langle w,y\rangle\ \forall y\in\mathbb R\}=\{w\in\mathbb R:|y|\geq wy\ \forall y\in\mathbb R\}=[-1,1].\]
Soit \(x\in\mathbb R^n\).
Montrer que \[\partial\|x\|_1=\{w\in\mathbb R^n:\langle w,x\rangle=\|x\|_1\text{ et }\|w\|_\infty\leq 1\}.\] On pourra utiliser que pour tout \(p,q\) tels que \(1/p+1/q=1\) on a \[\|x\|_p=\sup\left\{\langle w,x\rangle:\|w\|_q\leq 1\right\}.\]
On montre la double inclusion. Soit \(w\) tel que \(\langle w,x\rangle=\|x\|_1\) et \(\|w\|_\infty=1\). On a \(\forall y\in\mathbb R^n\), \[\|y\|_1\geq \langle w,y\rangle=\langle w,y-x+x\rangle=\|x\|_1+\langle w,y-x\rangle.\] Donc \(w\in\partial\|x\|_1\). Inversement, soit \(w\in\partial\|x\|_1\). Par définition \[\partial\|x\|_1=\{w\in\mathbb R^n:\|y\|_1\geq \langle w,y-x\rangle+\|x\|_1\ \forall y\in\mathbb R^n\}.\] Pour \(y=0\) et \(y=2x\), on a donc \[\|x\|_1\leq\langle w,x\rangle\quad\textrm{et}\quad 2\|x\|_1\geq \langle w,x\rangle+\|x\|_1\] d’où \(\|x\|_1=\langle x,w\rangle=\sum_iw_ix_i\). De plus en posant \(\tilde w=(0,\dots,0,\text{signe}(w_i),0,\dots,0)\) où la coordonnée non nulle correspond au \(\max_i(|w_i|)\) on a \(\|w\|_\infty=\langle w,\tilde w\rangle\) et \(\|\tilde w\|_\infty=\|\tilde w\|_1=1\). De plus \[\|\tilde w\|_1\geq \|x\|_1+\langle w,\tilde{w}-x\rangle=\|w\|_\infty\quad\Longrightarrow \|w\|_\infty\leq \|\tilde w\|_1=1.\]
En déduire \[\partial\|x\|_1=\{w\in\mathbb R^n:w_j=\textrm{signe}(x_j)\textrm{ si }x_j\neq 0, w_j\in[-1,1]\textrm{ si }x_j=0\}.\]
On a
\[ \begin{aligned} \partial\|x\|_1 & =\{w\in\mathbb R^n:\langle w,x\rangle=\|x\|_1\text{ et }\|w\|_\infty\leq 1\}\\ & = \{w\in\mathbb R^n:\sum_{i=1}^n(w_ix_i-|x_i|)=0\text{ et }\|w\|_\infty\leq 1\}. \end{aligned} \] Or si \(\|w\|_\infty\leq 1\) alors \(w_ix_i-|x_i|\leq 0\) \(\forall i=1,\dots,n\). Donc
\[ \begin{aligned} \partial\|x\|_1 & =\{w\in\mathbb R^n:(w_ix_i-|x_i|)=0,i=1,\dots,n\text{ et }\|w\|_\infty\leq 1\}\\ &=\{w\in\mathbb R^n:w_j=\textrm{signe}(x_j)1\textrm{ si }x_j\neq 0, w_j\in[-1,1]\textrm{ si }x_j=0\}. \end{aligned} \]
Étant données \(n\) observations \((x_i,y_i),i=1,\dots,n\) telles que \(x_i\in\mathbb R^p\) et \(y_i\in\mathbb R\) on rappelle que l’estimateur lasso \(\hat\beta(\lambda)\) est construit en minimisant \[\mathcal L(\beta)=\|Y-\mathbb X\beta\|_2^2+\lambda\|\beta\|_1. \tag{3.2}\]
On admettra que la sous-différentielle \(\partial \mathcal L(\beta)\) est donnée par \[\partial \mathcal L(\beta)=\left\{-2\mathbb X^t(Y-\mathbb X\beta)+\lambda z:z\in\partial\|\beta\|_1\right\}.\] Montrer que \(\hat\beta(\lambda)\) vérifie \[\mathbb X^t\mathbb X\hat\beta(\lambda)=\mathbb X^tY-\frac{\lambda}{2}\hat z\] où \(\hat z\in\mathbb R^p\) vérifie \[ \hat z_j\left\{ \begin{array}{ll} =\textrm{signe}(\hat\beta_j(\lambda)) & \textrm{si } \hat\beta_j(\lambda)\neq 0 \\ \in\left[-1;1\right] & \textrm{sinon.} \end{array}\right. \]
D’après les indications, on a \(0\in\partial \mathcal L(\hat\beta(\lambda))\). Donc il existe \(\hat z\in\partial\|\hat\beta(\lambda)\|_1\) tel que \[-2\mathbb X^t(Y-\mathbb X\hat\beta(\lambda))+\lambda \hat z=0\quad\Longleftrightarrow\quad \mathbb X^t\mathbb X\hat\beta(\lambda)=\mathbb X^tY-\frac{\lambda}{2}\hat z.\]
On suppose maintenant que la matrice \(\mathbb X\) est orthogonale.
Montrer que \[\textrm{signe}(\hat\beta_j(\lambda))=\textrm{signe}(\mathbb X_j^tY)\quad\textrm{lorsque }\hat\beta_j(\lambda)\neq 0\] et \(\hat\beta_j(\lambda)=0\) si et seulement si \(|\mathbb X_j^tY|\leq \lambda/2\).
\(\mathbb X\) étant orthogonale, on a pour \(\hat\beta_j(\lambda)\neq 0\) \[\hat\beta_j(\lambda)+\frac{\lambda}{2}\textrm{signe}(\hat\beta_j(\lambda))=\hat\beta_j(\lambda)\left(1+\frac{\lambda}{2|\hat\beta_j(\lambda)|}\right)=\mathbb X_j^tY,\] donc \(\hat\beta_j(\lambda)\) est du signe de \(\mathbb X_j^tY\). De plus si \(\hat\beta_j(\lambda)=0\) alors \(\mathbb X^t_jY=\frac{\lambda}{2}\hat z_j\) avec \(\hat z_j\in[-1,1]\). Donc \[|\mathbb X^t_jY|=\left|\frac{\lambda}{2}\hat z_j\right|\leq\frac{\lambda}{2}.\] A l’inverse si \(|\mathbb X_j^tY|\leq \lambda/2\) et si \(\hat\beta_j(\lambda)\neq 0\) alors \[\left|\hat\beta_j(\lambda)\left(1+\frac{\lambda}{2|\hat\beta_j(\lambda)|}\right)\right|=|\hat\beta_j(\lambda)|+\frac{\lambda}{2}=|\mathbb X^t_jY|\leq\frac{\lambda}{2}.\] Donc \(\hat\beta_j(\lambda)=0\).
En déduire \[\hat\beta_j(\lambda)=\mathbb X_j^tY\left(1-\frac{\lambda}{2|\mathbb X_j^tY|}\right)_+,\quad j=1,\dots,p\] où \((x)_+=\max(x,0)\). Interpréter ce résultat.
On obtient donc \[\hat\beta_j(\lambda)=\mathbb X_j^tY-\frac{\lambda}{2}\,\frac{\mathbb X_j^tY}{|\mathbb X_j^tY|}=\mathbb X_j^tY\left(1-\frac{\lambda}{2|\mathbb X_j^tY|}\right)\] si \(\mathbb X_j^tY\geq \frac{\lambda}{2}\) et \(\hat\beta_j(\lambda)=0\) sinon. D’où \[\hat\beta_j(\lambda)=\mathbb X_j^tY\left(1-\frac{\lambda}{2|\mathbb X_j^tY|}\right)_+,\quad j=1,\dots,d.\]
Exercice 3.3 (Unicité de l’estimateur lasso) Cet exercice est inspiré de Giraud (2015). Soit \(\hat\beta^{1}(\lambda)\) et \(\hat\beta^{2}(\lambda)\) deux solutions qui minimisent l’Équation 3.2. Soit \(\hat\beta=(\hat\beta^{1}(\lambda)+\hat\beta^{2}(\lambda))/2\).
Montrer que si \(\mathbb X \hat\beta^{1}(\lambda)\neq\mathbb X \hat\beta^{2}(\lambda)\) alors \[\|\mathbb Y-\mathbb X\hat\beta\|_2^2+\lambda\|\hat\beta\|_1<\frac{1}{2}\left(\|\mathbb Y-\mathbb X\hat\beta^1(\lambda)\|_2^2+\lambda\|\hat\beta^1(\lambda)\|_1+\|\mathbb Y-\mathbb X\hat\beta^2(\lambda)\|_2^2+\lambda\|\hat\beta^2(\lambda)\|_1\right).\] On pourra utiliser la convexité (forte) de \(x\mapsto\|x\|_2^2\).
On a
\[ \begin{aligned} \|\mathbb Y-\mathbb X\hat\beta\|_2^2+\lambda\|\hat\beta\|_1= & \left\|\frac{1}{2}(\mathbb Y-\mathbb X\hat\beta^1(\lambda))+\frac{1}{2}(\mathbb Y-\mathbb X\hat\beta^2(\lambda))\right\|_2^2+\lambda\left\|\frac{1}{2}(\hat\beta^1(\lambda)+\hat\beta^2(\lambda))\right\|_1 \\ < & \frac{1}{2}\left\|\mathbb Y-\mathbb X\hat\beta^1(\lambda))\right\|_2^2+\frac{1}{2}\left\|\mathbb Y-\mathbb X\hat\beta^2(\lambda))\right\|_2^2+\frac{1}{2}\lambda\|\hat\beta^1(\lambda)\|_1+\frac{1}{2}\lambda\|\hat\beta^2(\lambda)\|_1 \end{aligned} \] en utilisant la stricte convexité de \(x\mapsto\|x\|_2^2\) et l’inégalité triangulaire.
En déduire que \(\mathbb X \hat\beta^{1}(\lambda)=\mathbb X \hat\beta^{2}(\lambda)\).
Donc si \(\mathbb X \hat\beta^{1}(\lambda)\neq\mathbb X \hat\beta^{2}(\lambda)\) alors \[\|\mathbb Y-\mathbb X\hat\beta\|_2^2+\lambda\|\hat\beta\|_1< \|\mathbb Y-\mathbb X\hat\beta^1(\lambda)\|_2^2+\lambda\|\hat\beta^1(\lambda)\|_1\] ce qui est impossible par définition de \(\hat\beta^1(\lambda)\).