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:
normalize=True
Vs utilisation de Normalizer
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:
J'ai trois questions que je ne peux pas concilier:
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.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.Normalizer
indique 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.
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.
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érenceCette 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.
Normalizer
effectue la normalisation par rapport à chaque échantillon (c'est-à-dire par ligne). Vous voyez le code de référence ici .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()
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.
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.
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.
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
.
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.
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. :)