web-dev-qa-db-ja.com

線形回帰におけるStandardScalerとNormalizerの結果の比較

NormalizerStandardScalerを使用した結果を比較して、さまざまなシナリオで線形回帰のいくつかの例を調べていますが、結果は不可解です。

私はボストンハウジングのデータセットを使用しており、次のように準備しています。

_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
_

私は現在、以下のシナリオから得られる結果について推理しようとしています:

  • パラメータ_normalize=True_を使用した線形回帰の初期化とNormalizerの使用
  • 標準化あり/なしのパラメーター_fit_intercept = False_を使用した線形回帰の初期化。

総じて、私は結果を混乱させます。

すべてを設定する方法は次のとおりです。

_# 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)
_

次に、3つの個別のデータフレームを作成して、各モデルのR_score、係数値、および予測を比較しました。

各モデルの係数値を比較するデータフレームを作成するために、次のことを行いました。

_#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]
})
_

これが、各モデルのR ^ 2値を比較するためのデータフレームの作成方法です。

_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)
)
_

最後に、それぞれの予測を比較するデータフレームは次のとおりです。

_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)))
_

結果のデータフレームは次のとおりです。

係数:Dataframe comparing the coefficients from different models

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

予測:Dataframe comparing the prediction values from different models

調整できない3つの質問があります。

  1. 最初の2つのモデルの間に絶対的な違いがないのはなぜですか? _normalize=False_を設定しても何も起こらないようです。予測とR ^ 2値が同じであることは理解できますが、機能の数値スケールが異なるため、正規化がまったく影響しない理由がわかりません。 StandardScalerを使用すると係数が大幅に変化すると考えると、これは二重に混乱します。
  2. Normalizerを使用したモデルが他のモデルとは根本的に異なる係数値を引き起こす理由、特にLinearRegression(normalize=True)を使用したモデルがまったく変更を行わない場合は、理解できません。

それぞれのドキュメントを見ると、同一ではないにせよ非常に似ているように見えます。

sklearn.linear_model.LinearRegression() のドキュメントから:

normalize:ブール値、オプション、デフォルトFalse

このパラメーターは、fit_interceptがFalseに設定されている場合は無視されます。 Trueの場合、リグレッサXは、平均を差し引き、l2-ノルムで割ることにより、回帰前に正規化されます。

一方、_sklearn.preprocessing.Normalizer_ のドキュメントには、デフォルトでl2ノルムに正規化されていると記載されています

これら2つのオプションの機能に違いは見られません。また、なぜ係数値に他のオプションとの根本的な違いがあるのか​​わかりません。

  1. StandardScalerを使用したモデルの結果は一貫していますが、StandardScalerを使用し、_set_intercept=False_を設定したモデルのパフォーマンスが低い理由がわかりません。

線形回帰モジュール のドキュメントから:

fit_intercept:ブール値、オプション、デフォルトTrue

このモデルの切片を計算するかどうか。 Falseに設定すると、いいえ
切片が計算に使用されます(たとえば、データはすでに存在すると予想されます)
centered)。

StandardScalerはデータを中央揃えにするため、_fit_intercept=False_で使用すると一貫性のない結果が生成される理由がわかりません。

16
  1. 最初の2つのモデル間で係数に差がない理由は、正規化された入力データから係数を計算した後、Sklearnが背後で係数を非正規化するためです。 参考

この非正規化が行われたのは、テストデータの場合、co-effを直接適用できるためです。テストデータを正規化せずに予測を取得します。

したがって、normalize=Trueの設定は、係数に影響を与えますが、いずれにしても最適線には影響しません。

  1. Normalizerは、各サンプルに関する正規化を行います(行単位)。参照コード here が表示されます。

ドキュメントから:

サンプルを個別に正規化して単位ノルムにします。

一方、normalize=Trueは、各列/機能に関する正規化を行います。 参考

データの異なる次元での正規化の影響を理解するための例。 2つの次元x1とx2を取り、yをターゲット変数とします。図では、ターゲット変数の値が色分けされています。

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

図1、2、4のデータの最適線は同じになることがわかります。列/機能の正規化またはデータの標準化が原因でR2_-scoreが変更されないことを示します。それだけで、最終的には異なるco-effになります。値。

注:fig3の最適な線は異なります。

  1. Fit_intercept = Falseを設定すると、バイアス項が予測から差し引かれます。切片がゼロに設定されていることを意味します。そうでなければ、ターゲット変数の平均でした。

予測 インターセプトがゼロの場合、ターゲット変数がスケーリングされていない(平均= 0)場合、問題が発生すると予想されます。すべての行で22.532の違いを確認できます。これは、出力の影響を示しています。

8
Venkatachalam

Q1の回答

最初の2つのモデルの意味は_reg1_および_reg2_であると想定しています。そうでない場合はお知らせください。

データを正規化するかどうかに関係なく、線形回帰は同じ予測力を持ちます。したがって、_normalize=True_を使用しても予測には影響しません。これを理解する1つの方法は、正規化(列単位)が各列(_(x-a)/b_)の線形演算であり、線形回帰でのデータの線形変換は係数推定に影響を与えないことを確認することです。値。このステートメントはLasso/Ridge/ElasticNetには当てはまらないことに注意してください。

では、なぜ係数に違いがないのでしょうか?まあ、_normalize=True_は、ユーザーが通常望むのは正規化された特徴ではなく、元の特徴の係数であることも考慮に入れます。そのため、係数を調整します。これが理にかなっていることを確認する1つの方法は、より簡単な例を使用することです。

_# 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)
_

これは、両方の方法が[x1、x2]とyの間の実際の信号、つまりそれぞれ2と1を正しくキャプチャしていることを確認します。

Q2の回答

Normalizerはあなたが期待するものではありません。各行を行ごとに正規化します。そのため、結果は劇的に変化し、特定のケース(TF-IDFなど)を除いて、回避したい機能とターゲット間の関係が破壊される可能性があります。

方法を確認するには、上記の例を想定してください。ただし、yに関連しない別の機能_x3_を検討してください。 Normalizerを使用すると、_x1_が_x3_の値によって変更され、yとの関係の強度が低下します。

モデル(1,2)と(4,5)の間の係数の不一致

係数間の不一致は、フィッティングの前に標準化すると、係数は標準化された特徴に関連することになります。これは、回答の最初の部分で参照したのと同じ係数です。これらは、_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)
_

これは、数学的にz = (x - mu)/sigmaを設定すると、モデルreg4が_y = a1*z1 + a2*z2 + a0_を解決するためです。単純な代数:y = a1*[(x1 - mu1)/sigma1] + a2*[(x2 - mu2)/sigma2] + a0を使用してyとxの関係を回復できます。これはy = (a1/sigma1)*x1 + (a2/sigma2)*x2 + (a0 - a1*mu1/sigma1 - a2*mu2/sigma2)に簡略化できます。

_reg4.coef_ / scaler.scale__は、上記の表記法で_[a1/sigma1, a2/sigma2]_を表します。これは、係数が同じであることを保証するために_normalize=True_が行うこととまったく同じです。

モデル5のスコアの不一致。

標準化された特徴は平均がゼロですが、ターゲット変数は必ずしもそうではありません。したがって、切片を近似しない​​と、モデルはターゲットの平均を無視します。私が使用している例では、_y = 3 + ..._の「3」は適合していません。これにより、モデルの予測力が自然に低下します。 :)

4
Jorge Leitao