[2024.11.06] 필수 온라인 강의 Part16 Machine Learning Advanced CH04 변수 선택 방법
- 변수 선택이 가지는 의미 - 기존변수와 파생변수들 중에서 선택 - 변수가 많으면 과적합, 차원의 저주등의 문제가 생기고, 분석에서도 메모리문제, 분석 시간의 문제가 많음 - 데이터 수집단계에서도 기존변수가 많을 수록 수집에 대한 비용이 듬. - 비용을 줄이고, 학습과 추론 속도를 높이고, 과적합을 피하고 성능을 높일 수 있음
파생 변수 선택 방법 이론
Feature selection(변수 선택) - 학습에 필요한 변수를 중요도에 따라 선택하는 과정 (=모델 학습에 불필요한 변수를 생략하는 과정) → 변수의 중요도를 어떻게 정의하고, 평가할지에 따라 방법론이 나뉨
변수 선택을 시행하는 이유 - 차원의 저주 (Curse of Dimensionality) 해소 : 모델의 복잡도를 낮출 수 있음 ○ 차원의 저주 : 데이터의 차원이 학습 데이터 수보다 증가하면서 모델의 성능이 저하되는 현상 - 모델의 성능 향상 및 과적합(Overfitting) 완화 - 학습 및 추론시간, 메모리 개선 - 더 적은 변수들을 활용해 해석 가능성 증대
대표적인 변수선택의 3가지 접근법 - 변수의 중요도를 평가하는 방법, 기준에 따라 분류
Filter methods : 변수들간 통계적 관계를 기반으로 변수의 중요도 설정 - 학습의 결과가 변수 선택 과정에 영향을 미치지 않음 - 상대적으로 빠른 계산 속도
○ e.g. 상관관계, 분산 기반 방법 - 통계적 관계 = 변수 간의 상관관계, 분산 고려
- 상관관계 : 변수들 간의 상관계수를 계산해, 상관관계가 높은 변수들을 제거 - 연속형 변수들 사이에서 상관성이 높은 Feature들을 찾아서 제거
- 분산 : 분산이 낮은 변수들을 제거해 변동성이 낮은 변수 제거
- 카이제곱(Chi-square) test 기반 변수 선택 - 카이제곱 독립 검정 : 두 범주형 변수 간의 유의미한 관계가 있는지 검정하는 방법 - 귀무가설 (H0) : 두 변수들 사이에는 관계가 없다 - 대립가설 (H1) : 두 변수들 사이에는 관계가 있다 ➝ p-value가 유의하면, 귀무가설을 기각 + 대립가설을 인용(=두 변수 사이에는 관계가 있다) - 과정 1. Target variable(Y)와 각 독립변수들(X)에 대해 카이제곱 통계량을 구한다. 2. 카이제곱 통계량을 통해 p-value를 계산한다. 3. 카이제곱 통계량이 충분히 크면, p-value는 충분히 낮고 이 때 귀무가설을 기각한다 ➝ Target variable(Y)과 해당 독립변수(X)과 관계가 있다고 판단할 수 있다. - 카이제곱 검정을 이용한 변수 선택 - Target variable과 독립변수들의 관계성을 카이제곱 통계량으로 판단 - 카이제곱 통계량을 검정통계량으로 하고, p-value를 구해 유의수준으로 검정력을 판단 ➝ 최종적으로 관계가 있는 변수들만 선택
Wrapper methods : 실제 머신러닝 모델의 성능을 기반으로 변수의 중요도 설정 -모델을 반복적으로 학습시키고 검증하는 과정에서 최적의 변수 조합을 찾는 방법 ○ e.g. Forward selection, Backward elimination - 순차적 특성 선택(Sequential Feature Selection) :변수를 하나씩 추가하면서 탐색 - 전진선택(Forward Selection) - 아무런 Feature가 없는 상태에서 시작해 하나하나 유의미한 Feature를 추가해 나감 - “유의미”의 기준 ➝ 모델의 성능 및 평가지표 (성능이 상승하면 해당 Feature를 추가)
- 재귀적 특성 제거(Recursive Feature Elimination) :변수를 하나씩 제거하면서 탐색 - 후진제거(Backward Elimination) - 전체 Feature들 중에서 가장 무의미한 Feature를 제거해 나감 - 모든 feature를 가진 모델에서 시작해 모델에 도움이 되지 않는 Feature를 하나씩 줄여 나가는 방식 - “무의미”의 기준 ➝ 해당 Feature를 제거 시, 모델의 성능 하락폭이 얼마나 되는지?
Embedded methods : 모델 훈련 과정에서 변수의 중요도 설정
- 모델의 훈련 과정에서 변수의 중요도를 평가해, 이를 기반으로 모델에 대한 변수의 기여도를 결정하는 방법 ○ e.g. Feature importance, Regularizer 기반 선택
- 트리 모델 Feature importance 기반 : 트리 split 기여도에 따른 importance를 이용 - 트리 node 분할에 대해 각 feature의 기여도로 각 변수의 중요성을 판단하는 방법 - 트리 split 기준 : Gini 계수, Entropy → 학습 과정에서 Feature의 기여도를 Importance에 따라 조절
- 트리 node 분할의 기준 - 노드의 순수도를 나타내는 Gini 계수, Entropy 등을 활용 - 특정 feature가 트리의 순수도가 높은 분할에 도움이 된다면 해당 feature는 중요도가 높다고 이해
- 규제(Regularizer) 기반 : L1, L2 등의 규제를 이용해 변수의 기여도 결정 - L1, L2 등의 Regularization을 사용해 특정 Feature의 Weight를 0 또는 0으로 가깝게 만들어 Feature 제거의 효과 - L1 : Weight를 0으로 변환 - L2 & ElasticNet : Weight를 0으로 가깝게 변환 - 아래 Regularizer 항을 모델에 추가함으로써 실현
Feature selection의 실전 사례 - Correlation 기반 selection - Kaggle IEEE-CIS Fraud Detection 대회에서 일부 변수들을 상관계수(Correlation) 기반으로 제거 - 상관도가 높은 변수가 많을 때 유용 - 변수 집합 중 상관계수가 높은 하위 집합을 찾아, 가장 원소의 개수가 많은 변수를 대표 변수로 선택
- Feature importance 기반 selection - 학습된 트리 모델의 중요도를 기반으로 중요도가 낮은 변수들을 제거하는 방법 ➞ Feature importance가 낮을 수록 모델 성능 향상에 상대적으로 적은 기여를 할 것이라는 가정 ➞ 트리 모델의 성능에 따라 최적의 변수 조합을 선택
- Permutation importance 기반 selection - 검증 데이터셋의 Feature를 하나하나 shuffle하며 성능 변화를 관찰하는 방법 - Shuffle의 의미 = 해당 Feature를 Noise(의미없는 변수)로 만드는 과정 - 만약 해당 Feature가 중요한 역할을 하고있었다면, 모델 성능이 크게 하락할 것 - 검증 데이터셋 Feature의 shuffle로 인해 모델이 학습했던 분포와 크게 달라져 모델의 예측 성능이 하락 ➞ 해당 Feature가 상대적으로 모델 성능에 기여를 많이 했을수록 성능 하락폭이 큼 - 적용 과정 예시 1. 먼저 기존의 학습 데이터셋을 이용해 학습된 모델로 검증 데이터셋에 대한 예측값을 산출한 후, Target과 비교해 원본 평균 성능을 산출 2. 검증 데이터셋의 특정 변수들을 무작위로 Permutation을 통해 Noise로 작용하게 만들고, 이에 따른 예측값을 산출해 Target과 비교 후 Permutation 이후 성능을 산출 3. Permutation 전 원본 성능과 Permutation 후 성능의 차이를 통해 변수의 중요도를 판단 ➞ 성능 차이가 클수록 모델이 해당 Feature의 Permutation에 영향을 많이 받은 것을 의미 ➞ 해당 변수가 상대적으로 중요하다고 해석 가능 - 해석 -Permutation으로 인해 random성이 존재하므로, N번 반복해 중요도를 산출 - N번 반복에 따른 평균 ± 분산으로 표시 - Permutation 성능 감소량이 양수의 값을 가지는 변수들은 상대적으로 중요 - Permutation이 모델에 상대적으로 큰 영향을 미친다는 의미 - 주의할 점 - Feature 개수가 많다면 효율적이지 못할 가능성 존재 - Random permutation에 의존하기 때문에, 실행마다 Feature importance 결과가 상이 할 수 있는 문제
- Target permutation - Shuffle된 Target 변수 모델을 학습시킨 후, Feature importance와 Actual feature importance를 비교해 변수를 선택 - 진행 과정 1) Null importance 도출: Target 변수를 여러번 임의로 Shuffle해 모델 학습 후 Null importance의 분포 도출 → 목적변수와 관련 없는 변수를 모델이 어떻게 이해하는지를 보여줌 2) Original importance 도출: 원래 데이터셋에서 Importance 도출 3) 1)과 2)에서 구한 Importance를 비교해 실제로 중요한 변수를 선택 가능 - 장점 - Feature들끼리의 상호작용을 고려 가능 - 높은 분산을 가지거나, Target 변수와 관련 없는 변수들을 쉽게 도출 가능 - 변수 선택 → Scoring 1) Actual importance와 Null importance distribution이 떨어진 표본수 계산 2) Actual/Null Max, Mean 등의 비율을 계산해 선택
- Adversarial validation - 데이터셋과 검증 데이터셋이 얼마나 유사한지 판단하는 방법 - 학습 데이터셋의 Target 값은 1로, 검증 데이터셋은 0으로 지정 후 Binary classification 모델링 - 만약 학습 데이터셋과 검증 데이터셋의 경향이 비슷하다면, 위 모델의 분류 성능은 낮을 것 - 만약 학습 데이터셋과 검증 데이터셋의 경향이 다르다면, 위 모델의 분류 성능은 높을 것 → 이 모델의 분류에 도움이 되는 Feature는 과적합을 유발할 수 있으므로 제거하는 방식 - 진행 과정 : 학습과 검증 데이터셋에서 상이한 분포를 갖는 변수를 제거하는 방법 1) 학습 데이터셋은 0, 검증 데이터셋은 1로 변수 설정 2) 학습 + 검증의 데이터셋을 무작위로 분할 3) 위 2) 데이터로 1)의 Target을 분류 하도록 모델 학습 4) 3)의 모델 결과를 AUC나 Feature importance를 해석 - 실전 사례 - AUC가 높은 변수의 경우, 학습과 검증 데이터셋이 다른 분포를 가진 변수들이므로 성능에 악영향을 끼칠 가능성 존재
파생 변수 선택 방법 실습
데이터 준비 - Yahoo Finance OHLCV 데이터셋 이용
실습 데이터 구성하기
# 데이터셋 파일 위치로 경로 수정
os.chdir('데이터셋 파일이 있는 폴더 경로')
# OHLCV 데이터셋 불러오기
OHLCV_file = "OHLCV.parquet"
OHLCV_data = pd.read_parquet(OHLCV_file)
# 데이터가 방대하니 이번 실습에서는 2020-01-01 ~ 2023-07-31의 데이터만 활용하겠습니다.
# 날짜는 데이터셋의 index로 되어 있습니다.
# .loc을 통해서 날짜 범위를 지정해줍시다.
OHLCV_data = OHLCV_data.loc["2020-01-01":"2023-07-31"]
# 상장법인정보 파일도 불러오도록 하겠습니다.
company_file = "Company.parquet"
company_data = pd.read_parquet(company_file)
# OHLCV은 종목별 주가와 관련된 정보를 나타내는 데이터셋입니다.
display(OHLCV_data.head(10))
# 상장법인정보 데이터셋입니다.
display(company_data.head(10))
- 파생변수 생성
# 현재 데이터프레임의 인덱스가 날짜로 되어있습니다.
# 분석 편의를 위해서 Date컬럼을 새로 만들고, 인덱스는 새로 초기화 하겠습니다.
OHLCV_data["Date"] = OHLCV_data.index
OHLCV_data["Date"] = pd.to_datetime(OHLCV_data["Date"])
OHLCV_data.reset_index(drop=True, inplace=True)
# 두 파일을 결합시켜 OHCLV와 상장법인정보를 함께 볼수 있도록 하겠습니다.
OHLCV_data = pd.merge(OHLCV_data, company_data, on="code", how="inner")
OHLCV_data["Target"] = OHLCV_data.groupby("code")["Close"].shift(-1)
# 파생 변수1 - 가격 차이를 구합시다.
OHLCV_data["PriceRange"] = OHLCV_data["High"] - OHLCV_data["Low"]
# 파생 변수2 - 종목의 대푯값인 평균 가격를 구합시다.
OHLCV_data["AveragePrice"] = (OHLCV_data["Open"] + OHLCV_data["High"] + OHLCV_data["Low"] - OHLCV_data["Close"]) / 4
# 파생 변수3 - 종가가 시가보다 크면 주가의 방향은 양(1)으로, 반대로 종가가 시가보다 작으면 음(-1)을 표현하도록 하겠습니다.
OHLCV_data['PriceDirection'] = OHLCV_data.apply(lambda row: 1 if row['Close'] > row['Open'] else -1, axis=1)
# 파생 변수4 - 일일 수익률을 계산합니다.
OHLCV_data['DailyReturn'] = OHLCV_data.groupby('code')['Close'].transform(lambda x: x.pct_change() * 100)
# 파생 변수5 - 장기, 중기, 단기 이동평균을 파생변수로 추가합니다.
OHLCV_data["MovingAverage5d"] = OHLCV_data.groupby("code")["Close"].transform(lambda x: x.rolling(window=5).mean())
OHLCV_data["MovingAverage20d"] = OHLCV_data.groupby("code")["Close"].transform(lambda x: x.rolling(window=20).mean())
OHLCV_data["MovingAverage60d"] = OHLCV_data.groupby("code")["Close"].transform(lambda x: x.rolling(window=60).mean())
OHLCV_data["MovingAverage120d"] = OHLCV_data.groupby("code")["Close"].transform(lambda x: x.rolling(window=120).mean())
# 파생 변수6 - 볼린저 밴드(Bolinger bands)는 이동 평균을 기준으로 상한, 하한 밴드를 구성하여 추세와 변동성을 파악하는 기술입니다.
# 표준편차에 대한 상한 및 하한 밴드의 배수 설정
std_multiplier = 2
std = OHLCV_data.groupby("code")["Close"].transform(lambda x: x.rolling(window=20).std())
# 상한은 일반적으로 20일 이동평균에 2배의 표준편차 더하기
# 하한은 일반적으로 20일 이동평균에 2배의 표준편차 빼기
OHLCV_data["LowerBollingerBand"] = OHLCV_data["MovingAverage20d"] + (std_multiplier * std)
OHLCV_data["UpperBollingerBand"] = OHLCV_data["MovingAverage20d"] - (std_multiplier * std)
# 파생 변수7 - MACD (Moving Average Convergence & Divergence)는 추가 추세의 힘과 방향성을 파악하여 매매신호를 잡는 목적으로 사용됩니다.
OHLCV_data['ShortEMA'] = OHLCV_data["Close"].ewm(span=12, adjust=False).mean()
OHLCV_data['LongEMA'] = OHLCV_data["Close"].ewm(span=26, adjust=False).mean()
OHLCV_data['MACD'] = OHLCV_data['ShortEMA'] - OHLCV_data['LongEMA']
# 위와 같이 직접 구현하지 않고, 패키지 함수를 이용해 바로 진행
sfs = SFS(DecisionTreeRegressor(max_depth=10, random_state=1214, min_samples_split=100), # 어떤 모델이든 들어갈 수 있습니다. 여기서는 기준 모델로 DecisionTree를 사용하겠습니다.
forward=True, # forward = True이면 forward selection을 뜻합니다.
floating=False,
scoring="neg_root_mean_squared_error", # 기준이 될 성능 지표를 결정 여기서는 RMSE를 이용
cv=0,
n_jobs=-1)
display(sfs)
# 위에서 정의한 feature selection method를 학습
# 변수 선택 과정에서 모든 변수를 고려하기 때문에, feature의 개수에 따라 시간이 소요
sfs.fit(X_train, Y_train)
display(sfs.k_feature_names_) # 선택된 feature를 반환
# 위에서 선택된 feature들로 모델 학습 후 결과를 관찰
# 의사결정트리 분류기를 새로 초기화하고 중요한 특성만 사용하여 훈련
TP_X_train = X_train[['Close']]
TP_X_test = X_valid[['Close']]
start_time = time.time()
dt2 = DecisionTreeRegressor(max_depth=10, random_state=1214,min_samples_split=100)
dt2.fit(TP_X_train, Y_train)
y_pred = dt2.predict(TP_X_test)
end_time = time.time()
elapsed_time = end_time - start_time
print(f"변수 선택 후 코드 실행 시간: {elapsed_time} 초")
y_true = Y_valid
# 제곱근 평균 제곱 오차 (RMSE) 계산
rmse = mean_squared_error(y_true, y_pred, squared=False)
print("변수 선택 후 RMSE:", rmse)
Feature importance 기반 selection
# 먼저 LGBM 모델을 학습
gbm = lgb.LGBMRegressor(n_estimators=100000, # early stopping을 적용하기에 적당히 많은 반복 횟수를 지정
metric="rmse",
data_sample_strategy='goss', # sampling 방법을 goss로 적용
max_depth=12, # default값인 20에서 12로 변경
num_leaves=62, # default값인 31에서 62으로 변경
min_data_in_leaf=40 # default값인 20에서 40으로 변경
)
start_time = time.time()
gbm.fit(X_train, Y_train,
eval_set=[(X_train, Y_train), (X_valid, Y_valid)],
eval_metric ='rmse',
categorical_feature="auto",
callbacks=[lgb.early_stopping(stopping_rounds=50), # early stopping을 적용. 50번동안 metirc의 개선이 없다면 학습을 중단.
lgb.log_evaluation(period=10, show_stdv=True)] # 10번의 반복마다 평가점수를 로그에 나타냄.
)
y_pred = gbm.predict(X_valid)
end_time = time.time()
elapsed_time = end_time - start_time
print(f"변수 선택 전 코드 실행 시간: {elapsed_time} 초")
# 제곱근 평균 제곱 오차 (RMSE) 계산
y_true = Y_valid
rmse = mean_squared_error(y_true, y_pred, squared=False)
print("변수 선택 전 RMSE:", rmse)
# 위 importance를 이용해 importance가 100 이상인 변수들만 학습에 이용
sfm = SelectFromModel(gbm, threshold=100, prefit = True) # importance를 선정할 threshold를 설정
sfm.fit(X_train, Y_train) # 훈련 데이터를 사용하여 중요한 특성을 선택
X_important_train = sfm.transform(X_train)
X_important_valid = sfm.transform(X_valid)
start_time = time.time()
gbm = lgb.LGBMRegressor(n_estimators=100000, # early stopping을 적용하기에 적당히 많은 반복 횟수를 지정.
metric="rmse",
data_sample_strategy='goss', # sampling 방법을 goss로 적용
max_depth=12, # default값인 20에서 12로 변경
num_leaves=62, # default값인 31에서 62으로 변경
min_data_in_leaf=40 # default값인 20에서 40으로 변경
)
# 선택된 feature들로 재학습하겠습니다.
gbm.fit(X_important_train, Y_train,
eval_set=[(X_important_train, Y_train), (X_important_valid, Y_valid)],
eval_metric ='rmse',
categorical_feature="auto",
callbacks=[lgb.early_stopping(stopping_rounds=50), # early stopping을 적용. 50번동안 metirc의 개선이 없다면 학습을 중단.
lgb.log_evaluation(period=10, show_stdv=True)] # 10번의 반복마다 평가점수를 로그에 나타냄.
)
y_pred = gbm.predict(X_important_valid)
end_time = time.time()
elapsed_time = end_time - start_time
print(f"변수 선택 후 코드 실행 시간: {elapsed_time} 초")
y_true = Y_valid
# 제곱근 평균 제곱 오차 (RMSE) 계산
rmse = mean_squared_error(y_true, y_pred, squared=False)
print("변수 선택 후 RMSE:", rmse)
X_important_train.shape # 11개의 변수만 선택된 것을 확인할 수 있습니다.
- 선택된 11개의 feature('Close', 'Change', 'MACD', 'DateMonth', 'High', 'VolumeMean', 'Low', 'DateDay', 'Volume', 'PriceRange', 'AveragePrice') 들로 DecisionTreeRegressor도 학습
# 의사결정트리 분류기를 새로 초기화하고 위에서 선택된 중요한 특성만 사용하여 훈련.
FI_X_train = X_train[['Close', 'Change', 'MACD', 'DateMonth', 'High', 'VolumeMean', 'Low', 'DateDay', 'Volume', 'PriceRange', 'AveragePrice']]
FI_X_test = X_valid[['Close', 'Change', 'MACD', 'DateMonth', 'High', 'VolumeMean', 'Low', 'DateDay', 'Volume', 'PriceRange', 'AveragePrice']]
start_time = time.time()
dt = DecisionTreeRegressor(max_depth=10, random_state=1214,min_samples_split=100)
dt.fit(FI_X_train, Y_train)
y_pred = dt.predict(FI_X_test)
end_time = time.time()
elapsed_time = end_time - start_time
print(f"변수 선택 후 코드 실행 시간: {elapsed_time} 초")
y_true = Y_valid
# 제곱근 평균 제곱 오차 (RMSE) 계산
rmse = mean_squared_error(y_true, y_pred, squared=False)
print("변수 선택 후 RMSE:", rmse)
Permutation importance 기반 selection
# Permutation importance를 위 패키지를 활용
# 의사결정나무 모델을 이용합니다.
dt = DecisionTreeRegressor(max_depth=10, # 트리의 깊이를 규제.
random_state=1214, # 트리의 랜덤시드를 설정.
min_samples_split=100) # 해당하는 샘플이 100개 이상이면 split.
dt.fit(X_train, Y_train)
perm = PermutationImportance(dt,
scoring = "neg_mean_squared_error", # 평가 지표로는 회귀문제이기에 negative rmse를 사용. (neg_mean_squared_error : 음의 평균 제곱 오차)
random_state = 42,
n_iter=5).fit(X_valid, Y_valid)
eli5.show_weights(perm, feature_names = X_valid.columns.tolist()) # valid data에 대해 적합.
# Original model의 importance를 도출
# Decision tree를 이용해 train/test data를 분류하도록 학습
dt = DecisionTreeRegressor(max_depth=10, # 트리의 깊이를 규제.
random_state=1214, # 트리의 랜덤시드를 설정.
min_samples_split=100) # 해당하는 샘플이 100개 이상이면 split.
dt.fit(X_train, Y_train)
original_FI = dt.feature_importances_
# original feature importance
ser = pd.Series(original_FI, index=list(X_train.columns))
# 이제 target 변수를 임의로 permutation 해서, feature importance의 분포를 도출.
ITER = 20 # 20번 반복
new_FIs = []
for _ in tqdm(range(ITER)) :
new_y_train = np.random.permutation(Y_train)
# 앞선 트리모델을 그대로 이용
dt = DecisionTreeRegressor(
max_depth=10, # 트리의 깊이를 규제.
random_state=1214, # 트리의 랜덤시드를 설정.
min_samples_split=100) # 해당하는 샘플이 100개 이상이면 split.
# 셔플된 새로운 타겟 변수로 모델을 적합.
dt.fit(X_train, new_y_train)
new_FIs.append(dt.feature_importances_) # 새로운 feautre importance들을 수집
np.array(new_FIs).shape # 셔플된 피쳐별 모음
# 변수별로 null importance plot.
new_FIs = np.array(new_FIs)
for i in range(len(list(X_train.columns))) :
ori = original_FI[i]
new = new_FIs[:, i]
cur_feat = list(X_train.columns)[i]
plt.figure(figsize=(6, 4))
plt.vlines(x=ori, color='hotpink', ymin=0, ymax=50, linewidth=10, label='Original importance', alpha=0.4)
plt.hist(new, color='lightgreen', label='Importances from Null distribution')
plt.title('Importance of ' + cur_feat + ' variable')
plt.legend()
plt.show()
# 의사결정트리 분류기를 새로 초기화하고 중요한 특성만 사용하여 훈련.
TP_X_train = X_train[['Close', 'MACD', 'PriceRange', 'Volume', 'VolumeMean']]
TP_X_test = X_valid[['Close', 'MACD', 'PriceRange', 'Volume', 'VolumeMean']]
start_time = time.time()
dt2 = DecisionTreeRegressor(max_depth=10, random_state=1214,min_samples_split=100)
dt2.fit(TP_X_train, Y_train)
y_pred = dt2.predict(TP_X_test)
end_time = time.time()
elapsed_time = end_time - start_time
print(f"변수 선택 후 코드 실행 시간: {elapsed_time} 초")
y_true = Y_valid
# 제곱근 평균 제곱 오차 (RMSE) 계산
rmse = mean_squared_error(y_true, y_pred, squared=False)
print("변수 선택 후 RMSE:", rmse)
- Adversarial Validation : Train data와 Test data가 얼마나 유사한지 판단하는 방법
# Train은 0, Test는 1로 라벨 변수를 설정.
X_train['AV_label'] = 0
X_valid['AV_label'] = 1
# 위 두 데이터를 합치고, 셔플.
all_data = pd.concat([X_train, X_valid], axis=0, ignore_index=True)
all_data_shuffled = all_data.sample(frac=1)
X = all_data_shuffled.drop(['AV_label', 'DateYear', 'DateMonth'], axis=1) # Date 관련 변수도 제외해 주는 것을 기억하세요!
y = all_data_shuffled['AV_label']
X.columns # 아래와 같은 column을 이용.
# Decision tree를 이용해 Train/Test data를 분류하도록 학습.
from sklearn.tree import DecisionTreeClassifier
dt_adv = DecisionTreeClassifier(random_state=0)
dt_adv.fit(X, y)
ytrainpredict_rf = dt_adv.predict(X)
# 위 feature importance를 시각화
ser = pd.Series(dt_adv.feature_importances_, index=list(X.columns))
top15 = ser.sort_values(ascending=False)
plt.figure(figsize=(8,6))
plt.title("Feature Importances in Adversarial Validation process")
sns.barplot(x=top15, y=top15.index)
plt.show()
- 위에서 선택된 변수들을 제거하고, 모델을 학습 : 모델은 DecisionTree를 이용
# 위 train/test 분류의 importance가 0.003 이하인 변수들만 가지고 학습
# 이 변수들은 그나마 train과 test 분류에 기여하지 못하는(=train, test 분포가 비슷한) 변수들이라고 해석.
av_choosed = pd.DataFrame(ser, columns=['imp']).query('imp<0.003').reset_index()
av_choosed
# 의사결정트리 분류기를 새로 초기화하고 중요한 특성만 사용하여 훈련
TP_X_train = X_train[list(av_choosed['index'])]
TP_X_test = X_valid[list(av_choosed['index'])]
start_time = time.time()
dt2 = DecisionTreeRegressor(max_depth=10, random_state=1214,min_samples_split=100)
dt2.fit(TP_X_train, Y_train)
y_pred = dt2.predict(TP_X_test)
end_time = time.time()
elapsed_time = end_time - start_time
print(f"변수 선택 후 코드 실행 시간: {elapsed_time} 초")
y_true = Y_valid
# 제곱근 평균 제곱 오차 (RMSE) 계산
rmse = mean_squared_error(y_true, y_pred, squared=False)
print("변수 선택 후 RMSE:", rmse)