먼저 필요한 라이브러리를 설치하고 가져온 후, Google Drive를 연결하여 데이터를 불러왔다.
import os
import random
import pytz
import pandas as pd
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
import koreanize_matplotlib
import lightgbm as lgb
import torch
from tqdm import tqdm
from datetime import datetime
from collections import Counter
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import roc_curve, auc, confusion_matrix
from imblearn.over_sampling import SMOTE
from google.colab import drive
drive.mount('/content/drive')
또한, 재현성을 확보하기 위해 난수 시드를 설정하였다.
def reset_seeds(seed=42):
random.seed(seed)
os.environ['PYTHONHASHSEED'] = str(seed)
np.random.seed(seed)
torch.manual_seed(seed)
torch.cuda.manual_seed(seed)
torch.backends.cudnn.deterministic = True
타이타닉 데이터셋을 불러온 후 기본적인 정보를 확인하였다.
ori_train = pd.read_csv("train.csv")
ori_test = pd.read_csv("test.csv")
default_submission = pd.read_csv("submission.csv")
데이터의 구조를 확인한 결과, survived
열은 train.csv
에만 존재하였으며, test.csv
에는 포함되지 않았다.
age
, fare
의 결측값은 같은 pclass
와 gender
그룹 내 평균으로 대체embarked
는 최빈값(mode
)으로 대체cabin
값이 있는지 여부를 새로운 컬럼 has_cabin
으로 생성train['age'].fillna(train.groupby(['pclass', 'gender'])['age'].transform('mean'), inplace=True)
test['age'].fillna(test.groupby(['pclass', 'gender'])['age'].transform('mean'), inplace=True)
ori_te['age'].fillna(ori_te.groupby(['pclass', 'gender'])['age'].transform('mean'), inplace=True)
train['embarked'].fillna(train['embarked'].mode()[0], inplace=True)
test['embarked'].fillna(test['embarked'].mode()[0], inplace=True)
ori_te['embarked'].fillna(ori_te['embarked'].mode()[0], inplace=True)
새로운 특성을 추가하는 경우, train
, test
, ori_te
데이터셋 모두에 동일한 방식으로 반영하였다.
for df in [train, test, ori_te]:
df['avg_age_by_pclass'] = df.groupby('pclass')['age'].transform('mean')
df['avg_fare_by_pclass'] = df.groupby('pclass')['fare'].transform('mean')
for df in [train, test, ori_te]:
df['family_size'] = df['sibsp'] + df['parch'] + 1
df['is_alone'] = (df['family_size'] == 1).astype(int)
for df in [train, test, ori_te]:
df['infant'] = (df['age'] < 5).astype(int)
for df in [train, test, ori_te]:
df['gender_infant'] = df['gender'] + '_' + df['infant'].astype(str)
df['gender_pclass'] = df['gender'] + '_' + df['pclass'].astype(str)
reset_seeds()
# Light GBM 모델 평가
score_tr_lgb = model_lgb_V10.score(X_tr_scaled, y_tr)
score_te_lgb = model_lgb_V10.score(X_te_scaled, y_te)
print(f'{model_lgb_V10} : {score_tr_lgb}, {score_te_lgb}')
# AUC 점수 평가
y_pred = model_lgb_V10.predict_proba(X_te_scaled)[:,1]
fpr, tpr, thresholds = roc_curve(y_te, y_pred)
auc_te = auc(fpr, tpr)
print(f'{model_lgb_V10}: {auc_te}')
# 혼동 행렬 시각화
y_pred_class = model_lgb_V10.predict(X_te_scaled)
norm_conf_mx = confusion_matrix(y_te, y_pred_class, normalize="true")
plt.figure(figsize=(7, 5))
sns.heatmap(norm_conf_mx, annot=True, cmap="coolwarm", linewidth=0.5, fmt=".2f")
plt.xlabel('Predicted')
plt.ylabel('Actual')
plt.title('Confusion Matrix (Normalized)')
plt.show()
df_results = pd.DataFrame(args.results).assign(
score_diff=lambda df: abs(df['score_tr'] - df['score_te']),
auc_diff=lambda df: abs(df['score_te'] - df['auc_te']),
total_diff=lambda df: abs(df['score_tr'] - df['score_te']) + abs(df['score_te'] - df['auc_te'])
).sort_values(by=['total_diff', 'auc_te'], ascending=[True, False])
df_results
이 방법을 활용하여 가장 일반화 성능이 좋은 모델을 최종적으로 선택하였다.
groupby
)를 진행하는 과정에서 기준 컬럼에 결측치가 존재하여 처리가 완벽하게 이루어지지 않았다. # pclass와 gender로 그룹화하여 그룹별 평균값으로 결측치 대체
age_mean = train.groupby(['pclass', 'gender'])['age'].transform(lambda x: x.fillna(x.mean()))
fare_mean = train.groupby(['pclass', 'gender'])['fare'].transform(lambda x: x.fillna(x.mean()))
# embarked와 cabin에 대해 mode()를 사용하는 이유는 두 열이 범주형 데이터이기 때문
embarked_mode = train['embarked'].mode().values[0]
enc_tr.isnull().sum().sum(), enc_te.isnull().sum().sum(), enc_ori_te.isnull().sum().sum()
(0, 0, 0)
이 되어야 하지만, 일부 결측치가 여전히 존재했다. age
와 fare
컬럼의 결측치를 그룹별 평균값으로 채우는 과정에서 그룹화 기준 컬럼 자체(pclass
, gender
)에 결측치가 있는 경우, 대체가 제대로 이루어지지 않았다. # 그룹별 평균으로 결측치 대체 후에도 남아있는 경우, 전체 평균값으로 대체
age_mean = train.groupby(['pclass', 'gender'])['age'].transform(lambda x: x.fillna(x.mean()))
age_overall_mean = train['age'].mean()
fare_mean = train.groupby(['pclass', 'gender'])['fare'].transform(lambda x: x.fillna(x.mean()))
fare_overall_mean = train['fare'].mean()
# embarked는 최빈값으로 대체
embarked_mode = train['embarked'].mode().values[0]
enc_tr.isnull().sum().sum(), enc_te.isnull().sum().sum(), enc_ori_te.isnull().sum().sum()
(0, 0, 0)
으로 결측치가 완전히 제거되었다. ori_train = pd.read_csv(args.train_csv)
ori_test = pd.read_csv(args.test_csv)
default_submission = pd.read_csv(args.default_submission)
ori_train.shape, ori_test.shape, default_submission.shape
train.csv
데이터가 (916, 12)
인 경우, 원핫 인코딩을 하면 80개 이상의 특성이 생성된다. 대안적인 인코딩 기법
1) 빈도 기반 인코딩(Frequency Encoding)
'A' → 100
, 'B' → 50
, 'C' → 10
2) 타겟 인코딩(Target Encoding)
"A" 그룹의 생존률이 0.8
→ "A": 0.8
3) 임베딩(Embedding) 활용
결론
score_diff
와 auc_diff
를 계산하여 비교했다. df_results = pd.DataFrame(args.results).assign(
score_diff=lambda df: abs(df['score_tr'] - df['score_te']),
auc_diff=lambda df: abs(df['score_te'] - df['auc_te']),
total_diff=lambda df: abs(df['score_tr'] - df['score_te']) + abs(df['score_te'] - df['auc_te'])
).sort_values(by=['total_diff', 'auc_te'], ascending=[True, False])
df_results
total_diff
값이 가장 작은 모델을 선택하여 최종 제출 모델로 결정했다. import matplotlib.pyplot as plt
# 예제 데이터
experiments = ['Base Model', 'Feature Engineering', 'Hyperparameter Tuning', 'Final Model']
scores = [0.78, 0.82, 0.85, 0.87]
plt.figure(figsize=(8, 5))
plt.plot(experiments, scores, marker='o', linestyle='-')
plt.xlabel('Experiment Steps')
plt.ylabel('Model Score')
plt.title('Model Improvement Over Experiments')
plt.grid(True)
plt.show()
import numpy as np
# 벡터 (1차원)
vector = np.array([1, 2, 3])
print(vector.shape) # (3,)
# 행렬 (2차원)
matrix = np.array([[1, 2], [3, 4]])
print(matrix.shape) # (2,2)
Keep
: 현재 만족하고 있는 부분, 계속 이어갔으면 하는 부분Problem
: 불편하게 느끼는 부분, 개선이 필요하다고 생각되는 부분Try
: Problem에 대한 해결책, 실행 가능한 것머신러닝 경진대회에 적극적으로 참여하면서 실전 경험을 쌓았다.
꾸준한 학습 습관을 유지하고 있다.
코드 분석 능력을 키우기 위해 노력했다.
머신러닝 모델 성능 개선에 많은 시간을 투자하면서 토익 공부 시간이 부족했다.
머신러닝 개념 중 수학적인 이해가 부족한 부분이 있었다.
머신러닝 경진대회에서 상위권을 목표로 했으나, 최종적으로 6위에 머물렀다.
토익 공부 시간을 좀 더 체계적으로 배분하겠다.
머신러닝 모델 개선 전략을 좀 더 체계적이고, 구체적으로 세우겠다.