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 :
où est un vecteur colonne de taille p, donné par . Je note la matrice identité de taille p x p.
Ainsi donc
Cette équation peut se réécrire sous la forme
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 par , on obtient : et donc où
En remplaçant par dans la définition de on obtient et donc
Revenons maintenant à f : et nous avons donc bien notre équation
Supposons maintenant une application vers un espace de redescription H. Appliquons maintenant la régression ridge dans H :
où 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 .
À cette application correspond un noyau, défini par
Il suffit maintenant de remplacer le vecteur par le vecteur et la matrice par la matrice de Gram K, où et K, comme pour les SVMs est la matrice n x n telle que
On peut donc écrire
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 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 ?
L'astuce du noyau ne nous permet pas de déterminer 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 et , 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[data.columns[:-1]].values
# créer le vecteur d'étiquettes
y = data['quality'].values
# 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 :
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}".format(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 (yt, yp) in sizes.keys():
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 = {:.2f}".format(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 :
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 :
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 :
En résumé
Tout comme pour la SVM, l’astuce du noyau permet d’utiliser la régression ridge sur des problèmes non linéaires.
La SVR et kRR s’appliquent sur des problèmes de régression.
La validation croisée permet de choisir les bons hyperparamètres lors de l’utilisation de ces algorithmes avec Scikit-learn.
Vous avez découvert comment appliquer des algorithmes linéaires à des problèmes non linéaires.
Il est maintenant temps de tester vos connaissances à l’aide d’un petit quizz dans le chapitre suivant. Bonne chance !