본문 바로가기
데이터 분석 및 시각화

[데이터분석] ML_머신러닝 모델+의사결정나무

by 바다의 공간 2024. 8. 30.

0825

# 머신러닝 모델 (Machine Learning Model)

* 머신러닝 모델은 현재까지의 데이터로 미래의 데이터를 예측하기 위한 모델

* 예측변수(결과를 도출하기 위한 입력데이터)를 통해 타겟변수(결과로 도출된 출력데이터)를 찾아내기 위한 모델

* 예측변수(predictor variables) 예측하는데 활용하는 변수 또는 모델에 입력하는 값

* 타겟변수(target variables) 예측하고자 하는 변수 또는 모델이 출력하는 값

 

# 의사결정나무 모델 (Decision Model)

* 예/아니오로 2분된 답변을 연속적으로 취득하여 예측하는 모델 (yes/no)

* 구조가 단순하고 이해하기 쉬워 많은 예측 모델의 토대가 됨

* 타겟변수를 가장 잘 분리해 주는 예측변수에 주목하는 모델

(예를 들어 나이,흡연,음주예측변수당뇨병(타겟변수)에 가장 영향을 미치는건 흡연)

* 상위노드일수록 타겟변수를 잘 분리해 주는 예측변수를 선택하도록 함


소득 예측 모델 만들기

머신러닝 모델을 통한 소득 예측

 

# 의사결정나무 모델(지도학습) 만들기 순서 및 방법

1) 전처리

2) 모델 생성

3) 예측 및 성능 평가

 

# adult데이터 활용

* 미국인의 성별, 인종, 직업, 학력 등 인적 정보 CSV

48,842명, 변수 15개로 구성, 14개 변수는 예측변수, income변수는 타겟변수)

 

* adult데이터를 예측변수로 활용해 소득을 예측하는 모델 생성

* ML 프로젝트 생성

* assets 폴더 생성

* assets/adult.csv 복사

* pandas, numpy, scikit-learn, matplot 라이브러리 설치

 

# 크로스 밸리데이션

* 데이터를 분할해 일부는 모델 생성에 사용, 나머지는 성능 평가에 사용

* 트레이닝 세트 : 모델 생성에 사용되는 데이터

* 테스트 세트 : 성능을 평가하는데 사용되는 데이터

* scikit-learn 패키지를 이용해 트레이닝 세트와 테스트 세트로 분할

- test_size : 테스트 세트의 비율

- stratify : 범주 별 비율을 통일할 변수

- random_state : 난수 초기값

 

* 의사결정나무 노드 해석

1) 노드에 해당하는 관측치 비율 (전체 데이터의 몇 퍼센트가 해당 노드로 분류 되었는지)

2) 타겟 변수의 클래스별 비율 (알파벳 순)

3) 0.5 기준의 우세한 클래스 (타겟 변수의 두 클래스 중 어느쪽이 더 많은지)

4) 분리 기준 (노드 분리의 기준) :

여기서는 원핫인코딩으로 만들어진 기혼(1), 비혼(0) (원핫인코딩 : 특정값이면 1, 아니면 0으로 숫자화)\

value  클래스는 4번인 (기혼.미혼)데이터셋을 각각 23.9% / 76.1%로 나누고 분리합니다.

class=low(or hight) == low클래스는 income에 영향을 미친다고 볼 수 있습니다.

맨 위가  root 노드 , 다음은 브런치, 리프노드입니다.

각각 색이 짙을수록 우세합니다.

어떤 영향이 있을지는 어떻게하냐? 알고리즘이 진행함

 


1) 데이터 전처리 - 데이터 확인하기

 

Confusion Matrix( TF,FT, TT, FF) / 빅분기 필기시험

true label: 실제 데이터 predicted label: 예측데이터

* 정답여부:True/False, 예측방향:Positive/Negative

* 첫번째 열 모델이 2,382명(1,801+582)을high로 예측, 실제 high는 1,801, low는 582

* 두번째 열 모델이 12,270명(1,705+10,565)을 low로 예측, 실제 high는 1,705, low는 10,565

* 정확도(Accuracy, 예측해서 맞춘 비율) : TP+TN / TP+TN+FP+FN

/ 가장 중요함

* 정밀도(Precision, 관심클래스를 예측해서 맞춘 비율) : TP / TP +FP

* 재현율(Recall, 실제데이터에서 관심클래스를 찾아낸 비율) : TP / TP + FN

* F1 Score : 정밀도와 재현율을 함께 반영

 

 


실습 파일명 : 0825_01_desiciontree

# decisiontree.py

def p(str):
    print(str, '\n')

## 소득 예측 모델 만들기

# 라이브러리
import pandas as pd
import numpy as np

# 데이터 임포트
df = pd.read_csv("../assets/adult.csv")
#df.info()

# 데이터 전처리

# 연소득 5만달러 초과하면 high, 그렇지 않으면 low
df['income'] = np.where(df['income']=='>50K', 'high', 'low')
#p(df['income'].value_counts(normalize=True)) # 범주의 비율

# 타겟변수(=종속변수) 예측에 도움이 안되는 불필요한 변수 제거
df = df.drop(columns="fnlwgt")

# 원핫인코딩(one-hot encoding)을 이용한 문자타입변수를 숫자타입으로 변환
# 원핫인코딩 : 데이터를 모두 1이나 0으로 표시
target = df['income']
df = df.drop(columns='income') # 종속변수 제거
df = pd.get_dummies(df) # 원핫인코딩
df['income'] = target
df.info(max_cols=np.inf) # 변수의 수에 관계없이 모든 변수의 정보 출력

# scikit-learn을 이용하여 트레이닝 세트와 테스트 세트 분리
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)         # 난수 초기값
#p(df_train.shape)   # 트레이닝 세트의 행 개수, 변수 개수
#p(df_test.shape)    # 테스트 세트의 행 개수, 변수 개수

# 트레이닝 세트와 테스트 세트의 범주별 비율
#p(df_train["income"].value_counts(normalize=True))
#p(df_test["income"].value_counts(normalize=True))


## 의사결정나무 모델 생성

# 라이브러리 임포트
from sklearn import tree

# 의사결정나무분류기 생성
# random_state:랜덤시드값, max_depth:트리의최대깊이
clf = tree.DecisionTreeClassifier(random_state=1234, max_depth=3)

# 예측변수, 타겟변수 추출
train_x = df_train.drop(columns='income') # 예측변수 (전체 15개 변수 중 타겟변수를 제외)
train_y = df_train["income"] # 타겟변수 (예측변수를 통해서 예측하고자 하는 변수)

# 의사결정나무 모델 생성
# fit(X=예측변수, y=타겟변수)
model = clf.fit(X=train_x, y=train_y)

# 모델 시각화
import matplotlib.pyplot as plt

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

# 트리그래프
tree.plot_tree(
    model,  # 모델
    feature_names = train_x.columns, # 예측변수명들
    class_names = ['high', 'low'], # 타겟변수 클래스 (알파벳 오름차순)
    proportion = True, # 비율 표시 여부
    filled = True, # 채움 여부
    rounded = False, # 노드테두리 둥글게 할지 여부
    impurity = False, # 불순도 표시 여부
    label = 'root', # 제목 표시 위치
    fontsize = 10 # 글자 크기
)
plt.show()

 

의사결정나무

여기서 확인할 수 있는 자료는  root가 가장 큰 수입의 영향이 있고 그 다음은 결혼여부 그다음은 교육기간(학벌)

다음은 나이 등등 으로 의사결정나무가 뻗어나갑니다.


 

# 아래의 코드는 이어서 작성한 코드임

#comfusion_matrix

# 예측을 위한 예측변수, 타겟변수 추출 / test데이터 사용
test_x = df_test.drop(columns='income') #예측변수
test_y = df_test['income'] #타겟변수

# 모델을 통한 예측
df_test['pred'] = model.predict(test_x)
p(df_test)

#예측 성능 평가

# 컴퓨전매트릭스 라이브러리
from sklearn.metrics import confusion_matrix

# 컴퓨전매트릭스 생성
conf_mat = confusion_matrix(
    y_true = df_test['income'], #실제값
    y_pred = df_test['pred'], #예측값
    labels = ['high', 'low'] #레이블(클래스 배치 순서, 알파벳 오름차순)
)
p(conf_mat)

#컴퓨전매트릭스를 히트맵으로 표시



#그래프설정 되돌리기
#이전에 그래프설정을 초기화해주는 함수
plt.rcdefaults()

#라이브러리설치
from sklearn.metrics import ConfusionMatrixDisplay

p = ConfusionMatrixDisplay(
    confusion_matrix= conf_mat, #매트릭스 데이터
    display_labels= ('high', 'low'), #타겟변수 클래스명
)
p.plot(cmap='Blues') #컬러맵
plt.show()

 

오른쪽 이미지 출처(https://blog.naver.com/loyz/223424379648)

 

이제는 이 값들을 가지고 정확도를 구하는 함수를 작성했습니다.

#성능평가 지표 구하기
#라이브러리
import sklearn.metrics as metrics

# Accuracy : 정확도를 구하는 함수
acc = metrics.accuracy_score(
    y_true = df_test['income'], #실제값
    y_pred = df_test['pred'], #예측값
)
p(acc)

 

를 하면 값은0.8439227461953184 가 나오고 즉 84%가 나오게 됩니다.

 

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

 

정밀도(클래스별 정확도)는 0.7557700377675199 가 나오고 75.5%로 볼 수 있습니다.

 

 

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

재현율을 보면 0.5136908157444381 가 나오고 즉 51.3%입니다

 

 

# F1 Score

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

F1 Score은 0.6116488368143997 가 나오고 즉 61.1%입니다.

F1 Score은  재현율과 정밀도 그 사이로 나옵니다.


0825_02_decisionmodellib

# decisionmodellib.py

## 디씨젼트리 모델 라이브러리

# 라이브러리 로딩
import numpy as np
import pandas as pd
from sklearn.metrics import confusion_matrix, accuracy_score, classification_report
from sklearn.model_selection import train_test_split
from sklearn.tree import DecisionTreeClassifier, plot_tree
import matplotlib.pyplot as plt

# 데이터셋 로딩 함수
def importdata(filename):
    balance_data = pd.read_csv(
        filename,
        sep=',',
        header=None
    )
    print("데이터셋 길이 : ", len(balance_data))
    print("데이터셋 모양 : ", balance_data.shape)
    print("데이터셋 (상위 5행) : ", balance_data.head())
    return balance_data

#print(importdata(filename))

# 예측변수, 타겟변수 분리 함수
def splitdataset(balance_data):
    X = balance_data.values[:, 1:5] # 예측변수
    Y = balance_data.values[:, 0] # 타겟변수
    X_train, X_test, y_train, y_test = train_test_split(
        X, Y, test_size=0.3, random_state=100
    )
    return X, Y, X_train, X_test, y_train, y_test

# 디씨젼트리분류기 생성 및 학습 함수
def train_using_gini(X_train, X_test, y_train):
    clf_gini = DecisionTreeClassifier(
        criterion='gini', # 분류기의 종류(분류알고리즘)
        random_state=100, # 랜덤시드값
        max_depth=3, # 트리 뎁스
        min_samples_leaf=5 # leaf노드 최소 개수
    )
    clf_gini.fit(X_train, y_train)
    return clf_gini

# 예측 함수
def prediction(X_test, clf_object):
    y_pred = clf_object.predict(X_test)
    print("예측값 : ", y_pred)
    return y_pred

# 컨퓨젼매트릭스, 정확도, 레포트 출력 함수
def cal_accuracy(y_test, y_pred):
    print("컨퓨젼매트릭스 : ", confusion_matrix(y_test, y_pred))
    print("정확도 : ", accuracy_score(y_test, y_pred) * 100)
    print("레포트 : ", classification_report(y_test, y_pred))

# 디씨젼트리 가시화 함수
def plot_decision_tree(clf_object, feature_names, class_names):
    plt.figure(figsize=(15, 10))
    plot_tree(
        clf_object,
        filled=True,
        feature_names=feature_names,
        class_names=class_names,
        rounded=True
    )
    plt.show()
    
# 실행
filename = 'https://archive.ics.uci.edu/ml/machine-learning-databases/balance-scale/balance-scale.data'
df = importdata(filename)
X, Y, X_train, X_test, y_train, y_test = splitdataset(df)
clf = train_using_gini(X_train, X_test, y_train)
plot_decision_tree(
    clf,
    ['X1', 'X2', 'X3', 'X4'],
    ['L', 'B', 'R']
)

 


이렇게되면 함수는 일회용으로 사용할 가능성이 크기때문에 함수에 있는 상수인자값들을 모두 변환해주었습니다.

나중에 값이 변경되더라도 함수는 사용할 수 있게끔말이죠.

# decisionmodellib.py

## 디씨젼트리 모델 라이브러리

# 라이브러리 로딩
import numpy as np
import pandas as pd
from sklearn.metrics import confusion_matrix, accuracy_score, classification_report
from sklearn.model_selection import train_test_split
from sklearn.tree import DecisionTreeClassifier, plot_tree
import matplotlib.pyplot as plt


# 데이터셋 로딩 함수
def importdata(filename):
    balance_data = pd.read_csv(
        filename,
        sep=',',
        header=None
    )
    print("데이터셋 길이 : ", len(balance_data))
    print("데이터셋 모양 : ", balance_data.shape)
    print("데이터셋 (상위 5행) : ", balance_data.head())
    return balance_data


# print(importdata(filename))

# 예측변수, 타겟변수 분리 함수
def splitdataset(balance_data, X, Y, test_size, random_state):
    X_train, X_test, y_train, y_test = train_test_split(
        X, Y, test_size=test_size, random_state=random_state
    )
    return X, Y, X_train, X_test, y_train, y_test


# 디씨젼트리분류기 생성 및 학습 함수
def train_using(criterion, random_state, md, msl, X_train, X_test, y_train):
    clf = DecisionTreeClassifier(
        criterion=criterion,  # 분류기의 종류(분류알고리즘)
        random_state=random_state,  # 랜덤시드값
        max_depth=md,  # 트리 뎁스
        min_samples_leaf=msl  # leaf노드 최소 개수
    )
    clf.fit(X_train, y_train)
    return clf


# 예측 함수
def prediction(X_test, clf_object):
    y_pred = clf_object.predict(X_test)
    print("예측값 : ", y_pred)
    return y_pred


# 컨퓨젼매트릭스, 정확도, 레포트 출력 함수
def cal_accuracy(y_test, y_pred):
    print("컨퓨젼매트릭스 : ", confusion_matrix(y_test, y_pred))
    print("정확도 : ", accuracy_score(y_test, y_pred) * 100)
    print("레포트 : ", classification_report(y_test, y_pred))


# 디씨젼트리 가시화 함수
def plot_decision_tree(figsize, clf_object, feature_names, class_names):
    plt.figure(figsize=figsize)
    plot_tree(
        clf_object,
        filled=True,
        feature_names=feature_names,
        class_names=class_names,
        rounded=True
    )
    plt.show()


# 실행
filename = 'https://archive.ics.uci.edu/ml/machine-learning-databases/balance-scale/balance-scale.data'
df = importdata(filename)
X = df.values[:, 1:5]  # 예측변수
Y = df.values[:, 0]  # 타겟변수
X, Y, X_train, X_test, y_train, y_test \
    = splitdataset(df, X, Y, 0.3, 1234)
clf = train_using('gini', 1234, 3, 5, X_train, X_test, y_train)
plot_decision_tree(
    (15, 10),
    clf,
    ['X1', 'X2', 'X3', 'X4'],
    ['L', 'B', 'R']
)

 

최종적으로 컴퓨전매트릭스까지도 함수화를 만들어놓으면 그냥 언제든지 쓸 수 있는 함수로 사용할 수 있습니다.

# decisionmodellib.py

## 디씨젼트리 모델 라이브러리

# 라이브러리 로딩
import numpy as np
import pandas as pd
from sklearn.metrics import confusion_matrix, accuracy_score, classification_report
from sklearn.model_selection import train_test_split
from sklearn.tree import DecisionTreeClassifier, plot_tree
from sklearn.metrics import ConfusionMatrixDisplay
import matplotlib.pyplot as plt


# 데이터셋 로딩 함수
def importdata(filename):
    balance_data = pd.read_csv(
        filename,
        sep=',',
        header=None
    )
    print("데이터셋 길이 : ", len(balance_data))
    print("데이터셋 모양 : ", balance_data.shape)
    print("데이터셋 (상위 5행) : ", balance_data.head())
    return balance_data


# print(importdata(filename))

# 예측변수, 타겟변수 분리 함수
def splitdataset(balance_data, X, Y, test_size, random_state):
    X_train, X_test, y_train, y_test = train_test_split(
        X, Y, test_size=test_size, random_state=random_state
    )
    return X, Y, X_train, X_test, y_train, y_test


# 디씨젼트리분류기 생성 및 학습 함수
def train_using(criterion, random_state, md, msl, X_train, X_test, y_train):
    clf = DecisionTreeClassifier(
        criterion=criterion,  # 분류기의 종류(분류알고리즘)
        random_state=random_state,  # 랜덤시드값
        max_depth=md,  # 트리 뎁스
        min_samples_leaf=msl  # leaf노드 최소 개수
    )
    clf.fit(X_train, y_train)
    return clf


# 예측 함수
def prediction(X_test, clf_object):
    y_pred = clf_object.predict(X_test)
    print("예측값 : ", y_pred)
    return y_pred


# 컨퓨젼매트릭스, 정확도, 레포트 출력 함수
def cal_accuracy(y_test, y_pred):
    print("컨퓨젼매트릭스 : ", confusion_matrix(y_test, y_pred))
    print("정확도 : ", accuracy_score(y_test, y_pred) * 100)
    print("레포트 : ", classification_report(y_test, y_pred))


# 컨퓨젼매트릭스 가시화 함수
def plot_confusion_matrix(conf_mat, dl, cmap):
    p = ConfusionMatrixDisplay(
        confusion_matrix=conf_mat,  # 매트릭스 데이터
        display_labels=dl  # 타겟변수 클래스명
    )
    p.plot(cmap=cmap)  # 컬러맵
    plt.show()


# 디씨젼트리 가시화 함수
def plot_decision_tree(figsize, clf_object, feature_names, class_names):
    plt.figure(figsize=figsize)
    plot_tree(
        clf_object,
        filled=True,
        feature_names=feature_names,
        class_names=class_names,
        rounded=True
    )
    plt.show()


# 실행
filename = 'https://archive.ics.uci.edu/ml/machine-learning-databases/balance-scale/balance-scale.data'
df = importdata(filename)
X = df.values[:, 1:5]  # 예측변수
Y = df.values[:, 0]  # 타겟변수
X, Y, X_train, X_test, y_train, y_test \
    = splitdataset(df, X, Y, 0.3, 1234)
clf = train_using('gini', 1234, 3, 5, X_train, X_test, y_train)
plot_decision_tree(
    (15, 10),
    clf,
    ['X1', 'X2', 'X3', 'X4'],
    ['L', 'B', 'R']
)
순번 함수(매소드) 설명
1 tree.DecisionTreeClassifier(random_state=1234, max_depth=3) 의사나무결정 분류기 생성(옵션확인)
2 변수.flt(X= ,y= ) 의사결정나무 모델 생성 변수 (X= 예측변수, y=타겟변수)를 작성