[Machine Learning] Decision Tree를 이용한 와인 데이터 분석

Yeong·2025년 4월 11일
post-thumbnail

데이터셋 소개

요런.. 와인 데이터셋이 있다.

이런 정보들이 담겨있다.

  • fixed acidity: 고정 산도
  • volatile acidity: 휘발성 산도
  • citric acid: 시트르산
  • residual sugar: 잔류 당분
  • chlorides: 염화물
  • free sulfur dioxide: 자유 이산화황
  • total sulfur dioxide: 총 이산화황
  • density: 밀도
  • pH: pH
  • sulphates: 황산염
  • alcohol: alcohol
  • quality: 0~10 (높을수록 좋은 품질)
  • color: red는 1, white는 0
    (red와 white 데이터가 따로 있었는데 concat하면서 넣어준 컬럼)

quality는 어떻게 분류되어있을까..?

3부터 9까지의 정수로 정의되어있다. (3~9등급)

분포를 확인하기 위해 히스토그램을 그려보면,

레드와 화이트 모두 5~6등급이 많다.

레드 와인/ 화이트 와인 분류기

레드 와인과 화이트 와인을 분류해주는 모델을 만들어보자.

먼저, 정답(라벨)을 분리해준다.

X = wine.drop(['color'], axis=1)
y = wine['color']

그리고, 데이터를 훈련용과 테스트용으로 나눠주기

from sklearn.model_selection import train_test_split
import numpy as np

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=13)

잘 나뉘어졌는지 확인

np.unique(y_train, return_counts=True)

훈련용과 테스트용이 quality 따라 어느정도 구분이 되었을까?

import plotly.graph_objects as go

fig = go.Figure()
fig.add_trace(go.Histogram(x=X_train['quality'], name='Train'))
fig.add_trace(go.Histogram(x=X_test['quality'], name='Test'))

fig.update_layout(barmode='overlay')
fig.update_traces(opacity=0.75)
fig.show()

Train, Test 데이터 모두 위 히스토그램과 비슷한 모양으로 나온다.

결정나무 훈련

DecisionTreeClassifier 로 훈련시키기~

from sklearn.tree import DecisionTreeClassifier

wine_tree = DecisionTreeClassifier(max_depth=2, random_state=13)
wine_tree.fit(X_train, y_train)

학습 결과를 확인해 보자.
train, test 데이터를 주고 각각 예측을 시켜봄

from sklearn.metrics import accuracy_score

y_pred_tr = wine_tree.predict(X_train)  
y_pred_test = wine_tree.predict(X_test)

accuracy_score(y_train, y_pred_tr), accuracy_score(y_test, y_pred_test)

요런 수치가 나왔다.

데이터 전처리 (MinMaxScaler & StandardScaler)

와인 데이터의 fixed acidity, chlorides, quality 컬럼을 갖고 Boxplot을 그려보자.

fig = go.Figure()
fig.add_trace(go.Box(y=X['fixed acidity'], name='fixed acidity'))
fig.add_trace(go.Box(y=X['chlorides'], name='chlorides'))
fig.add_trace(go.Box(y=X['quality'], name='quality'))

fig.show()

  • 컬럼들의 최대/최소 범위가 다르고, 평균과 분산도 각각 다르다.
  • 앞서 Scaler 개념에서 나왔듯이 이러한 특성(feature)의 편향 문제는 최적의 모델을 찾는데 방해가 될수도 있다.

MinMaxScaler와 StandardScaler를 사용해보자.
(결정나무에서는 이런 전처리는 의미를 가지지 않음. 주로 Cost Function 최적화 시 유효할 때가 있다고 함. 둘 중 뭐가 좋을지는 해봐야 암)

from sklearn.preprocessing import MinMaxScaler, StandardScaler

MMS = MinMaxScaler() # max, min
SS = StandardScaler()

SS.fit(X)
MMS.fit(X)

X_ss = SS.transform(X)
X_mms = MMS.transform(X)

X_ss_pd = pd.DataFrame(X_ss, columns=X.columns)
X_mms_pd = pd.DataFrame(X_mms, columns=X.columns)

MinMaxScaler 사용

fig = go.Figure()
fig.add_trace(go.Box(y=X_mms_pd['fixed acidity'], name='fixed acidity'))
fig.add_trace(go.Box(y=X_mms_pd['chlorides'], name='chlorides'))
fig.add_trace(go.Box(y=X_mms_pd['quality'], name='quality'))

fig.show()

최대값, 최소값이 1과 0으로 맞춰졌다.

StandardScaler 사용

fig = go.Figure()
fig.add_trace(go.Box(y=X_ss_pd['fixed acidity'], name='fixed acidity'))
fig.add_trace(go.Box(y=X_ss_pd['chlorides'], name='chlorides'))
fig.add_trace(go.Box(y=X_ss_pd['quality'], name='quality'))

fig.show()

평균 0, 표준편차 1로 맞춰졌다.

MinMaxScaler를 적용해서 학습

X_train, X_test, y_train, y_test = train_test_split(X_mms_pd, y, test_size=0.2, random_state=13)

wine_tree = DecisionTreeClassifier(max_depth=2, random_state=13)
wine_tree.fit(X_train, y_train)

y_pred_tr = wine_tree.predict(X_train)
y_pred_test = wine_tree.predict(X_test)

y_pred_tr = wine_tree.predict(X_train)
y_pred_test = wine_tree.predict(X_test)

accuracy_score(y_train, y_pred_tr), accuracy_score(y_test, y_pred_test)

(결정나무에서는 이런 전처리는 거의 효과가 없다.)

StandardScaler를 적용해서 학습

X_train, X_test, y_train, y_test = train_test_split(X_ss_pd, y, test_size=0.2, random_state=13)

wine_tree = DecisionTreeClassifier(max_depth=3, random_state=13)
wine_tree.fit(X_train, y_train)

y_pred_tr = wine_tree.predict(X_train)
y_pred_test = wine_tree.predict(X_test)

y_pred_tr = wine_tree.predict(X_train)
y_pred_test = wine_tree.predict(X_test)

accuracy_score(y_train, y_pred_tr), accuracy_score(y_test, y_pred_test)


모델 분석

다시 돌아와서, 결정나무는 레드와 화이트를 어떤 기준으로 분류했을까?

import matplotlib.pyplot as plt
from sklearn import tree

fig = plt.figure(figsize=(12, 10))
_ = tree.plot_tree(wine_tree, feature_names=list(X_train.columns), class_names=['W', 'R'],
                   rounded=True, filled=True)

확대해서 보면, total sulfur dioxide가 중요한 역할을 한다는 것을 알 수 있음

feature들의 중요도 파악해보기..!

Decision Tree 모델에는 feature_importances__라는 정보가 있는데, 이걸 보기 편하게 컬럼명과 합쳐서 보면

데이터를 분류할 때 각 컬럼들이 이런 중요도가 있었구나~ 하고 파악할 수 있다.
(디시젼 트리에 준 MaxDepth를 조정해 주면 이 수치에도 변화가 옴)


이진 분류해보기 (와인 맛 분류)

taste 컬럼을 생성해서, quality 컬럼 기준으로 맛을 1과 0으로 이진화함 (quality가 5 이상이면 1, 아니면 0)

Decision Tree

그리고 동일하게 데이터 나눠주고, fit~
(학습용 데이터에서 taste, quality 컬럼은 제거)

X = wine.drop(['taste', 'quality'], axis=1) 

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=13)

wine_tree.fit(X_train, y_train)

accuracy score 확인

y_pred_tr = wine_tree.predict(X_train)
y_pred_test = wine_tree.predict(X_test)

accuracy_score(y_train, y_pred_tr), accuracy_score(y_test, y_pred_test)

어떤 기준으로 분류했는지 확인~ (맛은 SoSo와 Good으로 이름 붙여줌 ㅎ)

fig = plt.figure(figsize=(12, 10))
_ = tree.plot_tree(wine_tree, feature_names=list(X_train.columns), class_names=['SOSO', 'GOOD'],
                   rounded=True, filled=True)

평가 지표 확인

개념 정리에서 배웠던 평가 지표들을 확인해보자

from sklearn.metrics import accuracy_score, precision_score, recall_score
from sklearn.metrics import recall_score, f1_score, roc_auc_score, roc_curve

print("accuracy_score", accuracy_score(y_test, y_pred_test))
print("recall_score", recall_score(y_test, y_pred_test))
print("precision_score", precision_score(y_test, y_pred_test))
print("roc_auc_score", roc_auc_score(y_test, y_pred_test))
print("f1_score", f1_score(y_test, y_pred_test))

다 불러와주고 출력

predict_proba는 데이터들이 0일 확률, 1일 확률을 알려줌

wine_tree.predict_proba(X_test)

ROC 커브도 그려보자~

import matplotlib.pyplot as plt

pred_proba = wine_tree.predict_proba(X_test)[:, 1] # probability 2번째 컬럼만 사용
fpr, tpr, thresholds = roc_curve(y_test, pred_proba)

plt.figure(figsize=(10,8))
plt.plot([0,1], [0,1], 'r')
plt.plot(fpr, tpr)
plt.grid()
plt.show()

이런 ROC 커브를 얻을 수 있었다..!





(이 글은 제로베이스 데이터 취업 스쿨의 강의 자료 일부를 발췌하여 작성되었습니다.)

profile
데이터 엔지니어 도전기 / 스터디 노트

0개의 댓글