corrections mineures à l'occasion de l'édition des slides

This commit is contained in:
Pierre-Edouard Portier 2022-03-06 22:53:46 +01:00
parent 87a0d48c2e
commit 52f4ea14a2
2 changed files with 24 additions and 24 deletions

View File

@ -10,8 +10,8 @@ splitdata <- function(data,p) {
test = list(X = data$X[-entridx,,drop=FALSE], Y = data$Y[-entridx]))
}
# alphas[l] est une liste de valeurs pour l'hyperparamètre alpha.
# Notons Ridge[l] un modèle avec alpha <- alphas[l].
# lambdas[l] est une liste de valeurs pour l'hyperparamètre lambda.
# Notons Ridge[l] un modèle avec lambda <- lambdas[l].
# Découper aléatoirement le jeu de données d'entraînement en K plis F[i] disjoints.
# Pour l <- [1,...,L]
# Pour i <- [1,...,K]
@ -22,40 +22,40 @@ splitdata <- function(data,p) {
# Soit l' l'indice du maximum de Moy[l]
# Apprendre Ridge[l'] sur l'ensemble du jeu de données d'entraînement.
# Retourner ce modèle.
kfoldridge <- function(K, alphas, data, degre) {
kfoldridge <- function(K, lambdas, data, degre) {
N <- nrow(data$X)
folds <- rep_len(1:K, N)
folds <- sample(folds, N)
maes <- matrix(data = NA, nrow = K, ncol = length(alphas))
colnames(maes) <- alphas
alpha_idx <- 1
for(alpha in alphas) {
maes <- matrix(data = NA, nrow = K, ncol = length(lambdas))
colnames(maes) <- lambdas
lambda_idx <- 1
for(lambda in lambdas) {
for(k in 1:K) {
fold <- folds == k
coef <- ridge(alpha, data, degre, fold)
coef <- ridge(lambda, data, degre, fold)
pred <- polyeval(coef, data$X[fold,])
maes[k,alpha_idx] <- mean(abs(pred - data$Y[fold]))
maes[k,lambda_idx] <- mean(abs(pred - data$Y[fold]))
}
alpha_idx <- alpha_idx + 1
lambda_idx <- lambda_idx + 1
}
mmaes <- colMeans(maes)
minmmaes <- min(mmaes)
bestalpha <- alphas[which(mmaes == minmmaes)]
bestlambda <- lambdas[which(mmaes == minmmaes)]
fold <- folds == K+1 # vector of FALSE
coef <- ridge(bestalpha, data, degre, fold)
list(coef = coef, maes = maes, alpha = bestalpha)
coef <- ridge(bestlambda, data, degre, fold)
list(coef = coef, maes = maes, lambda = bestlambda)
}
# Résolution d'un système linéaire correspondant à la matrice de Gram pour
# un polynôme de degré fixé et avec l'ajout d'un facteur de régularisation en
# norme L2 dont l'importance est contrôlée par l'hyperparamètre alpha.
# norme L2 dont l'importance est contrôlée par l'hyperparamètre lambda.
# Les éléments du jeu de données indiqués par le vecteur booléen fold ne sont
# pas utilisés pour l'apprentissage du modèle. Cela permet d'implémenter une
# validation croisée à plusieurs plis.
ridge <- function(alpha, data, degre, fold) {
ridge <- function(lambda, data, degre, fold) {
xs <- c(data$X[!fold,])
A <- outer(xs, 0:degre, "^")
gram <- t(A) %*% A
diag(gram) <- diag(gram) + alpha
diag(gram) <- diag(gram) + lambda
solve(gram, as.vector(t(A) %*% data$Y[!fold]))
}

View File

@ -14,17 +14,17 @@ source("04_validation_croisee.R", local = knitr::knit_global())
# Principe de la validation croisée
Comment choisir la valeur du coefficient de régularisation $\alpha$ pour une régression ridge ? Notons en passant que $\alpha$ est un exemple de ce que l'on appelle un hyperparamètre car sa valeur doit être fixée avant de pouvoir apprendre les paramètres du modèle (dans notre cas, les coefficients d'un modèle linéaire).
Comment choisir la valeur du coefficient de régularisation $\lambda$ pour une régression ridge ? Notons en passant que $\lambda$ est un exemple de ce que l'on appelle un hyperparamètre car sa valeur doit être fixée avant de pouvoir apprendre les paramètres du modèle (dans notre cas, les coefficients d'un modèle linéaire).
Une possibilité est de diviser le jeu de données en deux parties, l'une utilisée pour apprendre le modèle prédictif, l'autre utilisée pour valider la qualité des prédictions sur des données qui n'ont pas été vues pendant la phase d'apprentissage. On parle de jeu d'entraînement et de jeu de test. Cette méthode est appelée "validation croisée".
Il s'agirait donc de tester plusieurs valeurs de l'hyperparamètre $\alpha$ sur le jeu d'entraînement et de conserver celle qui donne les meilleurs résultats sur un jeu de données de test qui n'a pas été utilisé pour l'entraînement. Cette approche pose problème. Le jeu de test est utilisé pour sélectionner le meilleur modèle, c'est-à-dire celui qui a le plus de chance de bien prédire pour de nouvelles données (i.e. de bien "généraliser"). Pour avoir une meilleure mesure de l'erreur, il est préférable de tester ce meilleur modèle sur des données qui n'ont jamais été utilisées pour comparer des modèles. Ainsi, nous pourrions réserver trois jeux de données : entraînement, validation (pour comparer différents modèles) et test (pour estimer l'erreur du modèle choisi).
Il s'agirait donc de tester plusieurs valeurs de l'hyperparamètre $\lambda$ sur le jeu d'entraînement et de conserver celle qui donne les meilleurs résultats sur un jeu de données de test qui n'a pas été utilisé pour l'entraînement. Cette approche pose problème. Le jeu de test est utilisé pour sélectionner le meilleur modèle, c'est-à-dire celui qui a le plus de chance de bien prédire pour de nouvelles données (i.e. de bien "généraliser"). Pour avoir une meilleure mesure de l'erreur, il est préférable de tester ce meilleur modèle sur des données qui n'ont jamais été utilisées pour comparer des modèles. Ainsi, nous pourrions réserver trois jeux de données : entraînement, validation (pour comparer différents modèles) et test (pour estimer l'erreur du modèle choisi).
Pour une approche souvent plus robuste, nous pouvons employer la stratégie dite de validation croisée à $K$ plis ("K Fold Cross-Validation"). Il s'agit de diviser aléatoirement le jeu d'entraînement en $K$ parties disjointes, appelées plis. Pour chaque valeur de l'hyperparamètre $\alpha$, nous apprenons $K$ modèles. Notons par exemple $M[\alpha_i,j]$, le j-ème des $K$ modèles appris pour la valeur $\alpha_i$ de l'hyperparamètre $\alpha$. Le jeu d'entraînement du modèle $M[\alpha_i,j]$ est constitué de l'union de $K-1$ plis, les $K$ plis initiaux auxquels on retire le j-ème pli qui joue le rôle de jeu de données de validation. Une estimation de l'erreur d'un modèle avec pour hyperparamètre $\alpha_i$ est obtenue en faisant la moyenne des erreurs des modèles $M[\alpha_i,j]$ sur les plis de validation. Ainsi, nous découvrons un meilleur hyperparamètre $\alpha_{best}$. Nous entraînons à nouveau un modèle sur l'ensemble du jeu d'entraînement (i.e., l'union des $K$ plis) avec un hyperparamètre $\alpha$ de valeur $\alpha_{best}$. Finalement, nous testons ce dernier modèle sur le jeu de test pour estimer son erreur sur des données encore jamais utilisées.
Pour une approche souvent plus robuste, nous pouvons employer la stratégie dite de validation croisée à $K$ plis ("K Fold Cross-Validation"). Il s'agit de diviser aléatoirement le jeu d'entraînement en $K$ parties disjointes, appelées plis. Pour chaque valeur de l'hyperparamètre $\lambda$, nous apprenons $K$ modèles. Notons par exemple $M[\lambda_i,j]$, le j-ème des $K$ modèles appris pour la valeur $\lambda_i$ de l'hyperparamètre $\lambda$. Le jeu d'entraînement du modèle $M[\lambda_i,j]$ est constitué de l'union de $K-1$ plis, les $K$ plis initiaux auxquels on retire le j-ème pli qui joue le rôle de jeu de données de validation. Une estimation de l'erreur d'un modèle avec pour hyperparamètre $\lambda_i$ est obtenue en faisant la moyenne des erreurs des modèles $M[\lambda_i,j]$ sur les plis de validation. Ainsi, nous découvrons un meilleur hyperparamètre $\lambda_{best}$. Nous entraînons à nouveau un modèle sur l'ensemble du jeu d'entraînement (i.e., l'union des $K$ plis) avec un hyperparamètre $\lambda$ de valeur $\lambda_{best}$. Finalement, nous testons ce dernier modèle sur le jeu de test pour estimer son erreur sur des données encore jamais utilisées.
# Application de la validation croisée à la régularisation de Tikhonov
Le code source accompagnant ce chapitre comprend une fonction `splitdata` pour diviser le jeu de données en jeu d'entraînement et jeu de test. Ensuite, la fonction `kfoldridge` applique la stratégie de la validation croisée à $K$ plis sur le jeu d'entraînement pour une liste de valeurs de l'hyperparamètre $\alpha$. Elle retourne les coefficients du meilleur modèle et les moyennes des valeurs absolues des erreurs commises sur les plis de validation.
Le code source accompagnant ce chapitre comprend une fonction `splitdata` pour diviser le jeu de données en jeu d'entraînement et jeu de test. Ensuite, la fonction `kfoldridge` applique la stratégie de la validation croisée à $K$ plis sur le jeu d'entraînement pour une liste de valeurs de l'hyperparamètre $\lambda$. Elle retourne les coefficients du meilleur modèle et les moyennes des valeurs absolues des erreurs commises sur les plis de validation.
```{r}
set.seed(1123)
@ -34,15 +34,15 @@ data = gendat(N,0.2)
splitres <- splitdata(data,0.8)
entr <- splitres$entr
test <- splitres$test
alphas <- c(1E-8, 1E-7, 1E-6, 1E-5, 1E-4, 1E-3, 1E-2, 1E-1, 1)
reskfold <- kfoldridge(K = 10, alphas = alphas, data = entr, degre = deg1)
lambdas <- c(1E-8, 1E-7, 1E-6, 1E-5, 1E-4, 1E-3, 1E-2, 1E-1, 1)
reskfold <- kfoldridge(K = 10, lambdas = lambdas, data = entr, degre = deg1)
plt(entr,f)
pltpoly(reskfold$coef)
```
Avec le code ci-dessus, nous générons un nouveau jeu de données composé de `r N` observations et nous calculons par validation croisée un polynôme de degré au plus égal à `r deg1` qui modélise au mieux ces données. La valeur de $\alpha$ retenue est : `r reskfold$alpha`.
Avec le code ci-dessus, nous générons un nouveau jeu de données composé de `r N` observations et nous calculons par validation croisée un polynôme de degré au plus égal à `r deg1` qui modélise au mieux ces données. La valeur de $\lambda$ retenue est : `r reskfold$lambda`.
Traçons un boxplot des erreurs commises sur les plis de validation pour chaque valeur de $\alpha$.
Traçons un boxplot des erreurs commises sur les plis de validation pour chaque valeur de $\lambda$.
```{r}
boxplot(reskfold$maes)