728x90
반응형

1. 결정 트리(Decision Tree)

==> 관측값과 목표값을 연결시켜주는 예측 모델로서 나무 모양으로 데이터를 분류

 

==> 수많은 트리 기반 모델의 기본 모델

 

==> 선형 모델은 각 변수에 대한 기울기값들을 최적화하여 모델을 만들어 나감.

 

==> 트리모델에서는 각 변수의 특정 지점을 기준으로 데이터를 분류해가면서 예측모델 만든다.

 

 

예측력 : 모델 학습을 통해 얼마나 좋은 예측치를 보여주는가를 의미

 

설명력 : 학습된 모델을 얼마나 쉽게 해석할 수 있는지를 뜻한다.

 

==> 알고리즘의 복잡도가 증가할 수록 예측도는 증가 , 설명력은 감소

 

ex) 의학 데이터에 대한 질병 발병률에 대한 예측 모델 ==> 설명력이 좋은 알고리즘이 적합

 

 

장점 :

㉠ 데이터에 대한 가정이 없는 모델이다. 선형 모델은 정규분포에 대한 가정이나 독립변수와 종속변수의 선형 관계 등을 가정으로 하는 모델이지만, 결정 트리는 데이터에 대한 가정이 없어서 자유롭게 적용할 수 있다.

 

아웃라이어에 영향을 거의 받지 않는다.

 

㉢ 트리 그래프를 통해서 직관적으로 이해하고 설명할 수 있다.

 

단점 : 

㉠ 트리 기반 모델들에 비하면 예측력이 떨어진다.

 

2. 데이터 확인

file_url = "https://media.githubusercontent.com/media/musthave-ML10/data_source/main/salary.csv"

data = pd.read_csv(file_url , skipinitialspace= True) #빈칸 모두 제거

data.head()

==> skipinitialspace =True ==> 앞에 있는 빈칸 제거하여 갖고오기

data.describe(include= 'all') #object형이 포함된 통계정보 출력

==> object형이 포함된 통게정보 출력

 

object형

==> unique, top , freq 행이 추가되었다. ==> 오직 object형의 변수들만을 위한 것이다. 기존의 숫자형 변수들은 NaN으로 처리되어 있다.

 

unique : 고윳값

top : 각 변수별로 가장 많이 등장하는 value

freq : top에 나와있는 value가 해당 변수에서 총 몇 건인지

 

3. 전처리 : 범주형 데이터

data['class'] = data['class'].map({'<=50K' : 0 , '>50K' : 1}) # 숫자로 변환
obj_list = []
for i in data.columns : #순회
    if data[i].dtype == 'object' : #데이터타입이 object이면
        obj_list.append(i) #리스트에 변수 이름을 추가

obj_list

==> data type이 object 인거 obj_list에 추가

 

['workclass',
 'education',
 'marital-status',
 'occupation',
 'relationship',
 'race',
 'sex',
 'native-country']

 

for i in obj_list:
    if data[i].nunique() >= 10: #변수의 고윳값이 10보다 크거나 같으면
        print(i , data[i].nunique()) #컬럼명과 고윳값 개수 출력

education 16
occupation 14
native-country 41

 

np.sort(data['education-num'].unique()) #고윳값을 오름차순으로 확인
data[data['education-num'] == 1] # 값이 1인 (True)행만 필터링

object에 대한 범주형 데이터 정렬

data[data['education-num'] == 1]['education'].unique()
#education-num이 1인 데이터들의 education 고윳값 확인

 

array([ 'Preschool'] , dtype = object)

 

for i in np.sort(data['education-num'].unique()):
    print(i, data[data['education-num'] ==i]['education'].unique() )

==> education-num이 고윳값별 education의 고윳값 확인

1 [' Preschool']
2 [' 1st-4th']
3 [' 5th-6th']
4 [' 7th-8th']
5 [' 9th']
6 [' 10th']
7 [' 11th']
8 [' 12th']
9 [' HS-grad']
10 [' Some-college']
11 [' Assoc-voc']

12 [' Assoc-acdm']
13 [' Bachelors']
14 [' Masters']
15 [' Prof-school']
16 [' Doctorate']

 

data.groupby('native-country').mean().sort_values('class')

==> 그룹별 평균 계산 후 class 기준으로 오름차순 정렬

 

 

※ 더미 변수를 설명할 때, 범주형 데이터를 무작정 숫자로 치환하여 모델링하는 방법은 좋지 않다.

but. 트리 기반의 모델을 사용 할 때 연속된 숫자들도 연속적으로 받아들이기 보다는 일정 구간을 나누어 받아들인다.

==> 트리가 충분히 깊어지면 범주형 변수를 숫자로 바꾼다고 해도 문제가 없다.

 

country_group = data.groupby('native-country').mean()['class']
country_group=  country_group.reset_index() #인덱스를 변수로 불러냄

country_group

그룹화 한뒤 인덱스를 변수화

4. 결측치 처리 및 더미 변수 변환

 

data.isna().mean() #결측치 비율 확인

결측치 비율

data['native-country'] = data['native-country'].fillna(-99) 
# native-country의 경우 각 국가별 class의 평균값으로 대체한 상황
# 트리 기반 모델에서는 결측치를 임의의 숫자로 채워도 괜찮다.

native-country의 경우 각 국가별 class의 평균값으로 대체한 상황 ==> 트리 기반 모델에서는 결측치를 임의의 숫자로 채워도 괜찮다.

 

data['workclass'] = data['workclass'].fillna('Private')
# 결측치를 Private(특정 값이 대부분을 차지하는 경우라면 해당 값으로 결측치를 채워주는 방법으로 대체)

==> 결측치를 Private(특정 값이 대부분을 차지하는 경우라면 해당 값으로 결측치를 채워주는 방법으로 대체)

 

data['occupation'] = data['occupation'].fillna('Unknown')
# 어떤 특정값이 압도적으로 많다고 하기가 어렵다. 이런 경우에는 별도의 텍스트 'Unknown'으로 채운다.

==> 어떤 특정값이 압도적으로 많다고 하기가 어렵다. 이런 경우에는 별도의 텍스트 'Unknown'으로 채운다.

 

data = pd.get_dummies(data , drop_first=True) #더미 변수로 변환
data

drop_first = True ==> 더미 변수의 갯수를 줄여준다.

 

5. 모델링 및 평가하기

from sklearn.model_selection import train_test_split

X_train , X_test , y_train , y_test = train_test_split(data.drop('class',axis =1 )  , data['class'] , test_size= 0.4 , random_state= 100)

==> 독립변수('class' 열 제외한 모든 열) , 종속변수 ('class') 열 

 

==> X_train : 학습 시킬 독립변수

==> y_train : 학습 시킬 종속변수

 

==> X_test : 예측 대상

 

==> y_test : 실제 정보

 

==> pred : 예측 값

 

from sklearn.tree import DecisionTreeClassifier

model = DecisionTreeClassifier() # 모델 객체 생성
model.fit(X_train , y_train) # 학습
pred = model.predict(X_test) # 예측

from sklearn.metrics import accuracy_score

accuracy_score(y_test , pred) # 정확도 계산

==> 0.8134309259354047

 

6. 분류 결정 트리

 

==> 분류와 회귀는 각기 다른 로직의 적용

 

분류 : DecisionTreeClassifier는 각 노드의 순도가 가장 높은 방향으로 분류한다.

 

순도 : 한 노드 안에 여러 종류가 아닌 한 종류의 목푯값만 있는 상태에 대한 지표이다.

 

ex) 노드 안에 사과 3개와 복숭아 3개가 있으면 두 과일이 반씩 있기 때문에 순도가 낮다. 한 노드 안에 사과 3개와 복숭아 0개가 있다면 순도가 높다고 볼 수 있다.

 

==> 순도를 평가하는 지표로는 지니 인덱스교차 엔트로피가 있다.

 

 

1> 지니 인덱스

 

지니 인덱스 수식

==> 지니 인덱스는 각 노드에 대해서 계산되며, p는 노드 안에 특정 아이템의 비율이다.

 

 

EX-01 ) 사과 2, 복숭아 2 일때 계산

 

==> 각각 50% 이므로 ,  1- (0.5**2 + 0.5**2) = 0.5

 

EX-02 ) 사과 1, 복숭아 3 일때 계산

 

==> 각각 25% , 75% 이므로 ,  1- (0.25**2 + 0.75**2) = 1- 0.625 = 0.375

 

EX-03 ) 사과 0, 복숭아 4 일때 계산

 

==> 각각 0% , 100%이므로, 1 - (0**2 + 1**2) = 0

 

 

==> 순도가 높을수록 지니 인덱스는 낮은 값을 보인다. 

 

==> 결정 트리에서는 지니 인덱스가 가장 낮은 값이 나오는 특정 변수의 특정 값을 기준으로 노드를 분류해 간다.

 

 

2> 교차 엔트로피

 

교차 엔트로피 수식

EX-01 ) 사과 2, 복숭아 2 일때 계산

 

==> 각각 50% 이므로 ,  - ( 0.5*Log_2(0.5)  + 0.5*Log_2(0.5)) = - ( -0.5 - 0.5) = 1

 

EX-02 ) 사과 1, 복숭아 3 일때 계산

 

==> 각각 25% , 75% 이므로 ,  - (0.25*Log_2(0.25)  + 0.75*Log_2(0.75)) = 0.81127

- (0.25*np.log2(0.25)  + 0.75*np.log2(0.75))

 

EX-03 ) 사과 0, 복숭아 4 일때 계산

 

==> 각각 0% , 100%이므로, -(0*log_2(0) + 1* log_2(1) ) = 0

 

==> 순도가 높을수록 교차 엔트로피는 낮은 값을 보인다. ==> 최대값은 1까지 나올 수 있다.

 

==> 사이킷런의 결정 트리에서는 기본값으로 지니 인덱스를 사용한다.

 

7. 매개변수 튜닝

 

오버피팅 : 예측 모델이 훈련 셋을 지나치게 잘 예측한다면 새로운 데이터를 예측 할 때 큰 오차를 유발 할 수 있다.

 

언더피팅 : 모델이 충분히 학습되지 않아 훈련셋에 대해서도 좋은 예측력을 내지 못하는 상황이다.

 

 

==> 결정 트리에서는 트리 깊이가 깊어질 수록, 수없이 많은 노드를 분류하여 모델을 만들수록 오버피팅 발생 가능성이 높다.

 

==> 이 문제를 해결하기 위해 결정 트리에서는 트리의 깊이를 제한하는 매개변수를 제공한다.

 

model = DecisionTreeClassifier()
model.fit(X_train , y_train)
train_pred = model.predict(X_train) #훈련셋 예측
test_pred = model.predict(X_test) #시험셋 예측
print('Train score : ' , accuracy_score(y_train , train_pred) , 'Test score : ' , accuracy_score(y_test , test_pred)) # 훈련셋 , 시험셋의 정확도 평가

Train score :  0.9780242279474493 Test score :  0.8147617341454676

 

==> 훈련셋에서는 정확도가 매우 높지만, 시험셋에서는 상대적으로 낮다. 이 정도의 격차가 발생하면 오버피팅이 발생했다고 할 수 있다.

 

=> 격차를 줄이기 위해 시험셋에서 정확도를 올리는 방향으로 매개변수를 설정한다.

 

model = DecisionTreeClassifier(max_depth= 5) #모델 객체 생성
model.fit(X_train , y_train) #학습
train_pred = model.predict(X_train) #훈련 셋 예측
test_pred = model.predict(X_test) # 시험 셋 예측
print('Train score : ' , accuracy_score(y_train , train_pred) , 'Test score : ' , accuracy_score(y_test , test_pred)) # 훈련셋 , 시험셋의 정확도 평가

Train score :  0.8540180856509129 Test score :  0.8499769667809797

 

==> 이전보다 Train score 값이 낮 아졌다. ==> 둘 사이의 차이는 매우 낮아졌다.

model = DecisionTreeClassifier(max_depth= 7) #모델 객체 생성
model.fit(X_train , y_train) #학습
train_pred = model.predict(X_train) #훈련 셋 예측
test_pred = model.predict(X_test) # 시험 셋 예측
print('Train score : ' , accuracy_score(y_train , train_pred) , 'Test score : ' , accuracy_score(y_test , test_pred)) # 훈련셋 , 시험셋의 정확도 평가

Train score :  0.8598532673605187 Test score :  0.8541741311357937

 

==> 이상적인 트리의 깊이를 지정하여 오버피팅을 낮추는 노력을 해야한다.

 

8. 트리 그래프

from sklearn.tree import plot_tree

fig = plt.figure(figsize =  (15,8)) # 그래프 크기 설정

ax = plot_tree(model) # 트리 그래프 출력

plt.show()

트리

==> 이 모델은 마지막에 훈련시킨 max_depth 7의 결과물이다.

 

from sklearn.tree import plot_tree

fig = plt.figure(figsize =  (30,15)) # 그래프 크기 설정

ax = plot_tree(model , max_depth= 3 , fontsize = 15) # 트리 그래프 출력

plt.show()

깊이 3인 트리

from sklearn.tree import plot_tree

fig = plt.figure(figsize =  (30,15)) # 그래프 크기 설정

ax = plot_tree(model , max_depth= 3 , fontsize = 15 , feature_names= X_train.columns) # 트리 그래프 출력

plt.show()

feature_names 매개변수에 변수 이름이 담긴 리스트 입력한 트리

==> 첫 번째 노드에는 분류 기준, 지니 인덱스(gini) , 총 데이터 수(samples), value는 목표값 0과 1이 각각 몇 개씩인지를 표현

 

==> Married-civ-spouse <=  0.5 기준으로 나누었다. ==> 조건에 맞으면 왼쪽 , 아니면 오른쪽 분류

 

==> 두 번째 노드의 gini 인덱스는 0.12로 나누어졌다.(순도가 높아졌다.) ==> value값(종속변수, class)을 보면 0인 경우가 훨씬 많도록 분류 되었다.

 

출처 : 데싸노트의 실전에서 통하는 머신러닝

(Golden Rabbit , 저자 : 권시현)

※혼자 공부용

728x90
반응형

+ Recent posts