<머신러닝 • 딥러닝 문제해결 전략> 7장의 내용을 실습했습니다.
베이스라인 모델 구축
데이터를 불러온 후 모든 피처를 원-핫 인코딩하고 -> 평가지표 계산함수 작성의 경우 ROC AUC 사이킷런을 사용해 해당 과정을 넘어간 뒤 -> 모델 훈련 시 로지스틱 회귀모델로 베이스라인 모델을 만들고 -> 성능검증 -> 제출 하도록 하겠습니다.
import pandas as pd
# 데이터 경로
data_path = '/kaggle/input/cat-in-the-dat/'
train = pd.read_csv(data_path + 'train.csv', index_col='id')
test = pd.read_csv(data_path + 'test.csv', index_col='id')
submission = pd.read_csv(data_path + 'sample_submission.csv', index_col='id')
피처 엔지니어링
해당 데이터는 문자를 포함한 데이터이므로 문자 데이터를 숫자 데이터로 변환하는 인코딩 작업을 해야합니다.
훈련 데이터와 테스트 데이터에 동일한 인코딩을 적용하기 위해 편의상 두 데이터를 합치겠습니다.
합친 다음 피처와 타깃값은 따로 분리해서 모델링해야하기 때문에 DataFrame에서 타깃값을 제거하겠습니다.
all_data = pd.concat([train, test]) # 훈련 데이터와 테스트 데이터 합치기
all_data = all_data.drop('target', axis=1) # 타깃값 제거
all_data
concat() 함수를 사용해 두 데이터프레임을 합치고 이어서 데이터를 합친 all_data에서 drop()함수를 사용해 타깃값을 제거해주었습니다. 그 결과 행 개수가 50만개, 타깃값이 빠져서 피처는 23개가 나왔습니다.
원-핫 인코딩
원-핫 인코더 객체를 생성하고 all_data의 모든 피처를 인코딩하여 새로운 변수 all_data_encoded에 저장하여 간단하게 인코딩이 끝났습니다.
from sklearn.preprocessing import OneHotEncoder
encoder = OneHotEncoder() # 원 핫 인코더 생성
all_data_encoded =encoder.fit_transform(all_data) # 원 핫 인코딩 적용
데이터 나누기
공통으로 적용할 인코딩이 끝났으니 훈련데이터와 테스트 데이터를 다시 나누겠습니다. concat() 함수를 사용해 훈련/테스트 데이터를 합쳤는데 이를 이용해 행 번호를 기준으로 데이터를 나눠보도록 하겠습니다.
num_train = len(train) # 훈련 데이터 개수
# 훈련 데이터와 테스트 데이터 나누기
X_train = all_data_encoded[:num_train] # 0 ~ num_train - 1행
X_test = all_data_encoded[num_train:] # num_train ~ 마지막 행
y = train['target']
# 타깃값을 y 변수에 저장
훈련 데이터는 모델 훈련에 사용하고, 검증 데이터는 제출 전에 모델 성능 검증에 사용하기 위해서 훈련데이터에서 일부를 검증 데이터로 나누도록 하겠습니다.
from sklearn.model_selection import train_test_split
# 훈련 데이터, 검증 데이터 분리
X_train, X_valid, y_train, y_valid = train_test_split(X_train, y,
test_size=0.1,
stratify=y,
random_state=10)
- train_test_split() : 전체 데이터를 훈련 / 검증(테스트) 데이터로 나누는 함수. 첫번째 인수로는 피처를(X_train), 두번째 인수로는 타깃값(y)를 전달한다. random_state는 시드값을 고정하여 다음에 실행해도 같은 값이 나오게 해준다.
- test_size : 검증 데이터 크기를 지정하는 파라미터. 값이 정수면 검증데이터의 개수를, 실수면 비율을 의미함. 본문에서는 0.1로 지정했으니 10%를 검증 데이터로 분리하겠다는 뜻.
- stratify : stratify 파라미터로 지정한 값을 각 그룹에 공정하게 배분. 본문에서는 타깃값인 y를 전달했으므로 타깃값이 훈련데이터와 검증 데이터에 같은 비율로 포함되게끔 나눠주는 역할을 한다. stratify 파라미터를 지정하지 않으면 훈련 데이터와 검증 데이터에 타깃값이 불균형하게 분포될 수 있다. 그렇기에 되도록이면 stratify 파라미터에 타깃값을넘겨주는게 바람직하다.
모델 훈련
이제 모델을 생성한 뒤 앞에서 준비한 데이터를 사용해 훈련해보겠습니다. 선형 회귀 방식을 응용해 분류를 수행하는 로지스틱 회귀 모델을 사용하겠습니다.
from sklearn.linear_model import LogisticRegression
logistic_model = LogisticRegression(max_iter=1000, random_state=42) # 모델 생성
logistic_model.fit(X_train, y_train) # 모델 훈련
- max_iter : 모델의 회귀 계수를 업데이트하는 반복 횟수. 모델 훈련 시 회귀 계수를 업데이트하면서 훈련하는데 이때 몇번 업데이트할지를 결정합니다.
- random_state : 값을 지정하면 여러번 실행해도 매번 똑같은 결과가 나온다. 아무값으로 지정해도 상관 없다.
모델 성능 검증
사이킷런은 타깃값 예측 메서드 두가지를 제공합니다.
- predict() : 타깃값 자체 (0이냐 1이냐)를 예측
- predict_proba() : 타깃값의 확률 (0일 확률과 1일 확률)을 예측
두 가지를 사용해 예측한 결과를 확인해보도록 하겠습니다
logistic_model.predict_proba(X_valid)
logistic_model.predict(X_valid)
본 대회에서는 타깃값이 1일 '확률'을 예측해야 하기 때문에 predict_proba() 메서드로 예측한 결과의 두 번째 열을 타깃 예측값으로 사용합니다.
# 검증 데이터를 활용한 타깃 예측
y_valid_preds = logistic_model.predict_proba(X_valid)[:, 1]
y_valid_preds 변수에는 검증 데이터 타깃값이 1일 확률이 저장됩니다.
이제 타깃 예측값인 y_valid_preds와 실제 타깃값인 y_valid를 이용해 ROC AUC 를 구해보겠습니다.
모델 성능을 검증하려는 절차로 ROC AUC 점수는 사이킷런의 roc_auc_score() 함수를 이용하면 쉽게 구할 수 있습니다.
from sklearn.metrics import roc_auc_score
# 검증 데이터 rou auc
roc_auc = roc_auc_score(y_valid, y_valid_preds)
print(f'검증 데이터 roc auc: {roc_auc:.4f}')
예측 및 결과 제출
테스트 데이터를 활용해 타깃값이 1일 확률을 예측하고, 결과를 제출해보겠습니다.
y_preds에 타깃값이 1일 확률을 저장합니다.
# 타깃값 1일 확률 예측
y_preds = logistic_model.predict_proba(X_test)[:, 1]
마지막으로 제출 파일을 만든뒤 커밋 후 제출해보겠습니다.
# 제출 파일 생성
submission['target'] = y_preds
submission.to_csv('submission.csv')
성능 개선 1
6장을 실습할 때에는 베이스라인 모델과 다른 모델을 사용해 더 높은 성능을 얻었지만, 이번 7장 실습에서는 베이스라인 모델 자체의 성능을 높여보겠습니다.
성능 향상을 위해 피처 맞춤 인코딩, 피처 스케일링, 하이퍼파라미터 최적화에 주안점을 두어 모델링을 진행하겠습니다.
피처 맞춤 인코딩
인코딩을 피처의 특성에 맞게 적용합니다. 베이스라인에서는 모든 피처를 일괄적으로 원-핫 인코딩 했습니다. 이번에는 피처 특성에 맞게 순서형 피처 ord_1, ord_2는 수작업으로, ord_3, ord_4, ord_5는 ordinal 인코딩을, 명목형 피처와 날자 피처에는 원핫 인코딩을 적용하겠습니다. 인코딩은 이진 피처, 순서형 피처, 명목형 피처, 날짜 피처 순으로 진행하겠습니다.
우선 데이터를 불러옵니다.
import pandas as pd
# 데이터 경로
data_path = '/kaggle/input/cat-in-the-dat/'
train = pd.read_csv(data_path + 'train.csv', index_col='id')
test = pd.read_csv(data_path + 'test.csv', index_col='id')
submission = pd.read_csv(data_path + 'sample_submission.csv', index_col='id')
데이터 합치기
인코딩 전에 훈련 데이터와 테스트 데이터를 합쳐 all_data를 만들고 타깃값을 제거합니다.
# 훈련 데이터와 테스트 데이터 합치기
all_data = pd.concat([train, test])
all_data = all_data.drop('target', axis=1) # 타깃값 제거
이진피처 인코딩
이진피처 요약표에서 보았듯 bin_0, bin_1, bin_2 피처는 이미 0과 1로만 잘 구성되어 있어 따로 인코딩하지 않아도 됩니다. 그러나 bin_3, bin_4 피처는 문자로 구성되어 있습니다. 이를 각각 1과 0으로 바꾸는데 이때 판다스의 map()함수를 사용합니다. 여기서는 map 함수에 딕셔너리를 전달해 결과를 반환했습니다.
all_data['bin_3'] = all_data['bin_3'].map({'F':0, 'T':1})
all_data['bin_4'] = all_data['bin_4'].map({'N':0, 'Y':1})
순서형 피처 인코딩
ord_0 피처는 이미 숫자로 구성돼있어 인코딩하지 않아도 됩니다.
ord_1과 ord_2 피처는 순서를 정해서 인코딩하고, ord_3부터 ord_5는 알파벳 순서대로 인코딩해야 합니다.
우선 ord_1, ord_2 피처부터 인코딩하도록 하겠습니다. 이번에도 map() 함수를 사용해서 수작업으로 인코딩하겠습니다.
이때 피처값 순서에 유의하도록합니다.
ord1dict = {'Novice':0, 'Contributor':1,
'Expert':2, 'Master':3, 'Grandmaster':4}
ord2dict = {'Freezing':0, 'Cold':1, 'Warm':2,
'Hot':3, 'Boiling Hot':4, 'Lava Hot':5}
all_data['ord_1'] = all_data['ord_1'].map(ord1dict)
all_data['ord_2'] = all_data['ord_2'].map(ord2dict)
ord_3부터 ord_5는 알파벳 순서대로 인코딩해야 합니다. 이때 사이킷런의 OrdinalEncoder를 사용하면 됩니다.
OrdinalEncoder 객체를 생성하고, ord_3, ord_4, ord_5 피처에 fit_transform() 을 적용해 인코딩합니다.
from sklearn.preprocessing import OrdinalEncoder
ord_345 = ['ord_3', 'ord_4', 'ord_5']
ord_encoder = OrdinalEncoder()
# ordinal 인코딩 적용
all_data[ord_345] = ord_encoder.fit_transform(all_data[ord_345])
# 피처별 인코딩 순서 출력
for feature, categories in zip(ord_345, ord_encoder.categories_):
print(feature)
print(categories)
알파벳 순서별로 잘 인코딩되었는지 출력해 확인해보았습니다.
명목형피처 인코딩
명목형 피처는 순서를 무시해도 되기 때문에 원-핫 인코딩을 적용하겠습니다. 지능형 리스트를 활용해 명목형 피처 리스트를 만듭니다. 명목형 피처는 nom_0부터 nom_9까지 총 10개입니다.
nom_features = ['nom_' + str(i) for i in range(10)] # 명목형 피처
# 정상 실행되는 코드
from sklearn.preprocessing import OneHotEncoder
onehot_encoder = OneHotEncoder() # 원핫인코더 객체 생성
#원핫 인코딩 적용
encoded_nom_matrix = onehot_encoder.fit_transform(all_data[nom_features])
encoded_nom_matrix
마지막으로 all_data 에서 기존 명목형 피처를 삭제하겠습니다. 추후 encoded_nom_matrix와 all_data를 합칠 건데 그러면 하나의 피처가 형식만 다르게 중복되어 들어가기 때문입니다.
all_data = all_data.drop(nom_features, axis=1) # 기존 명목형 피처 삭제
날짜 피처 인코딩
날짜 피처에도 원-핫 인코딩을 적용하겠습니다.
# 날짜 피처 인코딩
date_feature = ['day', 'month'] # 날짜피처
# 원핫 인코딩 적용
encoded_date_matrix = onehot_encoder.fit_transform(all_data[date_features])
all_data = all_data.drop(date_features, axis=1) # 기존 날짜 피처 삭제
encoded_date_matrix
피처 엔지니어링 2
피처 스케일링이란 서로 다른 피처들의 값의 범위가 일치하도록 조정하는 작업입니다.
피처 스케일링이 필요한 이유는 수치형 피처들의 유효 값 범위가 서로 다르면 훈련이 제대로 안될 수도 있기 때문입니다. 앞에서 이진, 명목형, 날짜 피처를 모두 0과 1로 인코딩했습니다. 하지만 순서형 피처는 여전히 여러가지 값을 갖고 있으므로 순서형 피처의 값 범위도 0~1 사이가 되도록 스케일링 해주겠습니다.
순서형 피처 스케일링
다른 피처들과 범의를 맞추기 위해 순서형 피처에 min - max 정규화를 적용하겠습니다.
min-max 정규화는 피처 값의 범위를 0~1로 조정합니다.
from sklearn.preprocessing import MinMaxScaler
ord_features = ['ord_' + str(i) for i in range(6)] # 순서형 피처
# min-max 정규화
all_data[ord_features] = MinMaxScaler().fit_transform(all_data[ord_features])
이상으로 모든 피처의 값 범위가 0~1로 맞춰졌습니다.
인코딩 및 스케일링된 피처 합치기
현재 all_data에 이진 피처와 순서형 피처가 인코딩 되어 있고,
명목형 피처와 날짜 피처는 원 - 핫 인코딩 되어 각각 encoded_nom_matrix와 encoded_date_matrix에 저장되어 있습니다.
이 세 데이터를 합치도록 하겠습니다.
그런데 all_data는 DataFrame, encoded_nom_matrix와 encoded_date_matrix는 CSR 형식이기 때문에
all_data를 CSR 형식으로 만들어 합치겠습니다. 이때 사이파이가 제공하는 csr_matrix()는 전달받은 데이터를 CSR 형식으로 바꿔줍니다.
from scipy import sparse
# 인코딩 및 스케일링 된 피처 합치기
all_data_sprs = sparse.hstack([sparse.csr_matrix(all_data),
encoded_nom_matrix,
encoded_date_matrix],
format='csr')
여기서 hstack()은 행렬을 수평 방향으로 합칩니다. 그리고 format='csr'을 전달하면 합친 결과를 CSR 형식으로 반환합니다. 인코딩된 모든 피처를 합친 all_data_sprs를 출력해봅시다.
all_data_sprs
마지막으로 훈련데이터와 테스트 데이터를 나눕니다.
# 데이터 나누기
num_train = len(train) # 훈련 데이터 개수
# 훈련 데이터와 테스트 데이터 나누기
X_train = all_data_sprs[:num_train] # 0~num_train -1행
X_test = all_data_sprs[num_train:] # num_train ~ 마지막행
y = train['target']
y는 모델 훈련 시 필요한 타깃값(정답)입니다. 베이스라인과 마찬가지로 훈련데이터를 다시 훈련데이터, 테스트데이터로 나누겠습니다
from sklearn.model_selection import train_test_split
# 훈련 데이터, 검증 데이터 분리
X_train, X_valid, y_train, y_valid = train_test_split(X_train, y,
test_size = 0.1,
stratify=y,
random_state=10)
하이퍼파라미터 최적화
최종 데이터를 활용해 모델을 훈련하고 결과를 제출하겠습니다. 이 과정에서 베이스라인 모델과 달리 하이퍼파라미터를 최적화를 진행합니다. 그리드서치를 활용해 로지스틱 회귀모델의 하이퍼파라미터를 최적화해보겠습니다.
탐색할 파라미터는 C와 max_iter입니다. C는 규제강도를 조절하는 파라미터로 값이 작을수록 규제강도가 세집니다.
모델을 생성하고 평가지표를 ROC AUC로 지정해 그리드서치를 수행해보겠습니다. %%tiem은 해당 셀의 실행 후 소요 시간을 출력해주는 기능입니다.
%%time
from sklearn.model_selection import GridSearchCV
from sklearn.linear_model import LogisticRegression
# 로지스틱 회귀 모델 생성
logistic_model = LogisticRegression()
# 하이퍼파라미터 값 목록
lr_params = {'C':[0.1, 0.125, 0.2], 'max_iter':[800, 900, 1000],
'solver': ['liblinear'], 'random_state':[42]}
# 그리드서치 객체 생성
gridsearch_logistic_model = GridSearchCV(estimator = logistic_model,
param_grid = lr_params,
scoring ='roc_auc', # 평가지표
cv = 5)
# 그리드서치 수행
gridsearch_logistic_model.fit(X_train, y_train)
print(' 최적 하이퍼파라미터:', gridsearch_logistic_model.best_params_)
저는 약 6분이 걸렸습니다. 최적 하이퍼파라미터는 C: 0.125, max_iter: 800입니다.
모델성능 검증
최적 하이퍼파라미터를 찾았으니 모델 성능이 얼마나 개선되었는지 확인해보겠습니다. 베이스라인처럼 검증 데이터로 모델 성능을 검증해보겠습니다. 먼저 검증 데이터로 타깃 예측값을 구합니다.
y_valid_preds = gridsearch_logistic_model.predict_proba(X_valid)[:, 1]
from sklearn.metrics import roc_auc_score # ROC_AUC 점수계산 함수
# 검증 데이터 ROC_AUC
roc_auc = roc_auc_score(y_valid, y_valid_preds)
print(f'검증 데이터 ROC AUC : {roc_auc:.4f}')
베이스라인 모델보다 0.008 개선된 것을 확인할 수 있습니다.
# 예측 및 결과 제출
# 타깃값 1일 확률 예측
y_preds = gridsearch_logistic_model.best_estimator_.predict_proba(X_test)[:,1]
# 제출 파일 생성
submission['target'] = y_preds
submission.to_csv('submission.csv')
'Data > MLDL' 카테고리의 다른 글
안전 운전자 예측 경진대회: 성능개선1(lightGBM 모델) (0) | 2022.11.14 |
---|---|
안전 운전자 예측 경진대회: 베이스라인 모델 (0) | 2022.11.14 |
자전거 대여 수요 예측(2) (0) | 2022.09.26 |
자전거 수요 예측 경진대회 (0) | 2022.09.26 |
하이퍼파라미터 최적화 (0) | 2022.09.19 |