Group Study (2024-2025)/Machine Learning 입문

[ML 입문] 4주차 스터디

chaeminyu 2024. 10. 30. 19:37

5-1. 로지스틱 회귀로 와인 분류하기

0. 개요

  • 결정 트리: 예, 아니오에 대한 질문을 이어나가며 정답을 찾아 학습하는 알고리즘
  • 불순도: 결정 트리가 최적의 질문을 하기 위한 기준. 사이킷런에서 지니 불순도와 엔트로피 불순도 제공

데이터 준비

  • 훈련세트와 테스트세트로 나눈 뒤 전처리
    • 두 세트 모두 적용되는 전처리 방식이 같다
import pandas as pd
wine = pd.read_csv('<https://bit.ly/wine_csv_data>')
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)

from sklearn.preprocessing import StandardScaler
ss = StandardScaler()
ss.fit(train_input)
train_scaled = ss.transform(train_input)
test_scaled = ss.transform(test_input)
  • 누락된 데이터가 있는지 확인하는 info() 함수
wine.info()
<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

1. 와인 분류하기

1-1. 로지스틱 회귀 적용

  • 로지스틱 회귀를 적용하여 얻은 데이터로 어떤 값이 어떤 영향을 끼치는지 추측 가능
from sklearn.preprocessing import StandardScaler

ss = StandardScaler()
ss.fit(train_input)

train_scaled = ss.transform(train_input)
test_scaled = ss.transform(test_input)

# 로지스틱 회귀 적용
from sklearn.linear_model import LogisticRegression

lr = LogisticRegression()
lr.fit(train_scaled, train_target)

print(lr.score(train_scaled, train_target)) # 0.7808350971714451
print(lr.score(test_scaled, test_target)) # 0.7776923076923077

1-2. 결정 트리 적용

  • 직관적 (예/아니오로 분류)
  • maxfeatures의 수 지정 시 지정된 수만큼만 사용
from sklearn.tree import DecisionTreeClassifier

dt = DecisionTreeClassifier(random_state=42)
dt.fit(train_scaled, train_target)

print(dt.score(train_scaled, train_target))
#0.996921300750433

print(dt.score(test_scaled, test_target))
#0.8592307692307692
  • 시각화 (트리 깊이 제한 없음)
import matplotlib.pyplot as plt
from sklearn.tree import plot_tree

# 그래프사이즈 설정, 가로 세로 inch기준
plt.figure(figsize=(10,7))
plot_tree(dt)
plt.show()

  • 시각화 (노드)
    • max_depth로 트리 깊이 제한
    • filled로 클래스에 맞게 노드 색 지정
    • features_names로 특성의 이름 전달
plt.figure(figsize=(10,7))
plot_tree(dt, max_depth=1, filled=True, feature_names=['alcohol', 'sugar', 'pH'])
plt.show()

 

1-3. 지니 불순도

  • gini (지니 불순도)
  • gini는 DecisionTreeClassifier 클래스의 criterion 매개 변수의 기본값
    • criterion의 매개 변수가 entrophy일 경우 → 엔트로피 불순도 사용
  • 자식 노드의 불순도를 샘플 개수에 비례하여 모두 더한 후 부모 노드의 불순도를 빼면 된다
  • 지니 불순도 = 0일 경우 순수 노드
  • 위 그림에서의 불순도 차이

 부모의 불순도 - (왼쪽노드 샘플 수 / 부모의 샘플 수) * 왼쪽 노드 불순도 - (오른쪽노드 샘플 수 / 부모의 샘플 수) * 오른쪽 노드 불순도 

  • 정보 이득 : 부모와 자식 노드 사이의 불순도 차이

1-4. 가지치기 하기

  • 간단한 방법 = 트리의 최대 깊이 지정
dt = DecisionTreeClassifier(max_depth=3, random_state=42)
dt.fit(train_scaled, train_target) # 전처리하지 않은 데이터

print(dt.score(train_scaled, train_target)) # 0.8454877814123533
print(dt.score(test_scaled, test_target)) # 0.8415384615384616
  • 시각화
plt.figure(figsize=(20,15))
plot_tree(dt, filled=True, feature_names=['alcohol', 'sugar', 'pH'])
plt.show()

5-2. 교차 검증과 그리드 서치

0. 개요

  • 일반화 성능의 올바른 예측을 위해 검증 세트, 교차 검증, 그리드 서치와 랜덤 서치 활용 → 하이퍼 파라미터 튜닝

1. 검증 세트

1-1. 데이터 준비

import pandas as pd
wine = pd.read_csv("<http://bit.ly/wine_csv_data>")

data = wine[['alcohol', 'sugar', 'pH']].to_numpy()
target = wine['class'].to_numpy()

1-2. 훈련/테스트/검증 세트 나누기

  • test_size 매개변수를 0.2로 정함
    • 훈련세트의 20%를 검증 세트로 만듦
  • 데이터가 많을 수록 좋음
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)
sub_input, val_input, sub_target, val_target = train_test_split(train_input, train_target, test_size=0.2, random_state=42)

1-3. 검증세트로 모델 평가

from sklearn.tree import DecisionTreeClassifier

dt = DecisionTreeClassifier(random_state=42)
dt.fit(sub_input, sub_target)

print(dt.score(sub_input, sub_target)) # 0.9971133028626413
print(dt.score(val_input, val_target)) # 0.864423076923077

2. 교차 검증

  • 검증 세트 만들 경우 훈련 세트가 줄어든다 ↔ 검증 세트를 조금 만들면 검증 점수가 불안정
  • ⇒ 교차 검증 사용시 안정적 점수 + 훈련에 많은 데이터 사용 둘 다 가능
  • k-폴드 교차 검증: 훈련 세트를 k개의 부분으로 나누어 교차 검증 수행
  • cross_validate() 교차 검증 함수 사용
    • 기본적으로 5개의 폴드
  • scores는 fit_time, score_time, test_score 키를 가진 딕셔너리 반환
  • test_score 키에 담긴 값 출력해서 교차 검증 최종 점수 확인
    • test_score에는 5개의 점수 평균값
from sklearn.model_selection import cross_validate

scores = cross_validate(dt, train_input, train_target)
print(scores)
import numpy as np

print(np.mean(scores['test_score'])) # 0.855300214703487
  • 교차 검증에서 훈련 세트를 섞고자 할 때 분할기 이용
    • cross_validate()가 회귀 모델일 경우 → KFold 분할기
    • cross_validate()가 분류 모델일 경우 → StratifiedKFold
from sklearn.model_selection import StratifiedKFold

scores = cross_validate(dt, train_input, train_target, cv=StratifiedKFold())
print(np.mean(scores['test_score'])) # 0.855300214703487

splitter = StratifiedKFold(n_splits=10, shuffle=True, random_state=42)
scores = cross_validate(dt, train_input, train_target, cv=splitter)
print(np.mean(scores['test_score'])) # 0.8574181117533719

3. 하이퍼파라미터 튜닝

  • 하이퍼 파라미터: 머신러닝 모델이 파라미터를 학습할 수 없어 사용자가 지정해야하는 파라미터
    • 사이킷런에서 그리드 서치 제공

3-1. 그리드 서치

  • 사이킷런의 GridSearchCv는 하이퍼파라미터 탐색과 교차 검증 한번에 수행
  • min_impurity_decrease 매개변수의 최적값을 찾아보자
from sklearn.model_selection import GridSearchCV

params = {'min_impurity_decrease': [0.0001, 0.0002, 0.0003, 0.0004, 0.0005]}

# 모델과 파라미터와 사용할 CPU 코어 개수 전달 (전부 사용)
gs = GridSearchCV(DecisionTreeClassifier(random_state=42), params, n_jobs=-1)

# 총 25번 계산 (cv 매개변수 기본값 = 5, 5 폴드 교차 검증 수행)
gs.fit(train_input, train_target)

# 최적의 매개변수는 best_params_에 저장
# 각 매개변수에서의 교차검증의 평균점수는 cv_result_속성의 mean_test_score에 저장
print(gs.best_params_) # {'min_impurity_decrease': 0.0001} 
print(gs.cv_results_['mean_test_score']) # [0.86819297 0.86453617 0.86492226 0.86780891 0.86761605]
  • 더 복잡한 매개변수 조합 탐색
    • 교차 검증 횟수 = 9 * 15 * 10 = 1350
    • 만들어지는 모델 = 6750 (5 폴드 교차 검증 수행)
params = {'min_impurity_decrease' : np.arange(0.0001, 0.001, 0.0001),
          'max_depth' : range(5,20,1),
          'min_samples_split' : range(2,100,10)
          }

gs = GridSearchCV(DecisionTreeClassifier(random_state=42), params, n_jobs=-1)
gs.fit(train_input, train_target)

print(gs.best_params_) # {'max_depth': 14, 'min_impurity_decrease': 0.0004, 'min_samples_split': 12}
# 최상의 교차 점수
print(np.max(gs.cv_results_['mean_test_score'])) # 0.8683865773302731

3-2. 랜덤 서치

  • 매개 변수 값이 수치 || 값의 범위나 간격을 미리 정하기 어려움 || 너무 많은 매개변수 조건 때문에 그리드 서치 오래걸림 ⇒ 랜덤 서치 추천
  • 매개 변수를 샘플링 할 수 있는 확률 분포 객체를 전달
# uniform(), randint()로 실수값과 정수값을 샘플링

from scipy.stats import uniform, randint
from sklearn.model_selection import RandomizedSearchCV

params = {'min_impurity_decrease' : uniform(0.0001, 0.001), #실수
          'max_depth' : randint(20, 50), #정수
          'min_samples_split' : randint(2, 25),
          'min_samples_leaf' : randint(1, 25),
          }
  • n_iter 매개변수로 샘플링 횟수를 지정
gs = RandomizedSearchCV(DecisionTreeClassifier(random_state=42), params, n_iter=100, n_jobs=-1, random_state=42)
gs.fit(train_input, train_target)

print(gs.best_params_) # {'max_depth': 39, 'min_impurity_decrease': 0.00034102546602601173, 'min_samples_leaf': 7, 'min_samples_split': 13}
print(np.max(gs.cv_results_['mean_test_score'])) # 0.8695428296438884
  • 최적의 모델로 테스트 세트의 성능 확인
dt = gs.best_estimator_
print(dt.score(test_input, test_target)) # 0.86

5-3. 트리의 앙상블

0. 개요

앙상블 학습: 더 좋은 예측 결과를 만들기 위해 여러개의 모델을 훈련하는 머신 러닝 알고리즘

  • 정형 데이터를 다루는데 좋음
    • 정형 데이터: 어떤 구조로 된 데이터
    • DB에 저장하기 쉽다
    • 비정형 데이터에는 신경망 알고리즘이 좋음 (딥러닝)
  • 결정 트리 기반으로 만들어져 있음

1. 랜덤 포레스트

  • 랜덤 포레스트: 결정 트리를 랜덤하게 만들어 결정 트리의 숲을 만듦 → 각 결정 트리의 예측을 사용해 최종 예측을 만듦
  • 각 트리 훈련을 위한 데이터를 랜덤하게 만든다
    • 입력된 훈련 데이터에서 랜덤하게 샘플 추출하여 훈련 데이터를 만든다 (중복되게 샘플 뽑을 수 있음 → 부트 스트랩 샘플)
    • 부트 스트랩 샘플: 중복을 허용한 샘플
  • RandomForestClassifier (분류 모델): 전체 특성 개수의 제곱근만큼의 특성 선택
  • RandomForestRegressor (회귀 모델): 전체 특성 사용
  • 사이킷런의 랜덤 포레스트는 100개의 결정트리를 기본적으로 훈련
    • 분류일 경우: 가장 높은 확률을 가진 클래스를 예측으로 삼는다
    • 회귀일 경우: 각 트리의 예측을 평균화

1-1. 랜덤 포레스트 적용 @ 분류모델

  • csv 파일을 numpy 배열로 변환 → 훈련세트와 테스트세트로 나눔
import numpy as np
import pandas as pd
from sklearn.model_selection import train_test_split

wine = pd.read_csv("<https://bit.ly/wine_csv_data>")

data = wine[['alcohol', 'sugar', 'pH']].to_numpy()
target = wine['class'].to_numpy()

train_input, test_input, train_target, test_target = train_test_split(data, target, random_state=42, test_size=0.2)
  • RandomForestClassifier 클래스로 모델을 만든 후, cross_validate()로 교차 검증 수행
from sklearn.model_selection import cross_validate #교차 검증
from sklearn.ensemble import RandomForestClassifier #랜덤포레스트 분류모델
# 랜덤포레스트 객체 생성
rf = RandomForestClassifier(n_jobs=-1, random_state=42)

# return_train_score로 훈련세트 점수도 같이 반환 / 기본값 false / 5폴드 교차검증
scores = cross_validate(rf, train_input, train_target, return_train_score=True, n_jobs=-1)

print(np.mean(scores['train_score']), np.mean(scores['test_score'])) # 0.9973541965122431 0.8905151032797809

→ 훈련세트 점수 > 검증세트 점수

⇒ 과대적합

  • 특정 중요도 출력 가능
rf.fit(train_input, train_target)
# 도수, 당도, pH
print(rf.feature_importances_) # [0.23167441 0.50039841 0.26792718]
  • Out Of Bag (OOB) 샘플: 부트스트랩 샘플에 포함되지 않고 남는 샘플
    • 검증 세트 역할 가능
    • oob_score=True로 지정해서 사용
    • OOB 점수 사용 시 교차 검증을 대신할 수 있음 → 훈련 세트에 더 많은 샘플 사용 가능
rf = RandomForestClassifier(n_jobs=-1, oob_score=True, random_state=42)
rf.fit(train_input, train_target)
rf.oob_score_ # 0.8934000384837406

2. 엑스트라 트리

엑스트라 트리: 위와 마찬가지로 100개의 결정 트리를 훈련 but 랜덤포레스트와 다르게 부트스트랩 샘플을 사용하지 않음

  • 결정 트리 만들 때 전체 훈련 세트 사용
  • 노드 분할 시 무작위 분할 (가장 좋은 부분을 찾지 않음)
  • DecisionTreeClassifier의 splitter 매개변수를 random으로 지정해서 사용할 수도 있음

2-1. 엑스트라 트리 적용

  • 사이킷런의 엑스트라 트리: ExtraTreesClassifier
from sklearn.ensemble import ExtraTreesClassifier

et = ExtraTreesClassifier(n_jobs=-1, random_state=42)
scores = cross_validate(et, train_input, train_target, return_train_score=True, n_jobs=-1)
print(np.mean(scores['train_score']), np.mean(scores['test_score'])) # 0.9974503966084433 0.8887848893166506
  • 무작위성이 더 크기에 더 많은 결정트리를 훈련해야하나 랜덤하게 노드를 분할하기에 빠른 계산 가능

3. 그레이디언트 부스팅

그레이디언트 부스팅: 깊이가 얕은 결정 트리를 사용하여 이전 트리의 오차를 보완하는 방식으로 앙상블하는 방법

  • 사이킷런의 GradientBoostingClassifier: 기본적으로 깊이가 3인 결정 트리 100개 사용
    • 깊이가 앝은 결정 트리 사용 → 과대적합에 강하고 높은 일반화 성능 기대 가능
  • 경사 하강법 사용하여 앙상블에 트리 추가
  • 분류 모델일 경우: 로지스틱 손실 함수 사용
  • 회귀 모델일 경우: 평균 제곱 오차 함수 사용

3-1. 그레디언트 부스팅 적용

from sklearn.ensemble import GradientBoostingClassifier
gb = GradientBoostingClassifier(random_state=42)
scores = cross_validate(gb, train_input, train_target, return_train_score=True, n_jobs=-1) # 훈련세트, 교차검증
print(np.mean(scores['train_score']), np.mean(scores['test_score'])) # 0.8881086892152563 0.8720430147331015
  • 위 교차 검증 결과를 통해 과대 적합에 강하다는 것을 확인
  • n_estimators (결정트리 개수) learning_rate (학습률, 기본값 0.1)를 증가시키면 성능 향상 가능
gb = GradientBoostingClassifier(n_estimators=500, learning_rate=0.2 ,random_state=42)
scores = cross_validate(gb, train_input, train_target, return_train_score=True, n_jobs=-1)
print(np.mean(scores['train_score']), np.mean(scores['test_score'])) # 0.9464595437171814 0.8780082549788999

4. 히스토그램 기반 그레이디언트 부스팅

히스토그램 기반 그레이디언트 부스팅: 정형 데이터를 다루는 머신러닝 알고리즘 중 인기짱

  • 입력 특성을 256개의 구간으로 나눔
  • 누락된 특성 전처리 필요 없음

4-1. 히스토그램 기반 그레이디언트 부스팅 적용

  • 성능을 높이려면 max_iter 매개변수 이용
from sklearn.experimental import enable_hist_gradient_boosting
from sklearn.ensemble import HistGradientBoostingClassifier

hgb = HistGradientBoostingClassifier(random_state=42)
scores = cross_validate(hgb, train_input, train_target, return_train_score=True)
print(np.mean(scores['train_score']), np.mean(scores['test_score'])) # 0.9321723946453317 0.8801241948619236
  • 위 결과를 토대로 과대적합을 억제하며 그레이디언트 부스팅보다 조금 더 높은 성능을 제공함을 확인
  • permutation_importance()함수로 특성 중요도를 계산
    • n_repeats 매개변수로 랜덤하게 섞을 횟수 지정 가능 (기본값 5)
from sklearn.inspection import permutation_importance

hgb.fit(train_input, train_target)

#특성 중요도, 평균, 표준편차 반환
#훈련세트 특성 중요도
result1= permutation_importance(hgb, train_input, train_target, n_repeats=10, random_state=42, n_jobs=-1)
print(result1.importances_mean) # [0.08876275 0.23438522 0.08027708]

#테스트세트 특성 중요도
result2= permutation_importance(hgb, test_input, test_target, n_repeats=10, random_state=42, n_jobs=-1)
print(result2.importances_mean) # [0.05969231 0.20238462 0.049     ]
print(hgb.score(test_input, test_target)) # 0.8723076923076923
  • 위 결과를 토대로 당도에 조금 더 집중함을 확인 가능

4-2. XGBoost

  • 사이킷런의 cross_validate()와 같이 사용 가능
  • tree_method 매개변수를 hist로 지정 시 히스토그램 기반 그레이디언트 부스팅을 사용 가능
from xgboost import XGBClassifier

xgb = XGBClassifier(tree_method='hist', random_state=42)
scores = cross_validate(xgb, train_input, train_target, return_train_score=True)
print(np.mean(scores['train_score']), np.mean(scores['test_score'])) # 0.8824322471423747 0.8726214185237284

4-3. LightGBM

  • cross_validate()와 같이 사용 가능
from lightgbm import LGBMClassifier

lgb = LGBMClassifier(random_state=42)
scores = cross_validate(lgb, train_input, train_target, return_train_score=True, n_jobs=-1)
print(np.mean(scores['train_score']), np.mean(scores['test_score'])) # 0.9338079582727165 0.8789710890649293
from lightgbm import LGBMClassifier

lgb = LGBMClassifier(random_state=42)
scores = cross_validate(lgb, train_input, train_target, return_train_score=True, n_jobs=-1)
print(np.mean(scores['train_score']), np.mean(scores['test_score'])) # 0.9338079582727165 0.8789710890649293

'Group Study (2024-2025) > Machine Learning 입문' 카테고리의 다른 글

[ML 입문] 6주차 스터디  (3) 2024.11.10
[ML 입문] 5주차 스터디  (1) 2024.11.06
[ML 입문] 3주차 스터디  (1) 2024.10.16
[ML 입문] 2주차 스터디  (0) 2024.10.07
[ML 입문] 1주차 스터디  (0) 2024.10.02