타이타닉... 가장 유명한 Caggle 챌린지고 데이콘에서도 학습차원에서 도전할 수 있당...
이번에 교육자료 만들면서 오래간만에 풀어봤는데 하다보니 재밌어서 내용이 길어짐...ㅜ
임포트 + 데이터 로드 + 일단 MLP로 함 돌려보기
일단 라이브러리 임포트... 교육자료 만드려고 한거라 지저분함... 다시 봐도 별걸 다 한 듯...
import os
import numpy as np
import pandas as pd
import sweetviz as sv
import xgboost
from sklearn.tree import DecisionTreeClassifier, DecisionTreeRegressor
from sklearn.model_selection import cross_val_score, train_test_split
from sklearn.linear_model import LinearRegression, Ridge, Lasso, LogisticRegression
from sklearn.metrics import r2_score,mean_absolute_error, mean_squared_error
from sklearn.neural_network import MLPRegressor, MLPClassifier
from sklearn.ensemble import RandomForestClassifier,VotingClassifier
from sklearn.model_selection import StratifiedKFold, RandomizedSearchCV, GridSearchCV
from tqdm import trange, notebook
데이터 읽어오고... 맨날 까먹어서 귀찮게하는 test 데이터에 fare 결측치부터 채워줌...
path = os.getcwd() + '/titanic'
train = pd.read_csv(path + '/train.csv')
test = pd.read_csv(path + '/test.csv')
test["Fare"].fillna(test.groupby("Pclass")["Fare"].transform("median"), inplace=True)
EDA 자동으로 해주는 넘넘 사랑스런 SweetViz로 데이터 탐색 한 번 해주고...
train_report = sv.analyze(train)
train_report.show_html(filepath = path + '/train_report.html')
test_report = sv.analyze(test)
test_report.show_html(filepath = path + '/test_report.html')
Sex 컬럼 0과 1로 원핫인코딩 하고....
train['Sex'] = train['Sex'].map({'male' : 0, 'female' : 1})
test['Sex'] = test['Sex'].map({'male' : 0, 'female' : 1})
Pclass, SibSq, Sex만 가지고 일단 모델 한 번 돌려봤어요. 첫 테스트 모델은 MLPRegressor(다층 퍼셉트론 회귀모델). Classifier 안쓰고 Regressor 쓴 이유는 타이타닉 데이콘 Scoring이 요새 RMSE에서 AUC로 바뀌어서인데 AUC면 당연히 바이너리가 더 잘 나올 것 같지만 왜인지 확률값이 더 잘 나오더라는 경험 때문...(잘못된 생각일 가능성 매우 높음ㅜㅜ)
x = train[['Pclass', 'SibSp', 'Sex']]
y = train['Survived']
x_train, x_test, y_train, y_test = train_test_split(x, y, train_size =0.7, test_size=0.3)
model = MLPRegressor(hidden_layer_sizes=[512, 4], max_iter=5000, alpha=0.005, random_state=42)
model.fit(x_train, y_train)
print(r2_score(y_train, model.predict(x_train))) #output: 0.37
print(r2_score(y_test, model.predict(x_test))) #output: 0.43
test_x = test[['Pclass', 'SibSp', 'Sex']]
test_y_pred = model.predict(test_x)
submission = pd.read_csv(path + "/submission.csv")
submission['Survived'] = test_y_pred
submission.to_csv(path + '/submission_v0.01.csv', index = False)
일단 이것만 했을 때 R-Square는 train: 0.37, test: 0.43 정도 나옴...(잉? 벌써 머지...) 리더보드에 제출해보니 AUC Score 0.7525나왔고 MLP의 성능에 새삼 다시 놀라고 저 컬럼들이 도대체 왜...? 너네가 왜...? 이런 의문이 들기 시작함...
데이터 전처리
본격적으로 전처리를 해보자요!
첫번째 전처리 대상은 Age. 결측치 갯수는 train, test에서 각각 177, 83개인 컬럼인데요. Pclass(객실등급), SibSp(형제자매+배우자), Parch(부모+자녀), Fare(요금)을 가지고 Ridge로 나이를 예측해서 결측치를 채울거에요.
#데이터셋에서 Age열에서 결측치 제외하고 추출: age_null_train
age_null_train = train.dropna(subset = ['Age'])
#age_null_train에서 학습데이터와 정답데이터 정의
train_xx = age_null_train[['Pclass', 'SibSp', 'Parch', 'Fare']]
train_yy = age_null_train['Age']
#학습데이터셋의 Age열에서 결측치가 있는 행 추출
age_null_at_train = train.loc[train['Age'].isnull()]
#테스트데이터셋의 Age열에서 결측치가 있는 행 추출
age_null_at_test = test.loc[train['Age'].isnull()]
#학습데이터셋에서 예측용 데이터 추출
age_null_at_train = age_null_at_train[['Pclass', 'SibSp', 'Parch', 'Fare']]
#테스트데이터셋에서 예측용 데이터 추출
age_null_at_test = age_null_at_test[['Pclass', 'SibSp', 'Parch', 'Fare']]
#모든 데이터를 실수 자료 형태로 전환
train_xx = train_xx.apply(pd.to_numeric)
train_xx = train_xx.astype(float)
train_yy = train_yy.apply(pd.to_numeric)
train_yy = train_yy.astype(float)
#모델 정의 및 학습
model = Ridge()
model.fit(train_xx, train_yy)
#예측
test_at_train_pred = abs(model.predict(age_null_at_train))
test_at_test_pred = abs(model.predict(age_null_at_test))
print(test_at_train_pred)
print(test_at_test_pred)
결과 나온거 보니까 나이 예측한 게 제.법. 그럴듯해 보여서 일단 만족...ㅎㅎ;;
이제 Age 컬럼의 결측치를 예측한 데이터로 채우겠어요ㅎㅎ
for i in range(len(train)):
if np.isnan(train.iloc[i, 5]) == True:
for j in range(len(test_at_train_pred)):
train.iloc[i, 5] = test_at_train_pred[j]
else:
pass
print('수정된 학습데이터셋 Age 값 확인: \n', train['Age'])
for i in range(len(test)):
if np.isnan(test.iloc[i, 4]) == True:
for j in range(len(test_at_test_pred)):
test.iloc[i, 4] = test_at_test_pred[j]
else:
pass
print('수정된 테스트데이터셋 Age 값 확인: \n', test['Age'])
데이터가 보강되었으니 아까 모델을 요것만 추가해서 다시 돌려보자! 빨리빨리! 궁금해궁금해!!!
#age 데이터를 추가해서 아까 모델 다시 돌려보기
x = train[['Pclass', 'SibSp', 'Age', 'Sex']]
y = train['Survived']
x_train, x_test, y_train, y_test = train_test_split(x, y, train_size =0.7, test_size=0.3)
model = MLPRegressor(hidden_layer_sizes=[512, 4], max_iter=5000, alpha=0.005, random_state=42)
model.fit(x_train, y_train)
print('train r2 score: ', r2_score(y_train, model.predict(x_train))) #output: 0.38
print('test r2 score: ', r2_score(y_test, model.predict(x_test))) #output: 0.42
test_x = test[['Pclass', 'SibSp', 'Age', 'Sex']]
test_y_pred = model.predict(test_x)
submission['Survived'] = test_y_pred
submission.to_csv(path + '/submission_v0.02.csv', index = False)
성능이 (아주 약간) 향상되었다!!! 너무너무 정말정말 기쁘다!!! 이 맛에 데이콘 하지ㅜㅜㅜ
요번엔 승객이 어느 항구에서 탑승했는지 알려주는 Embarked 데이터를 전처리를 해 줄 거에요. 근데 나중에 결국엔 날려요... 별로 필요 없더라구요... 일단 했어요.
#embarked 데이터 전처리: 원핫인코딩
train['Embarked'] = train['Embarked'].fillna('S')
test['Embarked'] = test['Embarked'].fillna('S')
train_embark_dummy = pd.get_dummies(train['Embarked'])
test_embark_dummy = pd.get_dummies(test['Embarked'])
train = pd.concat([train, train_embark_dummy], axis = 1)
test = pd.concat([test, test_embark_dummy], axis = 1)
#나중에 cabin데이터 결측치 채워줄 때 여기도 원핫인코딩 쓰는데 C 컬럼이 겹쳐서 이름 바꿔줌
train.rename(columns={'C' : 'C_'}, inplace = True)
test.rename(columns={'C' : 'C_'}, inplace = True)
이번엔 cabin 컬럼 결측치를 채워볼건데요. cabin 데이터는 탑승자의 객실 데이터에요. C102, B24, A85 뭐 이렇게 생겼는데 타이타닉호는 객실의 층 수가 나눠져 있고 층마다 섹터가 나눠져 있으니까 어느 섹터의 객실에 있느냐가 탈출하는 통로랑 관계가 있을 것 같아서 맨 앞글자인 영문 대문자만 쓰고 그 다음에 나오는 숫자는 다 날릴거에요. 남은 영문 대문자로는 원핫인코딩을 할거고 요금 데이터인 fare 컬럼으로 랜덤포레스트 classifier 써서 결측치를 예측해서 데이터셋을 채울거에요.
#데이터셋의 Cabin열에서 결측치 제외하고 추출: cabin_null_train
cabin_null_train = df1_train.dropna(subset = ['Cabin'])
#cabin_null_train에서 학습데이터와 정답데이터 정의
train_xxx = cabin_null_train[['Fare']]
train_cabin_dummy = pd.get_dummies(cabin_null_train['Cabin'])
train_yyy = train_cabin_dummy[['A', 'B', 'C', 'D', 'E', 'F', 'G', 'T']]
#학습데이터셋의 Cabin열에서 결측치가 있는 행 추출
cabin_null_at_train = df1_train.loc[df1_train['Cabin'].isnull()]
#테스트데이터셋의 Cabin열에서 결측치가 있는 행 추출
cabin_null_at_test = df1_test.loc[df1_test['Cabin'].isnull()]
#학습데이터셋에서 예측용 데이터 추출
cabin_null_at_train = cabin_null_at_train[['Fare']]
#테스트데이터셋에서 예측용 데이터 추출 및 결측치 대체(평균)
cabin_null_at_test = cabin_null_at_test[['Fare']]
cabin_null_at_test = cabin_null_at_test.fillna(cabin_null_at_test.mean())
#모든 데이터를 실수 자료 형태로 전환
train_xxx = train_xxx.apply(pd.to_numeric)
train_xxx = train_xxx.astype(float)
train_yyy = train_yyy.apply(pd.to_numeric)
train_yyy = train_yyy.astype(float)
#모델 정의 및 학습
model = RandomForestClassifier()
model.fit(train_xxx, train_yyy)
#예측 및 결과값 반올림(0또는 1)
test_at_train_pred = model.predict(cabin_null_at_train)
test_at_test_pred = model.predict(cabin_null_at_test)
print(test_at_train_pred)
print(test_at_test_pred)
결과는 이렇게 나왓는데 이번에도 역시 제법 그럴듯하게 객실 섹터 예측해준 것 같아서 다시 만족♥
이제 데이터셋 결측치 대체해주려고 보니까 test 데이터엔 T 로 시작하는 객실이 없었나보다. 에러가 난다. 그래서 예측한 Cabin 데이터에 T 컬럼 추가해주고 0으로 값을 줬어요. 그리고 다시 결측치 대체해주니까 잘 채워졌어요.
#예측한 test 데이터의 cabin 컬럼 데이터엔 T로 시작하는 객실이 안나와서 별도로 추가
df2_train = pd.get_dummies(df1_train['Cabin'])
df2_test = pd.get_dummies(df1_test['Cabin'])
df2_test['T'] = 0
#Cabin 결측치 채워주기 - Train 데이터셋
for i in notebook.tqdm(range(len(df1_train))):
if df2_train.sum(axis=1)[i] == 0:
for j in range(len(test_at_train_pred)):
df2_train.iloc[i] = test_at_train_pred[j]
else:
pass
print('수정된 학습데이터셋 cabin 값 확인: \n', df2_train)
#Cabin 결측치 채워주기 - Test 데이터셋
for i in notebook.tqdm(range(len(df1_test))):
if df2_test.sum(axis=1)[i] == 0:
for j in range(len(test_at_test_pred)):
df2_test.iloc[i] = test_at_test_pred[j]
else:
pass
print('수정된 테스트데이터셋 cabin 값 확인: \n', df2_test)
#합체
train = pd.concat([train, df2_train], axis = 1)
test = pd.concat([test, df2_test], axis = 1)
이제 다시 모델을 돌려볼건데 이제부터는 본격적으로 돌릴거라 불필요해보이는 데이터를 날려줬어요.
#학습에 안 쓸 불필요해보이는 데이터들 제거
train_final = train.drop(['PassengerId', 'Name', 'Ticket', 'Cabin', 'Embarked'], axis = 1)
test_final = test.drop(['PassengerId', 'Name', 'Ticket', 'Cabin', 'Embarked'], axis = 1)
이제까지 전처리한 데이터셋으로 아까 모델을 다시 또 돌렸는데...
#train - test - split
x = train_final.drop(['Survived'], axis = 1)
y = train_final['Survived']
x_train, x_test, y_train, y_test = train_test_split(x, y, train_size =0.7, test_size=0.3)
#지금까지 전처리한 데이터로 아까 모델 다시 돌려보기
model = MLPRegressor(hidden_layer_sizes=[512, 4], max_iter=5000, alpha=0.005, random_state=42)
model.fit(x_train, y_train)
print(r2_score(y_train, model.predict(x_train))) #output: 0.45
print(r2_score(y_test, model.predict(x_test))) #output: 0.38
train 데이터셋에서 R-Square는 좋아졌는데 test에선 떨어짐요...ㅜ 슬프지만 일단 저장해놓고 계속 해보는걸로...ㅜ
#예측과 제출파일 저장
test_x = test_final
test_y_pred = model.predict(test_x)
submission = pd.read_csv(path + "/submission.csv")
submission['Survived'] = test_y_pred
submission.to_csv(path + '/submission_v0.03.csv', index = False)
마지막 전처리로 Data Scaling을 했어요. fare나 age나 pclass 같은 데이터들은 min값과 max값 간 차이가 크고 특정 범위로 bias가 있을 수 있으니까 스케일링을 하는데 age랑 pclass는 MinMaxScaler를 썼고 fare는 bias가 꽤 커서 StandardScaler를 썼어요.
#Data Scaling의 중요성
from sklearn.preprocessing import StandardScaler, MinMaxScaler
train_test_data = [train_final, test_final]
scaler = MinMaxScaler()
for dataset in train_test_data:
array = dataset['Age'].values.reshape(-1,1) # 2D array로 변환
scaler.fit(array) # 스케일링에 필요한 값(최소값, range 등) 계산
dataset['AgeScale'] = pd.Series(scaler.transform(array).reshape(-1)) # 스케일링 후 series로 추가
for dataset in train_test_data:
array = dataset['Pclass'].values.reshape(-1,1) # 2D array로 변환
scaler.fit(array) # 스케일링에 필요한 값(최소값, range 등) 계산
dataset['PclassScale'] = pd.Series(scaler.transform(array).reshape(-1)) # 스케일링 후 series로 추가
scaler = StandardScaler()
for dataset in train_test_data:
array = dataset['Fare'].values.reshape(-1,1)
scaler.fit(array)
dataset['FareScale'] = pd.Series(scaler.transform(array).reshape(-1))
셀 주석이 곧 내용..ㅎㅎ
#진짜 본격적으로 여러 모델 돌려보기 전에 스케일링해서 필요없어졌거나 불필요해보이는 데이터들 날려주기
train_real_final = train_final.drop(['Age', 'Pclass', 'Fare', 'SibSp', 'Parch', 'C_', 'Q', 'S'], axis = 1)
test_real_final = test_final.drop(['Age', 'Pclass', 'Fare', 'SibSp', 'Parch', 'C_', 'Q', 'S'], axis = 1)
계속 쓰고 있던 MLPRegressor. 평가지표 나온 값은 print 문 옆에 주석으로 달아놨어요.
#MLPRegressor(계속 쓰고 있던 모델)
x = train_real_final.drop(['Survived'], axis = 1)
y = train_real_final['Survived']
x_train, x_test, y_train, y_test = train_test_split(x, y, train_size =0.7, test_size=0.3)
MLPR = MLPRegressor(hidden_layer_sizes=[512, 4], max_iter=5000, alpha=0.005, random_state=42)
MLPR.fit(x_train, y_train)
print(r2_score(y_train, MLPR.predict(x_train))) #output: 0.48
print(r2_score(y_test, MLPR.predict(x_test))) #output: 0.46
test_x = test_real_final
test_y_pred = np.around(MLPR.predict(test_x))
submission = pd.read_csv(path + "/submission.csv")
submission['Survived'] = test_y_pred
submission.to_csv(path + '/submission_v0.04.csv', index = False)
#예측한 결과값 반올림하고 acc도 측정해보기
preds = np.around(MLPR.predict(x_test))
accuracy = float(np.sum(preds==y_test))/y_test.shape[0]
print("accuracy: %f" % (accuracy)) #output: 0.82
요건 MLPClassifier로 해본건데 R-Square가 아까보다 떨어졌는데도 accuracy가 높아짐요... 잉....?ㅠㅠㅠ 혼란이 온다...
#MLPClassifier
MLPC = MLPClassifier(hidden_layer_sizes=(512, 4), max_iter=5000)
MLPC.fit(x_train, y_train)
print(r2_score(y_train, MLPC.predict(x_train))) #output: 0.30
print(r2_score(y_test, MLPC.predict(x_test))) #output: 0.33
test_x = test_real_final
test_y_pred = np.around(MLPC.predict(test_x))
submission = pd.read_csv(path + "/submission.csv")
submission['Survived'] = test_y_pred
submission.to_csv(path + '/submission_v0.05.csv', index = False)
preds = np.around(MLPC.predict(x_test))
accuracy = float(np.sum(preds==y_test))/y_test.shape[0]
print("accuracy: %f" % (accuracy)) #output: 0.84
#예측과 제출파일 저장
test_y_pred = MLPC.predict(test_x)
submission = pd.read_csv(path + "/submission.csv")
submission['Survived'] = test_y_pred
submission.to_csv(path + '/submission_v0.06.csv', index = False)
요건 XGBoost Regressor로 해본 거. train R-Square가 매우 높아졌고 test도 양호... 그렇지만 오버피팅 의심감...
#XGBoost Regressor
xgbR_model = xgboost.XGBRegressor(n_estimators=1000, learning_rate=0.01, gamma=0.1, subsample=0.7,
colsample_bytree=1, max_depth=9)
xgbR_model.fit(x_train, y_train)
print(r2_score(y_train, xgbR_model.predict(x_train))) #output: 0.89
print(r2_score(y_test, xgbR_model.predict(x_test))) #output: 0.46
test_y_pred = xgbR_model.predict(test_x)
submission = pd.read_csv(path + "/submission.csv")
submission['Survived'] = test_y_pred
submission.to_csv(path + '/submission_v0.11.csv', index = False)
XGBoost Classifier도 꼭 해봐야지 했어요. 이거는 parameter detector 써서 best parameter 찾아내고 모델 피팅 했어요.
#XGB Classifier와 parameter detector
params = {
'eta': [0.1, 0.01, 0.02, 0.001],
'min_child_weight': [1, 3, 5, 7, 10],
'gamma': [0, 0.5, 1, 1.5, 2, 3, 4, 5],
'subsample': [0.6, 0.7, 0.8, 0.9, 1.0],
'colsample_bytree': [0.6, 0.7, 0.8, 0.9, 1.0],
'max_depth': [3, 4, 5, 6, 7, 8, 9]
}
xgbM = xgboost.XGBClassifier(n_estimators=600, objective='binary:logistic', silent=True, nthread=1)
folds = 3
param_comb = 5
skf = StratifiedKFold(n_splits=folds, shuffle = True, random_state = 1001)
random_search = RandomizedSearchCV(
xgbM, param_distributions=params,
n_iter=param_comb,
scoring='roc_auc',
n_jobs=4,
cv=skf.split(x_train,y_train),
verbose=3,
random_state=1001)
random_search.fit(x_train, y_train)
찾아준 best parameter는.... 이렇게 나왔고요...
random_search.best_params_
'''
찾은 best parameter
{'subsample': 0.7,
'min_child_weight': 1,
'max_depth': 4,
'gamma': 3,
'eta': 0.01,
'colsample_bytree': 0.8}
'''
이 파라미터들을 넣어서 XGBClassifer를 돌렸구요.
#best parameter로 XGBClassifer 돌리기
xgbM_model = xgboost.XGBClassifier(
eta = random_search.best_params_['eta'],
n_estimators = 600,
objective = 'binary:logistic',
silent=True,
nthread=1,
subsample = random_search.best_params_['subsample'],
min_child_weight = random_search.best_params_['min_child_weight'],
max_depth = random_search.best_params_['max_depth'],
gamma = random_search.best_params_['gamma'],
colsample_bytree = random_search.best_params_['colsample_bytree'])
xgbM_model.fit(x_train, y_train)
print(r2_score(y_train, xgbM_model.predict(x_train))) #output: 0.41
print(r2_score(y_test, xgbM_model.predict(x_test))) #output: 0.39
preds = xgbM_model.predict(x_test)
accuracy = float(np.sum(preds==y_test))/y_test.shape[0]
print("accuracy: %f" % (accuracy)) #output: 0.85
test_x = test_real_final
test_y_pred = xgbM_model.predict(test_x)
submission = pd.read_csv(path + "/submission.csv")
submission['Survived'] = test_y_pred
submission.to_csv(path + '/submission_v0.07.csv', index = False)
여기까지 했을 때 최종 리더보드... 솔직히 만족스럽진 않지만 그래도 오늘 하루종일 해서 이제 너무 피곤...ㅜㅜ 잘래
'Data Analysis' 카테고리의 다른 글
다크모드 쓰는 사람들을 위한 폰트 컬러 (2) | 2022.08.20 |
---|---|
쉽게 쓰는 Torch FID Score 모듈 (0) | 2022.08.20 |
Crawling in NAVER Financial Summary, Make PEG/EPS Growth Rate/OPM Growth Rate etc: 네이버 파이낸셜서머리 크롤링해서 PEG, EPS성장율, 영업이익증가율 등 만들고 차트 그리기 (0) | 2022.04.22 |
sklearn Scaler (0) | 2022.02.19 |
이미지 데이터 증강 논문 읽기: Improving Deep Learning using Generic Data Augmentation(Luke Taylor et al., 2017) (0) | 2022.01.21 |
댓글