ML_다중 선형 회귀

wnsdnl·2025년 3월 2일

ML 머신러닝

목록 보기
2/11

📌 다중 선형 회귀

다중 선형회귀에서는 하나의 입력 변수가 아니라 여러 개의 입력 변수를 사용해서 목표 변수를 예측하는 알고리즘이다.

위 사진으로 데이터의 표현 방법을 알아보자. 피처의 개수는 nn개, 학습 데이터의 개수는 mm개이고, 목포 변수는 여전히 한 개이다.

집 크기, 방 수, 등등의 피처는 x1,x2,x3,...x_1, x_2, x_3, ...로 나타내고, 이를 가지는 입력 변수 하나는 x(i)x^{(i)}라 표현한다. 일반화 해서 표현하자면 다음과 같다. x(i)=[x1x2x3x4]x^{(i)} = \begin{bmatrix} x_1 \\ x_2 \\ x_3 \\ x_4 \\ \end{bmatrix} 따라서 x(i)x^{(i)}는 여러 개의 피처들을 담은 벡터이다.

ii번째 데이터의 jj번째 속성은 xj(i)x_j^{(i)} 와 같이 나타낼 수 있다.

📍 가설 함수

다중 선형회귀의 가설 함수는 피처의 개수가 많아짐에 따라 가설 함수 또한 다음과 같이 나타낼 수 있다.

hθ(x)=θ0+θ1x1+θ2x2++θnxnh_{\theta}(x) = \theta_0 + \theta_1x_1 + \theta_2x_2 + \cdots + \theta_nx_n

θ1\theta_1 은 집 크기가 미치는 영향, θ2\theta_2 은 방 수가 미치는 영향.. 등등이다.

💡 Vectorization

θ=[θ0θ1θ2θn],x=[1x1x2xn]\theta = \begin{bmatrix} \theta_0 \\ \theta_1 \\ \theta_2 \\ \vdots \\ \theta_n \\ \end{bmatrix}, x = \begin{bmatrix} 1 \\ x_1 \\ x_2 \\ \vdots \\ x_n \\ \end{bmatrix}

θ\thetaxx 를 위와 같이 벡터화 한다면 다중 선형 회귀 가설 함수는 아래와 같이 정의할 수 있다.

hθ(x)=θTxh_{\theta}(x) = \theta^T x

📍 다중 선형 회귀에서의 손실 함수와 경사 하강법

다중 선형회귀의 손실함수는 입력 변수가 하나일 때와 완전히 동일하다.

J(θ)=12mi=1m(hθ(x(i))y(i))2J(\theta) = \frac{1}{2m}\sum_{i=1}^{m}(h_\theta(x^{(i)}) - y^{(i)})^2

다중 선형회귀에서는 단순히 업데이트 해야할 θ\theta 값들이 많아지는 것이다. 다중 선형회귀에서의 경사 하강법을 수식으로 나타내면 아래와 같다.

θj=θjα1mi=1m(hθ(x(i))y(i))xj(i)\theta_j = \theta_j - \alpha \frac{1}{m} \sum_{i=1}^{m}(h_{\theta}(x^{(i)}) - y^{(i)}) \cdot x_j^{(i)}

jj00부터 nn까지 넣어가면서 θ0\theta_0 ~ θn\theta_n 까지 쭉 업데이트를 해야 경사 하강을 한 번 했다고 할 수 있다.

💡 Design Matrix

다중 선형회귀의 입력 변수를 행렬로 표현하면 다음과 같다.

X=[x0(1)x1(1)xn(1)x0(2)x1(2)xn(2)x0(m)x1(m)xn(m)]X = \begin{bmatrix} x_0^{(1)} & x_1^{(1)} & \cdots & x_n^{(1)} \\ x_0^{(2)}& x_1^{(2)} & \cdots & x_n^{(2)} \\ \vdots & & & \\ x_0^{(m)} & x_1^{(m)} & \cdots & x_n^{(m)} \\ \end{bmatrix}

행렬로 표현할 때 통일성을 위해서 가설함수 hθ(x)=θ0+θ1x1+θ2x2++θnxnh_{\theta}(x) = \theta_0 + \theta_1x_1 + \theta_2x_2 + \cdots + \theta_nx_n 에 값이 1인 가상의 0번 속성 x0x_{0}을 만들어준 것이다. 즉, hθ(x)=θ0x0+θ1x1+θ2x2++θnxnh_{\theta}(x) = \theta_0x_0 + \theta_1x_1 + \theta_2x_2 + \cdots + \theta_nx_n 처럼 표현할 수 있었고 위와 같은 행렬로 표현이 가능해졌다.

θ\theta 또한 벡터로 표현하면 다음과 같다.

θ=[θ0θ1θn]\theta = \begin{bmatrix} \theta_0 \\ \theta_1 \\ \vdots \\ \theta_n \\ \end{bmatrix}

그렇다면 예측 값은 행렬 XX와 벡터 θ\theta의 곱으로 나타낼 수 있다.

Xθ=[θ0x0(1)+θ1x1(1)+θnxn(1)θ0x0(2)+θ1x1(2)++θnxn(2)θ0x0(m)+θ1x1(m)++θnxn(m)]=[hθ(x(1))hθ(x(2))hθ(x(m))]X\theta = \begin{bmatrix} \theta_0x_0^{(1)} + \theta_1x_1^{(1)} + \cdots \theta_nx_n^{(1)} \\ \theta_0x_0^{(2)} + \theta_1x_1^{(2)} + \cdots + \theta_nx_n^{(2)} \\ \vdots \\ \theta_0x_0^{(m)} + \theta_1x_1^{(m)} + \cdots + \theta_nx_n^{(m)} \\ \end{bmatrix} = \begin{bmatrix} h_\theta(x^{(1)}) \\ h_\theta(x^{(2)}) \\ \vdots \\ h_\theta(x^{(m)}) \\ \end{bmatrix}

예측 오차는 XθX\theta 에서 yy 를 빼면 된다.

error=Xθy=[hθ(x(1))y(1)hθ(x(2))y(2)hθ(x(m))y(m)]error = X\theta - y = \begin{bmatrix} h_\theta(x^{(1)}) - y^{(1)} \\ h_\theta(x^{(2)}) - y^{(2)} \\ \vdots \\ h_\theta(x^{(m)}) - y^{(m)} \\ \end{bmatrix}

이로써 다중 선형회귀의 경사하강법은 다음과 같이 표현하는 것이 가능해진다.

[θ0θ1θn][θ0θ1θn]α1m[i=1m(hθ(x(i))y(i))x0(i)i=1m(hθ(x(i))y(i))x1(i)i=1m(hθ(x(i))y(i))xn(i)]\begin{bmatrix} \theta_0 \\ \theta_1 \\ \vdots \\ \theta_n \\ \end{bmatrix} \leftarrow \begin{bmatrix} \theta_0 \\ \theta_1 \\ \vdots \\ \theta_n \\ \end{bmatrix} - \alpha \frac{1}{m} \begin{bmatrix} \sum_{i=1}^{m}(h_{\theta}(x^{(i)}) - y^{(i)}) \cdot x_0^{(i)} \\ \sum_{i=1}^{m}(h_{\theta}(x^{(i)}) - y^{(i)}) \cdot x_1^{(i)} \\ \vdots \\ \sum_{i=1}^{m}(h_{\theta}(x^{(i)}) - y^{(i)}) \cdot x_n^{(i)} \\ \end{bmatrix}

이를 정리하면 아래와 같다.

θθα1m(XT(Xθy))\theta \leftarrow \theta - \alpha\frac{1}{m}(X^T(X\theta - y))

이렇게 행렬을 사용하여 표현하게 되면 쉽게 구현을 할 수 있다는 장점이 있다. 구현해본 코드는 아래와 같다.

import numpy as np

def prediction(X, theta):
    """다중 선형 회귀 가정 함수. 모든 데이터에 대한 예측 값을 numpy 배열로 리턴한다"""
    return X @ theta

def gradient_descent(X, theta, y, iterations, alpha):
    """다중 선형 회귀 경사 하강법을 구현한 함수"""
    m = len(X)  # 입력 변수 개수 저장
    
    for _ in range(iterations):
        error = prediction(X, theta) - y
        theta = theta - alpha / m * (X.T @ error)

    return theta
# 입력 변수
house_size = np.array([1.0, 1.5, 1.8, 5, 2.0, 2.5, 3.0, 3.5, 4.0, 5.0, 6.0, 7.0, 8.0, 8.5, 9.0, 10.0])  # 집 크기
distance_from_station = np.array([5, 4.6, 4.2, 3.9, 3.9, 3.6, 3.5, 3.4, 2.9, 2.8, 2.7, 2.3, 2.0, 1.8, 1.5, 1.0])  # 지하철역으로부터의 거리 (km)
number_of_rooms = np.array([1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4])  # 방 수

# 목표 변수
house_price = np.array([3, 3.2, 3.6 , 8, 3.4, 4.5, 5, 5.8, 6, 6.5, 9, 9, 10, 12, 13, 15])  # 집 가격

# 설계 행렬 X 정의
X = np.array([
    np.ones(16),
    house_size,
    distance_from_station,
    number_of_rooms
]).T

# 입력 변수 y 정의
y = house_price

# 파라미터 theta 초기화
theta = np.array([0, 0, 0, 0])

# 학습률 0.01로 100번 경사 하강
theta = gradient_descent(X, theta, y, 100, 0.01)

print(theta)
최적의 세타값들:
array([ 0.11484521,  1.21120425,  0.18270523,  0.30060782])

📍 정규 방정식 (Normal Equation)

경사 하강법은 손실함수 J(θ)J(\theta) 의 극소점을 향해 θ\theta를 조금씩 수정하는 것이라면, 정규 방정식은 J(θ)=0J^\prime(\theta) = 0 인 방정식을 풀어 손실함수의 기울기가 0이 되는 θ\theta를 단번에 찾는 방법이다.

정규 방정식은 아래와 같다.

θ=(XTX)1XTy\theta = (X^TX)^{-1}X^Ty

이 식이 도출되는 첫 단계는 먼저 손실함수를 행렬을 이용해서 표현해야 한다. 우리는 이전에 아래와 같은 손실함수를 보았다.

J(θ)=12mi=1m(hθ(x(i))y(i))2J(\theta) = \frac{1}{2m}\sum_{i=1}^{m}(h_\theta(x^{(i)}) - y^{(i)})^2

여기서 시그마로 구성된 식은 MSE 즉, 평균 제곱 오차의 합이다. 이것을 행렬 연산으로 다음과 같이 간단하게 표현할 수 있다.

J(θ)=12m(Xθy)T(Xθy)J(\theta) = \frac{1}{2m}(X\theta - y)^T(X\theta - y)

이제 J(θ)=0\nabla J(\theta) = 0 을 구하면 된다. 일단 미분을 할 것이니 앞의 상수 12m\frac{1}{2m}은 무시하자. (Xθy)T(Xθy)(X\theta - y)^T(X\theta - y)을 전개하면 아래와 같다.

((Xθ)TyT)(Xθy)((X\theta)^T - y^T)(X\theta - y)
=((θTXT)yT)(Xθy)= ((\theta^TX^T) - y^T)(X\theta - y)
=θTXTXθθTXTyyTXθ+yTy= \theta^TX^TX\theta - \theta^TX^Ty - y^TX\theta + y^Ty
=θTXTXθ2θTXTy+yTy= \theta^TX^TX\theta -2\theta^TX^Ty + y^Ty

이제 θ\theta에 대해서 미분을 하자.

θJ(θ)=2XTXθ2XTy\frac{\partial}{\partial \theta}J(\theta) = 2X^TX\theta - 2X^Ty

이제 미분한 식이 0이 되도록 하는 θ\theta 를 찾아야 하므로 θ\theta에 대해서 정리해주자.

2XTXθ2XTy=02X^TX\theta - 2X^Ty = 0
XTXθ=XTyX^TX\theta = X^Ty
θ=(XTX)1XTy\theta = (X^TX)^{-1}X^Ty

이로써 증명이 끝났다.

📍 경사 하강법 vs 정규 방정식


입력 변수가 1000개를 넘느냐를 기준으로 하는 게 일반적이다.

📍 Convex vs Non-Convex

손실함수 J(θ)J(\theta) 의 기울기를 구한 뒤에 이걸 이용해서 손실이 최소가 되게 하는 θ\theta를 찾았었다.
근데 단순히 경사 하강법과 정규 방정식만 이용하면 항상 손실 함수의 최소 지점을 찾을 수 있을까?

경사 하강법 같은 경우에는 임의의 지점에서 시작을 하고 경사 하강을 한 뒤 극소점에 도달해 경사 하강이 종료되었을 때, 그 지점이 전역 최솟값(Global minimum)을 가진다고 확신할 수 없다. 즉, 지역 최솟값(Local minimum)일 수도 있다는 것이다. 이러면 손실함수의 최저점을 찾아갈 수가 없다.


정규 방정식도 마찬가지이다. 미분값이 0이 되게 하는 θ\theta 값을 구해도, 그 θ\theta 에 대한 손실함수 값이 전역 최솟값인지는 모른다.

이렇게 함수가 Non-Convex한 경우에는 경사 하강법과 정규 방정식으로 값이 항상 전역 최솟값이라고 확신할 수는 없게 된다.

반대로 손실함수가 Convex하다면 즉, 아래로 볼록하다면 경사 하강법과 정규 방정식으로 값이 항상 전역 최솟값이라고 확신할 수 있다.

선형 회귀에서는 MSE를 손실 함수로 사용했는데, MSE는 오차의 '제곱'이니까 Convex 하게 된다. 따라서 선형 회귀를 할 때는 경사 하강법과 정규 방정식 모두 전역 최솟값을 보장한다.


출처: 코드잇

0개의 댓글