머신러닝

Feature Engineering - 데이터 인코딩(레이블 인코딩, 원-핫 인코딩)

Y0un9Ki 2024. 2. 23. 14:39

요번에는 머신러닝의 성능을 올릴 수 있는 두번째 방법에 대해 알아보고자 한다.

Feature Engineering(데이터 전처리)에 과정에는 크게 4가지로 분류를 했다.

  • 인코딩 변환 : 문자 컬럼을 숫자로 변경
  • Feature Scaling : 숫자 컬럼을 적절하게 표준화, 정규화
  • 주성분 분석(PCA) : 숫자 컬럼을 적절하게 줄여주는 역할 ==차원축소
  • 요인 분석(Factor Analysis) : 수자 및 문자 컬럼을 적절하게 줄여주는 역할

여기서 첫번째인 인코딩 변환을 먼저 알아보도록 하자!!!

 

데이터 전처리는 ML 알고리즘만큼 굉장히 중요한 것이다. ML알고리즘은 데이터에 기바 하고 있기 때문에 어떤 데이터를 입력으로 가지느냐에 따라 결과도 크게 달라지고 성능도 크게 바뀌기 때문이다.

 

일단  처음으로 ML 알고리즘을 적용하기 전에 데이터에 대해 미리 처리해야 할 기본 사항이 있다.

 

결손값 즉 NaN, Null 값이 있어서는 안됩니다. 결손값을 처리하는 방법은 몇몇 알려진 방법이 있습니다.

  • 피처 값 중에 NaN값이 얼마 없다면 그냥 그 피처의 평균값으로 대처하기
  • 피처 값 중에 NaN값이 대부분 이라면 해당 피처는 드롭하기 
  • Null값이 일정 수준 이상이 되는 경우 이 부분이 결정이 힘든 부분이다. 왜냐하면 그 피처의 중요도에 따라 일정수준이라는게 정해지기 때문이다. 이럴 경우 중요도가 높은 피처인데 Null을 단순히 피처의 평균값으로 대채해서 예측 왜곡이 심할 수 있는 문제가 생긴다면 상세히 검토해서 더 정밀한 대체 값으로 바꾸어야 한다.

사이킷런의 머신러닝 알고리즘은 무자열 값을 입력값으로 허용하지 않는다 그렇기에 모든 문자열 값은 머신러닝에 피처데이터로 쓰기 위해선 숫자 형으로 인코딩을 해야 한다. 

문자열 피처는 일반적으로 카테고리형 피처와 텍스형 피처를 의미

 

예를 들어보자!

 

피처데이터 중에 주민번호나 단순 문자열 아이디와 같은 경우 인코딩을 하지 않고 삭제하는게 더 좋다. 이러한 식벽자 피처는 단순히 데이터 로우를 식별하는 용도로 사용되기에 특정한 패턴을 찾을 수 없다. 여기서 머신러닝은 피처 데이터 간의 패턴을 파악해서 예측을 하는 프로세스인데 그러한 패턴이 없기에 예측에 중요한 요소가 될 수 없으며 알고리즘을 오히려 복잡하게 만들고 예측 성능을 떨어뜨린다.

 

여기서 알아두어야 할점!!!

머신러닝은 피처 데이터 간의 패턴을 파악해서 예측을 하는 프로세스인데 그러한 패턴이 없다는 것은 예측에 중요한 요소가 되지 않으며 알고리즘의 성능을 떨어뜨리기에 삭제 해야한다!!!  꼭 기억하기!!!

 

데이터 인코딩

머신러닝을 위한 대표적인 인코딩 방식은 레이블 인코딩원-핫 인코딩이 있다.

 

레이블 인코딩(Label encoding)

레이블 인코딩은 레이블 데이터(종속변수)의 문자열 피처인 카테고리 피처를 코드형 숫자 값으로 변환하는 것이다.

사이킷런의 공식문서에서는 레이블 인코딩은 종속변수인 레이블 데이터에 대하여 인코딩을 하라고 권장 하고 있다. 그렇기에 LabelEncoder을 사용할 때 입력값이 1차원 배열로 기대되는 것이 아마도 이것 때문일거라고 생각된다.

 

그렇기에 독립변수 피처 데이터의 카테고리형 문자열 데이터 인코딩 방법은 따로 작성해 놓았다.(OrdinaryEncoder)

https://srilankakim66.tistory.com/37

 

OrdinalEncoder (LabelEncoder vs OrdinalEncoder)

머신러닝을 공부하면서 문자열 데이터를 숫자형으로 바꾸는 사이킷런에서 제공하는 인코딩은 LabelEncoder만 쓰면 된다고 생각했다. 하지만 찾아보니 LabelEncoder는 레이블 데이터 즉 우리가 원하는

srilankakim66.tistory.com

 

이해를 돕기 위해 예를 들어보자

상품 데이터의 상품 구분이 TV, 냉장고, 전자레인지, 컴퓨터, 선풍기, 믹서 값으로 돼 있다면 이것을 레이블 인코딩을 통해  TV: 1, 냉장고: 2, 전자레인지: 3, 컴퓨터: 4, 선풍기: 5, 믹서: 6 과 같이 수치형 데이터 값으로 변환하는 것이다.

여기서 주의할 점은 '01', '02'와 같은 데이터도 문자열이기에 숫자형 값으로 변환해야 한다.

 

코드를 살펴보자!!

사이킷런에서는 레이블 인코딩을 위한 클래스를 지원한다. LabelEncoder 클래스를 사용하면 된다.

from sklearn.preprocessing import LabelEncoder

items=['TV','냉장고','전자레인지','컴퓨터','선풍기','선풍기','믹서','믹서']

# LabelEncoder를 객체 생성, fit, transform으로 Label 인코딩 수행
# 종속변수에 적용할 때 사용!!!
# 수치 예측 할 때, LabelEncoder 사용하지 않음 why? 이미 수치로 나와있기 때문에
# 범주 예측 할 때 labelencoder를 사용 예시) 양성/음성/잘 모르겠음 이런 데이터를 바꿀때 LabelEncoder를 사용
encoder = LabelEncoder()
encoder.fit(items)
labels = encoder.transform(items)
labels
array([0, 1, 4, 5, 3, 3, 2, 2])
 
결과값으로 문자열 피처 데이터가 레이블 인코딩을 통해 숫자형 피처 데이터로 변환된것을 볼 수 있다.

 

여기서 한가지 볼 수 있는것은 우리가 예시를 들었던 문자열 피처의 사이즈는 별로 크지 않기 때문에 금방 숫자형데이터랑 문자열 데이터를 대조해서 볼 수 있는데 문자열 피처의 사이즈가 많아지면은 어떤 숫자가 어떤 문자열 데이터를 의미하는 지 파악하기가 힘들다.

그렇기에 LabelEncoder 객체는 classes_ 라는 속성값으로 어떤 데이터 피처가 숫자로 변환되었는지 저장하게 된다.

 

encoder.classes_
array(['TV', '냉장고', '믹서', '선풍기', '전자레인지', '컴퓨터'], dtype='<U5')

 

또한 문자열 피처 데이터를 수치형 데이터로 인코딩 한것을 다시 디코딩 또한 할 수 있다.

그 때 사용하는 메서드는 inverse_transform( )이라는 메서드 이다.

encoder.inverse_transform([4,5,2]) # ==> 인코딩한 값을 다시 디코딩 하는 방법이다.
array(['전자레인지', '컴퓨터', '믹서'], dtype='<U5')

 

상품 데이터가 상품 분류, 가격 두 개의 속성으로 돼 있을 때 상품 분류를 레이블 인코딩하면 다음과 같이 변환된다.

 

레이블 인코딩은 문자열 값을 숫자형 카테고리 값으로 변환한다. 하지만 레이블 인코딩이 일괄적인 숫자 값으로 변환이 되면 몇몇 ML 알고리즘에 이를 적용할 경우 예측 성능이 떨어지는 경우가 발생하는 것을 볼 수 있다.

 

이러한 경우는 숫자 값의 크고 작음에 대한 특성이 작용하기 때문이다. 특정 ML알고리즘에서 1보다 2가 더 큰값이므로 숫자가 클수록 가중치가 더 부여되거나 더 중요하게 인식 할 가능성이 발생하기도 한다.

 

 

레이블 인코딩을 통한 숫자 변환값은 단순 코드이지 숫자 값에 따른 순서나 중요도로 인식돼서는 안된다.

이러한 특성 때문에 레이블 인코딩은 선형회귀와 같은 ML 알고리즘에는 적용하지 않아야 한다.

트리계열의 ML알고리즘은 숫자의 이러한 특성을 반영하지 않으므로 레이블 인코딩도 별문제가 없다.

 

 

정리해 보자면 레이블 인코딩을 통한 숫자 변환값은 단순 코드이지 숫자 값에 따른 순서나 중요도로 인식돼서는 안된다.

  • 선형회귀와 같은 ML 알고리즘에는 적용하면 안된다.
  • 트리 계열의 ML 알고리즘은 숫자의 이러한 특성을 반영하지 않으므로 레이블 인코딩을 사용해도 문제가 없다.

 

원-핫 인코딩은 레이블 인코딩의 이러한 문제점을 해결하기 위한 인코딩 방식이다.

 

원-핫 인코딩(One-Hot Encoding)

원-핫 인코딩은 피처 값의 유형에 따라 새로운 피처를 추가해 고유 값에 해당하는 칼럼에만 1을 표시하고 나머지 칼럼에는 0을 표시하는 방식이다.

 즉 행 형태로 돼 있는 피처의 고유 값을 열 형태로 차우너을 변환한 뒤, 고유 값에 해당하는 칼럼에만 1을 표시하고 나머지 칼럼에는 0을 표시한다.

 

먼저 원본 데이터는 8개의 레코드로 돼 있으며, 고유 값은 TV, 냉장고, 전자레인지, 컴퓨터, 선풍기, 믹서로 모두 6개 이다.

이 고유값들이 각각 수치형 데이터로 인코딩이 되고, 0부터 5까지 6개의 상품분류 고유 값에 따라 상품 분류 피처를 6개의 상품 분류 고유 값 피처로 변환.

그리고 해당 레코드의 상품분류 칼럼의 해당 상품분류인 값만 1을 부여하고 나머지는 0이 되는 것이다.

 

원-핫 인코딩은 사이킷런에서 OneHotEncoder 클래스로 변환이 가능하며 LabelEncoder와 다르게 약간 주의할 점이 있다.

OneHotEncoder는 입력값으로 2차원 데이터가 필요다는 것과, OneHotEncoder를 이용해 변환한 값이 희소 행렬(Sparse Matric) 형태이므로 이를 다시 toarray( ) 메서드를 이용해서 밀집 행렬(Dense Matrix)로 변환해줘야 하는 것이다.

 

 

코드로 살펴보자!!!

 

from sklearn.preprocessing import OneHotEncoder
import numpy as np

items=['TV','냉장고','전자레인지','컴퓨터','선풍기','선풍기','믹서','믹서']

# 2차원 ndarray로 변환한다.
items = np.array(items).reshape(-1,1) # reshape(-1,1)은 데이터 셋을 1열 기준으로 자동으로 행은 셋팅해라 라는 의미이다.
# reshape()의 ‘-1’이 의미하는 바는, 변경된 배열의 ‘-1’ 위치의 차원은 원래 배열의 길이와 남은 차원으로 부터 추정해서 자동으로 설정해라라는 의미
print(items.shape)

# 원-핫 인코딩을 적용한다.
oh_encoder = OneHotEncoder()
oh_encoder.fit(items)
oh_labels = oh_encoder.transform(items)
oh_labels.toarray()
(8, 1)
array([[1., 0., 0., 0., 0., 0.],
       [0., 1., 0., 0., 0., 0.],
       [0., 0., 0., 0., 1., 0.],
       [0., 0., 0., 0., 0., 1.],
       [0., 0., 0., 1., 0., 0.],
       [0., 0., 0., 1., 0., 0.],
       [0., 0., 1., 0., 0., 0.],
       [0., 0., 1., 0., 0., 0.]])

8개의 레코드와 1개의 칼럼을 가진 원본 데이터가(reshape(-1,1)로 입력을 넣어줌) 8개의 레코드와 6개의 칼럼을 가진 데이터로 변환되었다. 

우리가 이미지로 본 결과와 같이 나온것을 확인 할 수 있다.

원-핫 인코딩의 순서를 표현해주는 이미지이다. 이것을 보면 한층 더 이해가 쉬워질 것이다.

 

 

추가

 

 

하지만 여기서 판다스에는 원-핫 인코딩을 더 쉽게 지원하는 API가 있다. get_dummies( )를 이용하면 된다.

사이킷런의 OneHotEncoder와 다르게 문자열 카테고리 값을 숫자 형으로 변환할 필요 없이 바로 변환 할 수 있다.

 

코드로만 살펴보자!!

 

import pandas as pd

df = pd.DataFrame({'item':['TV','냉장고','전자레인지','컴퓨터','선풍기','선풍기','믹서','믹서'] })
pd.get_dummies(df)

짧은 코드로도 바로 변환 되는 것을 볼 수 있다.

 

여기서 알아야 할점!!!

원-핫 인코딩이나 판다스를 이용한 get_dummies( )는 1차원의 피처 데이터판다스의 테이블로 만들거나 numpy의 배열로 만들어서 처리를 하게 된다. 이렇게 되면 컬럼 수도 늘어나고 데이터의 크기도 더 커지게 되면서 ML의 학습시간이 늘어나는 결과를 초래하게 된다. 이념은 알고 있자!!!