이번에는 Pycaret을 통해서 Training을 진행한 모델들의 Feature Importance를 구하는 과정에 대해서 포스팅을 해볼 예정이다.

 

Feature Importance란 무엇인가?? 

Feature Importance는 기본적으로, 각 모델이 Target 값을 예측하는 과정에서 각 Feature들이 Prediction에 얼마나 큰 영향을 미쳤는지를 알려주는 지표이다.

Feature Importance를 구하는 방법은 여러가지가 있는데, 가장 기본적으로 Coefficient를 구하는 방법(Linear Model & Tree Model), Permutation importance, SHAP Value가 있다.

 

Linear Model

가장 기본적인 방법으로는 Linear Model을 생각했을 때, 각 Feature에 부여되는 Weight 값이 최종 Prediction에 얼마나 큰 영향을 미치는지 알 수 있는 척도가 될 수 있다. 단순하게 생각해서, 계수가 0.001인 Feature와 계수가 1000인 Feature가 있다고 가정할 때, 한 샘플에서 두 feature의 값이 동일한 정도로 변화하면, 계수가 1000인 Feature가 예측에 훨씬 큰 영향을 미친다고 볼 수 있다.

(다만, 여기에서는 각 Feature의 값들이 Normalization되었다는 것을 기준으로 한다. Normalization을 하지 않았다면, 각 Feature마다 value들의 range가 다르기 때문에, Weight을 단순히 비교하여 Feature Importance를 구할 수는 없다. )

 

Tree Model

Tree Model의 경우에는 각 Feature가 Tree를 분할하는 데에 얼마나 큰 영향을 미치는 지에 따라서 Feature Importance를 구할 수 있다. Tree에서 데이터를 분할하는 경우(Branch로 분할하는 경우)에는 하나의 Node 내에 존재하고 있는 데이터 셋의 Target Label의 비율이 얼마나 섞여있는지를 Impurity라 한다. Impurity를 계산하는 방법으로는 Entropy, GINI 등의 방법이 있다. 여기에서는 기술적으로 Feature Importance를 구하는 방식에 대해서 설명하는 것이므로, 이것에 대한 개념은 별도로 설명하지는 않겠다. 하지만, 대부분의 Tree Model의 경우 Impurity를 계산할 때에는 Default 값으로 GINI 계수를 사용한다.

(MLP와 KNN은 모델의 구조상 Feature Importance를 구할 수 없다. )

 

 

Linear Model에서 Feature Importance를 구하는 과정

LinearRegression( )을 예로 들어서 하지만, 대부분의 Linear 모델(Tree나 Ensemble 혹은 KNN, MLP 등이 아닌 모델은 대부분 여기에 속한다고 보면된다.) 은 이 코드를 통해서 학습된 모델의 Weight을 구할 수 있다.

# Model Declaration
model = LinearRegression()
# Training 
model.fit(X_train, y_train)

# Extract Feature Weights 
feat_importance = model.coef_

(다만 Kernel Ridge나, Support Vector Machine에서 kernel을 사용하는 경우에는, Scikit-learn에서 제공하는 해당 모델에 대한 Document를 확인해봐야 한다. Non-linear kernel을 사용하는 경우에는 Coefficient를 구할 수 없는 경우가 많다.)

 

Tree-based Model에서 Feature Importance를 구하는 과정

# Model Declaration
model = DecisionTreeRessor()
# Training 
model.fit(X_train, y_train)

# Extract Feature Importance
feat_importance = model.feature_importances_.flatten()

Tree모델(Decision Tree, Boosting 알고리즘 등)은 .feature_importances_ 를 통해서 Feature Importance를 구할 수 있다.

 

 

위의 방법들을 통해서 Machine Learning Model의 80% 이상은 Feature Importance를 간단하게 구할 수 있다. 하지만, Feature Importance를 구할 수 없는 모델들을 위해서 다른 방법들도 간단히 소개하겠다.

 

Permutation Importance

Permutation Importance의 개념은 모든 Feature에서 하나의 Feature를 제거하고 학습하여 성능을 측정하였을 때 성능이 하락하는만큼 제거한 Feature가 Prediction에 중요한 영향을 끼쳤다고 보는 것이다.

따라서, Permutation Importance를 계산하는 과정에서는 모든 Feature에 대해서 한번에 하나의 Feature의 값을 Data Sample에 상관없이 섞어 놓는다. 그 후, 모든 Feature를 학습하였을 때 성능과 하나의 Feature를 섞은 데이터를 학습하였을 때의 성능을 비교하여 각 Feature의 Importance를 구한다.

이 방법의 단점은 아무래도 Feature의 개수가 많다보면 연산량이 많아져 시간이 오래 걸린다는 점이다.

추가적으로 이와 비슷하게 게임 이론에서의 Shapley Value를 Machine Learning Feature Importance에 적용한 SHAP Value가 있다.

 

 

Pycaret에서 Feature Importance 구하기 

 

이전 포스팅에서 Model을 학습하고, 저장하는 방법과, 저장한 모델을 다시 불러오는 것까지 다뤘었다.

사실, load_model( )을 사용하여 Model을 불러오면, 모델 뿐만 아니라, setup 함수를 통해서 설정한 부분들까지 전부 불러오게 되고, 이것을 Pycaret에서는 Pipeline이라고 부른다.

 

Loading Models

def load_pipelines(dataset):
    
    # Load Model
    load_ada = load_model(f'./models/{dataset}/best_model/AdaBoostRegressor')# 1
    load_ard = load_model(f'./models/{dataset}/best_model/ARDRegression')# 2
    load_br = load_model(f'./models/{dataset}/best_model/BayesianRidge')# 3
    load_dt = load_model(f'./models/{dataset}/best_model/DecisionTreeRegressor')# 4
    load_en = load_model(f'./models/{dataset}/best_model/ElasticNet')# 5
    load_et = load_model(f'./models/{dataset}/best_model/ExtraTreesRegressor')# 6
    load_gbr = load_model(f'./models/{dataset}/best_model/GradientBoostingRegressor')# 7
    load_hr = load_model(f'./models/{dataset}/best_model/HuberRegressor')# 8
    load_kr = load_model(f'./models/{dataset}/best_model/KernelRidge')# 9
    load_knn = load_model(f'./models/{dataset}/best_model/KNeighborsRegressor')# 10
    load_lars = load_model(f'./models/{dataset}/best_model/Lars')# 11
    load_lasso = load_model(f'./models/{dataset}/best_model/Lasso')# 12
    load_llar = load_model(f'./models/{dataset}/best_model/LassoLars')# 13
    load_lgbm = load_model(f'./models/{dataset}/best_model/LGBMRegressor')# 14
    load_lr = load_model(f'./models/{dataset}/best_model/LinearRegression')# 15
    load_mlp = load_model(f'./models/{dataset}/best_model/MLPRegressor')# 16
    load_omp = load_model(f'./models/{dataset}/best_model/OrthogonalMatchingPursuit')# 17
    load_par = load_model(f'./models/{dataset}/best_model/PassiveAggressiveRegressor')# 18
    load_rf = load_model(f'./models/{dataset}/best_model/RandomForestRegressor')# 19
    load_ransac = load_model(f'./models/{dataset}/best_model/RANSACRegressor')# 10
    load_ridge = load_model(f'./models/{dataset}/best_model/Ridge')# 21
    load_svr = load_model(f'./models/{dataset}/best_model/SVR')# 22
    load_xgbr = load_model(f'./models/{dataset}/best_model/XGBRegressor')# 23
    load_tr = load_model(f'./models/{dataset}/best_model/TheilSenRegressor')# 24
    load_catboost = load_model(f'./models/{dataset}/best_model/Catboost')# 25
    load_gp = load_model(f'./models/{dataset}/best_model/GaussianProcessRegressor') # 26




    model_list = [load_ada, load_ard, load_br, load_dt, load_en, load_et, load_gbr, load_hr, load_kr, load_knn, load_lars, load_lasso, load_llar, load_lgbm,
             load_lr, load_mlp, load_omp, load_par, load_rf, load_ransac, load_ridge, load_svr, load_xgbr, load_tr, load_catboost, load_gp]
    model_name_list = ['Adaboost', 'ARD','Bayesian Ridge', 'Decision Tree', 'ElasticNet', 'ExtraTreesRegressor','GradientBoostingRegressor','HuberRegressor','KernelRidge','KNeighborRegressor','Lars','Lasso',
                  'LassoLars','LGBMRegressor','LinearRegression','MLPRegressor','OrthogonalMatchingPursuit','PassiveAggressiveRegressor','RandomForestRegressor','RANSAC','Ridge','SVR','XGBRegressor', 
                  'TheilsenRegressor', 'Catboost','GaussianProcess']
    
    return model_list, model_name_list



hcp_model_list, hcp_model_name_list = load_pipelines('hcp')

이전에 학습했던 모델을 ./models/hcp/best_mode/ 이라는 디렉토리에 저장을 해놓았다면, 함수의 입력 값으로 ‘hcp’를 입력하면 해당 모델들을 전부 불러와 Pipeline 자체를 저장하고, 그에 따른 이름 List도 반환을 해주는 함수를 만들었다.

 

Feature Importance 구하기

def save_feature_importance(train, test, dataset):
    # MLP와 KNN은 제외 
    model_list, model_name_list = load_pipelines(dataset)
    
    linear_list = [1,2,4,7,10,11,12,14,16,17,20,21,23] # 13개
    tree_list = [0,3,5,6,13,18,22,24] # 8개
    
    feat_imp_dict = {}
    
    # Save Linear Model Coefficient 
    for i in linear_list:
        model = model_list[i]['trained_model']
        feat_importance = model.coef_
        feat_imp_dict[model_name_list[i]] = feat_importance
    
    # Save Tree Model feature importance 
    for i in tree_list:
        model = model_list[i]['trained_model']
        feat_importance = model.feature_importances_
        feat_imp_dict[model_name_list[i]] = feat_importance.flatten()
        
        
        
    # RANSAC ,Gaussian Process, Kernel Ridge는 일반적인 Linear, Tree Model 방식으로는 적용  X 
    # RANSAC  # 22
    load_ransac = model_list[19]['trained_model']
    feat_importance = load_ransac.estimator_.coef_
    feat_imp_dict['RANSAC'] = feat_importance.flatten()
    
    # Gaussian Process  # 23 
    gp_model = model_list[25]['trained_model']
    gp_linear_reg = LinearRegression()
    gp_x = gp_model.X_train_
    gp_y = gp_model.y_train_
    gp_linear_reg.fit(gp_x, gp_y)
    feat_importance = gp_linear_reg.coef_
    feat_imp_dict['GaussianProcess'] = feat_importance.flatten()
    
    # Kernel Ridge  # 24
    load_kr = model_list[8]['trained_model']
    kr_x = model_list[8]['trained_model'].X_fit_
    kr_y = train['age']
    
    ridge_model = Ridge() 
    ridge_model.fit(kr_x, kr_y)
    
    feat_importance= ridge_model.coef_
    feat_imp_dict['KernelRidge'] = feat_importance.flatten()
    
    # Convert To DataFrame 
    feat_imp_df = pd.DataFrame(feat_imp_dict)
    
    return feat_imp_df


hcp_feat_imp = save_feature_importance('hcp')

위에서 언급했듯이 MLP 모델과 KNN 모델은 Feature Importance를 구할 수 없으므로, 이 모델들은 제외하였다. 이외의 모델들은 Pipeline을 Loading할 때와 리스트 요소들의 순서가 동일하다는 가정하에, Linear Model과 Tree Model, 그리고 그 외 다른 방법으로 Feature Importance를 구해야 하는 모델들의 Feature Importance를 구하고, 하나의 DataFrame에 이를 저장하는 함수이다.

(Gaussian Process는 Pycaret 패키지에는 없지만, Scikit-learn에서 제공하는 함수를 이용할 수 있다.)

Kernel Ridge의 경우에는 Kernel Trick을 사용하여 X 값을 변환한 .X_fit_ 에서 Ridge를 사용하여 이에 대한 Feature Importance를 구해준다.

 

이렇게 얻은 DataFrame은 각 Column은 Model을 나타내며, Row는 Feature를 나타낸다.

이렇게 Pycaret에서 제공하는 모델들의 Feature Importance를 구할 수 있는 방법에 대해서 소개하였다. Pycaret를 사용하지 않더라도, Machine Learning을 사용하다보면 Feature Importance를 구하는 경우가 종종 있는데, 이 경우에 위의 코드를 참고해서 Feature Importance를 쉽게 구할 수 있을 것이다. 

 

728x90
반응형
  • 네이버 블러그 공유하기
  • 네이버 밴드에 공유하기
  • 페이스북 공유하기
  • 라이프코리아트위터 공유하기
  • shared
  • 카카오스토리 공유하기