034 범주형 특성 자동 처리
키워드: 범주형, 카테고리, 인코딩
개요
범주형 특성(Categorical Features)은 실제 데이터에서 매우 흔합니다. 성별, 지역, 직업 등이 대표적입니다. 이 글에서는 FLAML이 범주형 특성을 어떻게 처리하는지, 그리고 최적의 인코딩 방법을 알아봅니다.
실습 환경
- Python 버전: 3.11 권장
- 필요 패키지:
flaml[automl], pandas, scikit-learn
pip install flaml[automl] pandas scikit-learn category_encoders
범주형 특성이란?
유형
import pandas as pd
import numpy as np
# 034 범주형 특성 예시
categorical_examples = pd.DataFrame({
'명목형': ['Red', 'Blue', 'Green', 'Red', 'Blue'], # 순서 없음
'순서형': ['Low', 'Medium', 'High', 'Medium', 'Low'], # 순서 있음
'이진형': ['Yes', 'No', 'Yes', 'Yes', 'No'], # 2개 값
'고카디널리티': ['ID_001', 'ID_002', 'ID_003', 'ID_004', 'ID_005'] # 많은 고유값
})
print("범주형 특성 유형:")
print(categorical_examples)
print(f"\n고유값 수:")
for col in categorical_examples.columns:
print(f" {col}: {categorical_examples[col].nunique()}개")
수치화가 필요한 이유
# 034 대부분의 ML 모델은 수치 입력만 받음
from sklearn.linear_model import LogisticRegression
X_categorical = pd.DataFrame({'color': ['Red', 'Blue', 'Green']})
y = [0, 1, 1]
try:
model = LogisticRegression()
model.fit(X_categorical, y)
except ValueError as e:
print(f"에러: {e}")
print("\n→ 범주형 특성을 수치로 변환해야 합니다!")
인코딩 방법 비교
1. Label Encoding
from sklearn.preprocessing import LabelEncoder
data = ['Red', 'Blue', 'Green', 'Red', 'Blue']
le = LabelEncoder()
encoded = le.fit_transform(data)
print("Label Encoding:")
print(f" 원본: {data}")
print(f" 인코딩: {encoded}")
print(f" 매핑: {dict(zip(le.classes_, range(len(le.classes_))))}")
# 034 주의: 순서가 생김 (Red=2 > Green=1 > Blue=0)
2. One-Hot Encoding
from sklearn.preprocessing import OneHotEncoder
ohe = OneHotEncoder(sparse_output=False)
encoded_ohe = ohe.fit_transform(pd.DataFrame({'color': data}))
print("\nOne-Hot Encoding:")
print(f" 원본: {data}")
print(f" 컬럼: {ohe.get_feature_names_out()}")
print(f" 인코딩:\n{encoded_ohe}")
3. Ordinal Encoding
from sklearn.preprocessing import OrdinalEncoder
ordinal_data = ['Low', 'Medium', 'High', 'Medium', 'Low']
# 034 순서 지정
oe = OrdinalEncoder(categories=[['Low', 'Medium', 'High']])
encoded_ord = oe.fit_transform(pd.DataFrame({'level': ordinal_data}))
print("\nOrdinal Encoding:")
print(f" 원본: {ordinal_data}")
print(f" 인코딩: {encoded_ord.flatten()}")
print(" (Low=0 < Medium=1 < High=2)")
FLAML의 범주형 처리
자동 처리 (LightGBM/XGBoost)
from flaml import AutoML
from sklearn.model_selection import train_test_split
# 034 범주형 특성이 있는 데이터 생성
np.random.seed(42)
n_samples = 1000
df = pd.DataFrame({
'age': np.random.randint(20, 60, n_samples),
'income': np.random.uniform(30000, 100000, n_samples),
'city': np.random.choice(['Seoul', 'Busan', 'Incheon', 'Daegu'], n_samples),
'education': np.random.choice(['High School', 'Bachelor', 'Master', 'PhD'], n_samples),
'gender': np.random.choice(['M', 'F'], n_samples),
})
# 034 타겟 생성
y = ((df['age'] > 40) & (df['income'] > 60000)).astype(int)
# 034 데이터 분할
X_train, X_test, y_train, y_test = train_test_split(df, y, test_size=0.2, random_state=42)
# 034 LightGBM/XGBoost는 범주형을 자동 처리
automl = AutoML()
automl.fit(
X_train, y_train,
task="classification",
time_budget=60,
estimator_list=["lgbm"], # LightGBM 사용
verbose=0
)
print(f"최적 모델: {automl.best_estimator}")
print(f"테스트 정확도: {automl.score(X_test, y_test):.4f}")
범주형 타입 지정
# 034 명시적으로 범주형 타입 지정
df_typed = df.copy()
df_typed['city'] = df_typed['city'].astype('category')
df_typed['education'] = df_typed['education'].astype('category')
df_typed['gender'] = df_typed['gender'].astype('category')
print("데이터 타입:")
print(df_typed.dtypes)
X_train_typed, X_test_typed, y_train, y_test = train_test_split(
df_typed, y, test_size=0.2, random_state=42
)
# 034 FLAML 학습
automl_typed = AutoML()
automl_typed.fit(
X_train_typed, y_train,
task="classification",
time_budget=60,
verbose=0
)
print(f"\n범주형 타입 지정 후 정확도: {automl_typed.score(X_test_typed, y_test):.4f}")
고카디널리티 특성 처리
문제점
# 034 고카디널리티 예시
high_cardinality = pd.DataFrame({
'user_id': [f'user_{i}' for i in range(1000)],
'product_id': [f'prod_{i%500}' for i in range(1000)],
'city': np.random.choice([f'city_{i}' for i in range(100)], 1000)
})
print("고카디널리티 특성:")
for col in high_cardinality.columns:
print(f" {col}: {high_cardinality[col].nunique()}개 고유값")
# 034 One-Hot Encoding 시 차원 폭발
# 1000 user_id + 500 product_id + 100 city = 1600개 컬럼!
해결 방법: Target Encoding
from category_encoders import TargetEncoder
# 034 타겟 인코딩 예시
np.random.seed(42)
df_high = pd.DataFrame({
'city': np.random.choice([f'city_{i}' for i in range(50)], 1000),
'category': np.random.choice([f'cat_{i}' for i in range(30)], 1000),
'value': np.random.randn(1000) * 10
})
y_high = (df_high['value'] > 0).astype(int)
# 034 데이터 분할
X_train_h, X_test_h, y_train_h, y_test_h = train_test_split(
df_high[['city', 'category']], y_high, test_size=0.2, random_state=42
)
# 034 Target Encoding
te = TargetEncoder()
X_train_encoded = te.fit_transform(X_train_h, y_train_h)
X_test_encoded = te.transform(X_test_h)
print("Target Encoding 결과:")
print(f" 원본 shape: {X_train_h.shape}")
print(f" 인코딩 후 shape: {X_train_encoded.shape}")
print(f"\n인코딩 예시 (city 컬럼):")
print(X_train_encoded['city'].head())
FLAML에서 고카디널리티 처리
# 034 방법 1: Target Encoding 후 FLAML
from category_encoders import TargetEncoder
def preprocess_high_cardinality(X_train, X_test, y_train, threshold=10):
"""고카디널리티 특성 전처리"""
encoders = {}
X_train_processed = X_train.copy()
X_test_processed = X_test.copy()
for col in X_train.select_dtypes(include=['object', 'category']).columns:
n_unique = X_train[col].nunique()
if n_unique > threshold:
# 고카디널리티: Target Encoding
encoder = TargetEncoder()
X_train_processed[col] = encoder.fit_transform(X_train[[col]], y_train)
X_test_processed[col] = encoder.transform(X_test[[col]])
encoders[col] = ('target', encoder)
else:
# 저카디널리티: Label Encoding
le = LabelEncoder()
X_train_processed[col] = le.fit_transform(X_train[col].astype(str))
X_test_processed[col] = le.transform(X_test[col].astype(str))
encoders[col] = ('label', le)
return X_train_processed, X_test_processed, encoders
# 034 적용
X_train_pre, X_test_pre, encoders = preprocess_high_cardinality(
X_train_h, X_test_h, y_train_h, threshold=10
)
print("인코딩 방식:")
for col, (method, _) in encoders.items():
print(f" {col}: {method} encoding")
인코딩 전략 비교
from sklearn.metrics import accuracy_score
# 034 여러 인코딩 방식 비교
results = {}
# 1. Label Encoding
le_encoders = {}
X_train_le = X_train.copy()
X_test_le = X_test.copy()
for col in ['city', 'education', 'gender']:
le = LabelEncoder()
X_train_le[col] = le.fit_transform(X_train_le[col])
X_test_le[col] = le.transform(X_test_le[col])
le_encoders[col] = le
automl_le = AutoML()
automl_le.fit(X_train_le, y_train, task="classification", time_budget=30, verbose=0)
results['Label Encoding'] = automl_le.score(X_test_le, y_test)
# 2. One-Hot Encoding
X_train_ohe = pd.get_dummies(X_train, columns=['city', 'education', 'gender'])
X_test_ohe = pd.get_dummies(X_test, columns=['city', 'education', 'gender'])
# 034 학습 데이터에 없는 컬럼 처리
for col in X_train_ohe.columns:
if col not in X_test_ohe.columns:
X_test_ohe[col] = 0
X_test_ohe = X_test_ohe[X_train_ohe.columns]
automl_ohe = AutoML()
automl_ohe.fit(X_train_ohe, y_train, task="classification", time_budget=30, verbose=0)
results['One-Hot Encoding'] = automl_ohe.score(X_test_ohe, y_test)
# 3. 자동 처리 (LightGBM)
automl_auto = AutoML()
automl_auto.fit(X_train, y_train, task="classification", time_budget=30,
estimator_list=["lgbm"], verbose=0)
results['Auto (LightGBM)'] = automl_auto.score(X_test, y_test)
print("\n인코딩 방식별 성능:")
for method, score in results.items():
print(f" {method}: {score:.4f}")
인코딩 선택 가이드
def recommend_encoding(n_unique, has_order=False):
"""인코딩 방식 추천"""
if has_order:
return "Ordinal Encoding"
elif n_unique == 2:
return "Binary / Label Encoding"
elif n_unique <= 10:
return "One-Hot Encoding"
elif n_unique <= 100:
return "Target Encoding 또는 LightGBM 자동"
else:
return "Target Encoding (필수)"
# 034 테스트
test_cases = [
(2, False, "성별"),
(4, False, "지역"),
(5, True, "교육수준"),
(50, False, "도시"),
(10000, False, "사용자ID")
]
print("인코딩 추천:")
print("-" * 50)
for n_unique, has_order, desc in test_cases:
recommendation = recommend_encoding(n_unique, has_order)
print(f"{desc} ({n_unique}개): {recommendation}")
정리
- 범주형 특성은 대부분의 ML 모델에서 수치화가 필요합니다.
- Label Encoding: 순서가 있거나 트리 모델에 적합
- One-Hot Encoding: 저카디널리티, 선형 모델에 적합
- Target Encoding: 고카디널리티에 효과적
- LightGBM/XGBoost는 범주형을 자동으로 처리합니다.
- 카디널리티에 따라 적절한 인코딩을 선택합니다.
category타입으로 명시하면 FLAML이 더 잘 처리합니다.
다음 글 예고
다음 글에서는 분류 파트 총정리를 진행합니다. Part 2에서 배운 내용을 정리하고 실전 체크리스트를 제공합니다.
FLAML AutoML 마스터 시리즈 #034