Régularisation
La régularisation est une technique répandue qui est utilisée dans de nombreux modèles d'apprentissage automatique pour contrôler la complexité du modèle et ainsi améliorer sa capacité de généralisation.
Dans cet article, je vais expliquer en profondeur le concept de régularisation et démontrer son utilisation dans plusieurs modèles d'apprentissage automatique.
Qu'est-ce que la régularisation ?
La régularisation est une technique pour éviter le surajustement en pénalisant les modèles complexes. L'idée est d'ajouter un terme de pénalité à la fonction de coût du modèle, de sorte qu'elle devienne dépendante de deux facteurs :
Coût( h ) = Erreur d'entraînement( h ) + λ Complexité( h )
λ est un hyperparamètre (appelé coefficient de régularisation ) qui contrôle le compromis entre le biais et la variance (pour une discussion sur le compromis biais-variance, voir mon article précédent ). Un λ plus élevé induira une pénalité plus importante sur la complexité du modèle, et conduira donc à des modèles plus simples avec une erreur plus élevée sur l'ensemble d'apprentissage mais avec une variance plus faible.
La complexité du modèle peut être mesurée de différentes manières. Par exemple, dans les modèles constitués d'un vecteur de paramètres (pondérations) w , tels que la régression linéaire ou les réseaux de neurones, nous utilisons la taille des paramètres (la norme du vecteur w ) comme mesure de la complexité du modèle.
Dans de tels modèles, il existe deux types courants de régularisation, selon la norme du vecteur w que nous utilisons :
- Régularisation L1 . Dans ce cas, on utilise la norme L1 du vecteur w , c'est-à-dire la somme des valeurs absolues des poids.
Par exemple, dans la régression linéaire, si nous avons m caractéristiques dans notre ensemble de données, alors le modèle aura m paramètres (pondérations) plus un terme de biais, nous pouvons donc écrire la norme L1 de w comme :
|| w ||₁ = |w₀| + |w₁| + … + |w ₘ | - Régularisation L2 . Dans ce cas, on utilise la norme L2 du vecteur w (squared ) , c'est-à-dire la somme des carrés des poids :
|| w ||₂² = w₀² + w₁² + … + w ₘ ²
Régression de crête
Lorsque la régularisation L2 est appliquée à la régression linéaire, nous appelons ce modèle régression ridge .
Pour rappel, en régression linéaire, on nous donne un ensemble de points d'apprentissage D = {( x ₁, y ₁), ( x ₂, y ₂), … , ( x ₙ, yₙ )}, où x ᵢ représente le les caractéristiques du point i et yᵢ représentent l'étiquette de ce point. x ᵢ est un vecteur composé de m caractéristiques, c'est-à-dire x ᵢ = ( xᵢ ₁, xᵢ ₂, …, xᵢₘ ).
Notre objectif est de trouver une ligne (ou un hyperplan) qui passe le plus près possible de tous les points de données, ou en d'autres termes, la fonction que notre modèle essaie d'apprendre (appelée hypothèse du modèle) est :
On aimerait que les prédictions du modèle soient aussi proches que possible des vraies étiquettes des points, ou en d'autres termes, on aimerait minimiser la moindre perte au carré :
En régression ridge, on ajoute à la fonction de coût un terme de pénalité qui dépend de la norme L2 du vecteur poids :
Dans Scikit-Learn, vous pouvez utiliser la classe Ridge pour effectuer une régression de crête, c'est-à-dire pour trouver les poids qui minimisent la fonction de coût ci-dessus. Par défaut, λ (appelé alpha dans Scikit-Learn) est défini sur 1.0, mais vous pouvez modifier sa valeur dans le constructeur de la classe.
Par exemple, examinons ce qui se passe lorsque nous essayons d'ajuster un polynôme à un ensemble de points donné en utilisant la régression de crête. Supposons que notre ensemble d'apprentissage se compose de 50 points de données qui ont été échantillonnés à partir de la fonction f ( x ) = sin( x ) dans l'intervalle [0, 10] avec un peu de bruit aléatoire.
Le code Python suivant génère ces points et les trace :
def make_data(n=50):
rng = np.random.RandomState(0)
X = rng.rand(n, 1) * 10
err = rng.normal(size=n) * 0.3
y = np.sin(X).ravel() + err
return X, y
def plot_data(X, y):
plt.scatter(X, y, color='k')
plt.xlabel('$x$')
plt.ylabel('$y$')
X, y = make_data()
plot_data(X, y)
The given data points
Nous définissons d'abord notre modèle comme un pipeline composé de 3 étapes :
- Un transformateur StandardScaler qui normalise les données. Lorsque vous utilisez la régularisation, il est important de normaliser vos données, car nous appliquons le même coefficient de régularisation λ à toutes les caractéristiques.
- Un transformateur PolynomialFeatures qui ajoute des caractéristiques polynomiales avec un degré inférieur ou égal à 10 à notre ensemble de données.
- Le régresseur Ridge lui-même.
from sklearn.preprocessing import PolynomialFeatures, StandardScaler
from sklearn.linear_model import Ridge
from sklearn.pipeline import Pipeline
def RidgePolynomialRegression(degree=10, alpha=1):
return Pipeline([('scaler', StandardScaler()),
('polyfeatures', PolynomialFeatures(degree)),
('ridge', Ridge(alpha))])
X_test = np.linspace(0, 10, 100).reshape(-1, 1)
fig, axes = plt.subplots(2, 5, figsize=(10, 4))
plt.subplots_adjust(hspace=0.5)
alpha = 0.00001
for ax in axes.flat:
reg = RidgePolynomialRegression(alpha=alpha)
reg.fit(X, y)
y_test = reg.predict(X_test)
ax.plot(X_test, y_test, color='r')
ax.set_title(f'$\lambda = ${alpha}')
ax.scatter(X, y, color='k', s=5)
alpha *= 10
Si nous examinons ce qui arrive aux poids lorsque nous augmentons λ , nous pouvons voir qu'ils deviennent de plus en plus petits :
alpha = 0.00001
for i in range(10):
reg = RidgePolynomialRegression(degree=10, alpha=alpha)
reg.fit(X, y)
coef = reg.named_steps['ridge'].coef_[1:]
print(coef)
alpha *= 10
[-0.34255958 4.65041874 1.52936947 -4.40599301 -1.90098186 2.23311211
0.88066028 -0.67051191 -0.1365452 0.08475464]
[-0.33965761 4.63580242 1.51570516 -4.36588145 -1.88217563 2.19393547
0.87079965 -0.65469155 -0.13481568 0.08249219]
[-0.31363162 4.50439571 1.39332345 -4.00624119 -1.71391192 1.84322694
0.78264629 -0.51321344 -0.11936373 0.06227446]
[-0.19071657 3.87021122 0.82356155 -2.31939344 -0.9393007 0.22556941
0.38056787 0.13218128 -0.04939819 -0.02922885]
[-0.04619268 3.00037721 0.25566347 -0.6695211 -0.28744623 -0.98332127
0.09608197 0.51757036 -0.00745002 -0.07428806]
[ 0.04317733 1.66557129 -0.03687544 0.17571591 -0.11153223 -0.41803515
0.12726457 -0.02719409 -0.03157695 0.03059514]
[ 0.0125075 0.60775006 0.01115537 0.34194187 -0.01115688 0.0516252
-0.02478579 -0.1976274 0.01210612 0.03770911]
[-0.00213858 0.13360357 -0.00969971 0.11558718 -0.02610679 0.0725844
-0.03817568 0.00108649 0.0228815 -0.01947446]
[-0.00410878 0.02199911 -0.0053407 0.02588268 -0.00708055 0.02730288
-0.00661614 0.02012954 0.00397905 -0.01315045]
[-0.00054706 0.00266053 -0.00068137 0.00342859 -0.00092493 0.00400394
-0.00119852 0.00334012 -0.00161305 -0.00245165]
from sklearn.model_selection import GridSearchCV
param_grid = {
'polyfeatures__degree': np.arange(1, 11),
'ridge__alpha': np.logspace(-5, 4, num=10)
}
grid = GridSearchCV(RidgePolynomialRegression(), param_grid, n_jobs=-1)
grid.fit(X, y)
print(grid.best_params_)
{'polyfeatures__degree': 8, 'ridge__alpha': 0.01}
Régression au lasso
La régression lasso est très similaire à la régression ridge, sauf que nous utilisons la régularisation L1 au lieu de la régularisation L2. La fonction de coût a donc la forme suivante :
En général, la régularisation L1 est une forme de régularisation plus forte que L2,
Exécutons la régression Lasso au lieu de la régression Ridge sur le même ensemble de données décrit précédemment. Tout ce que nous avons à faire est de remplacer la classe Ridge par la classe Lasso dans notre pipeline :
from sklearn.preprocessing import PolynomialFeatures, StandardScaler
from sklearn.linear_model import Lasso
from sklearn.pipeline import Pipeline
def LassoPolynomialRegression(degree=10, alpha=1):
return Pipeline([('scaler', StandardScaler()),
('polyfeatures', PolynomialFeatures(degree)),
('lasso', Lasso(alpha))])
X_test = np.linspace(0, 10, 100).reshape(-1, 1)
fig, axes = plt.subplots(2, 5, figsize=(10, 4))
plt.subplots_adjust(hspace=0.5)
alpha = 0.00001
for ax in axes.flat:
reg = LassoPolynomialRegression(alpha=alpha)
reg.fit(X, y)
y_test = reg.predict(X_test)
ax.plot(X_test, y_test, color='r')
ax.set_title(f'$\lambda = ${alpha}')
ax.scatter(X, y, color='k', s=5)
alpha *= 10
Nous pouvons clairement voir que la régularisation a eu un effet beaucoup plus fort sur le modèle que dans la régression ridge.
Examinons ce qui arrive aux poids du modèle lorsque nous augmentons λ :
alpha = 0.00001
for i in range(10):
reg = LassoPolynomialRegression(degree=10, alpha=alpha)
reg.fit(X, y)
coef = reg.named_steps['lasso'].coef_[1:]
print(coef)
alpha *= 10
[ 1.46823703e+00 2.63999643e+00 -1.16846632e+00 -1.37767850e+00
1.91631342e-02 1.35144644e-01 4.17104620e-02 2.77956760e-02
6.25525954e-03 -1.33167094e-03]
[ 1.46587728e+00 2.62935208e+00 -1.16566855e+00 -1.36644025e+00
1.95947992e-02 1.32103252e-01 4.09351664e-02 2.76081239e-02
6.36955259e-03 -1.23628815e-03]
[ 1.44081438e+00 2.52202831e+00 -1.13306732e+00 -1.25259396e+00
2.06435112e-02 1.00689833e-01 3.37898859e-02 2.59250307e-02
7.53377577e-03 -2.70373324e-04]
[ 1.19046738e+00 1.52316023e+00 -8.09126923e-01 -2.99714326e-01
-0.00000000e+00 -1.09742383e-01 -0.00000000e+00 2.25379843e-04
9.26904439e-03 6.37664693e-03]
[ 2.33325641e-01 3.68424685e-01 0.00000000e+00 0.00000000e+00
-0.00000000e+00 2.20183217e-02 -4.03913628e-02 0.00000000e+00
-3.60328411e-04 -8.72522225e-03]
[ 0. 0. -0. 0. -0. 0.
-0. 0. -0.00310639 -0.00095184]
[-0.00000000e+00 0.00000000e+00 -0.00000000e+00 0.00000000e+00
-0.00000000e+00 0.00000000e+00 -0.00000000e+00 0.00000000e+00
-0.00000000e+00 1.20777566e-05]
[-0. 0. -0. 0. -0. 0. -0. 0. -0. 0.]
[-0. 0. -0. 0. -0. 0. -0. 0. -0. 0.]
[-0. 0. -0. 0. -0. 0. -0. 0. -0. 0.]
Régression Elastic-Net
Le filet élastique est une régression intermédiaire entre les régressions de crête et de lasso. Il combine à la fois les pénalités L1 et L2 à l'aide d'un paramètre de mélange noté r (0 ≤ r ≤ 1) :
Lorsque r = 0, Elastic-Net devient identique à la régression ridge, et lorsque r = 1, Elastic-Net devient identique à la régression lasso.
Dans Scikit-Learn, vous pouvez utiliser la classe ElasticNet pour effectuer une régression nette élastique. Son constructeur prend deux paramètres : alpha qui représente le coefficient de régularisation λ (par défaut fixé à 1.0), et l1_ratio qui représente le paramètre de mélange r (par défaut fixé à 0.5).
Examinons ce qui se passe lorsque nous exécutons Elastic-Net avec λ = 1,0 et différentes valeurs de r (l1_ratio) sur le même ensemble de données :
Comme on peut le voir, l'augmentation de r induit une régularisation plus forte sur le modèle, jusqu'à ce qu'il devienne presque plat.
Remarques finales
Vous pouvez trouver tous les exemples de code mentionnés dans cet article sur mon dépôt github :https://github.com/roiyeho/medium/tree/main/regularization
Si vous aimez mon contenu, pensez à me suivre afin que je puisse continuer à vous fournir des articles plus intéressants :)
![Qu'est-ce qu'une liste liée, de toute façon? [Partie 1]](https://post.nghiatu.com/assets/images/m/max/724/1*Xokk6XOjWyIGCBujkJsCzQ.jpeg)



































