Adventure Time - Finn 3
본문 바로가기
AI/ML

Logistic Regression 코드로 구현해보기 (TensorFlow)

by hyun9_9 2026. 3. 9.

지난 포스트에서 Logistic Regression의 개념을 알아봤습니다.
이번엔 그 개념들이 실제 코드에서 어떻게 표현되는지 하나씩 확인해보겠습니다.


전체 코드

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)

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')

def logistic_regression(features):
    hypothesis = tf.sigmoid(tf.matmul(features, W) + b)
    return hypothesis

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

def grad(hypothesis, features, labels):
    with tf.GradientTape() as tape:
        loss_value = loss_fn(features, labels)
    return tape.gradient(loss_value, [W, b])

learning_rate = 0.01
EPOCHS = 3000

for step in range(EPOCHS + 1):
    for features, labels in iter(dataset):
        hypothesis = logistic_regression(features)
        W_grad, b_grad = grad(hypothesis, features, labels)
        W.assign_sub(learning_rate * W_grad)
        b.assign_sub(learning_rate * b_grad)
        if step % 300 == 0:
            print("Iter: {}, Loss: {:.4f}".format(step, loss_fn(features, labels)))

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))

1. 데이터 준비 : Classification

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

y_train = np.array([[0],[0],[0],[1],[1],[1]], dtype=np.float32)

지난 포스트에서 배운 Classification 그대로입니다.
결과가 반드시 0 또는 1 로 나뉘어야 합니다.

문제 0 1

데이터 x값이 작은 경우 x값이 큰 경우
시험 불합격 합격
스팸 스팸 아님 스팸

 

dataset = tf.data.Dataset.from_tensor_slices((x_train, y_train)).batch(len(x_train))

from_tensor_slices : 리스트, NumPy, TensorFlow 자료형에서 데이터셋을 만들어줍니다.
.batch(len(x_train)) : 전체 데이터를 한 배치로 묶습니다.


2. W, b 초기화

W = tf.Variable(tf.zeros([2, 1]), name='weight')
b = tf.Variable(tf.zeros([1]), name='bias')

입력 변수가 2개(x1, x2), 출력이 1개이므로 W의 shape는 [2, 1] 입니다.

[n, 2]  *  [2, 1]  =  [n, 1]
  X     *    W    =   결과

3. 가설 함수 : Sigmoid로 감싸기

지난 포스트에서 배운 핵심 흐름입니다.

H(X) = XW         → Linear
H(X) = g(XW + b)  → Logistic (Sigmoid로 감싼다)
def logistic_regression(features):
    hypothesis = tf.sigmoid(tf.matmul(features, W) + b)
    return hypothesis
  • tf.matmul(features, W) + b → Linear 연산 (XW + b)
  • tf.sigmoid(...) → 시그모이드 함수 적용

Sigmoid 함수 : 어떤 값이 들어와도 항상 0~1 사이로 변환해줍니다.

g(z) = 1 / (1 + e^-z)

z가 + (양수) → e^-z 가 0으로 수렴 → 1 / (1+0) = 1
z가 - (음수) → e^-z 가 ∞ 로 증가 → 1 / (1+∞) = 0

전체 흐름:

x → tf.matmul(X, W) + b  →  Linear
  → tf.sigmoid(...)       →  0~1 사이 값
  → Decision Boundary     →  Y ∈ {0, 1}

4. Cost 함수 : log 함수 사용

지난 포스트에서 배운 이유 그대로입니다.

Linear Regression처럼 단순히 제곱을 쓰면 시그모이드 때문에 구불구불한 그래프가 나와서 경사 하강법이 Local Minimum에 빠질 위험이 있습니다.
그래서 log 함수를 사용해 Convex(볼록)한 구조로 만들어줍니다.

cost(W, y) = -log(H(x))      if y = 1
           = -log(1 - H(x))  if y = 0

→ 합치면:
cost(W, y) = -y * log(H(x)) - (1-y) * log(1 - H(x))

코드로 표현하면:

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
  • labels * tf.math.log(hypothesis) → -y * log(H(x))
  • (1 - labels) * tf.math.log(1 - hypothesis) → -(1-y) * log(1-H(x))
  • tf.reduce_mean → 전체 평균

5. 경사 하강법으로 W, b 업데이트

def grad(hypothesis, features, labels):
    with tf.GradientTape() as tape:
        loss_value = loss_fn(features, labels)
    return tape.gradient(loss_value, [W, b])

W_grad, b_grad = grad(hypothesis, features, labels)
W.assign_sub(learning_rate * W_grad)
b.assign_sub(learning_rate * b_grad)

GradientTape 로 cost를 W와 b에 대해 미분해 gradient를 구하고,
학습률 0.01 씩 기울기 방향 반대로 이동하면서 cost가 최소인 W, b를 찾아갑니다.

W := W - α * gradient

아래 코드와 완전히 동일한 동작을 합니다.

optimizer = tf.keras.optimizers.SGD(learning_rate=0.01)
optimizer.apply_gradients(grads_and_vars=zip(grads, [W, b]))

SGD(Stochastic Gradient Descent) 는 확률적 경사 하강법으로,
assign_sub 과정을 자동으로 처리해주는 것입니다.


6. 정확도 계산 : Decision Boundary 적용

지난 포스트에서 배운 Decision Boundary 0.5 가 여기서 사용됩니다.

출력값 >= 0.5  →  1
출력값 <  0.5  →  0
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

① predicted : 0.5 기준으로 0 또는 1로 변환

predicted = tf.cast(hypothesis > 0.5, dtype=tf.float32)
hypothesis   = [0.2,  0.8,  0.9,  0.3]
> 0.5        → [False, True, True, False]
tf.cast      → [0.0,  1.0,  1.0,  0.0]

② accuracy : 예측값과 실제값 비교

accuracy = tf.reduce_mean(tf.cast(tf.equal(predicted, labels), dtype=tf.int32))
predicted = [0.0, 1.0, 1.0, 0.0]
labels    = [0.0, 1.0, 0.0, 0.0]
tf.equal  → [True, True, False, True]
tf.cast   → [1, 1, 0, 1]
평균       → 3/4 = 0.75 → 정확도 75%

학습 결과

Iter: 0,    Loss: 0.6874
Iter: 300,  Loss: 0.5054
Iter: 600,  Loss: 0.4535
...
Iter: 3000, Loss: 0.3013
Accuracy: 100%

학습이 진행될수록 Loss가 줄어들고 최종 정확도 100%가 나옵니다.


전체 흐름 정리

x_train 입력
    ↓
tf.matmul(X, W) + b         → Linear (XW + b)
    ↓
tf.sigmoid(...)              → Sigmoid : 0~1 사이 값으로 변환
    ↓
log 기반 cost 함수           → Convex 구조 유지 (Local Minimum 방지)
    ↓
GradientTape → gradient 계산
    ↓
W.assign_sub(lr * W_grad)   → 경사 하강법으로 W, b 업데이트
    ↓
반복 (3000 epochs)
    ↓
hypothesis > 0.5             → Decision Boundary 적용
    ↓
Y ∈ {0, 1}                  → 최종 분류 결과

정리

개념 수식 코드

Classification Y ∈ {0, 1} y_train = [0, 0, 0, 1, 1, 1]
Sigmoid 1 / (1 + e^-z) tf.sigmoid(tf.matmul(X, W) + b)
Cost -y·log(H(x)) - (1-y)·log(1-H(x)) labels * tf.math.log(hypothesis) + ...
Decision Boundary H(x) > 0.5 → 1 tf.cast(hypothesis > 0.5, ...)
경사 하강법 W := W - α * gradient W.assign_sub(learning_rate * W_grad)