728x90
반응형

 이전 포스트에서 타이타닉 데이터를 사용해 생존자 분류 모델을 만들어보았다. 이번 포스트에서는 이전 모델보다 성능 향상을 일으켜보자.

 

 

타이타닉 데이터 생존자 분류 모델 성능 향상

  • 이전 데이터셋 생성 과정에서 결측 값 처리까지는 동일하게 실시하도록 하겠다.
  • 그러나, 문자 데이터(숫자로 표기되지만, 실제론 문자인 데이터)는 원-핫 벡터로 바꿔 모델에 학습시켜보도록 하자.

 

 

0. 이전 코드 정리

  • 문자형 데이터 처리 이전까지의 코드를 정리하자.
import pandas as pd
import numpy as np
import os
from tensorflow.keras.layers import Dense
from tensorflow import keras
from copy import copy
# csv파일을 dictionary로 관리하기 쉽게 올림
def import_Data(file_path):

    result = dict()
    for file in os.listdir(file_path):

        file_name = file[:-4]
        result[file_name] = pd.read_csv(file_path + "/" + file)

    return result


# Rawdata 통합
def make_Rawdata(dict_data):

    dict_key = list(dict_data.keys())
    test_Dataset = pd.merge(dict_data["gender_submission"], dict_data["test"], how='outer', on="PassengerId")
    Rawdata = pd.concat([dict_data["train"], test_Dataset])
    Rawdata.reset_index(drop=True, inplace=True)
    
    return Rawdata


# 필요 없는 컬럼 제거(DataHandling 시작)
def remove_columns(DF, remove_list):
    
    # 원본 정보 유지를 위해 copy하여, 원본 Data와의 종속성을 끊었다.
    result = copy(Rawdata)

    # PassengerId를 Index로 하자.
    result.set_index("PassengerId", inplace = True)

    # 불필요한 column 제거
    for column in remove_list:

        del(result[column])
        
    return result


# 결측값 제거
def missing_value(DF):

    # Cabin 변수를 제거하자
    del(DF["Cabin"])
    
    # 결측값이 있는 모든 행은 제거한다.
    DF.dropna(inplace = True)
# Global Parameter
file_path = "./Dataset"
remove_list = ["Name", "Ticket"]


# 0. Rawdata 생성
Rawdata_dict = import_Data(file_path)
Rawdata = make_Rawdata(Rawdata_dict)


# 1. Data Handling 시작
# 필요 없는 column 제거
DF_Hand = remove_columns(Rawdata, remove_list)

# 결측값 처리
missing_value(DF_Hand)
DF_Hand

 

 

 

 

1. 문자형 데이터 원-핫 벡터 처리

  • 위 데이터 핸들링 결과에서 문자형 데이터는 다음과 같다.
  • Pclass, Sex, Embarked 이 3개 변수는 숫자로 치환한다 할지라도, 그 숫자는 실제 숫자가 아니다.
  • 이러한, 변수를 범주형 변수라고 한다.

 

1.1. 범주형 변수(Categorical Variable)

  • 범주형 변수란, 숫자로 치환한다 할지라도, 실제로는 숫자의 정보를 갖고 있지 않는 변수를 의미한다.
  • 범주형 변수에는 문자 그 자체인 명목 척도(Nominal scale)와 순서의 정보가 존재하는 서열 척도(Ordical scale)가 존재한다.
  • 예를 들어, "물컵", "주전자", "식칼", "도마", "프라이팬", "주걱", "행주"라는 변수가 있다고 생각해보자.
  • 이 변수들을 순서대로 숫자로 치환해줬을 때, "물컵" = 1, "주전자" = 2, "식칼" = 3, "도마" = 4, "프라이팬" = 5, "주걱" = 6, "행주" = 7로 하였다고 가정해보자.
  • 이 숫자는 우리의 눈에는 숫자로 보이지만, 실제론 숫자의 특성인 연산이 불가능하며, 비교할 수가 없다.
  • 물컵의 1이 프라이팬의 5보다 우월하다고 할 수 없으며, 프라이팬의 5가 물컵 1의 5개만큼의 가치가 있다고 할 수 없다.
  • 이 예시가 "초등학교", "중학교", "고등학교", "대학교", "대학원"으로 서열의 개념이 생긴다 할지라도, 그 간격이 일정하지 않으므로, 이러한 데이터를 연산할 수 없다.
  • 즉, 문자형 데이터는 단순하게 숫자로 치환해주는 걸로 끝내선, 실제 그들이 가지고 있는 의미를 제대로 담아낼 수가 없다는 소리다!

 

 

 

 

2. 원-핫 벡터(One-Hot Vector)

  • 나중에 인코딩 파트에서 다시 한번 다루겠지만, 원-핫 벡터는 가장 대표적인 문자를 벡터로 바꾸는 기법 중 하나다.
  • 원-핫 벡터를 만드는 과정은 원-핫 인코딩(One-Hot Encoding)이라 부른다.
  • 원-핫 벡터의 순서는 다음과 같다.
  1. 한 변수 안에 있는 중복을 제거한(Unique) 문자들을 대상으로 고유 번호를 매긴다.
  2. 이를 기반으로 희소 벡터를 생성한다.

 

2.1 정수 인코딩

  • 1. 과정을 "정수 인코딩"이라 한다.
  • 정수 인코딩은 앞서 우리가 범주형 변수(Categorical Variable)를 숫자로 치환해주는 과정과 동일하다.
  • 때론 이 정수 인코딩 시, 빈도를 고려하여, 인코딩 순서를 바꾸기도 한다.
  • 중복이 없는 단어와 숫자를 매칭 시켜 나온 결과물을 용어사전(Vocabulary)라고 한다.

 

2.2 희소 벡터 만들기

  • 희소 벡터란 표현하고자 하는 인덱스는 1로 나머지는 0으로 이루어진 벡터를 의미한다.
  • 원-핫 벡터는 생성된 용어사전(Vocabulary)을 기반으로 희소 벡터를 만드는 방법이다.
  • 예를 들어 다음과 같은 용어사전이 있다고 가정해보자.
  • Vocabulary = ["감자", "고구마", "피망", "사과", "딸기"] = [0,1,2,3,4]
  • 여기서 "피망"의 희소 벡터는 다음과 같다.
  • 피망 = [0, 0, 1, 0, 0]

 

2.3. 원-핫 인코딩의 한계점

  • 용어 사전의 크기가 크면 클수록 벡터의 크기가 커지므로, 벡터 저장을 위한 필요 공간이 커진다.
  • 즉, 단어가 1,000개라면, 단어 1,000개 모두 벡터의 크기가 1,000이므로, 입력될 텐서가 지나치게 커진다.
  • 단어를 단순하게 숫자로 바꾸고 해당 인덱스를 1로 나머지를 0으로 만든 것이므로, 의미, 단어 간 유사도를 표현하지 못한다.

 

 

 

 

3. 문자형 변수를 One-Hot 벡터로 치환해보자.

  • 원-핫 벡터 생성은 그 알고리즘이 상당히 단순하므로, 직접 구현해보도록 하겠다.
  • 생성될 원-핫 벡터는 대상 변수의 구성 원소의 빈도를 감안하여 생성하도록 하겠다.
  • DataFrame을 기반으로 작업하였으므로, DataFrame의 성질을 이용해보자.
def one_hot_Encoding(data, column):

    # 한 변수 내 빈도
    freq = data[column].value_counts()

    # 빈도가 큰 순서로 용어 사전 생성
    vocabulary = freq.sort_values(ascending = False).index

    # DataFrame에 용어 사전 크기의 column 생성
    for word in vocabulary:

        new_column = column + "_" + str(word)
        data[new_column] = 0

    # 생성된 column에 해당하는 row에 1을 넣음
    for word in vocabulary:

        target_index = data[data[column] == word].index
        new_column = column + "_" + str(word)
        data.loc[target_index, new_column] = 1

    # 기존 컬럼 제거
    del(data[column])
one_hot_Encoding(DF_Hand, 'Pclass')
one_hot_Encoding(DF_Hand, 'Sex')
one_hot_Encoding(DF_Hand, 'Embarked')
DF_Hand1

  • 위 코드는 DataFrame의 특징을 이용한 것으로, 각 변수별로 원소의 수가 많은 칼럼 순으로 먼저 생성한다.
  • 생성한 칼럼은 0으로 가득 채운다.
  • 원본 칼럼에서 각 원소에 해당하는 칼럼에 1을 채운다.

 

 

 

 

4. 데이터를 쪼개고 연속형 데이터의 스케일 조정을 해보자.

# 데이터 쪼개기
# Label 생성
y_test, y_train = DF_Hand["Survived"][:300].to_numpy(), DF_Hand["Survived"][300:].to_numpy()

# Dataset 생성
del(DF_Hand["Survived"])
X_test, X_train = DF_Hand[:300].values, DF_Hand[300:].values

 

  • 이전에는 연속형 데이터 셋에 최소-최대 스케일 변환만 적용하였으나, 이번엔 표준 정규 분포화도 할 수 있도록 짜 보자.
def scale_adjust(X_test, X_train, C_number, key="min_max"):
    
    if key == "min_max":
        
        min_key = np.min(X_train[:,C_number])
        max_key = np.max(X_train[:,C_number])
        
        X_train[:,C_number] = (X_train[:,C_number] - min_key)/(max_key - min_key)
        X_test[:,C_number] = (X_test[:,C_number] - min_key)/(max_key - min_key)
        
    elif key =="norm":
        
        mean_key = np.mean(X_train[:,C_number])
        std_key = np.std(X_train[:,C_number])
        
        X_train[:,C_number] = (X_train[:,C_number] - mean_key)/std_key
        X_test[:,C_number] = (X_test[:,C_number] - mean_key)/std_key
        
    return X_test, X_train
X_test, X_train = scale_adjust(X_test, X_train, 0, key="min_max")
X_test, X_train = scale_adjust(X_test, X_train, 3, key="min_max")
>>> X_test[0]
array([0.27345609, 1.        , 0.        , 0.01415106, 1.        ,
       0.        , 0.        , 1.        , 0.        , 1.        ,
       0.        , 0.        ])
  • 원-핫 벡터를 사용했을 때, 이전 모델과의 차이를 보기 위해, 이번에도 변수의 표준화는 최소-최대 스케일 변화를 실시하였다.

 

 

 

 

5. 학습 후 결과를 비교해보자.

# 모델 생성
model = keras.Sequential()
model.add(Dense(128, activation = "relu"))
model.add(Dense(64, activation = "relu"))
model.add(Dense(32, activation = "relu"))
model.add(Dense(16, activation = "relu"))
model.add(Dense(1, activation = "sigmoid"))

# 모델 Compile
opt = keras.optimizers.Adam(learning_rate=0.005)
model.compile(optimizer=opt,
              loss = "binary_crossentropy",
              metrics=["binary_accuracy"])
>>> model.fit(X_train, y_train, epochs = 500)

Epoch 1/500
24/24 [==============================] - 1s 1ms/step - loss: 0.5498 - binary_accuracy: 0.7345
Epoch 2/500
24/24 [==============================] - 0s 1ms/step - loss: 0.4263 - binary_accuracy: 0.8497
Epoch 3/500
24/24 [==============================] - 0s 1ms/step - loss: 0.2957 - binary_accuracy: 0.8976
Epoch 4/500
24/24 [==============================] - 0s 1ms/step - loss: 0.3229 - binary_accuracy: 0.8750
Epoch 5/500
24/24 [==============================] - 0s 1ms/step - loss: 0.2964 - binary_accuracy: 0.8851
Epoch 6/500
24/24 [==============================] - 0s 1ms/step - loss: 0.3451 - binary_accuracy: 0.8758

...

Epoch 496/500
24/24 [==============================] - 0s 1ms/step - loss: 0.1697 - binary_accuracy: 0.9294
Epoch 497/500
24/24 [==============================] - 0s 1ms/step - loss: 0.1827 - binary_accuracy: 0.9142
Epoch 498/500
24/24 [==============================] - 0s 997us/step - loss: 0.1731 - binary_accuracy: 0.9337
Epoch 499/500
24/24 [==============================] - 0s 1ms/step - loss: 0.1876 - binary_accuracy: 0.9143
Epoch 500/500
24/24 [==============================] - 0s 1ms/step - loss: 0.1641 - binary_accuracy: 0.9322
<tensorflow.python.keras.callbacks.History at 0x21c06cd4790>
>>> pred = model.predict(X_test).reshape(X_test.shape[0])
>>> pred = np.where(pred > 0.5, 1, 0)
>>> accuracy = 1 - (np.where((pred - y_test) == 0, 0, 1).sum()/len(y_test))
>>> print("Accuracy:", accuracy)
Accuracy: 0.7966666666666666
  • 이전 모델의 Accuracy가 0.78이 나왔으며, 범주형 데이터를 One-Hot Vector로 바꾼 이번 모델은 Accuracy가 0.79667로 소폭 상승하였다.

 

 

 범주형 데이터를 원-핫 벡터로 바꿔 성능이 소폭 상승 하긴 하였으나, 만족스러운 수준까지 성장하진 않았다. 다음 포스트에서는 하이퍼 파라미터 튜닝을 통해 성능을 보다 올려보도록 하자.

728x90
반응형

+ Recent posts