본문 바로가기

Machine Learning Models/Classic

Feature Selection - Random Forest (2)

반응형

Feature Selection - Random Forest (1)


지난 포스트에서 Random Forest 에서의 Gini impurity를 기반으로 한 feature importance 계산을 알아봤습니다. 그렇다면 계산된 feature importance 를 얼마만큼 신뢰할 수 있을까요? 

Example

다음 데이터에 대해 Random Forest 분류/회귀를 수행해보도록 하겠습니다.

rent.csv
1.41MB

먼저 price 열을 RandomForestRegressor 함수로 예측한 뒤 feature importance를 보도록 하겠습니다. 이때 random 이란 열을 만들어 무작위로 값을 넣습니다. Fitting이 되더라도 random 열의 feature importance는 낮아야 합니다.

from sklearn.ensemble import RandomForestRegressor

features = ['bathrooms', 'bedrooms', 'longitude', 'latitude', 'price']
dfr = df[features]
X_train, y_train = dfr.drop('price',axis=1), dfr['price']
X_train['random'] = np.random.random(size=len(X_train))
rf = RandomForestRegressor(
         n_estimators=100,
         min_samples_leaf=1,
         n_jobs=-1)
rf.fit(X_train, y_train)

놀랍게도 예측에 전혀 상관이 없어야 할 random 열의 feature importance가 다른 feature에 비해 높습니다. 뭔가 이상합니다. 그렇다면 interest_level 열을 RandomForestClassifier 로 분류하는 모델을 만들고 feature importance 를 보도록 하겠습니다. 이번에도 마찬가지로 random 열을 만들고 랜덤한 값을 넣습니다.

features = ['bathrooms', 'bedrooms', 'price', 'longitude', 'latitude', 'interest_level']
dfc = df[features]
X_train, y_train = dfc.drop('interest_level',axis=1), dfc['interest_level']
X_train['random'] = np.random.random(size=len(X_train))
rfc = RandomForestClassifier(
         n_estimators=100,
         # better generality with 5
         min_samples_leaf=5, 
         n_jobs=-1)
         #oob_score=True)
rfc.fit(X_train, y_train)

이번에도 비슷한 결과가 나왔습니다. random feature 가 중요도가 있을 수는 있지만 bedrooms/bathrooms 변수보다 중요도가 높다는 것은 말이 안되죠.

Problem

Mean decrease in impurity (Gini impurity) 기반의 feature importance 는 기본적으로 연속형 변수나 많은 범주를 가진 (high-cardinality)를 가진 변수의 importance 를 과대평가하는 경향이 있습니다. 이는 이러한 변수들이 노드 분기의 기준이 될 기회가 많아서라고 생각할 수 있습니다. 예를 들어 분기에 따른 information gain 자체는 적더라도 이러한 변수로 노드 분기 자체가 많아지면 상대적인 importance가 증가하게 되는 것이죠. 

물론 여러 수치형 변수를 정규화하고 범주를 줄이는 과정을 통해 어느 정도 해결할 수 있겠지만 의사결정나무 기반의 모델이 이러한 전처리 없이 수행할 수 있는 간편한 모델이라는 것을 생각할 때 꽤나 아쉬운 결과입니다. 그렇다면 어떤 방식으로 해결해야 할까요?

Permutation importance

이러한 상황에서 사용할 수 있는 방법이 바로 해당 변수의 데이터를 임의로 섞는 permutation importance 입니다. 지난 포스트에서는 scikit-learn 패키지의 permutation_importance 함수를 이용했지만 다음과 같이 쉽게 구현이 가능합니다. (여기서는 baseline 성능과 permuted 성능의 차이로 구현했습니다)

def permutation_importances(rf, X_train, y_train, metric):
    baseline = metric(rf, X_train, y_train)
    imp = []
    for col in X_train.columns:
        save = X_train[col].copy()
        X_train[col] = np.random.permutation(X_train[col])
        m = metric(rf, X_train, y_train)
        X_train[col] = save
        imp.append(baseline - m)
    return np.array(imp)

또한, permutation importance는 훈련에 쓰이지 않은 데이터에 대해서 측정해야 하므로 Random Forest 를 구성하는 의사결정나무에서 쓰이지 않은 데이터 (out of bag)로 성능을 측정하고 feature importance 를 계산하면,

from sklearn.metrics import r2_score

def oob_regression_r2_score(rf, X_train, y_train):
    X = X_train.values if isinstance(X_train, pd.DataFrame) else X_train
    y = y_train.values if isinstance(y_train, pd.Series) else y_train

    n_samples = len(X)
    predictions = np.zeros(n_samples)
    n_predictions = np.zeros(n_samples)
    for tree in rf.estimators_:
        unsampled_indices = _get_unsampled_indices(tree, n_samples)
        tree_preds = tree.predict(X[unsampled_indices, :])
        predictions[unsampled_indices] += tree_preds
        n_predictions[unsampled_indices] += 1

    if (n_predictions == 0).any():
        warnings.warn("Too few trees; some variables do not have OOB scores.")
        n_predictions[n_predictions == 0] = 1

    predictions /= n_predictions

    oob_score = r2_score(y, predictions)
    return oob_score

우리가 의도했던 것처럼 random 변수가 제일 중요도가 낮습니다. Random 변수를 무작위로 섞어도 기존과 차이가 없을 것이니 당연합니다. 분류에서도 마찬가지의 결과를 얻을 수 있습니다.

def oob_classifier_accuracy(rf, X_train, y_train):
    X = X_train.values
    y = y_train.values

    n_samples = len(X)
    n_classes = len(np.unique(y))
    predictions = np.zeros((n_samples, n_classes))
    for tree in rf.estimators_:
        unsampled_indices = _get_unsampled_indices(tree, n_samples)
        tree_preds = tree.predict_proba(X[unsampled_indices, :])
        predictions[unsampled_indices] += tree_preds

    predicted_class_indexes = np.argmax(predictions, axis=1)
    predicted_classes = [rf.classes_[i] for i in predicted_class_indexes]

    oob_score = np.mean(y == predicted_classes)
    return oob_score

 

홍머스 정리

  • 연속형 이거나 범주의 개수가 많은 high-cardinality 변수일수록 mean decrease in impurity 기반의 feature importance 가 과대평가된다.
  • 특히 모델이 과적합되어 지나치게 많은 분기가 이루어질 경우 이러한 현상이 더욱 심해진다. 따라서 제대로된 feature importance를 얻기 위해서는 모델을 과적합시키지 않는 것이 중요하다.
  • scikit-learn 에서 기본적으로 제공하는 feature importance 이외에도 permutation feature importance 등의 방법을 같이 고려하여 공통적으로 높은 중요도를 가진 feature 를 선택하여야 한다.

참조

반응형