«   2024/06   »
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30
Recent Posts
Today
Total
관리 메뉴

짜리몽땅 매거진

[ML] Feature Selection 본문

Data/Machine Learning

[ML] Feature Selection

쿡국 2024. 6. 22. 10:50

Feature Selection이란 모델링을 진행하기 전 전처리단에서 유의미한 변수들만 선택함으로써 모델의 성능을 개선하고 과적합 방지 등의 효과를 얻기 위해 사용하는 방법을 말한다.

 

여러 Feature Selection 기법들을 실습 코드와 함께 살펴보자.

 

1. Filter Method

통계 기법을 사용하여 상관관계가 높은 변수나 성능이 높은 변수를 추출하는 방법

 

(1) VarianceThreshold

분산이 낮은 데이터는 도움이 안된다고 판단하여 제거하는 방법으로 titanic 샘플 데이터를 활용해 실습을 진행했다.

from sklearn.feature_selection import VarianceThreshold

#피처 정리
X=titanic.drop(['survived','alive','who','adult_male','alone'],axis =1)
y= titanic['survived']

#VarianceThreshold
threshold =0.1
selector = VarianceThreshold(threshold)
X_reduced = selector.fit_transform(X)

# 선택된 특성을 확인하기 
selected_features=X.columns[selector.get_support(indices=True)]
print('selected_features 선택된 피처 ',selected_features)

 

(2) SelectKBest, chi2

카이제곱 값을 계산하여 변수를 선택한다. 변수와 타겟 간의 독립성을 검정하는 통계적인 방법으로, 카이제곱의 임계값보다 크다면 변수와 타겟 사이에 통계적으로 유의미한 관계가 있음을 나타낸다. 미리 범주형 변수들을 수치형 변수로 변환하는 인코딩 작업을 꼭 해야 한다.

from sklearn.feature_selection import SelectKBest, chi2

#범주형 데이터를 수치형으로 변환하고 인코딩 진행
label_encoder=LabelEncoder()
titanic['sex'] = label_encoder.fit_transform(titanic['sex'])
titanic['embarked'] = label_encoder.fit_transform(titanic['embarked'])
titanic['class'] = label_encoder.fit_transform(titanic['class'])

# 연속형 데이터를 범주형 데이터로 변환
disc=KBinsDiscretizer(n_bins =5, encode = 'ordinal', strategy='uniform')
titanic['age']=disc.fit_transform(np.array(titanic['age']).reshape(-1,1))
titanic['fare']=disc.fit_transform(np.array(titanic['fare']).reshape(-1,1))

# 카이제곱 적용 
chi_selector = SelectKBest(chi2, k='all')
chi_selector.fit(X,y)

# 카이제곱 점수 확인
chi_scores = pd.DataFrame({
    'Feature': X.columns,
    'Score':chi_selector.scores_}).sort_values(by='Score', ascending=True)

 

(3) Mutual Information

엔트로피 지수를 가지고 상호의존성을 측정하는 방법이다.

from sklearn.feature_selection import mutual_info_classif

#학습 데이터셋 정리
X = titanic[['pclass', 'sex', 'age', 'sibsp', 'parch', 'fare', 'embarked', 'class','deck']]
y = titanic['survived']

# 뮤추얼정보 계산
mi_scores = mutual_info_classif(X,y, discrete_features='auto', random_state=111)

# 뮤튜얼정보 점수 확인
mi_scores_df = pd.DataFrame({'Features':X.columns , 'MI Scores':mi_scores})
mi_scores_df = mi_scores_df.sort_values(by='MI Scores',ascending=True)

 

2. Wrapper Method

반복되는 알고리즘을 사용하는 지도 학습 기반의 차원 축소법으로, Wrapper Method 방식에는 전진 선택(Forward selection), 후진 제거(Backward elimination), Stepwise selection 방식을 주로 사용한다.

 

(1) RFE

반복적으로 훈련시키고, 가장 중요도가 낮은 특성을 하나씩 제거하는 방식으로, 몇 개 제거할지 등을 지정할 수 있다.

from sklearn.feature_selection import RFE

elector=RFE(estimator=model, n_features_to_select=2)
selector=selector.fit(X,y)

print('선택된 특성 출력', selector.support_)

 

(2) RFECV

RFE에서 확장된 버전으로, 교차검증 통해 최적의 특성 수를 자동으로 결정하고, 반복적으로 특성 제거하면서 변수를 선택한다. 이번 실습에서는 titanic 데이터를 활용해 로지스틱 회귀, 랜덤포레스트, SVC 세 개의 모델링 후 교차검증까지 진행하여 각 모델별 평가 점수를 통해 선택된 피처를 확인했다. 

rom sklearn.linear_model import LogisticRegression
from sklearn.ensemble import RandomForestClassifier
from sklearn.svm import SVC
from sklearn.feature_selection import RFECV
from sklearn.model_selection import StratifiedKFold
from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score

# 독립변수 종속변수 정리
X= titanic.drop(['survived','deck','alone','who','adult_male','alive','embark_town'],axis=1) 
y= titanic['survived']

# 훈련 데이터셋으로 분할
X_train, X_test, y_train, y_test= train_test_split(X, y, test_size=0.2, random_state=111)


## 모델정의
models ={
    'LogisticRegression':LogisticRegression(max_iter=200),
    'RandomForestClassifier': RandomForestClassifier(),
    'SVC': SVC(kernel='linear')
}

# 교차검증 설정
cv=StratifiedKFold(3)

#결과를 정리할 dataframe
results= pd.DataFrame(columns =['Model','Dataset','Accuracy','Precision','Recall','F1','Selected Features'])

# 각 모델 RFECV 수행

for name, model in models.items():
    selector = RFECV(estimator= model, step=1, cv=cv, scoring='accuracy')
    selector.fit(X_train, y_train)
    
    # train, test를 같이 비교하게끔 코드 수정
    for data in [('Train', X_train, y_train), ('Test',X_test, y_test)]:
        dataset_name, X_data, y_data = data
        y_pred = selector.predict(X_data)
        
        accuracy = accuracy_score(y_data, y_pred)
        precision = precision_score(y_data, y_pred)
        recall = recall_score(y_data, y_pred)
        f1 = f1_score(y_data,y_pred)
        
        selected_features = ', '.join(X.columns[selector.support_])
        
        #결과들을 append
        results = results.append({
            'Model':name,
            'Dataset':dataset_name,
            'Accuracy' : accuracy,
            'Precision': precision,
            'Recall':recall,
            'F1':f1,
            'Selected Features': selected_features
        }, ignore_index=True)
        
print(results)

출력된 results

(3) Boruta

Boruta는 랜덤 포레스트와 같은 트리 기반 모델을 여러 번 학습시켜 각 변수의 중요도를 평가하고, 이를 바탕으로 변수 선택을 수행하는 기법

from boruta import BorutaPy

X = df[['pclass','sex','age','sibsp','parch','fare','embarked','deck']]
y = df['survived']

# 랜덤포레스트 초기화 가지고 오기!

rf = RandomForestClassifier(class_weight= 'balanced', max_depth=5)

# Borutapy 불러오기
boruta_selector=BorutaPy(rf, n_estimators='auto',random_state=111)
# selector 학습
boruta_selector.fit(X.values, y.values)

 

3. Embedded Method

모델 학습 과정에서 변수 선택을 동시에 수행하는 기법

 

(1) Lasso

회귀 분석에서 사용되는 기법으로, 변수 선택과 정규화를 동시에 수행한다. 비용 함수에 L1 규제 항을 추가하여 계수의 절대값의 합을 최소화한다. 이로 인해 일부 계수가 0이 되며, 결과적으로 변수 선택이 이루어진다.

from sklearn.linear_model import Lasso
from sklearn.preprocessing import StandardScaler

#피처 정리
X = df[['pclass','sex','age','fare','embarked']]
y = df['survived'].values

X_train, X_test, y_train, y_test = train_test_split(X,y ,test_size=0.2, random_state=111)
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)

#라쏘회귀로 진행
lasso = Lasso(alpha =0.1)
lasso.fit(X_train_scaled, y_train)
lasso_coefs = lasso.coef_

# 피처 중요도 출력
features = X.columns
lasso_imp =pd.DataFrame({'Feature':features, 'Coefficient':lasso_coefs})

# 0이 아닌 가중지를 가진 피처들은 어떤 것인가?
selected_features= lasso_imp[lasso_imp['Coefficient'] !=0]

print('Lasso Selected Features')
print(selected_features)

 

4. Model-agnostic Explanation Method

모델의 예측을 설명하고 변수 중요도를 평가하는 데 사용하는 방법으로 XAI라고도 불린다.

 

(1) SHAP

게임 이론에서 유래한 샤플리 값(Shapley value)을 기반으로 하며, 각 변수의 기여도를 공정하게 분배한다. SHAP는 모델에 관계없이 변수의 중요도와 예측에 미치는 영향을 설명할 수 있다.

import shap

# 데이터 분할
X=df[['sex','age','fare']]
y=df['survived']
X_train, X_test, y_train, y_test =train_test_split(X,y, test_size=0.3, random_state=111)

#모델 간단한 학습
model = RandomForestClassifier(random_state=111)
model.fit(X_train, y_train)

## SHAP 값 구하기 
## SHAP Explainer 초기화 및 SHAP 값 계산

explainer = shap.TreeExplainer(model)
shap_values= explainer.shap_values(X_test) # shap 값을 간단하게 계산한다.

# shap 요약 플롯
shap.summary_plot(shap_values[0], X_test, plot_type='bar', feature_names = X_test.columns.tolist())

shap value를 시각화할 때 주로 사용하는 summary plot

 

(2) LIME

모델이 특정 데이터 포인트에 대해 예측을 내리는 과정을 단순화하여 로컬 선형 모델을 학습시킨다. LIME은 모델에 관계없이 적용 가능하며, 개별 예측의 해석을 돕는 데 유용합니다.

import lime
import lime.lime_tabular

# 데이터 분할
X = df[['pclass','sex','age','fare','embarked']].values
y = df['survived'].values

X_train, X_test, y_train, y_test = train_test_split(X,y ,test_size=0.2, random_state=111)

# 모델 학습
model = RandomForestClassifier(random_state=42)
model.fit(X_train, y_train)

#해석기를 통해 해당 피처 중요도 확인
explainer = lime.lime_tabular.LimeTabularExplainer(X_train, feature_names=['pclass','sex','age','fare','embarked'],class_names=['not survived','survived'])

# 특정 데이터 포인트에 대한 해석 
i = 1
exp =explainer.explain_instance(X_test[i],model.predict_proba, num_features=5)

#해석 결과를 시각화로 확인
exp.show_in_notebook(show_table=True, show_all =False)
print(exp.as_list())