4 분 소요

❗ MISSION ❗

화이트 와인인지 레드 와인인지 구별하기 -> HOW?

💡 sol 1. 로지스틱 회귀

1️⃣ 데이터 준비하기

타겟값이 화이트 와인(1)인지, 레드 와인(0)인지를 구해되는 상황이다. 둘 중에 하나를 타겟으로 정하는 것이므로 분류 모델을 사용하였다. 그 중에서 로지스틱 회귀 문제를 사용! 로지스티 회귀로 이진 분류하기

import pandas as pd
wine = pd.read_csv('https://bit.ly/wine_csv_data')
wine.head() # 판다스 데이터프레임으로 제대로 읽었는지 처음 5개 확인

wine.info()
wine.describe()

# 넘파이 배열로 바꾸기
data = wine[['alcohol', 'sugar', 'pH']].to_numpy()
target = wine['class'].to_numpy()

# 훈련 세트, 테스트 세트 분리
from sklearn.model_selection import train_test_split
train_input, test_input, train_target, test_target = train_test_split(data, target, test_size = 0.2, random_state=42)
print(train_input.shape, test_input.shape) # 훈련 세트 (5197, 3), 테스트 세트 (1300, 3)

# 표준화
from sklearn.preprocessing import StandardScaler
ss = StandardScaler()
ss.fit(train_input)
train_scaled = ss.transform(train_input)
test_scaled = ss.transform(test_input)

>>> 출력값
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 6497 entries, 0 to 6496
Data columns (total 4 columns):
 #   Column   Non-Null Count  Dtype
---  ------   --------------  -----
 0   alcohol  6497 non-null   float64
 1   sugar    6497 non-null   float64
 2   pH       6497 non-null   float64
 3   class    6497 non-null   float64

dtypes: float64(4)
memory usage: 203.2 KB
alcohol	sugar	pH	class
count	6497.000000	6497.000000	6497.000000	6497.000000
mean	10.491801	5.443235	3.218501	0.753886
std	1.192712	4.757804	0.160787	0.430779
min	8.000000	0.600000	2.720000	0.000000
25%	9.500000	1.800000	3.110000	1.000000
50%	10.300000	3.000000	3.210000	1.000000
75%	11.300000	8.100000	3.320000	1.000000
max	14.900000	65.800000	4.010000	1.000000
  • 판다스 데이터프레임에 유용한 메소드
    • info() : 각 열의 데이터 타입과 누락된 데이터가 있는지 확인에 유용
    • describe() : 열에 대한 간략한 통계를 출력, 위 결과값을 보고 스케일이 다름을 확인할 수 있다.

그리고! 훈련을 하기 위해서 데이터를 준비하는 과정은 항상 유사하다.

  1. 데이터 불러오기(check: wine.head())
  2. 넘파이 배열로 바꾸기 / 이때, 타깃값과 그 외의 클래스를 분리하여 넘파이 배열로 분리시킵니다 (~~.to_numpy())
  3. 훈련 세트와 테스트 세트로 분리(from sklearn.model_selection import train_test_split)
  4. (필요하다면) 표준화(from sklearn.preprocessing import StandardScaler)

2️⃣ 로지스틱 회귀 모델 사용

# 로지스틱 회귀 모델 사용
from sklearn.linear_model import LogisticRegression
lr = LogisticRegression()
lr.fit(train_scaled, train_target)
print(lr.score(train_scaled, train_target))
print(lr.score(test_scaled, test_target))
print(lr.coef_, lr.intercept_)

>>> 출력값
0.7808350971714451
0.7776923076923077
[[ 0.51270274  1.6733911  -0.68767781]] [1.81777902]

❗ 문제점 ❗
일단은 점수가 전체적으로 높지 않으므로 과소적합이 되었다.

💡 해결책 💡

  1. 규제 매개변수 값을 바꾼다.
  2. solver 매개변수를 통해서 선형 모델이 아닌 다른 알고리즘을 선택한다.
  3. 결정 트리

🔮 sol 2. 결정 트리

결정 트리 모들은 이유를 설명하기 쉽다. -> 왤까?

📍 점수 확인

from sklearn.tree import DecisionTreeClassifier
dt = DecisionTreeClassifier(random_state=42)
dt.fit(train_scaled, train_target)
print(dt.score(train_scaled, train_target)) # 훈련 세트
print(dt.score(test_scaled, test_target)) # 테스트 세트

>>> 출력값
0.996921300750433
0.8592307692307692

훈련 세트 점수가 테스트 세트 점수보다 높게 나와서 현재는 과대적합된 모델이라고 할 수 있다. 근데 결정 트리 클래스 DecisionTreeClassifier 는 plot_tree() 함수를 사용해 결정 트리를 이해하기 쉬운 그림으로 출력해줄 수 있다. 트리의 결정 구조를 확인한 후, 과대적합을 줄여보도록 하자!

import matplotlib.pyplot as plt
from sklearn.tree import plot_tree
plt.figure(figsize=(10, 7))
plot_tree(dt)
plt.show()

download1
그림 배경이 투명이라 잘 안보일 것 같긴 하지만,,, 트리를 통해서 훈련된 트리의 모습을 볼 수 있다. 🌳 로 표시된 해더는 plot_tree() 함수의 매개변수 또는 관련 내용이다.

🌳 max_depth 매개변수

위 트리 그림을 보면 너무 작아서 노드 안의 기준점이 하나도 보이지 않는다. 이를 볼 수 있게, 출력되는 노드의 개수를 조정하는 매개변수가 max_depth 매개변수이다.

plt.figure(figsize=(10, 7))
plot_tree(dt, max_depth=1, filled=True, feature_names=['alcohol', 'sugar', 'pH'])
plt.show()

download2

코드를 보면 max_depth=1 이라고 지정을 해놓았는데, 이는 루트 노드를 제외한 노드 1개만 출력해주는 것을 알 수 있다. 노드 가지의 왼쪽은 YES, 오른쪽은 NO를 의미하며, 기준점은 노드의 제일 위에 적혀있는 input 값이다. gini는 불순도로 다음 절에서 설명할 예정. samples는 총 샘플의 수를 의미하고 value는 클래스별 샘플 수를 의미한다.

🌳 filled 매개변수

filled = true 를 지정하면 클래스마다 색깔을 부여하고, 어떤 클래스의 비율이 높아질수록 점점 진한색을 나타낸다.

🌳 gini 매개변수

gini 매개변수는 지니 불순도를 의미한다. DecisionTreeClassifier 클래스의 criterion 매개변수의 기본값이 gini 이다. criterion 매개변수의 용도는 노드에서 데이터를 분할할 기준을 정하는 것으로 루트 노드의 당도가 왜 하필 -0.239를 기준으로 나뉘게 되었는지를 알 수 있다.

지니 불순도 = 1 - (음성 클래스 비율2 + 양성 클래스 비율2)

결정 트리 모델은 부모 노드와 자식 노드의 불순도 차이가 가능한 크도록 성장시킨다. 이 불순도 차이를 정보 이득이라고 하고, 정보 이득이 최대가 되도록 데이터를 나누는 것이다. 지니가 제곱을 통해서 불순도를 계산했다면 criterion=’entropy’라는 값은 로그를 통해서 불순도를 검사한다. 하지만 이는 부모 노드와 자식 노드간의 차이가 전체적으로 적게 나므로 이 책에서는 지니 불순도를 사용한다.

🌳 가지치기

아까 과대적합의 문제가 발생했다. 이 문제를 해결하기 위해서는 적절한 가지치기가 필요하다. 가지치기는 max_depth를 통해서 이루어지고 max_depth를 결정 지은 다음 이 숫자만큼을 훈련시키므로써 가지치기를 통해 훈련 점수를 제어한다.

# 가지치기
dt = DecisionTreeClassifier(max_depth=3, random_state=42)
dt.fit(train_scaled, train_target)
print(dt.score(train_scaled, train_target))
print(dt.score(test_scaled, test_target))

plt.figure(figsize=(20, 15))
plot_tree(dt, filled=True, feature_names=['alcohol', 'sugar', 'pH'])
plt.show()

>>> 출력값
0.8454877814123533
0.8415384615384616

download3

위 사진을 보면 파란 계열 노드가 있고 빨간 계열 노드가 있다. 파란 계열 노드는 양성 클래스가 많은 비율을 차지하는 경우이고, 빨간 노드는 음성 클래스가 많은 비율을 차지하는 경우이다. 자세히 보면 인풋값도 노드들마다 다른데 이를 통해서 해당 범위에 맞는 샘플들을 결정해 나아간다. 정말 중요한점은 결정 트리 알고리즘은 스케일이 필요없으므로 표준화 전처리 과정이 필요 없다.

🌗 결과

# 트리는 표준화 전처리가 필요 없음
dt = DecisionTreeClassifier(max_depth=3, random_state=42)
dt.fit(train_input, train_target)
print(dt.score(train_input, train_target))
print(dt.score(test_input, test_target))

plt.figure(figsize=(20, 15))
plot_tree(dt, filled=True, feature_names=['alcohol', 'sugar', 'pH'])
plt.show()

>>> 출력값
0.8454877814123533
0.8415384615384616

자세히 보면 전처리된 데이터가 아니라 전처리 전 데이터라는 점,,! download3

댓글남기기