다항 특성과 범주형 인코딩을 설계하는 데이터 전처리
상호작용 항·다항 특성으로 비선형 관계를 반영하고 One-Hot·Label 인코딩을 모델 특성에 맞게 설계하는 방법
2026-08-14 · 최초 발행 2024-04-29
특성 공간을 넓힐 때 함께 따라오는 선택
지도학습 성능을 끌어올리는 전처리는 결측값을 채우는 작업에 그치지 않는다. 수치형 변수 사이의 관계를 어떤 형태로 표현할지, 범주형 변수를 어떤 벡터로 바꿀지에 따라 모델이 학습하는 특성 공간 자체가 달라진다.
상호작용 항은 두 개 이상 특성의 곱으로 만들어진다. x1·x2, x1·x3처럼 결합한 항을 추가하면 선형 모델도 특성 사이의 비선형 상호작용 효과를 표현할 수 있다. 다항 특성은 원 특성의 제곱·세제곱 같은 고차항과 상호작용 항을 포함하는 확장 집합이다. 이를 통해 선형 모델이 비선형 관계를 근사할 수 있다.
범주형 변수에는 다른 판단이 필요하다. One-Hot 인코딩은 범주를 희소 이진 벡터로 바꾸므로 범주 사이에 서열을 부여하지 않는다. 선형 모델과 거리 기반 모델에 잘 맞는다. Label/Ordinal 인코딩은 각 범주를 정수 값에 대응시키며, 트리 기반 모델에서 사용하기 적합하다. 다만 실제로는 없는 순서 관계를 모델이 해석할 가능성은 남는다.
학습 데이터에서만 전처리를 적합한다
전처리의 Fit은 학습·검증 데이터를 나눈 뒤 학습 데이터에만 수행해야 한다. 이 순서를 지키지 않으면 검증 데이터의 정보가 변환 과정에 섞여 데이터 누수가 생긴다.
특성 확장을 적용할 때는 희소성과 차원 증가를 같이 봐야 한다. 다항 특성의 차수, 선택할 상호작용, 카테고리 수의 상한을 조절하면서 성능과 복잡도 사이의 균형을 잡는다.
선형·로지스틱 계열에서는 다항 특성과 상호작용 항의 효과가 크며 L2 규제를 함께 적용하는 방식을 고려할 수 있다. 범주형 변수는 One-Hot 인코딩이 적합하다. 반대로 트리·부스팅 계열은 모델이 자체적으로 비선형성을 다루므로 다항 항의 필요성이 낮다. 이 경우 범주형 변수에는 Label/Ordinal 인코딩이나 모델 내장 카테고리 처리를 활용할 수 있다.
다항·상호작용 항을 생성한 뒤에는 StandardScaler로 수치 범위를 맞춘다. 희소 행렬이 섞인 경우 with_mean=False가 필요하다. 다중공선성은 L2/L1 규제로 완화하고, 페널티와 C 또는 alpha를 튜닝한다.
차수(degree), interaction_only, 인코딩 방식은 교차검증으로 탐색한다. AUC, F1, 로그로스처럼 목적에 맞는 지표로 결정한다. 배포 이후에는 handle_unknown 전략과 인프리퀀트 그룹핑을 준비하고, 범주 변화도 계속 관찰해야 한다.
예측 과제에 맞춘 변환 조합
가격 또는 리스크 예측에 선형 모델을 쓸 때는 수치형 센서 x1, x2의 제곱항과 교호항으로 비선형성을 반영할 수 있다. 로지스틱 회귀나 릿지 회귀에는 L2 규제를 포함한다.
마케팅 반응 예측에서는 캠페인×요일, 예산×채널처럼 맥락을 드러내는 상호작용 항이 유용하다. 범주형 변수는 One-Hot으로 변환하고, 고빈도 범주만 남기는 인프리퀀트 그룹핑을 함께 적용할 수 있다.
제조 이상 감지에서는 공정 파라미터 사이의 상호작용 항이 복합 조건의 이상 징후를 포착하는 데 쓰인다. 트리 모델을 사용한다면 Label 인코딩으로 메모리 효율을 확보할 수 있다.
이커머스 클릭스루 예측처럼 product_id의 카디널리티가 높은 경우에는 One-Hot 대신 빈도 기준 그룹핑이나 해싱 확장을 검토한다. 주요 범주 변수는 One-Hot으로, 나머지는 Label 인코딩으로 처리하는 혼합 방식도 가능하다.
One-Hot과 Label/Ordinal 인코딩의 차이
| 항목 | One-Hot | Label/Ordinal |
|---|---|---|
| 성능(선형/거리기반) | 서열 왜곡 없음, 일반적으로 우수 | 서열 왜곡으로 성능 저하 가능 |
| 성능(트리/부스팅) | 분할 수 증가, 과적합 가능 | 분기 효율 우수, 보편적 선택 |
| 확장성(고유값 ↑) | 차원 폭발, 희소 행렬 전제 | 메모리 효율 우수 |
| 일관성(서열 정보) | 서열 제거 | 암묵적 서열 부여, 주의 필요 |
| 안정성(드리프트) | 신규 카테고리 처리 옵션 다양 | 신규값 인코딩 값 지정 필요 |
| 운영 편의 | 사전/열 유지 관리 필요 | 매핑 테이블 관리 간단 |
전처리부터 검증까지 이어지는 흐름
누수를 막는 전처리 파이프라인
전제조건은 Python 3.9+, scikit-learn >= 1.2, pandas, numpy다. 아래 예시는 다항·상호작용 항과 범주형 인코딩을 파이프라인에 넣고, 전처리 누수를 막은 상태에서 비교한다.
# pip install scikit-learn>=1.2 pandas numpy
from sklearn.datasets import make_classification
from sklearn.model_selection import cross_val_score, StratifiedKFold
from sklearn.compose import ColumnTransformer, make_column_selector as selector
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import OneHotEncoder, OrdinalEncoder, PolynomialFeatures, StandardScaler
from sklearn.impute import SimpleImputer
from sklearn.linear_model import LogisticRegression
import numpy as np
import pandas as pd
# 1) 예시 데이터 생성: 수치형 5 + 범주형 2
X_num, y = make_classification(n_samples=5000, n_features=5, n_informative=3, random_state=42)
rng = np.random.default_rng(42)
cat_a = rng.choice(['A','B','C','D'], size=5000, p=[0.5,0.2,0.2,0.1])
cat_b = rng.choice(['X','Y'], size=5000)
X = pd.DataFrame(X_num, columns=[f"num{i}" for i in range(5)])
X["cat_a"] = cat_a
X["cat_b"] = cat_b
cv = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)
# 2) 공통: 수치형 파이프라인(다항 특성 포함), 범주형 One-Hot
numeric_pipe_poly = Pipeline(steps=[
("imputer", SimpleImputer(strategy="median")),
("poly", PolynomialFeatures(degree=2, include_bias=False)), # interaction_only=True 로 교호작용만 선택 가능
("scaler", StandardScaler(with_mean=False)),
])
categorical_pipe_ohe = Pipeline(steps=[
("imputer", SimpleImputer(strategy="most_frequent")),
("ohe", OneHotEncoder(handle_unknown="infrequent_if_exist", min_frequency=0.01, sparse_output=True)),
])
preprocess_ohe_poly = ColumnTransformer(
transformers=[
("num", numeric_pipe_poly, selector(dtype_include=np.number)),
("cat", categorical_pipe_ohe, selector(dtype_include=object)),
]
)
clf = Pipeline(steps=[
("prep", preprocess_ohe_poly),
("model", LogisticRegression(max_iter=200, penalty="l2", n_jobs=-1))
])
scores = cross_val_score(clf, X, y, cv=cv, scoring="roc_auc")
print(f"AUC(One-Hot + 다항2): {scores.mean():.3f} ± {scores.std():.3f}")
# 3) 비교: 범주형을 Ordinal(Label) 인코딩으로 변경(트리 모델에 적합하지만 선형에도 시연)
categorical_pipe_ordinal = Pipeline(steps=[
("imputer", SimpleImputer(strategy="most_frequent")),
("ord", OrdinalEncoder(handle_unknown="use_encoded_value", unknown_value=-1)),
])
preprocess_ord_poly = ColumnTransformer(
transformers=[
("num", numeric_pipe_poly, selector(dtype_include=np.number)),
("cat", categorical_pipe_ordinal, selector(dtype_include=object)),
]
)
clf2 = Pipeline(steps=[
("prep", preprocess_ord_poly),
("model", LogisticRegression(max_iter=200, penalty="l2", n_jobs=-1))
])
scores2 = cross_val_score(clf2, X, y, cv=cv, scoring="roc_auc")
print(f"AUC(Label + 다항2): {scores2.mean():.3f} ± {scores2.std():.3f}")
degree∈{1,2}와 interaction_only 토글을 대상으로 다항 차수를 탐색할 수 있다. OneHotEncoder에는 handle_unknown="infrequent_if_exist"와 min_frequency를 사용해 드리프트에 대응한다. 희소 행렬이 함께 쓰이면 StandardScaler(with_mean=False)를 적용하고, L2 규제와 중요도 기반 특성 선택으로 다중공선성을 다룬다.
비선형 구조를 반영하면 AUC/정확도가 1~5%p 개선될 수 있으나 데이터셋에 따라 달라진다. 고차항을 과도하게 생성하면 과적합 위험이 커지므로 규제와 교차검증이 필요하다.
수치형 특성이 p개일 때 다항 2차의 출력 차원은 ≈ C(p+2,2)−1 ≈ p^2/2 + 3p/2이다. 카테고리가 k개인 One-Hot 인코딩은 k열을 추가하며, 드롭 옵션을 적용하면 k−1이 된다. 신규 카테고리 처리 전략을 적용하면 배포 실패 리스크를 낮출 수 있고, 파이프라인화는 재현 가능성과 배포 자동화를 수월하게 만든다.
선형·로지스틱 모델은 다항 2차와 One-Hot 인코딩 조합에서 출발할 수 있다. 트리·부스팅 모델에서는 Label/Ordinal 인코딩을 우선 검토하고 다항 항을 최소화한다. 고카디널리티 범주에는 인프리퀀트 그룹핑, 해싱, 타깃 인코딩 같은 확장 기법을 검토한다.