선형 회귀

기본적인 선형 회귀의 식은 다음과 같다.

\[\hat{y} = \theta_0 + \theta_1 x_1 + \theta_2 x_2 + ...+ \theta_n x_n\]
  • $\hat{y}$ 는 예측값이다.
  • n은 특성의 수이다.
  • $x_i$ 는 i번째 특성값이다.
  • $\theta_j$ 는 j번째 모델 파라미터이다.

벡터 형태로 간단하게 쓸 수도 있다.

\[\hat{y} = h_{\theta}(x) = \mathbf{\theta}^T \mathbf{x}\]

최소화할 비용함수 식은 다음과 같다.

\[MSE(\mathbf{X}, h_{\theta}) = \frac{1}{m} \displaystyle \sum_{i=1}^{m}{(\mathbf{\theta}^T \mathbf{x}^{(i)} - y^{(i)})^2}\]

앞선 chapter들에서는 사이킷런의 LinearRegression()을 사용해서 선형회귀를 수행했다. 하지만 사이킷런의 LinearRegression()이 기반을 두고 있는 scipy.linalg.lstsq() 함수를 직접 호출할 수도 있다.

theta_best_svd, residuals, rank, s = np.linalg.lstsq(X_b, y, rcond=1e-6)
theta_best_svd      # theta 값 출력

scipy.linalg.lstsq() 함수는 유사역행렬을 이용해서 계산을 수행한다. 특잇값 분해(Singular Value Decomposition)를 이용해서 계산되는 유사역행렬을 곧이어 살펴볼 정규방정식보다 더 효율적이고 극단적인 경우도 처리할 수 있다.


정규방정식

비용함수를 최소화하는 $\theta$ 를 찾기 위한 해석적인 방법으로 정규방정식이 있다. 식은 다음과 같다.

\[\mathbf{\hat{\theta} = (X^T X)^{-1} X^T y}\]

정규방정식 더 알아보기

코드 구현은 다음과 같이 한다.

from sklearn.preprocessing import add_dummy_feature

theta_best = np.linalg.inv(X_b.T @ X_b) @ X_b.T @ y

정규방정식의 계산 복잡도는 $O(n^{2.4})$ 에서 $O(n^{3})$ 사이이다. 선형 회귀의 계산 복잡도는 약 $O(n^{2})$ 이다.


경사 하강법 기본

반복해서 많이 살펴본 내용으로, 링크를 통해 대체한다.

Deep Learning Specialization

Python for ML

새롭게 알고 넘어갈 내용만 살펴보자.

  1. 선형 회귀를 위한 MSE 비용함수는 볼록 함수(convex function)이므로, 지역 최솟값이 없고 하나의 전역 최솟값만 있다.
  2. 특성의 스케일이 다를 경우 학습이 느려질 수 있다.
  3. 모델 파라미터가 많아질수록 공간의 차원이 커지고 검색이 어려워진다.


배치 경사 하강법

매 경사 하강법 스텝에서 전체 데이터를 모두 활용해 계산하는 것을 배치 경사 하강법(Batch Gradient Descent)이라고 한다. 물론 아래와 같은 gradient vector를 이용해서 한 번에 계산할 수도 있지만, 매우 큰 training set에서는 아주 느리다.

\[\nabla{MSE(\theta)} = \begin{pmatrix} \frac{\partial}{\partial \theta_0}\nabla{MSE(\theta)} \\ \frac{\partial}{\partial \theta_1}\nabla{MSE(\theta)} \\ \vdots \\ \frac{\partial}{\partial \theta_n}\nabla{MSE(\theta)} \end{pmatrix} = \frac{2}{m} \mathbf{X^T(X \theta - y)}\]

코딩으로 구현한 과정은 다음과 같다.

eta = 0.1       # learning rate
n_epochs = 1000
m = len(X_b)        # 샘플의 개수

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

for epoch in range(n_epochs):
    gradients = 2 / m * X_b.T @ (X_b @ theta - y)
    theta = theta - eta * gradients


확률적 경사 하강법

확률적 경사 하강법(Stochastic Gradient Descent, SGD)은 한 번에 랜덤으로 뽑은 하나의 샘플에 대해서만 경사를 계산하는 방식이다. 배치 경사 하강법보다는 당연히 불안정하고 전역 최적에 수렴할 가능성도 떨어지지만, 확실히 빠르다. 전역 최적이 수렴하지 못하는 문제는 매 반복에서 학습률을 조정하는 학습 스케줄을 이용해서 해결할 수 있다.

n_epochs = 50
t0, t1 = 5, 50      # 학습 스케줄 파라미터

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

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

for epoch in range(n_epochs):
    for iteration in range(m):
        random_index = np.random.randint(m)     # 샘플 하나를 랜덤하게 뽑기 위한 index 선정
        xi = X_b[random_index:random_index + 1]
        yi = y[random_index:random_index + 1]
        gradients = 2 * xi.T @ (xi @ theta - yi)        # 하나의 샘플이므로 m으로 나눌 필요 없다.
        eta = learning_schedule(epoch * m + iteration)  # 학습 스케줄링
        theta = theta - eta * gradients

사이킷런을 사용하면 다음과 같다.

from sklearn.linear_model import SGDRegressor

sgd_reg = SGDRegressor(max_iter=1000, tol=1e-5, penalty=None, eta0=0.1, n_iter_no_change=100, random_state=42)
sgd_reg.fit(X, y.ravel())

여기서 tol은 손실에 대한 한계점으로, n_iter_no_change 동안 이 값보다 손실이 작아질 때까지 실행된다. 바꿔 말해, n_iter_no_change 동안 손실이 tol 아래라면 실행이 끝난다. eta0은 학습률이다.


미니배치 경사 하강법

확률적 경사 하강법에서 샘플을 한 개만 사용하는 것이 께름칙했다면 미니배치 경사 하강법(Mini-batch Gradient Descent)을 사용하면 된다. 미니배치라 불리는 임의의 작은 샘플 세트에 대해서 gradient를 계산하고 파라미터를 업데이트 한다. 당연히 SGD보다 더 많은 샘플로 실시하기 때문에 좀 더 안정적이다.

n_epochs = 1000
t0, t1 = 5, 50      # 학습 스케줄 파라미터

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

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

for epoch in range(n_epochs):
    for iteration in range(m):
        random_index = np.random.randint(m)
        xi = X_b[random_index:random_index + 10]        # 배치 사이즈를 10으로 정함.
        yi = y[random_index:random_index + 10]
        gradients = 2 / m * xi.T @ (xi @ theta - yi)
        eta = learning_schedule(epoch * m + iteration)
        theta = theta - eta * gradients


다항 회귀

데이터가 단순한 직선이 아니라 복잡한 형태라도 선형 모델을 사용해서 해결할 수 있다. 가장 간단한 것은 각 특성의 거듭제곱을 새로운 특성으로 추가하고, 이렇게 확장된 특성을 포함하는 데이터셋으로 선형 모델을 훈련하는 것이다. 이를 다항 회귀(Polynomial Regression)라고 한다.

새로운 특성으로 추가할 때는 사이킷런의 PolynomialFeatures를 이용한다. 이 떄 degree 인자를 설정해야 하는데, degree=d는 특성이 n개인 배열을 특성이 $\frac{(n + d)!}{d!n!}$ 개인 배열로 변환한다.

from sklearn.preprocessing import PolynomialFeatures
poly_features = PolynomialFeatures(degree=2, include_bias=False)
X_poly = poly_features.fit_transform(X)

코드 보러가기



별도의 출처 표시가 있는 이미지를 제외한 모든 이미지는 강의자료에서 발췌하였음을 밝힙니다.

댓글남기기