본문 바로가기
Study/ML

3. 데이터의 전처리 (Pre-processing)

by 까망우동 2023. 5. 27.
반응형

Index

    활용하고자 하는 데이터를 EDA 등의 과정을 통해 탐색하고 이해했다면, 이 데이터를 기계학습 모델에 사용하기 적합하도록 데이터의 질을 높여주는 과정이 전처리(Pre-processing) 이다.

    기본적인 전처리 과정에는 Noise 혹은 Null 값처리, 인코딩(Encoding), 스케일링(Scaling) 및 정규화(Normalization) 과정 등이 있다. 

    PCA 등의 차원축소 기법, Feature Selection 및 Extraction 등에 대해서는 후속 포스팅에서 정리하겠다. 

     

    1. 데이터 인코딩 (Encoding) 

    데이터 인코딩은 기계학습에서 활용할 알고리즘에 적합한 형태로 데이터를 변환하는 것을 의미하며, 대게는 문자형/범주형 데이터를 숫자형으로 변환하는 과정을 말한다. 

      1-1.  레이블 인코딩

    • 카테고리(범주형) 피쳐를 코드형 숫자로 변환하는 것 
    • 숫자로 변환하다 보니 '크고 작음' 으로 인한 우선순위 발생 등의 부작용이 생김. 따라서 회귀모델보다는 트리형태 알고리즘에 주로 사용한다. 
    • LabelEncoder 클래스를 활용하여 인코딩, 디코딩을 수행할 수 있다. 
    from sklearn.preprocessing import LabelEncoder
    
    items=['TV','냉장고','컴퓨터','믹서','믹서','믹서','핸드폰']
    encoder = LabelEncoder()
    
    # 피처값을 인코딩할때 fit 과 transform 을 사용 
    encoder.fit(items)
    labels = encoder.transform(items)
    # labels = encoder.fit_transform(items)  이렇게 한번에 쓸수도 있음 
    print('인코딩 변환값:',labels)
    
    # 인코딩 후에는 classes_ 속성값에 0부터 순서대로 저장됨 
    print('인코딩 클래스:',encoder.classes_)
    
    # 디코딩 
    print('디코딩:',encoder.inverse_transform([4,1,2]))

     

      1-2.  원-핫 인코딩 (OneHotEncoder)

    • 레이블 인코딩의 숫자특성으로 인한 문제점 해결
    • 행 형태의 피쳐 고유값을 열 형태로 전환한뒤, 고유값만 1처리하여 2차원 메트릭스로 만든다. 
    • 변환된 값은 희소행렬(Sparse Matrix) 이기 때문에, toarray() 매서드를 이용해 밀집행렬(Dense Matrix) 변환 해줘야함
    • 사이킷런에 OneHotEncoder 클래스가 있지만 fit_transform 하고 밀집행렬 변환해주기까지 번거롭기에, 아래 코드와 같이 주로 Pandas 모듈의 get_dummies 라는 API 를 활용한다. 
    • get_dummies로 반환되는 값은 위에 그림과 같은 모습의 DataFrame 이다. 
    # Pandas 에서 간단하게 OneHotEncoder 를 활용하는 API 소개 
    # get_dummies
    import pandas as pd
    df = pd.DataFrame({'item':['TV','냉장고','컴퓨터','믹서','믹서','믹서','핸드폰']})
    pd.get_dummies(df)

     

    2. 피쳐 스케일링 (Feature Scaling) - 표준화, 정규화

    • 피쳐간에 상대적인 값 차이가 너무 크다면, 피쳐의 크기가 피쳐 중요도가 될 수 있기에 일정한 크기로 맞추는 작업
    • 대표적인 두 방법 ①표준화(Standardization)는 N(0,1) 정규화를 하는것, ②정규화(Normalization)는 모두 같은 크기/범위로 맞춰주는 것 
    • ★주의할 점 : Scaling 시에는 fit 은 학습데이터에만, 테스트 데이터에는 transform만 해야한다. fit 은 스케일링을 위한 기준정보(평균,최대/최소 등)를 설정하는 과정이기 때문에, 학습/테스트 따로하면 스켈링 기준이 다르게 된다.→ 따라서, 스케일링은 가능하면 data split 이전에 하는게 맞다 
    • 회귀문제에서는 타겟의 분포가 Skewed 되어있는지 꼭 확인하고 정규화 해줘야한다. (Skewness는 아래와 같이 Histogram으로 확인할 수도 있다) 

    fig, axes = plt.subplots(1, 2, figsize=(10, 2))
    sns.histplot(house_df['SalePrice'], kde=True, ax=axes[0])

    # 로그 변환
    log_SalePrice = np.log1p(house_df['SalePrice'])
    sns.histplot(log_SalePrice, kde=True, ax=axes[1])
    • 여러 종류의 피쳐중에서 숫자형이면서 Skewed 되어있는 피쳐들 위주로 스케일링하기. Skew 의 정도를 일일히 histogram 볼 수 없으니 아래와 같이 skew 매서드를 활용할 수도 있다. 
    from scipy.stats import skew

    # object가 아닌 숫자형 피처의 칼럼 index 객체 추출.
    features_index = house_df.dtypes[house_df.dtypes != 'object'].index
    # house_df에 칼럼 index를 [ ]로 입력하면 해당하는 칼럼 데이터 세트 반환. apply lambda로 skew( ) 호출
    skew_features = house_df[features_index].apply(lambda x : skew(x))
    # skew(왜곡) 정도가 1 이상인 칼럼만 추출.
    skew_features_top = skew_features[skew_features > 1]
    print(skew_features_top.sort_values(ascending=False)[:5])
     
    house_df[skew_features_top.index] = np.log1p(house_df[skew_features_top.index])
     

     

     2-1.  StandardScaler

    • 사이킷런의 StandardScalar 클래스는 평균이 0, 분산이 1 인 정규분포를 따르도록 변환해준다.  
    • SVM,선형회귀,로지스틱회귀는 데이터가 정규분포를 띈다는 가정하에 구현되었기에 표준화가 매우 중요하다. 
    • 대부분의 회귀문제에서는 타겟이 Skewed 되있다면 로그 정규화를 진행한다. 
    from sklearn.preprocessing import StandardScaler
    
    scaler = StandardScaler()
    scaler.fit(iris_df)
    # scaler 로 transform 하면 Numpy 의 ndarray 형태로 반한됨. 
    iris_scaled = scaler.transform(iris_df)
    iris_df_scaled = pd.DataFrame(data=iris_scaled, columns=iris_data_name)

     

     2-2.  MinMaxScaler

    • 0~1 사이의 값으로 맞추고자 할 때 사용
    from sklearn.preprocessing import MinMaxScaler
    
    scaler=MinMaxScaler()
    scaler.fit(iris_df)
    iris_scaled=scaler.transform(iris_df)
    iris_df_scaled=pd.DataFrame(data=iris_scaled, columns=iris_data_name)
    #print(iris_df_scaled.min(), iris_df_scaled.max())

     

    3. Oversampling & Under Sampling

    • Oversampling 과 Under Sampling 은 불균형한 데이터셋의 불균형을 완화해주는 기법이다. 
    • 데이터 유실 문제로 대게의 경우에는 Oversampling 이 사용되는데, Oversampling은 적은 라벨의 데이터를 많은 라벨 수준으로 증식 시켜주는 방식이다. Oversampling 에 대표적인 방법이 SMOTE 이다. 
    • Data Augmentation 과의 차이? 비슷한 맥락에서 사용할 수도 있지만 Oversampling 은 불균형 데이터셋을 맞춰 적은 클래스 데이터가 학습이 안되는걸 방지하는 목적에 가깝고, Augmentation은 주로 딥러닝 모델에서 더 강력한 모델을 만들기 위해 가지고 있는 데이터를 변형시켜 증식하는 목적이다. 

     

    3-1. SMOTE

    • SMOTE는 Synthetic Minority Over-sampling Technique의 약자로 대표적인 오버 샘플링 기법 중 하나이다.낮은 비율로 존재하는 클래스의 데이터를 최근접 이웃 K-NN 알고리즘알고리즘을 활용하여 새롭게 생성하는 방법이다.
    • 가상의 데이터를 증식하는 방식이기때문에, 검증/테스트셋에는 사용하면 안되며, 학습데이터에만 사용해야한다. 

    • SMOTE 활용코드. 통상적으로 Oversample 하면 재현율(Recall)은 올라가고 정밀도(Precision)은 떨어진다. 
    from imblearn.over_sampling import SMOTE

    smote = SMOTE(random_state=0)
    X_train_over, y_train_over = smote.fit_resample(X_train, y_train)
    print('SMOTE 적용 전 학습용 피처/레이블 데이터 세트: ', X_train.shape, y_train.shape)
    print('SMOTE 적용 후 학습용 피처/레이블 데이터 세트: ', X_train_over.shape, y_train_over.shape)
    print(type(X_train_over),type(y_train_over))
    print('SMOTE 적용 후 레이블 값 분포: \n', y_train_over.value_counts())
    # SMOTE 사용 후 Label 1에 해당하는 학습데이터가 Label 0 수준으로 증가함 확인

     

    4. (선형)회귀모델에서의 정규화 

    • 선형회귀는 피쳐와 타겟의 분포가 정규분포인 형태를 매우 선호한다. 특히 타겟의 분포가 Skewed 되어있으면 예측 성능이 안좋아진다. 
    • 일반적으로 중요한 피쳐들 + 타겟에 대해 스케일링/정규화 작업을 수행한다. 
    • *피처 데이터의 스케일링 : ① 평균0,분산1인 표준정규분포 정규화 혹은 0~1 사이값으로 표준화 ② 표준,정규화 된것을 다시 다항특성을 적용하여 변환 ③ 로그변환 (주로 사용)
    • *타겟 데이터의 스케일링 : 로그변환 
    • 스케일링으로 로그변환이 주로 사용되는 이유는, 정규분포 변환은 원본으로의 원복이 힘든 반면 로그변환은 넘파이의 log1p() 함수로 정규화 하고, 원복시에 expn1() 함수로 간단히 원상복구 할 수 있기 때문이다. 
    from sklearn.preprocessing import StandardScaler, MinMaxScaler, PolynomialFeatures

    # method는 표준 정규 분포 변환(Standard), 최대값/최소값 정규화(MinMax), 로그변환(Log) 결정
    # p_degree는 다향식 특성을 추가할 때 적용. p_degree는 2이상 부여하지 않음.
    def get_scaled_data(method='None', p_degree=None, input_data=None):
        if method == 'Standard':
            scaled_data = StandardScaler().fit_transform(input_data)
        elif method == 'MinMax':
            scaled_data = MinMaxScaler().fit_transform(input_data)
        elif method == 'Log':
            scaled_data = np.log1p(input_data)
        else:
            scaled_data = input_data
        # scaled_data 로 scale 된 이후에도 다항변환 할 수 있도록 코딩
        if p_degree != None:
            scaled_data = PolynomialFeatures(degree=p_degree,
                                             include_bias=False).fit_transform(scaled_data)
       
        return scaled_data
    반응형

    댓글