Machine Learning/[Kaggle Course] ML (+ 딥러닝, 컴퓨터비전)

[Kaggle Course] Binary Classification(이진 분류) - sigmoid, cross-entropy

WakaraNai 2020. 12. 30. 21:12
728x90
반응형

지금까지 neural network로 'regression problems'를 어떻게 해결할 수 있는 배웠습니다.

이제 머신러닝의 또다른 골칫거리, 바로 'Classification'을 neural network에 적용해봅시다.

가장 큰 차이점은 우리가 사용했던 loss function에서,

그리고 마지막 layer에서 나온 출력값의 종류에서 찾을 수 있습니다. (저게 차이점이 아니야!)

 

Binary Classification

두 그룹(class) 중 하나로 분류하는 것은 일반적인 머신러닝 기법입니다.

고객이 구매할 가능성이 있는지, 신용 카드 거래가 사기였는지,

우주에서 온 신호가 새로운 행성의 증거가 되는지 등, 모두 "Binary Calssification" 문제입니다.

 

raw data에서 이 같은 그룹들은 "Yes" 또는 "No", "Dog" 또는 "Cat" 의 문자열로 표현될 수 있습니다.

이런 데이터를 사용하기 전에는 그룹마다 label을 붙여줘야합니다.

그 그룹이 문자열이 아닌 0 또는 1의 숫자값을 가지도록 변환합니다.

숫자값으로 labeling을 거치고 나야 neural network에서 data를 사용할 수 있습니다.

 

Accuracy and Cross-Entropy

Accuracy는 classification 문제의 성공 여부를 측정할 수 있는 척도 중의 하나입니다.

옳은 예측/전체 예측 의 비율로 계산합니다. ( accuracy = number_correct / total )

항상 정확하게 예측하는 모델이라면 1.0 accuracy가 나옵니다.

다른 모든 것이 동일하다는 점에서, accuracy는 dataset의 그룹들이 거의 동일한 빈도로 나올 때마다 사용하기에 합리적인 척도입니다.

 

acurracy (외의 분류 성능 척도)에서 나타날 수 있는 문제는, loss function처럼 쓸 수 없다는 점입니다.

SGD는 곡선을 부드럽게 바꾸기 위해 loss function이 필요하지만, 

accuracy는 횟수의 비율이기에 "jumps"에서 변경됩니다.

그래서 loss function를 대체해주는 Cross-Entropy Function을 사용합니다.

 

loss function은 훈련 동안 network의 목표를 정해주는 역할입니다.

- Regression에서의 목표는 예상한 결과치와 모델이 예측한 결과치 사이의 거리를 최소화하는 것이었습니다.

그 거리를 측정하기 위해 MAE를 썼구요.

- Classification에서는, 확률 사이의 거리가 필요한데 cross-entropy가 그 값을 알려줍니다.

 

 

 

Cross-Entropy는 하나의 확률 분포에서 다른 확률 분포까지의 거리를 측정할 수 있는 방법 중 하나입니다.

1.0의 확률로 정확한 그룹들을 예측하는 network인지 알아봄으로써 가능합니다.

예측된 확률이 1.0에서 멀어질 수록, cross-entropy loss는 점점 더 커집니다.

 

cross-entropy를 사용해야 하는 이유에 대해 한 가지 더 말하자면, (좀 미묘하지만)

Classification loss에 cross-entropy를 사용하면,

신경 쓰고 있던 또다른 측정값/척도(ex_accuracy)도 함께 개선되는 경향을 있기 때문입니다.

 

Cross-Entropy는 틀린 확률 예측에 패널티를 줍니다.(보완?)

 

 

 

Making Probabliities with the Sigmoid Function

cross-entropy와 accuracy function 모두 입력값으로 확률값(0~1 사이 수)이 필요합니다.

dense layer의 결과로 나온 측정한 값들을 이에 맞게 변환하려면,

sigmoid activation이란 새로운 종류의 activation function을 적용해야합니다.

 

마지막 그룹의 예측을 얻으려면 threshold probability를 정의해야 합니다.

전형적으로 여기에 0.5를 사용하며, 이를 통해 반올림값으로 정확한 그룹을 내놓습니다.

(0~1 사이 값으로 변환이 끝난 그룹)

 

0.5 이하는 그 그룹의 label이 0, 0.5이며, 

0.5 이상은 그 그룹의 label이 1이라는 의미를 가집니다.

0.5 threshold는 Keras의 accuracy metric의 기본값입니다.

 

Sigmoid function 0~1 사이로 수를 변환 가능함을 증명하는 그래프

 

 

 

Ex. Binary Classification

ionosphere(전리층) dataset을 사용합니다.

지구 대기의 전리층(=중간권+열권+외기권)에서 얻은 레이더 신호에 대한 features를 포함하고 있습니다.

이 dataset으로, 신호가 어떤 물체의 존재를 보여주는지 아니면 그저 빈 공기인지 결정해보려 합니다.

 

Setup

import pandas as pd
from IPython.display import display

ion = pd.read_csv('../input/dl-course-data/ion.csv', index_col=0)
display(ion.head())

df = ion.copy()
df['Class'] = df['Class'].map({'good': 0, 'bad': 1})

df_train = df.sample(frac=0.7, random_state=0)
df_valid = df.drop(df_train.index)

max_ = df_train.max(axis=0)
min_ = df_train.min(axis=0)

df_train = (df_train - min_) / (max_ - min_)
df_valid = (df_valid - min_) / (max_ - min_)
df_train.dropna(axis=1, inplace=True) # drop the empty feature in column 2
df_valid.dropna(axis=1, inplace=True)

X_train = df_train.drop('Class', axis=1)
X_valid = df_valid.drop('Class', axis=1)
y_train = df_train['Class']
y_valid = df_valid['Class']

 

1. Define a model with 'sigmoid' activation at final layer  -> To get 그룹 probabilities

from tensorflow import keras
from tensorflow.keras import layers

model = keras.Sequential([
    layers.Dense(4, activation='relu', input_shape=[33]),
    layers.Dense(4, activation='relu'),    
    layers.Dense(1, activation='sigmoid'),
])

 

2. Add Cross-Entropy loss, Accuracy to model <- 'compile' method

'Adam' optimizer는 classification에서도 좋은 결과치를 내놓으니 그대로 사용했습니다.

그룹의 수가 2개로만 나눠지는 문제의 경우 '이진' 버전 (0 or 1) 으로 바꿔야 합니다.

(더 많은 그룹이 필요한 경우라면 다른 방법을 사용해야 합니다.

model.compile(
    optimizer='adam',
    loss='binary_crossentropy',
    metrics=['binary_accuracy'],
)

 

 

2-1. Add early stopping callback'

Classification problem의 모델은 훈련을 마치기까지 몇 시간이 걸릴 수 있으므로,

편의상,  'early stopping callback'도 추가해두겠습니다.

early_stopping = keras.callbacks.EarlyStopping(
    patience=10,
    min_delta=0.001,
    restore_best_weights=True,
)

history = model.fit(
    X_train, y_train,
    validation_data=(X_valid, y_valid),
    batch_size=512,
    epochs=1000,
    callbacks=[early_stopping],
    verbose=0, # hide the output because we have so many epochs
)

 

늘 그래왔듯, learning curve를 관찰해봅시다.

validation set에서의 loss와 accuracy가 가장 좋은 값을 말이죠, (각각 val_loss와 val_binary_accuracy를 말한 것)

(Early Stopping은 이러한 값들을 계산하기 위해 가중치들을 저장한다는 점 기억하시고!)

history_df = pd.DataFrame(history.history)
# Start the plot at epoch 5
history_df.loc[5:, ['loss', 'val_loss']].plot()
history_df.loc[5:, ['binary_accuracy', 'val_binary_accuracy']].plot()

print(("Best Validation Loss: {:0.4f}" +\
      "\nBest Validation Accuracy: {:0.4f}")\
      .format(history_df['val_loss'].min(), 
              history_df['val_binary_accuracy'].max()))
              
              
# Best Validation Loss: 0.6846
# Best Validation Accuracy: 0.7429

Exercise

 

setup

# Setup plotting
import matplotlib.pyplot as plt
plt.style.use('seaborn-whitegrid')
# Set Matplotlib defaults
plt.rc('figure', autolayout=True)
plt.rc('axes', labelweight='bold', labelsize='large',
       titleweight='bold', titlesize=18, titlepad=10)
plt.rc('animation', html='html5')

import pandas as pd

from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler, OneHotEncoder
from sklearn.impute import SimpleImputer
from sklearn.pipeline import make_pipeline
from sklearn.compose import make_column_transformer

hotel = pd.read_csv('../input/dl-course-data/hotel.csv')

X = hotel.copy()
y = X.pop('is_canceled')

X['arrival_date_month'] = \
    X['arrival_date_month'].map(
        {'January':1, 'February': 2, 'March':3,
         'April':4, 'May':5, 'June':6, 'July':7,
         'August':8, 'September':9, 'October':10,
         'November':11, 'December':12}
    )

features_num = [
    "lead_time", "arrival_date_week_number",
    "arrival_date_day_of_month", "stays_in_weekend_nights",
    "stays_in_week_nights", "adults", "children", "babies",
    "is_repeated_guest", "previous_cancellations",
    "previous_bookings_not_canceled", "required_car_parking_spaces",
    "total_of_special_requests", "adr",
]
features_cat = [
    "hotel", "arrival_date_month", "meal",
    "market_segment", "distribution_channel",
    "reserved_room_type", "deposit_type", "customer_type",
]

transformer_num = make_pipeline(
    SimpleImputer(strategy="constant"), # there are a few missing values
    StandardScaler(),
)
transformer_cat = make_pipeline(
    SimpleImputer(strategy="constant", fill_value="NA"),
    OneHotEncoder(handle_unknown='ignore'),
)

preprocessor = make_column_transformer(
    (transformer_num, features_num),
    (transformer_cat, features_cat),
)

# stratify - make sure classes are evenlly represented across splits
X_train, X_valid, y_train, y_valid = \
    train_test_split(X, y, stratify=y, train_size=0.75)

X_train = preprocessor.fit_transform(X_train)
X_valid = preprocessor.transform(X_valid)

input_shape = [X_train.shape[1]]

 

1. Define a Model

'Hotel California' dataset을 바탕으로 binary classifier를 이용하여 예약 취소를 예측하는 모델을 만드려 합니다.

droup layers와 batch normalization을 모두 적용한 model를 만들 겁니다. 오른쪽 그림을 바탕으로 만들어보세요. binary classifier 그림입니다.

 

from tensorflow import keras
from tensorflow.keras import layers

# YOUR CODE HERE: define the model given in the diagram
model = keras.Sequential([
    layers.BatchNormalization(input_shape=input_shape),
    layers.Dense(256, activation = 'relu'),
    layers.BatchNormalization(),
    layers.Dropout(rate=0.3),
    layers.Dense(256, activation = 'relu'),
    layers.BatchNormalization(),
    layers.Dropout(rate=0.3),
    layers.Dense(1, activation = 'sigmoid'),
])

 

 

2. Add Optimizer, Loss, and Metric

Adam optimizer와 binary 버전에 맞는 cross-entropy loss와 accuracy metric을 추가합시다

# YOUR CODE HERE
model.compile(
    optimizer='adam',
    loss='binary_crossentropy',
    metrics=['binary_accuracy'],
)

 

3. Train and Evaluate

model을 훈련시켜 learning curves를 봅시다.

아마도 60-70 epochs를 돌면서 1-2분 정도 걸릴 겁니다.

early_stopping = keras.callbacks.EarlyStopping(
    patience=5,
    min_delta=0.001,
    restore_best_weights=True,
)
history = model.fit(
    X_train, y_train,
    validation_data=(X_valid, y_valid),
    batch_size=512,
    epochs=200,
    callbacks=[early_stopping],
)

history_df = pd.DataFrame(history.history)
history_df.loc[:, ['loss', 'val_loss']].plot(title="Cross-entropy")
history_df.loc[:, ['binary_accuracy', 'val_binary_accuracy']].plot(title="Accuracy")

43번째 epoch에서 stop - loss: 0.3368, binary_accuracy: 0.8452 / val_loss: 0.3511, val_binary_accuracy: 0.8639

 

learning curves를 관찰해봅시다

 

1. underfit 또는 overfit이 model에 있어 보이나요?

 -> training loss가 계속 감소하는 경향을 보이지만, early-stopping callback으로 overfitting을 예방했습니다.

 (-> overfit은 training loss가 감소하는데, validation loss가 증가하기 시작하여 간격이 넓어지는 것입니다)

 

2. cross-entropy loss가 accuracy에 좋은 선택이었나요?

 -> accuracy가 오를 때 cross-entropy는 감소하는 비율도 같으므로, cross-entropy를 최소화하는 것은 좋은 선택입니다.

훈련은 성공적으로 마친 것 같습니다

(예측된 확률이 1.0에서 멀어질 수록, cross-entropy loss는 점점 더 커집니다.  목표는 예상한 결과치와 모델이 예측한 결과치 사이의 거리를 최소화하는 것입니다. Regression에서는 그 거리를 측정하기 위해 MAE를 썼구요. Classfication은 cross-entropy를 최소화하여 예측된 확륙 1.0에 가깝게 해주는 것이 목표입니다.)

 

728x90
반응형