선형 회귀를 위한 미니배치 경사 하강법 구현

경사 하강법(Gradient Descent)은 선형 회귀 모델의 파라미터를 최적화하는 대표적인 방법이다. 전체 데이터를 사용하는 배치 경사 하강법은 계산 비용이 크고, 무작위로 하나의 샘플만 사용하는 확률적 경사 하강법(SGD)은 수렴이 불안정할 수 있다. 이 글에서는 그 중간인 미니배치 경사 하강법을 단계별로 살펴보고, 성능을 개선하는 두 가지 방법을 소개한다.

1. 확률적 경사 하강법 (SGD)

매 반복마다 하나의 샘플을 무작위로 선택하여 기울기를 계산한다. 아래 코드는 100개의 데이터 포인트를 생성하고 SGD로 theta를 추정한다.

import numpy as np

np.random.seed(42)
X = 2 * np.random.rand(100, 1)
y = 4 + 3 * X + np.random.randn(100, 1)

X_b = np.c_[np.ones((100, 1)), X]

epochs = 10000
lr = 0.001
m = 100

theta = np.random.randn(2, 1)

for epoch in range(epochs):
    idx = np.random.randint(m)
    x_i = X_b[idx:idx+1]
    y_i = y[idx:idx+1]
    grad = x_i.T.dot(x_i.dot(theta) - y_i)
    theta -= lr * grad

print(theta)

2. 미니배치 경사 하강법

매 반복마다 일정 개수의 샘플(미니배치)을 사용한다. 배치 크기를 10으로 설정한 예제다.

import numpy as np

np.random.seed(42)
X = 2 * np.random.rand(100, 1)
y = 4 + 3 * X + np.random.randn(100, 1)
X_b = np.c_[np.ones((100, 1)), X]

epochs = 10000
lr = 0.001
batch_size = 10
m = 100
num_batches = m // batch_size

theta = np.random.randn(2, 1)

for epoch in range(epochs):
    for i in range(num_batches):
        idx = np.random.randint(m)
        X_batch = X_b[idx:idx+batch_size]
        y_batch = y[idx:idx+batch_size]
        grad = X_batch.T.dot(X_batch.dot(theta) - y_batch)
        theta -= lr * grad

print(theta)

3. 성능 개선 1: 에포크마다 데이터 셔플

에포크가 시작될 때마다 전체 데이터를 무작위로 섞어준다. 이렇게 하면 미니배치가 다양한 데이터 분포를 접하게 되어 수렴이 더 안정적이다.

import numpy as np

np.random.seed(42)
X = 2 * np.random.rand(100, 1)
y = 4 + 3 * X + np.random.randn(100, 1)
X_b = np.c_[np.ones((100, 1)), X]

epochs = 10000
lr = 0.001
batch_size = 10
m = 100
num_batches = m // batch_size

theta = np.random.randn(2, 1)

for epoch in range(epochs):
    # 데이터 셔플
    shuffle_idx = np.arange(m)
    np.random.shuffle(shuffle_idx)
    X_b_shuffled = X_b[shuffle_idx]
    y_shuffled = y[shuffle_idx]

    for i in range(num_batches):
        X_batch = X_b_shuffled[i*batch_size : (i+1)*batch_size]
        y_batch = y_shuffled[i*batch_size : (i+1)*batch_size]
        grad = X_batch.T.dot(X_batch.dot(theta) - y_batch)
        theta -= lr * grad

print(theta)

4. 성능 개선 2: 학습률 감소 (Learning Rate Decay)

반복 횟수가 증가함에 따라 학습률을 점차 줄이면 초기에는 빠르게 수렴하고 후반에는 세밀하게 조정할 수 있다. 아래는 t0 / (t + t1) 형태의 감소 함수를 사용한 예제다.

import numpy as np

np.random.seed(42)
X = 2 * np.random.rand(100, 1)
y = 4 + 3 * X + np.random.randn(100, 1)
X_b = np.c_[np.ones((100, 1)), X]

epochs = 10000
batch_size = 10
m = 100
num_batches = m // batch_size

t0, t1 = 5, 500

def learning_rate_schedule(t):
    return t0 / (t + t1)

theta = np.random.randn(2, 1)

for epoch in range(epochs):
    shuffle_idx = np.arange(m)
    np.random.shuffle(shuffle_idx)
    X_b_shuffled = X_b[shuffle_idx]
    y_shuffled = y[shuffle_idx]

    for i in range(num_batches):
        X_batch = X_b_shuffled[i*batch_size : (i+1)*batch_size]
        y_batch = y_shuffled[i*batch_size : (i+1)*batch_size]
        grad = X_batch.T.dot(X_batch.dot(theta) - y_batch)
        # 현재 반복 횟수에 따른 학습률 계산
        iter_count = epoch * m + i
        lr = learning_rate_schedule(iter_count)
        theta -= lr * grad

print(theta)

태그: Linear Regression Gradient Descent mini-batch python NumPy

9월 8일 11:57에 게시됨