본문 바로가기

개발 공부/머신러닝

[Machine Learning] One-Hot Encoding 정리

원핫인코딩은 범주형 데이터를 머신러닝 모델이 처리할 수 있도록 0과 1로 이루어진 여러 개의 컬럼으로 변환하는 방법이다.

예를 들어 pclass에 값이 1, 2, 3 세 종류라면:

pclass
1
2
3
 

원핫인코딩 후:

pclass_1  pclass_2  pclass_3
1         0         0
0         1         0
0         0         1
 

처럼 변환된다.

즉 행의 개수는 그대로 유지되고, 카테고리 종류만큼 열의 개수가 증가할 수 있다.

인코딩 전 : (712, 1)
인코딩 후 : (712, 3)

⠀

1. 인코딩할 컬럼 분리

cols_encoding = ["pclass", "sex", "embarked", "sibsp", "parch"]

tmp_tr = train[cols_encoding]
tmp_te = test[cols_encoding]
 

tmp_tr, tmp_te에는 원핫인코딩할 컬럼만 따로 저장한다.

반대로 age, fare처럼 인코딩하지 않고 그대로 사용할 컬럼은 별도로 저장한다.

⠀

cols = ["age", "fare"]

features_tr = train[cols]
features_te = test[cols]
 

정리하면:

features_tr, features_te
→ 인코딩하지 않을 컬럼

tmp_tr, tmp_te
→ 인코딩할 컬럼

⠀

2. OneHotEncoder 생성

encoder = ce.OneHotEncoder(use_cat_names=True)
 

use_cat_names=True를 사용하면 인코딩 후 컬럼 이름에 실제 카테고리 값이 포함되어 결과를 확인하기 쉽다.

예:

pclass_1.0
pclass_2.0
pclass_3.0

⠀

3. 인코딩 결과를 담을 빈 DataFrame 생성

enco_tr = pd.DataFrame()
enco_te = pd.DataFrame()
 

처음에는 아무 데이터도 없기 때문에:

 
enco_tr.shape
# (0, 0)
 

이다.

이후 각 컬럼을 인코딩한 결과를 enco_tr, enco_te에 계속 추가한다.

⠀

4. 컬럼을 하나씩 인코딩

for col in tmp_tr.columns:
 

tmp_tr의 컬럼을 하나씩 col에 넣으며 반복한다.

예:

1회차 → pclass
2회차 → sex
3회차 → embarked
4회차 → sibsp
5회차 → parch
 

따라서:

 
tmp_tr[col]
 

은 반복할 때마다 현재 처리 중인 컬럼을 의미한다.

⠀

5. astype('category')

tmp_tr[col].astype('category')
 

astype()은 데이터 타입을 변경하는 함수이다.

현재 처리 중인 컬럼 전체를 category 타입으로 변환

category 타입인 값만 선택하는 것이 아니라, 해당 컬럼 전체의 타입을 바꾸는 것이다.

⠀

6. Train 데이터는 fit_transform()

_enco = encoder.fit_transform(
    tmp_tr[col].astype('category')
)
 

fit_transform()은 두 작업을 동시에 수행한다.

fit
→ 어떤 카테고리가 존재하는지 학습

transform
→ 학습한 규칙으로 실제 데이터를 변환
 

예를 들어 pclass에 1, 2, 3이 있다면 이를 학습한 후:

pclass_1
pclass_2
pclass_3
 

세 개의 컬럼으로 변환한다.

⠀

7. 인코딩 결과를 기존 결과에 추가

enco_tr = pd.concat([enco_tr, _enco], axis=1)
 

axis=1은 컬럼 방향으로 옆에 붙인다는 의미이다.

처음에는:

enco_tr → (0, 0)
_enco   → (712, 3)
 

이므로 합친 후:

enco_tr → (712, 3)
 

이 된다.


다음 컬럼을 인코딩하면 기존 enco_tr 뒤에 새로운 컬럼이 계속 추가된다.

기존 enco_tr → (712, 3)
새로운 _enco → (712, 2)

결과 enco_tr → (712, 5)

⠀

8. Test 데이터는 transform()만 사용

_enco = encoder.transform(
    tmp_te[col].astype('category')
)
 

Test 데이터에는 fit_transform()을 사용하지 않는다.

Train 데이터에서 이미 인코딩 규칙을 학습했기 때문에, Test 데이터는 Train에서 학습한 규칙 그대로 변환만 해야 한다.

Train
fit_transform()
→ 규칙 학습 + 변환

Test
transform()
→ Train에서 배운 규칙으로 변환
 

머신러닝에서는 Test 데이터를 이용해 새로운 규칙을 학습하지 않는 것이 중요하다.

 

❗데이터 누수

만약 Test 데이터에도 fit() 또는 fit_transform()을 사용하면, Test 데이터의 정보를 이용해 새로운 규칙을 학습하게 된다.

이렇게 Test 데이터의 정보가 학습 과정에 섞이는 현상을 데이터 누수(Data Leakage)​라고 한다.

데이터 누수가 발생하면 모델의 성능이 실제보다 좋게 평가될 수 있고, 새로운 데이터에서는 성능이 떨어질 수 있다. = 과대적합

⠀

9. Test 인코딩 결과 누적

enco_te = pd.concat([enco_te, _enco], axis=1)
 

Train과 마찬가지로 Test의 인코딩 결과도 오른쪽으로 계속 붙인다.

최종적으로 예를 들어:

enco_tr → (712, 22)
enco_te → (179, 22)
 

처럼 만들어질 수 있다.

Train과 Test의 행 개수가 다른 이유는 처음부터 데이터를:

Train → 712개
Test  → 179개
 

로 나눴기 때문이다.

인코딩 때문에 행 개수가 줄어든 것은 아니다.

⠀

10. 인덱스 초기화

features_tr = features_tr.reset_index(drop=True)
features_te = features_te.reset_index(drop=True)

enco_tr = enco_tr.reset_index(drop=True)
enco_te = enco_te.reset_index(drop=True)
 

reset_index(drop=True)는 행 순서는 그대로 유지하면서 인덱스를 0부터 다시 부여하는 것이다.

예:

기존 인덱스
331, 733, 382, 704 ...

↓

reset_index(drop=True)

0, 1, 2, 3 ...
 

나중에 features_tr과 enco_tr을 concat()할 때 인덱스가 서로 맞도록 하기 위해 사용한다.

⠀

11. 인코딩하지 않은 데이터와 다시 합치기

df_tr = pd.concat([features_tr, enco_tr], axis=1)
df_te = pd.concat([features_te, enco_te], axis=1)
 

예를 들어:

features_tr → (712, 2)
enco_tr     → (712, 22)
 

이면 최종 결과는:

df_tr → (712, 24)
 

가 된다.

행은 그대로 712개이고, 컬럼은 2 + 22 = 24개가 된다.

⠀

전체 흐름

원본 Train / Test
        ↓
인코딩할 컬럼과 하지 않을 컬럼 분리
        ↓
Train의 각 컬럼을 category 타입으로 변경
        ↓
fit_transform()
        ↓
인코딩 결과를 enco_tr에 누적
        ↓
Test는 Train에서 학습한 규칙으로 transform()
        ↓
인코딩 결과를 enco_te에 누적
        ↓
인덱스 초기화
        ↓
기존 수치형 데이터와 인코딩 결과 concat
        ↓
최종 학습용 데이터 생성

⠀

 

반응형