다운로드
작성자: admin 작성일시: 2017-10-17 22:34:25 조회수: 2589 다운로드: 148
카테고리: 머신 러닝 태그목록:

스케일링과 변수 변환

회귀분석에 사용되는 데이터는 그 자체로 사용하기 보다는 스케일링이나 함수 변환 등의 전처리 과정을 거치는 경우가 많다. 전처리 과정은 공분산 행렬의 조건을 향상시키거나 데이터 간의 관계를 선형 모형에 맞게 바꾸기 위해 사용된다.

조건수

조건수(conditional number)는 공분산 행렬 $X^TX$의 가장 큰 고유치와 가장 작은 고유치의 비율을 뜻한다.

$$ \text{condition number} = \dfrac{\lambda_{\text{max}}}{\lambda_{\text{min}}} $$

조건수가 크면 역행렬을 계산할 때 오차가 미치는 영향이 커진다.

예를 들어 다음 연립방정식의 해를 구해보자.

$$ Ax = b $$

이 때 행렬 $A$가 단위 행렬이면 조건수는 1이다.

In [1]:
A = np.eye(4)

이 행렬 $A$와 곱해져서 상수 벡터 $b$가 되는 벡터 $x$를 역행렬 $A^{-1}$을 사용하여 계산할 수 있다. 이 예에서는 상수 벡터 $b$가 1-벡터이다.

In [2]:
b = np.ones(4)
sp.linalg.solve(A, b)
Out:
array([ 1.,  1.,  1.,  1.])

만약 상수 벡터에 약간의 오차가 있었다면 연립방정식의 해에도 동일한 수준의 오차가 발행한다.

In [3]:
sp.linalg.solve(A + 0.0001 * np.eye(4), b)
Out:
array([ 0.99990001,  0.99990001,  0.99990001,  0.99990001])

하지만 연립방정식을 이루는 행렬의 조건수가 커지면 상수항 오차가 작은 경우에도 해의 오차가 커지게 된다. 예를 들어 다음과 같은 행렬을 생각하자.

In [4]:
A = sp.linalg.hilbert(4)
A
Out:
array([[ 1.        ,  0.5       ,  0.33333333,  0.25      ],
       [ 0.5       ,  0.33333333,  0.25      ,  0.2       ],
       [ 0.33333333,  0.25      ,  0.2       ,  0.16666667],
       [ 0.25      ,  0.2       ,  0.16666667,  0.14285714]])
In [5]:
np.linalg.cond(A)
Out:
15513.73873892924

이 행렬과 곱해져서 상수 벡터가 되는 벡터를 역행렬을 사용하여 찾으면 다음과 같다.

In [6]:
sp.linalg.solve(A, b)
Out:
array([  -4.,   60., -180.,  140.])

조건수가 크면 약간의 오차만 있어도 해가 전혀 다른 값을 가진다. 따라서 조건수가 크면 회귀분석을 사용한 예측값도 오차가 커지게 된다.

In [7]:
sp.linalg.solve(A + 0.0001 * np.eye(4), b)
Out:
array([ -0.58897672,  21.1225671 , -85.75912499,  78.45650825])

회귀분석에서 조건수가 커지는 경우는 크게 두 가지가 있다.

  1. 변수들의 단위 차이로 인해 숫자의 스케일이 크게 달라지는 경우. 이 경우에는 스케일링(scaling)으로 해결한다.
  2. 다중 공선성 즉, 상관관계가 큰 독립 변수들이 있는 경우, 이 경우에는 변수 선택이나 PCA를 사용한 차원 축소 등으로 해결한다.

스케일링

보스턴 집값 데이터의 경우 회귀분석을 하면 조건수가 15,000 정도로 크게 나오는데 이는 각 독립 변수들이 0.1 단위부터 수백 단위까지 제각각의 크기를 가지고 있기 때문이다.

In [8]:
from sklearn.datasets import load_boston
boston = load_boston()
dfX = pd.DataFrame(boston.data, columns=boston.feature_names)
dfy = pd.DataFrame(boston.target, columns=["MEDV"])
df = pd.concat([dfX, dfy], axis=1)

model1 = sm.OLS.from_formula("MEDV ~ "
                             "CRIM + ZN + INDUS + NOX + RM + AGE + "
                             "DIS + RAD + TAX + PTRATIO + B + LSTAT + CHAS", 
                             data=df)
result1 = model1.fit()
print(result1.summary())
                            OLS Regression Results                            
==============================================================================
Dep. Variable:                   MEDV   R-squared:                       0.741
Model:                            OLS   Adj. R-squared:                  0.734
Method:                 Least Squares   F-statistic:                     108.1
Date:                Sat, 30 Jun 2018   Prob (F-statistic):          6.95e-135
Time:                        17:10:04   Log-Likelihood:                -1498.8
No. Observations:                 506   AIC:                             3026.
Df Residuals:                     492   BIC:                             3085.
Df Model:                          13                                         
Covariance Type:            nonrobust                                         
==============================================================================
                 coef    std err          t      P>|t|      [0.025      0.975]
------------------------------------------------------------------------------
Intercept     36.4911      5.104      7.149      0.000      26.462      46.520
CRIM          -0.1072      0.033     -3.276      0.001      -0.171      -0.043
ZN             0.0464      0.014      3.380      0.001       0.019       0.073
INDUS          0.0209      0.061      0.339      0.735      -0.100       0.142
NOX          -17.7958      3.821     -4.658      0.000     -25.302     -10.289
RM             3.8048      0.418      9.102      0.000       2.983       4.626
AGE            0.0008      0.013      0.057      0.955      -0.025       0.027
DIS           -1.4758      0.199     -7.398      0.000      -1.868      -1.084
RAD            0.3057      0.066      4.608      0.000       0.175       0.436
TAX           -0.0123      0.004     -3.278      0.001      -0.020      -0.005
PTRATIO       -0.9535      0.131     -7.287      0.000      -1.211      -0.696
B              0.0094      0.003      3.500      0.001       0.004       0.015
LSTAT         -0.5255      0.051    -10.366      0.000      -0.625      -0.426
CHAS           2.6886      0.862      3.120      0.002       0.996       4.381
==============================================================================
Omnibus:                      178.029   Durbin-Watson:                   1.078
Prob(Omnibus):                  0.000   Jarque-Bera (JB):              782.015
Skew:                           1.521   Prob(JB):                    1.54e-170
Kurtosis:                       8.276   Cond. No.                     1.51e+04
==============================================================================

Warnings:
[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.
[2] The condition number is large, 1.51e+04. This might indicate that there are
strong multicollinearity or other numerical problems.
In [9]:
result1.predict(df[:1])
Out:
0    30.008213
dtype: float64

StatsModels에서는 모형지정 문자열에서 scale 명령을 사용하여 스케일링을 할 수 있다. 이 방식으로 스케일을 하면 스케일링에 사용된 평균과 표준편차를 저장하였다가 나중에 predict 명령을 사용할 때도 같은 스케일을 사용하기 때문에 편리하다. Scikit-Learn의 파이프라인과 같은 역할을 한다.

In [10]:
model2 = sm.OLS.from_formula("MEDV ~ "
                             "scale(CRIM) + scale(ZN) + scale(INDUS) + scale(NOX) + scale(RM) + scale(AGE) + "
                             "scale(DIS) + scale(RAD) + scale(TAX) + scale(PTRATIO) + scale(B) + scale(LSTAT) + CHAS", 
                             data=df)
result2 = model2.fit()
print(result2.summary())
                            OLS Regression Results                            
==============================================================================
Dep. Variable:                   MEDV   R-squared:                       0.741
Model:                            OLS   Adj. R-squared:                  0.734
Method:                 Least Squares   F-statistic:                     108.1
Date:                Sat, 30 Jun 2018   Prob (F-statistic):          6.95e-135
Time:                        17:10:05   Log-Likelihood:                -1498.8
No. Observations:                 506   AIC:                             3026.
Df Residuals:                     492   BIC:                             3085.
Df Model:                          13                                         
Covariance Type:            nonrobust                                         
==================================================================================
                     coef    std err          t      P>|t|      [0.025      0.975]
----------------------------------------------------------------------------------
Intercept         22.3468      0.219    101.936      0.000      21.916      22.778
scale(CRIM)       -0.9204      0.281     -3.276      0.001      -1.472      -0.368
scale(ZN)          1.0810      0.320      3.380      0.001       0.453       1.709
scale(INDUS)       0.1430      0.421      0.339      0.735      -0.685       0.971
scale(NOX)        -2.0601      0.442     -4.658      0.000      -2.929      -1.191
scale(RM)          2.6706      0.293      9.102      0.000       2.094       3.247
scale(AGE)         0.0211      0.371      0.057      0.955      -0.709       0.751
scale(DIS)        -3.1044      0.420     -7.398      0.000      -3.929      -2.280
scale(RAD)         2.6588      0.577      4.608      0.000       1.525       3.792
scale(TAX)        -2.0759      0.633     -3.278      0.001      -3.320      -0.832
scale(PTRATIO)    -2.0622      0.283     -7.287      0.000      -2.618      -1.506
scale(B)           0.8566      0.245      3.500      0.001       0.376       1.338
scale(LSTAT)      -3.7487      0.362    -10.366      0.000      -4.459      -3.038
CHAS               2.6886      0.862      3.120      0.002       0.996       4.381
==============================================================================
Omnibus:                      178.029   Durbin-Watson:                   1.078
Prob(Omnibus):                  0.000   Jarque-Bera (JB):              782.015
Skew:                           1.521   Prob(JB):                    1.54e-170
Kurtosis:                       8.276   Cond. No.                         10.6
==============================================================================

Warnings:
[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.
In [11]:
result2.predict(df[:1])
Out:
0    30.008213
dtype: float64

변수 변환

다음과 같은 경우에는 로그 함수 혹은 제곱근 함수 등을 사용하여 변환된 변수를 사용하면 회귀 성능이 향상될 수도 있다.

  • 독립 변수나 종속 변수가 심하게 한쪽으로 치우친 분포를 보이는 경우
  • 독립 변수와 종속 변수간의 관계가 곱셈 혹은 나눗셉으로 연결된 경우
  • 종속 변수와 예측치가 비선형 관계를 보이는 경우

원래 선형회귀모형의 가정에는 독립 변수나 종속 변수가 반드시 대칭 분포를 보여야 한다는 가정은 없지만 정규 분포에 가까운 분포를 보일 수록 선형회귀모형의 성능이 좋아지는 경우가 많다.

보스턴 집값 데이터에서는 가격, DIS, PTRATIO, LSTAT 등이 비대칭 분포를 이루거나 가격과 반비례 관계를 가지므로 이 변수들을 로그로 변환한 후에 선형회귀분석을 하면 성능을 향상시킬 수 있다.

In [12]:
model3 = sm.OLS.from_formula("np.log(MEDV) ~ "
                             "scale(CRIM) + scale(I(CRIM ** 2)) + "
                             "scale(ZN) + scale(I(ZN ** 2)) + "
                             "scale(INDUS) + "
                             "scale(NOX) + scale(RM) + scale(AGE) + "
                             "scale(np.log(DIS)) + scale(RAD) + scale(TAX) + "
                             "scale(np.log(PTRATIO)) + scale(B) + scale(np.log(LSTAT)) + CHAS", 
                             data=df)
result3 = model3.fit()
print(result3.summary())
                            OLS Regression Results                            
==============================================================================
Dep. Variable:           np.log(MEDV)   R-squared:                       0.817
Model:                            OLS   Adj. R-squared:                  0.811
Method:                 Least Squares   F-statistic:                     145.4
Date:                Sat, 30 Jun 2018   Prob (F-statistic):          1.97e-169
Time:                        17:10:05   Log-Likelihood:                 164.24
No. Observations:                 506   AIC:                            -296.5
Df Residuals:                     490   BIC:                            -228.9
Df Model:                          15                                         
Covariance Type:            nonrobust                                         
==========================================================================================
                             coef    std err          t      P>|t|      [0.025      0.975]
------------------------------------------------------------------------------------------
Intercept                  3.0290      0.008    368.850      0.000       3.013       3.045
scale(CRIM)               -0.2548      0.030     -8.409      0.000      -0.314      -0.195
scale(I(CRIM ** 2))        0.1225      0.024      5.143      0.000       0.076       0.169
scale(ZN)                 -0.0388      0.031     -1.247      0.213      -0.100       0.022
scale(I(ZN ** 2))          0.0359      0.028      1.296      0.196      -0.019       0.090
scale(INDUS)               0.0009      0.016      0.055      0.957      -0.031       0.032
scale(NOX)                -0.1134      0.017     -6.518      0.000      -0.148      -0.079
scale(RM)                  0.0372      0.012      3.204      0.001       0.014       0.060
scale(AGE)                 0.0092      0.014      0.637      0.524      -0.019       0.038
scale(np.log(DIS))        -0.1260      0.018     -6.848      0.000      -0.162      -0.090
scale(RAD)                 0.1812      0.024      7.560      0.000       0.134       0.228
scale(TAX)                -0.1107      0.024     -4.660      0.000      -0.157      -0.064
scale(np.log(PTRATIO))    -0.0830      0.011     -7.607      0.000      -0.104      -0.062
scale(B)                   0.0296      0.009      3.195      0.001       0.011       0.048
scale(np.log(LSTAT))      -0.2243      0.014    -15.467      0.000      -0.253      -0.196
CHAS                       0.0797      0.032      2.463      0.014       0.016       0.143
==============================================================================
Omnibus:                       38.120   Durbin-Watson:                   1.185
Prob(Omnibus):                  0.000   Jarque-Bera (JB):              164.197
Skew:                           0.029   Prob(JB):                     2.21e-36
Kurtosis:                       5.790   Cond. No.                         13.5
==============================================================================

Warnings:
[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.
In [13]:
# 아웃라이어 제거
influence3 = result3.get_influence()
cooks_d2, pvals = influence3.cooks_distance
fox_cr = 4 / (len(df) - 2)
idx_outlier = np.where(cooks_d2 > fox_cr)[0]
# MEDV = 50 제거
idx_outlier = np.hstack([idx_outlier, np.where(boston.target == 50)[0]])
idx_outlier
Out:
array([  7,  54, 148, 152, 160, 214, 253, 267, 364, 365, 367, 368, 369,
       371, 372, 374, 380, 385, 397, 398, 399, 400, 401, 405, 409, 410,
       412, 413, 414, 415, 416, 418, 419, 426, 445, 489, 490, 492, 505,
       161, 162, 163, 166, 186, 195, 204, 225, 257, 267, 283, 368, 369,
       370, 371, 372])
In [14]:
pred = result3.predict(df)
ax = plt.subplot()
plt.scatter(np.log(dfy), pred)
plt.scatter(np.log(dfy.MEDV[idx_outlier]), pred[idx_outlier], s=300, c="r", alpha=0.5)
plt.show()
In [15]:
idx = list(set(range(len(df))).difference(idx_outlier))
df4 = df.iloc[idx, :].reset_index(drop=True)
In [16]:
model4 = sm.OLS.from_formula("np.log(MEDV) ~ "
                             "scale(CRIM) + scale(I(CRIM ** 2)) + "
                             "scale(ZN) + scale(I(ZN ** 2)) + "
                             "scale(INDUS) + "
                             "scale(NOX) + scale(RM) + scale(AGE) + "
                             "scale(np.log(DIS)) + scale(RAD) + scale(TAX) + "
                             "scale(np.log(PTRATIO)) + scale(B) + scale(np.log(LSTAT)) + CHAS", 
                             data=df4)
result4 = model4.fit()
print(result4.summary())
                            OLS Regression Results                            
==============================================================================
Dep. Variable:           np.log(MEDV)   R-squared:                       0.872
Model:                            OLS   Adj. R-squared:                  0.868
Method:                 Least Squares   F-statistic:                     199.9
Date:                Sat, 30 Jun 2018   Prob (F-statistic):          1.56e-185
Time:                        17:10:06   Log-Likelihood:                 317.45
No. Observations:                 456   AIC:                            -602.9
Df Residuals:                     440   BIC:                            -536.9
Df Model:                          15                                         
Covariance Type:            nonrobust                                         
==========================================================================================
                             coef    std err          t      P>|t|      [0.025      0.975]
------------------------------------------------------------------------------------------
Intercept                  3.0383      0.006    512.183      0.000       3.027       3.050
scale(CRIM)               -0.2115      0.027     -7.976      0.000      -0.264      -0.159
scale(I(CRIM ** 2))        0.0796      0.018      4.331      0.000       0.043       0.116
scale(ZN)                 -0.0464      0.022     -2.110      0.035      -0.090      -0.003
scale(I(ZN ** 2))          0.0435      0.020      2.206      0.028       0.005       0.082
scale(INDUS)               0.0036      0.011      0.323      0.747      -0.018       0.026
scale(NOX)                -0.0648      0.013     -5.001      0.000      -0.090      -0.039
scale(RM)                  0.0852      0.009      9.195      0.000       0.067       0.103
scale(AGE)                -0.0271      0.011     -2.438      0.015      -0.049      -0.005
scale(np.log(DIS))        -0.0968      0.013     -7.368      0.000      -0.123      -0.071
scale(RAD)                 0.1564      0.019      8.106      0.000       0.118       0.194
scale(TAX)                -0.0894      0.017     -5.153      0.000      -0.124      -0.055
scale(np.log(PTRATIO))    -0.0681      0.008     -8.872      0.000      -0.083      -0.053
scale(B)                   0.0446      0.007      6.699      0.000       0.031       0.058
scale(np.log(LSTAT))      -0.1378      0.012    -11.074      0.000      -0.162      -0.113
CHAS                       0.0659      0.026      2.580      0.010       0.016       0.116
==============================================================================
Omnibus:                       28.653   Durbin-Watson:                   1.309
Prob(Omnibus):                  0.000   Jarque-Bera (JB):               43.266
Skew:                           0.465   Prob(JB):                     4.03e-10
Kurtosis:                       4.188   Cond. No.                         15.7
==============================================================================

Warnings:
[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.

질문/덧글

질문 있습니다. krox*** 2018년 8월 13일 11:08 오전

항상 감사합니다. 잘 보고 배우고 있습니다.
질문이 있습니다.

model3 = sm.OLS.from_formula("np.log(MEDV) ~ "
"scale(CRIM) + scale(I(CRIM ** 2)) + "
"scale(ZN) + scale(I(ZN ** 2)) + "
"scale(INDUS) + "
"scale(NOX) + scale(RM) + scale(AGE) + "
"scale(np.log(DIS)) + scale(RAD) + scale(TAX) + "
"scale(np.log(PTRATIO)) + scale(B) + scale(np.log(LSTAT)) + CHAS",
data=df)
여기에서

1. I(CRIM ** 2), I(ZN**2) 의 I()는 어떤 함수인가요?
2. CRIM, ZN 을 제곱하는 이유가 있나요?? 제곱을 해야하는 경우와 log를 씌워줘야 하는 경우의 차이를 알고 싶습니다.
3. CRIM이나 ZN의 경우는 "scale(CRIM) + scale(I(CRIM ** 2)) " 처럼 두 개의 feature를 사용하는데, 이렇게 하는 이유가 궁금합니다.
3. 지수화 또는 로그화를 안해주는 feature들은 어떤 기준으로 scale만 해주는 건가요?