
데이터 분석가와 머신러닝 엔지니어가 겪는 가장 흔한 악몽 중 하나는 "훈련 데이터(Train)에서는 잘 작동하던 전처리 코드가 추론(Inference) 단계에서 에러를 뿜거나 성능이 급락하는 현상"입니다. 이는 전처리 단계와 모델 학습 단계가 파편화되어 있기 때문에 발생하는 고질적인 문제입니다. 특히 결측치 처리, 스케일링, 인코딩을 데이터프레임 단위로 수동 관리하면 'Data Leakage(데이터 누수)' 문제에서 자유로울 수 없습니다. 본 포스팅에서는 Scikit-learn(Sklearn) Pipeline을 활용하여 지저분한 전처리 과정을 하나의 깔끔한 모듈로 통합하고, 이를 통해 모델의 재현성을 확보하는 전문적인 엔지니어링 전략을 제시합니다.
1. 하드코딩 방식과 Pipeline 모듈화 방식의 치명적 차이 해결
단순히 df.fillna()나 df.apply()를 반복하는 하드코딩 방식은 프로토타이핑에는 유리할지 모르나, 상용 서비스 배포 시에는 치명적인 약점을 가집니다. 아래 표는 두 방식의 근본적인 차이점과 해결책을 요약한 것입니다.
| 구분 항목 | 수동 전처리 (Manual) | Sklearn Pipeline 모듈화 |
|---|---|---|
| 데이터 누수(Leakage) | 위험함 (전체 통계량 오염 가능성) | 안전함 (Train/Test 엄격 분리) |
| 코드 재사용성 | 낮음 (함수를 일일이 호출해야 함) | 매우 높음 (단일 객체로 저장 가능) |
| 하이퍼파라미터 튜닝 | 모델 파라미터만 가능 | 전처리 기법까지 GridSearch 가능 |
| 배포 및 서빙 | 전처리 코드 별도 관리 필요 | .pkl 하나로 전 과정 처리 해결 |
| 유지보수성 | 복잡 (코드 라인 비대화) | 명료 (선언적 워크플로우) |
2. 실무 적용을 위한 전문적인 파이프라인 Example 7가지
단순한 예제가 아닌, 실무에서 마주하는 결측치, 이상치, 범주형 데이터 처리 등 다양한 시나리오를 해결하는 핵심 코드입니다.
Example 1: 수치형과 범주형 데이터의 동시 처리를 위한 ColumnTransformer
서로 다른 데이터 타입을 가지는 컬럼들을 각각의 전략으로 동시에 처리하는 가장 표준적인 방법입니다.
from sklearn.pipeline import Pipeline
from sklearn.compose import ColumnTransformer
from sklearn.impute import SimpleImputer
from sklearn.preprocessing import StandardScaler, OneHotEncoder
# 1. 수치형 데이터용 파이프라인 (결측치 중앙값 대체 + 스케일링)
numeric_features = ['age', 'salary', 'experience']
numeric_transformer = Pipeline(steps=[
('imputer', SimpleImputer(strategy='median')),
('scaler', StandardScaler())
])
# 2. 범주형 데이터용 파이프라인 (결측치 최빈값 대체 + 원핫인코딩)
categorical_features = ['city', 'department']
categorical_transformer = Pipeline(steps=[
('imputer', SimpleImputer(strategy='most_frequent')),
('onehot', OneHotEncoder(handle_unknown='ignore'))
])
# 3. 통합 전처리기 구성
preprocessor = ColumnTransformer(
transformers=[
('num', numeric_transformer, numeric_features),
('cat', categorical_transformer, categorical_features)
])
Example 2: 전처리와 분류기(Classifier)의 결합
전처리기와 모델을 하나로 묶어 전체 프로세스를 fit 한 번으로 끝내는 방법입니다.
from sklearn.ensemble import RandomForestClassifier
# 모델 파이프라인 생성
clf_pipeline = Pipeline(steps=[
('preprocessor', preprocessor),
('classifier', RandomForestClassifier(n_estimators=100))
])
# 학습 (전처리와 모델 학습이 동시에 이루어짐)
clf_pipeline.fit(X_train, y_train)
# 예측 (전처리가 자동으로 적용되어 입력 데이터만 넣으면 됨)
predictions = clf_pipeline.predict(X_test)
Example 3: Custom Transformer를 통한 로그 변환 모듈화
기본 제공되지 않는 특정 수학적 변환(예: Log, Box-Cox)을 파이프라인에 포함하는 방법입니다.
import numpy as np
from sklearn.preprocessing import FunctionTransformer
# 로그 변환 함수 정의
log_transformer = FunctionTransformer(np.log1p, validate=False)
# 파이프라인의 특정 단계로 추가
numeric_transformer_v2 = Pipeline(steps=[
('imputer', SimpleImputer(strategy='median')),
('log', log_transformer),
('scaler', StandardScaler())
])
Example 4: 데이터 누수(Data Leakage) 없는 PCA 차원 축소 적용
훈련 데이터의 주성분을 기준으로 테스트 데이터를 투영하는 올바른 셔플링/변환 방법입니다.
from sklearn.decomposition import PCA
pca_pipeline = Pipeline(steps=[
('preprocessor', preprocessor),
('pca', PCA(n_components=0.95)), # 분산의 95% 보존
('classifier', RandomForestClassifier())
])
pca_pipeline.fit(X_train, y_train)
Example 5: 파이프라인 단계 내에서 최적의 전처리 기법 찾기 (GridSearch)
평균으로 대체할지, 중앙값으로 대체할지 자체를 하이퍼파라미터로 두고 실험하는 고급 방법입니다.
from sklearn.model_selection import GridSearchCV
param_grid = {
'preprocessor__num__imputer__strategy': ['mean', 'median'],
'classifier__n_estimators': [50, 100, 200],
'classifier__max_depth': [None, 10, 20]
}
grid_search = GridSearchCV(clf_pipeline, param_grid, cv=5)
grid_search.fit(X_train, y_train)
print(f"최적의 전처리 및 모델 조합: {grid_search.best_params_}")
Example 6: Feature Selection(특성 선택) 자동화 파이프라인
유의미한 변수만 자동으로 골라내어 모델 복잡도를 줄이는 로직을 추가합니다.
from sklearn.feature_selection import SelectKBest, f_classif
fs_pipeline = Pipeline(steps=[
('preprocessor', preprocessor),
('feature_selection', SelectKBest(score_func=f_classif, k=10)),
('classifier', RandomForestClassifier())
])
Example 7: 완전한 객체 직렬화 및 배포용 저장 방법
실제 프로덕션 환경에서 모델과 전처리기를 통째로 불러와 사용하는 방법입니다.
import joblib
# 전체 파이프라인 저장
joblib.dump(clf_pipeline, 'full_model_pipeline.pkl')
# 서빙 서버에서 로드 (전처리 코드가 필요 없음!)
loaded_model = joblib.load('full_model_pipeline.pkl')
new_data_pred = loaded_model.predict(raw_input_data)
3. 결론 및 독창적인 통찰
머신러닝의 성공은 모델의 알고리즘만큼이나 데이터 전처리의 정교함에 달려 있습니다. 단순히 코드를 줄이는 것이 목적이 아닙니다. Pipeline을 사용함으로써 우리는 실험 과정의 모든 단계를 'Version Control'이 가능한 형태로 고정할 수 있게 됩니다. 이는 팀 단위 협업에서 '내 컴퓨터에선 되는데 왜 네 컴퓨터에선 안 돼?'라는 질문을 영구적으로 제거하는 핵심 해결책입니다.
데이터 사이언티스트라면 이제 단순 스크립트 작성을 넘어, Software Engineering의 관점에서 파이프라인을 설계해야 합니다. 오늘 소개한 7가지 해결 방법을 통해 더욱 견고한 ML 시스템을 구축하시길 바랍니다.
정보 출처 및 참조
- Scikit-learn User Guide: "Pipelines and composite estimators" (v1.4)
- Hands-On Machine Learning with Scikit-Learn, Keras, and TensorFlow (2nd Edition) - Aurélien Géron
- Python Machine Learning - Sebastian Raschka & Vahid Mirjalili
- Scikit-learn API Reference:
sklearn.pipeline.Pipeline