피처 선택은 타겟(목표값, Target)과 가장 관련이 높은 변수만을 선정하여 피쳐의 수를 줄이는 방법으로, 관련이 없거나 중복되는 피쳐들을 필터링하고 간결한 부분집합을 구성하는 과정이다. 이는 모델을 단순화해주고, 훈련 시간을 축소해주며 차원의 저주를 방지, 과적합을 줄여 일반화해주는 장점이 있다.
Supervised: 종속변수를 활용하여 선택한다.
Unsupervised: 종속변수를 활용하지 않고 선택한다.
Filter: 통계적 방법을 활용하여 선택
Wrapper: 모델을 활용하여 선택
Embedded: 모델 훈련과정에서 자동적으로 선택
Hybrid: Filter와 Wrapper 방식을 혼합
모든 필터 배정 -> 최적의 부분집합 선택 -> 학습 알고리즘 -> 성능평가
from sklearn import datasets
from sklearn.feature_selection import VarianceThreshold
# iris 데이터셋을 로드
iris = datasets.load_iris()
X = iris.data # iris 데이터셋의 피쳐들
y = iris.target # iris 데이터셋의 타겟
X_names = iris.feature_names # iris 데이터셋의 피쳐 이름
y_names = iris.target_names # iris 데이터셋의 타겟 이름
# 분산이 0.2 이상인 피쳐들만 선택하도록 학습
sel = VarianceThreshold(threshold=0.2).fit(X)
print(f'{sel.variances_ = }') # 각 피쳐의 분산 확인
# 분산이 0.2 이상인 피쳐들만 선택 적용
X_selected = sel.transform(X) # 분산이 0.2 이상인 피쳐들만 선택
X_selected_names = [X_names[i] for i in sel.get_support(indices=True)] # 선택된 피쳐들의 이름
print(f'{X_selected_names = }')
print(f'{X_selected[:5] = }')
sel.variances_ = array([0.68112222, 0.18871289, 3.09550267, 0.57713289])
X_selected_names = ['sepal length (cm)', 'petal length (cm)', 'petal width (cm)']
X_selected[:5] = array([[5.1, 1.4, 0.2],
[4.9, 1.4, 0.2],
[4.7, 1.3, 0.2],
[4.6, 1.5, 0.2],
[5. , 1.4, 0.2]])
# Scikit-Learn 제공 피쳐 선택 메서드
- SelectKBest(): 고정된 k개의 피쳐 선택기
- SelectPercentile(): 분위수 기반 선택기
- SelectFpr(): False positive rate 기반 선택기
- SelectFdr(): 추정된 False discovery rate 기반 선택기
- SelectFwe(): familiy-wise error rate 기반 선택기
- GenericUnivariateSelect(): 단변량 피쳐 선택기
# Scikit-Learn 제공 피쳐 선택 기준
- f_classif: ANOVA F-value 분류
- F-value: 두 모집단(확률변수)의 분산의 비율을 나타내는 값
- mutual_info_classif: 상호정보량(mutual information) 분류
- 상호정보량: 하나의 확률변수가 다른 하나의 확률변수에 대해 제공하는 정보의 양
- 두 확률변수가 공유하는 엔트로피
- 두 확률변수가 독립이라면, 상호정보량은 0
- 두 확률변수의 상관관계가 강할수록 상호정보량이 커짐
- chi2: 카이제곱 분류
- 범주형 데이터에서 두 요인간 독립성 검정에서 사용
- 카이제곱 value가 크면 두 요인간 독립이 아니라는 의미(즉, 상관관계가 있음)
- f_regression: F-value 회귀
- mutual_info_regression: 상호정보량(mutual information) 회귀
from sklearn.feature_selection import SelectKBest
from sklearn.feature_selection import f_classif, f_regression, chi2
# k개의 베스트 피쳐를 선택
sel_fc = SelectKBest(f_classif, k=2).fit(X, y)
print('f_classif: ')
print(f'{sel_fc.scores_ = }')
print(f'{sel_fc.pvalues_ = }')
print(f'{sel_fc.get_support() = }')
print('Selected features: ', [X_names[i] for i in sel_fc.get_support(indices=True)]) # 선택된 피쳐들의 이름
sel_fr = SelectKBest(f_regression, k=2).fit(X, y)
print('\nf_regression: ')
print(f'{sel_fr.scores_ = }')
print(f'{sel_fr.pvalues_ = }')
print(f'{sel_fr.get_support() = }')
print('Selected features: ', [X_names[i] for i in sel_fr.get_support(indices=True)]) # 선택된 피쳐들의 이름
sel_chi2 = SelectKBest(chi2, k=2).fit(X, y)
print('\nchi2: ')
print(f'{sel_chi2.scores_ = }')
print(f'{sel_chi2.pvalues_ = }')
print(f'{sel_chi2.get_support() = }')
print('Selected features: ', [X_names[i] for i in sel_chi2.get_support(indices=True)]) # 선택된 피쳐들의 이름
fclassif:
sel_fc.scores = array([ 119.26450218, 49.16004009, 1180.16118225, 960.0071468 ])
selfc.pvalues = array([1.66966919e-31, 4.49201713e-17, 2.85677661e-91, 4.16944584e-85])
sel_fc.get_support() = array([False, False, True, True])
Selected features: ['petal length (cm)', 'petal width (cm)']
fregression:
sel_fr.scores = array([ 233.8389959 , 32.93720748, 1341.93578461, 1592.82421036])
selfr.pvalues = array([2.89047835e-32, 5.20156326e-08, 4.20187315e-76, 4.15531102e-81])
sel_fr.get_support() = array([False, False, True, True])
Selected features: ['petal length (cm)', 'petal width (cm)']
chi2:
selchi2.scores = array([ 10.81782088, 3.7107283 , 116.31261309, 67.0483602 ])
selchi2.pvalues = array([4.47651499e-03, 1.56395980e-01, 5.53397228e-26, 2.75824965e-15])
sel_chi2.get_support() = array([False, False, True, True])
Selected features: ['petal length (cm)', 'petal width (cm)']
모든 피쳐 배정 -> 부분집합 형성 -> 학습 알고리즘- > 성능평가
반복하여 최적의 부분집합을 찾는다.
- SVM(Support Vector Machine)을 사용하여 재귀적으로 제거하는 방법
- 전진 선택, 후진 제거, 단계적 방법 사용
# RFE(Recursive Feature Elimination) 적용
from sklearn.datasets import load_iris
from sklearn.feature_selection import RFE, RFECV, SelectFromModel, SequentialFeatureSelector
from sklearn.svm import SVC, SVR
# iris 데이터셋 로드
X, y = load_iris(return_X_y=True)
# 분류기 SVC 객체 생성, 선형분류, 3개의 클래스
svc = SVR(kernel="linear", C=3)
# RFE 객체 생성, 2개의 피쳐 선택, 1개씩 제거
rfe = RFE(estimator=svc, n_features_to_select=2, step=1)
# RFE+CV(Cross Validation), 5개의 폴드, 1개씩 제거
rfe_cv = RFECV(estimator=svc, step=1, cv=5)
# 데이터셋에 RFE 적용
rfe.fit(X, y)
print('RFE Rank: ', rfe.ranking_)
# rank가 1인 피쳐들만 선택
X_selected = rfe.transform(X)
X_selected_names = [X_names[i] for i in rfe.get_support(indices=True)] # 선택된 피쳐들의 이름
print(f'{X_selected_names = }')
print(f'{X_selected[:5] = }')
# 데이터셋에 RFECV 적용
rfe_cv.fit(X, y)
print('RFECV Rank: ', rfe_cv.ranking_)
# rank가 1인 피쳐들만 선택
X_selected = rfe_cv.transform(X)
X_selected_names = [X_names[i] for i in rfe_cv.get_support(indices=True)] # 선택된 피쳐들의 이름
print(f'{X_selected_names = }')
print(f'{X_selected[:5] = }')
RFE Rank: [2 3 1 1]
X_selected_names = ['petal length (cm)', 'petal width (cm)']
X_selected[:5] = array([[1.4, 0.2],
[1.4, 0.2],
[1.3, 0.2],
[1.5, 0.2],
[1.4, 0.2]])
RFECV Rank: [1 2 1 1]
X_selected_names = ['sepal length (cm)', 'petal length (cm)', 'petal width (cm)']
X_selected[:5] = array([[5.1, 1.4, 0.2],
[4.9, 1.4, 0.2],
[4.7, 1.3, 0.2],
[4.6, 1.5, 0.2],
[5. , 1.4, 0.2]])
- 그리디 알고리즘(Greedy Algorithm)으로 빈 부분 집합에서 특성 변수를 하나씩 추가하는 방법
- 전진 선택, 후진 제거 사용
# SFS(Sequential Feature Selector) : 순차적으로 특성을 선택하는 방법
from sklearn.feature_selection import SequentialFeatureSelector
from sklearn.neighbors import KNeighborsClassifier
from sklearn.datasets import load_iris
# 데이터를 로드하고, 분류기를 초기화한 후 SFS를 적용
X, y = load_iris(return_X_y=True)
knn = KNeighborsClassifier(n_neighbors=3)
sfs = SequentialFeatureSelector(knn, n_features_to_select=2, direction='backward')
# SFS를 학습하고, 선택된 특성을 출력
sfs.fit(X, y)
print('SFS selected: ', sfs.get_support())
# 선택된 피쳐들만 선택
X_selected = sfs.transform(X)
X_selected_names = [X_names[i] for i in sfs.get_support(indices=True)] # 선택된 피쳐들의 이름
print(f'{X_selected_names = }')
print(f'{X_selected[:5] = }')
SFS selected: [False False True True]
X_selected_names = ['petal length (cm)', 'petal width (cm)']
X_selected[:5] = array([[1.4, 0.2],
[1.4, 0.2],
[1.3, 0.2],
[1.5, 0.2],
[1.4, 0.2]])
모든 피쳐 배정 -> 부분집합 형성 -> 학습 알고리즘- > 성능평가
반복하여 최적의 부분집합을 찾는다.
from sklearn.feature_selection import SelectFromModel
from sklearn import tree
from sklearn.datasets import load_iris
# 데이터를 로드하고, 분류기를 초기화한 후 SFS를 적용
X, y = load_iris(return_X_y=True)
clf = tree.DecisionTreeClassifier()
sfm = SelectFromModel(estimator=clf)
# 모형 구조 확인 및 출력을 pandas로 설정
sfm.set_output(transform='pandas')
# 모형 학습
sfm.fit(X, y)
print('SFM threshold: ', sfm.threshold_)
# 선택된 피쳐들만 선택
X_selected = sfm.transform(X)
X_selected.columns = [X_names[i] for i in sfm.get_support(indices=True)] # 선택된 피쳐들의 이름
X_selected.head()