📊데이터공학

Feature Engineering

피처 엔지니어링

원시 데이터에서 ML에 유용한 특성 생성. 모델 성능에 큰 영향.

📖 상세 설명

피처 엔지니어링(Feature Engineering)은 원시 데이터를 머신러닝 모델이 학습하기 좋은 형태의 특성(Feature)으로 변환하는 과정입니다. "데이터와 알고리즘 중 데이터가 더 중요하다"는 격언처럼, 좋은 피처는 모델 성능을 크게 향상시킵니다.

주요 기법으로는 수치형 변환(로그 변환, 스케일링, 정규화), 범주형 인코딩(One-Hot, Label, Target Encoding), 시계열 피처(요일, 시간대, lag features, rolling statistics), 텍스트 피처(TF-IDF, 단어 수, 감성 점수), 상호작용 피처(곱, 비율, 차이) 등이 있습니다.

도메인 지식이 핵심입니다. 예를 들어 이커머스에서는 "마지막 구매 후 경과 일수", "평균 구매 주기", "장바구니 대비 구매 비율" 같은 피처가 효과적입니다. 금융에서는 "수입 대비 부채 비율", "최근 3개월 연체 횟수" 같은 피처가 중요합니다.

피처 선택(Feature Selection)도 중요합니다. 관련 없는 피처는 노이즈가 되어 모델 성능을 저하시킵니다. 상관관계 분석, 피처 중요도(Feature Importance), 재귀적 제거(Recursive Feature Elimination) 등으로 유의미한 피처를 선별합니다.

💻 코드 예제

# Pandas 기반 피처 엔지니어링 예제

import pandas as pd
import numpy as np
from sklearn.preprocessing import StandardScaler, LabelEncoder

# 샘플 데이터
df = pd.DataFrame({
    'user_id': [1, 1, 1, 2, 2, 3],
    'purchase_date': pd.to_datetime(['2024-01-01', '2024-01-15', '2024-02-01',
                                     '2024-01-10', '2024-01-20', '2024-02-15']),
    'amount': [100, 250, 180, 500, 120, 80],
    'category': ['electronics', 'clothing', 'electronics', 'food', 'food', 'clothing'],
    'is_weekend': [False, False, True, True, False, True]
})

print("원본 데이터:")
print(df)

# 1. 시계열 피처
df['day_of_week'] = df['purchase_date'].dt.dayofweek
df['month'] = df['purchase_date'].dt.month
df['is_month_start'] = df['purchase_date'].dt.is_month_start.astype(int)

# 2. 수치형 변환
df['amount_log'] = np.log1p(df['amount'])  # 로그 변환 (왜도 감소)
scaler = StandardScaler()
df['amount_scaled'] = scaler.fit_transform(df[['amount']])

# 3. 범주형 인코딩
# One-Hot Encoding
category_dummies = pd.get_dummies(df['category'], prefix='cat')
df = pd.concat([df, category_dummies], axis=1)

# Label Encoding
le = LabelEncoder()
df['category_encoded'] = le.fit_transform(df['category'])

# 4. 사용자별 집계 피처 (RFM 분석 스타일)
user_features = df.groupby('user_id').agg({
    'purchase_date': ['min', 'max', 'count'],
    'amount': ['sum', 'mean', 'std']
}).reset_index()
user_features.columns = ['user_id', 'first_purchase', 'last_purchase',
                         'purchase_count', 'total_amount', 'avg_amount', 'std_amount']

# Recency (마지막 구매 후 경과 일수)
reference_date = pd.Timestamp('2024-03-01')
user_features['recency_days'] = (reference_date - user_features['last_purchase']).dt.days

# Tenure (첫 구매부터 경과 일수)
user_features['tenure_days'] = (reference_date - user_features['first_purchase']).dt.days

# 구매 빈도 (일 평균)
user_features['purchase_frequency'] = user_features['purchase_count'] / user_features['tenure_days']

# 결측값 처리
user_features['std_amount'] = user_features['std_amount'].fillna(0)

print("\n사용자별 피처:")
print(user_features)

# 5. Lag 피처 (이전 값 참조)
df_sorted = df.sort_values(['user_id', 'purchase_date'])
df_sorted['prev_amount'] = df_sorted.groupby('user_id')['amount'].shift(1)
df_sorted['amount_diff'] = df_sorted['amount'] - df_sorted['prev_amount']

# 6. Rolling 피처 (이동 통계)
df_sorted['rolling_avg_3'] = df_sorted.groupby('user_id')['amount'].transform(
    lambda x: x.rolling(window=3, min_periods=1).mean()
)

print("\nLag/Rolling 피처 추가:")
print(df_sorted[['user_id', 'purchase_date', 'amount', 'prev_amount',
                  'amount_diff', 'rolling_avg_3']])

🗣️ 실무에서 이렇게 말해요

ML엔지니어: "모델 성능이 안 오르는데, 피처 엔지니어링을 더 해볼게요. RFM 피처랑 구매 간격 변동성 추가해보려고요."

분석가: "도메인 전문가한테 어떤 피처가 중요한지 물어보면 좋을 것 같아요."

시니어: "좋아요, 그리고 피처 중요도 분석해서 불필요한 피처 제거하는 것도 같이 진행합시다."

면접관: "피처 엔지니어링에서 가장 중요하게 생각하는 부분은 뭔가요?"

지원자: "Data Leakage 방지입니다. 미래 정보가 피처에 들어가면 학습 시 성능은 좋지만 실제 서비스에서 실패합니다. 시계열 데이터에서 특히 주의해야 하고, 학습/검증 분리 시점을 명확히 해야 합니다."

리뷰어: "이 Target Encoding에서 전체 데이터로 인코딩했는데, 이러면 validation에 정보 누수돼요."

작성자: "아, 학습 데이터로만 인코딩하고 transform해야 하는군요. cross-validation fold 안에서 처리하도록 수정할게요."

⚠️ 주의사항

📚 더 배우기