본문 바로가기
Tensorflow

[Kaggle] Titanic: Machine Learning from Disaster (2)

by 청양호박이 2020. 3. 6.

가끔 생각날때, 지금까지 정리해 본 모델을 가지고 예측모델 및 분석 대회 플랫폼인 Kaggle을 이따금 풀어보겠습니다. Kaggle은 예전에는 독자적인 회사였으나, 2017년 3월에 구글에 인수가 되었습니다. 여기에는 크게 Compete모드와 Data모드가 있으며, 기업이 될 수 있고 혹은 단체에서 문제를 등록하면 Data Scientist들이 이를 해결하기 위한 모델을 개발하고 경쟁하는 장 입니다. 경우에 따라서는 보상이 걸리기도 합니다.

 

지난번에 Logistic Regression을 알아보았기 때문에, 해당 모델로 해결이 가능한 가장 초급적인 문제를 풀어보고자 합니다. 해당문제는 이미 많은 사람들이 풀어본 문제이며, 입문문제로 꼽히기도 합니다.

바로 Titanic 승객들의 정보를 토대로 생존여부를 예측하는 문제입니다. 앞으로 문제는 아래의 단계로 분석해서 모델을 적용하겠습니다.

 

  • 데이터 불러오기
  • Train Data의 컬럼 종류 및 값 유형 분석
  • 결측치 및 문자형 데이터 처리
  • 상관관계가 있을만한 항목을 기준으로 학습에 사용할 컬럼 선정
  • Train Data 정리하기(One-Hot Encoding포함)
  • 모델 구성 및 학습 평가
  • 결과 분석 및 모델 선택
  • Test Data 적용 및 제출

지난 번에는 학습에 필요한 컬럼을 선택하고, 결측치 및 문자형 데이터 전처리를 진행 후 범주형 컬럼은 One-Hot Encoding을 추가하여 최종 학습에 필요한 데이터를 만들어 냈습니다. 이번에는 모델을 만들고 해당 모델에 데이터를 넣어 학습 시키고 그 결과를 검증하여, test.csv 적용 후 제출해 보도록 하겠습니다.

 

 

6. 모델 구성 및 학습 평가


지난번에 학습한 내용을 기준으로 적용을 할 것이기 때문에 logistic regression 모델을 사용하도록 하겠습니다. 기억나시나요?? Keras를 적용했을때, logistic regression은 Dense Layer에 activation function이 'sigmoid'였습니다. 그럼 Dense Layer를 여러가지 방식으로 쌓아보고 결과를 보도록 하겠습니다.

 

[1 layer - sigmoid]

class LRTitanic:
    def __init__(self):
        self.learning_rate = 0.001
        self.epochs = 500
        
    def buildModel(self):
        self.model = tf.keras.Sequential()
        self.model.add(tf.keras.layers.Dense(1, activation='sigmoid'))
        
        optimizer = tf.keras.optimizers.Adam(learning_rate=self.learning_rate)
        
        self.model.compile(loss='binary_crossentropy', optimizer=optimizer, metrics=['binary_accuracy'])
        
    def fitModel(self, x, y):
        history = self.model.fit(x, y, epochs=self.epochs, batch_size=100, shuffle=True)
        return history
    
    def predictModel(self, x):
        return self.model.predict(x)
    
    def evalModel(self, x, y):
        return self.model.evaluate(x, y)

위와 같이 sigmoid에 딱 1개의 layer를 쌓았습니다. 여기에 테스트 데이터를 넣고 돌려보면...

model = LRTitanic()
model.buildModel()
hist = model.fitModel(x_tr, y_tr)

##################################
Epoch 292/500
623/623 [==============================] - 0s 27us/sample - loss: 0.4592 - binary_accuracy: 0.8266

Epoch 500/500
623/623 [==============================] - 0s 22us/sample - loss: 0.4514 - binary_accuracy: 0.7945
print(hist.history.keys())
plt.plot(hist.history['loss'], 'r')
plt.plot(hist.history['binary_accuracy'], 'b')
plt.show()

다음과 같이 Loss는 낮아지고, accuracy는 높아져서 어느 한계에서는 변화가 없습니다. 학습은 잘 됬다고 판단할 수 있습니다. 가장 최고 정확도는 0.8266일때가 있습니다.

 

마지막으로 test data를 가지고 evaluate를 해보겠습니다. 해당 Model로는 0.7687의 정확도를 보여줍니다.

model.evalModel(x_te, y_te)

###########################
loss: 0.4753 - binary_accuracy: 0.7687

 

[3 layers - 2 relu, 1 sigmoid]

이번에는 layer를 좀 더 쌓아보겠습니다. 64node의 relu layer를 2개 쌓고, 마지막으로 sigmoid를 적용합니다.

    def buildModel(self):
        self.model = tf.keras.Sequential()
        self.model.add(tf.keras.layers.Dense(64, activation='relu'))
        self.model.add(tf.keras.layers.Dense(64, activation='relu'))
        self.model.add(tf.keras.layers.Dense(1, activation='sigmoid'))
        
        optimizer = tf.keras.optimizers.Adam(learning_rate=self.learning_rate)
        
        self.model.compile(loss='binary_crossentropy', optimizer=optimizer, metrics=['binary_accuracy'])

그 결과는...

Epoch 500/500
623/623 [==============================] - 0s 29us/sample - loss: 0.4194 - binary_accuracy: 0.8266

model.evalModel(x_te, y_te)

###########################
loss: 0.4691 - binary_accuracy: 0.7761

약간 개선이 되어 보입니다. 하지만 이전 모델의 최고치와 train data의 정확도는 동일하네요.

 

[5 layers - 4 relu, 1 sigmoid]

이번에는 더 촘촘히 쌓아보겠습니다. 

    def buildModel(self):
        self.model = tf.keras.Sequential()
        self.model.add(tf.keras.layers.Dense(256, activation='relu'))
        self.model.add(tf.keras.layers.Dense(256, activation='relu'))
        self.model.add(tf.keras.layers.Dense(64, activation='relu'))
        self.model.add(tf.keras.layers.Dense(64, activation='relu'))
        self.model.add(tf.keras.layers.Dense(1, activation='sigmoid'))
        
        optimizer = tf.keras.optimizers.Adam(learning_rate=self.learning_rate)
        
        self.model.compile(loss='binary_crossentropy', optimizer=optimizer, metrics=['binary_accuracy'])
Epoch 500/500
623/623 [==============================] - 0s 47us/sample - loss: 0.4194 - binary_accuracy: 0.8266
model.evalModel(x_te, y_te)

###########################
loss: 0.4904 - binary_accuracy: 0.7761

Layer를 더 쌓건... 뭐건 결국 Linear Regression으로 도출되는 모델은 저 정도가 한계치로 보여집니다.

 

 

7. 결과 분석 및 모델 선택


3가지 정도 모델을 통해서 결과를 도출했습니다. 이제 그 결과를 가지고 최종 모델을 선택하겠습니다.

 

[1 layer - sigmoid] - train : binary_accuracy: 0.7945, test : binary_accuracy: 0.7687

[3 layers - 2 relu, 1 sigmoid] - train : binary_accuracy: 0.8266, test : binary_accuracy: 0.7761

[5 layers - 4 relu, 1 sigmoid] - train : binary_accuracy: 0.8266, test : binary_accuracy: 0.7761

 

다음과 같습니다. 가장 적합한 model인 2번째 모델로 선정합니다.

 

 

8. Test Data 적용 및 제출


Test Data도 마찬가지로 Train Data와 했던 것과 같이 전처리가 필요합니다. 적용했던 전처리를 동일하게 적용해 보겠습니다.

 

[Data Read]

test = pd.read_csv('test.csv')
print(test.columns)

##############################
Index(['PassengerId', 'Pclass', 'Name', 'Sex', 'Age', 'SibSp', 'Parch',
       'Ticket', 'Fare', 'Cabin', 'Embarked'],
      dtype='object')

Survived 컬럼만 빼고 동일하게 존재합니다.

 

[isNull 확인]

print(test.isnull().sum())

##########################
PassengerId      0
Pclass           0
Name             0
Sex              0
Age             86
SibSp            0
Parch            0
Ticket           0
Fare             1
Cabin          327
Embarked         0
dtype: int64

사용하고자 하는 Pclass, Sex, Embarked 모두 null 이 없습니다.

 

[문자열 숫자형 변환]

test['Sex'] = test['Sex'].map({'male':0, 'female':1})
test['Embarked'] = test['Embarked'].map({'S':0, 'C':1, 'Q':2})

[One-Hot Encoding]

test = pd.get_dummies(test, columns=['Embarked'], prefix='Embarked')
print(test.columns)

##########################################
Index(['PassengerId', 'Pclass', 'Name', 'Sex', 'Age', 'SibSp', 'Parch',
       'Ticket', 'Fare', 'Cabin', 'Embarked_0', 'Embarked_1', 'Embarked_2'],
      dtype='object')

[사용할 컬럼 선택]

test.drop(labels=['PassengerId', 'Name', 'Age', 'SibSp', 'Parch', 'Ticket', 'Fare', 'Cabin'], axis=1, inplace=True)
print(test.columns)

#######################################
Index(['Pclass', 'Sex', 'Embarked_0', 'Embarked_1', 'Embarked_2'], dtype='object')

[실제 모델에 적용할 데이터 형으로 변환]

x_test_real = test.values
print(x_test_real.shape)
print(type(x_test_real))

#########################
(418, 5)
<class 'numpy.ndarray'>

이제 사용할 모든 준비가 되었습니다. 그렇다면, Model의 predict에 넣어서 제출할 결과를 도출합니다.

 

[Predict]

y_test_real = model.predictModel(x_test_real)
print(y_test_real.shape)

########################
(418, 1)

[제출 데이터 준비]

Kaggle에서는 train, test 에 대한 csv말고 제출을 위한 csv를 하나더 제공합니다. 여기에 내 결과를 넣어서 csv로 다시 만들어 제출하는 방식을 사용합니다.

submit = pd.read_csv('gender_submission.csv')
print(submit.head())

아참 현재 Keras에서 나오는 데이터는 결과가 0과1이 아닌 소수점 상태로 나옵니다. 따라서 강제적으로 0.5를 넘으면 1, 적으면 0으로 변경해준다음에 제출을 해야합니다.

for i in range(len(y_test_real)):
    if y_test_real[i][0]>0.5 : 
        y_test_real[i][0] = 1
    else:
        y_test_real[i][0] = 0

y_test_real = np.array(y_test_real, dtype=np.int64)
submit['Survived'] = y_test_real
submit.to_csv('submit_ayotera.csv', index=False)

이제 내 폴더에 해당 파일이 생겼습니다. 

이제 이 파일을 Kaggle에 제출하면 모든게 끝입니다.

여기에 제출은 하면...

요렇게 잘 제출됬다고하고 accuracy rate가 score로 뜨게 됩니다. 이 점수는 과연 leaderboard에서 어느정도를 차지할까요??

요렇게 마무리가 되었습니다. 

 

-Ayotera Lab-

댓글