본문 바로가기

공부방/Upstage AI Lab 4기

머신러닝 경진대회 | 결측치 메꿔주기

2024.09.07 - [프로젝트] - 머신러닝 경진대회 1 | 데이터 탐색 EDA

지난 편에 이어 데이터 전처리를 계속해보자. (다루고 있는 데이터는 structured data 정형 데이터)

데이터를 파악할 때, 수치형 변수인지 범주형 변수인지를 먼저 확인해본다.
(수치형은 또 연속형/이산형으로 나뉘고, 범주형 변수는 명목형, 순위형으로 나뉨)

숫자값을 가지는 데이터라면 숫자의 특징을 이용해서 결측치나 이상치를 파악할 수 있고, 글자로 된 데이터라면 다른 방식을로 모델이 이해할 수 있도록 처리해줘야하기 때문이다. (데이터를 모델이 잘 알아먹을 수 있도록 숫자로 변환해줘야 한다.)

 

범주형 데이터를 수치적으로 표현하는 방법: Indexing 인덱싱

각 범주의 순서를 임의로 정해 0번부터 번호를 부여하는 방법. 예를 들면 여자와 남자를 0과 1로 구분한다던가, 지역을 0부터 숫자로 붙인다던가

글자(알파벳)에 숫자를 부여하면, 단어나 문장도 숫자로 표현할 수 있다. -> NLP

이미지를 숫자로 표현하는 방법: 비트맵 / 동영상은 이미지 데이터를 여러 장 겹친 것으로 이해. -> Computer Vision

음성 Raw Wave 표현 방법: 마이크로 들어온 음성신호를 일정 주기로 샘플링 하여 저장. -> 음성처리

 

데이터 노이즈: 결측치, 이상치, 틀린 라벨, 중복 샘플, 도메인 외의 샘플 등등

데이터의 결측치가 엄청 많아서 이걸 어떻게 해야하나 고민됐다. 

참고로 강의에서는 결측치 처리 방안으로 어떤 논문에서 이런 얘기가 있다고 한다. 

결측치 비율이 10% 미만이면 제거, 대체가 좋고
10~20% 정도면 회귀 대체, Hot Deck
20% 이상은 회귀 대체

베이스라인 코드에서는 연속형 변수에 대해서 선형보간을 했고, 범주형 변수는 "unknown"으로 임의로 보간을 했다. 

선형보간: 빨간 색 두 점이 주어졌을 때 선분을 긋고 그 사이의 값을 추정하는 방법. 단순 비례식으로 x, y 추정. 

 

그런데 강의에서 듣기로,
결측치를 채울 때 Regression을 썼을 때 결과가 좋았다는 얘기가 있어서 선형회귀로 결측치를 채워보기로 했다. (카테고리 변수는 분류를 이용해 결측치를 채워주고, 연속형 변수는 회귀를 통해 대체해줄 수 있다고 강의에서 언급.) 결측치를 채울 때에는 결측값을 제외하고 데이터로부터 선형회귀모델을 훈련하면 된다고. (여기부터는 공부하면서 한거라 정보가 부정확할 수 있음)

LinearRegression() 을 쓰려고 했는데, 이런 에러가 났다. 

#ValueError: Input X contains NaN.

어디에 NaN 값이 들어갔나.. 일단 뭔가 결과를 빨리 봐야겠다 싶어서 클로드한테 물어보고 아래처럼 코드르 복붙해서 썼다.

더보기
from sklearn.ensemble import HistGradientBoostingRegressor
import pandas as pd
import numpy as np

reg_columns = ['k-전체세대수', 'k-주거전용면적', 
'k-관리비부과면적', 'k-전용면적별세대현황(60㎡이하)', 'k-전용면적별세대현황(60㎡~85㎡이하)', 
'k-85㎡~135㎡이하', '주차대수']

for column in reg_columns:
    if concat_select[column].isnull().sum() > 0:
        features = [col for col in reg_columns if col != column]
        
        train_data = concat_select.dropna(subset=features + [column])
        X = train_data[features]
        y = train_data[column]

        model = HistGradientBoostingRegressor(random_state=42)
        model.fit(X, y)

        missing_rows = concat_select[concat_select[column].isnull()]
        X_missing = missing_rows[features]

        y_pred = model.predict(X_missing)
        concat_select.loc[concat_select[column].isnull(), column] = y_pred

HistGradientBoostingRegressor를 쓰면 된다고 하는데. 이건 뭐지?

선형회귀모델에도 여러가지가 있는건지..? 

 

이게 무슨 의미인지 모르겠어서 지금 공부하고 있는 중

일단 선형회귀 모델을 사용하는 방법부터 정리해보면

1. 모델 학습을 진행하기 전에 데이터셋을 학습용과 평가용으로 분리한다. (-> 결측치 채우는 용도인데 분리가 필요한가..?)

그리고 이런 식으로 진행되는 것 같은데. 

# (다중) 선형회귀모델 초기화
multi_regressor = LinearRegression()

# 학습용 데이터셋을 활용해 학습 진행
multi_regressor.fit(train_data, train_target)

# 학습, 평가 데이터셋에서 회귀식의 예측값 계산
multi_train_pred = multi_regressor.predict(train_data)
multi_test_pred = multi_regressor.predict(test_data)

# 위의 예측값을 목표값과 비교해 MSE 손실함수의 값 계산
multi_train_mse = mean_squared_error(multi_train_pred, train_target)
multi_test_mse = mean_squared_error(multi_test_pred, test_target)

 

어떤 피쳐의 결측치를 Regression을 쓰면 좋은지, 그건 어떻게 아는걸까. 일단 연속형 변수들 중에서 결측치가 있는 피쳐를 뽑으면.

Regression 사용해서 결측치 넣을 피쳐들 :
['k-전체동수', 'k-전체세대수', 'k-연면적', 'k-주거전용면적', 
'k-관리비부과면적', 'k-전용면적별세대현황(60㎡이하)', 'k-전용면적별세대현황(60㎡~85㎡이하)', 
'k-85㎡~135㎡이하', '건축면적', '주차대수', '좌표X', '좌표Y', 'target']

 

Regression 연습하기

조금 더 와닿는 문제로 바꿔보자

우리 팀에는 능력자분들이 많으셔서 데이터셋에 더 많은 컬럼을 추가했는데, 그중에 나름 상관관계가 있을 것으로  보이는 피쳐들을 뽑았다. CPI에는 4350개의 결측치가 있고, 나머지 피쳐들은 결측치가 0이다. 

rgtest = df[['CPI', 'GDP', '3y_KTB_yield', 'base_rate', 'unemployment_rate']]

이렇게 생긴 데이터. 이제 CPI 결측치를 채워보자! 

1. 학습시킬 데이터 만들기

# (CPI의) 결측치가 없는 행만 선택해서 X_train으로 넣음
X_train = rgtest.dropna()[['GDP', '3y_KTB_yield', 'base_rate', 'unemployment_rate']]
# CPI의 결측치가 없는 행들의 CPI 값들
y_train = rgtest.dropna()['CPI']

#모델 학습
model = LinearRegression()
model.fit(X_train, y_train)

#결측치 예측 
X_missing = rgtest[rgtest['CPI'].isnull()][[
'GDP', '3y_KTB_yield', 'base_rate', 'unemployment_rate']]
#y_pred가 모델이 'GDP', '3y_KTB_yield', 'base_rate', 'unemployment_rate'로 예측한 CPI값
y_pred = model.predict(X_missing)

#결측치를 채워줌
rgtest.loc[df['CPI'].isnull(), 'CPI'] = y_pred

이렇게 하면 잘 채워졌다.