• 12 heures
  • Moyenne

Ce cours est visible gratuitement en ligne.

Ce cours est en vidéo.

Vous pouvez obtenir un certificat de réussite à l'issue de ce cours.

J'ai tout compris !

Mis à jour le 13/08/2018

Apprenez des étiquettes réelles avec une régression ridge à noyau

Connectez-vous ou inscrivez-vous gratuitement pour bénéficier de toutes les fonctionnalités de ce cours !

Il n'y a pas qu'aux SVM que l'on peut appliquer l'astuce du noyau ! Elle marche avec un certain nombre d'algorithmes linéaires, par exemple, la régression ridge régularisée.

Prenons n points en p dimensions, décrits par la matrice X (de taille n x p), et leurs étiquettes, décrites par le vecteur y (de taille n).

Pour une nouvelle observation x, la prédiction de la régression ridge est :

$\(f(x) = x \beta\)$

$\(\beta \)$ est un vecteur colonne de taille p, donné par $\(\beta = ( \lambda I_p + X^\top X)^{-1} X^\top y\)$ . Je note $\(I_p\)$ la matrice identité de taille p x p.

Ainsi donc

$\(f(x) = x ( \lambda I_p + X^\top X)^{-1} X^\top y.\)$

Cette équation peut se réécrire sous la forme

$\(f(x) = x X^\top ( \lambda I_n + X X^\top)^{-1} y\)$

Cela nécessite quelques manipulations mathématiques, que vous trouverez ci-dessous mais qui ne sont pas essentielles à la compréhension, si vous admettez cette réécriture...

En effet, en multipliant à gauche l'expression qui définit $\(\beta\)$ par  $\(( \lambda I_p + X^\top X)\)$ , on obtient : $\(( \lambda I_p + X^\top X) \beta = X^\top y\)$ et donc $\(\beta = X^\top \alpha\)$$\(\alpha = \frac{1}{\lambda} \left(y - X\beta \right).\)$

En remplaçant $\( \beta \)$ par $\( X^\top \alpha \)$ dans la définition de $\( \alpha, \)$ on obtient $\(\lambda \alpha = y - X X^\top \alpha\)$ et donc $\(\alpha = ( \lambda I_n + X X^\top)^{-1} y.\)$

Revenons maintenant à f : $\(f(x) = x \beta = x X^\top \alpha\)$ et  nous avons donc bien notre équation

$\(f(x) = x X^\top ( \lambda I_n + X X^\top)^{-1} y\)$

Supposons maintenant une application  $\(\phi\)$ vers un espace de redescription H. Appliquons maintenant la régression ridge dans H :

$\(f_\phi(x) = \phi(x) \Phi^\top ( \lambda I_n + \Phi \Phi^\top)^{-1} y\)$

$\(\Phi\)$ est la matrice de taille n x d (d étant la dimension de H) dont la i-ième ligne est l'image du i-ème point du jeu de données dans H, autrement dit le vecteur $\(\phi(x^{(i)})\)$ .

À cette application $\(\phi\)$ correspond un noyau, défini par $\(k(x, x') = \langle \phi(x), \phi(x') \rangle.\)$

Il suffit maintenant de remplacer le vecteur $\(\phi(x) \Phi^\top \)$ par le vecteur $\( \kappa\)$ et la matrice $\(\Phi \Phi^\top \)$ par la matrice de Gram K, où $\(\kappa = ( k(x, x^{(1)}), k(x, x^{(2)}), \dots, k(x, x^{(n)}))\)$ et K, comme pour les SVMs est la matrice n x n telle que $\( K_{il} = k(x^{(i)}, x^{(l)}).\)$

On peut donc écrire

$\(f_\phi(x) = \kappa ( \lambda I_n + K)^{-1} y\)$

et ici aussi, comme dans le cas des SVMs, entraîner et utiliser une régression ridge dans l'espace de redescription sans avoir à connaître $\( \phi \)$ ni à calculer explicitement l'image d'aucun point par cette application !

Cette méthode, que l'on appelle donc, vous l'aurez deviné, la régression ridge à noyau, s'appelle kernel ridge regression en anglais (ou kRR).

Oui mais attends... qu'est-il arrivé aux coefficients $\( \beta \)$ ?

L'astuce du noyau ne nous permet pas de déterminer $\(\beta \)$ explicitement. D'ailleurs souvenez-vous, dans le cas du noyau RBF gaussien, l'espace de redescription est de dimension infinie, et donc \beta aussi... Ça n'aurait pas grand sens d'essayer de le calculer !

Contrairement à sa version linéaire, la version à noyau de la régression ridge ne nous donne pas une forme explicite de la fonction de décision f en fonction des variables. C'est ce qu'on appelle une approche non-paramétrique (eh oui, malgré les paramètres du noyau), comme pour la SVM à noyau.

Il existe bien d'autres approches non-paramétriques, en particulier la méthode des plus proches voisins ou les arbres de décisions et les forêts aléatoires.

En quoi la kRR diffère-t-elle vraiment de la SVR ?

Les modèles appris par la régression ridge et une SVR ont exactement la même forme, mais pas les mêmes coefficients. En effet, on a choisi d'optimiser des fonctions de perte différentes pour l'une et pour l'autre. Dans le cas de la régression ridge, on choisit de minimiser l'erreur quadratique entre la prédiction et la réalité. Pour la SVR, on utilise en fait une fonction de perte dite insensible à \epsilon, c'est à dire qu'elle vaut la valeur absolue de la différence entre la prédiction et la réalité, sauf si cette différence est faible, comprise entre -\epsilon et \epsilon, auquel cas on l'estime négligeable et la fonction de perte vaut 0.

En pratique, entraîner une kRR sera plus efficace car la solution est analytique est exacte ; prédire sera par contre plus rapide avec une SVR.

En pratique avec scikit-learn

La régression ridge à noyau est implémentée dans scikit-learn dans la classe kernel_ridge.KernelRidge.

Nous allons utiliser les données concernant les caractéristiques physico-chimiques de vins blancs portugais disponibles sur l'archive UCI. Il s'agit ici de prédire le score (entre 3 et 9) donné par des experts aux différents vins.

Chargeons les données, séparons-les en un jeu d'entraînement et un jeu de test contenant respectivement 70% et 30% des données, et standardisons les variables sur le jeu d'entraînement.

# charger les données
import pandas as pd
data = pd.read_csv('winequality-white.csv', sep=';')

# créer la matrice de données
X = data.as_matrix(data.columns[:-1])

# créer le vecteur d'étiquettes
y = data.as_matrix([data.columns[-1]])
y = y.flatten()

# créer un jeu d'entrainement et un jeu de test (30% des données)
from sklearn import model_selection
X_train, X_test, y_train, y_test = \
    model_selection.train_test_split(X, y, test_size=0.3)
                                
# standardiser les données
from sklearn import preprocessing
std_scale = preprocessing.StandardScaler().fit(X_train)
X_train_std = std_scale.transform(X_train)
X_test_std = std_scale.transform(X_test)                               

Nous pouvons maintenant entraîner une kRR sur le jeu d'entraînement en utilisant des paramètres par défaut pour le paramètre de régularisation et la bande passante du noyau RBF gaussien :

# initialiser un objet de classification par kRR
from sklearn import kernel_ridge
predicteur = kernel_ridge.KernelRidge(alpha=1.0, # valeur par défaut 
                                     kernel='rbf', # noyau Gaussien
                                     gamma=0.01)   # valeur de 1/(2 * sigma**2)
                                     
# entraîner le classifieur sur le jeu d'entrainement
predicteur.fit(X_train_std, y_train)

# prédire sur le jeu de test
y_test_pred = predicteur.predict(X_test_std)

# calculer la RMSE sur le jeu de test
from sklearn import metrics
rmse = np.sqrt(metrics.mean_squared_error(y_test, y_test_pred))
print "RMSE: %.2f" % rmse

On obtient une RMSE de 0.72, ce qui est correct pour des nombres entiers allant de 3 à 9.

Pour visualiser les prédictions, on peut utiliser un nuage de points dans lequel la surface de chaque point est proportionnelle au nombre d'observations ayant exactement ces valeurs de score prédit et de score réel. Pour plus de détails, vous pouvez vous référer à la procédure utilisée dans le cours Comparez votre algorithme à des approches de régression naïves.

# créer une figure
fig = plt.figure(figsize=(6, 6))

# Compter, pour chaque paire de valeurs (y, y') où y est un vrai score et y' le score prédit,
# le nombre de ces paires.
# Ce nombre sera utilisé pour modifier la taille des marqueurs correspondants 
# dans un nuage de points
sizes = {}
for (yt, yp) in zip(list(y_test), list(y_test_pred)):
    if sizes.has_key((yt, yp)):
        sizes[(yt, yp)] += 1
    else:
        sizes[(yt, yp)] = 1        
keys = sizes.keys()

# afficher les prédictions
plt.scatter([k[0] for k in keys], 
            [k[1] for k in keys], 
             s=[sizes[k] for k in keys], 
            label='gamma = 0.01: RMSE = %0.2f' % rmse)

# étiqueter les axes et le graphique
plt.xlabel('Vrai score', fontsize=16)
plt.ylabel(u'Score prédit', fontsize=16)
plt.title('kernel Ridge Regression', fontsize=16)

# limites des axes
plt.xlim([2.9, 9.1])
plt.ylim([2.9, 9.1])

# afficher la légende
plt.legend(loc="lower right", fontsize=12)

On obtient le graphique suivant :

Score du vin prédit vs. vrai score, sur le jeu de test, pour une kRR.
Score du vin prédit vs. vrai score, sur le jeu de test, pour une kRR.

On observe une corrélation entre les scores prédits et les score réels.

Comme dans le cas de la SVM à noyau, le paramètre gamma du noyau RBF gaussien joue un rôle important : s'il est trop élevé, la matrice de Gram sur le jeu d'entraînement est dominée par sa diagonale et la kRR ne peut pas apprendre. Si l'on recommence avec gamma=50, on obtient les prédictions suivantes :

Score du vin prédit vs. vrai score, sur le jeu de test, pour une kRR avec différentes valeurs de gamma.
Score du vin prédit vs. vrai score, sur le jeu de test, pour une kRR avec différentes valeurs de gamma.

Les prédictions avec gamma=50 sont de très mauvaise qualité!

Pour optimiser le paramètre de régularisation alpha et le paramètre gamma du noyau gaussien, on peut utiliser une recherche sur grille :

# valeurs du paramètre C
alpha_range = np.logspace(-2, 2, 5)

# valeurs du paramètre gamma
gamma_range = np.logspace(-2, 1, 4)

# grille de paramètres
param_grid = {'alpha': alpha_range, 'gamma': gamma_range}

# score pour sélectionner le modèle optimal
score = 'neg_mean_squared_error'

# initialiser la validation croisée
grid_pred = model_selection.GridSearchCV(kernel_ridge.KernelRidge(kernel='rbf'),
                                    param_grid,
                                    cv=5,
                                    scoring=score)
                                    
# exécuter la validation croisée sur le jeu d'entraînement
grid_pred.fit(X_train_std, y_train)

# prédire sur le jeu de test avec le modèle sélectionné 
y_test_pred_cv = grid_pred.predict(X_test_std)

# calculer la RMSE correspondante
rmse_cv = np.sqrt(metrics.mean_squared_error(y_test, y_test_pred_cv))

On obtient une RMSE légèrement meilleure (0.71) en optimisant le modèle.

Les prédictions faites se superposent à peu près à celles obtenues avec gamma=0.01 :

Score du vin prédit vs. vrai score, sur le jeu de test, pour plusieurs kRR.
Score du vin prédit vs. vrai score, sur le jeu de test, pour plusieurs kRR.

Exemple de certificat de réussite
Exemple de certificat de réussite