Post

[사전학습] 3.5 데이터 축소 - 특징 선택

3. 데이터 전처리 이해와 실무

3.1 데이터 축소 : 특징 선택

특징 선택 (Feature Selection)

가장 좋은 성능을 보여줄 수 있는 데이터의 부분 집합(Subset)을 찾아내는 방법
=> 모델 생성에 밀접한 데이터의 부분 집합을 선택하여 연산 효율성 및 모델 성능을 확보

목적 및 필요성

연산 효율성

  • 특징 생성과는 다르게 원 데이터 공간 내 유의미한 특징을 선택하는 기법으로 연산 효율 및 적절한 특징을 찾기 위해 수행
  • 원본 데이터에서 가장 유용한 특징만을 선택하여 간단한 모델 구성 및 성능을 확보하고자 하는 것이 주요 목적

    특징 선택 방안

    1. 필터(Filter)

  • 특징들에 대한 통계적 점수를 부여하여 순위를 매기고 선택하는 방법론
  • 실행 속도가 빠르다는 측면에서 시간 및 비용 측면의 장점을 보임
    1) 카이제곱 필터 (Chi-square filter)
  • 범주형인 독립 및 종속 변수 간의 유의미성을 도출하기 위한 통계적 방안
  • 연소곃ㅇ 변수를 이산화(범주)를 하여 활용 가능
    2) 상관관계 필터(Correlation filter)
  • 연속형인 독립 및 종속변수 간 유의미성을 도출하기 위한 통계적 방안
  • 보통 임계치(threshold) 설정하여 변수 선택

2. 래퍼(Wrapper)

  • 특징들의 조합을 지도학습 기반 알고리즘에 반복적으로 적용하여 특징을 선택하는 방법론
  • 최적의 데이터 조합을 찾기 때문에 성능 관점 상 유용하나 시간과 비용 크게 발생
  • 원본 데이터 내 변수 간 조합을 탐색하여 특징 선택
  • 반복적 특징 조합 탐색
    • 원본 데이터 셋 내 변수들의 다양한 조합을 모델에 적용하는 방식
    • 최적의 부분 데이터 집합(Subset)을 도출하는 방법론
    • 대표적 방식으로 재귀적 특성 제거(Recursive Feature Elimination) 존재

3. 임베디드(Embedded)

  • 모델 정확도에 기여하는 특징들을 선택하는 방법으로 Filter와 Wrpper의 장점을 결합한 방법
  • 모델의 학습 및 생성과정에서 최적의 특징을 선택하는 방법
  • 모델을 학습하여 정확도에 기여하는 특징을 선택하는 방안
  • 모델 기반 특징 선택
    • 알고리즘 내 자체 내장 함수로 특징을 선택하는 방식으로, 모델 성능에 기여하는 특징을 도출
    • 모든 조합을 고려하고 결과를 도출하는 Wrapper 방식과 달리 학습과정에서 최적화된 변수를 선택
    • 트리 계열 모델 기반의 특징 선택이 대표적(랜덤포레스트 기반 Featuer Importance 기반)

4. 특징 선택 알고리즘

랜덤포레스트 모형 기반의 알고리즘

보루타 알고리즘(Boruta Algorithm)
  • Boruta Algorithm은 기존 데이터 임의로 복제하여 랜덤 변수(shadow) 생성하고 원 자료와 결합하여 랜덤포레스트 모형에 적용
  • shadow 보다 중요도가 낮을 경우 중요하지 않은 변수로 판단 후 제거

실습

1
2
import numpy as np
import pandas as pd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
cancer = pd.read_csv("./data/wdbc.data", header=None)

# 데이터 컬럼명 지정
cancer.columns = [
    "id", "diagnosis", "radius_mean", "texture_mean", "perimeter_mean", "area_mean", "smoothness_mean", "compactness_mean", 
    "concavity_mean", "concave_points_mean", "symmetry_mean", "fractal_dimension_mean", "radius_se", "texture_se",
    "perimeter_se", "texture_worst", "smoothness_se", "compactness_se", "concavity_se", "concave_points_se", "symmetry_se",
    "fractal_dimension_se", "radius_worst", "texture_worst", "perimeter_worst", "area_worst", "smoothness_worst", "compactness_worst", 
    "concavity_worst", "concave_points_worst", "symmetry_worst", "fractal_dimension_worst"
]

# ID를 Index화
cancer = cancer.set_index('id')
cancer
diagnosisradius_meantexture_meanperimeter_meanarea_meansmoothness_meancompactness_meanconcavity_meanconcave_points_meansymmetry_mean...radius_worsttexture_worstperimeter_worstarea_worstsmoothness_worstcompactness_worstconcavity_worstconcave_points_worstsymmetry_worstfractal_dimension_worst
id
842302M17.9910.38122.801001.00.118400.277600.300100.147100.2419...25.38017.33184.602019.00.162200.665600.71190.26540.46010.11890
842517M20.5717.77132.901326.00.084740.078640.086900.070170.1812...24.99023.41158.801956.00.123800.186600.24160.18600.27500.08902
84300903M19.6921.25130.001203.00.109600.159900.197400.127900.2069...23.57025.53152.501709.00.144400.424500.45040.24300.36130.08758
84348301M11.4220.3877.58386.10.142500.283900.241400.105200.2597...14.91026.5098.87567.70.209800.866300.68690.25750.66380.17300
84358402M20.2914.34135.101297.00.100300.132800.198000.104300.1809...22.54016.67152.201575.00.137400.205000.40000.16250.23640.07678
..................................................................
926424M21.5622.39142.001479.00.111000.115900.243900.138900.1726...25.45026.40166.102027.00.141000.211300.41070.22160.20600.07115
926682M20.1328.25131.201261.00.097800.103400.144000.097910.1752...23.69038.25155.001731.00.116600.192200.32150.16280.25720.06637
926954M16.6028.08108.30858.10.084550.102300.092510.053020.1590...18.98034.12126.701124.00.113900.309400.34030.14180.22180.07820
927241M20.6029.33140.101265.00.117800.277000.351400.152000.2397...25.74039.42184.601821.00.165000.868100.93870.26500.40870.12400
92751B7.7624.5447.92181.00.052630.043620.000000.000000.1587...9.45630.3759.16268.60.089960.064440.00000.00000.28710.07039

569 rows × 31 columns

1
2
3
4
5
from sklearn.model_selection import train_test_split

X_train, X_test, y_train, y_test = train_test_split(cancer.drop(['diagnosis'], axis=1), cancer[['diagnosis']], random_state=1)
print(X_train.shape)
print(X_test.shape)
1
2
(426, 30)
(143, 30)
1
2
train_df = pd.merge(X_train, y_train, left_index=True, right_index=True, how='inner')
train_df
radius_meantexture_meanperimeter_meanarea_meansmoothness_meancompactness_meanconcavity_meanconcave_points_meansymmetry_meanfractal_dimension_mean...texture_worstperimeter_worstarea_worstsmoothness_worstcompactness_worstconcavity_worstconcave_points_worstsymmetry_worstfractal_dimension_worstdiagnosis
id
92562215.2230.62103.40716.90.104800.208700.255000.0942900.21280.07152...42.79128.70915.00.141700.791701.170000.235600.40890.14090M
891514.9619.1097.03687.30.089920.098230.059400.0481900.18790.05852...26.19109.10809.80.131300.303000.180400.148900.29620.08472B
84840614.6820.1394.74684.50.098670.072000.073950.0525900.15860.05922...30.88123.401138.00.146400.187100.291400.160900.30290.08216M
92257710.3216.3565.31324.90.094340.049940.010120.0054950.18850.06201...21.7771.12384.90.128500.088420.043840.023810.26810.07399B
89170311.8517.4675.54432.70.083720.056420.026880.0228000.18750.05715...25.7584.35517.80.136900.175800.131600.091400.31010.07007B
..................................................................
86667419.7925.12130.401192.00.101500.158900.254500.1149000.22020.06113...33.58148.701589.00.127500.386100.567300.173200.33050.08465M
86925410.7514.9768.26355.30.077930.051390.022510.0078750.13990.05688...20.7277.79441.20.107600.122300.097550.034130.23000.06769B
85971717.2024.52114.20929.40.107100.183000.169200.0794400.19270.06487...33.82151.601681.00.158500.739400.656600.189900.33130.13390M
8824960214.0321.2589.79603.40.090700.069450.014620.0189600.15170.05835...30.2898.27715.50.128700.151300.062310.079630.22260.07617B
85494113.0318.4282.61523.80.089830.037660.025620.0292300.14670.05863...22.8184.46545.90.097010.046190.048330.050130.19870.06169B

426 rows × 31 columns

1
2
import matplotlib.pyplot as plt
import seaborn as sns
1
2
3
4
5
fig, axes = plt.subplots(10, 3, figsize=(20, 40))
axes = axes.flatten()

for i in range(30):
    sns.histplot(data=train_df, x=train_df.columns[1], hue='diagnosis', multiple='layer', ax=axes[i])

png

시각화 기반 특징들의 유의미함을 예상

  • 유의할 것이라고 예상되는 변수
    • radius_mean, perimeter_mean, area_mean, concave_point_mean, radius_worst, perimeter_worst, area_worst, concave_point_worst 등
  • 유의하지 않을 것이라 예상되는 변수
    • smoothness_mean, symmetry_mean, fractal_dimension_mean, texture_se, smoothness_se, symmetry_se, fractal_dimension_se 등

      RFE 기반 주요 특징 선택

1
2
from sklearn.svm import SVC
from sklearn.feature_selection import RFE
1
2
3
4
5
6
from sklearn.preprocessing import StandardScaler
# 스케일에 민감한 SVM의 특징에 따라 변수의 scaling을 따로 진행

scaler = StandardScaler()
scaler.fit(X_train)
scale_X_train = scaler.transform(X_train)
1
2
3
4
5
6
7
8
# RFE를 적용할 모델 SVM 지정
estimator_mdl = SVC(kernel="linear")
# SVM 학습 기반의 RFE 실행 및 유의미한 개수 지정 : 5개
svm_rfe = RFE(estimator=estimator_mdl, n_features_to_select=5)

svm_rfe_rst = svm_rfe.fit(scale_X_train, y_train.values.ravel())
svm_rfe_rst.ranking_
# 1로 나온 변수가 최종 유의미한 특징으로 도출된 컬럼
1
2
array([17, 23, 11, 16, 20, 25, 14,  9, 21, 13, 12, 24, 18,  1, 15, 10, 22,
        6, 26,  7,  1,  1,  2,  1, 19,  4,  3,  1,  5,  8])
1
2
3
4
# 도출된 특징 조합으로 test 진행
scale_X_test = scaler.transform(X_test)
prediction = pd.DataFrame(svm_rfe.predict(scale_X_test), columns=['pred_rst'])
prediction
pred_rst
0B
1M
2B
3M
4M
......
138B
139M
140M
141M
142B

143 rows × 1 columns

1
2
3
4
5
6
7
# 타겟 클래스 데이터 수치형 변경
y_test['diagnosis'] = y_test['diagnosis'].replace('M', 1)
y_test['diagnosis'] = y_test['diagnosis'].replace('B', 0)

prediction['pred_rst'] = prediction['pred_rst'].replace('M', 1)
prediction['pred_rst'] = prediction['pred_rst'].replace('B', 0)

1
2
3
4
5
6
7
# 결과 확인
# accuracy
from sklearn.metrics import accuracy_score
print("accuracy :", round(accuracy_score(y_test['diagnosis'], prediction['pred_rst']), 5))
# auc
from sklearn.metrics import roc_auc_score
print('auc :', round(roc_auc_score(y_test['diagnosis'], prediction['pred_rst']), 5))
1
2
accuracy : 0.95105
auc : 0.94659
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# RFE를 적용할 모델 SVM 지정
estimator_mdl = SVC(kernel="linear")
# SVM 학습 기반의 RFE 실행 및 유의미한 개수 지정 : 10개
re_svm_rfe = RFE(estimator=estimator_mdl, n_features_to_select=10)
re_svm_rfe_rst = re_svm_rfe.fit(scale_X_train, y_train.values.ravel())

re_prediction = pd.DataFrame(re_svm_rfe_rst.predict(scale_X_test), columns=['pred_rst'])

re_prediction['pred_rst'] = re_prediction['pred_rst'].replace('M', 1)
re_prediction['pred_rst'] = re_prediction['pred_rst'].replace('B', 0)

print("accuracy :", round(accuracy_score(y_test['diagnosis'], re_prediction['pred_rst']), 5))

print('auc :', round(roc_auc_score(y_test['diagnosis'], re_prediction['pred_rst']), 5))
1
2
accuracy : 0.93706
auc : 0.93523
1
2
3
4
5
6
7
8
chk_var = []
for name, svm_rfe_rst.ranking_ in zip(X_train.columns, svm_rfe_rst.ranking_):
    lst_chk  = [name, svm_rfe_rst.ranking_]
    chk_var.append(lst_chk)

chk_svm_rfe = pd.DataFrame(chk_var, columns = ['feature_names', 'svm_rfe_feature'])
chk_svm_rfe = chk_svm_rfe[chk_svm_rfe["svm_rfe_feature"]==1]
chk_svm_rfe
feature_namessvm_rfe_feature
13texture_worst1
20radius_worst1
21texture_worst1
23area_worst1
27concave_points_worst1

Embedded 기반 특징 선택

  • 모델 학습 성능에 기여하는 특징 선택하는 방안
1
2
3
# RF 모형 기반 특징 선택
from sklearn.feature_selection import SelectFromModel
from sklearn.ensemble import RandomForestClassifier
1
2
3
# RF 모형 생성
embeded_rf_selector = SelectFromModel(RandomForestClassifier(n_estimators=100, random_state=1))
embeded_rf_selector.fit(scale_X_train, y_train.values.ravel())
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
SelectFromModel(estimator=RandomForestClassifier(bootstrap=True, ccp_alpha=0.0,
                                                 class_weight=None,
                                                 criterion='gini',
                                                 max_depth=None,
                                                 max_features='auto',
                                                 max_leaf_nodes=None,
                                                 max_samples=None,
                                                 min_impurity_decrease=0.0,
                                                 min_impurity_split=None,
                                                 min_samples_leaf=1,
                                                 min_samples_split=2,
                                                 min_weight_fraction_leaf=0.0,
                                                 n_estimators=100, n_jobs=None,
                                                 oob_score=False,
                                                 random_state=1, verbose=0,
                                                 warm_start=False),
                max_features=None, norm_order=1, prefit=False, threshold=None)
1
2
3
4
# RF 기반 Embeded 특징 선택 결과
embeded_rf_support = embeded_rf_selector.get_support()
embeded_rf_feature = X_train.loc[:, embeded_rf_support].columns.tolist()
print(str(len(embeded_rf_feature)), 'selected features')
1
9 selected features
1
embeded_rf_feature
1
2
3
4
5
6
7
8
9
['radius_mean',
 'perimeter_mean',
 'area_mean',
 'concavity_mean',
 'concave_points_mean',
 'radius_worst',
 'perimeter_worst',
 'area_worst',
 'concave_points_worst']

Boruta Algorithm

  • Boruta Algorithm은 랜덤포레스트 모형 기반 특징 선택 알고리즘
  • 기존 데이터를 임의로 복제하여 랜덤변수(shadow 변수)를 생성하고 그 보다 낮은 중요도를 지닌 특징을 제외함
1
from boruta import BorutaPy
1
2
3
4
5
6
7
8
9
# boruta 알고리즘은 랜덤포레스트 모형 기반이므로 먼저 RF 모형 설정
rf = RandomForestClassifier(random_state=1)

# boruta 기반 특징 선택
boruta_selector = BorutaPy(rf, n_estimators="auto", random_state=1)

# 'auto'인 경우는 자동으로 데이터셋 사이즈를 고려하여 자동 설정
boruta_selector.fit(scale_X_train, y_train.values.ravel())
boruta_selector.ranking_
1
2
array([1, 1, 1, 1, 1, 1, 1, 1, 3, 5, 1, 5, 1, 1, 7, 1, 1, 1, 6, 3, 1, 1,
       1, 1, 1, 1, 1, 1, 1, 2])
1
2
3
4
5
6
7
8
9
# 최종 선택된 특징 컬럼이 어떤 컬럼인지 확인
chk_var_boruta=[]
for name, boruta_selector.ranking_ in zip(X_train.columns, boruta_selector.ranking_):
    lst_chk = [name, boruta_selector.ranking_]
    chk_var_boruta.append(lst_chk)

chk_boruta = pd.DataFrame(chk_var_boruta, columns=['feature_names', 'boruta_feature'])
chk_boruta = chk_boruta[chk_boruta["boruta_feature"]==1]
chk_boruta
feature_namesboruta_feature
0radius_mean1
1texture_mean1
2perimeter_mean1
3area_mean1
4smoothness_mean1
5compactness_mean1
6concavity_mean1
7concave_points_mean1
10radius_se1
12perimeter_se1
13texture_worst1
15compactness_se1
16concavity_se1
17concave_points_se1
20radius_worst1
21texture_worst1
22perimeter_worst1
23area_worst1
24smoothness_worst1
25compactness_worst1
26concavity_worst1
27concave_points_worst1
28symmetry_worst1
1
print(np.shape(chk_boruta))
1
(23, 2)
1
2
3
chk_boruta = pd.DataFrame(chk_var_boruta, columns=['feature_names', 'boruta_feature'])
chk_boruta = chk_boruta[chk_boruta["boruta_feature"]!=1]
chk_boruta
feature_namesboruta_feature
8symmetry_mean3
9fractal_dimension_mean5
11texture_se5
14smoothness_se7
18symmetry_se6
19fractal_dimension_se3
29fractal_dimension_worst2
1
This post is licensed under CC BY 4.0 by the author.

[사전학습] 3.4 데이터 변환 - 특징 생성

[사전학습] 4.1 일변량 비시각화