콘텐츠로 이동
Data Prep
상세

Feature Engineering (피처 엔지니어링)

메타 정보

항목 내용
분류 Data Preprocessing / Feature Construction / Feature Selection
핵심 논문 "Deep Feature Synthesis" (Kanter & Veeramachaneni, IEEE DSAA 2015), "ExploreKit: Automatic Feature Generation and Selection" (Katz et al., IEEE ICDM 2016), "OpenFE: Automated Feature Generation beyond Expert-level Performance" (Zhang et al., ICML 2024), "CAAFE: Context-Aware Automated Feature Engineering" (Hollmann et al., NeurIPS 2023), "LLM-FE: Automated Feature Engineering with LLMs as Evolutionary Optimizers" (arXiv 2025), "Shap-Select: Lightweight Feature Selection Using SHAP Values and Regression" (arXiv 2024)
주요 저자 James Max Kanter (Featuretools/DFS), Gilad Katz (ExploreKit), Tianping Zhang (OpenFE), Noah Hollmann & Frank Hutter (CAAFE)
핵심 개념 원시 데이터로부터 머신러닝 모델의 예측 성능을 극대화하는 입력 표현(feature)을 설계, 생성, 선택하는 과정
관련 분야 AutoML, Data-Centric AI, Tabular Learning, Transfer Learning, Dimensionality Reduction

정의

Feature Engineering은 원시 데이터를 머신러닝 모델이 효과적으로 학습할 수 있는 수치적 표현으로 변환하는 전 과정을 포괄한다. 도메인 지식 기반의 수동 피처 설계부터, 자동 피처 생성(Automated Feature Generation), 피처 선택(Feature Selection), 피처 변환(Feature Transformation)까지를 포함한다. "Applied machine learning is basically feature engineering" (Andrew Ng)이라는 격언이 보여주듯, 모델 성능의 상당 부분은 피처의 품질에 의해 결정된다.

Feature Engineering 파이프라인:

Raw Data (정형/비정형)
  |
  v
[1] Feature Extraction (피처 추출)
  - 텍스트: TF-IDF, embeddings
  - 이미지: CNN features
  - 시계열: lag, rolling stats
  |
  v
[2] Feature Construction (피처 생성)
  - 도메인 기반: 비율, 차이, 교차
  - 자동화: DFS, OpenFE, CAAFE
  - 수학 연산: log, sqrt, polynomial
  |
  v
[3] Feature Transformation (피처 변환)
  - 스케일링: StandardScaler, MinMax
  - 인코딩: One-Hot, Target Encoding
  - 차원축소: PCA, t-SNE, UMAP
  |
  v
[4] Feature Selection (피처 선택)
  - Filter: 상관계수, MI, chi-squared
  - Wrapper: RFE, Boruta
  - Embedded: L1/L2, tree importance
  |
  v
Final Feature Set --> Model Training

핵심 원리

1. Feature Construction (피처 생성)

도메인 지식 또는 자동 탐색을 통해 원시 피처로부터 새로운 피처를 생성한다.

import pandas as pd
import numpy as np
from sklearn.preprocessing import PolynomialFeatures

# === 수동 피처 생성 (도메인 지식 기반) ===

def create_domain_features(df: pd.DataFrame) -> pd.DataFrame:
    """부동산 데이터 예시: 도메인 지식 기반 피처 생성"""
    df = df.copy()

    # 비율 피처 (ratio)
    df['price_per_sqm'] = df['price'] / df['area_sqm']
    df['room_density'] = df['rooms'] / df['area_sqm']

    # 차이 피처 (difference)
    df['price_gap'] = df['asking_price'] - df['appraised_value']

    # 교차 피처 (interaction)
    df['floor_area_interaction'] = df['floor'] * df['area_sqm']

    # 집계 피처 (aggregation)
    df['district_avg_price'] = df.groupby('district')['price'].transform('mean')
    df['price_vs_district'] = df['price'] / df['district_avg_price']

    # 시간 피처 (temporal)
    df['sale_month'] = pd.to_datetime(df['sale_date']).dt.month
    df['days_on_market'] = (
        pd.to_datetime(df['sale_date']) - pd.to_datetime(df['list_date'])
    ).dt.days

    # 비선형 변환
    df['log_price'] = np.log1p(df['price'])
    df['area_squared'] = df['area_sqm'] ** 2

    return df


# === 다항 피처 생성 ===

def polynomial_features(X: np.ndarray, degree: int = 2) -> np.ndarray:
    """
    PolynomialFeatures: 모든 피처 조합의 다항식 생성
    주의: degree=2에서 n개 피처 -> n*(n+1)/2 + n + 1개로 폭발
    """
    poly = PolynomialFeatures(
        degree=degree,
        interaction_only=False,  # True면 교차항만
        include_bias=False
    )
    return poly.fit_transform(X)

2. Feature Encoding (범주형 피처 인코딩)

범주형 변수를 수치형으로 변환하는 다양한 전략이 존재하며, 각각의 trade-off가 있다.

방법 차원 증가 순서 보존 타겟 누수 위험 적합한 경우
One-Hot Encoding O(k) X 없음 저카디널리티 (k < 20)
Label Encoding 없음 O 없음 트리 모델
Target Encoding 없음 X 있음 (CV 필요) 고카디널리티
Frequency Encoding 없음 X 낮음 빈도가 의미 있을 때
Binary Encoding O(log k) X 없음 중간 카디널리티
WoE (Weight of Evidence) 없음 X 있음 이진 분류, 신용평가
CatBoost Encoding 없음 X 낮음 (순서 기반) CatBoost 내장
import category_encoders as ce
from sklearn.model_selection import KFold

def target_encoding_with_cv(
    train: pd.DataFrame,
    target: str,
    cat_cols: list,
    n_folds: int = 5,
    smoothing: float = 10.0
) -> pd.DataFrame:
    """
    Target Encoding with K-Fold CV (타겟 누수 방지)

    핵심: 학습 데이터에서 fold-out 방식으로 인코딩하여
          타겟 정보가 직접 피처에 누출되는 것을 방지

    smoothing: 글로벌 평균과의 혼합 비율
      encoded = (count * cat_mean + smoothing * global_mean) / (count + smoothing)
    """
    train = train.copy()
    kf = KFold(n_splits=n_folds, shuffle=True, random_state=42)

    for col in cat_cols:
        train[f'{col}_target_enc'] = np.nan

        for train_idx, val_idx in kf.split(train):
            # fold 내 학습 데이터로 인코딩 매핑 생성
            fold_train = train.iloc[train_idx]
            global_mean = fold_train[target].mean()

            agg = fold_train.groupby(col)[target].agg(['mean', 'count'])
            agg['smoothed'] = (
                (agg['count'] * agg['mean'] + smoothing * global_mean) 
                / (agg['count'] + smoothing)
            )
            mapping = agg['smoothed'].to_dict()

            # fold 외 검증 데이터에 적용
            train.loc[
                train.index[val_idx], f'{col}_target_enc'
            ] = train.iloc[val_idx][col].map(mapping).fillna(global_mean)

    return train

3. Feature Selection (피처 선택)

불필요한 피처를 제거하여 과적합을 방지하고 모델 해석성을 높인다. 세 가지 접근법이 존재한다.

from sklearn.feature_selection import (
    mutual_info_classif, SelectKBest, RFE
)
from sklearn.ensemble import RandomForestClassifier
import shap

# === Filter Method: 통계량 기반 ===

def filter_selection(X: pd.DataFrame, y: pd.Series, k: int = 20):
    """
    Filter: 모델 독립적, 빠르지만 피처 간 상호작용 무시
    - 상관계수: 선형 관계만 포착
    - Mutual Information: 비선형 관계도 포착
    - Chi-squared: 범주형 피처 전용
    """
    # Mutual Information
    mi_scores = mutual_info_classif(X, y, random_state=42)
    mi_ranking = pd.Series(mi_scores, index=X.columns).sort_values(ascending=False)

    # 다중공선성 제거 (상관계수 기반)
    corr_matrix = X.corr().abs()
    upper = corr_matrix.where(
        np.triu(np.ones(corr_matrix.shape), k=1).astype(bool)
    )
    # 상관계수 > 0.95인 피처 중 MI 점수가 낮은 쪽 제거
    to_drop = []
    for col in upper.columns:
        high_corr = upper.index[upper[col] > 0.95].tolist()
        for hc in high_corr:
            if mi_ranking[col] >= mi_ranking[hc]:
                to_drop.append(hc)
            else:
                to_drop.append(col)

    return mi_ranking.head(k).index.tolist(), list(set(to_drop))


# === Wrapper Method: Boruta ===

def boruta_selection(X: pd.DataFrame, y: pd.Series):
    """
    Boruta: Random Forest 기반 Wrapper Method
    - Shadow features (원본 피처 셔플)와 비교하여 통계적 유의성 검정
    - 모든 관련 피처를 찾는 all-relevant 방식 (vs. RFE의 minimal-optimal)
    """
    from boruta import BorutaPy

    rf = RandomForestClassifier(
        n_estimators=200,
        n_jobs=-1,
        random_state=42,
        max_depth=7
    )
    boruta = BorutaPy(
        estimator=rf,
        n_estimators='auto',
        max_iter=100,
        random_state=42
    )
    boruta.fit(X.values, y.values)

    selected = X.columns[boruta.support_].tolist()
    tentative = X.columns[boruta.support_weak_].tolist()

    return {
        'confirmed': selected,
        'tentative': tentative,
        'ranking': dict(zip(X.columns, boruta.ranking_))
    }


# === Embedded Method: SHAP 기반 ===

def shap_feature_importance(
    model, X: pd.DataFrame, top_k: int = 20
) -> pd.DataFrame:
    """
    SHAP (SHapley Additive exPlanations) 기반 피처 중요도
    - 게임이론의 Shapley value를 ML에 적용
    - 각 피처의 한계 기여도를 모든 피처 조합에 대해 계산
    - 모델 종류별 최적화: TreeExplainer, KernelExplainer 등
    """
    explainer = shap.TreeExplainer(model)
    shap_values = explainer.shap_values(X)

    # 다중 클래스인 경우 평균
    if isinstance(shap_values, list):
        importance = np.mean([np.abs(sv).mean(axis=0) for sv in shap_values], axis=0)
    else:
        importance = np.abs(shap_values).mean(axis=0)

    result = pd.DataFrame({
        'feature': X.columns,
        'shap_importance': importance
    }).sort_values('shap_importance', ascending=False)

    return result.head(top_k)

4. Automated Feature Engineering (자동 피처 엔지니어링)

수동 피처 설계의 한계를 극복하기 위한 자동화 기법들.

4.1 Deep Feature Synthesis (DFS)

import featuretools as ft

def deep_feature_synthesis_example():
    """
    Deep Feature Synthesis (Kanter & Veeramachaneni, 2015)

    핵심 아이디어:
    - 관계형 데이터의 엔티티 간 관계를 따라 피처를 자동 생성
    - 변환 프리미티브(transform)와 집계 프리미티브(agg)의 조합
    - 깊이(depth)를 늘리면 복합 피처 생성 (e.g., MEAN(SUM(amount)))

    프리미티브 예시:
      Transform: abs, log, sqrt, year, month, weekday, is_weekend
      Aggregation: mean, sum, count, std, max, min, mode, trend
    """
    # 엔티티셋 생성 (관계형 데이터)
    es = ft.EntitySet(id="transactions")

    es = es.add_dataframe(
        dataframe_name="customers",
        dataframe=customers_df,
        index="customer_id"
    )
    es = es.add_dataframe(
        dataframe_name="transactions",
        dataframe=transactions_df,
        index="transaction_id",
        time_index="transaction_time"
    )
    es = es.add_relationship("customers", "customer_id",
                              "transactions", "customer_id")

    # DFS 실행
    feature_matrix, feature_defs = ft.dfs(
        entityset=es,
        target_dataframe_name="customers",
        max_depth=2,                    # 피처 합성 깊이
        agg_primitives=["mean", "sum", "count", "std", "max", "min"],
        trans_primitives=["month", "weekday", "is_weekend"],
        n_jobs=-1
    )
    # 결과: MEAN(transactions.amount), STD(transactions.amount),
    #       COUNT(transactions), MEAN(transactions.MONTH(transaction_time)), ...

    return feature_matrix, feature_defs

4.2 OpenFE (ICML 2024)

from openfe import OpenFE, transform

def openfe_example(X_train, y_train, X_test):
    """
    OpenFE (Zhang et al., ICML 2024)

    핵심 혁신:
    1. Feature Boosting: LightGBM의 잔차(residual)에 대해
       후보 피처의 증분 성능을 효율적으로 평가
    2. Two-stage Pruning: 
       Stage 1: 빠른 통계 기반 필터링 (후보 수 대폭 감소)
       Stage 2: 실제 모델 성능 기반 정밀 평가

    장점:
    - 전문가 수준의 피처 생성 자동화
    - 49개 OpenML 벤치마크에서 기존 AutoFE 기법 대비 우수
    - 시간 효율성 (ExploreKit 대비 10-100x 빠름)
    """
    ofe = OpenFE()
    features = ofe.fit(
        data=X_train,
        label=y_train,
        n_jobs=8,
        n_data_for_train=10000,     # 평가용 서브샘플
        task='classification',       # or 'regression'
        feature_boosting=True,       # 핵심: 증분 성능 평가
        stage2_params={'verbose': -1}
    )

    # 상위 피처 적용
    X_train_new, X_test_new = transform(
        X_train, X_test,
        features[:20],              # 상위 20개 피처
        n_jobs=8
    )

    return X_train_new, X_test_new


# OpenFE 내부 동작 원리 (간략화)
def feature_boosting_concept(X, y, candidate_feature):
    """
    Feature Boosting 핵심 아이디어:

    1. 기존 피처로 LightGBM 학습 -> 잔차(residual) 계산
    2. 후보 피처 하나만으로 잔차를 예측하는 단일 트리 학습
    3. 잔차 예측 성능 = 해당 피처의 증분 기여도

    이 방식으로 각 후보를 독립적으로 평가하여
    O(n * k) 대신 O(n + k) 수준으로 효율화
    (n: 기존 피처 수, k: 후보 피처 수)
    """
    import lightgbm as lgb

    # Step 1: 기존 모델로 잔차 계산
    base_model = lgb.LGBMClassifier().fit(X, y)
    base_pred = base_model.predict_proba(X)[:, 1]
    residual = y - base_pred

    # Step 2: 후보 피처 하나로 잔차 예측
    single_tree = lgb.LGBMRegressor(
        n_estimators=1, max_depth=3
    ).fit(candidate_feature.values.reshape(-1, 1), residual)

    # Step 3: 잔차 예측 성능 = 증분 기여도
    score = single_tree.score(
        candidate_feature.values.reshape(-1, 1), residual
    )
    return score

4.3 CAAFE: LLM 기반 피처 엔지니어링 (NeurIPS 2023)

from caafe import CAAFEClassifier

def caafe_example(X_train, y_train, X_test, dataset_description: str):
    """
    CAAFE (Hollmann et al., NeurIPS 2023)

    핵심 혁신:
    - LLM(GPT-4 등)에 데이터셋 설명과 피처 정보를 제공
    - LLM이 도메인 지식을 활용하여 의미론적으로 유의미한 피처를 생성
    - 반복적 피드백: 생성된 피처의 성능을 LLM에 전달하여 개선

    장점:
    - 도메인 지식이 없어도 의미 있는 피처 생성 가능
    - 사람이 해석 가능한 피처 (코드 + 설명 제공)
    - 기존 AutoFE 대비 적은 후보로 높은 성능
    """
    clf = CAAFEClassifier(
        base_classifier="lightgbm",
        llm_model="gpt-4",
        iterations=10,              # LLM 반복 횟수
        dataset_description=dataset_description
    )
    clf.fit(X_train, y_train)
    predictions = clf.predict(X_test)

    # 생성된 피처 확인
    for feat in clf.generated_features_:
        print(f"Name: {feat['name']}")
        print(f"Code: {feat['code']}")
        print(f"Explanation: {feat['explanation']}")

    return predictions

주요 방법론 비교

방법 유형 입력 장점 단점 연산 비용
수동 설계 Manual 도메인 지식 해석 가능, 고품질 확장 불가, 전문가 의존 인력 비용 높음
PolynomialFeatures Brute-force 수치형 구현 단순 차원 폭발 O(n^d)
Deep Feature Synthesis Automated (관계형) EntitySet 관계형 데이터에 강점 비관계형 데이터 부적합 중간
ExploreKit Automated (탐색) 테이블 다양한 연산자 느림, 대규모 탐색 매우 높음
OpenFE Automated (부스팅) 테이블 빠르고 정확 비관계형 데이터만 낮음-중간
CAAFE LLM 기반 테이블 + 설명 의미론적 피처, 해석 가능 LLM API 비용, 비결정적 API 호출 비용
LLM-FE LLM + 진화 최적화 테이블 + 설명 진화적 탐색으로 더 넓은 공간 높은 API 비용 매우 높음

Feature Selection 방법론 비교

방법 분류 모델 의존성 피처 상호작용 고려 속도
Correlation Filter 없음 X (쌍별 선형만) 매우 빠름
Mutual Information Filter 없음 X (쌍별 비선형) 빠름
Chi-squared Filter 없음 X 빠름
RFE Wrapper 모델 필요 O (간접적) 느림
Boruta Wrapper RF 기반 O (RF 통해) 느림
L1 (Lasso) Embedded 선형 모델 X 빠름
Tree Importance Embedded 트리 모델 O 빠름
SHAP Embedded 모델 필요 O (Shapley value) 중간
Permutation Importance Model-agnostic 모델 필요 O (간접적) 중간

실전 피처 엔지니어링 파이프라인

from sklearn.pipeline import Pipeline
from sklearn.compose import ColumnTransformer
from sklearn.preprocessing import (
    StandardScaler, OneHotEncoder, FunctionTransformer
)
from sklearn.impute import SimpleImputer
import numpy as np
import pandas as pd

def build_feature_pipeline(
    numeric_cols: list,
    categorical_cols: list,
    high_cardinality_cols: list,
    datetime_cols: list
) -> ColumnTransformer:
    """
    실전 피처 엔지니어링 파이프라인

    설계 원칙:
    1. 누수 방지: fit은 train에만, transform은 train/test 모두
    2. 결측치 처리 -> 변환 -> 스케일링 순서
    3. 범주형: 카디널리티에 따라 인코딩 전략 분리
    4. 시간: 주기적 인코딩 (sin/cos)으로 연속성 보존
    """
    # 수치형 파이프라인
    numeric_pipeline = Pipeline([
        ('imputer', SimpleImputer(strategy='median')),
        ('log_transform', FunctionTransformer(
            np.log1p, validate=True
        )),
        ('scaler', StandardScaler())
    ])

    # 저카디널리티 범주형
    categorical_pipeline = Pipeline([
        ('imputer', SimpleImputer(strategy='most_frequent')),
        ('encoder', OneHotEncoder(
            handle_unknown='ignore',
            sparse_output=False,
            min_frequency=0.01    # 1% 미만 빈도는 기타 처리
        ))
    ])

    # 고카디널리티 범주형 (Target Encoding은 별도 처리 권장)
    high_card_pipeline = Pipeline([
        ('imputer', SimpleImputer(strategy='constant', fill_value='MISSING')),
        ('encoder', ce.TargetEncoder(
            smoothing=10.0,
            min_samples_leaf=20
        ))
    ])

    # 날짜 피처 (주기적 인코딩)
    def datetime_features(X):
        """시간 피처의 주기적 인코딩 (sin/cos)"""
        result = pd.DataFrame(index=X.index)
        for col in X.columns:
            dt = pd.to_datetime(X[col])
            # 월 (12개월 주기)
            result[f'{col}_month_sin'] = np.sin(2 * np.pi * dt.dt.month / 12)
            result[f'{col}_month_cos'] = np.cos(2 * np.pi * dt.dt.month / 12)
            # 요일 (7일 주기)
            result[f'{col}_dow_sin'] = np.sin(2 * np.pi * dt.dt.dayofweek / 7)
            result[f'{col}_dow_cos'] = np.cos(2 * np.pi * dt.dt.dayofweek / 7)
            # 시간 (24시간 주기)
            result[f'{col}_hour_sin'] = np.sin(2 * np.pi * dt.dt.hour / 24)
            result[f'{col}_hour_cos'] = np.cos(2 * np.pi * dt.dt.hour / 24)
        return result

    datetime_pipeline = Pipeline([
        ('extract', FunctionTransformer(datetime_features))
    ])

    # 전체 조합
    preprocessor = ColumnTransformer(
        transformers=[
            ('num', numeric_pipeline, numeric_cols),
            ('cat', categorical_pipeline, categorical_cols),
            ('high_cat', high_card_pipeline, high_cardinality_cols),
            ('dt', datetime_pipeline, datetime_cols),
        ],
        remainder='drop'
    )

    return preprocessor

피처 엔지니어링 안티패턴

안티패턴 문제 해결
타겟 누수 (Target Leakage) 타겟에서 파생된 피처 사용 -> 과적합 시간적 인과관계 검증, CV 기반 인코딩
무한 피처 생성 차원의 저주, 노이즈 과적합 피처 선택 병행, 중요도 기반 프루닝
스케일링 미적용 거리 기반 모델(KNN, SVM) 성능 저하 StandardScaler, RobustScaler
결측치 무시 정보 손실 또는 편향 결측 지시자(indicator) 피처 추가
학습/테스트 동시 fit 미래 정보 누출 Pipeline으로 fit/transform 분리
고카디널리티에 One-Hot 희소 행렬, 메모리 폭발 Target/Frequency Encoding 사용

관련 라이브러리

라이브러리 주요 기능 비고
scikit-learn 전처리, 선택, 파이프라인 표준 도구
Featuretools Deep Feature Synthesis 관계형 데이터 자동 FE
OpenFE Feature Boosting 기반 자동 생성 ICML 2024
CAAFE LLM 기반 Context-Aware FE NeurIPS 2023
category_encoders 20+ 범주형 인코딩 방법 Target, WoE, Binary 등
tsfresh 시계열 자동 피처 추출 789개 피처 자동 계산
Boruta (boruta_py) All-relevant 피처 선택 RF + shadow features
shap SHAP 기반 피처 중요도 해석 가능성

핵심 참고 자료

논문/자료 저자 발표 기여
Deep Feature Synthesis Kanter & Veeramachaneni IEEE DSAA 2015 관계형 데이터 자동 FE의 기초
ExploreKit Katz et al. IEEE ICDM 2016 메타러닝 기반 피처 탐색
OpenFE Zhang et al. ICML 2024 Feature Boosting, 효율적 자동 FE
CAAFE Hollmann, Muller, Hutter NeurIPS 2023 LLM 기반 Context-Aware FE
LLM-FE arXiv 2025 arXiv LLM + 진화 최적화 FE
Shap-Select arXiv 2024 arXiv SHAP + 회귀 기반 경량 선택
Feature Engineering and Selection Kuhn & Johnson 2019 (Book) 실전 FE 교과서
Automated Data Processing Survey Mumuni & Mumuni Journal of Big Data 2024 자동 전처리/FE 종합 서베이