🤖

본 콘텐츠의 이미지 및 내용은 AI로 생성되었습니다.

⚠️

본 콘텐츠의 이미지 및 내용을 무단으로 복제, 배포, 수정하여 사용할 경우 저작권법에 의해 법적 제재를 받을 수 있습니다.

이미지 로딩 중...

타이타닉 생존자 예측 완벽 가이드 - 슬라이드 1/7
A

AI Generated

2025. 12. 5. · 9 Views

타이타닉 생존자 예측 완벽 가이드

캐글의 대표적인 입문 프로젝트인 타이타닉 생존자 예측을 처음부터 끝까지 다룹니다. 데이터 전처리부터 머신러닝 모델 적용, 그리고 캐글 제출까지 실습해봅니다.


목차

  1. 타이타닉_데이터셋_이해
  2. 결측치_및_이상치_처리
  3. 범주형_변수_인코딩
  4. 로지스틱_회귀_분류
  5. 앙상블_모델_적용
  6. Kaggle_제출용_예측_파일_생성

1. 타이타닉 데이터셋 이해

데이터 분석을 막 시작한 김개발 씨는 선배로부터 "캐글 입문하려면 타이타닉부터 해봐"라는 조언을 들었습니다. 타이타닉이 영화 제목인 건 알겠는데, 이게 왜 데이터 분석의 첫 관문이 되었을까요?

타이타닉 데이터셋은 1912년 침몰한 타이타닉호 승객들의 정보와 생존 여부를 담고 있습니다. 마치 병원에서 환자의 여러 검사 수치를 보고 질병 유무를 예측하는 것처럼, 승객의 특성을 보고 생존 여부를 예측하는 문제입니다.

이 데이터셋은 분류 문제의 기초를 배우기에 완벽한 크기와 복잡도를 갖추고 있습니다.

다음 코드를 살펴봅시다.

import pandas as pd

# 캐글에서 다운로드한 데이터 불러오기
train_df = pd.read_csv('train.csv')
test_df = pd.read_csv('test.csv')

# 데이터 기본 정보 확인
print(f"훈련 데이터: {train_df.shape[0]}명의 승객")
print(f"테스트 데이터: {test_df.shape[0]}명의 승객")

# 주요 컬럼 살펴보기
print(train_df[['Survived', 'Pclass', 'Sex', 'Age', 'Fare']].head())

# 생존율 확인
survival_rate = train_df['Survived'].mean()
print(f"전체 생존율: {survival_rate:.2%}")

김개발 씨는 캐글 사이트에 접속해 타이타닉 데이터셋을 처음 마주했습니다. 화면에는 숫자와 텍스트로 가득 찬 표가 펼쳐져 있었습니다.

솔직히 말해서, 처음에는 막막했습니다. 선배 박시니어 씨가 옆에 와서 설명을 시작했습니다.

"타이타닉 데이터셋은 마치 여권 정보와 비슷해. 각 승객의 신상 정보가 담겨 있거든." 그렇다면 이 데이터셋에는 정확히 어떤 정보가 들어 있을까요?

가장 중요한 것은 Survived 컬럼입니다. 1이면 생존, 0이면 사망을 의미합니다.

우리의 목표는 바로 이 값을 예측하는 것입니다. 마치 의사가 여러 검사 결과를 보고 진단을 내리는 것처럼, 우리는 다른 정보들을 보고 생존 여부를 예측해야 합니다.

Pclass는 객실 등급입니다. 1등급, 2등급, 3등급으로 나뉘며, 당시 사회적 계층을 반영합니다.

영화에서 보았듯이 1등급 승객들은 더 좋은 위치의 객실에 머물렀습니다. SexAge는 말 그대로 성별과 나이입니다.

"여성과 아이 먼저"라는 구조 원칙이 실제로 적용되었을까요? 데이터가 그 답을 알려줄 것입니다.

SibSpParch는 함께 탑승한 가족 수를 나타냅니다. SibSp는 형제자매와 배우자, Parch는 부모와 자녀의 수입니다.

가족과 함께한 사람들의 생존율은 어땠을까요? Fare는 티켓 가격이며, Embarked는 승선 항구입니다.

C는 셰르부르, Q는 퀸즈타운, S는 사우샘프턴을 의미합니다. 김개발 씨는 train.csv와 test.csv 두 개의 파일이 있다는 것을 발견했습니다.

"왜 파일이 두 개죠?" 박시니어 씨가 답했습니다. "train.csv에는 정답이 있어.

이걸로 모델을 학습시키는 거야. test.csv에는 정답이 없어.

우리가 예측한 결과를 캐글에 제출하면, 캐글이 채점해주는 거지." 훈련 데이터에는 891명, 테스트 데이터에는 418명의 승객 정보가 담겨 있습니다. 전체 생존율은 약 38%입니다.

즉, 10명 중 약 4명만이 살아남았다는 뜻입니다. 이제 김개발 씨는 데이터의 전체적인 그림을 이해했습니다.

다음 단계는 이 데이터를 깨끗하게 정리하는 것입니다.

실전 팁

💡 - **df.info()**로 각 컬럼의 데이터 타입과 결측치 개수를 한눈에 파악하세요

  • **df.describe()**로 수치형 데이터의 통계량을 먼저 확인하는 습관을 들이세요

2. 결측치 및 이상치 처리

김개발 씨가 데이터를 살펴보던 중 이상한 점을 발견했습니다. Age 컬럼에 NaN이라는 값이 여기저기 보입니다.

"이게 뭐죠? 데이터가 잘못된 건가요?"

결측치란 데이터가 비어 있는 상태를 말합니다. 마치 설문조사에서 응답하지 않은 항목과 같습니다.

타이타닉 데이터에서는 Age, Cabin, Embarked 등에 결측치가 존재합니다. 이를 어떻게 처리하느냐에 따라 모델의 성능이 크게 달라집니다.

다음 코드를 살펴봅시다.

import pandas as pd
import numpy as np

# 결측치 확인
print(train_df.isnull().sum())

# Age 결측치를 중앙값으로 채우기
age_median = train_df['Age'].median()
train_df['Age'].fillna(age_median, inplace=True)

# Embarked 결측치를 최빈값으로 채우기
embarked_mode = train_df['Embarked'].mode()[0]
train_df['Embarked'].fillna(embarked_mode, inplace=True)

# Cabin은 결측치가 너무 많아 삭제
train_df.drop('Cabin', axis=1, inplace=True)

# 결과 확인
print(f"남은 결측치: {train_df.isnull().sum().sum()}")

박시니어 씨는 김개발 씨에게 친절히 설명했습니다. "NaN은 Not a Number의 약자야.

쉽게 말해 비어 있다는 뜻이지." 결측치를 그대로 두면 어떻게 될까요? 대부분의 머신러닝 알고리즘은 결측치를 처리하지 못합니다.

마치 계산기에 숫자 대신 물음표를 넣는 것과 같습니다. 오류가 발생하거나 잘못된 결과가 나옵니다.

그렇다면 결측치를 어떻게 처리해야 할까요? 첫 번째 방법은 삭제입니다.

결측치가 있는 행을 통째로 지우는 방법입니다. 하지만 타이타닉 데이터에서 Age 결측치는 177개나 됩니다.

이걸 다 지우면 전체 데이터의 약 20%가 사라집니다. 데이터가 귀한 상황에서 이는 큰 손실입니다.

두 번째 방법은 대체입니다. 비어 있는 값을 다른 값으로 채우는 것입니다.

평균값, 중앙값, 최빈값 등을 사용할 수 있습니다. Age의 경우 중앙값을 사용하는 것이 좋습니다.

왜 평균이 아닐까요? 타이타닉에는 어린아이부터 80세 노인까지 다양한 연령대가 있습니다.

이런 경우 극단적인 값에 영향을 덜 받는 중앙값이 더 안전합니다. Embarked는 단 2개의 결측치만 있습니다.

이 경우 최빈값, 즉 가장 많이 나타나는 값으로 채웁니다. 대부분의 승객이 사우샘프턴에서 탑승했으므로 'S'로 채웁니다.

Cabin은 어떨까요? 이 컬럼은 무려 687개의 결측치가 있습니다.

전체의 77%가 비어 있는 셈입니다. 이런 경우 대체하는 것보다 컬럼 자체를 삭제하는 것이 낫습니다.

김개발 씨가 물었습니다. "이상치는 뭔가요?" 이상치는 다른 데이터와 동떨어진 극단적인 값입니다.

예를 들어 Fare 컬럼에 512라는 값이 있습니다. 대부분이 10에서 50 사이인데 말이죠.

이런 값이 이상치입니다. 이상치를 항상 제거해야 할까요?

꼭 그렇지는 않습니다. 타이타닉의 경우 높은 요금은 실제로 1등급 최고급 객실 가격이었습니다.

진짜 데이터인 것이죠. 따라서 도메인 지식을 바탕으로 판단해야 합니다.

이제 김개발 씨의 데이터에는 결측치가 하나도 없습니다. 깨끗해진 데이터로 다음 단계를 진행할 준비가 되었습니다.

실전 팁

💡 - **df.isnull().sum()**으로 각 컬럼별 결측치 개수를 확인하세요

  • 결측치 비율이 50%를 넘으면 해당 컬럼 삭제를 고려하세요

3. 범주형 변수 인코딩

김개발 씨는 모델 학습을 시도했지만 오류가 발생했습니다. "Sex 컬럼에 male, female이라는 문자가 있어서 계산이 안 된다고요?"

인코딩이란 문자 데이터를 숫자로 변환하는 과정입니다. 컴퓨터는 male, female 같은 글자를 이해하지 못합니다.

마치 외국어를 번역하는 것처럼, 문자를 숫자로 바꿔주어야 합니다. 대표적인 방법으로 라벨 인코딩과 원-핫 인코딩이 있습니다.

다음 코드를 살펴봅시다.

import pandas as pd
from sklearn.preprocessing import LabelEncoder

# 성별을 숫자로 변환 (라벨 인코딩)
le = LabelEncoder()
train_df['Sex'] = le.fit_transform(train_df['Sex'])
# female=0, male=1

# Embarked를 원-핫 인코딩
embarked_dummies = pd.get_dummies(train_df['Embarked'], prefix='Embarked')
train_df = pd.concat([train_df, embarked_dummies], axis=1)
train_df.drop('Embarked', axis=1, inplace=True)

# 불필요한 컬럼 제거
train_df.drop(['Name', 'Ticket', 'PassengerId'], axis=1, inplace=True)

print(train_df.head())

박시니어 씨가 화이트보드에 그림을 그리며 설명했습니다. "머신러닝 모델은 수학 공식이야.

수학 공식에 사과, 배 같은 단어를 넣을 수 있어?" 김개발 씨가 고개를 저었습니다. 당연히 안 됩니다.

바로 이 때문에 인코딩이 필요합니다. 문자를 숫자로 바꾸는 번역 작업입니다.

가장 간단한 방법은 라벨 인코딩입니다. female을 0으로, male을 1로 바꾸는 것입니다.

마치 출석부에서 여학생에게 1번부터, 남학생에게 51번부터 번호를 매기는 것과 같습니다. Sex 컬럼에는 라벨 인코딩이 적합합니다.

값이 두 개뿐이고, 순서의 의미가 없기 때문입니다. 하지만 Embarked는 다릅니다.

C, Q, S 세 개의 항구가 있습니다. 이걸 0, 1, 2로 바꾸면 어떻게 될까요?

문제가 생깁니다. 모델이 "2가 0보다 크니까 S가 C보다 중요하다"고 잘못 해석할 수 있습니다.

실제로는 그런 순서나 크기 관계가 없는데 말이죠. 이런 경우 원-핫 인코딩을 사용합니다.

각 값을 별도의 컬럼으로 만드는 방법입니다. Embarked_C, Embarked_Q, Embarked_S 세 개의 컬럼이 생기고, 해당하는 곳에만 1, 나머지는 0이 들어갑니다.

마치 체크리스트와 같습니다. "셰르부르에서 탔나요?

예/아니오" 형식으로 질문을 바꾸는 것입니다. Name, Ticket, PassengerId는 어떨까요?

이 컬럼들은 각 승객마다 고유한 값을 가집니다. 인코딩해봤자 의미가 없습니다.

이름이 홍길동이라서 생존율이 높아지지는 않으니까요. 따라서 이런 컬럼들은 삭제합니다.

김개발 씨가 질문했습니다. "Pclass도 1, 2, 3인데 원-핫 인코딩 해야 하나요?" 좋은 질문입니다.

Pclass는 1등급, 2등급, 3등급으로 순서에 의미가 있습니다. 1등급이 3등급보다 좋은 것은 사실이니까요.

이런 경우는 그대로 숫자로 사용해도 됩니다. 다만, 원-핫 인코딩을 해서 성능을 비교해볼 수도 있습니다.

이제 모든 데이터가 숫자로 변환되었습니다. 드디어 모델을 학습시킬 준비가 완료되었습니다.

실전 팁

💡 - 두 개의 값만 있는 범주형 변수는 라벨 인코딩을 사용하세요

  • 세 개 이상이고 순서가 없다면 원-핫 인코딩을 사용하세요

4. 로지스틱 회귀 분류

드디어 모델을 만들 시간입니다. 김개발 씨는 설레는 마음으로 첫 번째 머신러닝 모델, 로지스틱 회귀를 학습시키려 합니다.

이름에 회귀가 들어가는데, 분류 문제에 쓴다니 조금 혼란스럽습니다.

로지스틱 회귀는 이름과 달리 분류 알고리즘입니다. 입력 데이터를 받아 0과 1 사이의 확률값으로 변환하고, 특정 임계값을 기준으로 클래스를 분류합니다.

마치 시험 점수를 합격과 불합격으로 나누는 것과 같습니다. 간단하면서도 해석이 쉬워 기준 모델로 많이 사용됩니다.

다음 코드를 살펴봅시다.

from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score, classification_report

# 특성과 타겟 분리
X = train_df.drop('Survived', axis=1)
y = train_df['Survived']

# 훈련/검증 데이터 분리
X_train, X_val, y_train, y_val = train_test_split(
    X, y, test_size=0.2, random_state=42
)

# 로지스틱 회귀 모델 학습
lr_model = LogisticRegression(max_iter=1000)
lr_model.fit(X_train, y_train)

# 예측 및 평가
y_pred = lr_model.predict(X_val)
accuracy = accuracy_score(y_val, y_pred)
print(f"검증 정확도: {accuracy:.4f}")

박시니어 씨가 커피를 마시며 설명했습니다. "로지스틱 회귀라는 이름 때문에 헷갈리지?

역사적인 이유로 그렇게 불리는 거야." 로지스틱 회귀의 핵심은 시그모이드 함수입니다. 이 함수는 어떤 값이든 0과 1 사이로 압축합니다.

마치 온도계의 눈금을 0도에서 100도 사이로 제한하는 것과 같습니다. 모델은 각 승객의 특성을 종합하여 점수를 계산합니다.

이 점수가 시그모이드 함수를 통과하면 생존 확률이 됩니다. 0.5보다 크면 생존, 작으면 사망으로 예측합니다.

코드를 살펴보겠습니다. 먼저 train_test_split으로 데이터를 나눕니다.

왜 나눌까요? 학교 시험을 생각해보세요.

공부할 때 본 문제만 시험에 나온다면 진짜 실력을 알 수 없습니다. 마찬가지로, 학습에 사용하지 않은 데이터로 모델을 평가해야 진짜 성능을 알 수 있습니다.

80%는 학습용, 20%는 검증용으로 나눕니다. random_state=42는 난수 고정입니다.

이 값을 지정하면 코드를 다시 실행해도 같은 결과가 나옵니다. 재현 가능한 실험을 위해 중요합니다.

max_iter=1000은 최대 반복 횟수입니다. 로지스틱 회귀는 최적의 가중치를 찾기 위해 여러 번 계산을 반복합니다.

기본값으로는 수렴하지 않을 수 있어 넉넉히 설정합니다. 모델을 학습시키는 것은 fit 메서드 한 줄입니다.

내부적으로 복잡한 수학이 돌아가지만, 우리는 그냥 호출만 하면 됩니다. 예측은 predict 메서드를 사용합니다.

검증 데이터를 넣으면 생존 여부를 예측해줍니다. 결과는 약 80%의 정확도입니다.

100명 중 80명의 생존 여부를 맞췄다는 뜻입니다. 김개발 씨가 물었습니다.

"80%면 좋은 건가요?" 단순히 "다 죽었다"고 예측하면 약 62%의 정확도가 나옵니다. 80%는 그것보다 훨씬 나은 결과입니다.

하지만 더 높일 수 있을까요? 바로 다음 단계에서 앙상블 모델을 시도해보겠습니다.

실전 팁

💡 - 항상 훈련 데이터와 검증 데이터를 분리하여 과적합을 방지하세요

  • random_state를 고정하면 실험 결과를 재현할 수 있습니다

5. 앙상블 모델 적용

김개발 씨의 로지스틱 회귀 모델은 80%의 정확도를 기록했습니다. 하지만 캐글 리더보드를 보니 상위권은 85%를 넘습니다.

어떻게 하면 성능을 더 올릴 수 있을까요?

앙상블은 여러 모델의 예측을 결합하여 더 좋은 결과를 얻는 방법입니다. 마치 중요한 결정을 내릴 때 여러 전문가의 의견을 종합하는 것과 같습니다.

대표적인 앙상블 방법으로 랜덤 포레스트그래디언트 부스팅이 있습니다.

다음 코드를 살펴봅시다.

from sklearn.ensemble import RandomForestClassifier, GradientBoostingClassifier
from sklearn.model_selection import cross_val_score

# 랜덤 포레스트 모델
rf_model = RandomForestClassifier(
    n_estimators=100,
    max_depth=5,
    random_state=42
)
rf_scores = cross_val_score(rf_model, X, y, cv=5)
print(f"랜덤 포레스트 교차검증 점수: {rf_scores.mean():.4f}")

# 그래디언트 부스팅 모델
gb_model = GradientBoostingClassifier(
    n_estimators=100,
    max_depth=3,
    random_state=42
)
gb_scores = cross_val_score(gb_model, X, y, cv=5)
print(f"그래디언트 부스팅 교차검증 점수: {gb_scores.mean():.4f}")

# 최종 모델 학습
rf_model.fit(X, y)

박시니어 씨가 비유를 들었습니다. "퀴즈쇼에서 청중에게 도움을 요청하는 것처럼, 여러 모델의 의견을 모으는 거야." 한 명의 천재보다 여러 명의 평범한 사람의 집단 지성이 더 정확한 경우가 많습니다.

앙상블의 원리가 바로 이것입니다. 랜덤 포레스트는 여러 개의 결정 트리를 만들어 투표하는 방식입니다.

마치 100명의 심사위원이 각자 판단하고 다수결로 결정하는 것과 같습니다. 각 트리는 데이터의 일부만 보고 학습합니다.

일부러 다양성을 만드는 것입니다. 어떤 트리는 A를 중시하고, 다른 트리는 B를 중시합니다.

이들의 예측을 종합하면 개별 트리보다 안정적인 결과가 나옵니다. n_estimators=100은 트리의 개수입니다.

max_depth=5는 트리의 최대 깊이입니다. 너무 깊으면 과적합, 너무 얕으면 성능이 낮아집니다.

그래디언트 부스팅은 조금 다른 방식입니다. 순차적으로 모델을 만들되, 이전 모델이 틀린 부분을 집중적으로 학습합니다.

마치 오답 노트를 만들어 틀린 문제만 반복 학습하는 것과 같습니다. 첫 번째 모델이 70점을 맞았다면, 두 번째 모델은 틀린 30점 부분을 집중 공략합니다.

이 과정을 반복하면 점점 더 정확해집니다. 코드에서 cross_val_score를 사용했습니다.

이것은 교차 검증입니다. 데이터를 5등분하여 4개로 학습하고 1개로 검증하는 과정을 5번 반복합니다.

더 신뢰할 수 있는 성능 추정치를 얻을 수 있습니다. 결과를 보면, 랜덤 포레스트와 그래디언트 부스팅 모두 로지스틱 회귀보다 높은 점수를 보입니다.

약 82~83%의 정확도입니다. 김개발 씨의 눈이 빛났습니다.

"그럼 이 모델로 제출하면 되겠네요!" 그렇습니다. 하지만 그 전에 테스트 데이터에 대한 예측을 만들어야 합니다.

실전 팁

💡 - 랜덤 포레스트는 과적합에 강하고 하이퍼파라미터 튜닝이 비교적 쉽습니다

  • 교차 검증으로 모델 성능을 더 신뢰성 있게 평가하세요

6. Kaggle 제출용 예측 파일 생성

드디어 마지막 단계입니다. 김개발 씨는 학습시킨 모델로 테스트 데이터의 생존 여부를 예측하고, 캐글에 제출할 파일을 만들어야 합니다.

첫 캐글 제출의 긴장되는 순간입니다.

캐글 제출 파일은 정해진 형식을 따라야 합니다. 타이타닉 대회의 경우 PassengerId와 Survived 두 개의 컬럼만 있는 CSV 파일을 요구합니다.

테스트 데이터에도 훈련 데이터와 동일한 전처리를 적용해야 올바른 예측이 가능합니다.

다음 코드를 살펴봅시다.

# 테스트 데이터 전처리 (훈련 데이터와 동일하게)
test_df['Age'].fillna(age_median, inplace=True)
test_df['Fare'].fillna(test_df['Fare'].median(), inplace=True)
test_df['Sex'] = le.transform(test_df['Sex'])
test_df.drop(['Cabin', 'Name', 'Ticket'], axis=1, inplace=True)

# 원-핫 인코딩
test_dummies = pd.get_dummies(test_df['Embarked'], prefix='Embarked')
test_df = pd.concat([test_df, test_dummies], axis=1)
test_df.drop('Embarked', axis=1, inplace=True)

# PassengerId 저장 후 제거
passenger_ids = test_df['PassengerId']
test_df.drop('PassengerId', axis=1, inplace=True)

# 예측
predictions = rf_model.predict(test_df)

# 제출 파일 생성
submission = pd.DataFrame({
    'PassengerId': passenger_ids,
    'Survived': predictions
})
submission.to_csv('submission.csv', index=False)
print(submission.head())

김개발 씨는 마지막 단계 앞에서 긴장했습니다. 지금까지 열심히 해왔는데, 마지막에 실수하면 안 되니까요.

박시니어 씨가 강조했습니다. "가장 중요한 건 테스트 데이터에도 똑같은 전처리를 하는 거야.

이걸 놓치면 예측이 엉망이 돼." 이것은 정말 중요한 포인트입니다. 훈련 데이터에서 Age를 중앙값으로 채웠다면, 테스트 데이터에서도 같은 중앙값으로 채워야 합니다.

새로 계산하면 안 됩니다. 마찬가지로 라벨 인코더도 fit_transform이 아닌 transform만 사용합니다.

이미 훈련 데이터로 학습된 인코더를 그대로 적용하는 것입니다. 테스트 데이터에는 Fare에도 결측치가 있습니다.

훈련 데이터에는 없었던 문제입니다. 이런 예상치 못한 상황도 처리해야 합니다.

전처리가 끝나면 predict 메서드로 예측합니다. 결과는 0과 1로 이루어진 배열입니다.

제출 파일의 형식은 캐글에서 정해줍니다. 타이타닉 대회는 두 개의 컬럼만 요구합니다.

PassengerIdSurvived입니다. 다른 컬럼이 있거나 형식이 다르면 제출이 거부됩니다.

to_csv로 파일을 저장합니다. index=False가 중요합니다.

이걸 빼면 불필요한 인덱스 컬럼이 추가되어 제출이 실패합니다. 김개발 씨는 생성된 submission.csv 파일을 캐글 사이트에 업로드했습니다.

잠시 후 점수가 표시되었습니다. 약 77%의 정확도입니다.

검증 점수보다 조금 낮지만, 첫 제출치고 나쁘지 않습니다. 박시니어 씨가 축하해주었습니다.

"첫 캐글 제출 축하해! 이제 피처 엔지니어링이나 하이퍼파라미터 튜닝으로 점수를 더 올려볼 수 있어." 김개발 씨는 뿌듯한 마음으로 다음 도전을 계획했습니다.

타이타닉은 시작일 뿐입니다. 더 많은 대회가 기다리고 있습니다.

실전 팁

💡 - 테스트 데이터 전처리 시 훈련 데이터에서 계산한 값을 그대로 사용하세요

  • 제출 전 submission.csv의 형식과 행 개수를 꼭 확인하세요

이상으로 학습을 마칩니다. 위 내용을 직접 코드로 작성해보면서 익혀보세요!

#Python#MachineLearning#Pandas#ScikitLearn#Kaggle#DataPreprocessing#Machine Learning,Python

댓글 (0)

댓글을 작성하려면 로그인이 필요합니다.