[파이썬 실습] 정규화 모델 실습(1)

목표 : 정규화 모델 구축

  • 1. 데이터 전처리
  • Ridge, Lasso, Elasticnet regression 구축 (hyperparameter 탐색)
  • 예측 결과 평가 및 변수 중요도 해석

 

1. 모듈 불러오기

from IPython.display import display, HTML
import warnings
warnings.filterwarnings('ignore')

# 데이터 전처리
import numpy as np
import pandas as pd

from sklearn.model_selection import train_test_split
from sklearn.linear_model import LinearRegression, Lasso, Ridge, ElasticNet, LassoCV, RidgeCV, ElasticNetCV
from sklearn.model_selection import KFold, GridSearchCV
from sklearn.preprocessing import StandardScaler
from sklearn.feature_extraction import DictVectorizer
from sklearn import metrics
from sklearn.metrics import mean_squared_error, r2_score, mean_absolute_error

# 시각화
import seaborn as sns
import matplotlib.pyplot as plt

# 한글 폰트 설정
plt.rc('font', family = 'Malgun Gothic')

 

2. 데이터 불러오기 : HousePrices

데이터 구조

  • 데이터 : 집 가격 예측 데이터
  • 관측치 개수 : 1460개
  • 변수 개수 : 80개 (집의 특성, 집 가격)

data = pd.read_csv('train1.csv', index_col = 'Id')
data.head()

데이터를 확인하면 80개의 변수로 구성이 되어있고 정수형, 범주형, 실수형, NaN 등 다양한 값들이 존재한다.

 

3. 탐색적 데이터 분석 및 데이터 전처리

데이터 shape 확인

print(f'관측치 수 : {data.shape[0]} \n변수 수 : {data.shape[1]}')

 

결측치, 데이터 타입 확인

data.info()
data.describe()

 

데이터 타입 변환 (int -> str(object))

수치형으로 데이터가 구성이 되어있지만 일부는 범주형이어야 되는 경우가 있다. 의미를 잘 파악해서 타입을 변환한다.

  • 아래 3가지 변수는 등급을 의미하는 변수이기 때문에 실제로 표기되는 값은 int형이지만 의미적으론 숫자 간의 관계가 독립적이기 때문에 범주형으로 바꾸는 것이 적합함
for categ in ['MSSubClass', 'OverallQual', 'OverallCond']:
    data[categ] = data[categ].astype(str)

 

 

target y 값 분포 확인

 

Train / Test 데이터 분리

X = data.drop('SalePrice', axis = 1)
y = data['SalePrice']

display(X.head(3))

 

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.3, random_state = 2021)

 

이제 전처리를 시작한다.

 

Null 확인 및 대체

  • Null, NaN, NA, '정해지지 않은 값' 개수 확인
NaNinfo = X_train.isnull().sum()
NaNinfo = NaNinfo.sort_values(ascending = False)
NaNinfo = NaNinfo[NaNinfo > 0]
NaNinfo

 

이 각각의 변수들이 어떤 값으로 구성되어 있는지 확인을 한다.

for col in NaNinfo.index:
    print(f'{col} : {data[col].unique().tolist()[:10]}')

위의 값을 보면 PoolQC는 nan, Ex, Fa, Gd로 구성이 되어있고, 각각 변수마다 결측치들이 존재하는 것을 알 수 있다.

y값에 결측치가 있다면 검증이 불가능하기 때문에 제거를 해야 한다.

  • Target : SalePrice에 결측값이 있다면 해당 관측치는 제거
  • Numerical : 평균값으로 대체(다양한 방법이 있을 수 있으나)
  • Categorical : 'NaN'으로 대체
for feature in ['LotFrontage', 'LotArea', 'MasVnrArea',
               'BsmtFinSF1', 'BsmtFinSF2', 'BsmtUnfSF', 'TotalBsmtSF',
               '1stFlrSF', '2ndFlrSF', 'LowQualFinSF', 'GrLivArea', 'BsmtFullBath', 'BsmtHalfBath', 'FullBath', 'HalfBath',
               'BedroomAbvGr', 'KitchenAbvGr', 'TotRmsAbvGrd', 'Fireplaces', 'GarageYrBlt',
               'GarageCars', 'GarageArea', 'WoodDeckSF', 'OpenPorchSF', 'EnclosedPorch', '3SsnPorch',
               'ScreenPorch', 'PoolArea', 'MiscVal']:
    X_train[feature] = X_train[feature].fillna(X_train[feature].mean())
    X_test[feature] = X_test[feature].fillna(X_train[feature].mean())
for feature in ['PoolQC', 'MiscFeature', 'Alley', 'Fence', 'FireplaceQu',
               'GarageCond', 'GarageType', 'GarageFinish', 'GarageQual',
               'BsmtExposure', 'BsmtFinType2', 'BsmtQual', 'BsmtCond', 'BsmtFinType1',
               'Electrical', 'MSZoning', 'Functional', 'Utilities', 'KitchenQual', 'SaleType',
               'Exterior1st', 'Exterior2nd', 'MasVnrType']:
    X_train[feature] = X_train[feature].fillna('NaN')
    X_test[feature] = X_test[feature].fillna('NaN')
category = list(X_train.select_dtypes(include = [object]))
category[:10]

위의 코드를 보면 범주형을 처리하기 위해 범주형을 모아서 카테고리로 리스트를 만들었다.

카테고리 변수는 숫자형이 아니기 때문에 인코딩 작업이 필요하다. 수치형 변수는 최대 최소 스케일링, 정규화 스케일링이 필요하다.

 

 

Label encoding vs One-hot encoding

카테고리 변수 인코딩 방법에는 Label encoding, One-hot encoding이 있다.

Label encoding은 정렬된 기준으로 숫자를 할당하는 방법이다.

One-hot encoding은 범주 종류에 대해 이진 값으로 할당하는 방법이다.

보통은 원-핫 인코딩을 많이 사용한다.

 

여기서도 원-핫 인코딩을 사용할 것이다.

vec = DictVectorizer()
vec.fit(X_train[category].to_dict('records'))

scaler = StandardScaler()
scaler.fit(X_train.drop(category, axis = 1))

위의 코드는 트레인셋에 대해 스케일러를 만들고 피팅하는 코드이다.

X_category = vec.transform(X_train[category].to_dict('records'))
X_train_category = pd.DataFrame(X_category.toarray(), columns = vec.feature_names_)
display(X_train_category.head())

X_train_without_category = X_train.drop(category, axis = 1)
X_scale = scaler.transform(X_train_without_category)
X_train_scale = pd.DataFrame(X_scale, columns = X_train_without_category.columns)
display(X_train_scale.head())

적용을 할 때엔 트레인셋과 테스트셋에 트랜스폼만 한다고 생각하면 된다.

X_train_final = pd.concat([X_train_scale, X_train_category], axis = 1)
X_train_final.tail()
X_category = vec.transform(X_test[category].to_dict('records'))
X_test_category = pd.DataFrame(X_category.toarray(), columns = vec.feature_names_)
display(X_test_category.head())

X_test_without_category = X_test.drop(category, axis = 1)
X_scale = scaler.transform(X_test_without_category)
X_test_scale = pd.DataFrame(X_scale, columns = X_test_without_category.columns)
display(X_test_scale.head())
X_test_final = pd.concat([X_test_scale, X_test_category], axis = 1)
X_test_final.tail()
X_train, y_train = X_train_final.values, y_train.values
X_test, y_test = X_test_final.values, y_test.values

여기까지가 데이터 전처리 부분이다


모델링

model_LR = LinearRegression(n_jobs = -1)
model_LR.fit(X_train, y_train)
display(pd.DataFrame(model_LR.coef_,
                    index = X_test_final.columns, columns = ['Linear regression 계수']))

일단 Linear Regression에 대해서 피팅을 실시했다.

아래 결과값은 coef값이다.

 

다음으로 라쏘, 릿지 모델을 만들 것이다.

람다값인 알파값을 임의로 설정해서 돌려봤다.

model_Lasso1 = Lasso(alpha = 0.0001, random_state = 1)
model_Lasso2 = Lasso(alpha = 100.0, random_state = 1)
model_Ridge1 = Ridge(alpha = 0.0001, random_state = 1)
model_Ridge2 = Ridge(alpha = 100.0, random_state = 1)

model_Lasso1.fit(X_train, y_train)
model_Lasso2.fit(X_train, y_train)
model_Ridge1.fit(X_train, y_train)
model_Ridge2.fit(X_train, y_train)
plt.figure(figsize = (10, 5))
plt.plot(sorted(np.abs(model_LR.coef_))[::-1], label = 'LR', c = 'gray')
plt.plot(sorted(np.abs(model_Lasso1.coef_))[::-1], label = 'Lasso alpha = 0.0001')
plt.plot(sorted(np.abs(model_Lasso2.coef_))[::-1], label = 'Lasso alpha = 100')
plt.plot(sorted(np.abs(model_Ridge1.coef_))[::-1], label = 'Ridge alpha = 0.0001')
plt.plot(sorted(np.abs(model_Ridge2.coef_))[::-1], label = 'Ridge alpha = 100')
plt.legend()
plt.ylim((-0.1, 1000))
plt.show()

그래프를 보면 X축은 변수의 종류, y축은 coef값이다.

라쏘에 대한 비교를 하자면 주황색이 작았던 파란색 대비 많은 변수의 값들이 계수가 0으로 도출이 되었다.

릿지를 보면 람다값이 큰 경우가 초록색 대비 계수 값들이 작은 값을 도출하는 것을 확인할 수 있었다.

 

 

TAGS.

Comments