Pytorch를 활용한 Kaggle 타이타닉 데이터셋 예측 연습

지승훈·2024년 11월 14일
0

기존 RandomForest를 사용한 Baseline 코드

# 필요한 라이브러리 임포트
import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score, classification_report

# 데이터 불러오기
train = pd.read_csv('train.csv')
test = pd.read_csv('test.csv')

# 데이터 전처리
def preprocess_data(df):
    # 결측치 처리
    df['Age'] = df['Age'].fillna(df['Age'].mean())
    df['Embarked'] = df['Embarked'].fillna(df['Embarked'].mode()[0])
    df['Fare'] = df['Fare'].fillna(df['Fare'].mean())
    
    # 범주형 변수 처리
    df['Sex'] = df['Sex'].map({'male': 0, 'female': 1})
    df['Embarked'] = df['Embarked'].map({'S': 0, 'C': 1, 'Q': 2})
    
    # 필요한 특성 선택
    features = ['Pclass', 'Sex', 'Age', 'SibSp', 'Parch', 'Fare', 'Embarked']
    return df[features]

# 학습 데이터 전처리
X = preprocess_data(train)
y = train['Survived']

# 학습 데이터와 검증 데이터 분리
X_train, X_val, y_train, y_val = train_test_split(X, y, test_size=0.2, random_state=42)

# RandomForest #모델 생성 및 학습
rf_model = RandomForestClassifier(n_estimators=100, random_state=42)
rf_model.fit(X_train, y_train)

# 검증 데이터로 예측
val_pred = rf_model.predict(X_val)

# 모델 성능 평가
print('검증 데이터 정확도:', accuracy_score(y_val, val_pred))
print('\n분류 보고서:')
print(classification_report(y_val, val_pred))

# 테스트 데이터 예측
test_processed = preprocess_data(test)
test_pred = rf_model.predict(test_processed)

# 제출 파일 생성
submission = pd.DataFrame({
    'PassengerId': test['PassengerId'],
    'Survived': test_pred
})
submission.to_csv('submission.csv', index=False)
print('\n제출 파일이 생성되었습니다.')

GPT를 이용하여 Pytorch 버전으로 변환했다.

import torch
import torch.nn as nn
from torch.utils.data import Dataset, DataLoader
import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler

# 데이터 불러오기
train = pd.read_csv('train.csv')
test = pd.read_csv('test.csv')

# 데이터 전처리
def preprocess_data(df):
    # 결측치 처리
    df['Age'] = df['Age'].fillna(df['Age'].mean())
    df['Embarked'] = df['Embarked'].fillna(df['Embarked'].mode()[0])
    df['Fare'] = df['Fare'].fillna(df['Fare'].mean())
    
    # 범주형 변수 처리
    df['Sex'] = df['Sex'].map({'male': 0, 'female': 1})
    df['Embarked'] = df['Embarked'].map({'S': 0, 'C': 1, 'Q': 2})
    
    # 필요한 특성 선택
    features = ['Pclass', 'Sex', 'Age', 'SibSp', 'Parch', 'Fare', 'Embarked']
    return df[features]

# 커스텀 데이터셋 클래스
class TitanicDataset(Dataset):
    def __init__(self, features, targets=None):
        self.features = torch.FloatTensor(features)
        self.targets = torch.FloatTensor(targets) if targets is not None else None
        
    def __len__(self):
        return len(self.features)
    
    def __getitem__(self, idx):
        if self.targets is not None:
            return self.features[idx], self.targets[idx]
        return self.features[idx]

# 신경망 모델 정의
class TitanicNet(nn.Module):
    def __init__(self, input_size):
        super(TitanicNet, self).__init__()
        self.layers = nn.Sequential(
            nn.Linear(input_size, 64),
            nn.ReLU(),
            nn.BatchNorm1d(64),
            nn.Dropout(0.3),
            
            nn.Linear(64, 32),
            nn.ReLU(),
            nn.BatchNorm1d(32),
            nn.Dropout(0.2),
            
            nn.Linear(32, 1),
            nn.Sigmoid()
        )
    
    def forward(self, x):
        return self.layers(x)

# 데이터 전처리 및 스케일링
X = preprocess_data(train)
y = train['Survived']

scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)

# 학습/검증 데이터 분할
X_train, X_val, y_train, y_val = train_test_split(X_scaled, y, test_size=0.2, random_state=42)

# 데이터셋 및 데이터로더 생성
train_dataset = TitanicDataset(X_train, y_train)
val_dataset = TitanicDataset(X_val, y_val)

train_loader = DataLoader(train_dataset, batch_size=32, shuffle=True)
val_loader = DataLoader(val_dataset, batch_size=32)

# 디바이스 설정
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')

# 모델 초기화
model = TitanicNet(input_size=X_train.shape[1]).to(device)
criterion = nn.BCELoss()
optimizer = torch.optim.Adam(model.parameters(), lr=0.001)

# 학습
num_epochs = 100
for epoch in range(num_epochs):
    model.train()
    for features, targets in train_loader:
        features, targets = features.to(device), targets.to(device)
        
        optimizer.zero_grad()
        outputs = model(features)
        loss = criterion(outputs.squeeze(), targets)
        
        loss.backward()
        optimizer.step()
    
    # 검증
    if (epoch + 1) % 10 == 0:
        model.eval()
        with torch.no_grad():
            correct = 0
            total = 0
            for features, targets in val_loader:
                features, targets = features.to(device), targets.to(device)
                outputs = model(features)
                predicted = (outputs.squeeze() >= 0.5).float()
                total += targets.size(0)
                correct += (predicted == targets).sum().item()
            
            accuracy = correct / total
            print(f'Epoch [{epoch+1}/{num_epochs}], Loss: {loss.item():.4f}, Validation Accuracy: {accuracy:.4f}')

# 테스트 데이터 예측
test_processed = preprocess_data(test)
test_scaled = scaler.transform(test_processed)
test_dataset = TitanicDataset(test_scaled)
test_loader = DataLoader(test_dataset, batch_size=32)

model.eval()
predictions = []
with torch.no_grad():
    for features in test_loader:
        features = features.to(device)
        outputs = model(features)
        predicted = (outputs.squeeze() >= 0.5).float()
        predictions.extend(predicted.cpu().numpy())

# 제출 파일 생성
submission = pd.DataFrame({
    'PassengerId': test['PassengerId'],
    'Survived': predictions
})
submission.to_csv('submission.csv', index=False)
print('\n제출 파일이 생성되었습니다.')

Kaggle 제출 결과

Score를 0.8 이상으로 튜닝하기 위해 몇가지 데이터 전처리 과정을 거쳐 보았다

1. 결측치 처리

	df['Age'] = df['Age'].fillna(df['Age'].median())
    df['Embarked'] = df['Embarked'].fillna(df['Embarked'].mode()[0])
    df['Fare'] = df['Fare'].fillna(df['Fare'].mean())

결측치가 많은 Age, Embarked, Fare Feature는 median, s, mean 값으로 채웠다

2. 나이 구간화

 df.loc[df['Age'] <= 10, 'Age_clean'] = 0
 df.loc[(df['Age'] > 10) & (df['Age'] <= 16), 'Age_clean'] = 1
 df.loc[(df['Age'] > 16) & (df['Age'] <= 20), 'Age_clean'] = 2
 df.loc[(df['Age'] > 20) & (df['Age'] <= 26), 'Age_clean'] = 3
 df.loc[(df['Age'] > 26) & (df['Age'] <= 30), 'Age_clean'] = 4
 df.loc[(df['Age'] > 30) & (df['Age'] <= 36), 'Age_clean'] = 5
 df.loc[(df['Age'] > 36) & (df['Age'] <= 40), 'Age_clean'] = 6
 df.loc[(df['Age'] > 40) & (df['Age'] <= 46), 'Age_clean'] = 7
 df.loc[(df['Age'] > 46) & (df['Age'] <= 50), 'Age_clean'] = 8
 df.loc[(df['Age'] > 50) & (df['Age'] <= 60), 'Age_clean'] = 9
 df.loc[df['Age'] > 60, 'Age_clean'] = 10

5세 단위로 자르고 50대는 10세단위 그리고 60세이상은 모두 묶음

3. 이름에서 직함 추출

    # 정규 표현식 사용해서 추출
    df['Title'] = df['Name'].str.extract(' ([A-Za-z]+)\.', expand=False)
    
    # 직함 그룹화
    rare_titles = ['Lady', 'Countess','Capt', 'Col','Don', 'Dr', 'Major', 'Rev', 'Sir.', 'Jonkheer', 'Dona']
    df.loc[df['Title'].isin(rare_titles), 'Title'] = 'Rare'
    df['Title'] = df['Title'].replace(['Mlle', 'Ms', 'Mme'], ['Miss', 'Miss', 'Mrs'])
    
    # 직함 매핑
    title_mapping = {"Mr": 1, "Miss": 2, "Mrs": 3, "Master": 4, "Rare": 5}
    df['Title'] = df['Title'].map(title_mapping)
    df['Title'] = df['Title'].fillna(0)

4. 객실 정보 처리

    # Pclass를 기준으로 Cabin 데이터 처리
    cabin_mapping = {
        'A': 0, 'B': 1, 'C': 2, 'D': 3,
        'E': 4, 'F': 5, 'G': 6, 'T': 7
    }
    df['Cabin_clean'] = df['Cabin'].str[:1]
    df['Cabin_clean'] = df['Cabin_clean'].map(cabin_mapping)
    df['Cabin_clean'] = df.groupby('Pclass')['Cabin_clean'].transform('median')

5. 가족 크기 계산

    df['FamilySize'] = df['SibSp'] + df['Parch'] + 1

6. 범주형 변수 처리

    df['Sex'] = df['Sex'].map({'male': 0, 'female': 1})
    df['Embarked'] = df['Embarked'].map({'S': 0, 'C': 1, 'Q': 2})
    
    # 필요한 특성 선택
    features = ['Pclass', 'Sex', 'Age_clean', 'SibSp', 'Parch', 
                'Fare', 'Embarked', 'FamilySize', 'Title',
                'Cabin_clean']

데이터 전처리 후 제출 결과

부족한 부분

1. 시간내에 정확도를 더 높이지 못함
2. 데이터에 대한 전체적인 이해
3. 각 필드에 어떤 특징이 있는지 고려하지 않고, 직관에 의존함
4. EDA를 제대로 하지 못해서 논리적으로 풀어나가지 못함
5. GPT 사용 과다

개선할 부분

1. 각 필드간 상관관계를 분석하고 시각화하여 특징을 파악하기
2. 하이퍼 파라미터 튜닝도 시도해보기

[참고]
https://www.kaggle.com/code/ldfreeman3/a-data-science-framework-to-achieve-99-accuracy/notebook
https://www.kaggle.com/code/jiripodivin/titanic-with-keras

0개의 댓글