3. 성별 없이 생존을 예측할 수 있을까? 우리의 첫 번째 모델#
데이터 과학 동아리 - 세 번째 모임
프롬: 다안 선배! 저번 시간에 타이타닉 데이터를 다양한 방법으로 분석했는데, 오늘은 뭘 배울 예정인가요?
다안: 오늘은 정말 특별한 날이야. 지금까지 데이터를 탐색하고 분석해왔다면, 오늘은 드디어 우리의 첫 번째 머신러닝 모델을 만들어볼 거야! 지난 시간에 발견한 패턴들을 바탕으로 타이타닉 생존자를 예측하는 인공지능을 학습시켜볼 거거든.
코더블: 와, 드디어 실제 예측 모델을 만들어보는군요! 지난 시간에 여러 변수들과 생존율 사이의 관계를 분석했으니, 이걸 활용하면 재미있는 모델이 나올 것 같아요.
프롬: (살짝 긴장한 표정으로) 머신러닝 모델이요? 그런데… 저는 코딩도 잘 모르는데 괜찮을까요?
다안: 걱정하지 마! 오늘은 내가 단계별로 차근차근 설명해줄 거야. 코더블이 코드로, 프롬은 프롬프트로 각자 방식에 맞게 따라오면 돼. 생각보다 어렵지 않을 거야. 먼저 데이터부터 읽어와보자.
import pandas as pd
train = pd.read_csv('train.csv')
test = pd.read_csv('test.csv')
submission = pd.read_csv('gender_submission.csv')
학습 과정 개요#
다안: 자, 이제 본격적으로 시작해볼까? 머신러닝 모델을 만드는 과정은 요리 레시피와 비슷해. 재료를 준비하고, 순서대로 따라하면 맛있는 요리가 완성되듯이, 우리도 단계별로 진행할 거야.
프롬: 요리라… 그럼 좀 더 이해하기 쉬울 것 같아요. 어떤 단계들이 있나요?
다안: 크게 네 단계로 나눌 수 있어:
학습에 사용할 변수 선택
지난 시간에 분석한 수치형 변수들 중에서 중요한 것들을 골라야 해
예를 들어, Pclass, Age, Fare 등이 후보가 될 수 있지
데이터 분할
모델을 학습시키는 부분과 성능을 검증하는 부분으로 나눠야 해
보통 8:2 비율로 나누는데, 마치 요리 학교에서 8할은 연습하고 2할은 시험 보는 것과 비슷해
모델 학습
우리는 Random Forest라는 알고리즘을 사용할 거야
여러 전문가의 의견을 종합하는 것과 같은 방식이라고 생각하면 돼
생존 여부 예측
학습된 모델로 test.csv의 승객 생존 여부를 예측하고
결과를 캐글에 제출해서 점수를 확인할 거야
코더블: 이렇게 단계별로 보니 더 명확해졌어요. 마치 레고 블록을 하나씩 쌓아가는 것 같네요!
프롬: (고개를 갸웃하며) 음… 특히 모델 학습 부분이 아직 상상이 잘 안 돼요. Random Forest가 뭐죠?
다안: 좋은 질문이야. Random Forest는 ‘숲’이라는 이름처럼 여러 개의 ‘의사결정 나무(decision tree)’로 이루어진 알고리즘이야. 이건 ‘앙상블(ensemble)’ 기법의 대표적인 예인데, 마치 여러 명의 전문가가 함께 의견을 내어 더 정확한 결정을 내리는 것과 같아.
예를 들어, 타이타닉 생존자를 예측할 때:
전문가 1: “이 승객은 1등석이니까 생존했을 것” (객실 등급만 봄)
전문가 2: “돈을 많이 낸 승객이니 생존했을 것” (요금만 봄)
전문가 3: “나이가 어린 승객이니 생존했을 것” (나이만 봄)
이렇게 각 전문가(나무)는 승객 정보의 일부만 보고 판단하지만, 모든 의견을 모아 다수결로 최종 결정을 내리면 더 믿을 만한 예측이 가능해져.
프롬: 아하! 마치 여러 사람이 토론해서 결론을 내리는 것처럼 여러 개의 작은 모델이 함께 일하는 거군요! 한 사람보다 여러 사람이 의견을 모으면 더 현명한 결정을 내릴 수 있는 것처럼요!
다안: 정확해! 이런 접근 방식이 단일 모델보다 더 안정적이고 정확한 예측을 할 수 있게 해줘.
코더블: 그럼 이제 본격적으로 모델을 만들어볼까요? 먼저 어떤 변수들을 사용할지 결정해야 할 것 같아요.
Note
이 섹션이 필요한 이유
이번 섹션에서는 다소 복잡한 코드들이 등장합니다. 하지만 걱정하지 마세요! 지금 이 코드들을 완벽히 이해할 필요는 없습니다.
자동차를 처음 운전하는 사람이 엔진의 작동 원리를 완벽히 이해하지 않아도 운전할 수 있는 것처럼, 여러분도 이 코드들의 세부적인 내용을 모두 이해하지 않아도 예측 모델을 만들 수 있습니다.
지금은 “이런 과정을 통해 인공지능이 학습을 하는구나” 정도로 이해하시면 충분합니다. 각 단계가 어떤 의미인지는 이후 챕터들에서 차근차근 설명드리겠습니다.
학습에 사용할 변수 선택#
다안: 자, 이제 우리 모델이 학습할 변수들을 선택해보자. 지난 시간에 분석한 결과를 생각해보면, 어떤 변수들이 생존과 관련이 있었지?
코더블: 히트맵에서 Pclass(객실 등급)가 생존과 상관관계가 -0.34로 꽤 높았어요. 그리고 Fare(요금)도 0.26으로 의미 있는 관계가 있었죠.
프롬: Age(나이)는 상관관계가 낮았지만(-0.07), 막대그래프로 봤을 때 어린이들의 생존율이 높았던 것 같아요!
다안: 좋은 관찰이야! 그래, 상관관계가 낮더라도 특정 구간에서 중요한 관계가 있을 수 있어. 그러면 우리는 5가지 수치형 변수를 사용해볼게:
Pclass: 객실 등급
Age: 승객의 나이
SibSp: 함께 탑승한 형제자매, 배우자 수
Parch: 함께 탑승한 부모, 자녀 수
Fare: 티켓 요금
이 변수들은 모두 숫자로 되어 있어서 머신러닝 알고리즘이 바로 이해할 수 있는 형태야.
코더블: 이 변수들을 파이썬 리스트로 만들어서 나중에 편하게 사용할 수 있게 해볼게요.
inc_fts = ['Pclass', 'Age', 'SibSp', 'Parch', 'Fare']
프롬: 이 다섯 개의 변수로 생존 여부를 예측할 수 있을까요? 지난 번에 본 성별(Sex) 정보는 왜 포함하지 않나요? 성별이 매우 중요했던 것 같은데요.
다안: 아주 예리한 질문이야! 성별은 정말 중요한 변수지만, 문자열(‘male’, ‘female’)이라서 지금 우리 모델에 바로 넣을 수 없어. 성별 정보를 활용하려면 숫자로 변환하는 과정이 필요한데, 그건 다음 장에서 배울 거야. 오늘은 우선 숫자로 된 정보만 가지고 첫 모델을 만들어볼 거야.
코더블: 그러니까 지금 이 모델은 성별 같은 중요한 정보가 빠진 ‘기본 모델’이라고 생각하면 될 것 같아요. 다음 장에서 더 발전된 모델을 만들 때 비교 기준이 될 수 있겠네요.
다안: 정확해! 그래서 이런 첫 모델을 ‘베이스라인(baseline)’ 모델이라고 부르기도 해. 이제 데이터를 준비하고 분할해보자.
데이터 분할#
다안: 이제 데이터를 컴퓨터가 학습하기 좋은 형태로 나누어보자. 마치 학생이 문제집으로 공부하고 모의고사로 실력을 확인하듯이, 데이터도 학습용과 검증용으로 나누는 게 중요해.
프롬: 어떻게 나누면 되는 거죠?
다안: 먼저 입력 데이터(X)와 결과 데이터(y)로 구분해야 해. X는 우리가 방금 선택한 변수들이고, y는 생존 여부(Survived)야. 그리고 test 데이터도 같은 변수들만 선택해서 X_test로 만들어야 해.
코더블: 제가 코드로 먼저 작성해볼게요.
X = train[inc_fts] # 선택한 특성들
y = train['Survived'] # 생존 여부
X_test = test[inc_fts] # 예측할 데이터
print(X.shape, y.shape, X_test.shape)
(891, 5) (891,) (418, 5)
프롬: 저도 프롬프트로 해볼게요!
# 프롬프트
1. 선택한 특성들(Pclass, Age, SibSp, Parch, Fare)은 X로 저장
2. 생존여부(Survived)는 y로 저장
3. test 데이터에서도 같은 특성들만 선택해서 X_test로 저장
4. 각 데이터의 크기를 확인
다안: 좋아! 이렇게 데이터를 준비했어. 결과를 보면:
X는 891명의 승객 정보에 5개의 특성이 있어 (891 x 5)
y는 891명의 생존/사망 정보가 있고 (891 x 1)
X_test는 418명의 승객 정보에 5개의 특성이 있어 (418 x 5)
이제 X와 y 데이터를 다시 학습용과 검증용으로 나누어보자. 보통 전체 데이터의 80%는 학습에 사용하고, 20%는 모델의 성능을 검증하는데 사용해.
코더블: train_test_split 함수를 사용해서 데이터를 분할할게요. random_state 파라미터는 매번 같은 방식으로 분할되도록 해주는 설정이에요.
from sklearn.model_selection import train_test_split
X_train, X_valid, y_train, y_valid = train_test_split(X, y, test_size=0.2, random_state=42)
프롬: 저도 프롬프트로 데이터를 나누는 걸 시도해볼게요!
# 프롬프트
1. X_train, X_valid, y_train, y_valid로 데이터를 8:2 비율로 나눠줘
2. random_state는 42로 설정해서 항상 같은 결과가 나오게 해줘
프롬: (혼란스러운 표정으로) 그런데 이렇게 나누는 것이 왜 중요한가요? 그냥 모든 데이터로 학습하면 안 되나요?
다안: 아주 좋은 질문이야! 이건 마치 공부와 시험의 관계와 비슷해. 만약 시험 문제를 미리 알고 공부한다면 진짜 실력을 측정할 수 없잖아?
머신러닝에서도 모델이 학습 데이터를 단순히 “암기”하는 것이 아니라 진짜 “이해”했는지 확인하기 위해 처음 보는 데이터(검증 데이터)로 테스트하는 거야. 이렇게 함으로써 모델이 새로운 데이터에 대해서도 잘 예측할 수 있는지 확인할 수 있어.
생각해봐. 교과서에 있는 문제만 계속 풀어서 100점을 받는 것과 처음 보는 응용문제도 풀 수 있는 건 완전히 다른 실력이잖아? 머신러닝도 마찬가지야.
코더블: 그리고 데이터를 나눌 때 random_state를 42로 설정한 것은 실험을 반복해도 같은 결과가 나오게 하기 위해서예요. 이렇게 하면 우리가 모델을 수정했을 때 정말 모델의 성능이 좋아진 건지, 아니면 단순히 데이터가 다르게 나뉘어서 그런 건지 구분할 수 있어요.
프롬: 아, 이제 이해가 잘 되네요! 그럼 이제 모델을 어떻게 학습시키나요?
모델 학습#
다안: 이제 Random Forest 알고리즘을 사용해 모델을 학습시켜보자. 앞서 설명했듯이, Random Forest는 마치 100명의 전문가에게 의견을 물어보고, 다수결로 최종 결정을 내리는 것과 같아.
코더블: 또 여기서도 random_state 파라미터를 사용해서 결과를 재현 가능하게 만들어요.
from sklearn.ensemble import RandomForestClassifier
model = RandomForestClassifier(random_state=42)
model.fit(X_train, y_train)
RandomForestClassifier(random_state=42)In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
RandomForestClassifier(random_state=42)
프롬: 또 random_state가 나왔네요. 그런데… 이게 다인가요? 모델 학습이 이렇게 간단한가요?
다안: 맞아, 겉으로 보기에는 너무 간단해 보이지? 하지만 .fit()
함수 안에서는 정말 복잡한 계산이 이루어지고 있어. 쉽게 설명하자면:
100개의 서로 다른 의사결정 나무가 만들어져
각 나무는 데이터의 일부만 보고 학습해
각 나무가 학습을 마치면, 새로운 승객에 대해 100개 나무의 예측을 종합해 최종 결정을 내려
이건 마치 100명의 학생이 각각 다른 참고서로 공부한 다음, 함께 모여 토론하여 답을 찾는 것과 비슷해.
프롬: 와… 한 줄의 코드가 그렇게 많은 일을 하고 있었군요! 신기해요.
다안: 그래, Random Forest는 특히 데이터 전처리를 많이 하지 않아도 괜찮고, 튜닝할 파라미터가 적어서 초보자에게 좋은 알고리즘이야. 그래서 캐글에서도 많이 사용되고 있지. 이제 학습된 모델이 얼마나 잘 예측하는지 확인해볼까?
프롬: 지금까지 한 과정을 돌아보면, 1) 필요한 변수들을 선택하고, 2) 데이터를 학습용과 검증용으로 나누고, 3) 모델을 학습시켰네요. 다음 단계는 모델이 얼마나 잘 맞추는지 확인하는 건가요?
모델 성능 평가#
다안: 정확해! 모델 학습이 완료되었으니, 이제 검증 데이터(X_valid)로 모델의 성능을 평가해보자. 앞서 우리가 20%를 따로 떼어놓은 이유가 바로 이것이야.
코더블: y_valid에는 실제 생존 여부가 저장되어 있으니, 모델이 예측한 결과와 비교해서 정확도를 계산할 수 있어요.
from sklearn.metrics import accuracy_score
y_pred = model.predict(X_valid)
accuracy = accuracy_score(y_valid, y_pred)
print(f"Accuracy: {accuracy:.5f}")
Accuracy: 0.73184
프롬: 저는 모델의 정확도를 평가하는 프롬프트를 만들게요!
# 프롬프트
X_valid 데이터로 모델의 예측을 수행하고, 실제 결과인 y_valid와 비교해서 정확도를 계산해줘
프롬: 와! 정확도가 73.184%네요! 이게 좋은 점수인가요?
다안: 첫 모델치고는 꽤 괜찮은 점수야. 무작위로 예측했다면 약 50%의 정확도를 얻었을 테니까, 73.184%는 분명 의미 있는 예측이라고 할 수 있어. 쉽게 말하면, 우리 모델이 100명 중에 약 73명의 생존 여부를 정확히 맞힌 거야!
하지만 아직 개선의 여지는 있어. 특히 우리가 성별 정보를 추가하면 정확도가 더 높아질 거야.
코더블: 정확히는 178개의 검증 데이터 중에서 약 130개(178 × 0.73184 ≈ 130)를 맞혔다는 의미예요! 처음 시도로는 정말 잘했다고 생각해요.
프롬: 그런데 전체 데이터가 아니라 20%만 가지고 평가했잖아요. 이 점수를 믿어도 될까요?
다안: 또 좋은 질문이야! 우리가 분리해둔 20%는 모델이 학습 과정에서 전혀 보지 못한 데이터야. 그래서 이 데이터로 평가한 점수는 모델이 새로운 데이터에 대해 얼마나 잘 일반화할 수 있는지를 보여주는 지표라고 할 수 있어. 이걸 ‘Validation Score’라고 부르지.
물론 완벽한 평가를 위해서는 최종적으로 test.csv 데이터로 예측해보고, 캐글에 제출해서 점수를 확인해야 해. 그게 진짜 시험이니까!
프롬: 그럼 이제 test 데이터로 예측해볼까요?
최종 예측 및 제출#
다안: 그래, 이제 학습된 모델로 실제 test 데이터의 생존 여부를 예측해보자. 이건 마치 실제 시험을 보는 것과 같아. 학습과 검증은 우리가 공부하는 과정이었고, 이제 진짜 시험을 보는 거야.
코더블: 먼저 모델로 예측한 다음, 결과를 submission 파일에 저장할게요.
predictions = model.predict(X_test)
submission['Survived'] = predictions
submission.to_csv('titanic_pred.csv', index=False)
submission.head()
PassengerId | Survived | |
---|---|---|
0 | 892 | 0 |
1 | 893 | 0 |
2 | 894 | 1 |
3 | 895 | 1 |
4 | 896 | 0 |
프롬: 저도 프롬프트로 최종 예측을 해볼게요!
# 프롬프트
1. 학습된 모델로 X_test 데이터의 생존 여부를 예측하고
2. 예측 결과를 submission 파일에 저장한 다음
3. titanic_pred.csv 파일로 내보내줘
프롬: 오, 이제 각 승객이 생존했는지 사망했는지 예측한 결과가 나왔네요! 이걸 캐글에 제출하면 되는 건가요?
다안: 맞아! 우리가 방금 생성한 ‘titanic_pred.csv’ 파일을 캐글에 업로드하면, 실제 답과 비교해서 점수를 알려줄 거야. 그렇게 해서 우리 모델의 진짜 성능을 확인할 수 있어.
코더블: 제가 방금 캐글에 제출해봤는데, 0.63397의 점수를 받았어요. 이건 test 데이터 418명 중 약 265명의 생존 여부를 정확하게 예측했다는 의미예요.
프롬: 어? 검증 데이터에서는 73%였는데, 왜 테스트 데이터에서는 63%로 떨어졌나요?
다안: 이것도 정말 좋은 질문이야! 이런 현상을 ‘과적합(overfitting)’이라고 부르기도 해. 쉽게 말하면, 모델이 학습 데이터의 특징에 너무 맞춰진 나머지, 새로운 데이터에서는 성능이 떨어지는 현상이야.
이건 마치 특정 학교 시험 유형에만 익숙해져서, 다른 학교의 문제를 풀 때 당황하는 것과 비슷해. 학습 데이터만의 특이한 패턴을 외워버린 거지.
하지만 걱정하지 마! 이는 머신러닝에서 흔히 겪는 자연스러운 과정이야. 실제 데이터 과학자들도 항상 이런 차이를 경험하고, 그 격차를 줄이기 위해 다양한 방법을 시도해.
코더블: 그래도 무작위 예측보다는 훨씬 나은 결과예요. 다음 번에는 성별 정보를 추가해서 모델을 개선해보면 좋을 것 같아요!
프롬: 그럼 이 점수는 좋은 건가요, 나쁜 건가요? 어떻게 평가해야 할지 모르겠어요.
다안: 객관적으로 평가하자면, 지금 우리 모델의 점수(0.63397)는 성별만 고려한 baseline(0.76555)보다는 낮아. 하지만 이건 당연한 결과야. 왜냐하면 우리는 아직 가장 중요한 변수인 성별(Sex)을 모델에 포함시키지 않았거든.
지금까지의 결과를 표로 정리하면 다음과 같아:
버전 |
피쳐 개수 |
Val. Score |
Public Score |
맞은 사람 수 |
설명 |
---|---|---|---|---|---|
1.3 |
5 |
0.73184 |
0.63397 |
265명 |
5개의 numeric 피쳐 사용 |
이 점수는 우리의 첫 번째 시도일 뿐이야. 처음 바이올린을 배우는 사람이 첫날부터 완벽한 연주를 기대할 수 없듯이, 머신러닝 모델도 처음부터 완벽할 수는 없어. 앞으로 다음과 같은 방법들을 통해 점수를 높여갈 거야:
문자형 변수들(성별, 승선 항구 등)을 활용하는 방법 배우기
결측치를 더 효과적으로 처리하기
변수들간의 관계를 활용하기
다양한 머신러닝 알고리즘 시도해보기
프롬: 와, 이렇게 간단한 코드로 인공지능 모델을 만들 수 있다니 정말 신기해요! 저도 집에 가서 성별 정보를 추가한 모델을 프롬프트로 만들어봐야겠어요.
코더블: 저는 결측치 처리 방법을 좀 더 공부해보려고요. 특히 Age와 Cabin처럼 결측치가 많은 변수를 어떻게 효과적으로 활용할 수 있을지 궁금하네요.
다안: 둘 다 좋은 계획이야! 다음 장에서는 성별(Sex)과 승선 항구(Embarked)라는 문자형 변수들을 어떻게 활용할 수 있는지 배워볼 거야. 이를 통해 우리의 예측 모델이 얼마나 개선되는지 함께 확인해보자!
데이터 속 숨은 이야기
Random Forest로 살펴보는 생존 가능성
우리가 사용한 Random Forest 알고리즘은 마치 100명의 전문가가 모여서 토론하는 것과 같습니다. 각 전문가는 승객의 여러 특성들(나이, 성별, 객실 등급 등) 중 일부만을 보고 “이 승객은 생존했을까요, 사망했을까요?”를 예측합니다.
예를 들어:
한 전문가는 “1등석 승객이니 생존했을 것”이라고 판단하고
다른 전문가는 “나이가 많은 남성이니 사망했을 것”이라고 보며
또 다른 전문가는 “가족과 함께 탑승했으니 생존했을 것”이라고 예측합니다
이렇게 100명의 전문가가 각자의 관점에서 내린 판단들을 모아 다수결로 최종 결정을 내리는 것이 Random Forest의 방식입니다. 이런 “집단 지성”의 힘을 빌려 우리는 63%의 정확도로 승객들의 생존 여부를 예측할 수 있었습니다.
이는 단순히 머신러닝 기법으로서만 흥미로운 것이 아니라, 인간의 의사결정 과정에도 시사하는 바가 큽니다. 복잡한 문제를 해결할 때 다양한 관점을 통합하는 것이 얼마나 중요한지를 보여주는 좋은 예라고 할 수 있습니다.