6 분 소요

Multi-variable linear regression

  • Hypothesis
  • Cost function: 가설과 실제값의 제곱의 평균. Cost를 최소화하는 W(기울기)를 찾아가는 것이 포인트
  • Gradient descent: Cost를 최소화하는 W를 찾을 수 있는 알고리즘 중 하나

변수가 여러개가 된다면 이에 따른 가중치도 여러개가 된다. 예를 들어서 변수가 3개라면 가중치(W)도 3개가 된다.

변수가 여러개가 된다면 Matrix 곱셈(행렬곱)을 활용하게 된다.

download1

일반적으로 matrix는 대문자로 표현을 한다. 매트릭스 연산 시 X가 먼저, W가 뒤에 오기 때문에 기술을 할때도 XW라고 한다. X를 사용하게 된다면 컬럼의 개수가 몇 개든, 데이터의 개수가 몇 개든 상관이 없이 무조건 X로 사용하기 때문에 간단하게 식 표현이 가능하다.

Multi variable linear regression LAB

Multi variable linear regression with no matrix

# Multi variable linear regression with no matrix
import tensorflow as tf

# data and label
x1 = [73., 93., 89., 96., 73]
x2 = [80., 88.,91., 98., 66.]
x3 = [75., 93., 90., 100., 70.]
Y = [152., 185., 180., 196., 142.]

# weights
w1 = tf.Variable(tf.random.normal([1]))
w2 = tf.Variable(tf.random.normal([1]))
w3 = tf.Variable(tf.random.normal([1]))
b = tf.Variable(tf.random.normal([1]))

learning_rate = 0.000001

hypothesis = w1 * x1 + w2 * x2 + w3 * x3 + b

for i in range(1000+1):
    # tf.GradientTape() to record the gradient of the cost function
    with tf.GradientTape() as tape:
        hypothesis = w1 * x1 + w2 * x2 + w3 * x3 + b
        cost = tf.reduce_mean(tf.square(hypothesis - Y))
    # calculates the gradients of the cost
    w1_grad, w2_grad, w3_grad, b_grad = tape.gradient(cost, [w1, w2, w3, b])

    # update w1, w2, w3 and b
    w1.assign_sub(learning_rate * w1_grad)
    w2.assign_sub(learning_rate * w2_grad)
    w3.assign_sub(learning_rate * w3_grad)
    b.assign_sub(learning_rate * b_grad)

    if i % 50 == 0:
        print("{:5} | {:12.4f}".format(i, cost.numpy()))

>>>
    0 |   21359.2578
   50 |     245.4268
  100 |      11.1252
  150 |       8.5033
  200 |       8.4521
  250 |       8.4294
  300 |       8.4072
  350 |       8.3851
  400 |       8.3629
  450 |       8.3410
  500 |       8.3189
  550 |       8.2970
  600 |       8.2751
  650 |       8.2533
  700 |       8.2315
  750 |       8.2098
  800 |       8.1882
  850 |       8.1667
  900 |       8.1451
  950 |       8.1236
 1000 |       8.1022

칼럼의 개수만큼 가중치를 따로따로 정의해주어야 됨을 알 수 있다.

Multi variable linear regression with matrix

# Multi variable linear regression with matrix
import numpy as np

data = np.array([
    # x1, x2, x3, y
    [73., 80., 75., 152.],
    [93., 88., 93., 185.],
    [89., 91., 90., 180.],
    [96., 98., 100., 196.],
    [73., 66., 70., 142.],
], dtype=np.float32)

# slice data
X = data[:, :-1]
y = data[:, [-1]]

# 가중치의 경우 칼럼이 3개(x1, x2, x3)니까 행은 3개가 필요하고, 결과값은 1개니까 열 개수는 1개가 필요하다.
W = tf.Variable(tf.random.normal([3, 1]))
b = tf.Variable(tf.random.normal([1]))

learning_data = 0.000001

def predict(X):
    return tf.matmul(X, W) + b

n_epochs = 2000
for i in range(n_epochs + 1):
    # record the gradient of the cost function
    with tf.GradientTape() as tape:
        cost = tf.reduce_mean((tf.square(predict(X) - y)))

    # calculates the gradients of the loss
    W_grad, b_grad = tape.gradient(cost, [W, b])

    # updates parameters (W and b)
    W.assign_sub(learning_rate * W_grad)
    b.assign_sub(learning_rate * b_grad)

    if i % 100 == 0:
        print("{:5} | {:10.4f}".format(i, cost.numpy()))

>>>
    0 | 33753.4961
  100 |    14.9227
  200 |    10.7120
  300 |    10.6551
  400 |    10.5989
  500 |    10.5431
  600 |    10.4877
  700 |    10.4324
  800 |    10.3776
  900 |    10.3230
 1000 |    10.2687
 1100 |    10.2147
 1200 |    10.1609
 1300 |    10.1075
 1400 |    10.0543
 1500 |    10.0015
 1600 |     9.9489
 1700 |     9.8966
 1800 |     9.8447
 1900 |     9.7929
 2000 |     9.7415

행렬을 사용하게 된다면 가설 함수의 결과값을 구할 때, 훨씬 간편하게 코드를 작성할 수 있음을 확인할 수 있다.

Logistic (Regression) Classification

출처: https://copycode.tistory.com/161?category=740659 + 스터디 강의

Classification

  • Exam: Pass or Fail
  • Spam: Not Spam or Spam
  • Face: Real or Fake
  • Tummor: Not Malignant or Malignant

이게 다 Binary Classification이다. 어떤 피처의 특징을 토대로 T / F로 나눌 수 있고 이를 통해서 Logistic Regression

Logistic vs Linear

  • Logistic: 두가지의 케이스로 구분할 수 있다. Show size / The number of workers
  • Linear: 연속적인 데이터를 통해서 그래프에 인근하는 데이터를 예측할 수 있다. Time / Weight / Height 처럼 연속적인 값들

Hypothesis Representiation

앞에서 배웠던 Linear Regression을 가지고 0.5보다 크면 1로 작으면 0으로 하면 될 것 같다는 생각이 든다. 하지만 이 알고리즘을 하용하게 된다면 특정 데이터가 크게 나온다면 원래는 1을 받을 수 있지만 그 데이터가 0을 받을 수 있다. 정확한 데이터를 표현하지 못하게 될 수 있다. 그렇기 때문에 Sigmod 함수를 사용해서 해당 데이터를 통해서 나온 Linear Regression을 0에서 1사이의 값으로 바꾼다.

Linear Regression을 통해서 나온 실수값을 Logistic Function(Sigmoid)을 통해서 0에서 1인 구간을 만들고 Decision Boundary(0과 1을 구별하는 함수)를 통해서 0.5이상이면 1로 값을 변경시키는 방식으로 표현한다. 이게 Logistic Hypothesis가 된다.

Cost Function

Cost function은 예측을 하는 값과 실제 값의 차이를 나타내는 함수이다. Linear Regression의 Cost Function의 경우는 실제 값들과의 차이에 대한 기울기를 이용해서 만들어지게 된다.

하지만 이 Cost Function 공식을 사용하게 된다면 Logistic Regression의 경우는 울퉁불퉁한 값이 나와서 Gradient descent algorithm을 통해서 최솟값을 구할 수 없게 된다.

download1

local cost의 최소값과 Global cost의 최솟값은 다를 수 있기 때문이다.

따라서, Logistic Regression의 cost function은 다음과 같이 정의할 수 있다.

download2

왜냐면 가설 함수에서 시그모이드를 통해서 0~1로 바꾸어 놓았기 때문에 log를 사용하게 된다면 확실히 0인지 1인지를 확인할 수 있기 때문이다. 예를 들어서 1로 수렴할수록 log를 가지고 있는 함수에 대한 y값은 0에 수렴하게 된다. 즉, 예측값이 1에 가까워질수록 cost function의 값은 작아지게 된다. 반대로 y에 가까워진다고 해도, -log에 해당한다면 그 cost 역시 작아지게 된다.

download1

따라서 Logistic Regression의 Cost Function은 다음과 같은 식을 가집니다.

download2

How to minimize the cost function

그럼 위에 있는 Cost Function에서 비용의 최소화되는 값을 어떻게 알 수 있을까?

download1

Cost Function을 구한 이유는 Gradient descent algorithm을 사용하기 위해서이다.

1️⃣ 시그모이드 함수를 가설 함수로 선언

download1

시그모이드 함수가 적용된 식을 볼 수 있다. 이를 가설 함수로 정의한 것이다.

# Sigmoid 함수를 가설로 선언합니다
def logistic_regression(features):
    hypothesis  = tf.divide(1., 1. + tf.exp(tf.matmul(features, W) + b))
    return hypothesis


2️⃣ 비용 함수

download2

0이나 1로 갈수록 비용 함숫값이 작아지는 함수를 그대로 코드로 구현한 것을 확인할 수 있다.

# 비용 함수를 정의합니다.
def loss_fn(hypothesis, features, labels):
    cost = -tf.reduce_mean(labels * tf.math.log(logistic_regression(features)) + (1 - labels) * tf.math.log(1 - hypothesis))
    return cost

3️⃣ Gradient Decent

# GradientTape를 통해 경사값을 계산합니다.
def grad(features, labels):
    with tf.GradientTape() as tape:
        loss_value = loss_fn(logistic_regression(features), features,  labels)
    return tape.gradient(loss_value, [W,b])

4️⃣ 경사 하강 실행 코드

EPOCHS = 1001

for step in range(EPOCHS):
    for features, labels  in iter(dataset):
        grads = grad(features, labels)
        optimizer.apply_gradients(grads_and_vars=zip(grads,[W,b]))
        if step % 100 == 0:
            print("Iter: {}, Loss: {:.4f}".format(step, loss_fn(logistic_regression(features),features,labels)))

❓이 부분이 뭘 하는 거지?

# GradientDescentOptimizer에서 learning_rate를 설정해서 optimizer를 선언한다.
optimizer = tf.keras.optimizers.SGD(learning_rate=0.01)

# 실제 optimizer.apply_gradients를 통해서 실제모델인 [W,b]가 update되서 모델이 만들어지게된다.
optimizer.apply_gradients(grads_and_vars=zip(grads,[W,b]))

5️⃣ Predict

# 추론한 값은 0.5를 기준(Sigmoid 그래프 참조)로 0과 1의 값을 리턴합니다.
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("Testset Accuracy: {:.4f}".format(test_acc))

>>>
dataset
<BatchDataset element_spec=(TensorSpec(shape=(None, 2), dtype=tf.float32, name=None), TensorSpec(shape=(None, 1), dtype=tf.float32, name=None))>
Iter: 0, Loss: 0.6874
Iter: 100, Loss: 0.5776
Iter: 200, Loss: 0.5349
Iter: 300, Loss: 0.5054
Iter: 400, Loss: 0.4838
Iter: 500, Loss: 0.4671
Iter: 600, Loss: 0.4535
Iter: 700, Loss: 0.4420
Iter: 800, Loss: 0.4319
Iter: 900, Loss: 0.4228
Iter: 1000, Loss: 0.4144
Testset Accuracy: 1.0000

전체 코드

import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
import tensorflow as tf

tf.random.set_seed(777)  # for reproducibility
print(tf.__version__)

# Data
x_train = [[1., 2.],
          [2., 3.],
          [3., 1.],
          [4., 3.],
          [5., 3.],
          [6., 2.]]
y_train = [[0.],
          [0.],
          [0.],
          [1.],
          [1.],
          [1.]]

x_test = [[5.,2.]]
y_test = [[1.]]


# Tensorflow data API를 통해 학습시킬 값들을 담는다 (Batch Size는 한번에 학습시킬 Size로 정한다)
# features,labels는 실재 학습에 쓰일 Data (연산을 위해 Type를 맞춰준다)
dataset = tf.data.Dataset.from_tensor_slices((x_train, y_train)).batch(len(x_train))#.repeat()
W = tf.Variable(tf.zeros([2,1]), name='weight')
b = tf.Variable(tf.zeros([1]), name='bias')

# Sigmoid 함수를 가설로 선언합니다
def logistic_regression(features):
    hypothesis  = tf.divide(1., 1. + tf.exp(tf.matmul(features, W) + b))
    return hypothesis

# 비용 함수를 정의합니다.
def loss_fn(hypothesis, features, labels):
    cost = -tf.reduce_mean(labels * tf.math.log(logistic_regression(features)) + (1 - labels) * tf.math.log(1 - hypothesis))
    return cost

# GradientDescentOptimizer에서 learning_rate를 설정해서 optimizer를 선언한다.
optimizer = tf.keras.optimizers.SGD(learning_rate=0.01)

# GradientTape를 통해 경사값을 계산합니다.
def grad(features, labels):
    with tf.GradientTape() as tape:
        loss_value = loss_fn(logistic_regression(features),features, labels)
    return tape.gradient(loss_value, [W,b])

EPOCHS = 1001

for step in range(EPOCHS):
    for features, labels  in iter(dataset):
        grads = grad(features, labels)
        # 실제 optimizer.apply_gradients를 통해서 실제모델인 [W,b]가 update되서 모델이 만들어지게된다.
        optimizer.apply_gradients(grads_and_vars=zip(grads,[W,b]))

        if step % 100 == 0:
            print("Iter: {}, Loss: {:.4f}".format(step, loss_fn(logistic_regression(features),features,labels)))

# 추론한 값은 0.5를 기준(Sigmoid 그래프 참조)로 0과 1의 값을 리턴합니다.
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("Testset Accuracy: {:.4f}".format(test_acc))

댓글남기기