본문 바로가기
AI / DL/엘리스 AI 데이터 분석 트랙

[AI 데이터 분석] 머신러닝 시작하기 02. 데이터 전 처리하기

by bri9htstar 2022. 10. 7.

머신러닝 과정

 

데이터 수집 데이터 분석 및 전 처리 머신러닝 학습 머신러닝 평가
크롤링 또는 DB 데이터를
통하여 데이터 수집
수집한 데이터를 분석하고
머신러닝에 사용할 형태
전 처리
머신러닝 모델을 사용하여
데이터를 학습
학습된 머신러닝 모델을
평가용 데이터를 사용하여
평가

평가에서 잘 안 되거나 전 처리가 잘 안 되면 다시 학습하거나, 분석하거나, 또 다른 데이터를 수집하는 단계로 되돌아간다.

 

데이터 전 처리의 역할

  1.  머신러닝의 입력 형태로 데이터 변환 (특성 엔지니어링)
  2.  결측값 및 이상치를 처리하여 데이터 정제
  3.  학습용 및 평가용 데이터 분리

대부분의 머신러닝 모델은 숫자 데이터를 입력 받는다. 일반적으로 행렬 형태로 입력 받는데, 여러 형태로 존재하는 다양한 데이터를 수치형 자료로 변환해주는 과정이 필요하다. 실제 데이터는 머신러닝 모델이 이해할 수 없는 형태이기 때문이다.

데이터의 결측값과 이상치가 있으면 머신러닝의 성능이 떨어지고, 모델이 입력으로 받지 못하는 경우가 있어 처리해야한다.

학습용과 평가용 데이터가 따로 있기 때문에, 데이터 전 처리 단계에서 준비해줘야 한다. 학습 데이터를 원본 데이터로 모두 사용하게 되면 객관성이 떨어지게 된다. 학습 데이터에 머신러닝이 과하게 초점을 맞추면 새로운 데이터에 대해 좋은 결과를 내지 못 할 수도 있기 때문에 객관성을 확보하기 위해 데이터 구분이 필요하다.

 

범주형 자료 전 처리

범주형 자료는 두 가지로 나뉜다. 범주의 크기가 의미 없다면 명목형 자료, 크기가 의미 있다면 순서형 자료이다.

대표적인 범주형 자료 변환 방식은 다음과 같다.

명목형 자료 순서형 자료
수치 맵핑 방식
더미(Dummy) 기법
수치 맵핑 방식

수치 맵핑 변환은 일반적으로 범주를 0, 1과 같은 수치로 맵핑하는 방법이다. 예를 들어 성별에 male, female과 같은 두 가지 범주가 있다고 하자. male → 0, female → 1로 맵핑하여 바꿔서 나타내는 것이다. (-1, 1), (0, 100) 등 다양한 케이스가 있지만 모델에 따라 성능이 달라질 수 있다. 세 개 이상의 범주가 있을 경우, 수치의 크기 간격을 같게 하여 수치 맵핑한다. 가령 세 개의 범주가 있다고 하면 (0, 1, 2)처럼 말이다.

 

더미(Dummy) 기법은 각 범주를 0 or 1로 변환하는 기법이다. 한 가지 변수 내에서 처리했던 수치 맵핑과 반대이다.

순서형 자료는 수치에 맵핑하여 변환하지만, 수치 간 크기 차이는 커스텀 가능하다. 이 크기 차이는 머신러닝 결과에 영향을 끼칠 수 있다. 어떤 선택지의 차이를 두고자 하자면, 크기 차이로 사용자의 의도를 표현할 수 있는 것이다.

# 명목형 자료 변환 - 수치 맵핑
import pandas as pd

# 데이터를 읽어옵니다.
titanic = pd.read_csv('./data/titanic.csv')
print('변환 전: \n',titanic['Sex'].head())

"""
1. replace를 사용하여 male -> 0, female -> 1로 변환합니다.
"""
titanic = titanic.replace({'male': 0, 'female': 1})

# 변환한 성별 데이터를 출력합니다.
print('\n변환 후: \n',titanic['Sex'].head())
# 명목형 자료 변환 - 더미 방식
import pandas as pd

# 데이터를 읽어옵니다.
titanic = pd.read_csv('./data/titanic.csv')
print('변환 전: \n',titanic['Embarked'].head())

"""
1. get_dummies를 사용하여 변환합니다.
"""
dummies = pd.get_dummies(titanic[['Embarked']])

# 변환한 Embarked 데이터를 출력합니다.
print('\n변환 후: \n',dummies.head())

 

수치형 자료 전 처리

머신러닝의 입력으로 바로 사용할 수 있으나, 모델의 성능을 높이기 위해서 데이터 변환이 필요하다.

대표적으로 스케일링범주화 두 가지 방법이 있다.

스케일링(Scaling)

변수 값의 범위 및 크기를 변환하는 방식이다. 변수(feature) 간의 범위가 차이가 나면 사용한다.

1) 정규화(Normalization) : 변수 X를 정규화한 값 X'. 0에서 1 사이 값으로 변환이 된다.

정규화 식

정규화를 통해 상대적으로 매우 큰 값이나 작은 값이 주는 영향을 줄일 수 있다.

import pandas as pd

"""
1. 정규화를 수행하는 함수를 구현합니다.
"""
def normal(data):
    
    data = (data-data.min())/(data.max()-data.min())
    
    return data

# 데이터를 읽어옵니다.
titanic = pd.read_csv('./data/titanic.csv')
print('변환 전: \n',titanic['Fare'].head())

"""
0	7.2500
1	71.2833
2	7.9250
3	53.1000
4	8.0500
"""

# normal 함수를 사용하여 정규화합니다.
Fare = normal(titanic['Fare'])

# 변환한 Fare 데이터를 출력합니다.
print('\n변환 후: \n',Fare.head())

"""
0	0.014151
1	0.139136
2	0.015469
3	0.103644
4	0.015713
"""

 

2) 표준화(Standardization) : 변수 X를 표준화한 값 X'. 평균이 0이고 표준편차가 1인 정규분포로 대략적으로 변환한다.

표준화 식

import pandas as pd

"""
1. 표준화를 수행하는 함수를 구현합니다.
"""
def standard(data):
    
    data = (data-data.mean())/data.std()
    
    return data
    
# 데이터를 읽어옵니다.
titanic = pd.read_csv('./data/titanic.csv')
print('변환 전: \n',titanic['Fare'].head())

"""
0	7.2500
1	71.2833
2	7.9250
3	53.1000
4	8.0500
"""

# standard 함수를 사용하여 표준화합니다.
Fare = standard(titanic['Fare'])

# 변환한 Fare 데이터를 출력합니다.
print('\n변환 후: \n',Fare.head())

"""
0	-0.502163
1	 0.786404
2	-0.488580
3	 0.420494
4	-0.486064
"""

범주화

변수의 값보다 범주가 중요한 경우에 사용한다. 예시로 시험 점수 데이터가 주어졌고, 시험 점수에 대해서 예측하는 머신러닝을 만든다고 해보자. 주어진 문제는 점수를 예측하는 것이 아닌, 내 점수가 평균 이상인지 아닌지를 판별하는 것이라고 한다. 이 때 높은지 낮은지에 대한 범주만 예측하는 것이라 시험 점수는 필요 없는 정보가 된다. 평균 이상을 1로, 평균 이하를 0으로 변환하면 머신러닝 목적에 맞게 전 처리하게 된다.

범주화 예시

 

데이터 정제 및 분리하기

결측값(Missing data) 처리하기

일반적인 머신러닝 모델의 입력값으로 결측값을 사용할 수 없다. 따라서 Null, None, Nan 등의 결측값을 처리해야한다.

대표적인 결측 값 처리 방식은 다음과 같다.

  1. 결측값이 존재하는 샘플 삭제
  2. 결측값이 많이 존재하는 변수 삭제
  3. 결측값을 다른 값으로 대체

결측값을 다른 값으로 대체할 때 보통 평균이나 중앙값을 쓰거나 대체값을 머신러닝으로 예측하기도 한다.

import pandas as pd

# 데이터를 읽어옵니다.
titanic = pd.read_csv('./data/titanic.csv')
# 변수 별 데이터 수를 확인하여 결측 값이 어디에 많은지 확인합니다.
print(titanic.info(),'\n')

"""
1. Cabin 변수(columns)를 제거합니다.
"""
titanic_1 = titanic.drop(columns=['Cabin'])
# Cabin 변수를 제거 후 결측값이 어디에 남아 있는지 확인합니다.
print('Cabin 변수 제거')
print(titanic_1.info(),'\n')

"""
2. 결측값이 존재하는 샘플을 제거합니다.
"""
titanic_2 = titanic_1.dropna()
# 결측값이 존재하는지 확인합니다.
print('결측값이 존재하는 샘플 제거')
print(titanic_2.info())

 

이상치(Outlier) 처리하기

이상치가 있으면 모델의 성능을 저하할 수 있다. 일반적으로 전 처리 과정에서 제거하며 어떤 값이 이상치인지 판단하는 기준이 중요하다. 이상치 판단 기준 방법을 크게 이렇게 나타낸다.

  1. 통계 지표(카이제곱 검정, IQR 지표 등)를 사용하여 판단
  2. 데이터 분포를 보고 직접 (눈으로) 판단
  3. 머신러닝 기법을 사용하여 이상치 분류

이상치를 분류하는 것 자체를 위한 머신러닝도 있어서 전 처리 과정임에도 불구하고 머신러닝을 통해 이상치를 제거할 수 있다.

import pandas as pd
import numpy as np

# 데이터를 읽어옵니다.
titanic = pd.read_csv('./data/titanic.csv')

# Cabin 변수를 제거합니다.
titanic_1 = titanic.drop(columns=['Cabin'])

# 결측값이 존재하는 샘플 제거합니다.
titanic_2 = titanic_1.dropna()

# (Age 값 - 내림 Age 값) 0 보다 크다면 소수점을 갖는 데이터로 분류합니다.
outlier = titanic_2[titanic_2['Age']-np.floor(titanic_2['Age']) > 0 ]['Age']

print('소수점을 갖는 Age 변수 이상치')
print(outlier)
print('이상치 처리 전 샘플 개수: %d' %(len(titanic_2)))
print('이상치 개수: %d' %(len(outlier)))

"""
1. 이상치를 처리합니다
 -> np.floor() 를 사용하면 입력값의 소수점을 제외한 정수 부분을 얻을 수 있습니다.
 -> titanic_2['Age'] 에서 np.floor(titanic_2['Age']) 을 뺀 값이 0이면 정수입니다.
 -> 즉, titanic_2 에서 titanic_2['Age']-np.floor(titanic_2['Age']) 값이 0 인 샘플만 titanic_3에 저장합니다.
"""
titanic_3 = titanic_2[titanic_2['Age']-np.floor(titanic_2['Age']) == 0 ] # _ 안을 채우세요.
print('이상치 처리 후 샘플 개수: %d' %(len(titanic_3)))

위 코드 실행 결과

 

데이터 분리

머신러닝 모델을 평가하기 위해서는 학습에 사용되지 않은 평가용 데이터가 필요하다. 약 7:3 ~ 8:2 비율로 학습용, 평가용 데이터를 분리한다. 학습용 데이터가 충분히 존재해야 머신러닝 성능이 확보하기 때문에 학습용을 많이 잡는다.

 

지도 학습의 경우는 feature 데이터와 label 데이터를 분리하여 저장한다.

Feature 데이터 : label을 예측하기 위한 입력 값

Label 데이터 : 예측해야 할 대상이 되는 데이터

예를 들어, 공부시간과 시험 점수 데이터가 주어져 있고 공부시간 대비 시험점수를 예측하고자 하면, 시험점수는 우리가 예측하고자 하는 데이터이니 Label 데이터가 되는 것이고, 공부 시간은 Feature 데이터가 되는 것이다.

import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split

# 데이터를 읽어옵니다.
titanic = pd.read_csv('./data/titanic.csv')

# Cabin 변수를 제거합니다.
titanic_1 = titanic.drop(columns=['Cabin'])

# 결측값이 존재하는 샘플 제거합니다.
titanic_2 = titanic_1.dropna()

# 이상치를 처리합니다.
titanic_3 = titanic_2[titanic_2['Age']-np.floor(titanic_2['Age']) == 0 ]
print('전체 샘플 데이터 개수: %d' %(len(titanic_3)))

"""
1. feature 데이터와 label 데이터를 분리합니다.
"""
X = titanic_3.drop(columns=['Survived']) # Survived 변수를 제거하여 X에 저장합니다.
y = titanic_3['Survived'] # Survived 변수를 y에 저장합니다.
print('X 데이터 개수: %d' %(len(X)))
print('y 데이터 개수: %d' %(len(y)))

"""
2. X,y 데이터를 학습용, 평가용 데이터로 분리합니다.
"""
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)

# 분리한 데이터의 개수를 출력합니다.
print('학습용 데이터 개수: %d' %(len(X_train)))
print('평가용 데이터 개수: %d' %(len(X_test)))

위 코드 실행 결과

댓글