캐글 타이타닉으로 배우는 자주 쓰는 머신러닝 기법 5가지
로지스틱 회귀부터 랜덤 포레스트, Extra Trees, Gradient Boosting, SVM, 그리고 Voting 앙상블까지
머신러닝을 공부할 때 가장 많이 접하는 예제 중 하나가 캐글의 Titanic 생존자 예측 문제입니다. 승객의 성별, 나이, 객실 등급, 요금 등의 정보를 이용해 생존 여부를 0과 1로 예측하는 이진 분류 문제입니다.
타이타닉 데이터는 행과 열로 구성된 전형적인 표 형식(tabular) 데이터입니다. 따라서 신경망을 깊게 만드는 것보다 데이터의 특성에 맞는 feature engineering과 전처리, 모델 선택이 더 중요할 때가 많습니다.
이번 글에서는 타이타닉 문제에 적용한 다음 머신러닝 기법을 각각 살펴보겠습니다.
- Logistic Regression
- Random Forest
- Extra Trees
- Gradient Boosting
- Support Vector Machine
- Soft Voting Ensemble
마지막에는 교차검증과 하이퍼파라미터 탐색을 거쳐 Kaggle 제출 파일을 만드는 과정까지 정리합니다.
1. 문제와 데이터 이해하기
타이타닉의 목표 변수는 Survived입니다.
0: 사망1: 생존
주요 입력 변수는 다음과 같습니다.
| 변수 | 의미 |
|---|---|
Pclass |
객실 등급 |
Sex |
성별 |
Age |
나이 |
SibSp |
함께 탑승한 형제자매·배우자 수 |
Parch |
함께 탑승한 부모·자녀 수 |
Fare |
운임 |
Cabin |
객실 번호 |
Embarked |
승선 항구 |
Name |
승객 이름 |
원본 변수만 사용할 수도 있지만, 타이타닉에서는 다음과 같은 파생 변수가 유용합니다.
df["FamilySize"] = df["SibSp"] + df["Parch"] + 1
df["IsAlone"] = (df["FamilySize"] == 1).astype(int)
df["Title"] = (
df["Name"]
.str.extract(r",\s*([^.]*)\.", expand=False)
.str.strip()
)
common_titles = ["Mr", "Mrs", "Miss", "Master"]
df["Title"] = df["Title"].where(
df["Title"].isin(common_titles),
"Other"
)
df["CabinKnown"] = df["Cabin"].notna().astype(int)
df["Deck"] = df["Cabin"].str[0].fillna("Unknown")
df["FarePerPerson"] = df["Fare"] / df["FamilySize"]
Title에는 성별과 대략적인 연령대가 함께 반영됩니다. FamilySize와 IsAlone은 구조 과정에서 가족 단위의 행동이 있었을 가능성을 모델이 학습하도록 도와줍니다.
2. 누수를 막는 전처리 Pipeline
모델보다 먼저 신경 써야 하는 부분은 데이터 누수(data leakage)입니다.
예를 들어 전체 데이터의 Age 중앙값을 계산한 뒤 train과 validation으로 나누면, validation 데이터의 정보가 training 과정에 조금 섞입니다. StandardScaler도 전체 데이터에 fit하면 같은 문제가 발생합니다.
Scikit-learn의 Pipeline과 ColumnTransformer를 사용하면 교차검증의 각 training fold 안에서만 결측값 처리와 scaling이 학습됩니다.
from sklearn.compose import ColumnTransformer
from sklearn.impute import SimpleImputer
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import OneHotEncoder, StandardScaler
numeric_features = [
"Age", "SibSp", "Parch", "Fare",
"FamilySize", "IsAlone", "FarePerPerson"
]
categorical_features = [
"Pclass", "Sex", "Embarked", "Title", "Deck"
]
numeric_transformer = Pipeline([
("imputer", SimpleImputer(strategy="median")),
("scaler", StandardScaler()),
])
categorical_transformer = Pipeline([
("imputer", SimpleImputer(strategy="most_frequent")),
("onehot", OneHotEncoder(handle_unknown="ignore")),
])
preprocessor = ColumnTransformer([
("num", numeric_transformer, numeric_features),
("cat", categorical_transformer, categorical_features),
])
이제 모델을 다음과 같이 전처리와 하나의 Pipeline으로 묶을 수 있습니다.
model_pipeline = Pipeline([
("preprocessor", preprocessor),
("classifier", model),
])
3. Logistic Regression
개념
Logistic Regression은 이름에 Regression이 들어가지만 대표적인 이진 분류 알고리즘입니다. 입력 변수들의 가중합을 구한 뒤 sigmoid 함수를 적용해 클래스 1일 확률을 계산합니다.
$$
P(y=1|x) = \frac{1}{1+e^{-(w^Tx+b)}}
$$
확률이 일반적으로 0.5 이상이면 1, 그렇지 않으면 0으로 분류합니다.
타이타닉에서의 역할
성별, 객실 등급, 호칭처럼 생존 여부와 비교적 분명한 관계가 있는 변수에서는 Logistic Regression이 좋은 기준 모델이 됩니다. 모델이 단순해 데이터가 많지 않은 타이타닉에서도 과적합 위험이 비교적 작습니다.
from sklearn.linear_model import LogisticRegression
logistic_model = LogisticRegression(
C=0.5,
max_iter=2000,
random_state=42
)
C는 규제 강도의 역수입니다.
C가 작을수록 규제가 강해짐C가 클수록 training 데이터에 더 세밀하게 맞춤
장점
- 학습과 예측이 빠릅니다.
- 확률을 출력할 수 있습니다.
- 각 변수의 계수를 통해 영향 방향을 해석할 수 있습니다.
- 이진 분류의 기준 모델로 사용하기 좋습니다.
단점
- 기본적으로 선형 결정 경계를 학습합니다.
- 복잡한 비선형 관계와 변수 간 상호작용을 자동으로 찾는 능력이 제한적입니다.
언제 자주 사용하는가?
- 이진 분류의 첫 번째 기준 모델이 필요할 때
- 결과 해석이 중요할 때
- 데이터 크기가 크고 빠른 학습이 필요할 때
4. Random Forest
개념
Random Forest는 여러 개의 결정 트리를 학습한 뒤 결과를 종합하는 bagging 계열 앙상블입니다.
각 트리는 다음과 같이 서로 다른 조건에서 학습됩니다.
- 원본 데이터에서 중복을 허용해 표본을 추출합니다.
- 노드를 분할할 때 전체 feature가 아닌 일부 feature만 후보로 사용합니다.
- 여러 트리의 다수결 또는 평균으로 최종 결과를 결정합니다.
서로 조금씩 다른 트리를 모으면 단일 결정 트리보다 과적합을 줄일 수 있습니다.
from sklearn.ensemble import RandomForestClassifier
random_forest = RandomForestClassifier(
n_estimators=500,
max_depth=7,
min_samples_leaf=2,
max_features="sqrt",
random_state=42,
n_jobs=-1
)
주요 하이퍼파라미터
n_estimators: 생성할 트리 개수max_depth: 각 트리의 최대 깊이min_samples_leaf: leaf node에 필요한 최소 표본 수max_features: 각 분할에서 확인할 feature 수
타이타닉에서의 역할
Random Forest는 Sex × Pclass, Title × Age, FamilySize × Fare처럼 변수 사이에 존재하는 비선형 관계를 자동으로 학습할 수 있습니다.
장점
- 별도의 복잡한 수식 없이 비선형 관계를 학습합니다.
- 이상치와 feature scaling에 비교적 덜 민감합니다.
- feature importance를 확인할 수 있습니다.
- 기본 설정만으로도 안정적인 결과를 내는 경우가 많습니다.
단점
- 트리 수가 많으면 모델 크기와 예측 시간이 증가합니다.
- 단일 트리보다 결과를 해석하기 어렵습니다.
- 깊이를 제한하지 않으면 작은 데이터에서 과적합될 수 있습니다.
언제 자주 사용하는가?
- 표 형식 데이터의 강력한 기준 모델이 필요할 때
- 선형 관계와 비선형 관계가 함께 존재할 때
- 변수 중요도를 빠르게 확인하고 싶을 때
5. Extra Trees
개념
Extra Trees는 Random Forest와 비슷하지만 더 많은 무작위성을 사용합니다. 이름은 Extremely Randomized Trees에서 왔습니다.
Random Forest는 여러 분할 후보를 비교해 좋은 분할 지점을 선택합니다. Extra Trees는 분할 지점까지 더 무작위로 선택한 뒤 그중 좋은 후보를 사용합니다.
이 무작위성은 개별 트리의 편향을 조금 높일 수 있지만, 트리 사이의 상관관계를 낮춰 전체 앙상블의 분산을 줄이는 데 도움이 됩니다.
from sklearn.ensemble import ExtraTreesClassifier
extra_trees = ExtraTreesClassifier(
n_estimators=500,
max_depth=9,
min_samples_leaf=2,
max_features="sqrt",
random_state=42,
n_jobs=-1
)
Random Forest와 차이
| 구분 | Random Forest | Extra Trees |
|---|---|---|
| 데이터 표본 | 주로 bootstrap 표본 사용 | 기본적으로 전체 표본 사용 |
| 분할 지점 | 후보 중 최적 지점 탐색 | 더 무작위로 후보 생성 |
| 무작위성 | 높음 | 더 높음 |
| 학습 속도 | 빠름 | 대체로 더 빠름 |
타이타닉에서의 역할
타이타닉 데이터는 891행으로 크지 않습니다. Extra Trees는 다양한 무작위 트리를 결합해 작은 데이터에서 특정 패턴에 과도하게 의존하는 문제를 줄이는 데 도움이 될 수 있습니다.
장점
- 학습 속도가 비교적 빠릅니다.
- 비선형 관계와 변수 상호작용을 잘 처리합니다.
- Random Forest와 다른 형태의 오차를 만들 수 있어 앙상블에 유용합니다.
단점
- 무작위성이 지나치면 중요한 경계를 충분히 세밀하게 학습하지 못할 수 있습니다.
- feature importance는 영향의 방향을 알려주지 않습니다.
- 서로 관련된 feature가 많으면 중요도가 여러 열에 나뉘거나 편향될 수 있습니다.
Feature Importance 해석 시 주의점
Title_Mr의 중요도가 높다고 해서 “Mr이면 생존한다”는 뜻은 아닙니다. 모델이 Mr인지 아닌지를 예측 과정에서 많이 사용했다는 뜻입니다. 실제 생존 가능성을 높였는지 낮췄는지는 별도로 확인해야 합니다.
6. Gradient Boosting
개념
Gradient Boosting은 여러 트리를 독립적으로 만드는 Random Forest와 달리, 트리를 순차적으로 학습합니다.
- 첫 번째 작은 트리가 예측합니다.
- 첫 번째 트리가 틀린 부분을 다음 트리가 보완합니다.
- 이전 모델들의 오차를 줄이는 방향으로 트리를 계속 추가합니다.
- 모든 작은 트리의 결과를 합쳐 최종 예측을 만듭니다.
from sklearn.ensemble import GradientBoostingClassifier
gradient_boosting = GradientBoostingClassifier(
n_estimators=150,
learning_rate=0.03,
max_depth=3,
min_samples_leaf=3,
subsample=0.85,
random_state=42
)
주요 하이퍼파라미터
n_estimators: 순차적으로 추가할 트리 수learning_rate: 각 트리가 전체 결과에 기여하는 정도max_depth: 개별 트리의 복잡도subsample: 각 트리 학습에 사용할 데이터 비율
일반적으로 learning_rate를 낮추면 더 많은 n_estimators가 필요합니다.
타이타닉에서의 역할
Gradient Boosting은 처음에는 성별과 객실 등급 같은 큰 패턴을 학습하고, 이후 트리들이 호칭, 가족 규모, 운임 등에서 발생하는 세부적인 오차를 보완할 수 있습니다.
장점
- 표 형식 데이터에서 강력한 성능을 보이는 경우가 많습니다.
- 복잡한 비선형 관계를 학습할 수 있습니다.
- 얕은 트리를 순차적으로 결합해 세밀한 패턴을 찾습니다.
단점
- 순차 학습이므로 Random Forest보다 병렬화하기 어렵습니다.
- learning rate와 트리 수의 조합에 민감합니다.
- 트리를 너무 많이 추가하면 과적합될 수 있습니다.
언제 자주 사용하는가?
- 표 형식 데이터에서 높은 정확도가 필요할 때
- Random Forest보다 세밀한 오차 보정을 원할 때
- XGBoost, LightGBM, CatBoost를 배우기 전 boosting 원리를 익힐 때
7. Support Vector Machine
개념
Support Vector Machine, 줄여서 SVM은 두 클래스를 구분하는 경계 중에서 margin이 가장 큰 경계를 찾습니다. Margin은 결정 경계와 가장 가까운 데이터 사이의 거리입니다.
RBF kernel을 사용하면 원래 공간에서 직선으로 구분하기 어려운 데이터도 비선형 경계로 분류할 수 있습니다.
from sklearn.svm import SVC
svm = SVC(
C=2.0,
kernel="rbf",
gamma="scale",
probability=True,
random_state=42
)
주요 하이퍼파라미터
C: 오분류 허용과 복잡한 경계 사이의 균형kernel: 데이터 변환 방법gamma: RBF kernel에서 각 데이터가 영향을 미치는 범위
SVM은 feature 크기에 민감하므로 StandardScaler 적용이 중요합니다.
타이타닉에서의 역할
Age, Fare, FamilySize와 one-hot feature 사이의 비선형 경계를 찾는 보조 모델로 사용할 수 있습니다. 트리 계열과 학습 방식이 달라 Voting 앙상블에서 다양한 의견을 제공한다는 장점도 있습니다.
장점
- 데이터가 아주 크지 않을 때 강력한 분류 성능을 낼 수 있습니다.
- kernel을 이용해 비선형 경계를 학습할 수 있습니다.
- margin을 최대화하는 원리로 일반화 성능을 확보합니다.
단점
- 데이터가 커지면 학습 시간이 크게 증가할 수 있습니다.
- scaling과
C,gamma설정에 민감합니다. - 트리 모델보다 결과를 직관적으로 해석하기 어렵습니다.
probability=True를 사용하면 학습 시간이 추가됩니다.
언제 자주 사용하는가?
- 표본 수가 아주 크지 않은 분류 문제
- 클래스 경계가 비선형일 가능성이 있을 때
- 서로 다른 종류의 모델을 앙상블하고 싶을 때
8. Stratified K-Fold Cross Validation
하나의 train/validation 분할에서 얻은 정확도는 어떤 승객이 validation에 들어갔는지에 따라 달라질 수 있습니다.
Stratified K-Fold는 생존자와 사망자의 비율을 유지하면서 데이터를 K개로 나눕니다. 각 fold를 한 번씩 validation으로 사용하고 나머지를 training으로 사용합니다.
from sklearn.model_selection import StratifiedKFold, cross_validate
cv = StratifiedKFold(
n_splits=5,
shuffle=True,
random_state=42
)
scores = cross_validate(
model_pipeline,
X,
y,
cv=cv,
scoring={
"accuracy": "accuracy",
"roc_auc": "roc_auc"
},
n_jobs=-1
)
print("평균 정확도:", scores["test_accuracy"].mean())
print("정확도 표준편차:", scores["test_accuracy"].std())
print("평균 ROC-AUC:", scores["test_roc_auc"].mean())
평균 점수뿐 아니라 표준편차도 확인하는 것이 좋습니다. 평균이 조금 높더라도 fold마다 점수가 크게 변한다면 모델이 불안정할 수 있습니다.
9. RandomizedSearchCV로 하이퍼파라미터 탐색
머신러닝 모델에는 학습 전에 사람이 정해야 하는 하이퍼파라미터가 있습니다. 모든 조합을 확인하는 Grid Search는 경우의 수가 많아지면 시간이 오래 걸립니다.
RandomizedSearchCV는 지정한 후보에서 일부 조합을 무작위로 선택해 평가합니다.
from sklearn.model_selection import RandomizedSearchCV
param_distributions = {
"classifier__n_estimators": [300, 500, 700, 1000],
"classifier__max_depth": [5, 7, 9, 12, None],
"classifier__min_samples_leaf": [1, 2, 3, 4],
"classifier__max_features": ["sqrt", "log2", 0.6, 0.8],
}
search = RandomizedSearchCV(
estimator=extra_trees_pipeline,
param_distributions=param_distributions,
n_iter=25,
scoring="accuracy",
cv=cv,
random_state=42,
n_jobs=-1,
refit=True
)
search.fit(X, y)
print(search.best_score_)
print(search.best_params_)
classifier__는 Pipeline 안의 classifier 단계에 전달할 파라미터라는 뜻입니다.
주의할 점은 탐색 횟수를 무작정 늘린다고 항상 Kaggle 점수가 오르지는 않는다는 것입니다. 교차검증 결과에 지나치게 맞춰지는 것도 일종의 과적합입니다.
10. Soft Voting Ensemble
개념
Voting은 여러 모델의 예측을 결합하는 앙상블 기법입니다.
- Hard Voting: 각 모델이 예측한 클래스의 다수결
- Soft Voting: 각 모델이 예측한 클래스 확률의 가중평균
Soft Voting은 모델의 확신 정도까지 반영할 수 있습니다.
from sklearn.ensemble import VotingClassifier
soft_voting = VotingClassifier(
estimators=[
("extra", extra_trees_pipeline),
("logistic", logistic_pipeline),
("gradient", gradient_pipeline),
("svm", svm_pipeline),
],
voting="soft",
weights=[2, 1, 2, 1],
n_jobs=-1
)
위 예시는 Extra Trees와 Gradient Boosting에 두 배의 가중치를 부여합니다.
왜 서로 다른 모델을 합칠까?
Random Forest와 Extra Trees는 트리 기반 모델이고, Logistic Regression은 선형 모델이며, RBF SVM은 margin과 kernel을 이용합니다. 학습 방식이 다르기 때문에 서로 다른 승객을 틀릴 가능성이 있습니다.
좋은 앙상블은 단순히 성능이 높은 모델만 모은 것이 아니라 서로 다른 종류의 오류를 만드는 모델을 결합한 것입니다.
장점
- 단일 모델의 우연한 오류를 완화할 수 있습니다.
- 예측이 더 안정적으로 변하는 경우가 많습니다.
- 서로 다른 알고리즘의 장점을 함께 사용할 수 있습니다.
단점
- 항상 단일 최고 모델보다 좋아지는 것은 아닙니다.
- 모델 수만큼 학습과 예측 비용이 증가합니다.
- 결과 해석이 더 어려워집니다.
- 확률 품질이 나쁜 모델이 포함되면 Soft Voting 성능이 낮아질 수 있습니다.
11. 모델별 특징 한눈에 비교하기
| 모델 | 핵심 원리 | 전처리 민감도 | 해석력 | 비선형 관계 | 학습 속도 |
|---|---|---|---|---|---|
| Logistic Regression | 선형 확률 모델 | 높음 | 높음 | 낮음 | 빠름 |
| Random Forest | 무작위 트리 bagging | 낮음 | 중간 | 높음 | 보통 |
| Extra Trees | 더 무작위화된 트리 앙상블 | 낮음 | 중간 | 높음 | 빠른 편 |
| Gradient Boosting | 오차를 순차적으로 보완 | 중간 | 낮음 | 높음 | 보통 |
| RBF SVM | 최대 margin과 kernel | 높음 | 낮음 | 높음 | 데이터가 크면 느림 |
| Soft Voting | 여러 모델의 확률 결합 | 구성 모델에 따라 다름 | 낮음 | 높음 | 느린 편 |
12. Kaggle 제출 파일 만들기
교차검증으로 선택한 최종 모델을 전체 training 데이터로 다시 학습한 뒤 test 데이터를 예측합니다.
final_model.fit(X, y)
test_prediction = final_model.predict(X_test).astype(int)
submission = pd.DataFrame({
"PassengerId": test_df["PassengerId"],
"Survived": test_prediction
})
submission.to_csv("submission.csv", index=False)
제출 전에는 반드시 형식을 확인합니다.
print(submission.shape)
print(submission.isnull().sum())
print(submission["Survived"].unique())
display(submission.head())
Survived 열에는 0과 1만 있어야 하며, Kaggle Titanic test 데이터라면 제출 파일은 일반적으로 418행이어야 합니다.
13. 마무리
타이타닉 같은 작은 표 형식 데이터에서는 모델을 무작정 복잡하게 만드는 것보다 다음 과정이 중요합니다.
- 문제의 의미를 반영한 feature를 만듭니다.
- Pipeline을 사용해 데이터 누수를 방지합니다.
- 하나의 validation 점수가 아니라 교차검증 평균과 표준편차를 확인합니다.
- 단순한 Logistic Regression부터 트리·boosting·SVM까지 비교합니다.
- 서로 다른 모델이 충분히 좋은 경우 Voting 앙상블을 시도합니다.
- 하이퍼파라미터 탐색 결과와 leaderboard에 지나치게 과적합하지 않도록 주의합니다.
처음부터 가장 복잡한 모델을 선택할 필요는 없습니다. 단순한 기준 모델을 만든 뒤 feature engineering, 전처리, 교차검증, 앙상블을 한 단계씩 추가하면 각 기법이 성능에 어떤 영향을 주는지 더 명확하게 배울 수 있습니다.
태그