Logistic Regression 알고리즘은 정확도가 높은 알고리즘으로 알려져 있어 실제 문제에 적용 가능하며 Nueral Networking과 Deep Learning에 중요한 요소이다.

Regression이란 이름을 가지고 있지만 실제로는 Classification 문제에 사용되는 알고리즘 중 하나이다.

 

Classification

우선 Classification에 대해 Binary Classification의 예를 통해 이해해보자.

 

1. Spam Email Detection: Spam or Ham

스팸메일을 구분해내는 프로그램은 이메일이 올 때 학습된 모델을 통해 Spam인지 Ham인지 구분한다.

 

2. Facebook feed: show or hide

페이스북에서는 Classification 알고리즘을 통해 이전에 봤거나 좋아했던 글들을 바탕으로 사용자에게 글들을 맞춤으로 보여준다. (흔히 말하는 유튜브 알고리즘도 마찬가지)

 

0, 1 encoding

1. Spam Detection: Spam(1) or Ham(0)

2. Facebook feed: show(1) or hide(0)

 

그렇다면 공부 시간에 따라 합격과 불합격을 예측하는 Classification을 적용해보자.

 

 

Pass(1) or Fail(0)이라는 관점에서 데이터를 생각해보면 위와 같이 나올 수 있는데 그렇다면 일부의 데이터를 이용해 직선을 그리고 제일 오른쪽에 있는 데이터가 새로 생겼다는 가정하에 직선을 그려보자.

 

위의 그래프를 보면 기존에 Pass 취급을 받았던 데이터 2개가 새로 직선을 만들어줌으로써 Fail이 될 수 있는 문제가 발생할 수 있고 이는 Classification의 관점에서 Linear 한 가설은 맞지 않음을 확인할 수 있다.

결론적으로 Linear Regression은 Regression에 맞는 알고리즘, Logistic Regression은 Classification에 맞는 알고리즘이라는 것을 알 수 있다.

 

또한, 다음 문제점으로 우리가 Pass or Fail과 같이 0과 1 사이의 값으로 이루어지는 Y(결과) 값을 가져야 하는데,

Linear 한 관점에서 우리는 H(x)=Wx+b의 가설을 세우고 W=0.5 b=0으로 가정하면 H(x)=0.5x가 될 것인데, 만약 x=100이라는 데이터가 들어간다면 H(x)는 50이 될 것이며 0, 1과는 거리가 먼 결과 값이 될 것이다. 그렇다면 이런 결과 값을 0과 1 사이의 값으로 변환시켜줄 필요가 있지 않을까? ( 결과적으로 Linear 한 가설은 Classification의 관점에 맞지 않다. )

 

Sigmoid (Logistic) function

 

위의 함수는 데이터를 새로운 함수에 넣고 구현함으로써 0과 1 사이의 값으로 변환시켜줄 수 있고 그 함수를 Sigmoid 함수라고 한다.

 

결과적으로 우리는 다음과 같은 함수를 가설을 얻을 수 있다.

 

가설을 세웠다면 이제 무엇을 해야겠는가? 바로, Cost 함수를 Minimize 하는 것이다.

그런데 이전까지의 Linear 한 가설의 Cost function과는 조금 다른 모양이 될 것임을 예측할 수 있다.

Linear 한 가설의 Cost function은 Wx(1차 방정식)의 제곱의 형태를 띠므로 2차 방정식 모양이 될 것이지만

Sigmoid를 제곱한다면 아래와 같은 모양을 띌 것이다.

 

그런데 위와 같은 함수 형태를 띠면 Gradient 알고리즘을 적용할 때 시작점에 따라 결과가 다르게 나올 수 있다. (기울기가 0이 되는 부분이 여러 군데 존재하기 때문에 Global Minimum이 아닌 Local Minimum에 도달할 수 있다.)

따라서 위와 같은 Cost function은 사용할 수 없고 새로운 가설이 세워졌으니 새로운 Cost function을 세워야 한다.

y = 1 일 때,

H(x) = 1 -> cost(1) = -log(1) = 0

H(x) = 0 -> cost(0) = -log(0) = 무한대

 

y = 0 일 때,

H(x) = 0 -> cost(0) = -log(1-0) = 0

H(x) = 1 -> cost(1) = -log(1-1) = 무한대

 

예측이 성공한다면 cost 값이 0이 될 것이며 예측에 실패한다면 무한대가 되는 것을 기반으로 Cost function을 위와 같이 구성한다. ( 두 개의 Cost function 그래프를 이어 붙이면 실제로 이전의 밥그릇 모양의 함수 형태가 된다. )

 

 

 

위와 같은 Cost function을 사용할 때 y = 1 인 부분과 y = 0 인 부분을 if 조건문을 통해 나눠야 하는 불편함이 있어서 두 개의 식을 하나로 합쳐서 사용한다.

 

y = 1 일 때는 1-y = 0 이 되어서 c = -log(1+(x))가 되며 y = 0 일 때는 -log(1-H(x))가 되어서 앞서서 봤던 케이스가 나뉜 Cost function과 같아질 것이다.

 

그 후, Minimize 하기 위하여 Gradient 알고리즘을 사용하기 위해 Cost function을 미분을 해야 한다.

 

실제 코드를 짤 때는 위와 같이 TensorFlow에서 제공하는 함수를 사용하면 된다.

이제 실제 소스 코드에는 어떤식으로 Logistic classification을 구현하는지 확인해 보자.

강의 상의 코드가 올바르게 작동하지 않기에 다른 코드를 가져와서 돌려보았다..

import tensorflow as tf
import numpy as np

x_train = np.array([
    [1, 2],
    [2, 3],
    [3, 1],
    [4, 3],
    [5, 3],
    [6, 2]], dtype=np.float32)
y_train = np.array([
    [0],
    [0],
    [0],
    [1],
    [1],
    [1]], dtype=np.float32)

x_test = np.array([[5, 2]], dtype=np.float32)
y_test = np.array([[1]], dtype=np.float32)

# tf.data.Dataset 파이프라인을 이용하여 값을 입력
# from_tensor_slices 클래스 매서드를 사용하면 리스트, 넘파이, 텐서플로 자료형에서 데이터셋을 만들 수 있음
dataset = tf.data.Dataset.from_tensor_slices((x_train, y_train)).batch(len(x_train))
W = tf.Variable(tf.zeros([2, 1]), name='weight')
b = tf.Variable(tf.zeros([1]), name='bias')

# 원소의 자료구조 반환
# dataset.element_spec

# Logistic regression (sigmoid function 사용) features = X
def logistic_regression(features):
    hypothesis = tf.sigmoid(tf.matmul(features, W) + b)
    return hypothesis

# cost function 구현
def loss_fn(features, labels):
    hypothesis = logistic_regression(features)
    cost = -tf.reduce_mean(labels * tf.math.log(hypothesis) + (1 - labels) * tf.math.log(1 - hypothesis))
    return cost

# Gradient descent 알고리즘 구현을 위한 기울기 반환 (미분)
def grad(features, labels):
    with tf.GradientTape() as tape:
        loss_value = loss_fn(features, labels)
    return tape.gradient(loss_value, [W, b])

# 경사 하강법 사용
optimizer = tf.keras.optimizers.SGD(learning_rate=0.01)

# 횟수 3000번
EPOCHS = 3000

# X와 Y 데이터를 features 와 labels로 나누는 형식으로 구현
for step in range(EPOCHS + 1):
    for features, labels in iter(dataset):
        hypothesis = logistic_regression(features)
        grads = grad(features, labels)
        optimizer.apply_gradients(grads_and_vars=zip(grads, [W, b]))
        if step % 300 == 0:
            print("Iter: {}, Loss: {:.4f}".format(step, loss_fn(features, labels)))
            # print(hypothesis)

# 정확도 출력을 위한 함수
def accuracy_fn(hypothesis, labels):
    predicted = tf.cast(hypothesis > 0.5, dtype=tf.float32)
    accuracy = tf.reduce_mean(tf.cast(tf.equal(predicted, labels), dtype=tf.int32))
    return accuracy


test_acc = accuracy_fn(logistic_regression(x_test), y_test)
print('Accuracy: {}%'.format(test_acc * 100))

 

hypothesis 변수를 만들때 matmul을 이용하여 XW를 구현하고 TensorFlow에서 제공하는 sigmoid 함수를 이용하여 이전에 우리가 세웠던 가설에 맞게 구현해준다.

cost도 마찬가지로 두개의 경우를 하나로 합친 수식을 log 함수를 사용하여 구현한다.

또한, cast 함수를 통해 0.5를 초과하는 hypothesis의 output은 1, 이하는 0으로 반환시켜주며 그 반환한 값을 이용하여 accuracy 변수에 우리가 학습시킨 결과가 실제 Y와 같은지 equal 함수와 cast 함수를 통하여 전체의 평균을 내준다.

(전체 중에 결과가 맞는 데이터가 몇% 인지 확인하기 위하여)

 

스탭이 진행됨에 따른 cost의 변화를 출력하고 모든 학습이 종료되었을 때 Cast 하기 전의 Hypothesis 값과 Cast 하고 난 뒤인 Predicted 값(0, 1) 그리고 결과적으로 몇% 의 정답이 맞았는지 Accuracy 값을 출력하여준다.

 

결과

 

결과를 확인해 보면 cost 값이 학습이 진행됨에 따라 점점 줄어들어 0에 수렴해 간다는 것을 확인할 수 있으며 Hypothesis 값을 통해 테스트 데이터 셋을 통한 output을 확인할 수 있으며 그 output을 cast 함수를 통해 0과 1로 나누어 주어 결과적으로 test 데이터의 모든 결과가 맞아떨어져서 Accuracy 값이 1(100%)이 됨을 확인할 수 있다.

 

'Machine Learning & Deep Learning' 카테고리의 다른 글

9. Learning rate, Data preprocessing, overfitting  (0) 2020.04.23
8. Softmax classification  (0) 2020.04.22
6. Loading Data from File  (0) 2020.04.18
5. Multi-variable linear regression  (0) 2020.04.17
4. Hypothesis and Cost  (0) 2020.04.16

+ Recent posts