[머신러닝] 릿지 회귀(Ridge Regression)란?

원핫 인코딩(One-hot encoding)

  • 단어 집합의 크기를 벡터의 차원으로 하여 표현하고 싶은 단어의 인덱스에 1의 값을 부여하고, 다른 인덱스에는 0을 부여하는 단어의 벡터 표현 방식이다.
  • 원핫 인코딩을 두 가지 과정으로 정리해보면,
    • 각 단어에 고유한 인덱스를 부여(정수 인코딩)한다.
    • 표현하고 싶은 단어의 인덱스의 위치에 1을 부여하고, 다른 단어의 인덱스의 위치에는 0을 부여한다.
  • 그 이유는,
    • 사람이 매우 쉽게 이해할 수 있는 데이터를 컴퓨터에게 주입시키기 위함이다.
    • 사이킷런(scikit-learn)에서 제공하는 머신러닝 알고리즘은 문자열 값을 입력 값으로 허락하지 않기 때문에 모든 문자열 값들을 숫자형으로 인코딩하는 전처리 작업(Preprocessing) 후에 머신러닝 모델에 학습을 시켜야 한다.

데이터 준비

1
2
3
4
5
6
7
8
9
10
11
12
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns

df = pd.DataFrame({
    'City': ['Seoul', 'Seoul', 'Seoul', 'Busan', 'Busan', 'Busan', 'Incheon', 'Incheon', 'Seoul', 'Busan', 'Incheon'],
    'Room': [3, 4, 3, 2, 3, 3, 3, 3, 3, 3, 2],
    'Price': [55000, 61000, 44000, 35000, 53000, 45000, 32000, 51000, 50000, 40000, 30000]
})

df

스크린샷 2021-08-11 14 05 55

  • City 컬럼에 있는 데이터는 도시 지역을 구분하는 범주형 변수(Categorical vaiable)이다.
    • 범주형 자료: 순서가 없는 명목형(Nominal) 또는 순서가 있는 순서형(Ordinal)
  • 도시는 높고 낮음이 없으므로 명목형 범주형 변수이다.

범주형 변수를 원핫 인코딩으로 변환

스크린샷 2021-08-11 14 08 15

  • Color 변수를 원핫 인코딩으로 변환해 가능한 모든 범주를 열로 나타sosek.
1
2
3
4
5
6
7
8
9
10
11
12
# City의 범주 비율
df['City'].value_counts(normalize=True)

'''
Seoul      0.363636
Busan      0.363636
Incheon    0.272727
Name: City, dtype: float64
'''

# seaborn countplot, City
sns.countplot(x=df['City']);

스크린샷 2021-08-11 14 10 54

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# Price 평균
df['Price'].mean()
'''
45090.90909090909
'''


# City 각 범주에 대한 Price 값
df.groupby('City')['Price'].mean()
'''
City
Busan      43250.000000
Incheon    37666.666667
Seoul      52500.000000
Name: Price, dtype: float64
'''


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

스크린샷 2021-08-11 14 12 50

  • 원핫 인코딩을 수행하면 각 카테고리에 해당하는 변수들이 모두 차원에 더해지게 된다.
  • 그러므로 카테고리가 너무 많은 경우(high cardinality)에는 사용하기 적합하지 않다.
    • 그럴 땐 Catboost)를 사용하면 된다.
1
2
3
# 불필요한 요소를 없인 더미 코딩
df_dum = pd.get_dummies(df, prefix=['City'], drop_first=True)
df_dum

스크린샷 2021-08-11 14 15 41

선형 회귀 모델 원핫 인코딩 특성 사용

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# import LinearRegression
from  sklearn.linear_model import LinearRegression


# Initialize LinearRegression
model_oh = LinearRegression()


# 모델 학습(fit)
model_oh.fit(df_oh[['City_Seoul','City_Busan','City_Incheon']], df_oh['Price'])


# model coef_, intercept_
print("coefficients: ", model_oh.coef_)
print("intercept:" , model_oh.intercept_)
'''
coefficients:  [ 8027.77777778 -1222.22222222 -6805.55555556]
intercept: 44472.22222222222
'''


# 더미 코딩
model_dum = LinearRegression()
model_dum.fit(df_dum[['City_Seoul', 'City_Incheon']], df_dum['Price'])
print("coefficient: ", model_dum.coef_)
print("intercept: ", model_dum.intercept_)
'''
coefficient:  [ 9250.         -5583.33333333]
intercept:  43250.0
'''


import plotly.express as px
px.scatter(
    df_oh,
    x='City_Seoul',
    y='Price',
    trendline='ols'
)

스크린샷 2021-08-11 14 21 42

1
2
3
4
5
6
7
import plotly.express as px
px.scatter(
    df_oh,
    x='City_Busan',
    y='Price',
    trendline='ols'
)

스크린샷 2021-08-11 14 23 57

카테고리 인코더(Category_encoders)

  • 카테고리 인코더 라이브러리를 사용하면 범주형 데이터에만(‘City’) 원핫 인코딩을 수행할 수 있다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
features = ['City','Room']
target = 'Price'

# 훈련/테스트 데이터를 분리
X_train = df[features][:8]
y_train = df[target][:8]
X_test = df[features][8:]
y_test = df[target][8:]


# 카테고리값을 원핫 인코딩을 사용하여 숫자로 변환
# import OneHotEncoder
from category_encoders import OneHotEncoder


# 원핫 인코딩
encoder = OneHotEncoder(use_cat_names = True)
X_train = encoder.fit_transform(X_train) # 훈련
X_test = encoder.transform(X_test) #테스트


# category_encoders를 사용하면 범주형변수를 가진 특성만 원핫 인코딩을 수행
# show X_train
X_train.head()

스크린샷 2021-08-11 14 29 21

1
2
## show X_test
X_test

스크린샷 2021-08-11 14 29 33

특성 선택(Feature Selection)

  • 특성 공학은 과제에 적합한 특성을 만들어 내는 과정이다.
    • 이 프로세스는 실무 현장에서 가장 많은 시간이 소요되는 작업 중 하나이다.
  • SelectKBest를 사용하면 특성이 많아도 쉽게 타겟값과 가장 상관관계를 높게 가지는 것들을 선택할 수 있다.

데이터 준비

1
2
3
4
5
6
7
8
9
10
# import house data
df = pd.read_csv('https://ds-lecture-data.s3.ap-northeast-2.amazonaws.com/kc_house_data/kc_house_data.csv')


# to_datetime을 통해 시간과 날짜를 다루기 쉬운 datetime64 형태로 변환
df['date'] = pd.to_datetime(df['date'])


# displot, 'price'
sns.displot(df['price']);

스크린샷 2021-08-11 14 32 28

1
2
3
4
5
6
7
# np.percentile 사용해 price 값이 상위 5%, 하위 5%인 데이터를 삭제
df = df[(df['price'] >= np.percentile(df['price'], 5)) & 
        (df['price'] <= np.percentile(df['price'], 95))] 


# displot, 'price'
sns.displot(df['price']);

스크린샷 2021-08-11 14 34 31

1
2
3
4
5
6
7
8
9
10
11
# 2015-03-01을 기준으로 훈련/테스트 세트를 분리 / 날짜 분리
cutOff = pd.to_datetime('2015-03-01')
train = df[df['date'] < cutOff]
test  = df[df['date'] >= cutOff]


# train/test shape
train.shape, test.shape
'''
((16772, 21), (4721, 21))
'''

새로운 특성을 생성하고 삭제

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
def engineer_features(X):

  # pandas.DataFrame.copy()
  X = X.copy() # 얕은 복사
    
  # 욕실 갯수을 정수형으로 처리
  X['bathrooms'] = X['bathrooms'].round(0).astype(int)

  # 총 방 수를 합하여 rooms로 합치기
  X['rooms'] = X['bedrooms'] + X['bathrooms']

  # 사용하지 않을 특성을 삭제합
  X = X.drop(['id', 'date', 'waterfront'],axis=1)

  return X


train = engineer_features(train)
test = engineer_features(test)


# 선택 가능한 특성들의 가지수를 계산한다.
# 특성은 가짓수가 늘어날수록 그 조합이 기하급수적으로 늘어난다.

from math import factorial

n = len(train.columns)

def n_choose_k(n, k):
    return factorial(n)/(factorial(k)*factorial(n-k))

combinations = sum(n_choose_k(n,k) for k in range(1,n+1))

combinations
'''
524287.0
'''
  • 가능한 방법이 많은데 필요한 좋은 특성을 뽑기엔, 수작업으로 할 수 없으니 SelectKBest를 사용해서 유용한 특성들을 찾아본다.
  • 좋은 특성을 뽑는 방법은 특성들 끼리는 상관성이 적으면서 타겟 특성과는 상관관계가 큰 것을 뽑는다.

데이터를 훈련/테스트 데이터로 분리

1
2
3
4
5
6
7
target = 'price'

## X_train, y_train, X_test, y_test 데이터로 분리
X_train = train.drop(columns=target)
y_train = train[target]
X_test = test.drop(columns=target)
y_test = test[target]

SelectKBest 이용

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# target(Price)와 가장 correlated 된 features를 k개 고르는 것이 목표다.
# 독립적으로 뽑히기 때문에 과적합을 줄이고 모델 성능을 높인다.
# 특성 개수가 줄어서 훈련시간도 감소한다. 

# f_regresison, SelectKBest
from sklearn.feature_selection import f_regression, SelectKBest

# selctor 정의
selector = SelectKBest(score_func=f_regression, k=10)

# 학습데이터에 fit_transform 
X_train_selected = selector.fit_transform(X_train, y_train)

# 테스트 데이터는 transform
X_test_selected = selector.transform(X_test)

X_train_selected.shape, X_test_selected.shape
'''
((16772, 10), (4721, 10))
'''

선택된 특성

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
all_names = X_train.columns

# selector.get_support()
selected_mask = selector.get_support()

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

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

print('Selected names: ', selected_names)
print('Unselected names: ', unselected_names)
'''
Selected names: Index(['bedrooms', 'bathrooms', 'sqft_living', 'view', 'grade', 'sqft_above',
                'sqft_basement', 'lat', 'sqft_living15', 'rooms'],
                dtype='object')
Unselected names: Index(['sqft_lot', 'floors', 'condition', 'yr_built', 'yr_renovated',
                  'zipcode', 'long', 'sqft_lot15'],
                  dtype='object')
'''

특성의 수 결정

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
# features를 몇 개 선택하는 것이 좋은 지 확인

from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_absolute_error, r2_score


training = []
testing = []
ks = range(1, len(X_train.columns)+1)


# 1 부터 특성 수 만큼 사용한 모델을 만들어서 MAE 값 비교
for k in range(1, len(X_train.columns)+ 1):
    print(f'{k} features')
    
    selector = SelectKBest(score_func=f_regression, k=k)
    
    X_train_selected = selector.fit_transform(X_train, y_train)
    X_test_selected = selector.transform(X_test)
    
    all_names = X_train.columns
    selected_mask = selector.get_support()
    selected_names = all_names[selected_mask]
    print('Selected names: ', selected_names)

    
    model = LinearRegression()
    model.fit(X_train_selected, y_train)
    y_pred = model.predict(X_train_selected)
    mae = mean_absolute_error(y_train, y_pred)
    training.append(mae)
    
    y_pred = model.predict(X_test_selected)
    mae = mean_absolute_error(y_test, y_pred)
    r2 = r2_score(y_test, y_pred)
    testing.append(mae)
    print(f'Test MAE: ${mae:,.0f}')
    print(f'Test R2: {r2} \n')

plt.plot(ks, training, label='Training Score', color='b')
plt.plot(ks, testing, label='Testing Score', color='g')
plt.ylabel("MAE ($)")
plt.xlabel("Number of Features")
plt.title('Validation Curve')
plt.legend()
plt.show()
'''
1 features
Selected names:  Index(['sqft_living'], dtype='object')
Test MAE: $167,321
Test R2: 0.4296149194220933 

2 features
Selected names:  Index(['sqft_living', 'grade'], dtype='object')
Test MAE: $157,239
Test R2: 0.4884712916259375 

3 features
Selected names:  Index(['sqft_living', 'grade', 'sqft_living15'], dtype='object')
Test MAE: $156,951
Test R2: 0.49204137332086395 

4 features
Selected names:  Index(['sqft_living', 'grade', 'sqft_above', 'sqft_living15'], dtype='object')
Test MAE: $154,920
Test R2: 0.5019286655041775 

5 features
Selected names:  Index(['bathrooms', 'sqft_living', 'grade', 'sqft_above', 'sqft_living15'], dtype='object')
Test MAE: $154,979
Test R2: 0.5020209934516053 

6 features
Selected names:  Index(['bathrooms', 'sqft_living', 'grade', 'sqft_above', 'sqft_living15',
       'rooms'],
      dtype='object')
Test MAE: $154,376
Test R2: 0.5051572843210005 

7 features
Selected names:  Index(['bathrooms', 'sqft_living', 'view', 'grade', 'sqft_above',
       'sqft_living15', 'rooms'],
      dtype='object')
Test MAE: $149,839
Test R2: 0.532627969843283 

8 features
Selected names:  Index(['bathrooms', 'sqft_living', 'view', 'grade', 'sqft_above', 'lat',
       'sqft_living15', 'rooms'],
      dtype='object')
Test MAE: $126,250
Test R2: 0.6266392465899573 

9 features
Selected names:  Index(['bedrooms', 'bathrooms', 'sqft_living', 'view', 'grade', 'sqft_above',
       'lat', 'sqft_living15', 'rooms'],
      dtype='object')
Test MAE: $126,250
Test R2: 0.6266392465899552 

10 features
Selected names:  Index(['bedrooms', 'bathrooms', 'sqft_living', 'view', 'grade', 'sqft_above',
       'sqft_basement', 'lat', 'sqft_living15', 'rooms'],
      dtype='object')
Test MAE: $126,250
Test R2: 0.6266392465899593 

11 features
Selected names:  Index(['bedrooms', 'bathrooms', 'sqft_living', 'floors', 'view', 'grade',
       'sqft_above', 'sqft_basement', 'lat', 'sqft_living15', 'rooms'],
      dtype='object')
Test MAE: $126,257
Test R2: 0.6273262157764277 

12 features
Selected names:  Index(['bedrooms', 'bathrooms', 'sqft_living', 'floors', 'view', 'grade',
       'sqft_above', 'sqft_basement', 'yr_renovated', 'lat', 'sqft_living15',
       'rooms'],
      dtype='object')
Test MAE: $125,801
Test R2: 0.6313660283782238 

13 features
Selected names:  Index(['bedrooms', 'bathrooms', 'sqft_living', 'sqft_lot', 'floors', 'view',
       'grade', 'sqft_above', 'sqft_basement', 'yr_renovated', 'lat',
       'sqft_living15', 'rooms'],
      dtype='object')
Test MAE: $125,916
Test R2: 0.6308283765247964 

14 features
Selected names:  Index(['bedrooms', 'bathrooms', 'sqft_living', 'sqft_lot', 'floors', 'view',
       'grade', 'sqft_above', 'sqft_basement', 'yr_renovated', 'lat',
       'sqft_living15', 'sqft_lot15', 'rooms'],
      dtype='object')
Test MAE: $125,920
Test R2: 0.6314307726640978 

15 features
Selected names:  Index(['bedrooms', 'bathrooms', 'sqft_living', 'sqft_lot', 'floors', 'view',
       'grade', 'sqft_above', 'sqft_basement', 'yr_built', 'yr_renovated',
       'lat', 'sqft_living15', 'sqft_lot15', 'rooms'],
      dtype='object')
Test MAE: $119,578
Test R2: 0.6677358410398033 

16 features
Selected names:  Index(['bedrooms', 'bathrooms', 'sqft_living', 'sqft_lot', 'floors', 'view',
       'grade', 'sqft_above', 'sqft_basement', 'yr_built', 'yr_renovated',
       'zipcode', 'lat', 'sqft_living15', 'sqft_lot15', 'rooms'],
      dtype='object')
Test MAE: $119,295
Test R2: 0.6697817823178407 

17 features
Selected names:  Index(['bedrooms', 'bathrooms', 'sqft_living', 'sqft_lot', 'floors', 'view',
       'condition', 'grade', 'sqft_above', 'sqft_basement', 'yr_built',
       'yr_renovated', 'zipcode', 'lat', 'sqft_living15', 'sqft_lot15',
       'rooms'],
      dtype='object')
Test MAE: $118,769
Test R2: 0.671066324876272 

18 features
Selected names:  Index(['bedrooms', 'bathrooms', 'sqft_living', 'sqft_lot', 'floors', 'view',
       'condition', 'grade', 'sqft_above', 'sqft_basement', 'yr_built',
       'yr_renovated', 'zipcode', 'lat', 'long', 'sqft_living15', 'sqft_lot15',
       'rooms'],
      dtype='object')
Test MAE: $118,992
Test R2: 0.6750956927563698 
'''

스크린샷 2021-08-11 15 00 29

  • 15개를 사용했을 때와 18개를 사용했을 때가 거의 차이가 나지 않으므로 코스트(Cost)를 생각하여 15개만 사용하는 것이 좋다.

릿지 회귀(Ridge Regression)

  • 릿지 회귀는 기존 다중회귀선을 훈련데이터에 덜 적합되도록 만든다. 그 결과로 더 좋은 모델이 만들어진다.
\[\beta_{ridge}$: $argmin[\sum_{i=1}^n(y_i - \beta_0 - \beta_1x_{i1}-\dotsc-\beta_px_{ip})^2 + \lambda\sum_{j=1}^p\beta_j^2]\]
  • n: 샘플수, p: 특성수, $\lambda$: 튜닝 파라미터(패널티)
  • alpha, lambda, regularization parameter, penalty term 모두 같은 뜻이다.

릿지 회귀를 사용하는 이유

  • 과적합을 줄이기 위해서 사용한다.
  • 과적합을 줄이는 간단한 방법 중 한 가지는 모델의 복잡도를 줄이는 것이다.
  • 특성의 갯수를 줄이거나 모델을 단순한 모양으로 적합하는 것이 좋다.
  • 편향을 조금 더하고, 분산을 줄이는 방법으로 정규화(Regularization)를 수행한다.
  • 정규화는 모델을 변형하여 과적합을 완화해 일반화 성능을 높여주기 위한 기법을 말한다.
  • 정규화의 강도를 조절해주는 패널티값인 람다는 다음과 같은 성질이 있다.
    • $\lambda$ → 0, $\beta_{ridge}$ → $\beta_{OLS}$
    • $\lambda$ → ∞, $\beta_{ridge}$ → 0.

OLS vs 릿지 회귀

1
2
3
import seaborn as sns
ans = sns.load_dataset('anscombe').query('dataset=="III"')
ans.plot.scatter('x', 'y');

스크린샷 2021-08-11 15 09 35

OLS

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
%matplotlib inline

ax = ans.plot.scatter('x', 'y')


# OLS 
ols = LinearRegression()
ols.fit(ans[['x']], ans['y'])


# 회귀 계수와 intercept 확인
m = ols.coef_[0].round(2)
b = ols.intercept_.round(2)
title = f'Linear Regression \n y = {m}x + {b}'


# 훈련 데이터로 예측
ans['y_pred'] = ols.predict(ans[['x']])


ans.plot('x', 'y_pred', ax=ax, title=title);

스크린샷 2021-08-11 15 11 04

릿지 회귀

  • $\lambda$ 값을 증가시키며 그래프를 통해 회귀 계수의 변화를 살펴본다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import matplotlib.pyplot as plt
from sklearn.linear_model import Ridge

def ridge_anscombe(alpha):
    """
    alpha : lambda, penalty term
    """
    ans = sns.load_dataset('anscombe').query('dataset=="III"')

    ax = ans.plot.scatter('x', 'y')

    ridge = Ridge(alpha=alpha, normalize=True)
    ridge.fit(ans[['x']], ans['y'])

    # 회귀 계수와 intercept
    m = ridge.coef_[0].round(2)
    b = ridge.intercept_.round(2)
    title = f'Ridge Regression, alpha={alpha} \n y = {m}x + {b}'

    # 예측
    ans['y_pred'] = ridge.predict(ans[['x']])

    ans.plot('x', 'y_pred', ax=ax, title=title)
    plt.show()
    

# 여러 알파값으로 반복해 그래프 확인
alphas = np.arange(0, 2, 0.4)
for alpha in alphas:
    ridge_anscombe(alpha=alpha)

스크린샷 2021-08-11 15 13 29

스크린샷 2021-08-11 15 13 57

  • 그래프를 보면, 알파(alpha)가 0인 경우에는 OLS와 같은 그래프 형태로 같은 모델임을 확인 할 수 있고, 알파 값이 커질 수록 직선의 기울기가 0에 가까워지면서 평균 베이스라인 모델(baseline)과 비슷해지는 모습을 볼 수 있다.
  • 이 패널티 값을 보다 효율적으로 구할 수 있는 방법은,
    • 특별한 공식이 있는 것은 아니며, 여러 패널티 값을 가지고 검증 실험을 해보는 방법을 사용한다.
    • 교차 검증(Cross-validation)을 사용해 훈련/검증 데이터를 나누어 검증 실험을 진행하면 된다.

릿지CV(RidgeCV)를 통한 최적 패널티(alpha, lambda) 검증

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from sklearn.linear_model import RidgeCV


alphas = [0.01, 0.05, 0.1, 0.2, 1.0, 10.0, 100.0]


ridge = RidgeCV(alphas=alphas, normalize=True, cv=3)
ridge.fit(ans[['x']], ans['y'])
print("alpha: ", ridge.alpha_)
print("best score: ", ridge.best_score_)
'''
alpha:  0.2
best score:  0.4389766255562206
'''
  • 릿지 회귀 직선의 생김새는 OLS와 매우 비슷하지만 이상치(outlier) 영향을 받는다.
1
2
3
4
5
6
7
8
9
10
11
12
13
ax = ans.plot.scatter('x', 'y')


m = ridge.coef_[0].round(2)
b = ridge.intercept_.round(2)
title = f'Ridge Regression, alpha={ridge.alpha_} \n y = {m}x + {b}'


ans['y_pred'] = ridge.predict(ans[['x']])


ans.plot('x', 'y_pred', ax=ax, title=title)
plt.show()

스크린샷 2021-08-11 15 31 09

집값 데이터에서 확인

  • 집값 예측 데이터로 돌아와서, 릿지 회귀의 패널티에 대한 영향을 확인한다. 특성은 sqft_living을 사용한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# https://scikit-learn.org/stable/modules/generated/sklearn.linear_model.Ridge.html
for alpha in [0.001, 0.01, 0.1, 1.0, 10.0, 100.0]:
    feature = 'sqft_living'
    print(f'Ridge Regression, with alpha={alpha}')
    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)
    print(f'Test MAE: ${mae:,.0f}')


    train.plot.scatter(feature, target, alpha=0.1)
    plt.plot(X_test[feature], y_pred, color='green')
    plt.show()

스크린샷 2021-08-11 15 33 44

스크린샷 2021-08-11 15 34 14

여러 특성 사용 릿지 회귀 학습

스크린샷 2021-08-11 15 37 48

스크린샷 2021-08-11 15 38 36

다항 함수에 릿지 회귀 적용
  • 다수의 특성을 사용하는 다항 함수에 릿지 회귀를 사용하면 정규화 효과를 더 잘 확인할 수 있다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
from sklearn.preprocessing import PolynomialFeatures
from sklearn.pipeline import make_pipeline

def RidgeRegression(degree=3, **kwargs):
    return make_pipeline(PolynomialFeatures(degree), 
                         Ridge(**kwargs))


for alpha in [0.001, 0.01, 0.0025, 0.05, 0.09, 0.12, 0.4, 1.0, 1, 5, 10, 100]:
        
    print(f'Ridge Regression, alpha={alpha}')


    # Ridge 모델 학습
    model = RidgeRegression(alpha=alpha, normalize=True)
    model.fit(X_train, y_train)
    y_pred = model.predict(X_test)


    # MAE for test
    mae = mean_absolute_error(y_test, y_pred)
    r2 = r2_score(y_test, y_pred)
    print(f'Test MAE: ${mae:,.0f}')
    print(f'R2 Score: {r2:,.4f}\n')

coefs = model.named_steps["ridge"].coef_
print(f'Number of Features: {len(coefs)}')
'''
Ridge Regression, alpha=0.001
Test MAE: $115,090
R2 Score: -0.5563

Ridge Regression, alpha=0.01
Test MAE: $112,684
R2 Score: 0.5540

Ridge Regression, alpha=0.0025
Test MAE: $113,995
R2 Score: 0.0050

Ridge Regression, alpha=0.05
Test MAE: $111,764
R2 Score: 0.6929

Ridge Regression, alpha=0.09
Test MAE: $111,875
R2 Score: 0.6943

Ridge Regression, alpha=0.12
Test MAE: $112,023
R2 Score: 0.6936

Ridge Regression, alpha=0.4
Test MAE: $112,310
R2 Score: 0.6928

Ridge Regression, alpha=1.0
Test MAE: $112,566
R2 Score: 0.6914

Ridge Regression, alpha=1
Test MAE: $112,566
R2 Score: 0.6914

Ridge Regression, alpha=5
Test MAE: $116,650
R2 Score: 0.6680

Ridge Regression, alpha=10
Test MAE: $121,811
R2 Score: 0.6442

Ridge Regression, alpha=100
Test MAE: $150,360
R2 Score: 0.5087

Number of Features: 1330
'''

최종 모델

  • 최종 모델을 만들기 위해서는 가지고 있는 데이터를 다 사용해 최적의 모델을 만들어야 한다.
  • 지금 가지고 있는 테스트 데이터를 검증 데이터로 사용하려면 릿지CV에 훈련 데이터로 함께 넣어 주어야 한다.
  • 릿지CV 내에서 훈련 데이터를 훈련/검증 데이터로 나누어 최고 스코어를 가지는 알파를 찾아준다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
X_total = pd.concat([X_train, X_test])
y_total = pd.concat([y_train, y_test])


# 모든 데이터를 사용해 최종 모델 생성
model = RidgeCVRegression(alphas=alphas, normalize=True, cv=5)
model.fit(X_total, y_total)


coefs = model.named_steps["ridgecv"].coef_
print(f'Number of Features: {len(coefs)}')

print(f'alpha: {model.named_steps["ridgecv"].alpha_}')
print(f'cv best score: {model.named_steps["ridgecv"].best_score_}')
'''
Number of Features: 1330
alpha: 0.19
cv best score: 0.7255485997685633
'''

coefs.max(), coefs.mean()
'''
(70325.16065760396, 42.115772616436196)
'''


# 회귀 계수 정렬
coefs.sort()
coefs
'''
array([-17325.27799422,  -1175.83656738,   -919.34030661, ...,
          715.54010685,    719.44854755,  70325.1606576 ])
'''

회귀 계수 그래프

  • 몇몇 중요한 특성들만 회귀 계수가 크고 대부분 0 근처에 있음을 볼 수 있다.
1
plt.plot(coefs)

스크린샷 2021-08-11 15 51 42

0%