자격증/빅데이터분석기사

[빅데이터분석기사] 작업 2유형 작성패턴 - 회귀문제(1)

Ddolgom 2022. 11. 24. 14:56
반응형

작업 2유형 분류문제에 이어서 회귀문제 코드패턴을 다루고자 한다.

 

사실 분류 모델과 비교 했을때, 전반적으로 달라지는 것들은 없으나,

정형화 공식 (역시 4개 파트로 나눌 수 있다.)

1. 데이터 가져오기

2. 결측치 확인 및 대체하기(제거하기)

ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ

3. 라벨 인코딩 (범주형 -> 수치형)

4. 원핫 인코딩 (범주형 ->수치형) 

 *  3, 4 부분에서 수치형 -> 범주형 같은 경우에는 binning의 과정을 해야 된다. (회귀(예측) 문제일 경우)

5. 파생변수 생성

6. 스케일링 (1. StandardScaler 2. MinMaxScaler) 1은 범주형 데이터일 경우 2는 수치형일 경우 변환해준다.

ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ

7. 데이터 분리 (train_test_split)

8. 모형학습 (1. 일반 단일 모형 2. 앙상블(결합모형)) --> 문제에서 요구하는대로 취사선택한다. RandomForest 모델을 쓰면 2번 앙상블도 충족되므로 일반적으로 많이 사용한다. 회귀 모형을 사용해야 한다.

9. 모형평가 (score내서 제대로 학습이 되었는지 확인하는 부분) 회귀모형의 평가지표가 완전히 다르다.

10. 하이퍼파라미터 튜닝 (아직까지 출제된적은 없으나, 출제범위이므로 가장 기본적인 GridSearchCV활용)

ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ

11. 예측값 저장 (index=False로 시험제출 폼에 맞추어서 내야 채점이 제대로 된다. ) 및 제출 데이터 확인

 

 

빨간색 부분들이 분류 모형과의 차이다.

 

그리고 실제로 시계열 데이터가 출제될 수도 있는데, 컬럼의 데이터 타입을 날짜로 바꾸는 연습도 필요하다.

이 부분은 실제로 추가 글을 통해서 새로운 데이터로 기본코드 정리를 하려고 한다.

(시간을 다루려면 회귀 모형을 사용해야 한다. 연속적인 데이터이기 때문에)

 

분류 모형으로 진행한 작업과의 코드 및 베이스라인은 차이가 거의 없으니, 동일한 흐름으로 패턴을 익히면 된다.

 

추가적으로 모델의 성능을 높이려면, 회귀모형이나 분류모형 같은 경우에 다중공선성을 체크하여 독립 변수간에 상관관계가 있는 중복되는 성향이 있다고 판단되는 데이터들은 제거해주는 것이 좋다. 

(다중공선성에 대한 내용은 검색을 하면 나오는데, 나중에 머신러닝 파트로 다룰 예정이다.)

 

그리고 아무런 상관관계에 영향성이 없는 컬럼 자체를 drop시켜도 된다.

하지만, 빅데이터 분석기사 시험의 특성상 높은 성능을 요구하지 않고, 사실 열을 삭제한다는 것은 이 관련 분야의 도메인 지식이 있거나 전문가의 판단이 들어가야 되는 부분이라서 시험에서 어떤 열을 삭제하고 진행하라는 지시사항이 없으면 

최대한 열 삭제 없이 진행을 해나가도 무방할 것으로 판단된다.

 

1. 데이터 가져오기

앞서 분류모델과 동일한 내용이다.

 

자세한 설명은 생략한다.

import pandas as pd
import numpy as np

# 데이터 파일 읽어오기

# 2. train, test 데이터 분리가 되어 있다고 가정한다.
x_train = pd.read_csv('data/x_train.csv', encoding = 'cp949')
x_test = pd.read_csv('data/x_test.csv', encoding = 'cp949')
y_train = pd.read_csv('data/y_train.csv', encoding = 'cp949')
print(x_train.head()) # 데이터의 상위 행 5줄을 확인하는 코드 기본적인 파악에서 중요하다.
print(x_train.head().T) # Transpose 행렬을 통해서 만약 열이 많다면 행과 열을 바꿔서 정보를 확인할 수 있다.

print(x_train.shape) # 행과 열을 확인한다. 기본적인 데이터 파악에서 중요하다.

print(x_train.info()) # 데이터프레임 형태의 데이터의 기본 정보 (데이터 유형)을 파악하는데 유용하다.

print(x_train.describe()) # 기본적인 기술적 통계량을 확인할수 있는 함수다. 해서 데이터의 분포를 확인하면 좋다.

2. 결측치 확인 및 대체하기(제거하기)

위와 같은 기본적인 과정을 거쳤으면 본격적인 가공이 시작되는 가장 중요한 부분이다. 일반적으로 데이터를 만드는 일을 하게 되면 데이터의 양이 많을 경우 누락되는 데이터가 나오게 되며 빅분기 시험 특성상 결측 데이터는 무조건 있다고 봐도 무방하다.

print(x_train.isna().sum()) # 결측치 확인 코드

결측치 확인이 되면, 처리하는 방식이 3가지가 있다. 

 

(1) 결측치 모조리 통으로 제거

(2) 평균값이나 중앙값을 구해서 대체

(3) 가장 많이 분포하는 값들을 넣기 (최빈값 넣기)

 

보통은 1번은 절대 시험에서 추천되지 않는 방법이다.

결측 데이터가 많으면 왜곡이 일어나게 되며, 데이터의 형태가 달라질수도 있다.

실무에서도 다루는 데이터의 도메인 지식을 가진 사람과 상의를 해서 지우는 것으로 합의 된 것이 아니면 보통은 통 삭제는 권장되지 않는다.

 

2번이나 3번으로 처리를 하게 되며, 보통은 시험에서 어떠한 식으로 대체를 하라는지 안내가 나오게 되며, 설령

안내가 없더라도 중앙값을 쓸지 평균값을 쓸지 최빈값을 쓸지 파악이 된다. 데이터의 형태를 보고 파악하면 된다.

(최솟값과 최댓값의 차이가 큰, 쏠려있는 데이터면 보통 중앙값을 많이 활용한다.)

 

중앙값으로 채우는 예시이다.

x_train['horsepower'] = x_train['horsepower'].fillna(x_train['horsepower'].median())
x_test['horsepower'] = x_test['horsepower'].fillna(x_test['horsepower'].median())

 

3. 라벨 인코딩

머신러닝에서 학습을 용이하게 하기 위해서는 숫자형으로 변환이 필요하다. 

분류모델링과 완전히 동일한 방법이다.

from sklearn.preprocessing import LabelEncoder

encoder = LabelEncoder()

label_list = ['origin','name']

x_train[label_list] = x_train[label_list].apply(encoder.fit_transform)
x_test[label_list] = x_test[label_list].apply(encoder.fit_transform)

4. 카테고리 타입으로 변환 후 원핫 인코딩

원핫 인코딩이란 카테고리별로 이진 특성(0, 1)을 만들어 해당하는 특성만 1, 나머지는 0으로 만드는 방법이다.

pandas의 get_dummies() 함수를 써야 한다.

category = ['origin']

for i in category:
	x_train[i] = x_train[i].astype('category')
    x_test[i] = x_test[i].astype('category')
    
x_train = pd.get_dummies(x_train)
x_test = pd.get_dummies(x_test)

 

5. 파생변수 생성 (구간화(비닝))

파생변수를 생성한다. 쉽게 얘기해서 추출하고자 하는 열에서 새로운 정보를 추출하여 의미 있는 데이터를 생성하는 과정이다.

여기에서는 구간화를 진행해 볼 것이다. 수치형(연속형)값들을 이산형 또는 범주형으로 변환하는 것으로

pandas에서 제공하는 cut()함수나 qcut()함수를 사용하여 수행할 수 있다.

cut()함수와 qcut()함수의 차이는 cut()함수는 값을 기반으로 하고 qcut()함수는 분위수 기반으로 한다.

여기에서는 qcut()함수를 사용한다. (q의 유례는 사분위수 quantile()함수에서 유래가 되었다.)

x_train['horsepower_qcut'] = pd.qcut(x_train['horsepower'], 5, labels = False)
x_test['horsepower_qcut'] = pd.qcut(x_test['horsepower'], 5, labels = False)

6. 스케일링

파생변수까지 생성해주었으면 데이터셋을 train,test로 나누어서 모델학습을 하기 전에 스케일링을 해주어야 한다.

스케일링을 수행하는 이유는 머신러닝 결과가 왜곡되지 않고, 특정한 독립변수를 무시하지 않게 하기 위함이다.

 

예시를 들면 교육소비 지출액과 학점으로 취업여부를 판별하는 분류모델을 만든다고 가정했을때,

학점은 만점이 4.5 교육소비 지출액은 개인마다 편차가 있겠지만, 10만원 단위에서 100만원 단위까지 있을 것이다.

스케일자체가 다르기 때문에 MinMaxScaler를 활용하게 된다면 0을 min으로 1을 max로하여 스케일을 맞추어서 데이터를 머신러닝 모델에 넣어주게 되면 결괏값이 좀더 정확하게 나올 것이다.

 

많이 사용되는 스케일링의 종류는 크게 2가지가 있다.

1. StandardScaler (평균값 : 0, 표준편차 : 1)

2. MinMaxScaler (최솟값 : 0, 최댓값 : 1)

StandardScaler는 범주형 데이터일 경우 MinMaxScaler는 수치형일 경우 활용되는데,

 

MinMaxScaler의 단점은 이상치에 민감하다는 것이다. 그렇지만, 수치형일 경우 많이 활용되며 

실제 아래 예시는 MinMaxScaler를 사용한 것이다.

from sklearn.preprocessing import MinMaxScaler

scaler = MinMaxScaler()

scale_list = ['displacement', 'horsepower', 'weight']

scaler.fit(x_train[scale_list]) # 스케일링 작업에서 fit()은 train 데이터만 적용 해주어야 한다.

x_train[scale_list] = scaler.transform(x_train[scale_list])
x_test[scale_list] = scaler.transform(x_test[scale_list])

스케일링에서 주의해야할 점이 있다.

fit() 함수와 transform()을 분리해서 진행하는 경우가 있고 fit_transform()함수로 동시에 적용해서 진행하는 경우도 있다.

하지만, 스케일링 작업시에 각각의 역할을 파악해야 하는데,

 

fit() : 데이터 변환을 학습한 상태, train 데이터만 적용

 

transform() : 실제 data의 scale을 조정,  train 데이터와 test 데이터에 적용이라는 점이다.

 

실제 전체 데이터를 train 데이터 : test 데이터 =  8:2의 비율로 분할했다고 가정해보자.

비율적인 것으로만 봐도 train 데이터의 양이 4배 많은데, train과 test에 각각 fit()을 적용하면, 데이터의 양의 차이 때문에 두 데이터의 scale이 동일하게 피팅 되지 못한다.

 

그래서 위의 코드처럼 fit은 train 데이터에만 적용을 해서 기준을 만들고 test는 fit()과정없이 transform만 적용해준다.

 

아니면, 다른 방법은 train_test_split함수로 분리하기 전에, 전체 데이터에 스케일링을 적용해주고 (fit_transform() 사용가능) train, test 데이터를 분리하는 것이 여러므로 낫다.

 

7. 데이터 분리 (train_test_split)

데이터를 검증하는 과정인데, 대략적으로 현재 데이터는 x_train, y_train, x_test가 있다.

x_train(문제)과 y_train(정답)으로 학습모델을 생성하여  x_test 데이터를 넣어서 최종 결과물을 낸다.

바로 진행을 해도 되지만, x_train과 y_train데이터를 다시 분할하여 validation(검증) 하는 과정을 거치는 것이 좋다.

 

k-fold 교차 검증을 통해서 데이터를 분할하여 검증을 해도 되지만, 여기서는 홀드아웃 교차검증만으로 검증을 해서 본다.

from sklearn.model_selection import train_test_split

X_TRAIN, X_TEST, Y_TRAIN, Y_TEST = train_test_split(x_train, y_train, test_size = 0.2)

print(X_TRAIN.shape)
print(X_TEST.shape)
print(Y_TRAIN.shape)
print(Y_TEST.shape)

X_TRAIN, X_TEST, Y_TRAIN, Y_TEST이 대문자로 되어 있다. 새로운 변수를 위한 것으로 앞서 나누었던

x_train, y_train 데이터를 또다시 4개의 데이터로 분할한 것이다. (검증용 데이터이다.)

 

앞서 분류에서 설정해주었던 stratify =[종속변수열]은 회귀모형에서는 하면 안된다.

 

8. 모형학습

(1) 일반 단일 모형

(2) 앙상블 (단일모형의 결합)

 

모델(모형)은 회귀문제이므로 Regressor모델을 써야 한다. 여기에서는 선형회귀 모델과 랜덤포레스트 회귀모델을 사용할 것이고, 마지막으로 stacking이라고 하는 앙상블 기법을 활용해볼 것이다.

앞서 분류에서는 VotingClassifier라는 앙상블 기법을 썼는데, Stacking을 활용해 볼 것이다.

 

여기서 코드가 조금 다른점을 유념하면 된다.

스태킹 같은 경우에는 여러 개별 모델이 예측한 결괏값을 다시 학습 데이터로 사용해서 만드는 모델이기에 코드가 다르다.

필자가 실제로 해커톤에서 성능을 조금이라도 높이기 위해 시도해보았던 방법 중 하나이다.

# 1 모델
from sklearn.linear_model import LinearRegression
model1 = LinearRegression()
model1.fit(X_TRAIN, Y_TRAIN)
pred1 = model1.predict(X_TEST)

# 2 모델
from sklearn.ensemble import RandomForestRegressor
model2 = RandomForestRegressor()
model2.fit(X_TRAIN, Y_TRAIN)
pred2 = model2.predict(X_TEST)

# 앙상블 (스태킹 모델)
from sklearn.ensemble import StackingRegressor

estimators = [[('lr', model1), ('rf', model2)]

model3 = StackingRegressor(estimators = estimators, final_estimator = RandomForestRegressor())
model3.fit(X_TRAIN , Y_TRAIN)
pred3 = model3.predict(X_TEST)

print(pred3)

스태킹 모형에서 estimators의 모델들의 결괏값이 final_estimator의 RandomForestRegressor()로 재학습이 되는 것을 의미한다.

 

9. 모형평가 (score내서 제대로 학습이 되었는지 확인하는 부분)

# 모형평가
from sklearn.metrics import mean_squared_error 
from sklearn.metrics import mean_absolute_error
from sklearn.metrics import mean_absolute_percentage_error
from sklearn.metrics import r2_score

# RMSE 만드는 방법 
import numpy as np 
print(np.sqrt(mean_squared_error(y_test, y_pred))

모형평가는 분류와는 완전히 다르다.

위에서와 같은 모형평가 지표들이 있고, 실제로 import를 할때는 위에서와 같이 번거롭게 할 필요없고

문제에서 요구하는 지표만 import를 하면 되고, 만약 시험에서 2개이상의 지표를 요구한다면,

from sklearn.metrics import mean_squared_error, r2_score

와 같이 쉼표로 넣어주면 동시에 import가 된다.

RMSE 같은 지표는 MSE에 루트만 씌워주면 되기 때문에, numpy 라이브러리를 불러와서 sqrt(루트)만 씌워주면 된다.

 

10. 하이퍼파라미터 튜닝 (아직까지 출제된적은 없으나, 출제범위이므로 가장 기본적인 GridSearchCV활용)

from sklearn.model_selection import GridSearchCV
params = {'n_estimators' :[50, 100], 'max_depth' : [4, 6]}

model4 = RandomForestRegressor()

clf = GridSearchCV(estimator = model4, param_grid = params, cv = 3)
clf.fit(X_TRAIN, Y_TRAIN)

print('최적의 하이퍼파라미터:', clf.best_params_)

 

분류에서처럼 그리드서치로 간단한 지표 2개정도씩만 비교하여 최적의 값을 찾아서 

model2 = RandomForestRegressor(max_depth=6, n_estimators=100) # 'max_depth': 6, 'n_estimators': 100

다음과 같이 모델링 한 부분에 다시 넣어서 실행시켜 주면 된다. max_depth라는 하이퍼파라미터값과 n_estimators 의 베스트 값이 6과 100이라 가정하고 넣은 코드이다. 실제로 모델성능이 소폭 향상됨을 알 수 있다.

 

11. 예측값 저장 (index=False로 시험제출 폼에 맞추어서 내야 채점이 제대로 된다. ) 및 제출데이터 확인

result = pd.DataFrame(model2.predict(x_test)) # 주의 X_TEST아님. 실제 x_test값을 넣어야 한다.
result = result.iloc[:, 0] # 앞에서 데이터프레임으로 변환했던 것은 predict 함수로 추출된 결과는 데이터 프레임의 형태가 아니므로 데이터 타입을 변경하기 위해서다. 
# 다시 iloc[:,0]으로 0번째 열에 데이터를 뽑는 것은 시리즈화의 과정으로 데이터프레임 2개를 합쳐서 또다른 데이터프레임을 만들수 없다. 시리즈로 되어있는 데이터타입만 합쳐서 데이터프레임화 시킬 수 있다.
pd.DataFrame({'id': x_test.index, 'result':result}).to_csv('00500.csv',index = False)

fianl = pd.read_csv('/content/00500.csv/, encoding = 'cp949)

print(final.head())

분류 모델과 동일한 부분이다.

자세한 코드 해설이 필요하다면 분류모델 글로 돌아가서 확인하면 된다.

 

어려운 점은 아무래도 코드를 암기해야하는 부분이다.

help함수를 잘 사용하고 영어에 능숙하다면 실제 open북 수준이겠지만, 고사장에 될수 있으면 기본 폼들을 암기하고 들어가며, 실제로 데이터 타입 문제와 train_test_split시에 다른 데이터가 들어가서 행이 달라서 발생하는 오류에 대한 트러블 슈팅 그리고 순서가 조금이라도 섞여버리면 결과를 도출하지 못할 수도 있다.

 

많은 코드에러를 내보고, 해결을 위한 생각을 해보고 데이터마다 형태와 정보가 다르기에 코드를 어떻게 바꿔서 적용시킬까에 대한 고민과 연습만이 합격에 가까워지는 지름길이다.

 

필자도 계속 다른 데이터를 가지고 연습을 하며 암기를 하고 있는데, 시험치기전까지 계속 밑빠진 독에 물을 붓는다는 심정으로 반복해야할 것 같다. (코드를 평소에 외우지 않고 복붙질만 한 결과이다ㅠ)

 

마지막으로 시계열 데이터를 간단하게 다루는 회귀모형을 진행해보고 빅데이터분석기사 시리즈 연재를 마치겠다.

 

1유형은 시간이 남는다면, 시험이 끝나고 정리해서 올려볼 생각이다.

반응형