본문 바로가기
국비 교육/머신러닝, 딥러닝

[머신러닝] 회귀분석 - L1, L2규제 ((문제) 보스톤 집값 예측, 대한민국 육군 몸무게 예측)

by 육츠 2023. 11. 20.
Contents 접기

보스톤 집값 예측

패키지로딩

from sklearn.model_selection import train_test_split
from sklearn.linear_model import LinearRegression, Lasso, Ridge, ElasticNet # LinearRegression: 규제가 적용되지 않은 선형회귀 모델
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import mean_squared_error

import numpy as np
import pandas as pd

데이터 로드 및 확인

boston = pd.read_csv('./dataset/HousingData.csv')
print(boston.shape)
display(boston.head())
# MEDV (주택가격): 종속변수

boston.dropna(inplace= True)

# 독립변수와 종속변수 분리
data_x = boston.drop('MEDV', axis= 1)
data_y = boston['MEDV']

결측치가 존재하여 독립변수와 종속변수간의 길이를 맞추기위해 데이터에 있는 결측치를 제거한 후 분리하였다.

data_x.describe() # 실제 학습 전 스케일링 (표준화) 필요

기술통계치를 사용하여 확인 결과 평균값의 폭이 너무 커, 크기를 맞추기 위해 스케일링 작업이 필요한 것을 알 수 있다.

학습, 평가 데이터 분리(전에 스케일링 하기)

scaled_data = StandardScaler().fit_transform(data_x)

x_train, x_test, y_train, y_test = train_test_split(scaled_data, data_y,
                                                   test_size= 0.3, random_state= 10)

알파값 변화에 따른 회귀계수 및 상관계수 계산
알파값 = 규제의 강도를 제어하는 상수[0,Inf) 범위의 부동소수점 값을 사용할 수 있다.
alpha = 0 이면 LinearRegression 에서 사용하는 일반적인(규제를 사용하지 않는) 최소제곱법과 같아진다.

회귀계수 = w, 상관계수 = r-squared 값

다양한 알파값에 의한 모델별 회귀계수값, 상관계수값 비교 함수 생성

# 규제 : w 가 계속해서 커지는 걸 방지

def regulation_compare(alpha, model_name):
    df = pd.DataFrame()
    # 리스트 형태로 넘어오기 때문에
    
    for a in alpha :
        print('회귀모형:', model_name)
        print('알파값:', a)

        if model_name == 'Ridge' :
            model = Ridge(alpha= a)
            
        elif model_name == 'Lasso' :
            model = Lasso(alpha= a)

        elif model_name == 'ElasticNet' :
            model = ElasticNet(alpha= a, l1_ratio= 0.5)
            # 릿지와 라쏘를 같이 사용하기 때문에 비율을 정해주어야함

        # 결정계수 값
        model.fit(x_train, y_train)
        print(f'결정계수: {model.score(x_test,y_test):.3f}') # 평가

        # 추정계수 w값 
        # 각 컬럼별의 w값을 알아내기 위한 딕셔너리
        weight = { f:w for f,w in zip(data_x.columns, model.coef_)} # 독립변수 : w 값 

        # 평가지표 (예측값, 정답 필요)
        y_hat = model.predict(x_test) # 예측값
        
        print(f'RMSE = {np.sqrt(mean_squared_error(y_test, y_hat)):.2f}') # 근의 값 구함
        print('='* 20)

        df['alpha = ' + str(a)] = pd.Series(weight) # 키값 = index, 값 = weight

    return df

 

Lasso 회귀모델 생성 (L1 규제 적용)

# 알파값이 커질수록 제거되는 항이 많아지기 때문에 결정계수가 낮아진것이다.
# weight 값이 0인 애들이 많아지는 것 = 항이 제거되는 것

# => 13개의 독립변수들이 모두 다 중요한 역할을 하고 있다.
# => 과적합 발생여부 판단 : 교차검증

alpha = [0.07, 0.1, 0.5, 1, 3] # 알파값을 아주 작은 값부터 시작
regulation_compare(alpha,'Lasso')
회귀모형: Lasso
알파값: 0.07
결정계수: 0.796
RMSE = 3.38
====================
회귀모형: Lasso
알파값: 0.1
결정계수: 0.798
RMSE = 3.37
====================
회귀모형: Lasso
알파값: 0.5
결정계수: 0.770
RMSE = 3.59
====================
회귀모형: Lasso
알파값: 1
결정계수: 0.738
RMSE = 3.84
====================
회귀모형: Lasso
알파값: 3
결정계수: 0.585
RMSE = 4.82
====================

 

Ridge 릿지 회귀모델 사용 (L2 규제 적용)

# 0: 일반적인 비용함수와 동일하다.
# 알파값을 10 -> 결정계수 높, 오차 낮
# 완전히 제외되는 항이 미존재 (w에 각 비례하는 값을 빼기 때문에)

# 독립변수 간에 영향이 있다.

# L1 규제 보다는 더 높은 규제 강도를 조정
alpha = [0, 1, 5, 10, 100]
regulation_compare(alpha,'Ridge')
회귀모형: Ridge
알파값: 0
결정계수: 0.782
RMSE = 3.50
====================
회귀모형: Ridge
알파값: 1
결정계수: 0.784
RMSE = 3.48
====================
회귀모형: Ridge
알파값: 5
결정계수: 0.791
RMSE = 3.42
====================
회귀모형: Ridge
알파값: 10
결정계수: 0.796
RMSE = 3.39
====================
회귀모형: Ridge
알파값: 100
결정계수: 0.776
RMSE = 3.54
====================

 

ElasticNet 엘라스틱 회귀모델 생성 (L1 + L2 규제)

# 알파값을 키웠을때는 현저히 결정계수가 낮아진다.(제외되는 항이 많아진다.)

alpha = [0, 1, 5, 10, 100]
regulation_compare(alpha, 'ElasticNet')
회귀모형: ElasticNet
알파값: 0
결정계수: 0.782
RMSE = 3.50
====================
회귀모형: ElasticNet
알파값: 1
결정계수: 0.725
RMSE = 3.93
====================
회귀모형: ElasticNet
알파값: 5
결정계수: 0.406
RMSE = 5.77
====================
회귀모형: ElasticNet
알파값: 10
결정계수: 0.108
RMSE = 7.07
====================
회귀모형: ElasticNet
알파값: 100
결정계수: -0.014
RMSE = 7.54
====================

 

과적합 방지 방법 중에 하나이다.(모든 상황에서 과적합이 발생하는 것은 아님)
과적합 없애기 위한 방법(변수제거, 주성분분석_차원축소, 규제, 정규화.. 등) 과적합이 나오지 않는 모델을 만드는 것이 최종 목적이다.

 

대한민국 육군 몸무게 예측

필요한 패키지 로드

import numpy as np
import pandas as pd
from sklearn.linear_model import LinearRegression, Lasso,Ridge
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_absolute_error, r2_score

데이터를 읽어오고, 결측치 있는지 확인

df = pd.read_csv('./dataset/Army-Dataset.csv')

print(df.shape)
df.head()
print(df.isna().sum())

기술통계량으로 확인하며 스케일링 여부 확인한다. (큰 차이가 없기 때문에 스케일링이 필수적이진 않다.)

df.describe()  #  스케일링이 필수적이진 않음

독립변수, 종속변수 분리

( 주석 = 내가 작성한 코드 ) 관련 없는 'Seq'와 종속변수인 'Weight'는 제외하고 독립변수 x로 만든다.

# data_x = df.drop(['Seq','Weight'], axis= 1) 
x = df.loc[:,'Chest':'Foot']
display(x.head())
# data_y = df['Weight']
y = df['Weight']

독립변수간 상관관계 확인

import seaborn as sns
import matplotlib.pyplot as plt

sns.pairplot(x)
plt.show() # 상관관계가 없음.

대부분의 형태가 이런 모양으로, 상관관계가 전혀 없다는 것을 알 수 있다.

체크 한 번 더

x.corr() # 다중공선성 관계를 가지는 변수는 없다.

 

데이터 스케일링(여기서는 데이터를 표준화한 후 사용)

from sklearn.preprocessing import StandardScaler

# 스케일링한 독립변수 가지고 본다면 새로운 데이터도 스케일링 해야함
scaler = StandardScaler().fit(x) # 스케일링 필요한 데이터를 가져오고 그에 대한 학습을 함
scaled_data = scaler.transform(x) # 데이터 변환

fit_transform 을 동시에 하게 되면 새로운 데이터를 넣을때 변환을 시킬 수 없기 때문에
나중에 새로운 데이터가 들어와도 똑같은 기준으로 fit() 하기 위해 두 함수를 분리시켜 적어놓는다.

 

학습, 평가 데이터 분리

x_train,x_test,y_train,y_test = train_test_split(scaled_data, y, 
                                                 train_size= 0.8, random_state= 10) # random_state : 필수 아님

' random_state '는 현재 동일한 데이터를 넣고 결과를 보기위해 작성한 것이므로 필수가 아니다.

모델 생성 및 학습

규제가 없는 방식, L1 규제, L2 규제 3가지 방식으로 진행하여 본다.

ridge_alpha = 10
lasso_alpha = 1

# 모형 생성
linear = LinearRegression()
ridge = Ridge(alpha= ridge_alpha)
lasso = Lasso(alpha=lasso_alpha)

# 학습
linear.fit(x_train,y_train)
ridge.fit(x_train,y_train)
lasso.fit(x_train,y_train)

 

예측 및 평가

# 예측
linear_y_hat = linear.predict(x_test)
ridge_y_hat = ridge.predict(x_test)
lasso_y_hat = lasso.predict(x_test)

linear_r2 = r2_score(y_test,linear_y_hat) # 정답과 예측값을 넣음 <> model.score() = 평가데이터를 넣음(안에서 예측을 다시 한 번 함)
ridge_r2 = r2_score(y_test,ridge_y_hat)
lasso_r2 = r2_score(y_test,lasso_y_hat)
# 정답은 같음

# 평가 결과값
linear_mae = mean_absolute_error(y_test, linear_y_hat)
ridge_mae = mean_absolute_error(y_test, ridge_y_hat)
lasso_mae = mean_absolute_error(y_test, lasso_y_hat)


print(f'R2 score - Linear : {linear_r2:.2f}, Ridge : {ridge_r2:.2f}, Lasso : {lasso_r2:.2f}') 
print(f'MAE score - Linear : {linear_mae:.2f}, Ridge : {ridge_mae:.2f}, Lasso : {lasso_mae:.2f}')
# 규제를 쓰나 안쓰나 차이가 없다.
R2 score - Linear : 0.89, Ridge : 0.89, Lasso : 0.88
MAE score - Linear : 3.54, Ridge : 3.54, Lasso : 3.68

Linear()  와의 비교를 통해 규제 여부는 관계없다는 것을 알았다.

새로운 값 넣어보기

1차원 -> 학습: 2차원으로 들어가야함 (or 이중리스트로 넣어도 된다.)

linear.predict(new_data)
# 학습데이터와는 단위가 다르기 때문에 스케일링 필요 (fit() 과 transform() 을 구분하여 작성한 이유이다.)

x.iloc[0] # 0번째 행 데이터

new_data = np.array([90, 178, 80, 55, 26.5]).reshape(1,5)
# linear.predict(new_data) # 학습데이터와는 단위가 다름 -> 스케일링 필요
linear.predict(scaler.transform(new_data))