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

경사 하강법(Gradient Descent) 코드로 구현해보기 (TensorFlow)

by hyun9_9 2026. 3. 5.

지난 포스트에서 경사 하강법의 개념을 알아봤습니다.
이번엔 실제 코드로 직접 구현하며 개념을 확인해보겠습니다.


전체 코드

1. Cost 함수 직접 구현 vs TensorFlow 비교

import numpy as np
import tensorflow as tf

X = np.array([1, 2, 3])
Y = np.array([1, 2, 3])

# 직접 구현 : (가설 - 정답) 제곱 후 다 더해 평균 내기
def cost_func(W, X, Y):
    c = 0
    for i in range(len(X)):
        c += (W * X[i] - Y[i]) ** 2
    return c / len(X)

# TensorFlow로 구현
def costTF_func(W, X, Y):
    hypothesis = X * W
    return tf.reduce_mean(tf.square(hypothesis - Y))

# W를 -3 ~ 5 사이를 15단계로 쪼개기
W_values = np.linspace(-3, 5, num=15)

print("---cost_func---")
for feed_W in W_values:
    curr_cost = cost_func(feed_W, X, Y)
    print("{:6.3f} | {:10.5f}".format(feed_W, curr_cost))

print("---costTF_func---")
for feed_W in W_values:
    curr_cost = costTF_func(feed_W, X, Y)
    print("{:6.3f} | {:10.5f}".format(feed_W, curr_cost))

2. 경사 하강법으로 W 업데이트

import tensorflow as tf

tf.random.set_seed(0)  # 재현성을 위해 시드 고정

x_data = [1., 2., 3., 4.]
y_data = [1., 3., 5., 7.]

W = tf.Variable(tf.random.normal([1], -100, 100))

for step in range(300):
    hypothesis = W * x_data
    cost = tf.reduce_mean(tf.square(hypothesis - y_data))

    alpha = 0.01  # 학습률

    # 그레디언트 : cost 함수를 W로 편미분한 값
    gradient = tf.reduce_mean(tf.multiply(tf.multiply(W, x_data) - y_data, x_data))
    # tf.multiply(W, x_data) - y_data  →  (Wx - y)
    # tf.multiply(..., x_data)          →  (Wx - y) * x
    # tf.reduce_mean(...)               →  1/m * ∑(Wx - y) * x

    # W 업데이트
    descent = W - tf.multiply(alpha, gradient)  # W - α * gradient
    W.assign(descent)                           # 새로운 값을 W에 저장


    if step % 10 == 0:
        print("{:5} | {:10.4f} | {:10.6f}".format(step, cost.numpy(), W.numpy()[0]))

코드 1 : Cost 함수, 두 가지 방법으로 구현

직접 구현 vs TensorFlow

지난 포스트에서 배운 비용 함수 수식입니다.

cost(W) = 1/m * ∑(Wx - y)²

이걸 파이썬으로 직접 구현하면:

def cost_func(W, X, Y):
    c = 0
    for i in range(len(X)):
        c += (W * X[i] - Y[i]) ** 2  # (Wx - y)² 누적
    return c / len(X)                 # 전체 개수로 나눠 평균

TensorFlow로 구현하면:

def costTF_func(W, X, Y):
    hypothesis = X * W                          # Wx
    return tf.reduce_mean(tf.square(hypothesis - Y))  # 평균((Wx-y)²)
---cost_func---
-3.000 |   74.66667
-2.429 |   54.85714
-1.857 |   38.09524
-1.286 |   24.38095
-0.714 |   13.71429
-0.143 |    6.09524
 0.429 |    1.52381
 1.000 |    0.00000
 1.571 |    1.52381
 2.143 |    6.09524
 2.714 |   13.71429
 3.286 |   24.38095
 3.857 |   38.09524
 4.429 |   54.85714
 5.000 |   74.66667

---costTF_func---
-3.000 |   74.66667
-2.429 |   54.85714
-1.857 |   38.09524
-1.286 |   24.38095
-0.714 |   13.71429
-0.143 |    6.09524
 0.429 |    1.52381
 1.000 |    0.00000
 1.571 |    1.52381
 2.143 |    6.09524
 2.714 |   13.71429
 3.286 |   24.38095
 3.857 |   38.09524
 4.429 |   54.85714
 5.000 |   74.66667

두 함수는 완전히 동일한 계산을 합니다. TensorFlow를 쓰면 훨씬 간결하게 표현할 수 있다는 것을 알 수 있습니다.

W 값에 따른 Cost 변화 확인

W_values = np.linspace(-3, 5, num=15)

np.linspace(-3, 5, num=15) 는 -3부터 5까지를 15단계로 균등하게 나눈 값입니다.
W를 바꿔가며 cost를 출력해보면, W=1 근처에서 cost가 가장 작아지는 것을 직접 눈으로 확인할 수 있습니다.
지난 포스트에서 설명한 U자(바구니) 그래프가 데이터로 나타나는 것입니다.


코드 2 : 경사 하강법으로 W 자동으로 찾기

랜덤한 초기값에서 시작

tf.random.set_seed(0)  # 코드를 다시 수행해도 동일하게 재현될 수 있게 시드 고정
W = tf.Variable(tf.random.normal([1], -100, 100))

W를 -100 ~ 100 사이의 랜덤한 값으로 초기화합니다.
어디서 시작하든 경사 하강법이 최적의 W를 찾아가는지 확인하는 것이 목표입니다.

그레디언트(기울기) 계산 및 W 업데이트

지난 포스트에서 배운 경사 하강법 수식입니다.

W := W - α * 1/m * ∑(Wx - y) * x

코드로 표현하면:

alpha = 0.01  # 학습률

# 그레디언트 : cost 함수를 W로 편미분한 값
gradient = tf.reduce_mean(tf.multiply(tf.multiply(W, x_data) - y_data, x_data))
# tf.multiply(W, x_data) - y_data  →  (Wx - y)
# tf.multiply(..., x_data)          →  (Wx - y) * x
# tf.reduce_mean(...)               →  1/m * ∑(Wx - y) * x

# W 업데이트
descent = W - tf.multiply(alpha, gradient)  # W - α * gradient
W.assign(descent)                           # 새로운 값을 W에 저장
  • tf.multiply : 곱셈 연산
  • W.assign : W 값을 새로운 값으로 업데이트

학습 결과 확인

300번 반복하면서 10번마다 결과를 출력합니다.

step |       cost |          W
    0 | 18332.2188 |  47.398293
   10 |  3855.3564 |  22.638384
   20 |   810.9046 |  11.283927
   30 |   170.6631 |   6.076973
   40 |    36.0217 |   3.689155
   50 |     7.7069 |   2.594144
   60 |     1.7524 |   2.091991
   70 |     0.5001 |   1.861713
   80 |     0.2368 |   1.756112
   90 |     0.1814 |   1.707684
  100 |     0.1698 |   1.685477
  110 |     0.1673 |   1.675292
  120 |     0.1668 |   1.670622
  130 |     0.1667 |   1.668481
  140 |     0.1667 |   1.667498
  150 |     0.1667 |   1.667048
  160 |     0.1667 |   1.666842
  170 |     0.1667 |   1.666747
  180 |     0.1667 |   1.666703
  190 |     0.1667 |   1.666684
  200 |     0.1667 |   1.666674
  210 |     0.1667 |   1.666670
  220 |     0.1667 |   1.666668
  230 |     0.1667 |   1.666667
  240 |     0.1667 |   1.666667
  250 |     0.1667 |   1.666667
  260 |     0.1667 |   1.666667
  270 |     0.1667 |   1.666667
  280 |     0.1667 |   1.666667
  290 |     0.1667 |   1.666667   ← W가 1.6에 수렴!

처음엔 W가 엉뚱한 값이지만, 학습을 거듭할수록 W가 점점 2에 가까워지고 cost는 0에 가까워집니다.

x_data = [1, 2, 3, 4], y_data = [1, 3, 5, 7] 에서 1.666~ 으로 설정한 상태에서 hypothesis 값은 1.6666675 3.333335 5.0000024 6.66667 으로 나오니까 얼추 y_data 와 비슷하다고 할 수 있다.


정리

개념 수식 코드

비용 함수 1/m * ∑(Wx - y)² tf.reduce_mean(tf.square(hypothesis - Y))
그레디언트 1/m * ∑(Wx - y) * x tf.reduce_mean(tf.multiply(tf.multiply(W, x_data) - y_data, x_data))
W 업데이트 W := W - α * gradient W.assign(W - tf.multiply(alpha, gradient))

어떤 랜덤한 초기값에서 시작하더라도 경사 하강법을 반복하면 결국 최적의 W를 찾아낸다는 것을 직접 확인했습니다. 🚀