[Tree Based Model] 결정트리(Decision Trees)

개요

  • 결정트리모델
    • 결정트리 모델은 특성을 해석하기 좋다.
    • 다른 모델보다 성능이 조금 떨어진다고 하더라도 해석하기가 좋아 많이 쓰인다.
    • 물론 샘플에 민감해서 특이구조가 바뀌기 쉽다는 단점도 있는데 그러면 해석도 바뀌게 된다.
    • 또 하나의 장점은 회귀나 분류문제에 적용이 가능하다.
    • 다음에 배울 앙상블 기법에 기초가 된다.

실습 전 전처리

  • 2009년 말에서 2010년 초까지 H1N1 독감관련된 설문조사
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
import pandas as pd
from sklearn.model_selection import train_test_split

# 데이터 import
target = 'vacc_h1n1_f'
# target = 'vacc_seas_f'
train = pd.merge(pd.read_csv('https://ds-lecture-data.s3.ap-northeast-2.amazonaws.com/vacc_flu/train.csv'), 
                 pd.read_csv('https://ds-lecture-data.s3.ap-northeast-2.amazonaws.com/vacc_flu/train_labels.csv')[target], left_index=True, right_index=True)
test = pd.read_csv('https://ds-lecture-data.s3.ap-northeast-2.amazonaws.com/vacc_flu/test.csv')
sample_submission = pd.read_csv('https://ds-lecture-data.s3.ap-northeast-2.amazonaws.com/vacc_flu/submission.csv')

# 훈련/검증 나누기
train, val = train_test_split(train, train_size=0.80, test_size=0.20, 
                              stratify=train[target], random_state=2)


train.shape, val.shape, test.shape
# ((33723, 39), (8431, 39), (28104, 38))

# 타겟의 비율
train[target].value_counts(normalize=True)
'''
0    0.760935
1    0.239065
Name: vacc_h1n1_f, dtype: float64

클래스가 2개(0, 1)인 분류 문제이다.
가장 큰 범주(0)가 76.15%
클래스가 불균형(imbalanced)한 분류 문제
'''

# 중복된 특성 확인
train.T.duplicated()
'''
h1n1_concern                   False
h1n1_knowledge                 False
behavioral_antiviral_meds      False
behavioral_avoidance           False
behavioral_face_mask           False
behavioral_wash_hands          False
behavioral_large_gatherings    False
behavioral_outside_home        False
behavioral_touch_face          False
doctor_recc_h1n1               False
doctor_recc_seasonal           False
chronic_med_condition          False
child_under_6_months           False
health_insurance               False
health_worker                  False
opinion_h1n1_vacc_effective    False
opinion_h1n1_risk              False
opinion_h1n1_sick_from_vacc    False
opinion_seas_vacc_effective    False
opinion_seas_risk              False
opinion_seas_sick_from_vacc    False
agegrp                         False
education_comp                 False
raceeth4_i                     False
sex_i                          False
inc_pov                        False
marital                        False
rent_own_r                     False
employment_status              False
census_region                  False
census_msa                     False
n_adult_r                      False
household_children             False
n_people_r                     False
employment_industry            False
employment_occupation          False
hhs_region                     False
state                          False
vacc_h1n1_f                    False
dtype: bool
'''

# 카디널리티 확인
train.describe(exclude='number').T.sort_values(by='unique')

스크린샷 2021-08-17 10 22 16

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
# 카테고리 많이 가지는 특성들의 범주 확인
train['employment_occupation'].value_counts()
'''
Management Occupations                                       1769
Office and Administrative Support Occupations                1556
Education, Training, and Library Occupations                 1286
Healthcare Practitioners and Technical Occupations           1200
Sales and Related Occupations                                1108
Business and Financial Operations Occupations                 764
Construction and Extraction Occupations                       538
Production Occupations                                        483
Transportation and Material Moving Occupations                483
Computer and Mathematical Occupations                         475
Food Preparation and Serving Related Occupations              405
Architecture and Engineering Occupations                      367
Arts, Design, Entertainment, Sports and Media Occupations     366
Personal Care and Service Occupations                         353
Community and Social Services Occupations                     335
Building and Grounds Cleaning and Maintenance Occupations     330
Installation, Maintenance, and Repair Occupations             299
Healthcare Support Occupations                                294
Not ascertained                                               268
Legal Occupations                                             242
Life, Physical, and Social Science Occupations                236
Protective Service Occupations                                229
Military Specific Occupations                                 158
Farming, Fishing, and Forestry Occupations                    106
Refused, classified                                            64
Name: employment_occupation, dtype: int64
'''

# 특성 엔지니어링
import numpy as np

def engineer(df):
    """특성을 엔지니어링 하는 함수입니다."""
    
    # 높은 카디널리티를 가지는 특성 제거
    selected_cols = df.select_dtypes(include=['number', 'object'])
    labels = selected_cols.nunique() # 특성별 카디널리티 리스트
    selected_features = labels[labels <= 30].index.tolist() # 카디널리티가 30보다 작은 특성만 선택
    df = df[selected_features]
    
    # 새로운 특성 생성
    behaviorals = [col for col in df.columns if 'behavioral' in col] 
    df['behaviorals'] = df[behaviorals].sum(axis=1)
    
    
    dels = [col for col in df.columns if ('employment' in col or 'seas' in col)]
    df.drop(columns=dels, inplace=True)
        
    return df


train = engineer(train)
val = engineer(val)
test = engineer(test)

# 타겟과 특성 분리
features = train.drop(columns=[target]).columns

# 훈련/검증/테스트 데이터를 특성과 타겟으로 분리
X_train = train[features]
y_train = train[target]
X_val = val[features]
y_val = val[target]
X_test = test[features]

사이킷럿 파이프라인(Pipelines)

  • 결측치 처리, 스케일링, 모델학습 등 머신러닝 프로세스에서 파이프라인(Pipelines)을 사용하면 중복 코드를 최소화하여 쉽게 연결할 수 있다.
  • 여러 ML 모델을 같은 전처리 프로세스에 연결시킬 수 있다.
  • 그리드서치(grid search)(?)를 통해 여러 하이퍼파라미터를 쉽게 연결할 수 있다.
    • 그리드서치 : 관심 있는 매개변수들을 대상으로 가능한 모든 조합을 시도하여 최적의 매개변수를 찾는 방법
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
from category_encoders import OneHotEncoder
from sklearn.impute import SimpleImputer
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegression
from sklearn.pipeline import make_pipeline

enc = OneHotEncoder()
imp_mean = SimpleImputer()
scaler = StandardScaler()
model_lr = LogisticRegression(n_jobs=-1)

X_train_encoded = enc.fit_transform(X_train)
X_train_imputed = imp_mean.fit_transform(X_train_encoded)
X_train_scaled = scaler.fit_transform(X_train_imputed)
model_lr.fit(X_train_scaled, y_train)

X_val_encoded = enc.transform(X_val)
X_val_imputed = imp_mean.transform(X_val_encoded)
X_val_scaled = scaler.transform(X_val_imputed)

# score method: Return the mean accuracy on the given test data and labels
print('검증세트 정확도', model_lr.score(X_val_scaled, y_val))

X_test_encoded = enc.transform(X_test)
X_test_imputed = imp_mean.transform(X_test_encoded)
X_test_scaled = scaler.transform(X_test_imputed)

y_pred = model_lr.predict(X_test_scaled)

# 검증세트 정확도 0.8185268651405527
  • 이랬던 코드가 파이프라인을 사용하면
1
2
3
4
5
6
7
8
9
10
11
12
13
pipe = make_pipeline(
    OneHotEncoder(), 
    SimpleImputer(), 
    StandardScaler(), 
    LogisticRegression(n_jobs=-1)
)
pipe.fit(X_train, y_train)

print('검증세트 정확도', pipe.score(X_val, y_val))

y_pred = pipe.predict(X_test)

# 검증세트 정확도 0.8185268651405527
  • 이렇게 코드가 간결해지고 가독성이 증가한다.

파이프라인에서 모델의 파라미터 등과 같은 정보 확인하는 방식

  • named_steps 속성을 사용해서 파이프라인의 각 스텝에 접근이 가능하다.
    • 유사 딕셔너리 객체(dictionary-like object)로 파이프라인 내 과정에 접근 가능하도록 한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
pipe.named_steps
'''
{'onehotencoder': OneHotEncoder(cols=['opinion_h1n1_vacc_effective', 'opinion_h1n1_risk',
                     'opinion_h1n1_sick_from_vacc', 'agegrp', 'census_msa']),
 'simpleimputer': SimpleImputer(),
 'standardscaler': StandardScaler(),
 'logisticregression': LogisticRegression(n_jobs=-1)}
'''

import matplotlib.pyplot as plt

model_lr = pipe.named_steps['logisticregression']
enc = pipe.named_steps['onehotencoder']
encoded_columns = enc.transform(X_val).columns
coefficients = pd.Series(model_lr.coef_[0], encoded_columns)
plt.figure(figsize=(10,30))
coefficients.sort_values().plot.barh();

스크린샷 2021-08-17 10 27 15

결정트리(Decision Tree)

  • 결정트리모델은 특성들을 기준으로 샘플을 분류해 나가는데 그 형태가 나무의 가지가 뻗어나가는 모습과 비슷해서 결정트리라는 이름을 갖고 있다.
  • 특성들의 수치를 가지고 질문을 통해 정답 클래스를 찾아는 과정이다.
  • 질문이나 말단의 정답을 노드(node)라 하며 노드를 연결하는 선을 엣지(edge)라 한다.

스크린샷 2021-08-17 10 30 47

  • 결정트리의 각 노드(node)는 뿌리(root)노드, 중간(internal)노드, 말단(external, leaf, terminal) 노드로 나뉜다.
  • 결정트리는 분류와 회귀문제 모두 적용이 가능하다.
  • 결정트리는 데이터를 분할해가는 알고리즘이다.
  • 분류 과정은 새로운 데이터가 특정 말단 노드에 속한다는 정보를 확인한 뒤 말단노드의 빈도가 가장 높은 범주로 데이터를 분류한다.

스크린샷 2021-08-17 10 32 26

  • 결정트리는 분류과정을 트리구조로 직관적으로 확인이 가능하다.
  • 여러 트리모델을 사용하는 앙상블(ensemble) 기법인 랜덤포레스트(Random Forests)와 그레디언트부스팅트리(Gradint Boosted Trees) 모델을 학습할 때 결정트리가 기초가 된다.

결정트리 학습 알고리즘

  • 결정트리를 학습하는 것은 노드를 어떻게 분할하는가에 대한 문제이다.
  • 노드 분할 방법에 따라 다른 모양의 트리구조가 만들어지게 될 것이다.
  • 결정트리의 비용함수를 정의하고 그것을 최소화 하도록 분할하는 것이 트리모델 학습 알고리즘이 된다.
  • 트리학습에 자주 쓰이는 비용함수 중 지니불순도와 엔트로피가 있다.

지니 불순도와 엔트로피

지니 불순도(Gini Impurity or Gini Index)

\[{\displaystyle {I}_{G}(p)=\sum _{i=1}^{J}p_{i}(1-p_{i})=1-\sum _{i=1}^{J}{p_{i}}^{2}}\]
  • 트리 한 노드의 모든 샘플이 같은 클래스에 속해있을 때 값이 0이 되고 이 때를 순수하다고 말하는데 범주들이 섞여있을수록 이 수치가 커지게 되어있다.

엔트로피(Entropy)

\[{\displaystyle \mathrm {H} (T)=\operatorname {I} _{E}\left(p_{1},p_{2},...,p_{J}\right)=-\sum _{i=1}^{J}{p_{i}\log _{2}p_{i}}}\]
  • 원래 열역학에서 쓰이는 표현인데 지니 불순도와 같이 한 노드에서 모든 샘플이 같은 클래스일 때 0이 된다.
  • 싸이킷런에서 트리모델을 학습할 때 디폴트로 지니 불순도를 많이 쓰지만 따로 엔트로피를 계산하도록 설정할 수 있다.
  • 엔트로피가 조금 더 균형잡힌 트리를 만든다고는 하지만 지니 불순도가 계산이 조금 더 빠르기 때문에 디폴트로 지니 불순도를 사용한다.
  • 결정트리는 분류과정을 직관적으로 확인할 수 있다.
  • 여기서 불순도(impurity) 라는 개념은 여러 범주가 섞여 있는 정도를 이야기한다.
  • 예를들어 A, B 두 클래스가 혼합된 데이터가 있을 때 (A, B) 비율이
    • (45%, 55%)인 샘플(두 범주 수가 비슷)은 불순도가 높은 것
    • (80%, 20%)인 샘플이 있다면 상대적으로 위의 상태보다 불순도가 낮은 것(순수도(purity)는 높음)

스크린샷 2021-08-17 12 39 41

  • 두 불순도의 차이값을 불순도의 감소값이라고 하는데 이 값을 최대한으로 할 수 있는 특성과 분할점을 선택해서 데이터로 트리구조를 만드는 것이 트리학습 알고리즘이다.
  • 지니불순도나 엔트로피는 위의 불순도 개념에서 보면 된다.
  • 불순도가 낮은 경우 지니불순도나 엔트로피는 낮은 값을 가지게 된다.
  • 노드를 분할하는 시점에서 가장 비용함수를 줄이는 분할특성과 분할지점을 찾아내는 프로세스가 필요하다
  • 불순도의 감소정도를 엔트로피를 사용하면 정보획득이라고도 말을 한다. 즉, 불순도 감소가 크면 정보획득이 크다.
  • 분할에 사용할 특성이나 분할지점(값)은 타겟변수를 가장 잘 구별해 주는(불순도의 감소가 최대가 되는, 정보획득이 가장 큰)것을 선택한다.
  • 정보획득(Information Gain)은 특정한 특성을 사용해 분할했을 때 엔트로피의 감소량을 뜻한다.
    • ${\displaystyle IG(T,a)=\mathrm {H} {(T)}-\mathrm {H} {(T \vert a)}}$ = 분할 전 노드 불순도 - 분할 후 자식노드들의 불순도

스크린샷 2021-08-17 12 46 53

사이킷런 사용 결정트리 구현

  • 파이프라인을 사용하면 위에서 본 코드에서 단wl 분류기만 바꾸어주면 된다.
  • 결정트리에서는 StandardScaler는 도움이 되지 않기 때문에 제외하고 학습한다.
    • 이유 : 하나의 피쳐에서만 구분을 하기 때문에(?)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from sklearn.tree import DecisionTreeClassifier

# 결정트리의 장점 중 한가지는 이전에 배운 회귀모델에 비해서 데이터 전처리 과정중 덜 신경써도 되는 점들이 있다.
# 예를 들어 데이터의 특성들의 스케일을 맞춰줄 필요가 없다.

pipe = make_pipeline(
    OneHotEncoder(use_cat_names=True),  
    SimpleImputer(), 
    DecisionTreeClassifier(random_state=1, criterion='entropy')
)

pipe.fit(X_train, y_train)
print('훈련 정확도: ', pipe.score(X_train, y_train))
print('검증 정확도: ', pipe.score(X_val, y_val))
# 훈련 정확도:  0.9908667674880646
# 검증 정확도:  0.7572055509429486

y_val.value_counts(normalize=True)
'''
0    0.761001
1    0.238999
Name: vacc_h1n1_f, dtype: float64
'''
  • 학습데이터는 99%이상 맞추는데 검증세트의 정확도는 다수범주(0)의 비율과 같게 나온다.
  • 확실한 과적함임을 확인할 수 있다. 대부분 0으로 예측했다.
  • 다수 범주로 예측하는 모델이 기준모델이라고 보면, 기준모델과 동일한 성능이 나오므로 학습이 잘 되었다고 판단할 수 없다.

만들어진 트리 확인

  • 거대한 트리는 한 눈에 관찰하기 어렵기 때문에 depth 제한을 3으로 두고 결정트리를 그린다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# graphviz 설치방법: conda install -c conda-forge python-graphviz
import graphviz
from sklearn.tree import export_graphviz

model_dt = pipe.named_steps['decisiontreeclassifier']
enc = pipe.named_steps['onehotencoder']
encoded_columns = enc.transform(X_val).columns

dot_data = export_graphviz(model_dt
                          , max_depth=3
                          , feature_names=encoded_columns
                          , class_names=['no', 'yes']
                          , filled=True
                          , proportion=True)


display(graphviz.Source(dot_data))

스크린샷 2021-08-17 12 54 44

과적합 해결

  • 복잡한 트리는 과적합 가능성을 높이기 때문에 복잡도를 낮추어 일반화를 유도한다.
  • 트리의 복잡도를 줄이기위해 자주 사용하는 하이퍼파라미터들
    • min_samples_split
    • min_samples_leaf
    • max_depth
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
# min_samples_leaf를 사용해 말단 노드(external node)에 최소한 존재해야 하는 샘플들의 수를 정한다.
pipe = make_pipeline(
    OneHotEncoder(use_cat_names=True), 
    SimpleImputer(), 
    DecisionTreeClassifier(min_samples_leaf=10, random_state=2)
)

pipe.fit(X_train, y_train)
print('훈련 정확도', pipe.score(X_train, y_train))
print('검증 정확도', pipe.score(X_val, y_val))
# 훈련 정확도 0.8577528689618361
# 검증 정확도 0.8029889692800379

# 비슷한 방법으로 max_depth 제한
pipe = make_pipeline(
    OneHotEncoder(use_cat_names=True), 
    SimpleImputer(), 
    DecisionTreeClassifier(max_depth=6, random_state=2)
)

pipe.fit(X_train, y_train)
print('훈련 정확도', pipe.score(X_train, y_train))
print('검증 정확도', pipe.score(X_val, y_val))
# 훈련 정확도 0.8283367434688491
# 검증 정확도 0.8269481674771676

특성 중요도(Feature importance)

  • 선형모델에서 회귀 계수를 사용하였다면, 결정트리에서는 대신 특성 중요도를 확인한다.
  • 회귀계수와 달리 특성중요도는 항상 양수값을 가진다.
  • 이 값을 통해 특성이 얼마나 일찍 그리고 자주 분기에 사용되는지 결정된다.
1
2
3
4
5
model_dt = pipe.named_steps['decisiontreeclassifier']

importances = pd.Series(model_dt.feature_importances_, encoded_columns)
plt.figure(figsize=(10,30))
importances.sort_values().plot.barh();

스크린샷 2021-08-17 12 59 47

  • 결정트리모델은 선형모델과 달리 비선형, 비단조(Non-monotonic), 특성상호작용(Feature interactions) 특징을 가지고 있는 데이터 분석에 용이하다.

스크린샷 2021-08-17 13 00 57

  • 단조(Monotonic), 비단조(Non-monotonic) 함수(?)
    • 단조함수 : 주어진 순서를 보존하는 함수이다. 기하학적으로, 실수 단조 함수의 그래프는 왼쪽에서 오른쪽으로 줄곧 상승하거나 줄곧 하강한다. 대수학적으로, 단조 함수는 두 순서 집합 사이의 준동형이다.
  • 특성상호작용
    • 특성들끼리 서로 상호작용을 하는 경우를 말한다.
    • 회귀분석에서는 서로 상호작용이 높은 특성들이 있으면 개별 계수를 해석하는데 어려움이 있고 학습이 올바르게 되지 않을 수 있다. 하지만 트리모델은 이런 상호작용을 자동으로 걸러내는 특징이 있다.

특성상호작용(Feature Interaction)

  • 예제 데이터로 특성상호작용을 만들어 선형회귀모델과 트리모델의 차이를 확인
    • 기본가격 : 150,000
    • Location : good일 때 +50,000
    • Size : big일 때 +100,000

스크린샷 2021-08-17 13 08 00

  • good and big인 경우 +100,000 규칙 추가(특성상호작용)

스크린샷 2021-08-17 13 08 43

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
cols = ['location','size','price']
# location: 1:good, 0:bad
# size: 1:big, 0:small
# big은 small보다 100,000 비싸고, good은 bad보다 50,000 가격이 더 나갑니다.
features = [[1, 1], 
            [1, 0], 
            [0, 1], 
            [0, 0]]

price = [[300000], 
        [200000], 
        [250000], 
        [150000]]

X_house = pd.DataFrame(columns=cols[:2], data=features)
y_house = pd.DataFrame(columns=[cols[2]], data=price)

선형회귀

1
2
3
4
5
6
7
8
9
10
11
12
from sklearn.linear_model import LinearRegression
linear = LinearRegression()
linear.fit(X_house, y_house)
print('R2: ', linear.score(X_house, y_house))
print('Intercept: ', linear.intercept_[0])
print('Coefficients')
pd.DataFrame(columns=cols[:2], data=linear.coef_)
'''
R2:  1.0
Intercept:  150000.0
Coefficients
'''

스크린샷 2021-08-17 13 10 59

회귀트리

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import graphviz
## jupyterlab 사용시: jupyter labextension install @jupyter-widgets/jupyterlab-manager
from ipywidgets import interact
from sklearn.tree import DecisionTreeRegressor, export_graphviz

# 트리구조 그리는 함수
def show_tree(tree, colnames):
    dot = export_graphviz(tree, feature_names=colnames, filled=True, rounded=True)   
    return graphviz.Source(dot)

from sklearn.tree import DecisionTreeRegressor
tree = DecisionTreeRegressor(criterion="mae")
tree.fit(X_house, y_house)
print('R2', tree.score(X_house, y_house))
show_tree(tree, colnames=X_house.columns)
# R2 1.0

스크린샷 2021-08-17 13 12 10

데이터에 특성상호작용이 존재하도록 수정

1
2
y_house.loc[0, 'price'] = 400000
y_house

스크린샷 2021-08-17 13 14 46

선형회귀

1
2
3
4
5
6
7
8
9
10
11
linear = LinearRegression()
linear.fit(X_house, y_house)
print('R2: ', linear.score(X_house, y_house))
print('Intercept: ', linear.intercept_[0])
print('Coefficients')
pd.DataFrame(columns=cols[:2], data=linear.coef_)
'''
R2:  0.9285714285714286
Intercept:  125000.00000000003
Coefficients
'''

스크린샷 2021-08-17 13 15 50

회귀트리

1
2
3
4
5
tree = DecisionTreeRegressor(criterion="mae")
tree.fit(X_house, y_house)
print('R2', tree.score(X_house, y_house))
show_tree(tree, colnames=X_house.columns)
# R2 1.0

스크린샷 2021-08-17 13 16 28

  • 트리모델은 선형회귀모델과 달리 특성상호작용에도 문제없이 가격을 예측한다.

결정트리를 비선형 회귀문제에 적용

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
columns = ['mobility', 'density']
data = [[80.574, -3.067]
,[84.248, -2.981]
,[87.264, -2.921]
,[87.195, -2.912]
,[89.076, -2.84]
,[89.608, -2.797]
,[89.868, -2.702]
,[90.101, -2.699]
,[92.405, -2.633]
,[95.854, -2.481]
,[100.696, -2.363]
,[101.06, -2.322]
,[401.672, -1.501]
,[390.724, -1.46]
,[567.534, -1.274]
,[635.316, -1.212]
,[733.054, -1.1]
,[759.087, -1.046]
,[894.206, -0.915]
,[990.785, -0.714]
,[1090.109, -0.566]
,[1080.914, -0.545]
,[1122.643, -0.4]
,[1178.351, -0.309]
,[1260.531, -0.109]
,[1273.514, -0.103]
,[1288.339, 0.01]
,[1327.543, 0.119]
,[1353.863, 0.377]
,[1414.509, 0.79]
,[1425.208, 0.963]
,[1421.384, 1.006]
,[1442.962, 1.115]
,[1464.35, 1.572]
,[1468.705, 1.841]
,[1447.894, 2.047]
,[1457.628, 2.2]]

thurber = pd.DataFrame(columns=columns, data=data)

# 시각화
thurber.plot('mobility', 'density', kind='scatter', title='Thurber');

스크린샷 2021-08-17 13 17 56

선형회귀

1
2
3
4
5
6
7
8
X_thurber = thurber[['mobility']]
y_thurber = thurber['density']
linear = LinearRegression()
linear.fit(X_thurber, y_thurber)
print('R2: ', linear.score(X_thurber, y_thurber))
ax = thurber.plot('mobility', 'density', kind='scatter', title='Thurber')
ax.plot(X_thurber, linear.predict(X_thurber));
# R2:  0.9210137417351627

스크린샷 2021-08-17 13 19 20

트리회귀

1
2
3
4
5
6
7
8
9
10
11
12
13
from ipywidgets import interact
from sklearn.tree import DecisionTreeRegressor, export_graphviz

def thurber_tree(max_depth=1):
    tree = DecisionTreeRegressor(max_depth=max_depth)
    tree.fit(X_thurber, y_thurber)
    print('R2: ', tree.score(X_thurber, y_thurber))
    ax = thurber.plot('mobility', 'density', kind='scatter', title='Thuber')
    ax.step(X_thurber, tree.predict(X_thurber), where='mid')
    plt.show()
    display(show_tree(tree, colnames=['mobility']))

interact(thurber_tree, max_depth=(1,6,1));

스크린샷 2021-08-17 13 21 56

스크린샷 2021-08-17 13 22 05

  • max_depth = 1인 경우는 선형회귀 보다 성능이 안 좋아 보이지만 max_depth를 더할 수록 선에 적합이 되어 비선형 데이터를 학습할 수 있음을 시각적으로 확인할 수 있다.
0%