728x90
1.Feature Selection
모델을 구성하는 주요 피처들을 선택
- 불필요한 다수의 피처들로 인해 모델 성능을 떨어뜨릴 가능성 제거
- 설명 가능한 모델이 될 수 있도록 피처들을 선별
Feature Selection 유형
- 피처값의 분포, 피처간 높은 상관도, 결정값과의 독립성 등을 고려
- 모델의 피처 중요도 (분류: Feature importance, 회귀: 회귀계수) 기반
Scikit Learn Feature Selection 지원
RFE (Recursive Feature Elimination)
- 모델 최초 학습 후 Feature 중요도 선정
- Feature 중요도가 낮은 속성들을 차례로 제거해 가면서 반복적으로 학습/평가를 수행하여 최적 feature 추출
- 수행시간이 오래 걸리고, 낮은 속성들을 제거해 나가는 메커니즘이 정확한 Feature Selection을 찾는 목표에 정확히 부합하지 않을 수 있음
- 데이터의 크기가 적을 때에만 사용 (되도록이면 사용하지 말 것)
SelectFromModel
- 모델 최초 학습 후 선정된 Feature 중요도에 따라 평균 / 중앙값의 특정 비율 이상인 Feature들을 선택
- 알고리즘이라고 보기는 힘들고, just 유틸리티로 보는 것이 맞다
2.Recursive Feature Elimination 기법 - 실습 Code
import matplotlib.pyplot as plt
from sklearn.svm import SVC
from sklearn.model_selection import StratifiedKFold
from sklearn.feature_selection import RFECV, RFE
from sklearn.datasets import make_classification
# 분류를 위한 Feature 개수가 25개인 데이터 1000개 생성
X, y = make_classification(
n_samples=1000,
n_features=25,
n_informative=3,
n_redundant=2,
n_repeated=0,
n_classes=8,
n_clusters_per_class=1,
random_state=0,
)
# SVC classifier 선택
svc = SVC(kernel="linear")
# REFCV로 Feature들을 반복적으로 제거해가면서 학습/평가 수행.
rfecv = RFECV(
estimator=svc, step=1, cv=StratifiedKFold(2), scoring="accuracy", verbose=2
)
rfecv.fit(X, y)
print("Optimal number of features : %d" % rfecv.n_features_)
# Plot number of features VS. cross-validation scores
plt.figure()
plt.xlabel("Number of features selected")
plt.ylabel("Cross validation score (nb of correct classifications)")
plt.plot(
range(1, len(rfecv.cv_results_["mean_test_score"]) + 1),
rfecv.cv_results_["split0_test_score"],
)
plt.show()
- sample의 수를 10000으로 조정
# Build a classification task using 3 informative features
X, y = make_classification(
n_samples=10000,
n_features=25,
n_informative=3,
n_redundant=2,
n_repeated=0,
n_classes=8,
n_clusters_per_class=1,
random_state=0,
)
# SVC classifier 선택
svc = SVC(kernel="linear")
# REFCV로 Feature들을 반복적으로 제거해가면서 학습/평가 수행.
rfecv = RFECV(
estimator=svc, step=1, cv=StratifiedKFold(2), scoring="accuracy", verbose=2
)
rfecv.fit(X, y)
print("Optimal number of features : %d" % rfecv.n_features_)
# Plot number of features VS. cross-validation scores
plt.figure()
plt.xlabel("Number of features selected")
plt.ylabel("Cross validation score (nb of correct classifications)")
plt.plot(range(1, len(rfecv.grid_scores_) + 1), rfecv.grid_scores_)
plt.show()
3.SelectFromModel 기법 - 실습 Code
from sklearn.datasets import load_diabetes
diabetes = load_diabetes()
X, y = diabetes.data, diabetes.target
print(diabetes.DESCR)
import matplotlib.pyplot as plt
import numpy as np
from sklearn.linear_model import LassoCV
lasso = LassoCV().fit(X, y)
importance = np.abs(lasso.coef_)
feature_names = np.array(diabetes.feature_names)
plt.bar(height=importance, x=feature_names)
plt.title("Feature importances via coefficients")
plt.show()
- SelectFromModel을 통해 mean, median과 같은 threshold를 변경하고, model을 다시 학습하고 또 recursive 하게 돌린다
from sklearn.feature_selection import SelectFromModel
from time import time
threshold = np.sort(importance)[-3] + 0.01
print('threshold:', threshold)
sfm = SelectFromModel(lasso, threshold='1.5 * median').fit(X, y)
print("Features selected by SelectFromModel: "
f"{feature_names[sfm.get_support()]}")
# threshold: 521.7543692954391
# Features selected by SelectFromModel: ['bmi' 's1' 's5']
4.Permutation(순열) Importance
- 특정 피처들의 값을 완전히 변조했을 때 모델 성능이 얼마나 저하되는지를 기준으로 해당 피처의 중요도를 산정
- 학습 데이터를 제거하거나 변조하면 다시 재학습을 수행해야 하므로 수행 시간이 오래 걸림
- 일반적으로 테스트 데이터(검증 데이터)에 특정 피처들을 반복적으로 변조한뒤 해당 피처의 중요도를 평균적으로 산정
Permutation Importance Process
- 원본 모델의 기준 평가 성능을 측정
- 개별 Feature 별로 아래를 수행
- 1. 설정된 iteration값 별로 아래 수행
- a. 해당 feature로 shuffle
- b. 모델 성능 평가
- 2. 기준 평가 성능에서 모델 성능이 얼마나 저하되었는지 평가
- 1. 설정된 iteration값 별로 아래 수행
Permutation Importance - 실습 Code
from sklearn.datasets import load_diabetes
from sklearn.model_selection import train_test_split
from sklearn.linear_model import Ridge
from sklearn.metrics import r2_score
diabetes = load_diabetes()
X_train, X_val, y_train, y_val = train_test_split(
diabetes.data, diabetes.target, random_state=0
)
# 학습, 예측, R2 Score 평가
model = Ridge(alpha=1e-2).fit(X_train, y_train)
y_pred = model.predict(X_val)
print("r2 score:", r2_score(y_val, y_pred))
- permutation_importance(regressor, X_val, y_val, n_repeats, random_state)
- n_repeats: shuffle 횟수
- regressor: fitting한 estimator Class
from sklearn.inspection import permutation_importance
r = permutation_importance(model, X_val, y_val, n_repeats=30, random_state=0)
# 가장 평균 permutation importance가 높은 순으로 내림차순 정렬 후 평균 permutation importance값과 표준 편차 출력
for i in r.importances_mean.argsort()[::-1]:
if r.importances_mean[i] - 2 * r.importances_std[i] > 0:
print(
diabetes.feature_names[i],
" ",
np.round(r.importances_mean[i], 4),
" +/- ",
np.round(r.importances_std[i], 5),
)
# s5 0.2042 +/- 0.04964
# bmi 0.1758 +/- 0.0484
# bp 0.0884 +/- 0.03284
# sex 0.0559 +/- 0.02319
- permutation_importance.importances_mean( )
- 각 Feature당 원본 evaluation_score에서 평균적으로 성능이 얼마나 감소했는지 계산
- argsort( )
- 원본 행렬 정렬 시 정렬된 행렬의 원래 인덱스를 필요로 할 때 (key mapping시) ng.argsort()를 이용함
- np.argsort()는 정렬 행렬의 원본 index를 ndarray형으로 반환
r.importances_mean
# array([-0.00199237, 0.05587285, 0.17579633, 0.08836144, 0.04219777,
# 0.00203309, 0.00203936, 0.00318738, 0.20422681, 0.00278711])
5.왜 Feature Importance는 Feature Selection의 절대적인 기준이 될 수 없는가?
'정보/데이터를 분할하는 기준'과 '전체적인 모델 성능의 향상'를 나타내는 기준이 서로 다르다
- Feature Importance는 최적 tree 구조를 만들기 위한 피처들의 impurity(지니계수, 정보이득지수)가 중요 기준임.
결정 값과 관련이 없어도(noise 데이터) feature importance가 높아질 수 있음
- Feature importance는 학습 데이터를 기반으로 생성됨. 테스트 데이터에서는 달라질 수 있음
- Feature importance는 number형의 높은 cardinality feature에 biased 되어 있음
Permutation Importance VS Random Forest Feature Importance
import matplotlib.pyplot as plt
import numpy as np
from sklearn.datasets import fetch_openml
from sklearn.ensemble import RandomForestClassifier
from sklearn.impute import SimpleImputer
from sklearn.inspection import permutation_importance
from sklearn.compose import ColumnTransformer
from sklearn.model_selection import train_test_split
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import OneHotEncoder
- Column을 2개 추가(단순 noise 추가)했을 때, 영향도를 Permutation Importance VS Feature Importance로 체크
- 'random_cat'과 'random_num' 이라는 Column 2개를 추가
# titanic 데이터 세트 로딩.
X, y = fetch_openml("titanic", version=1, as_frame=True, return_X_y=True)
rng = np.random.RandomState(seed=42)
# 3가지 값으로 category값 random 설정.
X["random_cat"] = rng.randint(3, size=X.shape[0])
# X건수만큼 고유한 random 값 설정.
X["random_num"] = rng.randn(X.shape[0])
categorical_columns = ["pclass", "sex", "embarked", "random_cat"]
numerical_columns = ["age", "sibsp", "parch", "fare", "random_num"]
X = X[categorical_columns + numerical_columns]
X_train, X_test, y_train, y_test = train_test_split(X, y, stratify=y, random_state=42)
# Null 값 처리, category 값 encoding
categorical_pipe = Pipeline(
[
("imputer", SimpleImputer(strategy="constant", fill_value="missing")),
("onehot", OneHotEncoder(handle_unknown="ignore")),
]
)
numerical_pipe = Pipeline([("imputer", SimpleImputer(strategy="mean"))])
preprocessing = ColumnTransformer(
[
("cat", categorical_pipe, categorical_columns),
("num", numerical_pipe, numerical_columns),
]
)
# 데이터 전처리 후 RandomForest로 학습
rf = Pipeline(
[
("preprocess", preprocessing),
("classifier", RandomForestClassifier(random_state=42)),
]
)
rf.fit(X_train, y_train)
Pipeline(steps=[('preprocess',
ColumnTransformer(transformers=[('cat',
Pipeline(steps=[('imputer',
SimpleImputer(fill_value='missing',
strategy='constant')),
('onehot',
OneHotEncoder(handle_unknown='ignore'))]),
['pclass', 'sex', 'embarked',
'random_cat']),
('num',
Pipeline(steps=[('imputer',
SimpleImputer())]),
['age', 'sibsp', 'parch',
'fare', 'random_num'])])),
('classifier', RandomForestClassifier(random_state=42))])
print("RF train accuracy: %0.3f" % rf.score(X_train, y_train))
print("RF test accuracy: %0.3f" % rf.score(X_test, y_test))
# RF train accuracy: 1.000
# RF test accuracy: 0.820
ohe = rf.named_steps["preprocess"].named_transformers_["cat"].named_steps["onehot"]
feature_names = ohe.get_feature_names_out(input_features=categorical_columns)
feature_names = np.r_[feature_names, numerical_columns]
tree_feature_importances = rf.named_steps["classifier"].feature_importances_
sorted_idx = tree_feature_importances.argsort()
y_ticks = np.arange(0, len(feature_names))
fig, ax = plt.subplots()
ax.barh(y_ticks, tree_feature_importances[sorted_idx])
ax.set_yticklabels(feature_names[sorted_idx])
ax.set_yticks(y_ticks)
ax.set_title("Random Forest Feature Importances (MDI)")
fig.tight_layout()
plt.show()
- 검증 데이터 세트로 Permutation Importance 수행
result = permutation_importance(
rf, X_test, y_test, n_repeats=10, random_state=42, n_jobs=2
)
sorted_idx = result.importances_mean.argsort()
fig, ax = plt.subplots()
ax.boxplot(
result.importances[sorted_idx].T, vert=False, labels=X_test.columns[sorted_idx]
)
ax.set_title("Permutation Importances (test set)")
fig.tight_layout()
plt.show()
728x90