[AI 이론] 데이터 전 처리 한방에 끝내기 (Data preprocessing feat.titanic)

    머신러닝

    이번 글에서는 데이터 전 처리의 관점에서만 머신러닝을 이야기하고 머신러닝의 소개는 다음 기회에 하겠습니다.

    머신러닝 과정 이해하기

    머신러닝의 전체적인 과정을 정리하면 위 그림과 같아요..
    데이터를 수집하고, 이를 분석한 후 머신러닝에 사용하기 적합한 형태로 전 처리를 합니다.
    전 처리한 데이터를 이용해 머신러닝을 학습해 모델을 만들고, 이 모델을 평가용 데이터를 활용해 평가하게 됩니다.
    모델의 성능이 좋지 않으면 앞의 과정 중 일부를 다시 진행합니다.


    데이터 전 처리

    데이터의 전 처리에는 pandas, numpy, matplotlib 등의 python 라이브러리 등이 사용됩니다.
    라이브러리에 대한 소개는 python 카테고리(링크)에 글이 있으니 읽어 보시면 도움이 되실 거예요.

     

    'Software/Python' 카테고리의 글 목록

     

    kay-dev.tistory.com

    그리고 이번 글의 예시는 kaggle의 titanic 문제를 활용했습니다.
    보다 자세한 과정을 보고 싶으시면 "kay's Github kaggle prac(링크)"를 참고하세요.

    역할

    왜 데이터 전 처리 과정이 필요할까요?
    데이터 전 처리의 역할은 크게 3가지가 있어요.

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

    데이터 변환

    먼저 데이터 변환부터 자세히 볼게요.

    출처: elice

    대부분의 머신러닝 모델은 숫자형 데이터를 입력으로 받습니다. 특히 행렬의 형태로 입력받죠.
    하지만 실제 데이터는 다양한 형태로 존재합니다. 이미지, 자연어, 범주형, 시계열 등등 말이죠.
    감이 좀 오시나요?
    바로 이때 데이터 변환이 필요합니다.  전 처리를 통해 머신러닝이 이해할 수 있는 수치형 자료로 변환해주는 역할이죠.

    데이터 정제

    다음은 데이터 정제입니다.

    출처: elice

    위 사진은 유명한 Kaggle의 타이타닉 문제의 data네요.(링크)

     

    Titanic - Machine Learning from Disaster | Kaggle

     

    www.kaggle.com

    앞서 데이터 정제는 결측 값과 이상치를 처리한다고 소개했죠. 
    Age 중 35.3이라는 수치는 우리가 생각하는 일반적인 나이로 보기는 힘들죠. 이런 값이 이상치입니다.
    그리고 Cabin의 NaN 은 결측 값으로 데이터가 없다고 생각하시면 됩니다.
    이런 값들은 머신러닝에 필요치 않은 값들이니까 정제가 필요해요.

    데이터 분리

    마지막은 데이터 분리입니다.

    출처: elice

    데이터 분리는 데이터를 학습용과 평가용으로 분리하는 전 처리 과정입니다.
    이를 분리하는 이유는 모델을 보다 "객관적으로 평가하기 위해서"입니다.
    학습 데이터로 평가를 진행하면 모델의 성능은 좋은 것으로 보이겠지만 조금 다른 데이터가 들어왔을 때는 그 성능을 장담할 수 없기 때문이죠.

     


    범주형 자료 전 처리

    위에서 얘기한 kaggle의 타이타닉 생존자 Data를 바탕으로 설명할게요.

    데이터 확인하기

    출처: kaggle

    데이터를 전 처리하기 전 어떤 데이터가 있는지 확인하는 과정이 필요합니다.
    그래야 활용성이 높은 데이터를 예상해볼 수 있고, 전 처리의 방향이 대략적으로 정해지죠.
    위 사진은 titanic Data의 일부입니다. 맨 윗줄이 column 이죠.

    범주형 자료 몇 가지만 확인하면 아래처럼 분류가 됩니다.

    • 범주형 자료
      • 순서형 자료: Pclass
      • 명목형 자료: PassengerId, Survived, Name, Sex, Ticket, Cabin, Embarked... 

    변환 방식

    이제 데이터를 변환하는 방식을 볼게요.
    대표적인 범주형 자료의 변환 방식은 아래와 같습니다.

    • 명목형 자료: 수치 매핑 방식, 더미 기법
    • 순서형 자료: 수치 매핑 방식

    명목형 자료 변환 - 수치 매핑

    아래 Data는 전 처리를 거치지 않은 최초의 Data입니다. 비교를 위한 기준으로 볼게요.

    일반적으로 2개의 범주를 갖는 데이터는 0,1로 매핑하는 방식을 사용합니다.
    물론 -1,1이나 0,100으로 매핑할 수도 있지만 모델이 따라 성능이 달라질 수 있어요.

    3개 이상의 범주는 수치의 크기 간격을 같게 수치 매핑을 진행합니다.
    예를 들면 0,1,2,3,...처럼 말이죠.

    두 경우를 한 번에 볼 예시가 있어요.

    import pandas as pd
    
    titanic = pd.read_csv("data\\titanic\\train.csv")
    titanic_preprocessing = titanic.replace({'male':0, 'female':1, 'S':0, 'Q':1, "C":2})

    기존 Data와 비교하면 변환된 Data들이 보이죠?

    명목형 자료 변환 - 더미 기법

    더미 기법은 각 범주를 0 or 1로 변환하는 방식입니다.

    "음.. 그럼 수치 매핑과 같은 건가요?"🤔

    아니요. 차이가 있습니다. 예시를 보면서 설명해볼게요.

    dummies_sex = pd.get_dummies(titanic[['Sex']])
    dummies_embarked = pd.get_dummies(titanic[['Embarked']])

    데이터를 보면 못 보던 column들이 생겼습니다.
    원래 Sex, Embarked로 각각 1개의 Column들로 존재하던 Data 가 2개, 3개의 Column으로 나눠져 있네요.
    이 점이 차이점입니다.
    수치 매핑은 하나의 Column에 (0,1) 혹은 (0,1,2,3,...)의 Data로 변환해서 입력해주는 방식이라면,
    더미 기법은 기존 Column에서 범주의 수만큼 Column을 추가하고 해당하는 곳엔 "1"을 그렇지 않은 곳엔 "0"의 데이터를 입력하는 방식입니다.

    순서형 자료 변환 - 수치 매핑 기법

    명목형 자료에서 수치 매핑을 봤죠. 비슷합니다.
    차이점은 3개 이상의 범주일 때 나오는데요, 수치 간 크기 차이를 조정할 수 있다는 점입니다.
    물론 그 차이는 머신러닝의 모델에 영향을 줍니다.


    수치형 자료 전처리

    데이터 확인하기

    수치형 데이터는 크기를 갖는 값으로 이루어져 있죠.
    사실 머신러닝의 입력으로 사용할 수는 있지만 전 처리 과정을 거치죠. 그 이유는 모델의 성능을 높이기 위함입니다.

    변환 방식

    수치형 데이터의 대표적인 변환 방식은 아래와 같아요.

    • 스케일링 (Scaling): 변수 값의 범위 및 크기를 변환하는 방식으로 변수 간의 범위가 차이 나면 사용하기 좋아요.
      • 정규화 (Normalization)
      • 표준화 (Standardization)
    • 범주화: 변수값보다 범주가 더 중요한 경우 사용해요.

    이번에 비교에 사용할 기준은 위 사진입니다.
    수치형 Data는 4가지가 보이지만 예시에서는 Fare 만 사용할게요.

    스케일링(Scaling) - 정규화(Normalization)

    정규화는 위의 공식을 통해 진행됩니다.
    정규화를 하면 Data가 0~1의 값을 가지게 됩니다. 가장 큰 값은 '1', 가장 작은 값은 '0'을 갖게 되는 거죠.
    정규화는 왜 하는 걸까요?
    특정 머신러닝 기법은 값이 큰 feature에 영향을 많이 받는 경우가 있어요.
    따라서 모든 feature들이 비슷한 영향을 주도록 만들어 주는 거죠!

    직접 해보면서 이해해볼게요.
    소개한 공식을 함수로 구현하는 과정이 있으니 참고하세요.

    # 정규화 수행 함수 구현
    def normalization(data):
        data = (data - data.min())/(data.max() - data.min())
        return data
        
    fare = normalization(titanic['Fare'])

     

    스케일링(Scaling) - 표준화(Standardization)

    표준화는 위의 공식을 통해 진행됩니다.
    Data가 정규분포를 갖고 있을 때 평균을 "0", 표준편차를 "1"인 표준 정규분포로 변환하는 것입니다.
    표준화도 특정 feature가 영향을 많이 주는 것을 방지하기 위해 사용해요.

    표준화도 직접 해보죠.
    소개한 공식을 함수로 구현하는 과정이 있으니 참고하세요.

    # 표준화 함수 구현
    def standardization(data):
        data = (data - data.mean())/data.std()
        return data
        
    fare = standardization(titanic['Fare'])

    범주화

    범주화는 Data의 값보다 범주가 중요한 경우 사용한다고 했어요.
    예를 통해서 알아볼게요.

    시험을 봤는데, 각각 평균보다 높은지 낮은지만 확인한다면 어떨까요?
    Data의 값에 해당하는 점수 자체보다는 평균 이상, 이하인 범주가 필요하겠죠.
    이럴 때 사용하는 방법입니다.

     


    데이터 정제 및 분리하기

    결측 값 (Missing Data)

    결측 값은 Null, None, NaN 등으로 표현됩니다.
    일반적으로 머신러닝 모델은 입력으로 결측 값을 사용할 수 없어요.
    우리는 이런 결측 값들을 처리할 줄 알아야 한다는 뜻이죠!!

    결측값 찾기

    결측 값을 처리하려면 결측값을 찾아야 합니다.
    방법은 다양하지만 몇 가지만 소개할게요.

    먼저 DataFrame의 전반적인 정보를 함께 표현하는 방식입니다.
    많은 정보들이 있지만 결측 값에 대한 내용만 보겠습니다.
    아래 그림처럼 전체 Index의 수가 나오고, Column별로 Null이 아닌 Data를 Count 해서 제공합니다.
    즉, 전체 Index 보다 Null이 아닌 Data의 수가 적다면 결측 값이 있는 Column이라는 의미죠.

    titanic.info()

    이번엔 좀 더 직관적으로 찾을 수 있는 방법들입니다.
    결측 값의 위치마다 표시되거나, 결측값의 수를 카운트하는 방법이죠.

    titanic.isnull()            # 결측값은 True를 그 외는 False 를 반환
    
    titanic.isnull.sum()        # 각 Column 별로 결측값의 수를 카운트

     

    결측값 처리

    이제 대표적인 결측값 처리 방식을 소개할게요.
    이번에도 비교를 위한 기준은 titanic data입니다.

    1️⃣ 결측 값이 존재하는 샘플 삭제
    쉽게 말해 결측값이 있는 Data의 row(행) 하나를 삭제한다는 뜻입니다.
    아래 예시를 보면 결측값이 있던 index들이 사라진 것을 볼 수 있습니다. (2,4,5,7,... , 888이 안 보이네요.)

    titanic_dropna = titanic.dropna()

    2️⃣ 결측 값이 많이 존재하는 변수 삭제
    말 그대로 결측값이 많은 column(열)을 지운다는 뜻입니다.
    아래 예시를 보면 "Cabin" column이 사라졌네요.

    titanic_drop = titanic.drop(columns=["Cabin"])

    3️⃣ 결측 값을 다른 값으로 대체
    결측 값이 많지 않은 column의 경우 해당 column(열)의 다른 Data를 바탕으로 값을 대체하는 방법입니다.
    평균값, 중간값, 머신러닝으로 예측한 값 등을 사용할 수 있어요.
    아래 예시는 Age column의 결측 값은 column 평균값으로, Cabin column의 결측 값을 C000로 대체했습니다.
    (실제 활용하는 것을 고려하지 않은 예시를 위한 대체입니다.)

    titanic_fillna = titanic.fillna({'Age':titanic['Age'].mean(), 'Cabin':'C000'})

     

    이상치 (Outlier)

    이상치는 머신러닝의 입력으로 사용될 수는 있지만 모델의 성능을 저하시켜요.
    그래서 전 처리 과정에서 제거해줍니다.
    이때, 어떤 값이 이상치인지 판단하는 기준을 세우는 것이 중요해요.

    이상치 판단 기준

    • 통계 지표를 사용해서 판단하기: 카이제곱 검정, IQR 지표 등
    • 데이터 분포를 통해 직접 판단하기: Data 자체, 도수분포표, 시각화 자료 등
    • 머신러닝 기법을 사용해 이상치 분류하기: 이상치 처리를 위한 머신러닝 모델을 활용

    이제 이상치를 확인하고, 처리하는 예시를 보겠습니다.
    titanic data의 Age column에서 정수가 아닌 값들을 이상치로 보겠습니다.

    import numpy as np
    import pandas as pd
    
    titanic_outlier = titanic[titanic['Age'] - np.floor(titanic['Age']) > 0]['Age']  # 이상치 확인
    print(titanic_outlier.head(5))

    np.floor라는 새로운 함수가 보이네요.
    매개변수에서 소수점을 제외한 정수부 분만 return 하는 함수입니다.
    즉, 처음부터 정수인 Data에는 "0"을 return 한다는 말이죠. 이 성질을 활용해서 이상치를 찾았습니다.

    아래도 같은 원리로 이상치가 확인된 column을 지우는 방식으로 처리를 해봤어요.
    위에서 이상치로 확인된 Index들이 사라졌네요.

    titanic_outlier2 = titanic[titanic['Age'] - np.floor(titanic['Age']) == 0]
    titanic_outlier2[87:125]

    데이터 분리하기

    머신러닝 과정을 소개할 때 간단히 이야기한 내용이죠.
    데이터 분리는 머신러닝 모델을 평가하기 위해 학습에 사용하지 않은 평가용 데이터를 확보하는 과정입니다.
    이를 분리하는 이유는 모델을 보다 "객관적으로 평가하기 위해서"입니다.
    학습 데이터로 평가를 진행하면 모델의 성능은 좋은 것으로 보이겠지만 조금 다른 데이터가 들어왔을 때는 그 성능을 장담할 수 없기 때문이죠.
    일반적으로 학습용:평가용을 7:3, 8:2의 비율로 분리합니다.

    지도 학습 데이터 분리

    지도 학습은 Feature Data와 Label Data를 분리합니다.

    Feature Data: Label Data를 예측하기 위한 입력값
    Label Data: 예측해야 할 대상이 되는 Data

    다시 titanic data입니다.
    kaggle의 이 문제는 Data를 통해 생존자 예측하는 model을 만드는 것이 목표입니다.
    그럼 예측할 대상인 생존을 나타내는 Survived는 Label Data를 의미하겠네요.
    그리고 나머지 Data들이 Feature Data가 됩니다.

    이제 마지막 예시입니다.
    과정은 크게 2개입니다. Label과 Feature를 분리하고, 분리된 Data들을 각각 학습용과 평가용으로 나눌게요.
    train과 test를 나눌 때는 sklearn이라는 라이브러리를 사용했어요. 비율은 7:3입니다!

    # feature, label 분리
    X = titanic.drop(columns=['Survived'])
    y = titanic['Survived']
    
    print("feature:", (len(X)))
    print("label:", len(y))
    
    '''
    feature: 891
    label: 891
    '''
    
    # train, test 분리
    from sklearn.model_selection import train_test_split
    
    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)
    print("train:", len(X_train))
    print("test:", len(X_test))
    
    '''
    train: 623
    test: 268
    '''


    여기까지 "데이터 전 처리"에 대해 알아봤어요.
    다음에는 "선형 회귀"를 알아보겠습니다.
    글이 도움이 되셨다면 공감 버튼을 눌러주세요. 😊

    댓글

    Designed by JB FACTORY