본문 바로가기

Machine Learning_모델설계_Python

선형회귀_5 다중선형회귀실습

안녕하세요 배도리입니다. 다중선형회귀를 직접 코드를 통해 실습해보겠습니다. 데이터는 자주 쓰이는 보스턴집값을 이용하겠습니다.

 

http://lib.stat.cmu.edu/datasets/boston 데이터 입니다. 한번 스윽 보세용

 

이제 시작하겠습니다.

import pandas as pd
import numpy as np

raw_df = pd.read_csv(data_url, sep="\s+", skiprows=22, header=None)
#\s는 공백을 의미하고, +는 하나 이상의 연속된 공백을 의미 공백으로 구분된 값을 올바르게 읽음
#데이터 파일의 처음 22행을 건너뛰고 읽기 시작하도록 지시합니다. 이 데이터셋의 상단에는 설명 텍스트와 같은 메타데이터가 포함되어 있기 때문입니다.
# header=None: 이 인자는 데이터 파일에 열 이름이 없음을 나타냅니다. 기본적으로 read_csv 함수는 첫 번째 행을 열 이름으로 사용하려고 시도합니다.
# header=None 인자는 이 동작을 중지하고, 대신 열 이름으로 숫자 인덱스를 사용
raw_df
 

 

 

데이터를 보니까 짝수행은 현재 3열까지만 자료가 나오고 이후 결측값으로 나오고 있네요. 일단 짝수행의 2열까지 옆으로 붙여서 만들고 이후 독립변수와 종속변수(본인소유주택가격)를 나누겠습니다.  좀더 자세히 설명하면 0,2,4,6,.. 짝수행을 추출 그리고 1,3,4,,,홀수행 에서  2열까지(칼럼 숫자 0,1) 추출. 이후 hstack으로 가로방향으로 연결. 그러면 독립변수는 짝수행에서 11개 홀수행에서 2개 총 13개가 됩니다. 그리고 홀수행(1,3,5..) 3열(칼럼숫자 2)를 빼서 종속변수로 만듭니다.

 X = np.hstack([raw_df.values[::2, :], raw_df.values[1::2, :2]]) #독립변수 13개
#raw_df.values[::2, :]: 원본 데이터셋의 짝수 행들(0, 2, 4, ...)을 추출합니다
#raw_df.values[1::2, :2]: 원본 데이터셋의 (1, 3, 5, ...) 행 중 처음 두 열을 추출합니다.(0열 1열)
#위에서 추출한 두 부분을 가로 방향으로 연결합니다. 결과적으로 완전한 독립 변수 데이터셋인 배열 X
 
y = raw_df.values[1::2, 2] #종속변수: 본인 소유 주택가격(중앙값)
# [1::2]를 사용하면 홀수 인덱스의 원소들을 선택 마지막 3열 인 24.00 21.60 값들을 호출
y = y.reshape(-1,1)
#종속 변수 데이터셋의 형태가 명확하게 2차원 배열이어야 합니다. (n, 1) 형태의 배열로 변환함으로써, 이러한 라이브러리와의 호환성을 보장합니다.
#-1은 배열의 길이를 자동으로 계산하도록 지시하는 특별한 값입니다. 이 경우, y 배열의 길이는 변경되지 않고, 형태만 (n,)에서 (n, 1)로 변경
feature_names = np.array(['CRIM','ZN','INDUS','CHAS','NOX','RM','AGE','DIS','RAD','TAX','PTRATIO', 'B', 'LSTAT'])
X_data = pd.DataFrame(data=X,columns=feature_names)
X_data
 

 

 

종속변수 y도 볼까요

y

 

 

matplotlib 라이브러리를 이용하여 독립 변수 데이터셋 X의 분포를 히스토그램으로 시각화하겠습니다.

import matplotlib.pyplot as plt
#독립 변수 데이터셋인 X를 1차원 배열로 변환합니다. 이렇게 하면 각 독립 변수의 값을 하나의 배열로 볼 수 있습니다
plt.hist(X.flatten(), bins=30); #독립변수 데이터분포 살펴보기
#생성한 1차원 배열의 히스토그램을 그립니다. bins=30은 히스토그램의 구간 개수를 30으로 설정합니다. 구간 개수가 높을수록 데이터의 분포가 더 세밀하게 표현됩니다.
#bins히스토그램에서 데이터를 나누는 구간의 개수를 의미

히스토그램

흠... 데이터가 0~100에 많이 분포되어 있음을 확인했습니다.

 

 

데이터셋의 모든 변수들(독립 변수와 종속 변수) 간의 상관계수 행렬을 계산하고, 이를 히트맵으로 시각화하겠습니다. 이를 통해 다중공선성이 있는 변수들을 확인할 수 있습니다. 상관계수는 -1부터 1까지의 값을 가지며, 절댓값이 1에 가까울수록 두 변수 간의 선형 관계가 강하다는 것을 의미합니다. 보통 상관계수의 절댓값이 0.7~0.8 이상일 때 두 변수 간에 높은 상관관계가 있다고 판단합니다. 

import seaborn as sns
# 이 코드는 Boston Housing 데이터셋의 모든 변수들(독립 변수와 종속 변수) 간의 상관계수 행렬을 계산하고, 이를 히트맵으로 시각화합니다.
raw_data = pd.DataFrame(data=np.hstack([X,y]), columns=np.append(feature_names, 'MEDV'))
correlation_matrix = raw_data.corr().round(2)
#모든 변수들 간의 상관계수 행렬을 계산합니다. 이 행렬은 각 변수들 간의 선형 상관 관계를 나타냅니다. round(2)를 사용하여 상관계수를 소수점 둘째 자리까지 반올림
plt.subplots(figsize=(9,9))  
# 9x9 크기의 그래프를 생성합니다.
sns.heatmap(data=correlation_matrix, annot=True, cmap='YlGnBu')
# heatmap 함수를 사용하여 상관계수 행렬을 히트맵으로 시각화합니다. annot=True로 설정하여 각 셀에 상관계수 값을 표시하고, cmap='YlGnBu'로 컬러맵을 설정
plt.show()

상관계수 히트맵

흠... 몇몇 독립변수가 상관계수가 높음을 확인했습니다.

 

 

 

이제 데이터의 분포가 고르지 않은걸 알았으니 스케일링을 해보겠습니다. 우선 스케일링과 표준화는 서로 다른 개념입니다.

 

스케일링: 데이터의 범위를 특정 범위(예: 0과 1 사이)로 변경하는 과정을 의미합니다. 스케일링은 데이터의 최소값과 최대값을 사용하여 변환합니다. 이는 MinMaxScaler와 같은 도구를 사용하여 수행됩니다.

표준화: 데이터의 평균을 0, 표준 편차를 1로 만드는 과정을 의미합니다. 표준화는 각 데이터에서 평균을 뺀 후 표준 편차로 나누는 방식으로 수행됩니다. 이는 StandardScaler와 같은 도구를 사용하여 수행됩니다.

 

둘 다 데이터의 범위를 조절하거나 분포를 변경하는 데 사용되지만, 적용하는 방식과 결과가 다릅니다. 예를 들어, 데이터가 정규 분포를 따르는 것이 중요한 경우 표준화를 사용하며, 모든 피처 값을 동일한 범위로 제한하려는 경우 스케일링을 사용할 수 있습니다. 저희는 후자이므로 스케일링을 하겠습니다.

 

이 코드는 Boston Housing 데이터셋의 독립 변수들을 전처리하기 위해 MinMaxScaler를 사용합니다. MinMaxScaler는 데이터의 값 범위를 특정 범위(여기서는 0에서 5)로 조정합니다.

from sklearn.preprocessing import MinMaxScaler #클래스는 데이터를 특정 범위로 스케일링하는 기능을 제공합니다.

minmax_scale = MinMaxScaler(feature_range=(0,5)).fit(X) #min:0, max:5
#: MinMaxScaler 객체를 생성하고, 범위를 0에서 5로 설정한 후, 이 객체를 데이터셋의 독립 변수 X에 적용합니다. 이렇게 하면 데이터의 최소값이 0, 최대값이 5가 되도록 스케일링을 학습
X_scaled = minmax_scale.transform(X) #feature scaling을 적용한 독립변수
#학습된 스케일링 정보를 사용하여 독립 변수 X의 값을 변환합니다. 이렇게 하면 데이터셋의 독립 변수들이 모두 0에서 5 사이의 범위로 조정
X_scaled
 

 

 

스케일링후에는 이제 전체 데이터셋을 학습 데이터와 테스트 데이터로 분할합니다. train_test_split 함수를 사용하여 데이터셋을 무작위로 나누고, 학습 데이터와 테스트 데이터의 비율을 지정합니다.

from sklearn.model_selection import train_test_split
#이 함수는 데이터셋을 학습 데이터와 테스트 데이터로 나누는 기능을 제공합니다.
 
X_train, X_test, y_train, y_test = train_test_split(X_scaled, y, test_size=0.30, random_state=10)
#스케일링된 독립 변수 X_scaled와 종속 변수 y를 전달하여 데이터를 학습 데이터와 테스트 데이터로 분할합니다.
#. test_size=0.30로 설정하여 전체 데이터의 30%를 테스트 데이터로 사용하고, 나머지 70%는 학습 데이터로 사용합니다.
#random_state=10을 설정하여 데이터 분할의 무작위성을 고정
 
X_train.shape, X_test.shape, y_train.shape, y_test.shape
#각 데이터셋의 차원(모양)을 출력합니다. 이 값은 분할된 데이터셋의 크기를 확인하는 데 도움이 됩니다.
 
((354, 13), (152, 13), (354, 1), (152, 1))

 

 

 

스케일도 했고 데어터셋도 나눴고 이제는 모델을 적용하여 학습시켜보겠습니다.

 

OLS (Ordinary Least Squares)

선형 회귀 분석에서 가장 널리 사용되는 방법입니다. OLS 방법은 종속 변수와 독립 변수 사이의 선형 관계를 찾기 위해 최소제곱법을 사용합니다. 이 방법은 모델의 예측값과 실제 관측값 사이의 오차의 제곱합을 최소화하는 회귀 계수를 찾습니다. 이 최소화 문제는 실제로 미분을 이용한 최적화 문제로서 해결할 수 있으며, 이때 사용하는 방법이 저희가 배운  정규 방정식 (Normal Equation)을 사용합니다.

 

이 코드는 sklearn.linear_model 모듈의 LinearRegression 클래스를 사용하여 선형 회귀 모델을 학습하고, 학습된 모델의 계수(coefficients)와 절편(intercept)을 출력합니다

.
from sklearn.linear_model import LinearRegression
#LinearRegression 클래스를 불러옵니다. 이 클래스는 선형 회귀 모델을 생성하고 학습하는 기능을 제공합니다.

ols_reg = LinearRegression()
#LinearRegression 객체를 생성하여 ols_reg 변수에 저장합니다.
ols_reg.fit(X_train, y_train) #학습데이터를 통하여 선형모델 생성
#학습 데이터셋(X_train, y_train)을 사용하여 선형 회귀 모델을 학습시킵니다.
print('Coefficients: ', ols_reg.coef_)
#학습된 선형 회귀 모델의 계수(coefficients)를 출력합니다. 계수는 각 독립 변수에 대응하는 선형 관계의 기울기를 나타냅니다.
print('Intercept: ', ols_reg.intercept_)
#: 학습된 선형 회귀 모델의 절편(intercept)을 출력합니다. 절편은 선형 회귀식에서 상수항을 나타냅니다
 
Coefficients: [[-2.73628801 1.30031854 -0.1323611 0.32440662 -1.48715326 3.49878282 0.21995085 -3.40487933 1.39215267 -1.28188776 -1.5256934 1.02718026 -3.94915675]]
Intercept: [25.4141332]

 

 

 

학습된 선형 회귀 모델의 계수를 표시하기 위해 데이터프레임을 보겠습니다.

 
ols_df = pd.DataFrame(ols_reg.coef_, columns=feature_names)
ols_df.T
#전치된 데이터프레임은 각 독립 변수와 그에 대응하는 선형 회귀 모델의 계수를 보기 쉽게 표시해 줍니다. 이

 

 

 

이제 테스트데이터로 예측을 수행하겠습니다.

y_pred_ols = ols_reg.predict(X_test
y_pred_ols[:5]
 
array(
[[31.4243217 ],
[31.96785487],
[30.93785448],
[22.34313349],
[18.83846235]])

 

 

 

성능을 평가해볼까요?

#성능평가
from sklearn.metrics import mean_absolute_error, mean_squared_error, r2_score

print('MAE:', round(mean_absolute_error(y_test,y_pred_ols),3))
print('MSE:', round(mean_squared_error(y_test,y_pred_ols),3))
print('RMSE:', round(mean_squared_error(y_test,y_pred_ols,squared=False),3))
print('R_Squared:', round(r2_score(y_test,y_pred_ols),3))
 
 
MAE: 3.707
MSE: 29.327
RMSE: 5.415
R_Squared: 0.7
 

 

 

추가적으로 산점도를 통해 모델의 예측 성능을 시각적으로 평가할 수 있습니다. 예측값과 실제 값이 비슷하다면 점들이 대각선에 가까울 것입니다. 반면 예측값과 실제 값이 차이가 크다면 점들이 대각선에서 멀어질 것입니다. 이를 통해 모델이 데이터를 얼마나 잘 예측하는지 파악할 수 있습니다.

 
plt.scatter(y_test, y_pred_ols, s=10, c='green')
plt.xlabel("Prices: $Y_i$")
plt.ylabel("Predicted prices: $\hat{Y}_i$")
plt.title("Prices vs Predicted prices: $Y_i$ vs $\hat{Y}_i$")

min_val = min(min(y_test), min(y_pred_ols))
max_val = max(max(y_test), max(y_pred_ols))
plt.plot([min_val, max_val], [min_val, max_val], 'k--', lw=2, label='y=x line')

plt.show()

산점도

 

 

하지만 지금 데이터 독립 변수 간에 다중 공선성이 존재하기 때문에 과적합이 발생할 수 있습니다. 이 경우, 모델이 학습 데이터에 지나치게 적합되어 새로운 데이터에 대한 예측 성능이 저하됩니다. 막아야겠죠? 다음 게시글에서 릿지, 라쏘로 예측 성능이 올라가는지 한번 확인해보겠습니다.