Skip to main content

인코딩

대부분의 ML 모델은 수치 데이터만 처리할 수 있으므로, 범주형(Categorical) 데이터를 수치로 변환해야 합니다. 인코딩 방법에 따라 모델 성능이 크게 달라질 수 있으므로, 데이터 특성과 모델 유형에 맞는 인코딩을 선택하는 것이 중요합니다.

학습 목표

  • Label Encoding과 Ordinal Encoding의 차이를 이해한다
  • One-Hot Encoding을 적용하고 고카디널리티 문제를 처리할 수 있다
  • Target Encoding의 원리와 주의점을 설명할 수 있다
  • 데이터 특성에 맞는 인코딩 방법을 선택할 수 있다

왜 중요한가

범주형 변수에 잘못된 인코딩을 적용하면 모델이 존재하지 않는 순서나 크기 관계를 학습합니다. 예를 들어, “개발=1, 영업=2, 마케팅=3”으로 인코딩하면 모델은 “마케팅 > 영업 > 개발”이라는 잘못된 순서를 학습할 수 있습니다.

Label Encoding

각 범주에 고유한 정수를 부여합니다. 순서가 있는 범주형 변수에 적합합니다.
import numpy as np
import pandas as pd
from sklearn.preprocessing import LabelEncoder, OrdinalEncoder, OneHotEncoder

np.random.seed(42)
df = pd.DataFrame({
    'department': np.random.choice(['개발', '영업', '마케팅', '인사'], 10),
    'education': np.random.choice(['고졸', '학사', '석사', '박사'], 10),
    'city': np.random.choice(['서울', '부산', '대전', '광주', '대구'], 10),
    'salary': np.random.normal(4500, 800, 10).round(0)
})

# Label Encoding
le = LabelEncoder()
df['department_encoded'] = le.fit_transform(df['department'])
print("Label Encoding:")
print(df[['department', 'department_encoded']])
print(f"\n클래스 매핑: {dict(zip(le.classes_, range(len(le.classes_))))}")
Label Encoding은 순서가 없는 범주형 변수(명목형)에 사용하면 안 됩니다. 모델이 임의로 부여된 숫자 크기를 의미 있는 것으로 학습할 수 있습니다.

Ordinal Encoding

순서가 있는 범주형 변수에 명시적 순서를 지정하여 인코딩합니다.
# Ordinal Encoding: 학력에 순서 지정
oe = OrdinalEncoder(categories=[['고졸', '학사', '석사', '박사']])
df['education_encoded'] = oe.fit_transform(df[['education']])
print("Ordinal Encoding:")
print(df[['education', 'education_encoded']])
# 고졸=0, 학사=1, 석사=2, 박사=3

One-Hot Encoding

각 범주를 별도의 이진 컬럼으로 변환합니다. 순서가 없는 범주형 변수에 적합합니다.
# sklearn OneHotEncoder
ohe = OneHotEncoder(sparse_output=False, drop='first')  # 다중공선성 방지
encoded = ohe.fit_transform(df[['department']])
encoded_df = pd.DataFrame(
    encoded,
    columns=ohe.get_feature_names_out(['department'])
)
print("One-Hot Encoding (drop first):")
print(encoded_df)

# pandas get_dummies (더 간단)
dummies = pd.get_dummies(df['department'], prefix='dept', drop_first=True)
print("\npd.get_dummies:")
print(dummies)

고카디널리티 처리

범주가 매우 많은(High Cardinality) 변수는 One-Hot 인코딩 시 컬럼이 폭발합니다.
# 고카디널리티 변수: 도시 이름 100개
cities = [f'city_{i}' for i in range(100)]
high_card = pd.DataFrame({
    'city': np.random.choice(cities, 1000)
})

print(f"고유값: {high_card['city'].nunique()}개")
print(f"One-Hot 시 컬럼 수: {high_card['city'].nunique() - 1}개")

# 해결 방법 1: 빈도 기반 그룹화
freq = high_card['city'].value_counts()
rare_cities = freq[freq < 10].index
high_card['city_grouped'] = high_card['city'].replace(
    {city: '기타' for city in rare_cities}
)
print(f"그룹화 후 고유값: {high_card['city_grouped'].nunique()}개")

# 해결 방법 2: 빈도 인코딩
freq_map = high_card['city'].value_counts(normalize=True).to_dict()
high_card['city_freq'] = high_card['city'].map(freq_map)
print(f"\n빈도 인코딩 예시:")
print(high_card[['city', 'city_freq']].head())

Target Encoding

범주를 타겟 변수의 평균값으로 대체합니다. 고카디널리티 변수에 효과적이지만, 과적합 위험이 있습니다.
# Target Encoding 구현
np.random.seed(42)
df_te = pd.DataFrame({
    'city': np.random.choice(['서울', '부산', '대전'], 100),
    'price': np.random.normal(5000, 1000, 100)
})

# 도시별 평균 가격으로 인코딩
target_mean = df_te.groupby('city')['price'].mean()
df_te['city_target'] = df_te['city'].map(target_mean)

print("Target Encoding:")
print(target_mean.round(0))
print(df_te[['city', 'price', 'city_target']].head(10))
Target Encoding은 타겟 정보를 피처에 포함시키므로, 반드시 학습 데이터에서만 인코딩 값을 계산하고 검증/테스트 데이터에 적용해야 합니다. 교차검증 내에서 사용하세요.
# 안전한 Target Encoding: K-Fold 기반
from sklearn.model_selection import KFold

def target_encode_kfold(df, col, target, n_folds=5):
    """K-Fold 기반 Target Encoding (데이터 누수 방지)."""
    encoded = pd.Series(index=df.index, dtype=float)
    global_mean = df[target].mean()
    kf = KFold(n_splits=n_folds, shuffle=True, random_state=42)

    for train_idx, val_idx in kf.split(df):
        train_mean = df.iloc[train_idx].groupby(col)[target].mean()
        encoded.iloc[val_idx] = df.iloc[val_idx][col].map(train_mean)

    # 매핑되지 않은 값은 전체 평균으로 대체
    encoded.fillna(global_mean, inplace=True)
    return encoded

df_te['city_target_safe'] = target_encode_kfold(df_te, 'city', 'price')

인코딩 선택 가이드

인코딩적합한 변수모델카디널리티
Label순서형 (크기/등급)트리 모델무관
Ordinal순서형 (명시적 순서)모든 모델무관
One-Hot명목형 (순서 없음)선형/신경망낮음 (< 20)
빈도명목형트리 모델높음
Target명목형모든 모델높음

AI/ML에서의 활용

  • 트리 모델: Label/Ordinal Encoding이 잘 작동합니다 (순서 무관하게 분할)
  • 선형 모델: One-Hot Encoding 필수 (크기 관계를 학습하므로)
  • 딥러닝: Embedding Layer가 범주형 인코딩을 대체할 수 있습니다
  • 파이프라인: ColumnTransformer로 컬럼별 다른 인코딩을 적용합니다
트리 모델은 Label Encoding으로도 잘 작동합니다. 오히려 One-Hot Encoding은 피처 수를 늘려 트리의 분할 효율을 떨어뜨릴 수 있습니다. 범주 수가 많으면 Label 또는 Target Encoding을 권장합니다.

체크리스트

  • 명목형과 순서형 범주 변수를 구분할 수 있다
  • One-Hot Encoding을 적용하고 drop_first의 이유를 설명할 수 있다
  • 고카디널리티 변수에 적절한 인코딩을 선택할 수 있다
  • Target Encoding의 데이터 누수 위험을 이해한다
  • 모델 유형에 맞는 인코딩을 선택할 수 있다

다음 문서