귀퉁이 서재

DATA - 20. 다중공선성(Multicollinearity)과 VIF(Variance Inflation Factors) 본문

데이터 분석

DATA - 20. 다중공선성(Multicollinearity)과 VIF(Variance Inflation Factors)

데이터 파수꾼 Baek Kyun Shin 2019. 5. 1. 13:03

이번 시간에는 다중공선성과 VIF에 대해 알아보겠습니다.

독립 변수 X는 종속 변수 Y 하고만 상관 관계가 있어야 하며, 독립 변수끼리 상관 관계가 있어서는 안 됩니다. 독립 변수간 상관 관계를 보이는 것을 다중공선성(Multicollinearity)이라고 합니다. 다중공선성이 있으면 부정확한 회귀 결과가 도출됩니다. (X와 Y의 상관 관계가 반대로 나온다던가 검정 결과가 다르게 나온다던가 말이죠.)

회귀 모델에 다중공선성이 있는지 파악하는 방법은 두 가지가 있습니다.

1. 산점도 그래프 (Scatter plot Matrix)

2. VIF (Variance Inflation Factors, 분산팽창요인)

산점도 그래프를 통해 독립 변수끼리 상관 관계가 있는지 파악하는 방법에 대해서는 아래 Python 코드 실습에서 살펴보겠습니다.

VIF는 다중 회귀 모델에서 독립 변수간 상관 관계가 있는지 측정하는 척도입니다. (Reference1) 수식은 아래와 같습니다.

위 식에서 R² (결정계수)에 대해 궁금하신 분들은 이전 챕터 (DATA - 17. 최소자승법(OLS)을 활용한 단순 선형 회귀(Simple Linear Regression))(Simple Linear Regression)) 아래 쪽을 참고해주시기 바랍니다.

VIF가 10이 넘으면 다중공선성 있다고 판단하며 5가 넘으면 주의할 필요가 있는 것으로 봅니다. 독립 변수 a와 b가 서로 상관 관계가 있다고 했을 때 두 변수 모두 VIF가 높습니다. 어느 하나만 VIF가 높은 경우는 없습니다. 박수도 오른손과 왼손이 있어야 칠 수 있듯이 서로 연관 있는 변수끼리 VIF가 높습니다. 아래 예제를 통해 자세히 알아보겠습니다.

다중공선성과 VIF 실습

파이썬을 활용하여 다중공선성과 VIF에 대해 실습해보겠습니다. seaborn, dmatirces, variance_inflation_factor 라이브러리가 추가되었습니다.

import pandas as pd
import numpy as np
import seaborn as sns
from patsy import dmatrices
import statsmodels.api as sm;
from statsmodels.stats.outliers_influence import variance_inflation_factor
%matplotlib inline

df = pd.read_csv('house_prices.csv')
df.head()

데이터는 이전 챕터에서 실습했던 것과 동일합니다. 지역(neighborhood), 집 크기(area), 침실 개수(bedrooms), 화장실 개수(bathrooms), 집 스타일(style)에 따라 집 값(price)가 어떻게 변하는지 나타내는 데이터입니다. 역시 제 깃헙에서 받아가실 수 있습니다.

다중공선성을 파악할 수 있는 방법 중 하나는 산점도(Scatter plot)를 그리는 것입니다. 침실 개수(bedrooms), 화장실 개수(bathrooms), 집 크기(area) 간 상관 관계가 있는지 Scatter plot으로 알아보겠습니다. sns.pairplot을 통해 독립 변수간 1대 1 상관 관계를 눈으로 파악할 수 있습니다. (Reference2)

sns.pairplot(df[['bedrooms', 'bathrooms', 'area']]);

bathrooms, bedrooms, area간 1대 1 상관 관계입니다. 2행 1열은 bathrooms와 bedrooms간의 그래프입니다. 강한 양의 상관 관계가 있는 것을 볼 수 있습니다. bathrooms가 많으면 bedrooms도 많다는 뜻입니다. 1행 2열은 2행 1열 그래프에서 x와 y의 축만 바꾼 것입니다. 3행 1열에서도 bedrooms와 area가 양의 상관 관계가 있는 것을 볼 수 있습니다. bathrooms와 area도 마찬가지도 양의 상관 관계입니다. 가장 강한 상관 관계를 보이는 것은 화장실 개수(bathrooms)와 침실 개수(bedrooms)입니다. 1행 1열, 2행 2열, 3행 3열은 각 bedrooms, bathrooms, area의 histogram입니다.

이렇듯 간단한 산점도만으로도 독립변수간 상관관계 (다중공선성)을 대략 파악할 수 있습니다.

독립 변수를 drop하지 않고 OLS 회귀 결과를 구해보겠습니다.

df['intercept'] = 1
lm = sm.OLS(df['price'], df[['intercept', 'bedrooms', 'bathrooms', 'area']])
results = lm.fit()
results.summary()

bedrooms의 coef가 -2925.8063입니다. 즉, 침실 개수(bedrooms)와 집 값(price)이 음의 상관 관계를 갖는다는 뜻입니다. bedrooms가 많아질수록 price가 떨어진다는 말입니다. 이전 챕터에서 했던 것 처럼 bedrooms와 price끼리만 OLS 회귀를 해보면 양의 상관 관계를 보입니다. 실제는 양의 상관 관계를 보이는데 여기서는 음의 상관 관계를 보이는 것으로 결과가 잘못 나왔습니다. 이는 위에서 설명한 것처럼 다중공선성으로 인해 회귀 결과가 잘못나온 것입니다.

다중공선성이 있다면 이처럼 회귀 결과를 왜곡합니다. 따라서 회귀를 하기 전에 다중공선성은 반드시 제거해야 합니다.

Scatter plot을 통해 다중공선성을 파악해봤으니 이제 VIF를 통해 다중공선성을 파악해보겠습니다. (Reference3)

y, X = dmatrices('price ~ area + bedrooms + bathrooms', df, return_type = 'dataframe')

vif = pd.DataFrame()
vif["VIF Factor"] = [variance_inflation_factor(X.values, i) for i in range(X.shape[1])]
vif["features"] = X.columns 
vif

여기서 X는 6027개의 row를 갖고 아래와 같은 형태입니다. intercept는 기본적으로 들어갑니다.

y도 마찬가지로 6027개의 row를 갖고 아래와 같은 형태입니다.

dmatrices가 원하는 변수만을 matrix 형태로 만들어준 것입니다.

vif["VIF Factor"] = [variance_inflation_factor(X.values, i) for i in range(X.shape[1])]

이 코드는 행렬 X의 column(여기서는 area, bedrooms, bathrooms)를 순회하면서 해당 column(즉, 변수)의 VIF 값을 계산해줍니다.

최종적으로 아래 결과가 나왔습니다.

bedrooms와 bathrooms가 10 이상이므로 서로 강한 상관 관계를 보입니다. bedrooms와 bathrooms 중 하나를 drop 하고 회귀를 하면 다중공선성 문제가 해결됩니다. 두 강아지가 서로 싸울 때 한 강아지를 멀찌감치 떼어 놓으면 둘이 싸우지 못합니다. 마찬가지로 다중공선성이 있는 두 변수 중 하나를 제거하면 해당 회귀 모델에서 다중공선성이 없어집니다.

추가로, 해당 변수가 다른 변수와 전혀 상관 관계가 없다면 VIF = 1이고, 해당 변수의 R-squared 값은 0입니다.

VIF 10 이상 변수 drop을 통한 다중공선성 해결 실습

lm = sm.OLS(df['price'], df[['intercept', 'bedrooms', 'area']])
results = lm.fit()
results.summary()

bathrooms 변수를 drop 하고 다시 OLS 회귀를 했습니다. bedrooms의 coef가 1626.8306으로 양수가 되었습니다. 아까는 음수였는데 이제는 양수가 되었습니다. bedrooms의 coefficient는 계수를 의미합니다. 침실 개수(bedsrooms)가 늘어남에 따라 집 값(price)도 높아진다는 뜻입니다. 다중공선성 문제를 해결하니 올바른 결과가 도출되었습니다. 이제 VIF도 다시 구해보겠습니다.

y, X = dmatrices('price ~ area + bedrooms', df, return_type = 'dataframe')

vif = pd.DataFrame()
vif["VIF Factor"] = [variance_inflation_factor(X.values, i) for i in range(X.shape[1])]
vif["features"] = X.columns 
vif

모든 VIF가 10미만으로 다중공선성 문제가 해결되었습니다.

추가적으로, R-squared값은 bedrooms를 drop하지 않은 모델과 drop한 모델 모두 0.678입니다. 바로 앞 챕터에서 R-squared의 의미를 알아봤습니다. 결정 계수를 뜻하는 단어로 회귀모형 유용성의 척도입니다. R-squared가 drop 전과 후가 동일하게 0.678이라는 말은 bedrooms와 bathrooms 둘 모두가 필요하지 않다는 뜻입니다. 둘 중 하나만 있어도 된다는 겁니다. bedrooms를 drop했음에도 모델의 예측력에 영향을 끼치지 않았습니다.

References

Reference1 : Variance Inflation Factor (VIF) Explained

Reference2 : seaborn.pairplot

Reference3 : statsmodels.stats.outliers_influence.variance_inflation_factor

6 Comments
  • 프로필사진 행인 2019.06.11 13:00 유용한 글 감사합니다. 큰도움되네
  • 프로필사진 데이터 파수꾼 Baek Kyun Shin 2019.06.11 13:04 신고 읽어주셔서 감사합니다 ~^^
  • 프로필사진 나그네 2020.08.26 14:12 유용한 글 잘 읽었습니다 :)
    근데 마지막 summary를 보면 bedroom의 p>|t| 의 값이 0.05를 넘어서 유의미하지 않다고 나오는데 이 부분은 어떻게 해결해야하나요?
  • 프로필사진 데이터 파수꾼 Baek Kyun Shin 2020.08.27 19:13 신고 읽어주셔서 감사드립니다~
    포인트를 잘 짚어주셨네요 :) 이 글은 다중공선성만 염두하고 작성했습니다. 엄밀히 말한다면, 말씀하신대로 p-value가 0.05를 넘지 않으므로 통계적으로 유의하지 않다고 해석하는 것이 맞습니다.
    다만 본 글은 다중공선성을 어떻게 없애는 지에 포커스를 둔 것입니다. 위 결과대로만 판단했을 때는 bedrooms과 price가 양의 상관 관계를 보인다고 말할 수 없겠군요. p값에 매몰되는 것도 좋은 방법은 아니지만 이론적으로만 봤을 때는 p가 0.05보다 크므로 통계적으로 유의하지 않은 게 맞습니다 ㅎㅎ
    미처 생각치 못한걸 잘 짚어주셔서 감사합니다!
  • 프로필사진 궁금합니다 2020.09.08 16:41 안녕하세요. 머신러닝으로 회귀 모델을 구할때 다중공선성이 영향을 주나요?

    https://qastack.kr/stats/168622/why-is-multicollinearity-not-checked-in-modern-statistics-machine-learning

    해당 링크 보면 다중공선성 있어도 괜찮다는 것 같아서요.
  • 프로필사진 데이터 파수꾼 Baek Kyun Shin 2020.09.08 18:39 신고 안녕하세요! 질문 감사드립니다. 링크 달아주신 포스팅의 원문을 저도 찾아봤습니다.

    그 원문에는 이렇게 나와있습니다.

    Finally, consider the actual impact of multicollinearity. It doesn't change the predictive power of the model (at least, on the training data) but it does screw with our coefficient estimates. In most ML applications, we don't care about coefficients themselves, just the loss of our model predictions, so in that sense, checking VIF doesn't actually answer a consequential question. (But if a slight change in the data causes a huge fluctuation in coefficients [a classic symptom of multicollinearity], it may also change predictions, in which case we do care -- but all of this [we hope!] is characterized when we perform cross-validation, which is a part of the modeling process anyway.)

    즉, 회귀모델은 계수(coefficients)를 예측하는 것이 목표이지만, 머신러닝 모델은 계수를 예측하는 것이 목표가 아닙니다. 머신러닝 모델은 수많은 피처(features)가 있을 때 어떤 결과(target)가 있는지 예측하는 것이 목표입니다. 각 피처의 계수가 무엇인지는 머신러닝 모델에서 중요하지 않습니다. 머신러닝 모델은 어떤 피처를 전달하면 어떤 결과를 주는가에 관심이 있습니다.

    따라서 말씀하신대로 최근 머신러닝에서는 다중공선성을 별로 신경 쓰지 않는 것이고, 전통적인 회귀 모델에서는 다중공선성을 신경 쓰는 것 같습니다.

    이 포스팅에서 저는 머신러닝 모델을 쓰지 않았고, sm이라는 통계 모듈을 사용하여 단순 회귀를 했습니다.

    제가 맞게 설명했는지 모르겠네요 ㅎㅎ 질문해주셔서 덕분에 저도 배웠습니다. 감사합니다.
댓글쓰기 폼