본문 바로가기
머신러닝(Machine Learning)

머신러닝 실습: 분류분석 연습2

by Blaze_블즈 2024. 3. 3.

안녕하세요

블레이즈 테크노트의 

블레이즈입니다. 

 

 

 

이전 포스팅에서는 분류분석에 대한 데이터 전처리 작업을 살펴보았습니다. 

2024.03.02 - [머신러닝(Machine Learning)] - 머신러닝 실습: 분류분석 연습1

 

 

오늘은 그 데이터를 활용해 본격적으로 모델 학습과 평가 과정을 진행해보겠습니다.

 

분류 모델에 대한 이론적 배경이 필요하신 분들은 이전 이론 포스팅을 참고해주세요.

로지스틱 회귀, 선형 판별 분석, K-최근접 이웃 분석 등 다양한 분류 모델에 대해 자세히 다루었습니다.

 

2024.03.01 - [분류 전체보기] - 머신러닝 이론 분류모델: 로지스틱 회귀, 선형 판별 분석, k-최근접 이웃 분석

 

 

이번 포스팅에서는 본격적으로 모델 학습과 평가를 진행해보도록 하겠습니다.

 

 

첫 번째 모델은 로지스틱 회귀모델입니다. 

로지스틱 회귀는 분류를 위한 대표적인 선형 모델로, 특히 이진 분류 문제에 자주 사용됩니다.

 

from sklearn.linear_model import LogisticRegression

start = time()
lr_clf = LogisticRegression(random_state = 0)
lr_clf.fit(scaled_tr_x, under_tr_y)
end = time()
print('학습 소요시간: {:2d}m {:.3f}s'.format(int((end-start)/60),(end-start)%60))

 

 

start = time()
lr_pred = lr_clf.predict(scaled_te_x)
# logistic regression의 label별 예측 확률 할당/ ROC curve 시각화 및 AUC 계산에 사용
#로지스틱 회귀 예측 확률
lr_prob = lr_clf.predict_proba(scaled_te_x) 
end = time()
print('예측 소요시간: {:2d}m {:.3f}s'.format(int((end-start)/60),(end-start)%60))

 

 

lr_pred

 

 

lr_prob

 

 

로지스틱 회귀 모델의 예측 결과는 lr_pred에, 각 클래스에 속할 확률은 lr_prob에 저장됩니다.

 

첫 세 개의 예측 결과가 1, 0, 1이라는 것은

첫 번째와 세 번째는 양성 클래스(1)로, 두 번째는 음성 클래스(0)로 분류된 것입니다.

 

이때 예측 결과는 threshold 0.5를 기준으로 판단을 내린 것입니다. 

 

확률을 보면,

첫 번째 데이터는 [0.16152097,0.83847903]로 나타나

양성 클래스로 분류되었음을 알 수 있습니다.

 

 

두 번째 데이터는 [0.97124527,0.02875473]로 음성 클래스로,

 

세 번째 데이터는 [0.46844645,0.53155355]로 다시 양성 클래스로 분류되었습니다.

 

이 확률들은 각각의 예측이 얼마나 확신을 가지고 이루어졌는지를 나타냅니다.

 

 

get_clf_eval(te_y, lr_pred, lr_prob)

 

 

 

 

오차행렬(Confusion Matrix)은 분류 문제에서 모델의 성능을 평가하기 위해 사용되는 표입니다.

 

이 행렬은 실제 값과 모델이 예측한 값을 기반으로, 예측이 얼마나 정확했는지를 보여줍니다.

오차행렬은 주로 이진 분류 문제에서 사용되지만, 다중 클래스 분류 문제에도 적용할 수 있습니다.

 

오차행렬은 다음과 같은 네 가지 기본 요소로 구성됩니다:

  • True Positive (TP): 양성으로 예측했고, 실제로도 양성인 경우.
  • True Negative (TN): 음성으로 예측했고, 실제로도 음성인 경우.
  • False Positive (FP): 양성으로 예측했지만, 실제로는 음성인 경우. (1종 오류 또는 거짓 양성)
  • False Negative (FN): 음성으로 예측했지만, 실제로는 양성인 경우. (2종 오류 또는 거짓 음성)

이러한 요소를 통해 오차행렬은 다음과 같이 표현됩니다:

실제 / 예측양성 예측 (Positive Predicted)음성 예측 (Negative Predicted)

실제 양성 (Positive Actual) True Positive (TP) False Negative (FN)
실제 음성 (Negative Actual) False Positive (FP) True Negative (TN)

 

오차행렬을 사용해 정확도, 정밀도, F1점수를 계산합니다.

 

 

ROC curve는 다음과 같습니다. 

 

AUC는 curve 아래 면적을 나타낸 것으로 1에 가까울수록 높은 성능을 나타냅니다. 

 

 

 

다음으로 선형 판별 분석 모델 LDA 입니다. 

LDA는 클래스 간 분리를 최대화하는 방식으로 작동하는 모델입니다.

 

from sklearn.discriminant_analysis import LinearDiscriminantAnalysis

start = time()
lda_clf = LinearDiscriminantAnalysis() # LinearDiscriminantAnalysis (LDA) 생성
lda_clf.fit(scaled_tr_x,under_tr_y) # LDA classifier 학습
end = time()
print('학습 소요시간: {:2d}m {:.3f}s'.format(int((end-start)/60),(end-start)%60))

start = time()
lda_pred = lda_clf.predict(scaled_te_x) # LDA classifier 예측
lda_prob = lda_clf.predict_proba(scaled_te_x) # LDA classifier label 별 예측확률 할당
end = time()
print('예측 소요시간: {:2d}m {:.3f}s'.format(int((end-start)/60),(end-start)%60))

 

get_clf_eval(te_y, lda_pred, lda_prob)

get_clf_roc_curve(te_y, lda_prob)

 

 

성능을 보면

선형 판별 분석 모델은 로지스틱 회귀모델과 유사한 성능을 나타내었습니다. 

 

 

다음으로 k-최근접 이웃 분석 모델입니다. 

KNN은 데이터 포인트들의 근접성을 기반으로 분류를 수행하는 모델입니다.

 

from sklearn.neighbors import KNeighborsClassifier

start = time()
knn_clf = KNeighborsClassifier(n_neighbors=3) # k가 3인 KNeighborsClassifier 생성
knn_clf.fit(scaled_tr_x,under_tr_y) # KNeighborsClassifier 학습
end = time()
print('학습 소요시간: {:2d}m {:.3f}s'.format(int((end-start)/60),(end-start)%60))

start = time()
knn_pred = knn_clf.predict(scaled_te_x) # kNN label 예측
knn_prob = knn_clf.predict_proba(scaled_te_x) # kNN label label별 예측 확률 구하기
end = time()
print('예측 소요시간: {:2d}m {:.3f}s'.format(int((end-start)/60),(end-start)%60))

 

get_clf_eval(te_y, knn_pred, knn_prob) # kNN 평가 지표 출력

get_clf_roc_curve(te_y, knn_prob) # kNN ROC curve 시각화

 

 

 

knn 분석의 AUC는 0.6725로 이전 분석법보다 약간 낮았습니다. 

 

 

하이퍼파라미터 최적화를 진행했습니다. 

 

from sklearn.preprocessing import Binarizer

 

 

Binarizer는 사이킷런(sklearn) 라이브러리에서 제공하는 전처리(preprocessing) 도구 중 하나로,

연속적인 수치 데이터를 기준점(threshold)을 기반으로 이진 값(0 또는 1)으로 변환하는 기능을 수행합니다.

 

특히 분류 문제에서 threshold 기준으로 데이터를 두 그룹으로 나누어야 할 때 유용하게 사용됩니다.

 

 

저는 로지스틱 회귀 모델을 통해 생성된 예측 확률을 그대로 사용하되,

Binarizer를 통해 다양한 임계값(threshold)을 적용하여 예측 레이블을 다시 할당했습니다.

 

 임계값에 따라 같은 확률 값이라도 다른 예측 결과(양성 또는 음성)를 낼 수 있습니다.

 

기본적으로 로지스틱 회귀 모델은 확률이 0.5 이상이면 양성 클래스(1)로, 그렇지 않으면 음성 클래스(0)로 분류합니다.

하지만 이 기본 임계값이 최적이 아닐 수 있기 때문에 다양한 임계값을 테스트해봤습니다.

threshold_list = [0.3,0.35, 0.4,0.45, 0.5,0.55, 0.6,0.65, 0.7, 0.8] # threshold 리스트 생성
result_df = pd.DataFrame(index=threshold_list, columns=['정확도','정밀도','재현율','F1'])

clf = LogisticRegression(random_state = 0)
clf.fit(scaled_tr_x, under_tr_y)
pred = clf.predict(scaled_tr_x)
prob = clf.predict_proba(scaled_tr_x) # logistic regression

for thr in threshold_list: ##
    binarizer = Binarizer(threshold=thr)
    pred_new = binarizer.fit_transform(prob[:,1].reshape(-1,1)) # threshold를 변경하면서 예측 label을 다시 할당 ex) thr=0.3 y >= 0.3 -> 1 y < 0.3 -> 0 (일반적으로는 thr=0.5)
    result_df.loc[thr,'정확도'] = accuracy_score(under_tr_y, pred_new).round(3)
    result_df.loc[thr,'정밀도'] = precision_score(under_tr_y, pred_new).round(3)
    result_df.loc[thr,'재현율'] = recall_score(under_tr_y, pred_new).round(3)
    result_df.loc[thr,'F1'] = f1_score(under_tr_y, pred_new).round(3)

result_df

 

 

아래와 같이 threshold를 0.55와 0.45로 변경해서 모델 학습을 진행해봤습니다. 

 

clf = LogisticRegression(random_state = 0)
clf.fit(scaled_tr_x, under_tr_y)
te_prob = clf.predict_proba(scaled_te_x)
binarizer = Binarizer(threshold=0.55)
te_pred_new = binarizer.fit_transform(te_prob[:,1].reshape(-1,1))
get_clf_eval(te_y,te_pred_new,te_prob)

 

clf = LogisticRegression(random_state = 0)
clf.fit(scaled_tr_x, under_tr_y)
te_prob = clf.predict_proba(scaled_te_x)
binarizer = Binarizer(threshold=0.45)
te_pred_new = binarizer.fit_transform(te_prob[:,1].reshape(-1,1))
get_clf_eval(te_y,te_pred_new,te_prob)

 

 

감사합니다. 

블레이즈 테크 노트.