본문 바로가기

Hello MLop/ML

MLop_ML_DecisionTree_급여 예측 분류

22021110

23일차

 


오늘 포스팅할 내용은 DecisionTree, 결정트리이다.

불러온 데이터는 kaggleSalary Prediction Classification (급여 예측 분류) 이다.

 

간단한 데이터세트 정보이다.

 

 


DecisionTree, 결정트리?

 

💡사전적 의미

의사 결정 규칙과 그 결과들을 트리 구조로 도식화한 의사 결정 지원 도구의 일종.

 

쉽게 풀이하자면,

 

분류해서 정리되는 과정이다.

 

이사를 예로 들면 좋을것 같은데

 

이사는 보통 현재 집의 물건들을 모두 패키징해서

새로운 집에 다시 정리하는 과정인데

 

이 과정이 사실 결정트리와 같다고 생각한다.

 

기존 집의 모든 나의 물건들을

새로운 집에 정리할 때 가장 먼저 하는 분류는 무었인가?

바로 위치이다.

 

거실이야? 안방이야? 주방이야? 화장실이야? 방1이야? 방2야?

 

거실이면 여기, 안방이면 저기, ....

 

안방에서도 옷이면 여기.

가전제품이면 여기.

책이면 여기.

 

이렇게 특정 기준(질문)에 따라 데이터가 구분되고 다음 행동을 하는 모델을

DecisionTree결정트리라고 한다.

 

따라서 맨 위(Root Node)에서부터 맨 아래(Terminal Node,Leaf Node)까지 도달하는 과정이

나무를 뒤집어 놓은 모양과 닮았다 하여 트리라는 단어가 붙는다.

 

💡

노드(Node) : 결정 트리에서 질문이나 정답을 담은 네모 상자

Root Node : 처음 분류 기준 (즉, 첫 질문)

Terminal Node or Leaf Node : 마지막 노드

 


이제 데이터를 불러오자.

 


 

.info()로 데이터 타입을 확인해 보니

결측치가 존재하며, 데이터 타입object(범주형 변수)int(연속형 변수)가 섞여 있는것을 확인했다.

 


.describe()로 확인해보니

연속형 변수는 위의 주석과 같은 컬럼들이 있었고

.describe(include=['O'])로 확인해서

범주형 변수 또한 주석과 같은 컬럼들이 존재한다는것을 인지했다.

 

 


그럼 이제 범주형 변수부터 전처리 해보자.

 

 

우리가 분류하고자 하는 기준 'class'컬럼은 

현재 <=50K, >50K로 데이터가 저장되어있기에 

<=50K0으로

>50K1로 변환하여 저장하자.

 


 

위와 같이 .map({'<=50K'0'>50K'1})를 사용해

바꿔 주었다.

 


 

새로운 리스트 obj_list를 만들고

반복문리스트 컴프리핸션을 통해 남은 object타입컬럼명을 뽑는 코드이다.

 


이제 만든 obj_list에 들어있는 컬럼명들을

순서대로 컬럼명과 해당 컬럼의 nunique()를 출력하는 반복문이다.

 


위의 출력을 보고 우리는 결정을 해야한다.

 

연속형 변수라면 values평균이나 최빈값을 넣어줄 수 있지만

범주형 변수이기 때문에 힘들고

 

drop을 하는 방법도 있고

 

전부다 .get_dummies로 처리할 수 있다.

 


 

하지만 결정을 하는데 모든 colume의 기준들이 필요하지 않기 때문에

시간상 valuesnunique의 개수10개 이상인 column들만 전처리 하기로 결정했다.

 

그래서 위와 같은 반복문을 짜서

nunique의 개수 10개 이상인 column들만 출력했다.

 


'education'컬럼.value_counts() 하니위에서 보았던 16개의 종류가 나왔다.

 

이를 어찌 처리해야하나 고민하다가'education-num' 컬럼매칭이 되지 않을까란 생각이 들었다.

 

 

 

그래서 잠시 'education-num' 컬럼을 불러와서 출력해보고

 


위와 같이

반복문을 통해 두개의 컬럼들 모두 16개의 values를 가지고 있기에

range(1,17)을 잡고

 

f스트링 뒤의 코드를 해석하자면

df[df['education-num'] == n]['education'].unique()

# 잘라서 보면 이해 하기 쉽다.

 

n =1 이 들어왔을 때

1. print( df[df['education-num'] == 1] )

 

 

➡️ 'education-num'컬럼의 value1전체 df 데이터 출력

 


2. print( df[df['education-num'] == 1]['education'] )

 

➡️ 'education-num'컬럼의 value 1'education' 컬럼의 value 출력

 


3. print( df[df['education-num'] == 1]['education'].unique() )

 

 

➡️ 'education-num'컬럼의 value 1 'education' 컬럼의 value종류 출력

 


결론 n=1~16일때 'education-num'컬럼의 value 1~16 'education' 컬럼의 value  종류 출력

 

각각의 'education-num'의 value들'education'을 하나 씩 배정 받았다 확인.


이제 'education-num' 과 'education'의 관계를 알았으니

'education'컬럼은 삭제(.drop())해 주자.

 

 


 

'occupation' 컬럼의 경우

직업이라는 특성이 있고 이미 잘 묶여 있어서 패스했다.

 


'native-country' 컬럼의 경우

미국이 가장 많았다.

 

여기서도 여러 선택지가 있었지만

나라별 연봉 평균 으로 바꾸기로 했다.

 


 

.groupby를 통해

나라별 연봉 평균을 구하고

 

.sort_values(ascending=False)를 넣어

순위 확인을 위해 정리했다.

 

 

그럼 이제 이 데이터를 원래 df에 합쳐보자.

 


country_group 변수에위에서 구한 데이터를 저장하고

 


이 데이터의 인덱스를 확인했더니 원본 df의 인덱스와 다르다.

 

.reset_index()를 해서 다시 저장해준다.

원본 데이터의 index를 함부로 손대면 안되는 이유, index 통일의 중요성 

 


 

.merge를 통해 원본 dfcountry_group을 합쳐준다.

 

💡 df = df.merge(country_groupon = 'native-country'how='left')

country_group

뭐와 합칠거야? = country_group

 

on = 'native-country'

기준이 뭐야? = 컬럼 'native-country'

on이 안들어가면 on=none이므로 두 데이터의 공통 열이름(id)을 기준으로 inner(교집합) 조인하게 된다.

 

how='left'

어떻게 합칠건데? = country_gorup의 고유값을 기준으로

 


그럼 이제 'country_class_mean' 컬럼도 없애주자

 

 

 

 

위의 class_x와 class_y는

 

merge당시 각 데이터 프레임의 class컬럼이름은 같지만

value들은 달라서 구분해서 합쳐진건데

 

이 또한 컬럼명을 바꿔서 구분을 해주도록 하자.

 

 


이제 범주형 데이터의 결.측.치를 처리해보자.

 

 

 

현재 'workclass' , 'occupation', 'native-country' 에 결측치가 존재하는것을 확인 할 수 있다.

 

 


평균이 가장 적었던 'native-country' 컬럼에는

임의의 값 99를 넣어 결측치 처리를 해주었다.

 

 


 

'workclass'컬럼의 경우 values의 평균을 구해 가장 높은

Private로 결측치를 채워줬다.

 


'occupation' 컬럼의 경우

정말 손댈 이유가 전 혀 없기에

결측치를 Unknown으로 채워 줬다.

 

마지막으로 결측치 확인을 다시하고

범주형 데이터전처리가 모두 끝이 났다는것을 확인했다.

 


이제 정리한 범주형 데이터들을

get_dummies를 활용해 연속형 데이터로 바꿔준다.



 

훈련셋과 시험셋을 분리해주고

사이키런.tree 라이브러리에서 결정트리(DecisionTreeClassifier)모델을 임포트 해주고

훈련시키고 예측하고 정확도 확인까지 한 과정이다. 

 

 


 

결정트리 알고리즘은

나무를 뒤집어 놓은 모양으로 뻗어나가는데,

 

위의 출력과 같이 train score가 학습이 너무 잘돼test score보다 정확도가 높은것을 알 수 있다.이를 과최적화(오버피팅)라고 한다.

 

이러한 과최적화(오버피팅)을 방지하기 위해 우리는

깊이(depth)를 튜닝해서 최적의 깊이를 구해야 한다.


 

위의 데이터는 깊이2일때

가장 train scoretest score가 정확도의 차이가 나지 않기에

가장 적합하다고 할 수 있다.


좀 더 육안으로 이해하기 쉽게

시각화해보자.

 

 

 

앞서 말해왔던

결정트리 모델을 시각화한 결과이다.

 

나무를 뒤집어 놓은 모양으로 생긴것을 확인할 수 있으며

깊이3으로 하면

시작부터 각각 총 3번의 갈림길(Node)에서 선택을 했다는 것을 알 수 있다.

 

 

💡 gini? 불순도(Impurity) - 지니계수(Gini Index)?

 

쉽게 설명하면 불순도를 측정해서 값으로 보여주는것이다.

 

즉, 이 데이터를 어떤 기준으로 분류했을때 동일한 객체들로만 잘 모아줄까를 고려해서나눠야 하는데 

 

gini계수는 현재 node불순도를 측정해서 얼마나 잘 걸러졌는지 수치로 표현한 것이다.

= 현재 노드에 있는 데이터들이 동일한 객체 비율 대비 동일하지 않고 따로 노는 객체 비율

 

따라서 가지가 쳐질수록 gini계수0으로 가까워 진다면

그 클래스에 속한 불순도가 낮으므로 좋다는 뜻이다.