Developer's Development

3.2.14 [모델 검증 및 평가] 모델 검증 및 모델 성능 고도화 본문

데이터 분석과 머신러닝, 딥러닝/모델 검증 및 평가

3.2.14 [모델 검증 및 평가] 모델 검증 및 모델 성능 고도화

mylee 2025. 8. 3. 18:41
모델 검증

 

모델 성능 평가는 데이터셋에 대해 모델이 어떻게 동작하는지에 대한 평가이다. 성능 평가 방법에는 교차검증, 임의 분할 교차검증 외에도 혼동 행렬(Confusion Matrix)을 통한 성능 분석이나 Roc Curve, AUC 등을 활용한 평가 방법이 있다. 성능이 높은 모델은 다양한 지표에서 균형을 맞추며, 과적합을 방지하고 실제 환경에서도 잘 작동한다.

 

  • 교차 검증 (Cross-Validation)

데이터를 여러 폴드로 나누어 학습과 검증을 반복함으로써 모델의 일반화 성능을 평가하는 방법이다.

K-폴드 교차검증이 가장 널리 사용된다.

이를 통해 데이터셋이 작은 경우에도 과적합을 방지하고, 모델의 성능을 더욱 정확하게 평가할 수 있다.

 

👉🏻 K-폴드 교차검증

데이터를 K개의 폴드로 나누고, 각 폴드를 한 번씩 검증 데이터로 사용하고 나머지를 학습 데이터로 사용하여 모델을 학습시킨 후, 각 폴드에서의 성능을 평균하여 모델의 성능을 평가한다.

 

  • 임의 분할 교차검증 (ShuffleSplit)

데이터를 임의로 분할하여 여러 번 학습과 검증을 수행한다. 데이터 분포가 일정하지 않을 때 유용하다.

 

 

교차 검증

 

  • 교차 검증의 필요성

👉🏻 테스트 횟수가 적은 경우 모델의 성능을 신뢰하기 어렵다. 테스트를 여러 번 거친 후, 평균치가 높아야 해당 모델의 성능을 신뢰할 수 있다. 그런데 만약 가지고 있는 데이터셋이 작다면, 같은 데이터셋 안에서 랜덤하게 여러 번 추출해서 결과를 확인하는 '교차 검증'을 할 수 있다.

 

👉🏻 과적합 (over fitting)

학습용 데이터에서는 성능이 높게 나왔으나 테스트 데이터에서는 성능이 낮게 나오는 경우로, 학습용 데이터에서만 최적화 되어 실전에 약한 상태이다.

 

👉🏻 인공지능 연구 윤리

학습시킨 모델의 성능 파악 시 테스트 데이터만 쓰는게 아니라 학습 데이터의 예측도 얼마나 맞았는지 확인해야 한다. 단, 데이터셋을 만들 때 테스트 데이터가 일부라도 학습용 데이터에 섞이면 안된다. 학습에 사용한 데이터는 테스트 시 당연히 정확도가 높게 나와, 모델의 성능을 높게 평가할 것이기 때문이다.

따라서 교차 검증 시에도 학습 데이터에 테스트 데이터가 섞이지 않도록 주의해야 한다.

 

  • KFold

작은 양의 데이터셋으로 효율적 학습을 위해 연습문제 갯수를 늘리는 것으로, K는 폴더 갯수를 의미한다. 학습용 데이터를 K개의 폴더로 나누고 하나의 폴더를 테스트셋, 나머지는 학습용으로 시작하여 갯수만큼 학습 시킨 후 평균을 구하는 것이다. 성능을 객관적으로 판단하기 위해 이런 방식을 사용한다. 많은 데이터를 가지고 성능을 판단할 수 있어 좋지만, 데이터셋이 커질수록 시간이 오래 걸린다는 단점이 있다.

 

  • StratifiedKFold

데이터셋에서 정답 데이터의 종류에 따라 균형을 맞춰 분배하기 위해 StratifiedKFold를 사용할 수 있다. (정답 데이터 종류의 균형이 맞아야 올바른 학습이 가능하다.)

데이터셋을 균형적으로 분리하여 총 여섯 개의 모델을 만들고 다섯 번의 검증을 거치게 된다. 이 때, 검증 횟수를 늘리고 싶다면 depth를 더 추가해 주면 된다.

 

  • corss_val_score

모델 성능을 확인하기 위해 교차 검증을 통한 결과만 확인하는 것으로, 학습시킨 모델을 반환하지는 않는다.

 

  • GridSearchCV

파라미터를 통해 성능이 잘 나오는 것이 무엇인지 판단하는 모델로, 직접 데이터셋을 나누는 것보다 GridSearchCV를 사용하는 것이 더 편리하다.

 

 

평가 매트릭스 해석 및 평가

 

  • 평가 매트릭스 (혼동 행렬)

혼동 행렬은 모델의 예측 결과를 실제 값과 비교하여 분류 성능을 평가하는 방법이다. 예측 클래스와 실제 클래스를 행렬로 표시하며, 각 요소는 모델이 얼마나 정확하게 예측했는지를 보여준다.

평가 매트릭스는 모델의 예측 결과를 요약한 표로, 정확도(Accuracy), 정밀도(Precision), 재현율(Recall), F1-score 등을 계산하는 데 사용된다.

 

  • 정확도 (Accuracy)

전체 데이터 중 모델이 맞춘 비율을 나타낸다.

 

  • 정밀도 (Precision)

긍정으로 예측한 것들 중 실제로 긍정인 비율을 나타낸다. (모델의 탐지 결과를 얼마나 믿을 수 있는가)

정밀도가 높아야 하는 경우 ex) 스팸 메일 필터링

 

  • 재현율 (Recall)

실제 긍정인 것들 중에서 모델이 긍정으로 예측한 비율을 나타낸다. (모델이 데이터 내 모든 '이상'을 탐지하였는가)

재현율이 높아야 하는 경우 ex) 암 여부 판별

 

  • F1-Score

정밀도와 재현율의 조화 평균을 나타낸다.

 

  • AUC와 ROC

ROC Curve(Receiver Operating Characteristic Curve)는 참 긍정 비율(True Positive Rate, TPR)과 거짓 긍정 비율(False Positive Rate, FPR)의 관계를 나타내는 곡선이다.

참 긍정 비율(TPR)은 재현율(Recall)과 같으며, 거짓 긍정 비율(FPR)은 모델이 잘못 양성으로 예측한 비율을 의미한다.

ROC Curve는 FPR이 증가할 때 TPR이 얼마나 증가하는지 보여준다. 이 곡선 아래 면적이 클수록, 즉 AUC 값이 클수록 모델의 성능이 좋다는 것을 의미한다.

AUC는 ROC 곡선 아래의 면적을 의미하며, 0과 1 사이의 값을 가진다.

 

 

회귀 모델 평가

 

  • 평균 절대 오차 (MAE, Mean Absolute Error)

MAE는 예측값과 실제값 사이의 절대적인 차이를 평균한 값이다.

값이 작을수록 예측 오차가 작다는 것을 의미한다.

 

  • 평균 제곱 오차 (MSE, Mean Squared Error)

MSE는 예측값과 실제값 사이의 제곱된 차이의 평균이다.

MAE는 큰 오차에 대한 패널티가 더 크기 때문에 이상치에 민감하다.

 

  • 평균 제곱근오차 (RMSE, Root Mean Squared Error)

RMSE는 MSE에 제곱근을 씌운 값으로, 단위를 원래 데이터의 단위와 동일하게 만든다.

MSE보다 해석이 쉽고, 이상치에 대한 민감도를 고려하면서도 직관적으로 사용할 수 있다.

 

  • 결정 계수 (R², Coefficient of Determination)

R²는 모델이 데이터를 얼마나 잘 설명하는지를 나타내는 지표이다.

값이 1에 가까울수록 모델이 데이터를 잘 설명함을 의미하며, 0에 가까울수록 설명력이 낮음을 의미한다.

 

 

모델 성능평가 지표 수립

 

모델 성능을 평가하기 위해 문제의 특성에 따라 적합한 지표를 선택해야 한다.

ex) 분류 문제에서는 F1 스코어, 회귀 문제에서는 평균 제곱 오차(MSE)가 자주 사용된다.

ex) 불균형 데이터셋에서는 정확도만으로는 성능을 제대로 평가할 수 없으므로, 정밀도, 재현율, F1-score 등의 지표를 함께 고려해야 한다.

 

 

모델 성능 고도화

 

  • 모델 성능 평가

학습된 모델이 새로운 데이터에서 얼마나 잘 작동하는지 확인하는 과정이다.

모델을 평가하는 방법 : Hold-Out Validation, Cross-Validation, Bootstrap Sampling

학습 곡선과 검증 곡선을 분석하여 과적합이나 과소적합 여부를 판단할 수 있다.

 

  • 모델 성능 향상

👉🏻 특징 공학(Feature Engineering)

더 나은 입력 데이터를 설계하여 모델의 예측력을 높인다.

 

1. 파생 변수(Feature Creation) 생성

- 여러 개의 기존 변수를 조합하여 새로운 변수를 만드는 기법

- ex) 날짜 → 연도, 월, 요일 / 시간 데이터 → 주간, 야간 구분

2. 다항 특성 생성 (Polynomial Features)

- 비선형 관계를 표현하기 위해 기존 변수를 조합하여 새로운 다항식 특성을 추가

3. 로그 변환 (Log Transformation)

- 데이터의 분포를 정규화하여 선형 모델에서 더 좋은 성능을 내도록 변환

4. 스케일링 (Scaling)

- 표준화(Standardization) : 평균을 0, 표준편차를 1로 맞춤

- 정규화(Normalization) : 최소값을 0, 최대값을 1로 맞춤

 

👉🏻 하이퍼파라미터 튜닝

하이퍼파라미터는 모델의 학습 과정에 영향을 미치는 설정 값이자 모델이 학습하는 과정에서 직접 조정되지 않는 매개변수로, 사용자가 직접 설정해야 하는 값이다. 적절한 하이퍼파라미터를 설정하면 과적합(Overfitting)과 과소적합(Underfitting)을 방지할 수 있고, 일반화 성능을 향상시킬 수 있다.

- 학습률, 배치 크기, 네트워크 구조 등을 최적화한다.

- 그리드 서치(Grid Search)와 랜덤 서치(Random Search)는 하이퍼파라미터 튜닝에 사용되는 대표적인 기법이다.

- GridSearchCV를 사용해 SVM의 하이퍼파라미터를 최적화한다. 여기서 C 값커널 종류를 탐색하여 최적의 조합을 찾는다.

 

👉🏻 앙상블 학습

여러 개의 모델을 결합하여 성능을 높이는 방법이다.

여러 모델의 예측을 결합하여 더 나은 결과를 얻는다.

대표적인 앙상블 방법으로는 랜덤 포레스트, 배깅(Bagging), 부스팅(Boosting) 등이 있다.

 

👉🏻 데이터 증강

1. 이미지 데이터 증강

- 이미지 데이터를 회전, 크롭, 밝기 조절 등으로 변형하여 데이터 수를 증가

2. 텍스트 데이터 증강

- WordNet을 이용한 단어 교체, Synonym Replacement 등의 기법 활용

3. 데이터 오버샘플링 (Over-sampling)

- 불균형 데이터 문제를 해결하기 위해 소수 클래스 데이터를 인위적으로 증가시키는 방법

- 대표적인 기법 : SMOTE(Synthetic Minority Over-sampling Technique)

 

 

실습 (평가)

 

# 전처리 함수 정의

from sklearn.preprocessing import LabelEncoder
from sklearn.preprocessing import StandardScaler

def fillna(df):
    """
    결측치 처리 함수
    - Age : 평균치로 대체
    - Cabin : 'N' 기본값으로 대체
    - Embarked : 'N' 기본값으로 대체
    """
    df['Age'] = df['Age'].fillna(df['Age'].mean())
    df['Cabin'] = df['Cabin'].fillna('N')
    df['Embarked'] = df['Embarked'].fillna('N')

    return df


def drop_feature(df):
    """
    모델 훈련과 관련 없는 속성 제거
    - PassengerId, Name, Ticket
    """
    return df.drop(['PassengerId', 'Name', 'Ticket'], axis=1)


def encode_feature(df):
    """
    범주형 데이터를 숫자로 인코딩
    - Sex, Cabin, Embarked
    """
    df['Cabin'] = df['Cabin'].str[:1]    # Cabin 데이터의 앞 글자만 가져옴

    categories = ['Sex', 'Cabin', 'Embarked']
    for cate_item in categories:
        label_encoder = LabelEncoder()
        df[cate_item] = label_encoder.fit_transform(df[cate_item])
    
    return df


def preprocess_data(df):
    """
    전처리 함수 호출
    """
    df = drop_feature(df)
    df = fillna(df)
    df = encode_feature(df)

    return df


def scailing_feature(train_data, test_data):
    """
    특성 스케일링 (정규화)
    """
    scaler = StandardScaler()
    train_scaled = scaler.fit_transform(train_data)
    test_scaled = scaler.transform(test_data)
    
    return train_scaled, test_scaled

 

  • 분류모델 평가

👉🏻 정확도 (Accuracy score)

- 전체 샘플 중에서 올바르게 예측한 샘플의 비율

- 데이터가 불균형한 경우 정확도는 비현실적인 성능을 낼 수 있음

# 잘못 학습된 모델 만들어보기
from sklearn.base import BaseEstimator
import numpy as np
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score

# 성별로만 판별하는 모델 작성
class MyTitanicClassifier(BaseEstimator):
    # 훈련 메서드
    def fit(self, X, y):
        pass

    # 결과 예측 메서드
    def predict(self, X):
        pred = np.zeros((X.shape[0], 1))
        for i in range(X.shape[0]):
            sex = X['Sex'].iloc[i]
            if sex == 0:		# 여성이면
                pred[i] = 1		# 생존이라고 예측
        return pred
       
# 데이터 로드
df = pd.read_csv('./data/titanic.csv')

# 입력-라벨 데이터 분리
X = df.drop('Survived', axis=1)
y  = df['Survived']

# 데이터 전처리
X = preprocess_data(X)

# 학습-테스트 데이터 분리
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0)

# 모델 훈련
my_classifier = MyTitanicClassifier()
my_classifier.fit(X_train, y_train)

# 예측
pred_train = my_classifier.predict(X_train)
pred_test = my_classifier.predict(X_test)

# 정확도로 평가 (accuracy_score)
print(f"훈련 데이터 정확도: {accuracy_score(y_train, pred_train)}")
print(f"평가 데이터 정확도: {accuracy_score(y_test, pred_test)}")

 

혼동 행렬 (Confusion Matrix)

from sklearn.metrics import confusion_matrix, precision_score, recall_score

 

👉🏻 정밀도 (Precision)

- 양성이라고 예측한 것(TP + FP) 중에 실제 양성(TP)일 확률

- 정밀도가 중요한 지표인 경우: 음성인 데이터를 양성으로 예측하면 안되는 경우 (스팸메일 분류 등)

matrix = confusion_matrix(y_test, pred_test)
matrix  # array([[115, 24],
#                [25, 59]])

p_score = 59 / (59 + 24)
p_score, precision_score(y_test, pred_test)  # (0.7108433734939759,0.7108433734939759)  /같은 결과

 

👉🏻 재현율 (Recall)

- 실제 양성(TP + FN) 중에 양성으로 예측(TP)한 확률

- 재현율이 중요한 지표인 경우 : 양성인 데이터를 음성으로 예측하면 안되는 경우 (암 진단, 보험/금융 사기 탐지 등)

recall_score(y_test, pred_test)  # 0.7023809523809523
# 평가 지표 출력 함수
def evaluate_binary_classification(y_true, y_pred):
    print('혼동행렬:')
    print(confusion_matrix(y_true, y_pred))
    print(f'정확도: {accuracy_score(y_true, y_pred)}, 정밀도: {precision_score(y_true, y_pred)}, 재현율: {recall_score(y_true, y_pred)}')
    
# 잘못 학습된 모델 만들어보기 2
class MyDeathClassifier(BaseEstimator):
    def fit(self, X, y):
        pass

    def predict(self, X):
        return np.zeros((X.shape[0], 1))  # 전부 사망
    
# 모델 학습
my_classifier = MyDeathClassifier()
my_classifier.fit(X_train, y_train)

# 예측
pred_train = my_classifier.predict(X_train)
pred_test = my_classifier.predict(X_test)

# 평가
evaluate_binary_classification(y_train, pred_train)
evaluate_binary_classification(y_test, pred_test)  # 2개 모두 정확도는 60점을 넘지만, 정밀도와 재현율은 0


# 제대로 학습
from sklearn.linear_model import LogisticRegression

# 모델 학습
lr_clf = LogisticRegression()
lr_clf.fit(X_train, y_train)

# 예측
pred_train = lr_clf.predict(X_train)
pred_test = lr_clf.predict(X_test)

# 평가
evaluate_binary_classification(y_train, pred_train)
evaluate_binary_classification(y_test, pred_test)  # 정확도, 정밀도, 재현율 모두 비슷하지만, 정밀도와 재현율의 상충관계

 

👉🏻 정밀도와 재현율의 trade-off

- 분류 결정 임계치(threshold)를 낮추면? Positive로 예측할 확률이 높아진다! ▶️ 정밀도는 낮아지고, 재현율은 높아진다.

- 분류 결정 임계치(threshold)를 높이면? Positive로 예측할 확률이 낮아진다! ▶️ 정밀도는 높아지고, 재현율은 낮아진다.

 

👉🏻 ROC Curve - AUC

ROC Curve (Receiver Operation Characteristic Curve, 수신자 조작 특성 커브)

- x축: FPR(False Postive Rate), 실제 음성을 잘못 예측한 비율

  - FPR = FP / (FP + TN)

- y축: TPR(True Positive Rate), recall score

  - TPR = TP / (FN + TP)

 

AUC (Area Under Curve): ROC 곡선 아래 면적 (1에 가까울수록 성능 좋음)

from sklearn.metrics import roc_curve
from sklearn.metrics import auc

y_true_sample = np.array([0, 0, 1, 1])
y_pred_sample = np.array([0.1, 0.4, 0.35, 0.8])

fpr, tpr, thresholds = roc_curve(y_true_sample, y_pred_sample)
fpr, tpr, thresholds


model = LogisticRegression()
model.fit(X_train, y_train)

y_pred_proba = model.predict_proba(X_test)[:, 1]

fpr, tpr, thresholds = roc_curve(y_test, y_pred_proba)
roc_auc = auc(fpr, tpr)

plt.plot(fpr, tpr, color='blue', label=f'ROC Curve (AUC={roc_auc:.2f})')
plt.plot([0, 1], [0, 1], color='gray', linestyle='--')
plt.xlabel('FPR (False Postive Rate)')
plt.ylabel('TPR (True Positive Rate)')
plt.legend()
plt.show()

 

👉🏻 회귀모델 평가

# 샘플 데이터
y_true = [3, 0.5, 2, 7]
y_pred = [2.5, 0, 2, 9]

from sklearn.metrics import mean_squared_error          # MSE (평균 제곱 오차)
from sklearn.metrics import root_mean_squared_error     # RMSE (제곱 평균 제곱근 오차)
from sklearn.metrics import mean_absolute_error         # MAE (평균 절대 오차)
from sklearn.metrics import mean_squared_log_error      # MSLE (평균 제곱 로그 오차)
from sklearn.metrics import root_mean_squared_log_error # RMSLE (제곱 평균 제곱근 로그 오차)
from sklearn.metrics import r2_score                    # R^2 결정계수

print(mean_squared_error(y_true, y_pred))
print(root_mean_squared_error(y_true, y_pred))
print(mean_absolute_error(y_true, y_pred))
print(mean_squared_log_error(y_true, y_pred))
print(root_mean_squared_log_error(y_true, y_pred))
print(r2_score(y_true, y_pred))

 

  • 교차검증 (Cross Validation)

모델을 더욱 신뢰성 있게 평가하는 방법

데이터셋을 여러 개로 나누고, 각 부분이 한번씩 검증 데이터로 사용되도록 하는 방법

훈련-검증을 반복하며 학습 진행

과대적합 방지 효과

 

👉🏻 K-fold

from sklearn.datasets import load_iris
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import KFold
from sklearn.metrics import accuracy_score
import numpy as np

# 데이터 로드
iris_input, iris_target = load_iris(return_X_y=True)  # 특성 X와 타깃 y로 반환
np.unique(iris_target, return_counts=True)

# 모델 생성
lr_clf = LogisticRegression()

# 교차검증 KFold 객체 생성
# - n_splits: 폴드 개수
# - shuffle: 폴드로 나누기 전에 섞을지 여부
kfold = KFold(n_splits=5, shuffle=True, random_state=42)

# k번 반복하면서 평가한 정확도를 저장할 배열
cv_accuracy = []

# kfold 객체를 활용해 데이터 인덱스를 폴드로 구분해 나눔
for train_index, val_index in kfold.split(iris_input):
    X_train, y_train = iris_input[train_index], iris_target[train_index]
    X_val, y_val = iris_input[val_index], iris_target[val_index]
    
    print(np.unique(y_train, return_counts=True))
    print(np.unique(y_val, return_counts=True))
    print("=======================================")

    lr_clf.fit(X_train, y_train)				# 모델 학습
    y_pred = lr_clf.predict(X_val)				# 예측
    acc_score = accuracy_score(y_val, y_pred)	# 정확도 계산
    cv_accuracy.append(acc_score)				# 결과 배열에 정확도 추가

print("훈련별 정확도:", cv_accuracy)
print("분류모델 정확도:", np.mean(cv_accuracy))

 

👉🏻 Stratified-K-Fold

from sklearn.model_selection import StratifiedKFold

# 모델 생성
lr_clf = LogisticRegression()

# 교차검증 StratifiedKFold 객체 생성
stratified_kfold = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)

cv_accuracy = []

# kfold 객체를 활용해 데이터 라벨의 비율에 맞게 인덱스를 폴드로 구분해 나눔
for train_index, val_index in stratified_kfold.split(iris_input, iris_target):
    X_train, y_train = iris_input[train_index], iris_target[train_index]
    X_val, y_val = iris_input[val_index], iris_target[val_index]

    print(np.unique(y_train, return_counts=True))
    print(np.unique(y_val, return_counts=True))
    print("=======================================")

    lr_clf.fit(X_train, y_train)				# 모델 학습
    y_pred = lr_clf.predict(X_val)				# 예측
    acc_score = accuracy_score(y_val, y_pred)	# 정확도 계산
    cv_accuracy.append(acc_score)				# 결과 배열에 정확도 추가

print("훈련별 정확도:", cv_accuracy)
print("분류모델 정확도:", np.mean(cv_accuracy))

 

👉🏻 cross_val_score

교차 검증을 통해 모델 성능을 평가하는 점수

내부적으로 지정한 횟수만큼 학습/검증을 나누어 반복 처리

from sklearn.model_selection import cross_val_score

lr_clf = LogisticRegression(max_iter=1000)

# 첫 번쨰 인자: 모델
# 두 번째 인자: 입력 데이터 (X 데이터)
# 세 번째 인자: 라벨 데이터 (y 데이터)
# cv: 반복 횟수(KFold 사용, StratifiedKFold 객체 전달 가능)
# scoring: 평가 지표(accuracy, precision, recall, f1)
# 반환값 = 반복한 훈련별 검증 점수 '배열'

scores = cross_val_score(lr_clf, iris_input, iris_target, cv=5, scoring='accuracy')

print("훈련별 정확도:", scores)
print("분류모델 정확도:", np.mean(scores))
from sklearn.model_selection import cross_validate

lr_clf = LogisticRegression(max_iter=1000)

scores = cross_validate(
                        lr_clf,                             # 모델
                        iris_input,                         # 입력 데이터
                        iris_target,                        # 라벨 데이터
                        cv=5,                               # 반복 횟수 (KFold 사용, StratifiedKFold 객체 전달 가능)
                        scoring=['accuracy', 'f1_macro'],   # 다중 평가 지표 사용 가능 (accuracy, precision, recall, f1)
                        return_train_score=True             # 학습 점수도 함께 반환할지 여부
                        )

scores

 

  • Hyper Parameter Tuning

hyper parameter: 모델 선정과 관련해 직접 지정할 수 있는 매개변수

model parameter: 회귀계수(가중치), 절편 등 모델의 학습 대상이 되는 변수

 

👉🏻 GridSearchCV

from sklearn.datasets import load_iris
from sklearn.neighbors import KNeighborsClassifier
from sklearn.model_selection import GridSearchCV

# 데이터 로드
iris_input, iris_target = load_iris(return_X_y=True)

# 모델 생성
knn = KNeighborsClassifier()

# 테스트할 파라미터 값
params = {
    'n_neighbors': range(1, 13, 2) 
}

# 첫 번째 인자: 모델
# 두 번째 인자: 테스트 할 파라미터 (딕셔너리)
# scoring: 평가 지표 (accuracy, precision, recall, f1)
# cv: 반복 횟수
grid = GridSearchCV(knn, params, scoring='accuracy', cv=5)
grid.fit(iris_input, iris_target)

print("최적의 파라미터:", grid.best_params_)
print("최적화된 모델 객체:", grid.best_estimator_)
print("최적화된 점수:", grid.best_score_)

best_knn = grid.best_estimator_
# best_knn.predict(iris_input)
best_knn.score(iris_input, iris_target)

 

👉🏻 RandomSearchCV

하이퍼 파라미터의 값 목록이나 값의 범위를 제공하는데, 이 범위 중에 랜덤하게 값을 뽑아내 최적의 하이퍼 파라미터 조합을 찾는다.

- 탐색범위가 넓을 때 짧은 시간 내에 좋은 결과를 얻을 수 있다.

- 랜덤하게 값을 추출해 계산하므로, 전역 최적값을 놓칠 수 있다.

from sklearn.model_selection import RandomizedSearchCV

# 모델 생성
knn = KNeighborsClassifier()

# 테스트할 파라미터 값
params = {
    'n_neighbors': range(1, 1000, 2)
}

# n_iter: 탐색할 최적의 하이퍼 파라미터 조합 수 (기본값: 10)
#         값이 크면 시간이 오래 걸림 / 값이 작으면 좋은 조합을 찾을 가능성 저하
rd_search = RandomizedSearchCV(knn, params, cv=5, n_iter=10, random_state=0)
rd_search.fit(iris_input, iris_target)

print("최적의 파라미터:", rd_search.best_params_)
print("최적화된 모델 객체:", rd_search.best_estimator_)
print("최적화된 점수:", rd_search.best_score_)

 

  • Hyper Parameter 최적화 라이브러리

👉🏻 HyperOpt

pip install hyperopt
from hyperopt import hp
import hyperopt
from hyperopt import fmin, tpe, Trials

# 검색 공간
search_space = {
    'x': hp.quniform('x', -10, 10, 1),
    'y': hp.quniform('y', -15, 15, 1)
}

# 목적 함수
def objective(search_space):
    x = search_space['x']
    y = search_space['y']
    return {
        'loss': x ** 2 + 20 * y,
        'status': hyperopt.STATUS_OK
    }
    
trials = Trials()

best_val = fmin(
    fn = objective,
    space = search_space,
    algo = tpe.suggest,
    max_evals = 500,
    trials = trials
)
best_val

trials.results
trials.vals
# hyperopt를 활용한 XGBoost 하이퍼 파라미터 튜닝
from xgboost import XGBClassifier
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split, cross_val_score
from hyperopt import fmin, tpe, Trials, hp
import hyperopt

# 데이터 로드 및 분리
data = load_breast_cancer()
X_train, X_test, y_train, y_test = train_test_split(data.data, data.target, random_state=42)

# 1. 검색 공간
search_space = {
    'n_estimators': hp.quniform('n_estimators', 100, 500, 100),
    'max_depth': hp.quniform('max_depth', 3, 10, 1),
    'learning_rate': hp.uniform('learning_rate', 0.01, 0.2),
    'colsample_bytree': hp.uniform('colsample_bytree', 0.5, 1)
}

# 2. 목적 함수
# cross_val_score - accuracy -> (-)
def xgb_objective(ss):
    xgb_clf = XGBClassifier(
        n_estimators = int(ss['n_estimators']),
        max_depth = int(ss['max_depth']),
        learning_rate = ss['learning_rate'],
        colsample_bytree = ss['colsample_bytree']
    )
    mean_acc = cross_val_score(xgb_clf, X_train, y_train, scoring='accuracy', cv=3).mean()
    return {
        'loss': -1 * mean_acc,
        'status': hyperopt.STATUS_OK
    }

# 3. Trials() + fmin()
trials = Trials()
best = fmin(
    fn = xgb_objective,
    space = search_space,
    algo = tpe.suggest,
    max_evals = 50,
    trials = trials
)
best

 

👉🏻 Optuna

pip install optuna
import optuna
import optuna.visualization as vis

# 목적 함수
def objective(trial):
    x = trial.suggest_uniform('x', -10, 10)
    y = trial.suggest_uniform('y', -15, 15)
    return (x - 3) ** 2 + (y + 5) ** 2

# 스터디 생성
study = optuna.create_study(direction='minimize')

# 최적화 실행
study.optimize(objective, n_trials=500)

study.best_value
study.best_params

vis.plot_param_importances(study).show()
vis.plot_optimization_history(study).show()
# optuna를 활용한 XGBoost 하이퍼 파라미터 튜닝
# 1. 목적 함수
def xgb_optuna_objective(trial):
    params = {
        'n_estimators': trial.suggest_int('n_estimators', 100, 500, 100),
        'max_depth': trial.suggest_int('max_depth', 3, 10),
        'learning_rate': trial.suggest_float('learning_rate', 0.01, 0.2),
        'colsample_bytree': trial.suggest_float('colsample_bytree', 0.5, 1.0)
    }
    xgb_clf = XGBClassifier(**params)
    return cross_val_score(xgb_clf, X_train, y_train, scoring='accuracy', cv=3).mean()

# 2. study 객체 생성 -> 최적화
study = optuna.create_study(direction='maximize')
study.optimize(xgb_optuna_objective, n_trials=50)

# 3. 결과 출력
print(study.best_params)
print(study.best_value)
# HyperOpt vs Optuna
from sklearn.metrics import classification_report

xgb_hopt = XGBClassifier(
    n_estimators = 500,
    max_depth = 6,
    learning_rate = 0.17,
    colsample_bytree = 0.5
)

xgb_optuna = XGBClassifier(
    n_estimators = 500,
    max_depth = 7,
    learning_rate = 0.14,
    colsample_bytree = 0.53
)

xgb_hopt.fit(X_train, y_train)
xgb_optuna.fit(X_train, y_train)

hopt_pred = xgb_hopt.predict(X_test)
optuna_pred = xgb_optuna.predict(X_test)

print('HyperOpt 최적 파라미터 적용')
print(classification_report(y_test, hopt_pred))
print('Optuna 최적 파라미터 적용')
print(classification_report(y_test, optuna_pred))