4주차 | 머신러닝(Machine learning)(2)

KT AIVLE SCHOOL 5기 4주차에 진행한 머신러닝(Machine learning) 강의 내용 정리 글입니다.

K-Fold Cross Validation

(1) Random Split의 문제

  • 새로운 데이터에 대한 모델의 성능을 예측 못한 상태로 최종 평가 수행 → 더욱 정교한 평가 절차가 필요

(2) 개념

  • 모든 데이터가 평가에 한 번, 학습에 k-1번 사용 (단 k ≥ 2)
  • K개의 분할에 대한 성능을 예측 → 평균과 표준편차 계산 → 일반화 성능
  • 학습 데이터로 K-Fold Cross Validation 진행해야 함 (중요)
  • 장점과 단점
    • 장점
      • 모든 데이터 학습과 평가에 사용 가능
      • 과소적합 문제 방지 가능, 좀 더 일반화된 모델 만들 수 있음
    • 단점
      • 반복 횟수가 많아서 모델 학습과 평가에 많은 시간이 소요

(3) 실습

# 1. 데이터 준비
# Target 확인
target = 'Outcome'
# 데이터 분리
x = data.drop(target, axis=1)
y = data.loc[:, target]
# 학습용, 평가용 데이터 7:3으로 분리
x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.3, random_state=1)
# 정규화
scaler = MinMaxScaler()
x_train_s = scaler.fit_transform(x_train)
x_test_s = scaler.transform(x_test)

# 2. 성능 예측
models = {'Decision Tree' : DecisionTreeClassifier(max_depth = 5, random_state=1),
              'KNN' : KNeighborsClassifier(),
              'Logistic Regression' : LogisticRegression()}

def cross_val_models_classification(models, cv = 10, scoring = 'accuracy'):
    result = {}
    for model_name, model in models.items():
        if model_name == 'KNN':
            cv_score = cross_val_score(model, x_train_s, y_train, cv = cv, scoring = scoring)
        else:
            cv_score = cross_val_score(model, x_train, y_train, cv = cv, scoring = scoring)

        print(model_name, cv_score)
        print('평균:', cv_score.mean())
        print('표준편차:', cv_score.std())
        print()

        result[model_name] = cv_score.mean()
    
    return result

result = cross_val_models_classification(models)

# 시각화
plt.figure(figsize = (5, 3))
plt.barh(y = list(result), width = result.values())
plt.show()

# 성능 평가

model = models['Decision Tree']
model.fit(x_train, y_train)
y_pred = model.predict(x_test)

print(confusion_matrix(y_test, y_pred))
print(classification_report(y_test, y_pred))

Hyperparameter 튜닝

(1) Hyperparameter

  • 모델의 성능 향상을 위해 최선의 하이퍼파라미터 값을 찾는 다양한 시도를 해야 함
    • Grid Search, Random Search
  • KNN : k 값이 작을수록 복잡, 거리 계산법에 따라 성능이 달라질 수 있음
  • Decision Tree
    • max_depth : 작을수록 모델 단순
    • min_samples_leaf, min_samples_split : 클수록 단순

(2) Random Search, Grid Search

  • Grid Search : 파라미터 값 범위 모두 탐색, Random Search : 파라미터 값 범위에서 몇 개 선택
  • Grid Search: 내부적인 K-Fold Cross Validation을 위해 cv 값을 지정 → 실제 수행되는 횟수: 파라미터 조합 수 x cv
  • Random Search : n_iter로 지정한 수행 횟수 만큼의 파라미터 조합 수 탐색
  • 유용한 탐색 방법: Random Search로 넓은 범위를 탐색 후 최적의 파라미터 조합 주변을 Grid Search 로 탐색
  • 모델링 목표 : 적절한 예측력을 위해 적절한 복잡도의 모델 완성

(3) 실습

# 1. 데이터 준비
target = 'medv'
x = data.drop(target, axis=1)
y = data.loc[:, target]
x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.3, random_state=1)

# 2. 성능 예측
model_dt = DecisionTreeRegressor(random_state = 1)
cv_score = cross_val_score(model_dt, x_train, y_train , cv = 10, scoring = 'r2')
print(cv_score)
print(cv_score.mean())
print(cv_score.std())

# 3. 모델 튜닝
# 파라미터 선언
  # max_depth: 1~50
param = {'max_depth' : range(1, 51)}

# Random Search 선언
  # cv=5
  # n_iter=20
  # scoring='r2'
model = RandomizedSearchCV(model_dt,          # 기본 모델
                           param,            # 파라미터 범위
                           cv = 5,            # K-Fold 개수
                           n_iter = 20,      # 선택할 임의 파라미터 개수
                           scoring = 'r2')   # 평가 방법

## GridSearchCV 선언
# model = GridSearchCV(model_dt,          # 기본 모델
#                      param,            # 파라미터 범위
#                      cv = 5,            # K-Fold 개수
#                      scoring = 'r2')   # 평가 방법

# 학습하기
model.fit(x_train, y_train)

# 중요 정보 확인
print('=' * 80)
print(model.cv_results_['mean_test_score'])
print('-' * 80)
print('최적파라미터:', model.best_params_)
print('-' * 80)
print('최고성능:', model.best_score_)
print('=' * 80)

# 변수 중요도
plt.figure(figsize=(5, 5))
plt.barh(y=list(x), width=model.best_estimator_.feature_importances_)
plt.show()

# 4. 성능 평가
# 예측하기
y_pred = model.predict(x_test) # 그냥 모델로 해도 최고 성능 파라미터 조합으로 예측 수행
# 평가하기
print('MAE:', mean_absolute_error(y_test, y_pred))
print('R2-Score:', r2_score(y_test, y_pred))

앙상블(Ensemble)

(1) 앙상블 이해

  • 여러개의 모델을 결합하여 훨씬 강력한 모델을 생성하는 기법
  • 캐글(Kaggle)과 같은 대회에서 상위 순위 많이 차지
  • 방법: 보팅, 배깅, 부스팅, 스태킹

(2) 보팅(Voting)

  • 여러 모델들(다른 유형의 알고리즘 기반)의 예측 결과투표를 통해 최종 예측 결과를 결정하는 방법
    • 하드 보팅 : 다수 모델이 예측한 값이 최종 결과
    • 소프트 보팅 : 모든 모델이 예측한 레이블 값의 결정 확률 평균 중에서 가장 확률이 높은 값 최종 선택

(3) 배깅(Bagging)

  • 정의 및 특징
    • Bootstrap Aggregating의 약자
    • 데이터로부터 부트스트랩 한 데이터로 모델들을 학습 → 모델들의 예측 결과를 집계해 최종 결과를 얻는 방법
    • 같은 유형의 알고리즘 기반 모델들 사용
    • 데이터 분할 시 중복 허용(복원 랜덤 샘플링 방식이라고 함)
    • 범주형: 투표 방식, 수치형: 평균 → 결과 집계 방식
  • Random Forest
    • 정의 및 특징
      • 대표적인 배깅 알고리즘
      • 여러 Decision Tree 모델이 전체 데이터에서 배깅 방식으로 각자의 데이터 샘플링
      • 모델들이 개별적으로 학습을 수행한 뒤 모든 결과를 집계하여 최종 결과 결정
    • 두 가지 의미의 Random
      • 랜덤하게 데이터 샘플링
      • 개별 모델이 트리 구성 할 때 분할 기준이 되는 Feature 랜덤하게 선정
        • 무작위로 뽑은 n개의 Feature들 중에서 가장 정보 이득이 큰 Feature를 기준으로 트리 분할 → 개별 모델마다 다른 구조의 트리를 구성할 것임
    • 주요 하이퍼파라미터
      • 대부분 Decison Tree와 같은 하이퍼파라미터 가짐
      • n_estimators : 만들어질 Decision tree 개수
      • max_feature : 최선의 분할을 위해 고려할 Feature 수

(4) 부스팅(Boosting)

  • 정의 및 특징
    • 같은 유형의 알고리즘 기반 모델 여러 개에 대해 순차적으로 학습을 수행
    • 이전 모델이 제대로 예측하지 못한 데이터에 대해서 가중치를 부여하여 다음 모델이 학습과 예측을 진행하는 방법
    • 예측 성능이 뛰어나 앙상블 학습을 주도
    • 배깅에 비해 성능이 좋지만, 속도가 느리고 과적합 발생 가능성이 있음 → 상황에 맞게 적절히 사용해야 함
    • 대표적인 부스팅 알고리즘: XGBoost, LightGBM
  • XGBoost(eXtreme Gradient Boosting)
    • 정의 및 특징
      • GBM(Gradient Boost Machine) : 부스팅을 구현한 대표적인 알고리즘 중 하나
      • GBM 알고리즘을 병렬 학습이 가능하도록 구현한 것이 XGBoost
      • 회귀, 분류 문제를 모두 지원하며, 성능과 자원 효율이 좋아 많이 사용됨
      • XGBoost 장점: 높은 예측 성능, 빠른 수행 시간(GBM 대비), 규제(regularization), 가지치기(Tree Pruning), 내장된 교차 검증(Cross Validation), 결측치 자체 처리(그래도 명시적으로 결측치 처리 진행 권고)
      • 하지만 여전히 느려서 빠른 LightGBM 사용하는게 좋음
    • 주요 하이퍼파라미터
      • n_estimators : weak learner 개수로, 개수가 많을 수록 일정 수준까지는 성능이 좋아질 수 있음
      • max_depth : 트리 기반 알고리즘의 max_depth와 같은 의미

(5) 스태킹(Stacking)

  • 여러 모델의 예측 값을 최종 모델의 학습 데이터로 사용하여 예측하는 방법
  • 예시
    • KNN, Logistic Regression, XGBoost 모델을 사용해 4종류 예측값을 구한 후 이 예측 값을 최종 모델인 Randomforest 학습 데이터로 사용
  • 현실 모델에서 많이 사용되지 않으며, 캐글(Kaggle) 같은 미세한 성능 차이로 승부를 결정하는 대회에서 사용됨
  • 기본 모델로 4개 이상 선택해야 좋은 결과를 기대할 수 있음

(6) 실습

# 1. 데이터 준비
def data_preprocess(data, target):
    # 1. X, y 분리
    X = data.drop(target, axis = 1)
    y = data.loc[:, target]
    
    # 2. 학습용, 평가용 분리
    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.3, random_state = 1)
    
    # 3. 정규화(KNN을 위해서)
    scaler = MinMaxScaler()
    X_train_s = scaler.fit_transform(X_train)
    X_test_s = scaler.transform(X_test)
    
    return X_train, X_test, y_train, y_test, X_train_s, X_test_s
 
# 2. 모델링
model_rf = RandomForestClassifier(max_depth = 5, random_state = 1)
model_xgb = XGBClassifier(max_depth = 5, random_state = 1)
model_lgb = LGBMClassifier(max_depth = 5, 
                       random_state = 1,  
                       importance_type = 'gain',  # split: 분기 횟수(default), gain: 정보 이득 횟수
                       verbose = -1) # verbose : 음수값을 주면 학습 과정 생략

# Feature 중요도 확인
plt.barh(y = list(x), width = model.feature_importances_)
plt.show()

# Feature 중요도 확인(정규화) -> LightGBM은 수치로 나오기 때문에 비율로 보고싶다면 아래와 같이 사용 가능
fi_norm = model.feature_importances_ / np.sum(model.feature_importances_)
plt.barh(y = list(x), width = fi_norm)
plt.show()

종합 실습

분류

# 1. 환경 준비
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import warnings
warnings.filterwarnings(action='ignore')
%config InlineBackend.figure_format = 'retina'
from sklearn.model_selection import train_test_split, GridSearchCV
from sklearn.preprocessing import MinMaxScaler
from sklearn.neighbors import KNeighborsClassifier
from sklearn.tree import DecisionTreeClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.ensemble import RandomForestClassifier
from xgboost import XGBClassifier
from lightgbm import LGBMClassifier
from sklearn.metrics import *

# 2. 데이터 이해
data.head()
data.describe()

# 3. 데이터 준비
def data_preprocess(data, drop_cols, dumm_cols , target):
    # 1. 변수 제거
    data.drop(drop_cols, axis = 1, inplace = True)
    
    # 2. X, y 분리
    X = data.drop(target, axis = 1)
    y = data.loc[:, target]
    
    # 3. 가변수화
    X = pd.get_dummies(X, columns = dumm_cols, drop_first = True, dtype = int)
    
    # 4. 학습용, 평가용 분리
    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.3, random_state = 1)
    
    # 5. 정규화(KNN을 위해서)
    scaler = MinMaxScaler()
    X_train_s = scaler.fit_transform(X_train)
    X_test_s = scaler.transform(X_test)
    
    return X_train, X_test, y_train, y_test, X_train_s, X_test_s

# 4. 성능 예측
def cv_classification(models, cv = 10, scoring = 'accuracy'):
    result = {}
    for model_name, model in models.items():
        if model_name == 'KNN':
            cv_score = cross_val_score(model, X_train_s, y_train, cv = cv, scoring = scoring)
        else:
            cv_score = cross_val_score(model, X_train, y_train, cv = cv, scoring = scoring)
        
        result[model_name] = cv_score.mean()
    return result
    
# 5. 결과 확인
# 성능 비교
print('=' * 40)
for m_name, score in result.items():
    print('*', m_name, score.round(3))
print('=' * 40)
# 성능 시각화 비교
plt.barh(list(result.keys()), result.values())
plt.show()

# 6. 성능 튜닝
def grid_tuning_class(model, param, cv = 5, scoring = 'accuracy'):
    
    model = GridSearchCV(model, param, cv = cv, scoring = scoring)
    
    # 학습하기(많은 시간이 소요될 수 있음)
    model.fit(X_train, y_train)
    return model

# 변수 중요도 시각화
plt.barh(list(X_train), model.best_estimator_.feature_importances_)
plt.show()

# 7. 성능 평가
y_pred = model.predict(X_test)
print(confusion_matrix(y_test, y_pred))
print(classification_report(y_test, y_pred))

회귀

# 1. 환경 준비
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import warnings
warnings.filterwarnings(action='ignore')
%config InlineBackend.figure_format = 'retina'
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import MinMaxScaler
from sklearn.linear_model import LinearRegression
from sklearn.neighbors import KNeighborsRegressor
from sklearn.tree import DecisionTreeRegressor
from sklearn.ensemble import RandomForestRegressor
from xgboost import XGBRegressor
from lightgbm import LGBMRegressor
from sklearn.model_selection import cross_val_score, GridSearchCV
from sklearn.metrics import *

# 2. 데이터 이해
data.head()
data.describe()
data.info()
def eda_num(num_cols, data):
    print('<<Numerical cols>>\n')
    print('=' * 80)
    for i in range(len(num_cols)):
        print(data[[num_cols[i]]].describe().T)
        print()
    print('=' * 80)
    
    plt.figure(figsize = (18, 9))
    for i in range(len(num_cols)):
        plt.subplot(3, 2, (2 * i) + 1)
        sns.histplot(x = num_cols[i], data = data, kde = True)
        plt.subplot(3, 2, (2 * i) + 2)
        sns.boxplot(x = num_cols[i], data = data)
    plt.show()
    
def eda_cat(cat_cols, data):
    print('\n<<Categorical cols>>\n')
    print('=' * 80)
    for i in range(len(cat_cols)):
        print(data[cat_cols[i]].value_counts())
        print()
    print('=' * 80)
    
    plt.figure(figsize = (18, 6))
    for i in range(len(cat_cols)):
        plt.subplot(1, 3, i + 1)
        sns.countplot(x = cat_cols[i], data = data)
    plt.show()

def eda_num_num(num_cols, target, data):
    print('\n<<Numerical cols -> target>>\n')
    plt.figure(figsize = (18, 6))
    for i in range(len(num_cols)):
        plt.subplot(1, 3, i + 1)
        sns.scatterplot(x = num_cols[i], y = target, data = data)
    plt.show()
    
    corr_matrix = data.corr()
    sns.heatmap(corr_matrix, annot = True, cmap = 'Blues', cbar = False, square = True, fmt = '.2f', annot_kws = {'size' : 8})
    plt.show()
    
def eda_cat_num(cat_cols, target, data):
    print('\n<<Categorical cols -> target>>\n')
    plt.figure(figsize = (18, 6))
    for i in range(len(cat_cols)):
        plt.subplot(1, 3, i + 1)
        sns.barplot(x = cat_cols[i], y = target, data = data)
    plt.show()

# 3.데이터 준비
def data_preprocess(data, dumm_cols , target):
    # 1. X, y 분리
    X = data.drop(target, axis = 1)
    y = data.loc[:, target]
    
    # 2. 가변수화
    X = pd.get_dummies(X, columns = dumm_cols, drop_first = True, dtype = int)
    
    # 3. 학습용, 평가용 분리
    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.3, random_state = 1)
    
    # 4. 정규화(KNN을 위해서)
    scaler = MinMaxScaler()
    X_train_s = scaler.fit_transform(X_train)
    X_test_s = scaler.transform(X_test)
    
    return X_train, X_test, y_train, y_test, X_train_s, X_test_s

# 4.성능 예측
def cv_regression(models, cv = 10, scoring = 'r2'):
    result = {}
    for model_name, model in models.items():
        if model_name == 'KNN':
            cv_score = cross_val_score(model, X_train_s, y_train, cv = cv, scoring = scoring)
        else:
            cv_score = cross_val_score(model, X_train, y_train, cv = cv, scoring = scoring)
        
        result[model_name] = cv_score.mean()
    return result
    
    
# 5.결과 확인
# 성능 비교
print('=' * 40)
for m_name, score in result.items():
    print(m_name, score.round(3))
print('=' * 40)
# 성능 시각화 비교
plt.barh(list(result.keys()), result.values())
plt.show()

# 6.성능 튜닝
def grid_tuning_reg(model, param, cv = 5, scoring = 'r2'):
    
    model = GridSearchCV(model, param, cv = cv, scoring = scoring)
    
    # 학습하기(많은 시간이 소요될 수 있음)
    model.fit(X_train, y_train)
    return model
    
# 변수 중요도 시각화
plt.barh(y = list(X_train), width = model.best_estimator_.feature_importances_)
plt.show()

# 7.성능 평가
y_pred = model.predict(X_test)
print(mean_absolute_error(y_test, y_pred))
print(r2_score(y_test, y_pred))