
캐글(Kaggle) 타이타닉 생존자 예측 대회에 처음 제출하면 대부분 0.76555라는 애매한 점수를 받게 됩니다. 이 글은 그 숫자가 어디서 나오는지 짚어보고, 캐글 타이타닉 정확도 80% 넘기기를 실제로 달성하기 위해 baseline 76.5%에서 순서대로 올린 3단계(성별·등급 규칙 → 특성공학 → 모델 교체)를 코드와 함께 정리합니다. 캐글 계정을 막 만들고 첫 제출을 앞둔 분이라면 이 순서대로 따라 하는 것만으로 상위권 진입에 필요한 기본기를 확보할 수 있습니다.
baseline 76.5%는 어디서 나온 숫자인가
캐글 타이타닉 대회의 데이터 폴더에는 gender_submission.csv라는 예시 제출 파일이 기본 제공됩니다. 이 파일은 별도의 모델 학습 없이 “여성 승객은 전원 생존, 남성 승객은 전원 사망”이라는 단 하나의 규칙만 적용한 결과입니다. 이 파일을 그대로 제출(Submit Predictions)하면 public leaderboard 기준 정확도 0.76555가 나옵니다. 실제 타이타닉 참사에서 여성과 아동 우선 구조 원칙이 작동했기 때문에, 성별 하나만으로도 꽤 높은 정확도가 나오는 것입니다.
문제는 많은 입문자가 이 숫자를 최종 목표로 착각하거나, 반대로 “겨우 이 정도 규칙으로 76%가 나온다고?”라며 지나치는 경우입니다. 이 baseline의 의미는 명확합니다. 앞으로 만드는 어떤 모델이든 최소한 이 성별 규칙보다는 더 많은 정보를 활용해야 한다는 기준선(threshold) 역할을 합니다. 공식 대회 페이지와 데이터 설명은 Kaggle Titanic 대회 페이지의 Data 탭에서 확인할 수 있습니다.
1단계: Pclass·Sex만으로 실제 모델 학습하기
첫 번째 단계는 규칙이 아니라 진짜 지도학습 모델을 처음 돌려보는 것입니다. train.csv에서 결측치가 없는 컬럼인 Pclass(객실 등급)와 Sex(성별)만 가지고 로지스틱 회귀를 학습시킵니다. 이 두 변수만 써도 성별 단일 규칙보다 더 세밀한 확률 기반 예측이 가능해집니다.
import pandas as pd
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import cross_val_score
train = pd.read_csv("train.csv")
X = pd.get_dummies(train[["Pclass", "Sex"]], columns=["Sex"], drop_first=True)
y = train["Survived"]
model = LogisticRegression()
scores = cross_val_score(model, X, y, cv=5)
print(scores.mean())

이 코드에서 pd.get_dummies는 문자열인 Sex 컬럼을 0/1 숫자로 바꿔주는 원-핫 인코딩 함수이고, cross_val_score는 데이터를 5등분(cv=5)해서 매번 다른 부분을 검증셋으로 쓰며 평균 정확도를 계산해주는 sklearn 함수입니다. 이 단계에서는 나이나 요금 같은 결측치가 있는 컬럼을 아직 건드리지 않는 것이 핵심입니다. 결측치 처리는 다음 단계에서 다루며, 여기서는 “규칙 기반 76.5%”를 “학습 기반 모델”로 바꾸는 전환 자체에 의미를 둡니다. 등급과 성별을 교차한 생존율 패턴(1등급 여성의 생존율이 3등급 남성보다 압도적으로 높은 구조)을 모델이 스스로 학습하게 되는 첫 경험이기도 합니다.
2단계: Title·FamilySize 특성공학으로 정보량 늘리기
두 번째 단계는 원본 데이터에 숨어 있는 정보를 꺼내는 특성공학(feature engineering)입니다. 타이타닉 데이터셋에서 가장 널리 알려진 기법은 Name 컬럼에서 호칭(Title)을 추출하는 것입니다. 예를 들어 “Braund, Mr. Owen Harris”라는 이름에서 정규표현식으로 “Mr”만 뽑아내면, 단순 성별보다 더 세분화된 사회적 지위·나이대 정보를 얻을 수 있습니다.
train["Title"] = train["Name"].str.extract(r",\s*([^.]+)\.")
train["Title"] = train["Title"].replace(
["Lady", "Countess", "Capt", "Col", "Don", "Dr",
"Major", "Rev", "Sir", "Jonkheer", "Dona"], "Rare"
)
train["Title"] = train["Title"].replace({"Mlle": "Miss", "Ms": "Miss", "Mme": "Mrs"})
train["FamilySize"] = train["SibSp"] + train["Parch"] + 1
train["IsAlone"] = (train["FamilySize"] == 1).astype(int)
train["Age"] = train.groupby("Title")["Age"].transform(lambda s: s.fillna(s.median()))
이 코드에서 세 가지가 동시에 일어납니다. 첫째, 정규표현식 ,\s*([^.]+)\.로 쉼표와 마침표 사이의 호칭 문자열을 뽑아냅니다. 둘째, 희귀 호칭(Lady, Col, Dr 등)을 “Rare”로 묶어 표본이 너무 적은 범주가 모델을 과적합시키는 것을 방지합니다. 셋째, Age 결측치를 전체 평균이 아니라 Title별 중앙값으로 채우는데, 이렇게 하면 “Master”(어린 남아)와 “Mr”(성인 남성)의 나이 분포 차이를 반영할 수 있어 단순 전체 중앙값 대체보다 정보 손실이 적습니다. SibSp(형제자매/배우자 수)와 Parch(부모/자녀 수)를 더한 FamilySize는 “혼자 탑승”과 “대가족”이 생존율에서 보이는 U자형 패턴을 모델이 학습할 수 있게 해줍니다.
3단계: RandomForest로 교체하고 교차검증으로 확인하기
세 번째 단계는 선형 모델인 로지스틱 회귀를 트리 기반 앙상블 모델인 RandomForestClassifier로 바꾸는 것입니다. 타이타닉 데이터는 변수 간 상호작용(예: 등급과 성별의 조합, 나이와 호칭의 조합)이 생존율에 크게 작용하는데, 트리 기반 모델은 이런 비선형 상호작용을 별도의 수식 설계 없이 자동으로 포착합니다.

from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import cross_val_score
features = ["Pclass", "Sex", "Age", "Fare", "FamilySize", "IsAlone", "Title", "Embarked"]
X = pd.get_dummies(train[features], columns=["Sex", "Title", "Embarked"], drop_first=True)
X["Fare"] = X["Fare"].fillna(X["Fare"].median())
y = train["Survived"]
rf = RandomForestClassifier(
n_estimators=300, max_depth=5, random_state=42, n_jobs=-1
)
scores = cross_val_score(rf, X, y, cv=5)
print(scores.mean(), scores.std())
여기서 max_depth=5로 트리 깊이를 제한하는 이유는 타이타닉 학습 데이터가 891행밖에 되지 않아, 깊은 트리를 그대로 두면 훈련셋에는 완벽히 맞지만 리더보드 제출 시 성능이 떨어지는 과적합이 쉽게 발생하기 때문입니다. n_estimators=300은 300개의 결정 트리를 만들어 다수결로 최종 예측을 정하는 앙상블 크기이고, random_state=42는 재현 가능한 결과를 위한 난수 고정값입니다. 이 3단계까지 거치면 1단계의 단순 로지스틱 모델보다 교차검증 정확도가 유의미하게 상승하는 것을 scores.mean() 값으로 직접 확인할 수 있으며, 커뮤니티에 공개된 다수의 타이타닉 노트북에서도 Title·FamilySize 특성공학과 트리 기반 모델을 결합한 뒤 리더보드 점수가 baseline 대비 상승했다는 사례가 꾸준히 보고됩니다. 정확한 점수는 특성 선택과 하이퍼파라미터에 따라 달라지므로, 반드시 본인의 cross_val_score 결과와 실제 제출 후 리더보드 점수를 비교해 보는 것이 중요합니다. 모델별 정확도 검증 방법은 scikit-learn 공식 문서의 cross_val_score 설명에서 세부 파라미터를 확인할 수 있습니다.
제출 전 체크리스트와 다음 단계
세 단계를 정리하면 다음과 같습니다. 1단계는 gender_submission.csv의 규칙 기반 예측을 벗어나 Pclass와 Sex만으로 실제 지도학습 모델을 돌려보는 단계, 2단계는 Name에서 Title을 추출하고 FamilySize를 만들어 원본 데이터의 숨은 정보를 끌어내는 특성공학 단계, 3단계는 선형 모델을 RandomForestClassifier 같은 트리 앙상블로 교체해 변수 간 상호작용을 잡아내는 단계입니다.
실제 제출(test.csv 예측 → submission.csv 생성 → Kaggle Submit Predictions) 전에는 test.csv에도 동일한 전처리 함수(Title 추출, FamilySize 계산, 결측치 대체)를 빠짐없이 적용했는지 반드시 확인해야 합니다. 훈련 데이터와 테스트 데이터의 전처리 로직이 조금이라도 다르면 로컬 교차검증 점수와 실제 리더보드 점수 사이에 큰 괴리가 생깁니다. 다음 단계로는 GridSearchCV나 RandomizedSearchCV로 max_depth, n_estimators 같은 하이퍼파라미터를 체계적으로 탐색하거나, LightGBM·XGBoost 같은 그래디언트 부스팅 모델로 교체해 보는 것을 추천합니다.
자주 묻는 질문(FAQ)
Q1. gender_submission.csv를 그대로 제출해도 되나요?
가능합니다. 이 파일은 캐글이 제공하는 정식 제출 양식 예시이므로 그대로 업로드하면 채점이 되고 0.76555 점수를 받게 됩니다. 다만 이는 대회 참여 방법을 익히기 위한 예시일 뿐, 실제 모델 없이는 그 이상의 점수 향상을 기대할 수 없습니다.
Q2. Age 결측치를 그냥 평균값으로 채우면 안 되나요?
채워도 제출은 됩니다. 다만 Title별 중앙값으로 나눠서 채우는 방식이 “Master”(소년)와 “Mr”(성인)처럼 서로 다른 나이대 집단의 특성을 보존해주기 때문에, 전체 평균 하나로 일괄 대체하는 것보다 정보 손실이 적습니다.
Q3. RandomForest 다음에는 어떤 모델을 시도해야 하나요?
LightGBM이나 XGBoost 같은 그래디언트 부스팅 계열이 타이타닉처럼 표(tabular) 형태 데이터에서 널리 쓰입니다. 다만 데이터 규모가 891행으로 작은 편이라 복잡한 모델일수록 교차검증과 하이퍼파라미터 튜닝을 더 꼼꼼히 병행해야 과적합을 피할 수 있습니다.