본문 바로가기
기계학습(ML)/Linear Models

n213 Ridge Regression, onehotencoding

by kiimy 2021. 6. 12.
728x90
import pandas as pd
import numpy as np
from sklearn.linear_model import LinearRegression,  LogisticRegression,
Ridge, RidgeCV, Lasso, LassoCV, ElasticNet,ElasticNetCV
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import train_test_split
from sklearn.metrics import r2_score, mean_squared_error

##범주 비율

df['City'].value_counts(normalize=True)

## aggregation을 사용해 City 각 범주에 대한 여러 통계량을 볼 수도 있습니다.
## (min, max, mean, median) df.groupby('City')['Price'].agg(['min','max','mean','median'])


## install

! pip install sklearn --upgrade

! pip install category_encoders

from category_encoders import OneHotEncoder

encoder = OneHotEncoder(use_cat_names = True) 

X_train = encoder.fit_transform(X_train)

X_test = encoder.transform(X_test)

==> 범주형 변수를 가진 특성만 인코딩

### Encoding###

Categorical(범주 척도), Quantitative(양적 척도)

Categorical(범주 척도) == 범주형

*명목형 Nominal

값이 달라짐에 따라 좋거나 나쁘다고 할 수 없는 경우

(= 평균을 계산하는것이 의미가 X, 퍼센트로 표현가능)

==> 혈액형 A, B, O, AB ==? 1, 2, 3, 4 / 성별, 색깔, 취미, 주거지역

*순서형 Ordinal

값이 커짐에 따라 만족도가 좋아지고 있다고 할 수 있는 경우

==> 만족도, 병의 단계, 성적표


Quantitative(양적 척도) == 수치형

*이산형

소수점의 의미가 없음 / 수치적인 의미는 가지고 있으나 소수점으로 표현되지 않는 경우

==> 불량품 수, 사고 건수, 차량대수 

 

*연속형

수치적인 의미가 있으며 소수점으로 표현되는 경우

==> 몸무게, 시간, 길이

# np.percentile 사용해 이상치 제거

df = df[(df['price'] >= np.percentile(df['price'], 0.05)) &
                                              (df['price'] <= np.percentile(df['price'], 99.5))]

*encoding

Categorical Encoding의 두가지 방법.

1. Label Encoding

2. One-Hot Encoding

# 머신러닝을 할 때 기계가 이해할 수 있도록 모든 데이터를 수치로 변환해주는 전처리 작업이 필수

# ==> get_dummies() 더미로 가변수화 // 수치형 데이터로만 바꾸면 될텐데 왜 굳이??

# df_dum = pd.get_dummies(df, prefix=['City'], drop_first=True) 첫번째 카테고리 삭제(변수를 줄여 오차를 줄여줌)

==> 3개의 변수중 두개가 0이면 나머지 하나는 1이기때문에

# ==> 수치형 데이터로만 변환을 하게 되면 서로 간의 관계성이 생기게 된다

 

'''

예를 들어, 월요일을 1, 화요일을 2, 수요일을 3이라고

 

단순하게 수치형 데이터로 변환하게 되면 해당 데이터들 간

 

1+2 = 3이라는 관계성이 존재하게 된다. 

 

그러나 실제 데이터인 월요일, 화요일, 수요일 간에는 그러한 관계성이 없다

따라서, 사실이 아닌 관계성으로 인해 잘못된 학습이 일어날 수 있으므로

서로 무관한 수, 즉 더미로 만든 가변수로 변환함으로서 그러한 문제를 막아준다

'''

* fit_transform, transform

fit_transform()을 fit() 과 transform() 함께 수행하는 메소드 입니다. 기존의 fit() 과 transform() 각각을

수행하는 번거로움을 줄여줍니다.  위 코드를 예로 들면

scaler.fit(X_train)
scaled_X_train = scaler.transform(X_train)은

=> scaled_X_train = scaler.fit_transform(X_train) 과 같이 한 라인으로 대체 될 수 있습니다. 

하지만 테스트 데이터에 scaled_X_test = scaler.fit_transform(X_test)를 적용해서는 안됩니다.

이를 수행하면 scaler 객체가 기존에 학습 데이터에 fit 했던 기준을 모두 무시하고 다시 테스트 데이터를

기반으로 기준을 적용하기 때문입니다. 

때문에 테스트 데이터에 fit_transform()을 적용해서는 안됩니다.

*f_regression, SelectKBest( 유용한 특성들만 추출 )

==> 회귀계수 축소시켜줌 == 특성들과는 상관성이 낮은것

from sklearn.feature_selection import f_regression, SelectKBest

## selctor 정의합니다.

selector = SelectKBest(score_func=f_regression, k=10) ==> k 개수

## 학습데이터에 fit_transform

X_train_selected = selector.fit_transform(X_train, y_train)

## 테스트 데이터는 transform

X_test_selected = selector.transform(X_test)

#### 선택된 특성이름 확인###

all_names = X_train.columns

## selector.get_support() selected_mask = selector.get_support() ==> support해주는것들 꺼내겠다.

## 선택된 특성들 selected_names = all_names[selected_mask]

## 선택되지 않은 특성들 unselected_names = all_names[~selected_mask]



model = LinearRegression()

model.fit( X_train_selected, y_train )

# train, test

y_pred = model.predict( X_train_selected )

mean_absolute_error(y_train, y_pred)



y_pred = model.predict( X_test_selected )

mean_absolute_error(y_test, y_pred)

r2 = r2_score(y_test, y_pred)

*Ridge Regression

= 과적합을 줄이기 위해서(모델의 복잡도를 줄이는 것) 특성의 갯수를 줄이거나 모델을 단순한모양으로 적합

==> bias를 더하고 분산을 줄이는 방법 ( 정규화 ) == 모델을 변경하고 과적합을 완화하여 일반화 성능 높이는 것

* 정규화를 통해 특이값으로 인한 과도한 기울기 보정 ( 람다를 통해서 기울기 조정 )

* 영향력이 적은 특성의 회귀계수의 값을 감소시켜 특성 선택 할 수 있음

==> 모델의 복잡도를 임의로 조정할 수 있음(사용자 마음)

feature = 'sqft_living'

model = Ridge(alpha=alpha, normalize=True)

model.fit(X_train[[feature]], y_train)

# Get Test MAE

y_pred = model.predict(X_test[[feature]])

mae = mean_absolute_error(y_test, y_pred)

* Rasso Regression

- 필요없는 특성을 0으로 만듬


** Ridge CV 교차 검증을 통해서 람다값을 설정하고 과적합을 줄일 수 있음

교차 검증을 통한 성능 평가의 목적

1. Unseen 데이터에 대한 성능을 예측하기 위해,

2. 더 좋은 모델을 선택하기 위해 (혹은 Hyperparameter Tuning) 입니다
출처: https://3months.tistory.com/321 [Deep Play]

 

람다가 클수록 회귀계수들을 0으로 수렴(즉, 과적합을 줄임) == 기울기가 작아짐 == 이상치 영향을 들받음

너무 크면 underfitting

람다가 작을수록 (0에 가까워지면) 기준모델과 비슷해짐

너무 작으면 overfitting


그런데 이러한 제약 조건을 붙임으로 얻을 수 있는 효과는 무엇일까요? 만약 alpha 값을 크게 설정해주면 그만큼 가중치들의 절대값들은 작아져야합니다. 그러면 그만큼 기울기가 줄어들겠죠? 기울기가 줄어든다는 말은 특성들이 출력에 미치는 영향력(이상치)이 줄어든다고 볼 수 있습니다. 즉, 현재 특성들에 덜 의존하겠다는 뜻으로 볼 수 있습니다.

반대로 alpha 값을 작게 설정해주면 가중치들의 절대값들은 조금 커지게 됩니다. 만약 alpha 값을 줄이고 줄여서 0에 가깝게 설정해준다면 사실상 선형 회귀와 동일해지겠죠. alpha가 커지면 커질수록 과소적합이 될 가능성이, alpha가 작으면 작을수록 과대적합이 될 가능성이 커집니다. 따라서 적정한 alpha값을 찾아주는 것은 바로 릿지 회귀를 사용하는 사람의 책임이 됩니다. 

 

결정계수를 구했더니 훈련셋의 결정계수는 0.67이었고, 테스트셋의 결정계수는 0.66이었습니다. 테스트셋의 결정계수가 0.66이므로 성능이 엄청 나쁜 것은 아니지만 그렇다고 좋은 것도 아닙니다. 예측 성능이 충분히 좋진 않다는 뜻입니다. 그러나 훈련셋의 결정계수와 큰 차이가 없는 것은 좋은 일입니다. 훈련셋에 과대적합(overfitting)이 되지 않았고, 오히려 과소적합(underfitting)인 상황이기 때문입니다. 가장 좋은 결과는 테스트셋 점수와 훈련셋 점수가 모두 1에 가깝고, 둘의 차이가 적은 경우입니다. 


데이터의 특성이 단 하나일 경우에는 선형 회귀나 릿지 회귀나 다를 것이 없습니다. 똑같은 성능을 보입니다. 그러나 특성이 다수일 경우에는 릿지 회귀가 좀 더 잘 작동할 가능성이 큽니다. 릿지 회귀는 선형 회귀와 달리 모델의 복잡도를 조정할 수 있기 때문입니다. 복잡도를 조정할 수 있다는 말은 사용자가 설정 가능한 파라미터가 있다는 뜻으로 받아들이셔도 좋습니다.

==> 다항함수의 경우 정규화 효과를 더 잘 확인 가능

다항함수를 사용하면, 변수들의 차수가 늘어나 데이터셋의 feature들이 엄청 늘어나고, 데이터에 과적합을 일으킨다.

(=Ridge 모델을 사용하면 feature 중 영향력이 높은 feature만 남길 수 있다.)

 

선형 회귀의 경우  최적의 가중치들을 선택하기 위해서 타깃값과 예측값의 차를 제곱해서 평균낸 것, 즉 평균제곱오차(mean squared error, MSE)를 최소로 만드는 가중치들을 찾습니다.

 

평균제곱오차

 

릿지 회귀는 여기에다가 제약 조건을 덧붙입니다. "그 가중치들의 절대값들을 가능한 한 작게 만들어라!" 따라서 릿지 회귀는 다음과 같은 식을 최소로 만드는 가중치들을 찾습니다. 

 

여기서 alpha가 사용자가 설정할 수 있는 파라미터입니다. ( 이 합이 최소가 되는 가중치를 찾는다 )

 

np.arrange / linspace

np.arrange(1,10,2)
(= np.array(range(1, 10, 2)) )
== list(range(1,10,2)) 

np.linspace(1,10,20) = 시작, 끝, [개수]
'''
linspace의 개수값은 Default값으로 설정되어 있어, 입력하지 않으면 Default값인 50으로 설정

소수점으로 1부터 작은 수부터 큰수로 20개 나옴( 10도 포함)

np.linspace(1,10,10)

정수 1~10 나옴
'''
linspace는 끝 '이하'이기 때문에 끝값이 데이터에 반드시 포함되는 반면,
arange는 '미만'으로 절대 포함될 수 없는 값이라는 차이점 존재

 

 

최소값, 최대값의 value인 index: np.argmin(), np.argmax()
list_a = [1,2,3,4,5]

np.argmax(list_a) == 가장 큰 value의 index가 추출 

output >>> 4

 

alphas = [0, 0.001, 0.01, 0.1, 1]

ridge = RidgeCV(alphas=alphas, normalize=True, cv=5) # cv= k값( K-fold cross validation )

ridge.fit(X_train_selected, y_train)

print(ridge.alpha_, ridge.best_score_) # 패널티(람다)값이 나오고 제일 높은 r2값이 나옴


model= Ridge(alpha=0.001, normalize=True)

model.fit(X_train_selected, y_train)

y_pred= model.predict(X_test_selected)

mae= mean_absolute_error(y_test, y_pred)

r2= r2_score(y_test, y_pred)

*최종모델

최종 모델을 만들기 위해서는 가지고 있는 데이터를 다 사용해 최적의 모델을 만들어야 합니다.

지금 가지고 있는 테스트 데이터를 검증 데이터로 사용하려면 RidgeCV에 훈련 데이터로 함께 넣어 주어야 합니다. RidgeCV내에서 훈련 데이터를 훈련/검증 데이터로 나누어 최고 스코어를 가지는 alpha를 찾아 줄 것입니다.

#code

X_total = pd.concat([X_train, X_test])

y_total = pd.concat([y_train, y_test])

model = RidgeCV(alphas=alphas, normalize=True, cv=5)

model.fit(X_total, y_total)
print(f'alpha: {model.named_steps["ridgecv"].alpha_}')
print(f'cv best score: {model.named_steps["ridgecv"].best_score_}')
#best_score_ : R2

n214과대적합 모델을 개선하는 한 방법은, 검증오차가 훈련오차에 가까워 질때 까지 훈련세트를 더 투입하는 것

728x90

댓글