Juni_DEV

Forward Selection부터 SHAP까지: 피처 선택과 모델 해석을 위한 최적화 기법 본문

Artificial Intelligence/Kaggle

Forward Selection부터 SHAP까지: 피처 선택과 모델 해석을 위한 최적화 기법

junni :p 2024. 9. 8. 23:49
반응형

Kaggle Featured 대회인 ISIC 2024 - Skin Cancer Detection with 3D-TBP에 참여하면서, 다양한 피처를 분석하고 각 피처의 의미 있는지 여부를 확인하는 과정을 거쳤습니다. 이 과정에서, 모델 학습 시 어떤 피처들이 예측에 중요한 영향을 미치는지 확인하며 관련 기법들을 공부하게 되었습니다. Forward Selection, Backward Elimination, Stepwise, Genetic Algorithm, 상관관계 분석, Feature Importance 확인, SHAP 등 총 7가지 기법을 적용해 분석을 시도했습니다.

1. Forward Selection (전방 선택)

Forward Selection은 피처를 하나씩 추가하면서 모델 성능을 개선하는 방식의 피처 선택 기법입니다.

  • 장점: 연산 비용이 비교적 적음.
  • 단점: 피처 간 상호작용을 잘 반영하지 못할 수 있음.
  • 여기서는 주로 R², AIC 같은 성능 지표를 기준으로 모델을 개선합니다.

기본 과정:

  1. 피처가 없는 상태에서 시작.
  2. 하나씩 피처를 추가하면서 성능이 가장 많이 개선되는 피처를 선택.
  3. 더 이상 성능이 개선되지 않을 때까지 반복.
from sklearn.linear_model import LinearRegression
from sklearn.model_selection import train_test_split
from sklearn.metrics import r2_score

X, y = df_train[feature_cols], df_train[target_col]
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)

selected_features = []
remaining_features = list(feature_cols)
best_score = -float('inf')

while remaining_features:
    scores = []
    for feature in remaining_features:
        model = LinearRegression()
        current_features = selected_features + [feature]
        model.fit(X_train[current_features], y_train)
        y_pred = model.predict(X_test[current_features])
        scores.append((r2_score(y_test, y_pred), feature))

    best_new_score, best_new_feature = max(scores) # 성능이 가장 좋은 피처 추가
    if best_new_score > best_score:
        selected_features.append(best_new_feature)
        remaining_features.remove(best_new_feature)
        best_score = best_new_score
    else:
        break

print(f"선택된 피처: {selected_features}")

2. Backward Elimination (후방 제거)

Backward Elimination은 피처 선택 기법 중 하나로, 모든 피처를 포함한 모델에서 출발하여 유의미하지 않은 피처를 하나씩 제거하는 방식입니다.

  • 장점: 불필요한 피처를 제거하여 모델을 더 간결하게 만들고, 과적합(overfitting)을 방지할 수 있음.
  • 단점: 초기 모델이 모든 피처를 포함하므로 계산 비용이 큼.
  • 통계적 유의성을 평가할 때, 일반적으로 p-value를 기준으로 합니다.

기본 과정:

  1. 모든 피처를 포함한 모델을 학습.
  2. 피처의 p-value를 확인하고, 통계적 유의미성을 기준으로 가장 영향이 적은 피처를 제거.
  3. 모델을 다시 학습시키고, 성능이 더 이상 개선되지 않을 때까지 반복.
import statsmodels.api as sm

X, y = df_train[feature_cols], df_train[target_col]

X = sm.add_constant(X)# 상수 항 추가
model = sm.OLS(y, X).fit() # 모델 피팅
print(model.summary()) # 모델 요약 결과에서 p-value 확인

# p-value가 높은 피처를 제거하고, 모델 학습을 다시 진행해야 함

3. Stepwise (단계적 선택)

Stepwise는 Forward Selection과 Backward Elimination을 결합한 방법으로, 피처를 추가하고 필요에 따라 다시 제거하는 과정을 반복하면서 모델 성능을 최적화하는 방법입니다.

  • 장점: Forward Selection보다 더 유연하게 피처를 선택할 수 있음. 과적합을 줄이는 데 효과적임.
  • 단점: 연산 비용이 Forward Selection에 비해 클 수 있음.

기본 과정:

  1. Forward Selection처럼 피처를 하나씩 추가.
  2. 성능이 떨어지는 피처는 Backward Elimination 방식으로 불필요한 피처를 제거.
  3. 성능이 더 이상 개선되지 않을 때까지 반복.
from mlxtend.feature_selection import SequentialFeatureSelector as SFS
from sklearn.linear_model import LinearRegression

X, y = df_train[feature_cols], df_train[target_col]

# 단계적 선택 기법 적용
sfs = SFS(estimator, k_features='best', forward=True, floating=True, scoring='r2', cv=5)
sfs = sfs.fit(X, y)

print(sfs.k_feature_names_) # 선택된 피처들

=> Backward, Forward, Stepwise 같은 피처 선택 기법은 결측치(NaN)를 직접 처리하지 못합니다. 
이는 모든 데이터를 사용해 성능 기반 계산을 하기 때문입니다. ISIC 2024 대회의 Train set에 결측치가 많아, 이 기법을 사용하려면 먼저 결측치를 처리해야 할 것 같습니다.

4. Genetic Algorithm (유전 알고리즘)

유전 알고리즘은 자연선택을 모방한 최적화 알고리즘으로, 피처 선택에도 사용할 수 있습니다.
유전 알고리즘은 진화 과정처럼 다양한 피처 집합을 조합하고, 교차 및 변이를 통해 최적의 피처 집합을 찾아내며, 특성 선택에서도 매우 효과적입니다. deap, TPOT 등의 라이브러리를 사용해 적용할 수 있습니다.

  • 장점: 매우 복잡한 문제에서도 최적 또는 근사해를 찾을 수 있음.
  • 단점: 다른 피처 선택 방법들에 비해 계산량이 많고 느림.

기본 과정:

  1. 초기 피처 집합(개체군)을 무작위로 생성.
  2. 각 피처 집합 대해 모델 성능을 평가.
  3. 성능이 좋은 피처 집합끼리 교차 및 변이를 적용.
  4. 최적의 피처 집합을 찾아 성능이 충분히 향상될 때까지 반복.
from tpot import TPOTClassifier

X, y = df_train[feature_cols], df_train[target_col]

# 유전 알고리즘을 활용한 피처 선택 및 모델 학습
tpot = TPOTClassifier(generations=5, population_size=50, verbosity=2)
tpot.fit(X, y)

# 최적의 파이프라인 출력
print(tpot.fitted_pipeline_)

Error (Timeout)

RuntimeError: There was an error in the TPOT optimization process. This could be because the data was not formatted properly, because a timeout was reached or because data for a regression problem was provided to the TPOTClassifier object. Please make sure you passed the data to TPOT correctly. If you enabled PyTorch estimators, please check the data requirements in the online documentation: <https://epistasislab.github.io/tpot/using/>
  • 데이터 포맷 문제: X와 y 데이터가 TPOT이 요구하는 형식과 맞지 않을 때 발생할 수 있습니다. => dataframe을 numpy 배열로 변환했으나 그래도 해결 안 됨   
  • 잘못된 모델 사용: TPOTClassifier는 분류 문제에 사용됩니다. 만약 회귀 문제에 해당 데이터를 사용하려고 한다면, TPOTClassifier 대신 TPOTRegressor를 사용해야 합니다. => 분류 문제 맞음
  • 시간제한 초과: TPOT에서 최적화를 진행하는 동안 시간이 초과될 경우 오류가 발생할 수 있습니다.

⇒ 모델 축소... 그래도 안되네요ㅜㅜ (같은 에러 발생)

tpot = TPOTClassifier(
    generations=2,  # 세대 수 줄이기
    population_size=10,  # 인구 크기 줄이기
    verbosity=2,
    max_eval_time_mins=10,  # 시간 제한 늘리기
    config_dict='TPOT light'
)

=> 확인해보니 Genetic Algorithm도 결측치를 처리할 수가 없었습니다.
TPOT이 결측치(NaN)를 허용하지 않기 때문에, 데이터 내 결측치를 대체하거나 제거하는 작업을 미리 수행해야 합니다.


5. Tabular 데이터의 상관관계 분석

위에서 설명한 기법들은 모두 피처 선택과 관련이 있지만, 상관관계 분석은 각 피처 간의 관계를 이해하는 데 중요한 역할을 합니다. 상관관계 분석을 통해 피처 간의 연관성을 파악하여, 모델이 예측할 때 중복된 정보를 피하거나, 강한 상관 관계를 가진 피처를 제거하여 모델 성능을 개선할 수 있습니다.

상관관계 분석 방법:

  • 피어슨 상관 계수: 두 연속형 변수 간의 선형 관계를 측정합니다.
  • 스피어만 순위 상관 계수: 두 변수의 순위 간 관계를 측정하며, 선형 관계가 아닌 경우에도 사용할 수 있습니다.
  • 히트맵(heatmap): 피처 간 상관관계를 시각적으로 표현하는 데 유용합니다.

기본 과정:

  1. 피어슨 상관 계수를 계산하여 상관관계가 높은 피처를 확인.
  2. 상관관계가 높은 피처 중 하나를 제거.
import seaborn as sns
import matplotlib.pyplot as plt

X, y = df_train[feature_cols], df_train[target_col]

corr_matrix = X.corr() # 상관 행렬 계산

plt.figure(figsize=(10, 8))
sns.heatmap(corr_matrix, annot=True, cmap='coolwarm') # 히트맵을 통해 상관 관계 시각화
plt.show()

⇒ Feature가 너무 많다 보니까 컬럼이 너무 많아서 보기 힘드네요.
threshold 추가해서 0.8 이상인 것만 확인하도록 수정했으나 그래도 보기 힘듭니다.

6. Feature Importance

모델을 미리 학습시킨 후, 피처 중요도를 계산하여 상위 n개의 중요한 피처만 선택해 상관관계를 시각화하는 방법입니다.

from sklearn.ensemble import RandomForestRegressor
import pandas as pd

# RandomForest로 피처 중요도 계산
model = RandomForestRegressor()
model.fit(X, y)

# 피처 중요도 상위 20개 선택
importances = model.feature_importances_
indices = np.argsort(importances)[-20:]

# 상위 20개 피처로 상관 행렬 계산
top_features = X.columns[indices]
filtered_corr_matrix = corr_matrix.loc[top_features, top_features]

# 히트맵 시각화
plt.figure(figsize=(10, 8))
sns.heatmap(filtered_corr_matrix, annot=True, cmap='coolwarm', vmin=-1, vmax=1)
plt.show()

# 클러스터 맵을 이용해 상관 행렬 시각화
plt.figure(figsize=(12, 10))
sns.clustermap(corr_matrix, cmap='coolwarm', vmin=-1, vmax=1)
plt.show()

사용하던 모델이 하이퍼파라미터 튜닝된 앙상블 모델이라 Voting Classifier는 피처 중요도를 제공하지 않는 문제가 있었습니다. 그래서 각 모델의 파이프라인에서 개별 분류기(Classifier)를 가져와 피처 중요도를 시각화하는 방향으로 진행했습니다.
각 모델을 간단히 학습시킨 후, 추가된 피처들의  중요도를 확인했습니다. 이후 세 가지 모델의 결과를 합산하여 피처 중요도가 0인 컬럼들을 삭제했고, 이 과정을 중간에 least_importance 컬럼 드롭하는 부분에 반영했습니다.

# 중요도가 0인 피처 제외
non_zero_importance_df = importance_df[importance_df['Total Importance'] > 0]
sorted_columns = non_zero_importance_df['Feature'].tolist() # 확인용

print("Sorted feature names based on importance (excluding zero importance):")
print(sorted_columns)

7. SHAP (SHapley Additive exPlanations)

SHAP는 머신러닝 모델의 예측 결과에 대한 각 피처(feature, 변수)의 기여도를 설명하는 방법입니다. 이를 통해 모델이 왜 특정 예측값을 도출했는지 해석할 수 있습니다. SHAP는 게임 이론에서 유래한 Shapley 값을 기반으로, 각 피처가 예측값에 기여하는 정도를 공정하게 계산합니다.

  • 장점: 모델이 블랙박스처럼 보일 때도 이를 해석할 수 있게 해줌. 모든 피처에 대해 일관된 기여도를 계산할 수 있음.
  • 단점: 연산량이 많아서 복잡한 모델에서는 계산 속도가 느릴 수 있음.

사용 예시:

SHAP는 일반적으로 회귀나 분류 모델에서 각 피처가 예측에 어떤 영향을 미치는지 이해하려 할 때 사용됩니다. 예를 들어, 고객이 대출을 받을 가능성을 예측하는 모델에서, SHAP를 통해 어떤 피처가 대출 승인에 긍정적 또는 부정적 영향을 미쳤는지 확인할 수 있습니다. SHAP는 이미 학습된 모델을 해석하는 데 사용됩니다. 학습된 모델을 기반으로 SHAP 값을 계산하여 각 피처가 모델의 예측에 어떻게 기여하는지 알 수 있습니다.

기본 과정:

  1. 모델을 학습. (예: XGBoost, LightGBM 등).
  2. 학습된 모델을 이용해 SHAP 값을 계산.
  3. SHAP 값의 시각화를 통해 각 피처가 예측 결과에 미친 영향을 확인.
import shap

# 모델 학습
X, y = df_train[feature_cols], df_train[target_col]
estimator.fit(X, y)

# SHAP 값 계산
explainer = shap.Explainer(estimator)
shap_values = explainer(X)

# SHAP 시각화
shap.summary_plot(shap_values, X)

⇒ 엥 왜 Error 작렬이세요

ImportError: cannot import name 'tarfile_extractall' from 'sklearn.utils.fixes' 
(/usr/local/lib/python3.10/dist-packages/sklearn/utils/fixes.py)

---------------------------------------------------------------------------
NOTE: If your import is failing due to a missing package, you can
manually install dependencies using either !pip or !apt.

To view examples of installing some common dependencies, click the
"Open Examples" button below.
---------------------------------------------------------------------------
!pip install scikit-learn==0.24.2

==>
Collecting scikit-learn==0.24.2
  Using cached scikit-learn-0.24.2.tar.gz (7.5 MB)
  Installing build dependencies ... done
  Getting requirements to build wheel ... done
  error: subprocess-exited-with-error
  
  × Preparing metadata (pyproject.toml) did not run successfully.
  │ exit code: 1
  ╰─> See above for output.
  
  note: This error originates from a subprocess, and is likely not a problem with pip.
  Preparing metadata (pyproject.toml) ... error
error: metadata-generation-failed

× Encountered error while generating package metadata.
╰─> See above for output.

note: This is an issue with the package mentioned above, not pip.
hint: See above for details.

⇒ 얘도 colab scikit-learn이 난리네요. 제 colab에 문제가 있을수도 있을 것 같습니다..
VS code에서는 잘 되길래 데이터를 다운로드해서 확인해보려고 했더니 메모리 에러가 나네요... 크아악


결론:

Forward Selection, Backward Elimination, Stepwise는 모델 학습 과정에서 중요한 피처를 선택하거나 불필요한 피처를 제거하는 데 사용됩니다. 이 기법들은 모델 성능을 기준으로 피처를 평가하여 최적의 조합을 찾는 방식입니다. Genetic Algorithm은 복잡한 문제에서 최적의 피처 조합을 찾는 데 효과적입니다. 상관관계 분석은 피처 선택 전에 데이터를 이해하고, 상관성이 높은 피처를 제거해 중복을 줄이는 데 유용합니다. Feature Importance 확인은 학습된 모델에서 각 피처가 예측에 얼마나 기여하는지 측정해 중요한 피처를 파악하는 방법입니다. 마지막으로, SHAP는 모델의 예측 결과를 해석하는 데 중점을 두며, 각 피처가 예측에 얼마나 영향을 미쳤는지 설명하는 데 유용합니다.

이번 대회에서는 데이터 결측치를 처리하는 대신, Feature Importance만 확인하여 무의미한 데이터를 삭제하는 방향으로 진행했습니다. 다양한 피처 선택 기법을 적용해보지 못한 점은 아쉽지만, 다음에는 다양한 기법들을 활용해 볼 수 있을 것 같습니다.

반응형
Comments