카테고리 없음

ML-분류 (Classification)

co-yong 2025. 7. 25. 22:02

6. ML - 분류 (Classification)


  • 범주형 종속 변수를 예측하기 위해 사용하며, 다중 클래스 분류라고도 부른다. (클래스 개수가 2라면 이진 분류라고도 함)
  • 레이블이 있는 학습 데이터를 통해 학습 후, 평가 데이터나 새로운 데이터에 대해 예측을 수행
  • 분류 모델로는 Logistic Regression, KNN, Decision Tree, SVM 등이 있다.

평가 지표

기본적으로는 정확도 (Accuracy)가 사용되며 이는 학습/평가 데이터로 성능 평가하기 좋지만, 데이터가 불균형한 경우 설명력 불충분함.

  • 혼동 행렬 (Confusion Matrix) : 예측과 실제 값을 비교하여 TP/FP/TN/FN을 시각적으로 표현

https://spotintelligence.com/2024/09/06/confusion-matrix-a-beginners-guide-how-to-tutorial-in-python/

지표명 수식 설명
정확도 (Accuracy) Accuracy = (TP + TN) / (TP + FP + FN + TN) 전체 샘플 중 맞춘 비율
정밀도 (Precision) Precision = TP / (TP + FP) Positive로 예측한 것 중 실제 Positive 비율, 내가 True라고 한것중에 얼마나 덜 틀리는가
재현율 (Recall, Sensitivity, TPR) Recall = TP / (TP + FN) 실제 Positive 중 맞춘 비율, 얼마나 틀렸는지는 신경쓰지 않음.
특이도 (Specificity, TNR) Specificity = TN / (TN + FP) 실제 Negative 중 맞춘 비율, 얼마나 맞췄는지는 신경쓰지 않음.
F1 Score F1 = 2 * (Precision * Recall) / (Precision + Recall) 정밀도와 재현율의 조화 평균
  • ROC 곡선 및 AUC : cut-off (확률 몇까지 Y로 판정할지 그 기준점) 값이 변함에 따라 Y/N의 비율이 얼마나 되는지를 시각적으로 보여줌.
    • X 축에 False Negative, Y 축에 True Negative를 통해 그림. (곡선이 왼쪽 상단에 가까울수록 좋은 성능)
    • AUC (ROC 곡선 아래의 면적) 이 1에 가까울 수록 완벽한 분류기를 의미. 0.5값이라면 무작위 분류와 동일.

주요 모델

로지스틱 회귀

wx+b 직선으로 반으로 나누었을 때 class 구별이 잘 되는 case에서 사용 ! (분류의 경계가 선형)

  • 선형 회귀처럼 feature와 weight의 선형결합을 활용 -> 로지스틱 함수에 적용하여 결과를 0~1 확률로 변환 (분류기법임을 인지하자)
  • x (입력) -> wx + b -> logistic 함수에 대입 -> 출력
  • 선형 회귀 기반이므로 쉽고 구현이 간단하고, 빠르다. 선형 경계를 사용하기에, 비선형 데이터에는 적합하지 않다. (주로 이진분류에서 사용)

*logistic 함수(시그모이드 함수)의 결과값은 실제로는 확률값이 아니지만, w와 b에 의해 그 값이 조정됨으로 인해 확률처럼 해석을 할 수 있는 값으로 간주된다.

train_test_split(stratify=y_multi)
# 기존 shuffle로 인해 type이나 개수가 일부 쏠릴 수 있음 -> 분류 시 층화추출 적용

y_proba = model.predict_proba(X_test) # 클래스 예측 전 확률값 까지만 출력

# 예측 결과 출력
print("예측 결과:", y_pred[:10])
print("예측 확률값 ", y_proba[:10]) # 이차원배열중 행별로 arg_max시 값이 큰 녀석의 인덱스가 나옴 -> y_pred와 동일
print("예측 확률값에서 추출 ", y_proba[:10].argmax(axis=1))

# 결정 함수 (모델에서의 원시적인 예측 함수)
pred_dec = model.decision_function(X_test)
print(pred_dec) # 출력되는 값은 wx+b 값을 의미함. 모든 클래스에 대해 존재하지는 않고, 중간에 계산 로직이 더 있는 경우에 대해 존재.

img.png

decision function (예측 수행) -> logistic (Sigmoid) (값을 0~1로 바꾸고) -> porba (이차원 행렬) -> argmax (A/B로 바꾸고) -> pred 결과

  • 선형회귀 기반이기에 coef_ (가중치)와 intercept_ (절편) 도 출력해볼 수 있다.

Decision Tree

데이터를 여러 개의 규칙 기반의 분기로 나누어 결과를 예측.

주요 분할 기준

  • 지니 지수 (Gini index) : 불순도를 측정
    • 임의로 두 개를 추출했을 때 서로 다른 클래스가 뽑힐 확률
    • 지니 지수가 낮은 클래스가 최선의 분할 기준으로 취급
  • 정보 이득 (Information Gain) : 엔트로피 감소를 측정
    • 불확실성이나 예측의 혼란도를 의미. 분할 전후의 엔트로피 차이 비교를 통해 불확실성이 가장 적은, 즉 정보 이득이 가장 큰 분할을 선택한다.

threshold (=최초의 Root Node 값) 설정 : unique한 값 추출후 그 값들의 중앙값으로 설정

*장점

  • 의사결정 과정을 쉽게 이해하고 시각화 할 수 있다.
  • feature scailing 같은 전처리기 없이도 동작 가능
  • 비선형 데이터와의 관계도 학습 가능

*단점

  • 매우 복잡한 트리를 형성할 수 있으며, 과적합 될 위험이 높다. (depth 高)
  • 데이터에 작은 변화가 생겨도 트리 구조가 크게 변할 수 있어 불안정성이 높다.

KNN (K-Nearest Neighbors)

비모수적 분류 및 회귀 알고리즘. 새로운 데이터 포인트의 클래스 또는 값을 예측하기 위해 가장 가까운 K개의 포인트를 사용한다.
그렇다면 최근접한 이웃을 찾을 때, 무엇을 기준으로 할것인가?

  1. 유클리드 거리 (p=2)
  2. 맨해튼 거리 (p=1)
  3. 민코프스키 거리 (p=무한대)
  • 분류 뿐만 아니라 회귀에서도 사용가능. 분류 : 이웃 다수결로 클래스 결정 / 회귀 : 이웃의 평균값을 사용하여 예측
  • lazy learning 기법 중 하나임.
  • 거리 관련 기법은 scale에 영향을 받는다면 l2, 이상치에 영향을 받는다면 l1을 선택하자. feature scaling 권장.
  • 수치형 데이터 뿐만 아니라 다양한 데이터 타입에 적용 가능
  • 비선형 경계 학습 가능
  • 데이터 셋의 크기가 커질 수록 예측에 필요한 계산량이 기하급수적으로 증가
  • 모든 특성을 동일한 중요도로 고려. (가중치 조정 불가) -> 특성 스케일링 중요
  • 차원의 저주 문제로 인해, 고차원 데이터에서 성능이 급격히 떨어질 수 있음.
  • 이상치가 있는 경우 성능 저하.

** 여기서 비모수적이라는 말은, 학습 파라미터가 없음을 의미한다.

from sklearn.neighbors import KNeighborsClassifier

knn = KNeighborsClassifier(n_neighbors=3, p=2)

KNN 성능 개선 방안

  • 데이터의 특성에 맞는 거리 측정 기법 선택
  • 최적의 k값 선택을 위해 교차 검증 사용

SVM (Support Vector Machine)

주료 이진 분류 문제에 사용. 데이터 포인트를 구분하는 최적의 초평면(hyperplane)을 찾음.(회귀에도 사용 가능)

  • 두 클래스간의 마진을 최대화하는 방향으로 초평면을 선택.
    • Support Vectors : 초평면을 정의하는 데이터 초인트들로, 마진 경계를 형성. SVM의 결정 함수에 직접적인 영향을 미침.
    • 선형/비선형 문제에서도 knrnel trick을 사용 (선형으로 경계를 구분하는 초평면을 만들 수 없을 때, 차원을 높이고 계산하기 위함.)
      • trick이라고 하는것은 실제로 고차원으로 만들지 않고, 내부적으로만 차원이 바뀐것처럼 계산하기 때문.
    • hard margin이 아닌 soft margin도 존재. (초평면 경계 사이에 어느정도의 데이터 포인트가 들어와도 용인해줌.)
      • C값이 0에 가까워질수록 데이터 포인트를 많이 허용.-> 오차를 더 허용한다. / 1에 가까워질수록 허용하지 않음.
  • 고차원 데이터셋에 효과적. (특성 수가 많아도 잘 작동함)
  • 마진을 최대화 하는 과정에서 과적합 방지 및 일반화 성능이 향상됨
  • 커널 함수를 통해 선형적으로 분리할 수 없는 문제를 해결 가능
  • 데이터 포인트 수나 feature수가 많은 경우 계산 비용이 매우 높을 수 있다.
  • 커널 함수 및 하이퍼파라미터(C,gamma) 선택이 모델 성능에 큰 영향을 미치며 그 최적화 과정이 어려움.
    • C : 오차 허용 정도를 조절. 커질수록 마진이 좁아지고 특화됨./ gamma : RBF 커널을 사용하는 경우 결정 경게의 유연성을 조절. 클 수록 복잡한 경계를 형성.
  • 불균형한 데이터셋에 대해 성능이 떨어짐. (가중치 조정 및 다른 알고리즘과 결합하는 등의 추가 작업 필요)
  • kernel = 'linear', 'rbf'. / C = / gamma =
  • 무조건 hyperplane 자체는 선형임.

성능 개선 방안 - 앙상블

  • Bagging (Bootstrap Aggregation)
    • 샘플을 여러 번 뽑아(복원 추출) 각 모델을 학습시켜 결과물을 집계 한다.
    • Random Forest
  • Boosting
    • 여러 개의 약한 학습기를 순차적으로 연결하여 이전 모델에서 발생한 오류에 대해 높은 가중치를 부여
    • 병렬이 아닌 순차적으로 동작!!
    • 대표적인 모델로 Adaptive Boosting과 Gradient Boosting이 존재 (현재는 XGBoost, LightGBM등도 존재..)

Random Forest

  • 여러 개의 decision trees를 결합하여 강력한 예측 모델을 만드는 앙상블 학습 방법
  • 각 나무는 원본 데이터로부터 임의 복원 추출 (bootstrapping)된 데이터로 학습하고, 예측 시 다수결 투표(voting)나 평균(회귀의 경우)을 통해 최종 결과를 제공한다.
  • 안정적이고 높은 예측 성능을 보이지만, 각 나무의 결정 과정을 해석하기 어렵고, 메모리 사용량이 많아질 수 있다.

주요 파라미터

  • n_estimators : 사용할 decision tree 나무 수
  • criterion : 노드 분할의 품질을 측정하는 함수를 지정 -> gini,entropy...
  • max_depth : 나무의 최대 깊이 제한
  • min_samples_split : 내부 노드를 분할하기 위한 최소 샘플 수를 지정. 높을 수록 나무가 덜 복잡
  • min_samples_leaf : 리프 노드가 되기 위한 최소 샘플 수를 지정. 값이 클수록 과적합 감소.
  • max_features : 각 분할시 고려할 최대 특성 수. 작은 값을 사용하면 각 나무가 서로 더 달라져 과적합 방지.
  • random_state : 랜덤 시드 값
  • n_jobs : 병렬 처리에 사용할 CPU 코어 수. -1설정시 모든 코어 사용
  • class_weight : 클래스별 가중치 설정. 불균형할때 성능 향상에 도움이 됨.

Gradient Boosting Tree

초기에는 Gradient Regression Tree(회귀 모델)로 나왔으며, 회귀와 본류 모두 가능하다. (회귀/분류 모두 내부적으로는 Regression Tree를 사용)

voting

# voting을 진행해보자. 가중치의 총합은 1로.
from sklearn.metrics import r2_score

w = 0.5
pred = w*gbm_pred + (1-w) * rf_pred
root_mean_squared_error(y_test, pred)
r2_score(y_test, pred)

import numpy as np
import matplotlib.pyplot as plt

results = []
best_w = 0
best_r2_score = 0

for w in np.arange(0.05, 1.0, 0.01):
    pred = w*gbm_pred + (1-w)* rf_pred
    score = r2_score(y_test, pred)
    if(best_r2_score < score):
        best_r2_score = score
        best_w = w
    results.append(score)

print(best_w, best_r2_score)
plt.plot(results)

base모델과 meta모델을 통한 stacking

# stacking (가중치를 주기 어려울 때 사용)
data = {
    'gbm' : gbm_pred,
    'rf' : rf_pred,
    'label' : y_test
}

new = pd.DataFrame(data)
newx = new.iloc[:, :-1]
newy = new['label']

from sklearn.linear_model import LinearRegression

lr = LinearRegression(fit_intercept=False) # bias 없이 원점을 지나는 형태로
lr.fit(newx, newy)

lr.coef_
  • 성향이 같은 모델들은 취합해도 성능 개선에 큰 도움이 되지 않음.

불균형 데이터

레이블 데이터의 클래스 비율이 현저히 차이나는 데이터

ex) 스팸 이메일 분류를 하는데 스팸 메일이 전체의 1%에 불과한 경우.

model_weighted = LogisticRegression(max_iter=100000, random_state=42, class_weight='balanced')

ConfusionMatrixDisplay.from_estimator(model_weighted, X_test, y_test)
plt.show()  # 혼동 행렬을 화면에 출력
  • 데이터 리샘플링
    • Oversampling : 소수 클래스의 데이터를 복제하여 데이터셋을 증강. ex)SMOTE, ADASYN
    • Undersampling : 다수 클래스의 데이터를 무작위로 제거. ex) RandomUnderSampler, TomekLinks
    • 조합 샘플링 : Oversampling과 Undersampling을 결합. ex) SMOTETom (SMOTE + TomekLinks)
  • 가중치 조절 : 손실 함수 계산시 소수 클래스에서 더 큰 가중치를 부여

=> imbalanced-learn 라이브러리에서 다양한 리샘플링 기법을 제공

*근본적인 해결책으로는 데이터를 더 수집하는 방법이 있다.


Q&A 


Q. 정보량이란 무엇인가요?

A.
정보량(information content)은 어떤 사건이 얼마나 놀라운(surprising)지를 나타내는 수치입니다.
→ 발생 확률이 낮을수록 정보량이 크고,
→ 발생 확률이 높을수록 정보량이 작습니다.

$I(x) = -\log P(x)$

  • $I(x)$: 사건 $x$의 정보량
  • $P(x)$: 사건 $x$가 발생할 확률
  • $\log$: 로그 함수인데, 보통 2를 밑으로 쓰는 $\log_2$를 사용

즉, 예상치 못한 사건이 일어날수록 우리에게 더 많은 “정보”를 준다고 볼 수 있습니다.

Q. 여러 파라미터간 최초 튜닝시 기본적으로 사용하는 값, 범위가 어떤 상황에도 범용적으로 통용되는가? 파라미터별 기본값을 한 곳에서 한 번에 볼 수 있는 곳이 있는가?

A. 파라미터 기본값은 범용적인 정답이 아니라 ‘시작점’일 뿐입니다.

  • 예: Decision Tree는 ‘gini’가 기본값으로 많이 쓰이지만 상황에 따라 변경 필요
  • 데이터 분할 비율(예: 75%), random_state 값(예: 0, 42)도 기본값에서 출발하되 튜닝 권장
  • 한 번에 모든 파라미터 기본값을 볼 수 있는 공식 자료는 없으며, 각 모델이나 라이브러리(예: scikit-learn) 공식 문서 참고 필요

Q. Scaler를 바꿔도 큰 성능 차이가 없었는데, 언제 Scaler를 바꿔 사용하는 게 좋은지 알려 달라.

A. Scaler가 중요한 경우는 크게 두 가지입니다:

  1. Gradient Descent 기반 모델:
    • 스케일이 다르면 수렴 속도가 느려짐
    • 반복 횟수 적을수록 영향 큼 (예: max_iter=10)
    • 반복 횟수 충분히 크면(Standard: 1000) 차이 감소
  2. 거리 계산 기반 모델:
    • KNN, K-means처럼 거리 쓰는 알고리즘은 스케일 영향 절대적

Scaler 종류별 추천:
| 상황 | 권장 Scaler | 설명 |
|----------------|-------------------|---------------------------|
| 이상치 적음 | StandardScaler | 평균0, 분산1로 정규화 |
| 값이 제한된 경우 | MinMaxScaler | 0과 1 사이로 변환(이미지 등) |
| 이상치 많음 | RobustScaler | 중앙값과 IQR 기준 조정 |

  • 실제 학습·평가기준과 다를 수 있으나, 일반화된 모델 구축을 위해선 스케일러 적용 권장
  • Kaggle 등 특수 환경에선 성능 악화시 생략 가능

Q. 이론과 코딩(실습)을 어떻게 연결하는 것이 좋을까?

A.

  • 이론과 코딩은 별개로 익숙해지면서 점차 연결됩니다.
  • 처음부터 매번 이론과 코드를 직접 연결해 작성하기보다, 코딩 도구와 라이브러리에 익숙해지는 것이 우선
  • 이론으로 모델을 이해하고, 코딩은 그 모델을 구현·조합하는 ‘도구 사용’이라 생각하면 편함
  • 예: 머신러닝은 scikit-learn, 딥러닝은 TensorFlow나 PyTorch처럼 프레임워크 익숙해지며 자연스럽게 연결됨

Q. 분류 모델의 기본 임계값은 항상 0.5인가? 모델별 Default 임계값 확인 방법은?

A.

  • 이진 분류 기본 임계값은 보통 0.5입니다.
  • 특수 상황(예: 감염병 예측 등)에서 임계값 조정 가능
  • 임계값 조절 시 predict_proba를 통해 직접 확률 받아 처리해야 하며, predict는 기본 0.5 임계값 사용
  • 모델별 기본 임계값이나 설정은 각 라이브러리 공식 문서에서 확인 가능

이렇게 정리하면 각 Q&A가 한눈에 들어오면서 핵심만 명확히 파악됩니다.

Q. _로 끝나는 애들은 뭐야?

A.

  • scikit-learn에서 모델 객체의 속성 이름이 _(언더스코어)로 끝나는 것들은 모델 학습(fit) 후에 생성되는 값임을 의미합니다.
  • 주로 모델의 “학습 결과” 혹은 “내부 파라미터”를 담고 있습니다.
  • ex) coef_, intercept_, feature_importances_

추가

인코딩 w/sklearn

데이터 분할 전에 진행해야 함!
열별로 fit_transform 따로 해야 함!

  • label encoding
    • 1차원으로 구성된 문자열 변수를 정수 변수로 인코딩
  • Ordinal encoding
    • 2차원으로 구성된 문자열 변수를 정수 변수로 인코딩

 

차원(feature) 축소 기법 - PCA

데이터의 특성 수를 줄이면서도 데이터의 중요한 구조적 특성이나 분포를 최대한 보존하려는 목표를 가진다.

왜? 저차원으로 만들고자 하는가?

  • 고차원 데이터는 데이터 포인트 간의 거리가 희석되어, 거리 기반 알고리즘의 성능이 저하될 수 있어 차원의 저주 문제가 발생할 수 있다.
  • 고차원 데이터는 모델의 복잡도가 증가해 학습 및 예측 시간이 길어진다.

차원 축소 유형 2가지

  • Feature Selection : 불필요한 변수를 버리는것
  • Feature Extraction : 기존의 특성을 조합하는 방식. 선형 기법인 PCA와 비선형 기법인 t-SNE가 존재.

PCA (Principal Component Analysis) 동작 방식

  1. 데이터의 공분산 행렬을 계산
    • 공분산은 두 데이터 쌍이 같이 커지거나 같이 작아지는지를 수치로 나타냅니다.
  2. 공분산 행렬의 고유 벡터와 고유 값을 계산하여, 데이터의 최대 분산 방향을 찾는다.
    • “이 데이터에서 가장 ‘많이 퍼진 방향’을 찾는다”
  3. 고유 값이 큰 순서대로 고유벡터를 정렬하여 주성분을 형성
    • 정보를 가장 많이 담고 있는 “축”들을 정렬해서 우리가 쓸 주요 축으로 선택합니다.
  4. 원본 데이터를 주성분 축으로 투영하여 차원을 축소
    • “이 복잡한 데이터를 단순하게 바꿔치기 하자!”

실제 적용 순서

  1. 표준화
  2. 특성 개수만큼 주성분 추출
  3. 각 주성분의 분산별로 나열 (누적 분산 비율 >0.8이라면 거기까지만 잘라서 사용) 0.8뒤 elbow point있다면 거기까지는 사용해도 ok

최초 우상향 벡터 기준으로 각 피쳐에 대해 직교하는 벡터들 정렬. 영향도 큰것부터 0.8될때까지 누적합.