데이터 분석(Data Analysis)/쉽게 배우는 파이썬 데이터 분석

[다섯째마당_데이터 과학의 세계] 15 머신러닝을 이용한 예측 분석

Kim MyeongOk 2024. 1. 9. 21:45

다섯째마당_15_머신러닝을이용한예측분석__쉽게배우는파이썬데이터분석.hwp
0.62MB

머신러닝 모델을 이용해 예측 분석하는 방법을 알아보자.


15-1 머신러닝 모델 알아보기

머신러닝 모델이 무엇인지, 머신러닝 모델을 이용해 어떻게 예측을 하는지 알아보겠음.

 

 

머신러닝 모델이란?

머신러닝모델 만들기 = 함수만들기

머신러닝 모델은 함수와 비슷함. 함수에 값을 입력하면 규칙에 따라 계산한 값을 출력하듯이 머신러닝 모델도 값을 입력하면 정해진 규칙에 따라 계산한 예측값을 출력함. 머신러닝 모델이 함수와 다른 점은 만드는 방법임. 함수를 만들 때는 사람이 계산 규칙을 정해 입력해야 하지만 머신러닝 모델을 만들 때는 사람이 계산 규칙을 정하지 않고 컴퓨터가 데이터에서 패턴을 찾아 스스로 규칙을 정하게 됨.

 

예를 들어 환자의 정보를 입력하면 당뇨병 발병 여부를 예측하는 모델을 만든다면, 사람이 해야 할 일은 환자의 정보와 당뇨병 발병 여부 데이터를 수집해 머신러닝 알고리즘에 입력하는 것뿐임. 그러면 컴퓨터는 환자의 정보와 당뇨병 발병 간의 패턴을 찾아낸 다음 환자의 정보를 입력하면 당뇨병 발병 여부를 출력하는 모델을 만듦. 어떤 사람의 당뇨병 발병 여부를 알고 싶다면 환자의 정보를 모델에 입력하기만 하면 됨.

 

예측 변수와 타켓 변수

머신러닝 모델을 만들 때 두 종류의 변수를 사용함.

- 예측 변수(predictor variable): 예측하는데 활용하는 변수 또는 모델에 입력하는 값을 예측 변수라 함.

- 타겟 변수(target variable): 예측하고자 하는 변수 또는 모델이 출력하는 값을 타겟 변수라 함.

 

만약 환자의 성별, 나이, 흡연 여부, 음주 여부로 당뇨병 발병을 예측하는 모델을 만든다면 성별, 나이, 흡연 여부, 음주 여부는 예측 변수로 사용하고, 당뇨병 발병 여부는 타겟 변수로 사용함.

 

머신러닝 모델을 이용해 미래 예측하기

머신러닝 모델은 미래의 값을 예측하는 용도로 자주 사용됨. 과거의 값을 예측 변수로 사용하고 미래의 값을 타겟 변수로 사용하면 미래를 예측하는 모델을 만들 수 있음. 예를 들어 현재 환자의 신체 정보를 입력하면 3년 뒤의 당뇨병 발병 여부를 예측하는 모델을 만들 수 있음.

머신러닝 모델은 다양한 사업 영역에서 미래를 예측하는 데 활용됨. 온라인 커머스는 고객이 구매할 가능성이 높은 상품을 예측해 추천하고, 마케팅사는 서비스에 가입할 가능성이 높은 이용자를 예측해 홍보 전화를 걸고, 금융사는 고객이 대출금을 제때 상환할지 예측해 가입 승인 여부를 결정하고, 반도체 공장은 설비 고장을 예측해 설비가 고장 나기 전에 미리 정비함. 미래를 예측하면 부가 수익을 창출하고 비용을 줄일 수 있으므로 머신러닝은 여러 분야에서 주목받는 기술임.

 

 

의사결정나무 모델

머신러닝 모델은 종류가 매우 다양하고 새로운 알고리즘이 끊임없이 개발되고 있음. 그 중에서 의사결정나무 모델은 구조가 단순하고 작동 원리를 이해하기 쉬워 여러 분야에 사용되고 있음. 규모가 크고 복잡한 모델도 의사결정나무 모델에 기반을 두고 만들 때가 많음.

 

의사결정나무 모델이란?

의사결정나무(decision tree)모델은 마치 스무고개 놀이처럼 순서대로 주어진 질문에 yes/no로 답하면 마지막 결론을 얻는 구조로 되어 있음. 질문이 나열된 모양이 가지를 뻗은 '나무'와 비슷하고, 예측값을 무엇으로 할지 '의사 결정'해 주기 때문에 의사결정나무라는 이름을 가지고 있음.

 

의사결정나무 모델을 이용해 예측하기

의사결정나무 모델을 만들면 yes/no로 답할 수 있는 질문 목록을 갖게 됨. 새 데이터가 주어졌을 때 질문 목록에 따라 순서대로 답을 하면 최종적으로 둘 중 한 가지 값을 부여받게 됨. 예를 들어 환자의 나이, 흡연 여부, 음주 여부로 당뇨병 발병 여부를 예측하는 모델을 만들었다고 해보겠음.

 

새로 온 환자 A는 나이가 45세이고 음주를 하지 않음. 모델의 첫 번째 질문은 '나이가 40세 이하인가?'. 이 질문에 A의 대답은 'no'이므로 오른쪽 가지를 타고 내려감. 두 번째 마주한 질문 '음주 여부'A의 대답은 'no'이므로 이번에도 오른쪽 가지를 타고 내려감. 최종적으로 환자 A'정상'으로 예측됨.

 

두 번째 환자 B는 나이가 30세이고 흡연을 함. B40세 이하이니 질문의 대답이 'yes'이므로 왼쪽 가지를 타고 내려감. 두 번째 마주한 질문 '흡연 여부'B의 대답은 'yes'이므로 왼쪽 가지를 타고 내려감. 최종적으로 환자 B'발병'으로 예측됨. 이처럼 데이터에 모델의 질문에 답할 수 있는 값만 있으면 발병/정상 둘 중 한 가지로 예측값을 부여받게 됨.

tip) 발병/정상처럼 둘 중 한 가지 값으로 분류하는 모델을 이진 분류 모델(binary classification model)이라고 함. 의사결정나무 모델은 셋 이상의 값으로 분류하는 다중 분류 모델(multiclass classification model)도 만들 수 있음.

 

의사결정나무 모델이 만들어지는 원리

다음과 같이 4가지 예측 변수로 당뇨병 발병 여부를 예측하는 모델을 만든다고 가정하고 모델이 만들어지는 과정을 단계별로 살펴보겠음.

- 예측 변수: 흡연 여부, 음주 여부, 성별, 나이

- 타겟 변수: 당뇨병 발병 여부

 

1단계. 타겟 변수를 가장 잘 분리하는 예측 변수 선택하기

의사결정나무 모델은 어떤 순서로 어떤 질문을 할지 정하는 과정을 거쳐 만들어짐. 가장 먼저 첫 번째 질문을 할 때 예측 변수 중에서 무엇을 사용할지 정함. 의사결정나무 알고리즘은 '타겟 변수를 가장 잘 분리해 주는 예측 변수'를 착아 첫 번째 질문을 삼음. 예측 변수가 타겟 변수를 분리해 주는 정도는 다음 절차로 알 수 있음.

1. 모든 예측 변수를 yes/no로 답할 수 있는 질문을 만듦. '흡연 여부''흡연을 하십니까?', '음주 여부''음주를 하십니까?', '성별''남자입니까?' 또는 '여자입니까?'의 형태로 만듦.

2. 모델을 만드는데 사용할 데이터를 앞에서 만든 각각의 질문에 대입한 다음 'yes'로 답한 데이터만 추출함.

3. 추출한 데이터 중 발병인과 정상인의 비율을 구함. 발병인과 정상인의 비율 차이가 크면 클수록 예측 변수가 타겟 변수를 잘 분리해낸다고 볼 수 있음.

 

앞의 그림을 보면 '성별 = 남자''yes'로 답한 사람 중 발병과 정상의 비율이 둘 다 50%로 같음. 그러므로 성별을 묻는 것은 발병과 정상을 분리해 내는데 별로 도움이 되지 않음.

반면 '음주 여부'는 성별보다 발병과 정상을 잘 분리해냄. 음주 여부에 'yes'로 답한 사람을 전부 환자로 분류하면 적어도 60%는 맞출 수 있음.

셋 중에 타겟 변수를 가장 잘 분리해주는 예측 변수는 '흡연 여부'. 흡연 여부에 'yes'로 답한 사람을 전부 환자로 분류하면 80%를 맞출 수 있음. 만약 당뇨병 발병 여부를 예측하는데 질문을 딱 하나만 해야 한다면 흡연 여부를 물어봐야 함.

 

앞에서 살펴본 변수는 범주 변수이므로 yes/no로 답할 수 있는 질문을 곧바로 만들 수 있지만 나이와 같은 연속 변수는 그렇지 않음. 연속 변수는 '나이가 x세 이하인가?'의 형태로 질문을 만들어야 yes/no로 답할 수 있음. 이때 몇 세를 기준으로 질문할지 정해야 함. 타겟 변수를 가장 잘 분리해 주는 기준 나이를 알아내려면 '18세 이하인가?', '19세 이하인가?' '60세 이하인가?'

 

이처럼 모든 예측 변수로 질문을 만든 다음 타겟 변수를 가장 잘 분리해 주는 변수를 찾아 첫 번째 질문에 사용함. 이 예에서는 흡연 여부를 선택하게 됨.

tip) 실제 계산에서는 노드 분리 전후를 비교하여 지니 계수(gini index)나 엔트로피(entropy)와 같은 불순도(imourity)를 가장 줄여주는 예측 변수를 선택함.

 

2단계. 첫 번째 질문의 답변에 따라 데이터를 두 노드로 분할하기

질문의 답변이 같아서 함께 분류된 집단을 노드(node)라고 함. 노드는 의사결정나무 도식에 사각형으로 표현됨.  

전체 데이터를 첫 번째 질문의 답변에 따라 서로 다른 노트로 보냄. 앞의 예에서는 '흡연 여부'의 답변이 'yes'면 왼쪽 노드, 'no'면 오른쪽 노드로 보내면 됨.

 

3단계. 각 노드에서 타겟 변수를 가장 잘 분리하는 예측 변수 선택하기

각 노드에서 타겟 변수를 가장 잘 분리해 주는 예측 변수를 선택함. 1단계 작업을 노트별로 반복하는 것임. '흡연 여부'는 이미 사용했으니 제외하고 나머지 변수 중에서 타겟 변수를 가장 잘 분리해 주는 두 번째 예측 변수를 찾으면 됨.

 

4단계. 노드가 완벽하게 분리될 때까지 반복하기

노드에 한 범주만 남아 완벽하게 분리될 때까지 변수를 선택하고 노드를 분할하는 과정을 반복함. 노드에 발병과 정상 중 한쪽 범주만 남으면 분할을 종료함.

 

의사결정나무 모델의 특징

노드마다 분할 횟수가 다르다

예측 변수를 선택하고 노트를 분할하는 횟수가 노드마다 다르므로 가지가 뻗어 나간 횟수도 노드마다 제각각임. 어떤 노드가 다섯 번 분할해야 한 범주만 남지만 어떤 노드는 두 번만에 한 범주만 남을 수 있음. 따라서 모든 사람에게 같은 횟수로 질문하는 게 아니라 앞의 질문에 어떻게 답변했는지에 따라 서로 다른 횟수로 질문하게 됨.

 

노드마다 선택되는 예측 변수가 다르다

예측 변수 선택 작업을 노트별로 따로 하므로 노드마다 선택되는 변수가 다름. 예를 들어 흡연자에게 '음주 여부', 비흡연자에게 '나이'가 타겟 변수를 가장 잘 분리해 주는 예측 변수일 수 있음. 따라서, 모든 사람에게 일괄적으로 같은 질문을 하는 게 아니라 앞의 질문에 어떻게 답변했는지에 따라 서로 다른 질문을 하게 됨.

 

어떤 예측 변수는 모델에서 탈락한다

어떤 예측 변수는 노드를 분할하는 과정에서 한 번도 선택되지 않아 모델에서 탈락할 수 있음. 예를 들어 당뇨병 발병 예측 모델을 만들 때 '발 크기' 변수는 타겟 변수를 분리해주는 정도가 다른 변수보다 항상 떨어져서 분할을 종료할 때까지 한 번도 선택되지 않을 수 있음. 따라서 데이터에 들어 있는 모든 변수를 예측에 사용하는 게 아니라 일부는 사용하고 일부는 제외하게 됨.


15-2 소득 예측 모델 만들기

adult 데이터는 미국인의 성별, 인종, 직업, 학력 등 다양한 인적 정보를 담고 있는 인구 조사 데이터임. adult 데이터를 이용해 인적 정보로 소득을 예측하는 의사결정나무 모델을 만들어 보겠음. 모델을 만드는 절차는 다음과 같음.

 

먼저 adult 데이터를 불러와 구조를 살펴보겠음.

import pandas as pd
df = pd.read_csv('adult.csv')
df.info()

 

adult 데이터는 48,842명의 정보를 담고 있으며 변수 15개로 구성됨. 이 중 연소득을 나타낸 income을 타겟 변수, 나머지 14개 변수를 예측 변수로 사용하겠음.

변수명 의미
age 나이
workclass 근로 형태
fnlwgt 인구 통계 가중치
education 최종 학력
education_num 교육 기간
marital_status 결혼 상태
occupation 직종
relationship 가구주와의 관계
race 인종
sex 성별
capital_gain 자본 소득(USD)
capital_loss 자본 손실(USD)
hours_per_week 주당 근무 시간
native_country 출신 국가
income 연소득

 

{아 두면 좋아요} 소득 예측 모델을 어디에 활용할까?

고가 상품 구매 프로모션

소득 예측 모델은 도급 구입차나 명품 시계처럼 고가의 상품을 구매하도록 독려하는 프로모션에 활용할 수 있음. 소득이 높아 상품을 구매할 가능성이 높은 사람을 예측하면 프로모션 활동에 드는 시간과 비용을 절약할 수 있음.

 

저소득층 지원 대상 확대

정부에서 저소득층 지원 사업 대상자를 찾아낼 때에도 소득 예측 모델을 활용할 수 있음. 저소득층을 선제적으로 찾아 지원하면 사람들이 스스로 지원 요건을 확인하고 신청하게 할 때보다 프로그램 대상을 확대할 수 있음.

 

[Do it! 실습] 전처리하기

머신러닝 모델을 만들 때 가장 먼저 하는 작업은 모델을 만드는 데 적합하도록 데이터를 가동하는 것임. 본격적으로 모델을 만들기 전에 데이터를 처리하는 작업이므로 이를 데이터 전처리(data preprocessing)라 함. adult 데이터를 전처리하겠음.

 

1. 타겟 변수 전처리

먼저 타겟 변수 income을 검토하여 전처리하겠음. income은 조사 응답자의 연소득 5만 달러를 초과하는지 여부를 나타냄. 다음 코드의 출력 결과를 보면 연소득이 5만 달러를 초과하는 사람(>50K)23.9%, 5만 달러 이하인 사람(<=50K)76%.

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

tip) df.value_counts()normalize = True를 입력하면 범주의 비율을 구함.

 

import numpy as np
df['income'] = np.where(df['income'] == '>50K', 'high', 'low')
df['income'].value_counts(normalize = True)

 

2. 불필요한 변수 제거하기

이름, 아이디, 주소 같은 변수는 대부분의 값이 고유값이어서 반복되는 패턴이 없고 타겟 변수와도 관련성이 없음. 이런 변수는 타겟 변수를 예측하는데 도움이 되지 않고 모델링 시간만 늘리는 역할을 하므로 제거해야함.

fnlwgtadult 데이터를 이용해 미국의 실제 인구를 추청할 때 사용하는 가중치임. 인종, 성별, 나이 등 인구 통계 속성이 같으면 fnlwgt가 같음. fnlwgt는 타겟 변수를 예측하는데 도움이 되지 않는 변수이므로 모델을 만들 때 사용하지 않도록 제거하겠음.

df = df.drop(columns = 'fnlwgt')

 

3. 문자 타입 변수를 숫자 타입으로 바꾸기

모델을 만드는 데 사용되는 모든 변수는 숫자 타입이어야 함. df.info()의 출력 결과를 보면 Dtypeobject인 문자 타입 변수들이 있음. 이 변수들을 모델을 만드는 데 활용하려면 숫자 타입으로 바꾸어야 함.

 

원핫 인코딩하기

변수의 범주가 특정 값이면 1, 그렇지 않으면 0으로 바꾸면 문자 타입 변수를 숫자 타입으로 만들 수 있음. 이렇게 값을 10으로 바꾸는 방법을 원핫 인코딩(one-hot encoding)이라 함.

dfsex를 추출해 원핫 인코딩하는 방법을 알아보겠음. 다음 코드의 출력 결과를 보면 df_tmpsex'Male' 또는 'Female'로 되어 있는 문자 타입 변수임.

df_tmp = df[['sex']]
df_tmp.info()

df_tmp['sex'].value_counts()

 

pd.get_dummies()에 데이터 프레임을 입력하면 문자 타입 변수를 원핫 인코딕을 적용해 변환함. 다음 코드의 출력 결과를 보면 sex가 사라지고 그 대신 sex_Femalesex_Male이 만들어졌음.

# df_tmp의 문자 타입 변수에 원핫 인코딩 적용
df_tmp = pd.get_dummies(df_tmp)
df_tmp.info()

tip) 원핫 인코딩으로 만들어진 변수의 타입은 bool. boolFalse, True를 담을 수 있는 데이터 타입임.

 

sex_FemalesexFemale이면 1, 그렇지 않으면 0으로 된 변수임. sex_Male은 반대로 sexMale이면 1, 그렇지 않으면 0으로 된 변수임. 두 변수 중 한쪽이 1이면 다은 한쪽은 반드시 0이 됨.

df_tmp[['sex_Female', 'sex_Male']].head()

 

 

 

df.info() 출력 결과에 개별 변수의 정보가 출력되지 않은 이유는 변수가 100개 이하일 때만 변수 정보를 출력하도록 설정되어 있기 때문임. df.info()max_cols = np.inf를 입력하면 변수의 수와 관계없이 모든 변수의 정보를 출력함. 출력 결과를 보면 변수가 108개로 늘어나고, 문자 타입 변수가 전부 숫자 타입으로 바뀌었다는 것을 알 수 있음.

target = df['income']           # income 추출

df = df.drop(columns = 'income')# income 제거
df = pd.get_dummies(df)         # 문자 타입 변수 원핫 인코딩

df['income'] = target           # df에 target 삽입
df.info()

 

df.info() 출력 결과에 개별 변수의 정보가 출력되지 않은 이유는 변수가 100개 이하일 때만 변수 정보를 출력하도록 설정되어 있기 때문임. df.info()max_cols = np.inf를 입력하면 변수의 수와 관계없이 모든 변수의 정보를 출력함. 출력 결과를 보면 변수가 108개로 늘어나고, 문자 타입 변수가 전부 숫자 타입으로 바뀌었다는 것을 알 수 있음.

import numpy as np
df.info(max_cols = np.inf)

(...생략...)

 

4. 데이터 분할하기

모델을 만들 때는 가지고 있는 모든 데이터를 사용하는게 아니라 일부만 무작위로 추출해 사용해야 함.

 

모든 데이터를 사용해 모델을 만들면 성능 평가 점수르 신뢰할 수 없다

모델을 만들고 나면 모델이 타겟 변수를 얼마나 정확하게 예측하는지 알아보기 위해 성능을 평가함. 그런데 모델을 만들 때 사용한 데이터를 성능을 평가할 때 그대로 사용하면 평가 점수를 신뢰할 수 없게 됨. 예측 정확도가 높게 나오더라도 모델의 성능이 좋아서인지 아니면 이미 경험한 데이터라 잘 맞춘 것인지 알 수 없기 때문임.

이는 이는 학생이 연습 문제로 공부한 다음 똑같은 문제로 시험을 치르는 상황과 비슷함. 공부할 때 풀어본 문제를 다시 푸는 것에 불과하므로 실력을 제대로 평가할 수 없는 것임.

 

크로스 밸리데이션: 신뢰할 수 있는 성능 평가 점수를 얻는 방법

신뢰할 수 있는 성능 평가 점수를 얻으려면 가지고 있는 데이터에서 일부만 추출해 모델을 만들 때 사용하고, 나머지는 남겨 두었다가 성능을 평가할 때 사용해야 함. 다음 그럼처럼 데이터를 훈련용과 평가용으로 나누어 사용하는 것임. 이렇게 하면 모델이 한 번도 경험한 적 없는 데이터로 성능을 평가하므로 평가 점수를 신뢰할 수 있게 됨.

이는 학생이 문제 은행에서 일부만 뽑아 공부한 다음 나머지 문제로 시험을 치르는 상황과 비슷함. 공부할 때 이미 풀어본 문제가 아니라 처음 보는 문제를 얼마나 잘 푸는지 봐야만 실력을 제대로 평가할 수 있는 것임.

 

이처럼 데이터를 분할해 일부는 모델을 만들 때 사용하고 나머지는 평가할 때 사용하는ㄴ 방법을 크로스 밸리데이션(cross validation, 교차 검증)이라고 함. 분할한 데이터 중에서 모델을 만들 때 사용하는 데이터를 트레이닝 세트(training set, 훈련 세트), 성능을 평가할 때 사용하는 데이터를 테스트 세트(test set, 시험 세트)라 부름.

tip) 모델을 만드는 작업을 'training'이라고 표현함. 우리말로는 '훈련' 또는 '학습'으로 번역함.

 

adult 데이터 분할하기

scikit-learn 패키지를 이용해 데이터를 트레이닝 세트와 테스트 세트로 분할하겠음. scikit-learn은 머신러닝 모델을 만들 때 가장 많이 사용되는 패키지임.

tip) scikit-learnscikit으로 줄여서 부르기도 함. 패키지를 로드할 때는 패키지명에 scikit-learn이 아니라 sklearn을 입력해야 함.

 

sklearn.model_selectiontrain_test_split()을 이용하면 데이터를 트레이닝 세트와 테스트 세트로 분할할 수 있음. train_test_split()에는 다음과 같은 파라미터를 입력해야 함.

 

- test_size: 데이터 세트의 비율. 트레이닝 세트와 테스트 세트의 비율은 보통 7:3 또는 8:2로 정함. 정답은 없지만 데이터가 많을수록 트레이닝 세트의 비율을 늘리고 반대로 데이터가 적을수록 테스트 세트의 비율을 늘림.

- stratify: 범주별 비율을 통일할 변수. stratify에 타겟 변수를 입력하면 트레이닝 세트와 테스트 세트에 타겟 변수의 범주별 비율을 비슷하게 맞춰줌. 타겟 변수를 입력하지 않으면 타겟 변수의 범주별 비율이 데이터 세트마다 달라지므로 평가 결과를 신뢰하기 어려움.

- random_state: 난수 초깃값. train_test_split()은 난수를 이용해 데이터를 무작위로 추출하므로 함수를 실행할 때마다 추출되는 데이터가 조금씩 달라짐. 난수를 고정하면 코드를 반복 실행해도 항상 같은 데이터가 추출됨.

 

다음 코드는 함수의 출력 결과를 df_train, df_test 두 변수에 할당하는 형태로 되어 있음. train_test_split()은 트레이닝 세트와 테스트 세트를 함께 출력함. 할당할 변수를 2개 지정하면 트레이닝 세트는 앞에 입력한 변수, 테스트 세트는 뒤에 입력한 변수에 할당함.

from sklearn.model_selection import train_test_split
df_train, df_test = train_test_split(df,
                                     test_size = 0.3,        # 테스트 세트 비율
                                     stratify = df['income'],# 타겟 변수 비율 유지
                                     random_state = 1234)    # 난수 고정

tip) 난수를 이용하면 코드의 결과는 OS나 패키지 버전에 따라 조금씩 다를 수 있음.

 

df.shape로 두 데이터 세트를 살펴보면 변수가 108개로 같고, 행위 수는 다름.

# train
df_train.shape

# output: (34189, 108)
# tsst
df_test.shape

# output: (14653, 108)

 

타겟 변수의 범주별 비율은 두 데이터 세트 모두 비슷함. train_test_split()stratify에 타겟 변수를 지정했기 때문임.

# train
df_train['income'].value_counts(normalize = True)

# tsst
df_test['income'].value_counts(normalize = True)

 

[Do it! 실습] 의사결정나무 모델 만들기

전처리를 완료했으니 이제 모델을 만들겠음. 모델을 만들 때는 df_train을 사용함. df_test는 마지막에 모델을 평가할 때 사용함.

 

모델 설정하기

sklearntree.DecisionTreeClassifier() 클래스를 이용하면 모델을 만들 수 있음. 먼저 모델을 만드는 데 사용할 clf를 만들겠음. tree.DecisionTreeClassifier()에는 다음과 같은 파라미터를 입력함.

 

- random_state: 난수 초깃값. 변수 선택 과정에서 난수를 이용해 코드를 실행할 때마다 결과가 조금씩 달라짐. 난수를 고정하면 코드를 여러 번 실행해도 항상 결과가 항상 같음.

- max_depth: 나무의 깊이. 노드를 최대 몇 번까지 분할할지 정함. 숫자가 클수록 노드를 여러 번 분할 해 복잡한 모델을 만듦. 값을 지정하지 않으면 노드를 최대한 많이 분할함. 여기서는 단순한 모델을 만들도록 3을 입력하겠음.

from sklearn import tree
clf = tree.DecisionTreeClassifier(random_state = 1234, # 난수 고정
                                  max_depth = 3)       # 나무 깊이

tip) clfclassifier의 줄임말임. 고소득/저소득처럼 데이터를 몇 개 중 하나로 분류하는 분류 모델(classification model) 또는 분류기(Classifier)라 함.

 

모델 만들기

앞에서 만든 clf()를 이용해 모델을 만들겠음. df_train에서 예측 변수와 타겟 변수를 각각 추출해 데이터 프레임을 만듦. 그런 다음 clf.fit()X에는 예측변수, y에는 타겟 변수를 입력함. X는 대문자이니 주의하기 바람.

train_x = df_train.drop(columns = 'income') # 예측 변수 추출
train_y = df_train['income']

model = clf.fit(X = train_x, y = train_y)   # 모델 만들기

 

 {알아 두면 좋아요} 모델을 만들 때 난수를 고정하는 이유

의사결정나무 모델 알고리즘은 타겟 변수를 가장 잘 분리해 주는 예측 변수를 선택해 노트를 분할함. 그런데 여러 예측 변수가 똑같이 타겟 변수를 잘 분리해내는 경우가 있음. 이럴 때 난수를 무작위로 예측 변수를 선택하기 때문에 코드를 실행할 때마다 결과가 조금씩 달라짐. 코드를 여러 번 실행해도 같은 결과가 나오게 하려면 난수를 고정해야 함.

 

[Do it! 실습] 모델 구조 살펴보기

완성된 모델을 그래프로 만들어 구조를 살펴보겠음. tree.plot_tree()을 이용하면 모델을 시각화할 수 있음. 먼저 그래프를 크고 선명하게 표현하도록 설정한 다음 그래프를 출력하겠음.

import matplotlib.pyplot as plt
plt.rcParams.update({'figure.figsize' : [12, 8], # 그래프 크기 설정
                     'figure.dpi'  : '100'})     # 해상도 설정

tree.plot_tree(model);                           # 그래프 출력

tip) 그래프 출력 코드 뒤에 ;를 입력하면 메시지를 제외하고 그래프만 출력함.

tree.plot_tree()에 몇 가지 파라미터를 추가해 그래프를 보기 좋게 수정하겠음.

tree.plot_tree(model,
               feature_names = list(train_x.columns), # 예측 변수명
               class_names = ['high', 'low'],         # 타겟 변수 클래스, 알파벳순
               proportion = True,                    # 비율 표기
               filled = True,                        # 색칠
               rounded = True,                       # 둥근 테두리
               impurity = False,                     # 불순도 표시
               label = 'root',                        # label 표시 위치
               fontsize = 10);                        # 글자 크기

 

 

노드의 값

그래프에서 가장 위에 있는 첫 번째 노드를 이용해 그래프 해석하는 방법을 알아보겠음. 다른 노드도 같은 순서로 값이 표현되어 있음.

전체 데이터의 몇 퍼센트가 해당 노드로 분류됐는지 나타냄. 첫 번째 노드는 아직 한 번도 나뉘지 않았으므로 데이터의 100%가 이 노드에 속함.

다음 단계의 노드로 내려가면 데이터가 여러 노드로 배분되므로 비율이 줄어듦. 예를 들어 두 번째 단계의 노드를 보면 왼쪽 노드는 54.2%, 오른쪽 노드는 45.8%. 같은 단계의 노드 비율을 모두 더하면 100%가 됨.

타겟 변수의 클래스별 비율을 알파벳순으로 나타냄. 타겟 변수 income'high''low' 'high'가 알파벳순으로 우선하므로 'high', 'low' 순으로 비율이 표시되어 있음. 값을 보면 전체 데이터 중 'high'23.9% 'low'76.1%라는 것을 알 수 있음.

tip) 타겟 변수의 범주를 클래스(class)라고 함.

0.5를 기준으로 타겟 변수의 두 클래스 중 어느 쪽이 더 많은지 나타냄. 'high'23.9%, 'low'76.1%'low'가 더 많으므로 'low'가 표시됨.

노드를 분리할 때 사용할 기준을 나타냄. 이 기준을 충족하는 데이터는 왼쪽, 충족하지 않는 데이터는 오른쪽 노드로 할당됨.

 

marital_status_Married-civ-spouse는 원핫 인코딩으로 만들어진 변수로 기혼이면 1, 비혼이면 0으로 되어 있음. 따라서 비혼이면 변수의 값이 0이므로 'marital_status_Married-civ-spouse <= 0.5(비혼)' 조건을 충족해 왼쪽 노드로 내려가고, 기혼이면 조건을 충족하지 않으므로 오른쪽 노드로 내려감. , 비혼은 왼쪽노드, 기혼은 오른쪽 노드로 내려감.

다른 노드도 불리 기준이 표시되지만 마지막 단계의 '끝 노드'는 더 이상 나뉘지 않으므로 분리 기준이 표시되지 않음.

tip) 첫 번째 단계의 노드를 뿌리 노드(root node), 마지막 단계의 노드를 끝 노드(terminal node)라 함. 어떤 노드의 윗 단계에 있는 노드를 부모 노드(parent node), 아랫 단계에 있는 노드를 자식 노드(child node)라 함.

 

왼쪽 노드

이번에도 첫 번째 노드의 조건에 따라 나뉘어진 두 번째 단계의 노드를 해석해보겠음. 먼저 왼쪽 노드를 살펴보겠음.

 

첫 번째 노드의 조건 'marital_status_Married-civ-spouse <= 0.5(비혼)'을 충족한 비혼자가 이 노드에 할당됨. 비혼자는 전체의 54.2%. 비혼자의 income'high' 6.4%, 'low' 93.6%'low'가 더 많음. 다음으로 노드를 나누는 기준은 'capital_gain <= 7073.5'.

 

오른쪽 노드

첫 번째 노드의 조건 'marital_status_Married-civ-spouse <= 0.5(비혼)'을 충족하지 않은 기혼자가 이 노드에 할당됨. 기혼자는 전체의 45.8%. 기혼자의 income'high' 44.7%, 'low' 55.3%'low'가 더 많음. 다음으로 노드를 나누는 기준은 'education_num <= 12.5'.

 

 

노드의 색

노드의 색깔

노드의 색깔은 우세한 타겟 변수의 클래스에 따라 정해짐. 그래프를 보면 'high'의 비율이 높은 노드는 주황색, 'low'의 비율이 높은 노드는 파란색 계열로 표현되어 있음.

 

노드의 색 농도

노드의 색농도는 '한 클래스의 구성 비율이 우세한 정도'를 나타냄. 한 클래스의 비율이 다른 클래스보다 높을수록 농도가 진하고, 두 클래스의 비율이 비슷할수록 농도가 연함. 두 번째 단계의 노드를 보면 왼쪽 노드는 'high'보다 'low'의 비율이 월등히 높아 농도가 진한 반면, 오른쪽 노드는 두 클래스의 비율이 비슷하므로 농도가 연함.

tip) '한 클래스의 비율이 우세한 정도'를 순도(purity)라 함.

 

[Do it! 실습] 모델을 이용해 예측하기

앞에서 만든 모델을 활용해 새 데이터의 타겟 변수를 예측하는 방법을 알아보겠음. 먼저 모델을 만들 때 사용하지 않은 df_test에서 예측 변수와 타겟 변수를 각각 추출하겠음.

test_x = df_test.drop(columns = 'income') # 예측 변수 추출
test_y = df_test['income']                # 타겟 변수 추출

 

model.predict()를 이용하면 모델을 이용해 새 데이터의 타겟 변수 값을 예측할 수 있음. model.predict()test_x를 입력해 타겟 변수 예측값을 구한 다음 df_test['pred']에 할당하겠음. df_test를 출력하면 가장 오른쪽에 pred가 만들어진 것을 확인할 수 있음. pred는 모델이 test_x에 들어있는 예측 변수만 이용해서 구한 값임.

# 예측값 구하기
df_test['pred'] = model.predict(test_x)
df_test

 

incomepred의 값을 보면 모델의 예측이 맞았는지 알 수 있음. 두 변수의 값이 같으면 예측이 맞은 것이고, 두 변수의 값이 다르면 예측이 틀린 것임.

 

[Do it! 실습] 성능 평가하기

예측값을 실제값과 대조해 예측이 얼마나 잘 맞았는지 모델의 성능을 평가하겠음. 성능 평가 지표는 종류가 다양하고 특징이 서로 달라서 어떤 지표가 높더라도 다른 지표는 낮을 수 있음. 그러므로 모델을 사용하는 목적에 맞게 평가 기준으로 삼을 지표를 선택해야 함.

 

 

confusion matrix 만들기

모델이 예측한 값 중 맞는 경우와 틀린 경우의 빈도를 나타낸 컨퓨전 매트릭스(confusion matrix, 혼동 행렬)를 만들겠음. sklearn.metricsconfusion_matrix()를 이용하면 컨퓨전 매트릭스를 만들 수 있음. confusion_matrix()에는 다음 파라미터를 입력함.

- y_true: 타겟 변수

- y_pred: 예측 변수

- labels: 클래스 배치 순서

from sklearn.metrics import confusion_matrix
conf_mat = confusion_matrix(y_true = df_test['income'], # 실제값
                            y_pred = df_test['pred'],   # 예측값
                            labels = ['high', 'low'])   # 클래스 배치 순서
conf_mat

 

sklearn.metricsConfusionMatrixDisplay()를 이용해 컨퓨전 매트릭스로 히트맵(heatmap)을 만들어 값을 살펴보겠음. 히트맵은 격자 위에 값을 표시하고 값이 클수록 셀의 색농도를 진하게 표현한 그래프임. ConfusionMatrixDisplay() C, M, D는 대문자이니 주의하기 바람.

plt.rcParams.update(plt.rcParamsDefault) # 그래프 설정 되돌리기

from sklearn.metrics import ConfusionMatrixDisplay
p = ConfusionMatrixDisplay(confusion_matrix = conf_mat,      # 컨퓨전 매트릭스
                           display_labels = ('high', 'low')) # 타겟 변수 클래스명

p.plot(cmap = 'Blues')                                       # 컬러맵 적용해 출력
plt.show()

 

 

confusion matrix 해석하기

컨퓨전 매트릭스의 행은 실제(true) 빈도를 의미함. 첫 번째 행은 income이 실제로 high인 사람, 두 번째 행은 실제로 low인 사람을 나타냄.

열은 모델이 예측한(predicted) 빈도를 의미함. 첫 번째 열은 모델이 high로 예측한 사람, 두 번째 열이 low로 예측한 사람을 의미함.

컨퓨전 매트릭스를 보면 예측이 맞은 빈도와 틀린 빈도를 알 수 있음. 행과 열의 레이블(label)이 같은 왼쪽 대각선의 두 셀은 예측이 맞은 빈도와 틀린 빈도를 나타냄. 반대로 레이블이 서로 다른 오른쪽 대각선의 두 셀은 예측이 틀린 빈도를 나타냄. 앞에서 출력한 컨퓨전 매트릭스를 보면 다음과 같은 사실을 알 수 있음.

 

첫 번째 열의 값

- 모델이 2,383(1,801 + 582)high로 예측했음.

- 이 중 실제로 high인 사람은 1,801(정답)

- 이 중 실제로 low인 사람은 582(오답)

 

두 번째 열의 값

모델이 12,270(1,705 + 10,565)low로 예측했음.

- 이 중 실제로 low인 사람은 1,705(정답)

- 이 중 실제로 high인 사람은 10,565(오답)

 

 

컨퓨전 매트릭스의 셀 이름

컨퓨전 매트릭스의 각 셀은 다음과 같은 단어로 표현함.

- 정답 여부(True/False): 모델의 예측값이 실제값과 일치하면 True, 일치하지 않으면 False

- 예측 클래스(Positive/Negative): 타겟 변수의 클래스 중 모델이 예측하고자 하는 관심 클래스는 Positive, 그 반대는 Negative, 여기서는 모델의 목적이 고소득자를 찾아내는 것이므로 incomehighPositive, lowNegative가 됨.

 

컨퓨전 매트릭스의 셀 이름은 True/FalsePositive/Negative의 첫 글자를 따서 TP, TN, FP, FN으로 줄여서 표현할 때가 많음. 줄임말을 알아두면 성능 지표 공식을 이해하기 편함.

 

 

성능 평가 지표 구하기

성능 평가 지표를 구하면 모델의 예측이 얼마나 정확한지 알 수 있음.

 

Accuracy

accuracy(정확도)는 모델이 '예측해서 맞춘 비율'을 의미함. 컨퓨전 매트릭스 전체 셀 합계에서 왼쪽 대각선 셀의 합계가 차지하는 비율이 accuracy. 모델의 성능을 평가할 때 기본적으로 accuracy를 가장 먼저 구함.

tip) 성능 평가 지표는 영어 표현을 더 많이 쓰기 때문에 한글로 표기하지 않음.

accuracy는 앞에서 만든 conf_mat을 이용해 직접 계산할 수도 있지만 sklearn.metricsaccuracy_score()를 이용하면 구할 수 있음. 다음 코드의 출력 결과를 보면 accuracy가 약 84.3%라는 것을 알 수 있음.

import sklearn.metrics as metrics
metrics.accuracy_score(y_true = df_test['income'], # 실제값
                       y_pred = df_test['pred'])   # 예측값

# output: 0.8439227461953184

 

accuracy는 타겟 변수의 클래스별 비율이 불균형하면 신뢰하기 어렵다는 제한점이 있음. 이 장에서 사용한 adult 데이터는 연소득이 low에 해당하는 사람이 76%로 매우 많음. 그러므로 어떤 데이터를 입력하든 항상 low로 예측하는 이상한 모델을 만들더라도 accuracy는 최소 76%가 됨. 따라서 accuracy만 봐서는 점수가 높더라도 모델의 성능이 좋아서인지 아니면 자료가 불균형해서인지 판단할 수 없음.

 

Precision

precision(정밀도)은 모델이 '관심 클래스를 예측해서 맞춘 비율'을 의미함. 고소득자 예측 모델이 incomehigh로 예측한 사람 중에서 실제로 high인 사람의 비율이 precision. 다른 예로 당뇨병 예측 모델이라면 모델이 발병으로 예측한 사람 중에서 실제 발병한 사람의 비율이 precision. 컨퓨전 매트릭스 첫 번째 열의 셀 합계에서 위쪽 셀이 차지하는 비율을 구하면 precision이 됨.

metrics.precision_score()를 이용하면 precision을 구할 수 있음. 다음 코드의 출력 결과를 보면 모델의 precision75.5%. 따라서 모델이 incomehigh로 예측한 사람 중 75.5%가 실제로 high이고, 나머지 24.5%low인데 high로 잘못 분류한 것임.

metrics.precision_score(y_true = df_test['income'], # 실제값
                        y_pred = df_test['pred'],   # 예측값
                        pos_label = 'high')         # 관심 클래스

# output: 0.7557700377675199

 

Recall

recall(재현율)은 모델이 '실제 데이터에서 관심 클래스를 찾아낸 비율'을 의미함. 고소득자 예측 모델에서는 income이 실제로 high인 사람 중에서 모델이 high로 예측해서 찾아낸 사람의 비율이 recall. 다른 예로 당뇨변 예측 모델이라면 실제 당뇨병 발병자 중에서 모델이 발병으로 예측해서 찾아낸 비율이 recall. 컨퓨전 매트릭스 첫 번째 행의 셀 합계에서 왼쪽 셀이 차지하는 비율을 구하면 recall이 됨.

recallprecision은 계산할 때 분모를 놓는 것이 다름. precision은 모델이 '관심 클래스로 예측한 빈도'를 분모로 놓고 구하는 반면, recall'실제 관심 클래스의 빈도'를 분모로 놓고 구함.

tip) sensitivity(민감도)recall과 같은 의미로 쓰임.

metrics.recall_score()를 이용하면 recall을 구할 수 있음. 다음 코드의 출력 결과를 보면 모델의 recall51.3%. 따라서 모델이 incomehigh로 예측한 사람 중 51.3%를 모델이 high로 찾아냈고, 나머지 48.7%low로 잘못 예측해서 놓친 것임.

metrics.recall_score(y_true = df_test['income'], # 실제값
                     y_pred = df_test['pred'],   # 예측값
                     pos_label = 'high')         # 관심 클래스

# output: 0.5136908157444381

 

F1 score

recallprecision이 모두 중요할 때는 recallprecision의 크기를 함께 반영한 F1 score를 사용함. F1 scorerecallprecision의 조화평균으로, 0~1 사이의 값을 지니며 성능이 높을수록 1에 가까운 값이 됨. recallprecision을 곱해서 구하기 때문에 둘 중 하나라도 0이면 0이 됨.

tip) F1 scoreF1-score, F-measure라고도 함.

 

F1 scoreaccuracy와 달리 타겟 변수의 클래스가 불균형해도 모델의 성능을 잘 표현함. 예를 들어 Negative 클래스가 훨씬 많은 불균형 데이터를 예측할 때 어떤 모델은 데이터를 대부분 Negative로만 예측하고 Positive로는 거의 예측하지 않을 수 있음. 이런 모델은 Positive 클래스를 거의 맞추지 못해 recallprecision이 낮은데도 accuracy를 구하면 매우 높게 나옴. 반면 F1 scorerecallprecision의 조화평균이기 때문에 매우 낮게 나옴.

F1 scorerecallprecision을 고루 반영하고 클래스가 불균형해도 모델의 성능을 잘 나타내므로 여러 모델의 성능을 한가지 지표로 비교해야 할 때 특히 자주 사용됨.

metrics.f1_score()를 이용하면 F1 score를 구할 수 있음. 다음 코드의 출력 결과를 보면 모델의 F1 score0.61.

metrics.f1_score(y_true = df_test['income'], # 실제값
                 y_pred = df_test['pred'],   # 예측값
                 pos_label = 'high')         # 관심 클래스

# output: 0.6116488368143997

 

어떤 성능 평가 지표를 사용해야 할까?

성능 평가 지표는 특징이 서로 달라서 어떤 지표가 높더라도 다른 지표는 낮을 수 있음. 그러므로 모델을 사용하는 목적에 맞게 평가 기준을 삼을 지표를 선택해야 함. accuracy는 모델의 일반적인 성능을 나타내므로 항상 살펴봐야 하고, 이에 더해 목적에 따라 precision 또는 recall 중 한 가지 이상을 함께 살펴봐야 함.

 

precision: 관심 클래스가 분명할 때

모델을 사용하는 목적이 타겟 변수의 클래스 중에서 관심을 두고 한쪽 클래스를 정확하게 예측하는 것이라면 precision 기준으로 성능을 평가해야 함. 예를 들어 고소득자를 예측해 고가의 제품을 홍보한다면 모델이 고소득자로 예측했을 때 얼마나 잘 맞는지 살펴봐야 하므로 precision을 기준으로 평가해야 함. 이처럼 타겟 변수의 한쪽 클래스에 분명한 관심이 있을 때 precision을 사용함.

 

recall: 관심 클래스를 최대한 많이 찾아내야 할 때

모델을 사용하는 목적이 관심 클래스를 최대한 많이 찾아내는 것이라면 recall 기준으로 성능을 평가해야 함. 예를 들어 전염병에 감염된 사람 중에서 몇 퍼센트를 감염된 것으로 예측하는지 살펴봐야 하므로 recall을 기준으로 평가해야 함.

 

관심 클래스로 예측해서 틀릴 때 손실 VS 관심 클래스로 놓칠 때 손실

평가 기준으로 삼을 지표를 결정하는 또 가른 방법은 데이터를 '관심 클래스로 예측해서 틀릴 때'의 손실과 '관심 클래스를 놓칠 때'의 손실 중 무엇이 더 큰지를 높고 판단하는 것임. 데이터를 관심 클래스로 예측해서 틀릴 때 손실이 더 크면 precision, 반대로 관심 클래스를 놓칠 때 손실이 더 크면 recall을 평가 기준으로 사용하면 됨.

 

예시 1) 고소득자에게 값비싼 선물을 보내 구매를 독려하는 마케팅 활동

- 관심 클래스로 예측해서 틀릴 때의 손실: 구매할 가능성이 낮은 저소득자에게 값비싼 선물을 보냄.

- 관심 클래스로 놓칠 때의 손실: 구매할 가능성이 있는 고소득자에게 선물을 보내지 않음.

이때는 데이터를 관심 클래스로 예측해서 틀릴 때의 손실이 더 큼. 따라서 비관심 클래스(negative)를 관심 클래스(positive)로 예측하는 오류(false positive)를 줄여야 하므로 precision 기준으로 모델을 평가해야 함.

 

예시 2) 전염병에 걸린 사람을 찾아 격리하는 방역 활동

- 관심 클래스로 예측해서 틀릴 때의 손실: 정상인을 확진자로 분류해서 불필요하게 격리함.

- 관심 클래스로 놓칠 때의 손실: 확진자를 격리하지 않아서 전염병이 확산됨.

이때는 관심 클래스를 놓칠 때의 손실이 더 큼. 따가서 관심 클래스(positive)를 비관심 클래스(negative)로 예측하는 오류(false negative)를 줄여야 하므로 recall 기준으로 모델을 평가해야 함.

tip) 비관심 클래스(Negative)를 관심 클래스(Positive)로 잘못 예측하는 False Positive1종 오류(Type 1 error) 또는 알파 오류(α error)라 함. 반대로 관심 클래스(Positive)를 비관심 클래스(Negative)로 잘못 예측하는 False Negative2종 오류(Type 1 error) 또는 베타 오류(β error)라 함.

 

F1 score: recallprecision이 모두 중요할 때

데이터를 관심 클래스로 예측해서 틀릴 때의 손실과 관심 클래스를 놓칠 때의 손실이 둘 다 중요하다면 recallprecision을 모두 판단 기준으로 삼아야 함. 그런데 여러 모델을 만들어 성능을 비교할 때 평가 기준 지표가 여러 개면 지표에 따라 성능이 좋다고 판단되는 모델이 다를 수 있음. 이럴 때는 F1 score를 평가 기준으로 삼으면 됨. F1 score를 이용하면 여러 모델 중에 어떤 모델이 우수한지 한 가지 기준으로 비교할 수 있음.

 

모델의 성능 지표가 얼마면 될까?

모델의 성능 지표는 얼마를 넘겨야 모델을 사용할 수 있다는 절대적인 기준이 없음. 모델이 쓸만한지는 기존에 해오던 방식으로 예측했을 때와 모델을 이용해 예측했을 때의 성능 지표를 비교해야 판단할 수 있음. 예를 들어 모델의 accuracy60%밖에 안 된다고 하더라도 기존 방식으로 사람이 직접 예측했을 때 accuracy55%라면 모델을 사용하는 게 낫다고 판단할 수 있음. 반대로 모델의 accuracy95%로 높더라도 기존 방식의 accuracy96%라면 기존 방식을 유지하는 게 낫다고 판단할 수 있음.

예측 방식을 선택할 때는 예측에 드는 시간과 비용도 고려해야 함. 예측 성능이 아무리 좋더라도 예측하는 데 시간이 너무 오래 걸리고 비용이 많이 든다면 사용하기 어려움.

따라서 모델을 이용해 예측할 때와 기존 방식으로 예측할 때 드는 시간과 비용을 비교해 모델을 사용할지 여부를 결정해야 함.

 

{알아 두면 좋아요} F1 score를 구할 때 왜 조화평균을 사용할까?

조화평균은 '역수의 산술평균의 역수'. 주어진 수들의 역수를 구해 산술평균을 구한 다음 다시 역수를 구하면 조화평균이 됨.

F1 score를 구할 때 주어진 수들의 합을 수의 개수로 나누는 산술평균이 아니라 조화평균을 이용하는 이유는 recallprecision 중 하나라도 작으면 값을 크게 낮추기 위해서임. 예를 들어 recall99%, precision1%일 때 산술평균을 구하면 50%지만, 조화평균을 구하면 1.98%.

- 산술평균: (0.99 + 0.01) / 2 = 0.5

- 조화평균: 2 * (0.99 * 0.01) / (0.99 + 0.01) = 0.0198


정리하기

## 1. 전처리

# 데이터 불러오기
import pandas as pd
df = pd.read_csv('adult.csv')

# 1. 타겟 변수 전처리
import numpy as np
df['income'] = np.where(df['income'] == '>50K', 'high', 'low')

# 2. 불필요한 변수 제거하기
df['income'].value_counts(normalize = True)

# 3. 문자 타입 변수를 숫자 타입으로 바꾸기
target = df['income']           # income 추출
df = df.drop(columns = 'income')# income 제거
df = pd.get_dummies(df)         # 문자 타입 변수 원핫 인코딩
df['income'] = target           # df에 target 삽입

# 4. 데이터 분할하기
from sklearn.model_selection import train_test_split
df_train, df_test = train_test_split(df,
                                     test_size = 0.3,        # 테스트 세트 비율
                                     stratify = df['income'],# 타겟 변수 비율 유지
                                     random_state = 1234)    # 난수 고정

## 2. 의사결정나무 모델 만들기

# 모델 선정하기
from sklearn import tree
clf = tree.DecisionTreeClassifier(random_state = 1234, # 난수 고정
                                  max_depth = 3)       # 나무 깊이
                                  
# 모델 만들기
train_x = df_train.drop(columns = 'income') # 예측 변수 추출
train_y = df_train['income']
model = clf.fit(X = train_x, y = train_y)   # 모델 만들기

# 모델 구조 살펴보기
import matplotlib.pyplot as plt
tree.plot_tree(model,
               feature_names = list(train_x.columns), # 예측 변수명
               class_names = ['high', 'low'],         # 타겟 변수 클래스, 알파벳순
               proportion = True,                    # 비율 표기
               filled = True,                        # 색칠
               rounded = True,                       # 둥근 테두리
               impurity = False,                     # 불순도 표시
               label = 'root',                        # label 표시 위치
               fontsize = 10);                        # 글자 크기

## 3. 모델을 이용해 예측하기

# 예측하기
test_x = df_test.drop(columns = 'income') # 예측 변수 추출
test_y = df_test['income']                # 타겟 변수 추출
df_test['pred'] = model.predict(test_x)   # 예측값 구하기

## 4. 성능 평가하기

# confusion matrix 만들기
from sklearn.metrics import confusion_matrix
conf_mat = confusion_matrix(y_true = df_test['income'], # 실제값
                            y_pred = df_test['pred'],   # 예측값
                            labels = ['high', 'low'])   # 클래스 배치 순서

# confusion matrix 시각화
from sklearn.metrics import ConfusionMatrixDisplay
p = ConfusionMatrixDisplay(confusion_matrix = conf_mat,      # 컨퓨전 매트릭스
                           display_labels = ('high', 'low')) # 타겟 변수 클래스명
p.plot(cmap = 'Blues')                                       # 컬러맵 적용해 출력
plt.show()

# accuracy
metrics.accuracy_score(y_true = df_test['income'], # 실제값
                       y_pred = df_test['pred'])   # 예측값

# precision
metrics.precision_score(y_true = df_test['income'], # 실제값
                        y_pred = df_test['pred'],   # 예측값
                        pos_label = 'high')         # 관심 클래스

# recall
metrics.recall_score(y_true = df_test['income'], # 실제값
                     y_pred = df_test['pred'],   # 예측값
                     pos_label = 'high')         # 관심 클래스

# F1 score
metrics.f1_score(y_true = df_test['income'], # 실제값
                 y_pred = df_test['pred'],   # 예측값
                 pos_label = 'high')         # 관심 클래스

 

 

 

 

- 15장 END -