인코딩
대부분의 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 ( " \n pd.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을 권장합니다.
체크리스트
다음 문서