web-dev-qa-db-fra.com

Comparaison des résultats de StandardScaler vs Normalizer en régression linéaire

Je travaille sur quelques exemples de régression linéaire sous différents scénarios, en comparant les résultats de l'utilisation de Normalizer et StandardScaler, et les résultats sont déroutants.

J'utilise l'ensemble de données sur le logement de Boston et je le prépare de cette façon:

import numpy as np
import pandas as pd
from sklearn.datasets import load_boston
from sklearn.preprocessing import Normalizer
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LinearRegression

#load the data
df = pd.DataFrame(boston.data)
df.columns = boston.feature_names
df['PRICE'] = boston.target

J'essaie actuellement de raisonner sur les résultats que j'obtiens des scénarios suivants:

  • Initialisation de la régression linéaire avec le paramètre normalize=True Vs utilisation de Normalizer
  • Initialisation de la régression linéaire avec le paramètre fit_intercept = False Avec et sans standardisation.

Collectivement, je trouve les résultats déroutants.

Voici comment je configure tout:

# Prep the data
X = df.iloc[:, :-1]
y = df.iloc[:, -1:]
normal_X = Normalizer().fit_transform(X)
scaled_X = StandardScaler().fit_transform(X)

#now prepare some of the models
reg1 = LinearRegression().fit(X, y)
reg2 = LinearRegression(normalize=True).fit(X, y)
reg3 = LinearRegression().fit(normal_X, y)
reg4 = LinearRegression().fit(scaled_X, y)
reg5 = LinearRegression(fit_intercept=False).fit(scaled_X, y)

Ensuite, j'ai créé 3 cadres de données distincts pour comparer le R_score, les valeurs de coefficient et les prévisions de chaque modèle.

Pour créer la trame de données pour comparer les valeurs des coefficients de chaque modèle, j'ai fait ce qui suit:

#Create a dataframe of the coefficients
coef = pd.DataFrame({
    'coeff':                       reg1.coef_[0],
    'coeff_normalize_true':        reg2.coef_[0],
    'coeff_normalizer':            reg3.coef_[0],
    'coeff_scaler':                reg4.coef_[0],
    'coeff_scaler_no_int':         reg5.coef_[0]
})

Voici comment j'ai créé la trame de données pour comparer les valeurs R ^ 2 de chaque modèle:

scores = pd.DataFrame({
    'score':                        reg1.score(X, y),
    'score_normalize_true':         reg2.score(X, y),
    'score_normalizer':             reg3.score(normal_X, y),
    'score_scaler':                 reg4.score(scaled_X, y),
    'score_scaler_no_int':          reg5.score(scaled_X, y)
    }, index=range(1)
)

Enfin, voici le dataframe qui compare les prédictions de chacun:

predictions = pd.DataFrame({
    'pred':                        reg1.predict(X).ravel(),
    'pred_normalize_true':         reg2.predict(X).ravel(),
    'pred_normalizer':             reg3.predict(normal_X).ravel(),
    'pred_scaler':                 reg4.predict(scaled_X).ravel(),
    'pred_scaler_no_int':          reg5.predict(scaled_X).ravel()
}, index=range(len(y)))

Voici les trames de données résultantes:

COEFFICIENTS: Dataframe comparing the coefficients from different models

SCORES: Dataframe comparing the R^2 values from different models

PRÉDICTIONS: Dataframe comparing the prediction values from different models

J'ai trois questions que je ne peux pas concilier:

  1. Pourquoi n'y a-t-il pas absolument aucune différence entre les deux premiers modèles? Il semble que le réglage normalize=False Ne fasse rien. Je peux comprendre que les prédictions et les valeurs R ^ 2 soient les mêmes, mais mes caractéristiques ont des échelles numériques différentes, donc je ne sais pas pourquoi la normalisation n'aurait aucun effet. Cela est doublement déroutant lorsque l'on considère que l'utilisation de StandardScaler modifie considérablement les coefficients.
  2. Je ne comprends pas pourquoi le modèle utilisant Normalizer provoque des valeurs de coefficient aussi radicalement différentes des autres, surtout lorsque le modèle avec LinearRegression(normalize=True) ne fait aucun changement.

Si vous regardiez la documentation de chacun, il semble qu'ils soient très similaires sinon identiques.

Depuis les documents sur sklearn.linear_model.LinearRegression () :

normaliser: booléen, facultatif, par défaut Faux

Ce paramètre est ignoré lorsque fit_intercept est défini sur False. Si Vrai, les régresseurs X seront normalisés avant la régression en soustrayant la moyenne et en divisant par la norme l2.

Pendant ce temps, les documents sur sklearn.preprocessing.Normalizerindique qu'il se normalise par défaut à la norme l2 .

Je ne vois pas de différence entre ce que font ces deux options et je ne vois pas pourquoi l'une aurait des différences aussi radicales dans les valeurs des coefficients de l'autre.

  1. Les résultats du modèle utilisant StandardScaler sont cohérents pour moi, mais je ne comprends pas pourquoi le modèle utilisant StandardScaler et définissant set_intercept=False Fonctionne si mal.

À partir des documents sur le module de régression linéaire :

fit_intercept: booléen, facultatif, par défaut True

s'il faut calculer l'ordonnée à l'origine pour ce modèle. Si la valeur est False, non
l'ordonnée à l'origine sera utilisée dans les calculs (par exemple, les données devraient déjà être
centré).

Le StandardScaler centre vos données, donc je ne comprends pas pourquoi leur utilisation avec fit_intercept=False Produit des résultats incohérents.

16
Jonathan Bechtel
  1. La raison de l'absence de différence de coefficients entre les deux premiers modèles est que Sklearn dénormalise les coefficients en arrière-plan après avoir calculé les coefficients à partir des données d'entrée normalisées. Référence

Cette dénormalisation a été effectuée car pour les données de test, nous pouvons directement appliquer les coefficients. et obtenir la prédiction sans normaliser les données de test.

Par conséquent, la définition de normalize=True ont un impact sur les coefficients, mais ils n'affectent pas de toute façon la ligne de meilleur ajustement.

  1. Normalizer effectue la normalisation par rapport à chaque échantillon (c'est-à-dire par ligne). Vous voyez le code de référence ici .

De la documentation:

Normaliser les échantillons individuellement à la norme unitaire.

tandis que normalize=True effectue la normalisation par rapport à chaque colonne/fonction. Référence

Exemple pour comprendre l'impact de la normalisation à différentes dimensions des données. Prenons deux dimensions x1 et x2 et y soit la variable cible. La valeur de la variable cible est codée par couleur sur la figure.

import matplotlib.pyplot as plt
from sklearn.preprocessing import Normalizer,StandardScaler
from sklearn.preprocessing.data import normalize

n=50
x1 = np.random.normal(0, 2, size=n)
x2 = np.random.normal(0, 2, size=n)
noise = np.random.normal(0, 1, size=n)
y = 5 + 0.5*x1 + 2.5*x2 + noise

fig,ax=plt.subplots(1,4,figsize=(20,6))

ax[0].scatter(x1,x2,c=y)
ax[0].set_title('raw_data',size=15)

X = np.column_stack((x1,x2))

column_normalized=normalize(X, axis=0)
ax[1].scatter(column_normalized[:,0],column_normalized[:,1],c=y)
ax[1].set_title('column_normalized data',size=15)

row_normalized=Normalizer().fit_transform(X)
ax[2].scatter(row_normalized[:,0],row_normalized[:,1],c=y)
ax[2].set_title('row_normalized data',size=15)

standardized_data=StandardScaler().fit_transform(X)
ax[3].scatter(standardized_data[:,0],standardized_data[:,1],c=y)
ax[3].set_title('standardized data',size=15)

plt.subplots_adjust(left=0.3, bottom=None, right=0.9, top=None, wspace=0.3, hspace=None)
plt.show()

enter image description here

Vous pouvez voir que la ligne la mieux adaptée aux données des figures 1, 2 et 4 serait la même; signifie que le score R2_ ne changera pas en raison de la normalisation des colonnes/fonctionnalités ou de la normalisation des données. Juste cela, il se retrouve avec différents coefficients. valeurs.

Remarque: meilleure ligne d'ajustement pour fig3 serait différent.

  1. Lorsque vous définissez fit_intercept = False, le terme de biais est soustrait de la prédiction. Cela signifie que l'ordonnée à l'origine est définie sur zéro, ce qui autrement aurait été la moyenne de la variable cible.

La prédiction avec interception comme zéro devrait donner de mauvais résultats pour les problèmes où les variables cibles ne sont pas mises à l'échelle (moyenne = 0). Vous pouvez voir une différence de 22,532 dans chaque ligne, ce qui signifie l'impact de la sortie.

8
Venkatachalam

Réponse au premier trimestre

Je suppose que ce que vous voulez dire avec les 2 premiers modèles est reg1 Et reg2. Faites-nous savoir si ce n'est pas le cas.

Une régression linéaire a le même pouvoir prédictif si vous normalisez ou non les données. Par conséquent, l'utilisation de normalize=True N'a aucun impact sur les prévisions. Une façon de comprendre cela est de voir que la normalisation (par colonne) est une opération linéaire sur chacune des colonnes ((x-a)/b) Et que les transformations linéaires des données sur une régression linéaire n'affectent pas l'estimation du coefficient, ne font que changer leurs valeurs. Notez que cette déclaration n'est pas vraie pour Lasso/Ridge/ElasticNet.

Alors, pourquoi les coefficients ne sont-ils pas différents? Eh bien, normalize=True Prend également en compte le fait que ce que l'utilisateur veut normalement, ce sont les coefficients sur les fonctionnalités d'origine, pas les fonctionnalités normalisées. En tant que tel, il ajuste les coefficients. Une façon de vérifier que cela a du sens est d'utiliser un exemple plus simple:

# two features, normal distributed with sigma=10
x1 = np.random.normal(0, 10, size=100)
x2 = np.random.normal(0, 10, size=100)

# y is related to each of them plus some noise
y = 3 + 2*x1 + 1*x2 + np.random.normal(0, 1, size=100)

X = np.array([x1, x2]).T  # X has two columns

reg1 = LinearRegression().fit(X, y)
reg2 = LinearRegression(normalize=True).fit(X, y)

# check that coefficients are the same and equal to [2,1]
np.testing.assert_allclose(reg1.coef_, reg2.coef_) 
np.testing.assert_allclose(reg1.coef_, np.array([2, 1]), rtol=0.01)

Ce qui confirme que les deux méthodes capturent correctement le signal réel entre [x1, x2] et y, à savoir les 2 et 1 respectivement.

Réponse à Q2

Normalizer n'est pas ce que vous attendez. Il normalise chaque ligne par ligne. Ainsi, les résultats changeront radicalement et détruiront probablement la relation entre les fonctionnalités et la cible que vous souhaitez éviter, sauf dans des cas spécifiques (par exemple TF-IDF).

Pour voir comment, supposons l'exemple ci-dessus, mais considérons une fonctionnalité différente, x3, Qui n'est pas liée à y. L'utilisation de Normalizer entraîne la modification de x1 Par la valeur de x3, Ce qui diminue la force de sa relation avec y.

Écart des coefficients entre les modèles (1,2) et (4,5)

La différence entre les coefficients est que lorsque vous standardisez avant l'ajustement, les coefficients seront par rapport aux caractéristiques normalisées, les mêmes coefficients que j'ai mentionnés dans la première partie de la réponse. Ils peuvent être mappés aux paramètres d'origine en utilisant reg4.coef_ / scaler.scale_:

x1 = np.random.normal(0, 10, size=100)
x2 = np.random.normal(0, 10, size=100)
y = 3 + 2*x1 + 1*x2 + np.random.normal(0, 1, size=100)
X = np.array([x1, x2]).T

reg1 = LinearRegression().fit(X, y)
reg2 = LinearRegression(normalize=True).fit(X, y)
scaler = StandardScaler()
reg4 = LinearRegression().fit(scaler.fit_transform(X), y)

np.testing.assert_allclose(reg1.coef_, reg2.coef_) 
np.testing.assert_allclose(reg1.coef_, np.array([2, 1]), rtol=0.01)

# here
coefficients = reg4.coef_ / scaler.scale_
np.testing.assert_allclose(coefficients, np.array([2, 1]), rtol=0.01)

En effet, mathématiquement, en définissant z = (x - mu)/sigma, le modèle reg4 résout y = a1*z1 + a2*z2 + a0. Nous pouvons récupérer la relation entre y et x grâce à une algèbre simple: y = a1*[(x1 - mu1)/sigma1] + a2*[(x2 - mu2)/sigma2] + a0, qui peut être simplifiée en y = (a1/sigma1)*x1 + (a2/sigma2)*x2 + (a0 - a1*mu1/sigma1 - a2*mu2/sigma2).

reg4.coef_ / scaler.scale_ Représente [a1/sigma1, a2/sigma2] Dans la notation ci-dessus, ce qui est exactement ce que normalize=True Fait pour garantir que les coefficients sont les mêmes.

Écart du score du modèle 5.

Les entités normalisées ont une moyenne nulle, mais la variable cible ne l'est pas nécessairement. Par conséquent, le fait de ne pas ajuster l'ordonnée à l'origine fait que le modèle ne tient pas compte de la moyenne de la cible. Dans l'exemple que j'ai utilisé, le "3" dans y = 3 + ... N'est pas ajusté, ce qui diminue naturellement le pouvoir prédictif du modèle. :)

4
Jorge Leitao