변수 변환

src/content/documents/machine-learning/variable-transformation-log-power.json

변환은 분포를 예쁘게 만드는 장식이 아니다

변수 변환은 원래 값의 표현을 바꿔 모델이 관계를 더 단순하게 학습하도록 돕는 작업이다. 오른쪽 꼬리가 긴 금액을 압축하거나, 분산이 평균과 함께 커지는 현상을 줄이거나, 곱셈 관계를 덧셈 관계에 가깝게 만들 때 유용하다.

정규분포처럼 보이게 만드는 것 자체가 목표는 아니다. 교차검증 성능, 잔차 패턴, 해석 가능성이 실제로 나아지는지 원래 표현과 비교한다.

로그와 제곱근의 직관과 제약

  • log(x): x가 양수일 때 큰 값을 강하게 압축한다. 원래 척도에서 배수 변화가 중요할 때 잘 맞는다.

  • log1p(x): log(1+x)라서 0을 포함할 수 있지만 x는 -1보다 커야 한다. 무조건 상수를 더하면 해석과 결과가 상수 선택에 의존한다.

  • sqrt(x): 0 이상에서 로그보다 완만하게 압축한다. 카운트 변수의 분산 안정화 후보가 될 수 있다.

  • 역변환이 필요한 예측이라면 expm1, 제곱 등 원래 척도로 돌아오는 과정과 예측 편향도 함께 검토한다.

Box-Cox와 Yeo-Johnson 선택

두 방법은 지수 λ를 데이터에서 최대우도로 추정하는 단조 power transform이다. Box-Cox는 모든 값이 엄격히 양수여야 하고, Yeo-Johnson은 0과 음수를 허용한다. scikit-learn의 PowerTransformer는 기본으로 Yeo-Johnson을 사용하며 변환 후 평균 0·분산 1 표준화도 수행한다.

실습: 학습 데이터에서만 변환식 학습하기

import numpy as np
from sklearn.preprocessing import PowerTransformer

X_train = np.array([[1.0], [2.0], [4.0], [8.0], [16.0]])
X_test = np.array([[32.0]])

pt = PowerTransformer(method="box-cox", standardize=False)
train_t = pt.fit_transform(X_train)
test_t = pt.transform(X_test)

print(round(float(pt.lambdas_[0]), 3))
print(np.round(train_t.ravel(), 3).tolist())
print(np.round(test_t.ravel(), 3).tolist())
print(np.all(np.diff(train_t.ravel()) > 0))

scikit-learn 버전에 따라 최대우도 λ의 마지막 자릿수는 달라질 수 있다. 검증할 핵심은 fit_transform을 train에만 호출하고, test에는 transform만 호출하며, 단조 변환이라 값의 순서가 유지된다는 점이다.

0.0
[0.0, 0.693, 1.386, 2.079, 2.773]
[3.466]
True

변환 전 반드시 확인할 것

  • 0·음수·결측치가 있는가: 변환의 정의역과 결측 처리 순서를 확인한다.

  • 값이 측정치인가 코드인가: 우편번호 같은 숫자형 식별자에는 로그를 적용하지 않는다.

  • 0이 실제 의미인가: 임의의 +1은 편리하지만 단위가 바뀌면 의미도 달라질 수 있다.

  • 모델이 변환을 필요로 하는가: 선형·거리 기반 모델은 효과가 클 수 있지만 트리는 단조 변환에 덜 민감하다.

  • 해석 단위가 무엇인가: 로그 목표값 모델의 오차를 원래 금액 오차와 혼동하지 않는다.

데이터 누수와 Pipeline

PowerTransformer의 λ, 표준화 평균과 분산은 데이터에서 학습된다. 전체 데이터에 먼저 fit하면 테스트 분포가 학습 과정에 들어간다. scikit-learn 공식 문서도 이를 누수 위험으로 경고하고 Pipeline 사용을 권한다.

from sklearn.linear_model import Ridge
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import PowerTransformer

model = make_pipeline(
    PowerTransformer(method="yeo-johnson"),
    Ridge(alpha=1.0),
)
# cross_val_score(model, X, y, cv=5)라면 각 학습 폴드 안에서만 변환기를 fit한다.

효과를 검증하는 방법

  • 원본과 변환 후 히스토그램·분위수·이상치 범위를 비교한다.

  • 회귀라면 잔차 대 예측값 그래프에서 부채꼴 모양이 줄었는지 본다.

  • 동일한 분할과 지표로 원본 모델과 변환 모델을 교차검증한다.

  • 운영 입력의 정의역 위반과 변환 후 비유한값을 자동 검사한다.

  • 목표값을 변환했다면 원래 척도로 역변환한 지표도 함께 보고한다.

참고 자료

댓글 0

댓글을 불러오는 중…