728x90
반응형

선형 회귀 모델 - 경사 하강법(Gradient descent, GD)

 지난 포스트까지 정규방정식(Normal Equation)과 최소제곱법(Least Squares method)을 이용하여 선형 회귀(Linear Regression)의 모델 파라미터를 계산해보았다.

 정규방정식이나 최소제곱법을 이용하여 모델 파라미터를 계산하는 경우, 공식이 꽤나 단순하고 단 한 번만 연산을 해도 된다는 장점이 있지만, 칼럼의 양이 2배 늘어나는 경우 정규방정식은 계산 시간이 최대 약 8배까지 증가하며, 최소제곱법은 계산 시간이 최대 4배까지 증가한다는 단점이 있다고 하였다.

 때문에, 데이터의 피처(컬럼의 수)가 매우 많거나 데이터 자체가 큰 경우, 정규방정식이나 최소제곱법을 사용하여 선형 회귀의 모델 파라미터를 계산하는 것보다. 경사 하강법(Gradient descent)을 사용하여 계산하는 것이 보다 유리하다.

 이번 포스트에서는 경사 하강법에 대해 알아보고, 경사하강법을 사용하여 선형 회귀 모형으로 학습을 해보도록 하겠다.

 

 

 

 

 

1. 경사 하강법이란?

  • 앞서 우리는 선형 회귀 모델의 손실 함수인 MSE에 대해 알아보았다.

$$ MSE = \frac{1}{n}\sum_{i=1}^{n}(\hat{y_i} - y_i)^2 $$

  • MSE에서 $\hat{y_i} = \theta x + b$이므로($\theta$: 계수, $x$: 실제 값, $b$: 절편), 학습을 통해 알고자 하는 미지수인 모델 파라미터 $\theta$는 1개의 최적해를 갖는 이차 함수 형태인 것을 알 수 있다.
  • 때문에 손실 함수의 개형은 아래와 같다.

  • 선형 회귀 모델을 비롯한 머신러닝 알고리즘에서 최고의 모델 파라미터를 찾는 방법은 손실 함수(Loss function)을 최소로 만드는 점 $\alpha$를 찾는 것이다.
  • 정규방정식이나 최소제곱법은 우리가 찾고자 하는 $\alpha$를 한 번에 찾는 방법이고, 경사 하강법은 손실 함수의 랜덤 한 위치에서 기울기를 계산하고, 기울기가 0이 되는 방향으로 학습률(Learning rate)만큼 점진적으로 이동하여, 최적해 $alpha$를 찾는 방법이다.
  • 위 과정은 전역 최적해(Global minimub)를 찾기 위해 반복 수행되며, 반복 수행된다는 의미는 다양한 랜덤한 위치에서 경사 하강법이 실시된다는 의미다.
  • 보다 자세히 경사 하강법의 원리를 알고자 하는 경우, 이전 포스트인 "딥러닝-6.0. 최적화(1)-손실함수와 경사하강법"을 참고하기 바란다.

 

1.1 경사 하강법의 한계점

  • 경사 하강법의 태생적 문제로 인해 다음과 같은 한계점이 존재한다.
  1. 데이터가 많아질수록 계산량 증가: 학습 데이터의 양이 늘어나는 경우, 계산량이 매우 커져 학습 속도가 느려진다.
  2. 지역 최소해(Local minimum) 문제: 실제 손실 함수의 형태는 위 그래프처럼 매끈하지 않고, 울퉁불퉁하기 때문에 실제로 찾아야 하는 전역 최적해(Global minimum)가 아닌 지역 최소해(Local minimum)를 찾을 수 있다.
  3. Plateau 문제: 평평한 지역(Plateau)에 들어가서, 학습이 더 이상 진행되지 못하는 문제
  4. Zigzag 문제: 찾아야 하는 해가 많아질수록 차원이 복잡해져, 제대로 해를 찾지 못하는 문제

 

 

 

 

 

2. 경사 하강법의 공식 - 배치 경사 하강법(Batch Gradient Descent)


  • 앞서 봤던 MSE 공식을 이번에는 벡터 형태로 가지고 와보자.

$$ MSE(X, h_{\theta}) = MSE(\theta) = \frac{1}{n}\sum_{i=1}^{n}(\theta^Tx^{(i)} - y^{(i)})^2 $$

  • 위 식을 모델 파라미터 $\theta$에 대해 편미분 해보자.

$$\frac{\partial}{\partial \theta_j}MSE(\theta) = \frac{2}{n}\sum_{i=1}^{n}(\theta^T x^{(i)} - y^{(i)})x_{j}^{i}$$

  • 위 편미분 결과에 대하여, 모든 모델 파라미터 $\theta_j$의 성분에 대한 편미분으로 구성된 열벡터로 만들어보고, 이를 행렬식으로 바꿔보자.

$$\bigtriangledown_{\theta} MSE(\theta) =
\begin{pmatrix} \frac{\partial}{\partial \theta_0}MSE(\theta)
\\ \frac{\partial}{\partial \theta_1}MSE(\theta)
\\ \vdots 
\\ \frac{\partial}{\partial \theta_0}MSE(\theta)
\end{pmatrix} = \frac{2}{m}X^T(X\theta - y)$$

  • 경사 하강법 공식은 다음과 같다.

$$x_{i+1} = x_i - \eta \bigtriangledown f(x_i)$$

  • 위 경사 하강법 공식에서 손실함수 $\bigtriangledown f(x_i)$의 위치에, 위에서 구한 $MSE$의 편미분 행렬식인 $\bigtriangledown_{\theta} MSE(\theta)$을 넣어주고, 미지수$x$도 선형 회귀의 모델 파라미터인 $\theta$로 바꿔 선형 회귀 모델에 대한 경사 하강법 공식으로 만들어주자.

$$\theta_{i+1} = \theta_i - \eta \bigtriangledown MSE(\theta) = \theta_i - \frac{2}{m} \eta X^T(X\theta - y)$$


  • 위 공식은 매 경사 하강법 스텝에서 전체 훈련 세트 $X$에 대해 계산한다. 이를 배치 경사 하강법(Batch gradient descent)라 하며, 매 스텝에서 훈련 데이터 전체를 대상으로 최적해를 찾기 때문에 계산 시간도 길고, 소모되는 컴퓨터 자원의 양도 많다.
  • 때문에 매우 큰 데이터를 대상으로 할 때는 위 공식인 배치 경사 하강법을 사용하지 않는 것이 좋으며, 무작위 샘플 데이터 셋을 추출해, 그 샘플에 대해서만 경사 하강법을 실시하는 확률적 경사 하강법(Stochastic Gradient Descent, SGD)을 실시하는 것이 좋다.
  • 그러나, 경사 하강법은 특성 수에 민감하지 않기 때문에, 특성 수가 매우 많은 경우, 정규방정식이나 특잇값 분해(SVD)를 이용한 최소제곱법보다 경사 하강법을 사용하여 학습하는 것이 좋다.
  • 경사 하강법 공식의 $\eta$는 학습률(Learning rate)라 하며, 에타라고 발음한다. 경사 하강법은 현재(n)의 파라미터 $\theta$에서 MSE의 편미분 벡터에 학습률을 곱한 수치만큼 아래 방향으로 이동한다.

 

 

 

 

 

3. 넘파이 코드를 이용해서 배치 경사 하강법을 구현해보자.

  • 위에서 행렬식으로 만든 경사 하강법 함수는 다음과 같다.

$$\theta_{i+1} = \theta_i - \frac{2}{m} \eta X^T(X\theta - y)$$

  • 경사 하강법은 최적의 모델 파라미터를 찾기 위해 전체 데이터에 대하여 m번 반복하여 계산된다.
  • 이 반복되는 것을 학습 단위라고 하며, Epoch(에포크), Batch size(배치 사이즈), Iteration(이터레이션)에 대한 기본 개념을 알아야 한다.
  1. Epochs: 전체 데이터를 학습 시키는 횟수
  2. Batch size: 연산 한 번에 들어가는 데이터의 크기
  3. Iteration: 1 epoch를 마치는데 필요한 모델 파라미터 업데이터의 횟수

 

3.1. 사용할 데이터

  • 데이터는 이전 포스트에서 사용했던 컬럼이 1개인 데이터를 사용해보겠다.
import numpy as np
import matplotlib.pyplot as plt
np.random.seed(1234)

def f(x):
    return 3 * x + 4

X = 2 * np.random.rand(100, 1)
Y = f(X) + np.random.randn(100, 1)

# 상수항을 위해 모든 원소가 1인 컬럼을 추가함
X_b = np.c_[X, np.ones((100, 1))]
  • 생성한 데이터를 sklearn 라이브러리의 LinearRegression 모델을 사용하여, 학습시켜보겠다.
  • sklearn의 LinearRegression에 학습시킬 때는 상수항을 위해 모든 원소가 1인 컬럼을 추가하지 않아도 된다.
# sklearn 라이브러리의 선형 회귀 모형
from sklearn.linear_model import LinearRegression

LR_model = LinearRegression()
LR_model.fit(X, Y)
>>> LR_model.coef_, LR_model.intercept_
(array([[2.94132381]]), array([4.07630632]))
  • 숨겨진 패턴인 함수 $f(x) = 3x + 4$의 계수 3과 상수 4에 근사한 결과가 나왔다.

 

3.2. 배치 경사 하강법 코드

def Batch_GD(x, y, eta=0.01, iteration_N=1000):
    
    # 초기 무작위 파라미터
    theta = np.random.randn(x.shape[1], 1)

    for iteration in range(iteration_N):

        # MSE의 편미분 벡터
        gradients = (2/x.shape[0])* x.T.dot(x.dot(theta) - y)
        theta = theta - eta * gradients
        
    return theta
>>> Batch_GD(X_b, Y, 0.01, 1000)
array([[3.02789289],
       [3.97237607]])
  • 학습률 eta나 파라미터 갱신 횟수 iteration_N과 같은 분석가가 직접 설정하는 파라미터를 머신러닝에서는 하이퍼 파라미터라고 한다.
  • 학습률이 크면 클수록 빠르게 최적해에 수렴하나, 너무 크게 이동하므로 전역 최적해에 수렴하지 못할 수 있다.
  • 학습률을 보다 크게 학습시켜보자.
>>> Batch_GD(X_b, Y, 0.1, 1000)
array([[2.94132381],
       [4.07630632]])
  • 학습률이 0.1로 10배나 커지자, 보다 빠르게 수렴하여 최소제곱법(LSM)을 사용하여 회귀 모델의 최적의 파라미터를 계산하는 sklearn의 LinearRegression과 같은 결과가 나왔다.

 

 

 

 

 

4. 학습률과 Iteration에 따른 수렴 속도의 차이 시각화

  • 위에서 만든 Batch_GD 함수를 약간 수정하여, 갱신된 파라미터에 대한 예측값을 plot으로 시각화하는 함수를 만들어보자.
# 전체 시각화
def draw_eta(x, y, eta_, iteration_N_):
    
    plt.scatter(x, y)
    
    # 상수항을 위한 모든 원소가 1인 컬럼 추가
    x_b = np.c_[x, np.ones((X.shape[0], 1))]
    Batch_GD_Visual(x_b, y, eta=eta_, iteration_N=iteration_N_)
    
    # 제목
    plt.xlabel("X", fontsize = 15)
    plt.ylabel("Y", fontsize = 15, rotation = 0)
    plt.title(f"$\eta$={eta_}", fontsize = 20, pad=15)
    
    plt.xlim(0, 2)
    plt.ylim(0, 15)
    
    plt.show()



# 갱신되는 theta에 따른 경사 하강법 시각화
def Batch_GD_Visual(x, y, eta=0.01, iteration_N=1000):

    # 초기 무작위 파라미터
    theta = np.random.randn(x.shape[1], 1)

    for iteration in range(iteration_N):

        # MSE의 편미분 벡터
        gradients = (2/x.shape[0])* x.T.dot(x.dot(theta) - y)
        theta = theta - eta * gradients
        
        # 경사 하강법을 통해 바뀐 theta로 예측한 값
        new_Y = np.array([[0,1],[2,1]]).dot(theta)
        plt.plot([0,2], new_Y, c = "r", alpha = 0.2)
        
    return theta
  • draw_eta는 시각화 코드를 정리해놓은 함수다.
  • Batch_GD_Visual은 갱신되는 $\theta$에 따른 경사 하강법을 시각화하는 함수로, 옅은 빨간색 선을 그린다.

4.1. iteration은 50이고, 학습률이 변하는 경우

  • iteration 1000은 꽤 큰 값이므로, 작은 50으로 설정해놓고 학습률만 바꿔보자.
  • 학습률이 0.01인 경우
eta_ = 0.01
iteration_N_ = 50
draw_eta(X, Y, eta_, iteration_N_)

 

  • 학습률이 0.1인 경우
eta_ = 0.1
iteration_N_ = 50
draw_eta(X, Y, eta_, iteration_N_)

 

  • 학습률이 0.5인 경우
eta_ = 0.5
iteration_N_ = 50
draw_eta(X, Y, eta_, iteration_N_)

  • 위 결과를 보면, 학습률이 커지면 커질수록 빠르게 최적해를 찾아가는 것을 볼 수 있지만, $\eta=0.5$ 같이 지나치게 큰 경우, 모델 파라미터가 큰 폭으로 요동치는 것을 볼 수 있다.
  • 위에서 볼 수 있듯, 데이터에 맞는 적절한 학습률을 찾는 것은 매우 중요하다.

4.2. 학습률은 0.01이고, iteration이 변하는 경우

  • 학습률을 최적해에 비교적 늦게 수렴하는 0.01로 잡고, 파라미터 갱신 횟수인 iteration만 바꿔보자.
    (꽤 많은 머신러닝 알고리즘에서 Learning rate의 기본값을 0.01로 잡아놓는다.)
  • iteration이 10인 경우
eta_ = 0.01
iteration_N_ = 10
draw_eta(X, Y, eta_, iteration_N_)

  • iteration이 30인 경우
eta_ = 0.01
iteration_N_ = 30
draw_eta(X, Y, eta_, iteration_N_)

  • iteration이 100인 경우
eta_ = 0.01
iteration_N_ = 100
draw_eta(X, Y, eta_, iteration_N_)

  • 위 결과를 보면, 파라미터 갱신량도 필요치보다 작은 경우, 최적해를 찾기 전에 경사 하강이 끝나버리고, 필요치 보다 큰 경우, 최적해를 찾고 파라미터가 더 이상 변하지 않는 시간 낭비가 발생하는 것을 볼 수 있다.
  • 이에 대한 해결책으로는, iteration을 애초에 크게 잡은 후, 경사 하강법 알고리즘에 규제를 추가하여, 모델 파라미터의 변화량이 허용오차(tolerance) $\varepsilon$보다 작아지면 최적해에 도달한 것으로 판단하고 모델 파라미터 갱신을 중지시키는 것이다.

 

 

[참고 서적]

 

 

 

 이번 포스트에서는 배치 경사 하강법을 이용하여 선형 회귀 모형을 만들어보았다. 배치 경사 하강법은 데이터가 지나치게 큰 경우 계산 시간이 지나치게 오래 걸리는 정규방정식이나 최소제곱법보다 빠르게 최적해를 찾을 수 있다.

 그러나, 학습률, 파라미터 갱신 횟수(iteration)과 같은 하이퍼 파라미터를 제대로 잡지 않는다면, 학습 시간이 필요보다 길어지거나, 최적해에 수렴하지 못할 수 있으므로, 최적의 하이퍼 파라미터를 찾을 수 있어야 한다.

 배치 경사 하강법은 모든 데이터에 대한 편미분 벡터를 계산하게 되므로, 데이터의 크기가 이보다 더 커지는 경우, 학습 시간이 더 길어질 수 있다. 다음 포스트에서는 이 문제를 해결한 확률적 경사 하강법에 대해 알아보도록 하겠다.

728x90
반응형
728x90
반응형

선형 회귀의 구현 - 정규방정식과 최소제곱법

 지난 포스트에서 머신러닝에서 선형 회귀 모델의 기본적인 원리에 대해 알아보았다. 이번 포스트는 정규방정식을 넘파이 함수로 구현해보고, 정규방정식으로 찾아낸 최적의 모델 파라미터를 이용해서 새로운 데이터에 대해 예측을 해보자.

 

 

 

 

1. 선형성을 갖는 무작위 데이터를 만들어보자.

  • 임의의 선형 데이터를 만들어 정규방정식에 넣어보도록 하자.
import numpy as np
import matplotlib.pyplot as plt
np.random.seed(1234)

def f(x):
    return 3 * x + 4

X = 2 * np.random.rand(100, 1)
Y = f(X) + np.random.randn(100, 1)
  • np.random.rand(shape): 0~1 사이의 표준 정규 분포 난수를 shape의 형태대로 생성
  • np.random.randn(shape): 평균 0, 표준편차 1의 가우시안 표준 정규 분포 난수를 shape의 형태대로 생성
  • 0에서 1 사이의 난수 X에 2를 곱하여, 값의 범위를 더 넓게 하였다.
  • 숨겨진 패턴은 함수 $f(x) = 3x + 4$이며, $f(x)$에 $N(0,1)$인 가우시안 표준 정규 분포를 더해 노이즈를 주었다.
plt.scatter(X, Y)

plt.xlabel("X", fontsize=15)
plt.ylabel("Y", rotation=0, fontsize=15)
plt.show()

 

 

 

 

 

2. 넘파이 코드를 사용해서 정규방정식을 구현해보자.

  • 정규방정식 함수는 다음과 같다.

$$\hat{\theta} = (X^TX)^{-1}X^Ty$$

  • 위 정규방정식 함수를 넘파이 라이브러리의 함수를 사용해서 함수로 만들어보자.
# 정규방정식
def NormalEquation(X, Y):
    
    return np.linalg.inv(np.dot(X.T, X)).dot(X.T).dot(Y)
  • np.linalg.inv(): 역행렬
  • np.dot(mat1, mat2): 행렬 mat1과 mat2의 행렬 곱 연산을 한다.
  • mat1.dot(mat2): 행렬 mat1과 mat2의 행렬 곱 연산을 한다.

 

2.1. 배열 X와 Y를 넣어보자

>>> NormalEquation(X, Y)
array([[6.00122666]])
  • 위 데이터에서 숨겨진 패턴인 함수 $f(x)$는 $f(x) = 3x + 4$이므로, 3과 4가 출력되어야 하지만, 원소가 단 하나만 출력되었다.
  • 이는 함수 $f(x)$에 들어가는 배열 X에 절편에 대한 칼럼이 반영되지 않았기 때문이다.
  • 절편 4는 4*1이므로, 배열 X에 모든 원소가 1인 칼럼을 하나 추가해주고, 정규방정식을 수행해보도록 하자.

 

2.2. 절편에 대한 칼럼이 반영된 배열 X와 Y를 넣어보자.

# 절편에 대한 1로 구성된 벡터를 추가함.
>>> X_b = np.c_[X, np.ones((100, 1))]

# 정규방정식을 통해 최적의 계수를 찾아냄
>>> NormalEquation(X_b, Y)
array([[2.94132381],
       [4.07630632]])
  • np.c_[array1, array2]: array1과 array2를 컬럼 방향으로 붙인다(오른쪽에 붙인다)
  • 위 결과를 보면, 숨겨진 패턴인 함수 $f(x) = 3x + 4$의 계수인 3과 4와 아주 가까운 값이 도출된 것을 볼 수 있다.
  • 숨겨진 패턴의 계수 3과 4가 나오지 않은 이유는, 의도적으로 노이즈를 주었기 때문이며, 그 노이즈를 반영하였을 때, 숨겨진 패턴에 가장 가까운 모델 파라미터가 출력된 것이다.

 

2.3. 변수 $x$가 2개인 데이터에 대하여 정규방정식을 이용해 최적해를 구해보자.

  • 이번에는 숨겨진 패턴이 $h(x) = 3x_1 + 4x_2 + 5$인 함수에 대하여 구해보자.
  • 변수  $x$가 2개이므로, 컬럼의 수는 2개가 되어야 한다.
X1 = 2 * np.random.rand(100, 2)

def h(x):
    x1 = x[:,0]
    x2 = x[:,1]
    
    result = 3 * x1 + 4 * x2 + 5
    
    return result.reshape(100, 1)

Y1 = h(X1) + np.random.randn(100, 1)

# 절편을 위해 모든 원소가 1인 컬럼 추가
X1_b = np.c_[X1, np.ones((100, 1))]
>>> NormalEquation(X1_b, Y1)
array([[2.93017703],
       [4.19897028],
       [4.90893137]])
  • 각 계수의 값이 3, 4, 5에 근사하게 나왔으나, 실제 값과 어느 정도 거리가 있다.
  • 데이터의 양이 늘어날수록 실제 계수에 더 가까워진다.
X2 = 2 * np.random.rand(10000, 2)

def h(x):
    x1 = x[:,0]
    x2 = x[:,1]
    
    result = 3 * x1 + 4 * x2 + 5
    
    return result.reshape(10000, 1)

Y2 = h(X2) + np.random.randn(10000, 1)
X2_b = np.c_[X2, np.ones((10000, 1))]
>>> NormalEquation(X2_b, Y2)
array([[2.98528199],
       [4.01290386],
       [5.00091078]])

 

 

 

 

 

3. 찾아낸 모델 파라미터를 이용해서 예측을 해보자.

  • 정규방정식을 통해 계산해 낸 선형 회귀 모델의 파라미터 $\hat{\theta}$를 사용해서 새로운 데이터가 들어갔을 때, 그 결과를 예측해보자.
def predict_linear_R(X, theta):
    
    R_n = X.shape[0]
    
    try:
        C_n = X.shape[1]
    except:
        C_n = 1
    
    X_Reshape = X.reshape(R_n, C_n)
    X_concat_1 = np.c_[X_Reshape, np.ones((R_n,1))]
    
    return X_concat_1.dot(theta)

 

3.1. 독립변수가 1개인 경우

  • 처음 만들었던 칼럼이 1개 있는 데이터의 최적 모델 파라미터에 대하여, 새로운 데이터를 넣어 새로운 데이터에 대한 예측 값을 뽑아보자.
  • 새로운 데이터는 [0, 1, 2]로 구성된 벡터이다.
def f(x):
    return 3 * x + 4

X = 2 * np.random.rand(100, 1)
Y = f(X) + np.random.randn(100, 1)

X_b = np.c_[X, np.ones((100, 1))]
best_theta = NormalEquation(X_b, Y)
>>> best_theta
array([[2.94132381],
       [4.07630632]])
# 새로운 데이터
>>> new_X = np.array([0, 1, 2]).reshape((3,1))

# 새로운 데이터를 학습된 선형 회귀 모형에 넣어 예측된 결과
>>> pred_LR = predict_linear_R(new_X, best_theta)
>>> pred_LR
array([[4.07630632],
       [7.01763013],
       [9.95895394]])
  • 기존에 그린 산점도 위에 예측된 결과를 그려보자.
plt.scatter(X, Y, label="real")
plt.plot(new_X, pred_LR.reshape(new_X.shape), color = "red", label="predict")

plt.xlabel("X", fontsize=15)
plt.ylabel("Y", rotation=0, fontsize=15)
plt.legend(loc="lower right")
plt.show()

 

3.2. 독립변수가 2개인 경우

X1 = 2 * np.random.rand(100, 2)

def h(x):
    x1 = x[:,0]
    x2 = x[:,1]
    
    result = 3 * x1 + 4 * x2 + 5
    
    return result.reshape(100, 1)

Y1 = h(X1) + np.random.randn(100, 1)
X1_b = np.c_[X1, np.ones((100, 1))]
best_theta1 = NormalEquation(X1_b, Y1)
>>> best_theta1
array([[2.93017703],
       [4.19897028],
       [4.90893137]])
>>> new_X1 = np.array([[0, 1],[0.5, 1.5],[1, 2]])

>>> predict_linear_R(new_X1, best_theta1)
array([[ 9.10790166],
       [12.67247531],
       [16.23704897]])
  • 독립변수가 2개 이상인 경우, 독립 변수의 축 2개 + 종속변수의 축 1개로 3개 이상의 축이 생겨, 시각화가 어려워진다. 그러므로, 시각화는 생략하도록 하겠다.
  • 최적의 모델 파라미터만 찾아내면, 쉽게 회귀 모델을 사용해서, 예측값을 찾아낼 수 있다.

 

 

 

 

 

4. 사이킷런을 사용해서 선형 회귀를 실시해보자.

  • 사이킷런을 사용하면, 더 쉽게 위 과정을 진행할 수 있다.
from sklearn.linear_model import LinearRegression

lin_R = LinearRegression()
  • 사용하고자 하는 모델인 선형 회귀 모형을 객체로 만들었다.
  • 앞서 만든 데이터 X와 Y를 사용하여, 최적의 모델 파라미터를 찾아보자.
# 선형 회귀 모형 학습
>>> lin_R.fit(X, Y)

# 계수와 절편 출력
>>> lin_R.coef_, lin_R.intercept_
(array([[2.94132381]]), array([4.07630632]))

# Numpy로 만든 정규방정식의 결과
>>> best_theta
array([[2.94132381],
       [4.07630632]])
  • 모델.fit(array1, array2): 원하는 모델을 학습시킨다. array1은 훈련 데이터(Test data), array2는 레이블 데이터(Label data)다.
  • 선형회귀모델.intercept_, 선형회귀모델.coef_: sklearn을 사용하여, 선형 회귀 모형을 학습시키면, 계수와 절편의 값이 따로 출력된다. coef_는 계수이며, intercept_는 절편이다.
  • sklearn을 사용하여 계산된 결과와 numpy를 사용하여 만든 정규방정식의 결과가 동일한 것을 볼 수 있다.
  • 이번에는 학습된 모델 파라미터를 사용하여, 새로운 데이터가 들어갔을 때의 결과를 예측해보자.
# sklearn을 이용하여 예측한 결과
>>> lin_R.predict(new_X)
array([[4.07630632],
       [7.01763013],
       [9.95895394]])
       
# numpy 라이브러리를 사용하여 예측한 결과
>>> predict_linear_R(new_X, best_theta)
array([[4.07630632],
       [7.01763013],
       [9.95895394]])
  • 모델.predict(array): array에 모델 파라미터를 계산하여, 예측된 결과를 출력한다.

 

 

 

 

 

5. 정규방정식의 한계점

5.1. 의사역행렬(Pseudoinvese matrix)과 최소제곱법(LSM)

  • 앞서 우리가 학습하였던 정규방정식에는 큰 한계점이 존재한다.
  • 정규방정식 공식 $\hat{\theta} = (X^TX)^{-1}X^Ty$에는 역행렬이 포함되어 있는데, 역행렬은 항상 존재하는 것이 아니기 때문에 역행렬이 존재하지 않는다면, 정규방정식 공식은 작동하지 않을 수 있다.
  • 때문에 등장하는 것이 무어-펜로즈 역행렬(Moore-Penrose pseudoinverse matrix)이라고도 불리는 의사역행렬(Pseudoinverse matrix)을 사용하여 다음과 같은 공식으로 최적의 모델 파라미터를 찾아내게 된다.

$$ \hat{\theta} = X^{+}y$$

  • 의사역행렬은 특잇값 분해(Singular value decomposition, SVD)라는 표준 행렬 분해 기법을 통해 계산되며, 의사역행렬은 역행렬이 없는 행렬에 대해서도 계산을 할 수 있기 때문에 정규방정식의 한계점을 해결할 수 있으며, 계산 속도 또한 빠르다.
  • 선형 회귀 모델의 회귀 계수(모델 파라미터)를 추정할 때, 가장 많이 사용되는 기법인 최소제곱법(Least squares method, LSM)은 의사역행렬을 사용하여 해를 구하므로, sklearn의 선형 회귀 모델은 정규방정식이 아닌 최소제곱법을 기반으로 계산한다.
>>> np.linalg.lstsq(X_b, Y, rcond=1e-6)
(array([[2.94132381],
        [4.07630632]]),
 array([92.72583695]),
 2,
 array([14.98211623,  3.69398772]))
  • np.linalg.lstsq(array1, array2): array1, array2의 최소제곱해를 선형 행렬 방정식으로 반환한다.
  • 위 함수를 사용하면 총 4개의 결과가 반환되는데, 여기서 맨 처음에 반환된 결과인 최소제곱해만 신경 쓰면 된다.
# 최소제곱해
>>> np.linalg.lstsq(X_b, Y, rcond=1e-6)[0]
array([[2.94132381],
       [4.07630632]])
       
       
# Numpy로 만든 정규방정식의 결과
>>> best_theta
array([[2.94132381],
       [4.07630632]])
  • 보시다시피 최소제곱법을 사용해서 구하는 최소제곱해와 정규방정식의 결과는 동일한 것을 알 수 있다.
  • 그러나, 최소제곱법은 의사역행렬을 통해 계산되기 때문에 역행렬이 존재하지 않는 대상에 대해서도 계산이 가능하다.

 

5.2. 정규방정식의 계산복잡도

  • 정규방정식은 역행렬 계산, 행렬 곱 등 다양한 행렬 연산이 들어가기 때문에 계산 복잡도(Computation complexity)가 매우 크다.
  • 정규방정식을 사용할 때, 특성의 수가 2배 늘어나면, 계산 시간이 약 5.3배에서 8배로 증가하며, 특잇값 분해(SVD)를 통해 의사역행렬을 구한다 할지라도 특성의 수가 2배 늘어나는 경우, 계산 시간이 약 4배 증가하게 된다.
  • 즉, 칼럼의 수가 늘어나면 늘어날수록 계산 시간이 기하급수적으로 늘어나기 때문에, 칼럼이 많은 데이터를 정규방정식이나 최소제곱법으로 구하는 경우, 시간이 매우 오래 걸리게 된다.
    (행의 수가 늘어나는 경우, 늘어난 샘플 수만큼 선형적으로 소모 시간이 증가하기 때문에 행의 수는 큰 영향을 안 준다.)
  • 만약, 칼럼의 양이나 데이터 자체가 매우 많은 경우, 계산량이 지나치게 많아, 메모리를 넘어설 수 있기 때문에, 정규방정식이나 최소제곱법을 사용하지 못할 수도 있다.

 

 

[참고]

numpy.org/doc/stable/reference/generated/numpy.linalg.lstsq.html

 

numpy.linalg.lstsq — NumPy v1.20 Manual

Cut-off ratio for small singular values of a. For the purposes of rank determination, singular values are treated as zero if they are smaller than rcond times the largest singular value of a. Changed in version 1.14.0: If not set, a FutureWarning is given.

numpy.org

blog.daum.net/jungjin1980/77

 

특이값 분해

출처 : http://darkpgmr.tistory.com/106 활용도 측면에서 선형대수학의 꽃이라 할 수 있는 특이값 분해(Singular Value Decomposition, SVD)에 대한 내용입니다. 보통은 복소수 공간을 포함하여 정의하는 것이..

blog.daum.net

 

 

 

 지금까지 정규방정식과 정규방정식의 한계점을 보완한 최소제곱법에 대해 간략히 알아보고, 코드화해 보았다. 최소제곱법은 선형 회귀 모형을 다룰 때, 굉장히 심도 있게 공부해야 하는 부분이므로, 기초 통계학 부분에서 보다 자세히 다루도록 하겠다.

 다음 포스트에서는 선형 회귀 모형을 정규방정식이나 최소제곱법이 아닌, 경사하강법을 사용해서 구하는 방법에 대해 알아보도록 하겠다.

728x90
반응형
728x90
반응형

선형 회귀(Linear Regression)

 통계학의 꽃이라고도 불리는 선형 회귀(Linear Regression)는 수많은 머신러닝 알고리즘의 기반이 되기도 하기 때문에, 반드시 그 원리를 이해해야하는 알고리즘 중 하나다.

 선형 회귀의 원리를 단순하게 말하자면, 관찰값으로부터 가장 거리가 짧은 데이터를 대표할 수 있는 선을 긋는 것이며, 그 선을 회귀식이라고 한다. 관찰값으로부터 가장 짧은 거리인지를 평가하는 방법은 최소제곱법(Least Square Method)이 주로 사용된다.

 

 

 

 

1. 회귀식


$$\hat{y} = \theta_0 + \theta_1x_1 + \theta_2x_2 + \cdots + \theta_nx_n \ \ \ \cdots①$$

$$\hat{y} = h_\theta(x) = \theta \cdot x   \ \ \ \cdots②$$


  • 회귀식은 기본적으로 해가 $n$개인 일차 연립방정식의 형태이며, 계수와 특성의 값의 곱의 합에 편향(Bias, 절편 - Intercept)을 더한 것이다.
  • $\theta_n$는 해당 관찰값의 계수로, 관찰값 $x_n$가 예측값에 미치는 영향을 보여준다.
  • 계수 $\theta$는 음과 양의 부호를 가질 수 있으며, 이를 통해 해당 특성(변수, 필드)가 종속변수(예측값)에 어떠한 영향을 주는지 볼 수 있다.

1.1. 식 ①의 설명

  • $\hat{y}$: 예측값으로, 종속변수(Dependent variable)이라 한다.
  • $n$: 특성의 수, 독립변수(Independent variable)의 수이다.
  • $x_i$: $i$번째 특성값으로, $i$번째 독립변수를 의미한다.
  • $\theta_j$: $j$번째 모델 파라미터로, 머신러닝 알고리즘 스스로가 학습을 통해 찾아내는 값이다.

1.2. 식 ②의 설명

  • 식 ①을 벡터 형태로 바꾼 것으로, $\theta$와 $x$가 열 벡터(Column vector) 라면, 예측은 $\hat{y} = \theta^Tx$가 된다.
  • 열 벡터란 하나의 열(Column)을 가진 2D 배열이며, $\theta^T$는 열벡터 $\theta$의 전치(Transpose)이다.
  • 두 열 벡터 $\theta$와 $x$의 길이는 서로 동일하므로, 전치 벡터와 길이가 동일한 벡터를 곱하면, 동일한 위치의 원소끼리 곱하고, 더하는 효과가 발생한다.
  • $\theta$: 편향 $\theta_0$와 계수 $\theta_1, \theta_2, \cdots, \theta_n$에 대한 모델 파라미터 벡터다.
  • $x$: $x_0, x_1, \cdots, x_n$까지의 데이터의 특성 벡터다. $x_0$는 편향과 곱해지는 값이므로 무조건 1이다.
  • $\theta \cdot x$: 벡터 $\theta$와 $x$의 점곱으로, $\theta_0x_0 + \theta_1x_1 + \theta_2x_2 + \cdots + \theta_nx_n$과 같다.
  • $h_\theta$: 모델 파라미터 $\theta$를 사용한 가설(Hypothesis) 함수다.

 

 

 

 

 

2. 손실함수 RMSE & MSE

  • 선형 회귀 모델의 학습은 훈련 데이터 셋(Train dataset)에 가장 잘 맞는 모델 파라미터($\theta$)를 찾아내는 것이다.
  • 이를 위해 모델이 훈련 데이터에 얼마나 잘 맞는지 측정해야한다.
  • 선형 회귀 모형은 평균 제곱근 오차(Root Mean Square Error, RMSE)를 사용하여, 모델의 성능을 평가하며, RMSE를 최소화하는 $\theta$를 찾아내는 것이 선형 회귀 모델의 학습 과정이다.
  • RMSE 공식은 다음과 같다.

$$RMSE = \sqrt{\frac{1}{n}\sum_{i=1}^{n}(\hat{y_i} - y_i)^2}$$

$$ RMSE(X, h_{\theta}) = RMSE(\theta) = \sqrt{\frac{1}{n}\sum_{i=1}^{n}(\theta^Tx^{(i)} - y^{(i)})^2} $$


  • 평균 제곱근 오차(RMSE)의 원리는 표준편차와 동일하다. 예측값과 실제 관측값의 편차 제곱의 합의 평균에 제곱근을 씌워 제곱의 합을 보정해주는 것이다.
  • 평균 제곱근 오차(RMSE)에 대하여 더 자세히 알고자하는 경우, 다음 포스트 "딥러닝-5.2. 손실함수(3)-평균제곱근오차(RMSE)"를 참고하길 바란다.
  • 예측값과 실제값의 편차의 평균에 가장 근사한 지표는 RMSE지만, 실제 선형 회귀 모델을 만들 때는 평균 제곱 오차(Mean Square Error, MSE)가 더 많이 사용된다.
  • MSE 공식은 다음과 같다.

$$ MSE = \frac{1}{n}\sum_{i=1}^{n}(\hat{y_i} - y_i)^2 $$

$$ MSE(X, h_{\theta}) = MSE(\theta) = \frac{1}{n}\sum_{i=1}^{n}(\theta^Tx^{(i)} - y^{(i)})^2 $$


  • RMSE보다 MSE를 사용하는 이유는 제곱근을 사용하지 않았기 때문에 실제 값보다 증폭된 결과가 도출되긴 하지만, 공식이 더 간단하며, 미분하기도 쉽다.
    어차피 MSE로 도출된 결과들을 비교하기 때문에 다른 모델과 비교할 때, 보다 엄격한 결과가 나오는 RMSE를 굳이 사용할 필요가 없다.
  • MSE에 대해 보다 자세히 알고자 하는 경우, 다음 포스트 "딥러닝-5.1. 손실함수(2)-평균제곱오차(MSE)"를 참고하기 바란다.
  • 선형 회귀 모델에서의 학습은 비용 함수(Cost function, 손실 함수 - Loss function)인 RMSE나 MSE를 최소로 만드는 $\theta$를 찾는 것이다.

 

 

 

 

 

3. 정규방정식(Normal equation)


$$\hat{\theta} = (X^TX)^{-1}X^Ty$$


  • $\hat{\theta}$: 비용 함수를 최소화하는 $\theta$값이다.
  • $y$: $y^(1)$부터 $y^{(m)}$까지 포함하는 타깃 벡터다.
  • 정규방정식(Normal equation, Ordinary least squares, linear least squrares)는 선형 회귀에서 모델 파라미터인 $\theta$를 예측하기 위해 사용하는 최적화 알고리즘이다.
  • 위 정규방정식은 MSE를 통해 유도 된다.

 

3.1. 정규방정식의 유도

  • MSE 공식은 다음과 같다. $MSE(\theta) = \frac{1}{n}\sum_{i=1}^{n}(\theta^Tx^{(i)} - y^{(i)})^2$ 
  • 위 공식을 보면, MSE 공식은 $\theta^T$에 대하여 이차함수의 개형을 갖는 것을 알 수 있다.
  • 그러므로, MSE을 $theta^T$에 대하여 편미분의 결과가 0이 나오게 하는 $\theta^T$의 값이 MSE를 최소로 만드는 값임을 알 수 있다.
  • 위 내용을 이용하여, 정규방정식을 유도해보자.

 

3.2. 정규방정식과 경사하강법의 차이점

  • 대표적인 최적화 알고리즘인 경사하강법(참고: "딥러닝-6.0. 최적화(1)-손실함수와 경사하강법")은 학습률(Learning rate)를 기반으로 점진적으로 최적해를 찾아간다.
  • MSE를 유도하여 만들어진 정규방정식은 행렬 연산을 통해 한 번에 최적해를 구한다.
  • 정규방정식은 행렬 연산을 통해 결과를 구하기 때문에 피처의 크기(Column * Row의 양)가 커지면 커질수록 계산 시간이 오래 걸린다.
  • 경사하강법은 계산이 일어나 기본적으로 소모되는 시간이 크긴 하지만, 아무리 피처의 크기가 크더라도 일정 시간 안에 최적해를 찾아낼 수 있다.
  • 즉, 피처의 크기가 지나치게 크다면 선형 회귀에서도 경사하강법을 사용하는 것이 좋으며, 피처의 크기가 적당한 수준이라면, 정규방정식을 사용하도록 하자.

 

 

[참고]

 

 

 

 다음 포스트에서는 파이썬 넘파이(Numpy) 함수만을 사용해서 선형 회귀 모델을 구현해보고, 사이킷런을 사용해서 선형 회귀 모델을 사용해보도록 하자.

728x90
반응형
728x90
반응형

테스트와 검증

 이전 포스트에서는 머신 러닝 사용 시 주의해야 할 사항에 대해 알아보았다. 이번 포스트에서는 학습이 끝난 모델을 어떻게 평가하는지에 대해 알아보도록 하겠다.

 

 

 

 

1. 데이터 셋 나누기

  • 학습이 완료된 모델이 새로운 데이터에 대해 얼마나 잘 일반화되는지 알아보기 위해선, 학습에 사용하지 않은 새로운 데이터를 실제로 넣어보고, 잘 작동하는지 확인해봐야 한다.
  • 그러나, 현장에서 새로운 데이터를 구하는 것은 굉장히 어려운 일이기 때문에, 학습에 사용할 훈련 데이터를 훈련 데이터 셋(Train dataset)과 테스트 데이터 셋(Test dataset)으로 나누는 것이 가장 현실적이다.
  • 훈련 데이터셋으로 학습을 진행하고, 테스트 데이터 셋을 이용해서 모델을 평가하면, 테스트 데이터 셋에 대한 모델의 오차 비율을 얻을 수 있으며, 이를 일반화 오차(Generalization error) 또는 외부 샘플 오차(Out-of-sample error)라고 한다.
  • 훈련 데이터에 대한 오차는 낮으나, 테스트 데이터에 대한 오차인 일반화 오차가 높다면, 이는 모델이 훈련 데이터에 과적합(Overfitting) 되었다는 것을 의미한다.
  • 위 경우, 훈련 데이터에 과적합되는 것을 피하기 위해, 일반화 오차를 줄이는 방향으로, 하이퍼 파라미터 튜닝 등 다양한 작업을 수행할 수 있는데, 이 경우 단순히 결과 지표를 뽑기 위해 사용해야 하는 테스트 데이터 셋에 모델이 과적합되는 문제가 발생할 수 있다.

1.1. 검증 데이터 셋(Validation dataset)

  • 검증 데이터 셋은 모델이 시험 셋(Test dataset)에 과대적합되는 것을 막기 위해, 훈련 데이터 셋에서 일부분을 추가로 추출하여, 모델 적합도의 기준으로 사용하는 데이터 셋이다.
  • 전체 데이터 셋의 규모가 적거나, 검증 데이터 셋이 훈련 데이터 셋을 대표하지 못한다면, 데이터가 편향되는 결과가 발생할 수 있으므로, 교차 검증(Cross-Validation)과 같은 다양한 방법으로, 이를 해결한다.
  • 물론, 데이터 셋의 양이 굉장히 많다면, 교차 검증은 현실적으로 사용이 불가능하며, 훈련 셋의 각 클래스별 비율에 맞는 데이터 셋을 추출하여, 대표성이 높은 검증 셋을 뽑을 수 있다.
  • 이외 데이터 셋에 대한 보다 자세한 정보를 알고자 하면, 이전 포스트인 "Tensorflow-1.0. 기초(1)-데이터 셋 만들기"와 "Tensorflow-3.2. 이미지 분류 모델(2)-검증 셋(Validation set)"을 참고하기 바란다.

 

 

 

 

 

2. 하이퍼 파라미터 튜닝

  • 하이퍼 파라미터 튜닝은 위에서 만든 학습 셋(Train set), 검증 셋(Validation set), 시험 셋(Test set)을 사용하여, 모델을 평가하며, 모델의 일반화 오차가 최소화되는 하이퍼 파라미터를 탐색하는 과정이다.
  • 앞서 이야기 한, 학습 셋으로 만들어진 모델의 하이퍼 파라미터를 시험 셋에 과대적합 시키는 현상을 막기 위해, 검증 셋을 사용하는 것을 홀드 아웃 검증(Holdout validation)이라고 한다.
  • 하이퍼 파라미터를 탐색하는 과정은 그리드 서치(Greed search), 랜덤 서치(RandomizedSearch), 베이지안 옵티마이저(Baysian optimizer) 등의 방법을 통해 가장 적합한 하이퍼 파라미터를 찾아낸다.

 

 

 

 

 

3. 공짜 점심 없음(No free lunch, NFL) 이론

  • 1996년 David Wolpert가 발표한 "The Lack of A Priori Distinctions Between Learning Algorithms."에서 나온 공짜 점심 없음 이론은, 경험해보기 전까진 해당 데이터에 더 잘 맞을 것이라고 보장할 수 있는 모델은 존재하지 않는다는 내용이다.
  • 머신러닝 알고리즘에서 사용되는 모델들은 관측된 데이터들을 간소한 것으로, 새로 들어온 데이터에서 일반적이지 않을 것으로 보이는 불필요한 세부 사항을 제거하는 것이다.
  • 이러한 불필요한 세부 사항은 각 머신러닝 알고리즘이 만들어진 원리에 따라 다르게 정해진다.
  • 때문에, 가장 핫한 머신러닝 모델 중 하나인 딥러닝 모델의 성능이 아무리 좋다고 할지라도, 경우에 따라서는 훨씬 단순한 선형 회귀 모델이 해당 데이터에는 더 적합할 수도 있다는 의미이다.
  • 때문에 해당 데이터에 관련된 다양한 모델들을 평가해보고, 이를 비교하여, 적절한 모델을 선택하도록 해야 한다.
  • 이는, 모든 머신러닝 모델을 각 데이터에 적용하라는 것이 아니라, 데이터의 복잡도, 데이터의 크기, 해당 데이터 성격에 관련된 모델 등 다양한 요소를 감안하여, 적합한 머신러닝 모델을 일부 선택한 후, 이들을 비교해보라는 의미이다.

 

 

[참고 서적]

728x90
반응형
728x90
반응형

머신러닝 사용 시 주의해야 할 사항들

 머신러닝은 데이터를 기반으로 머신러닝 알고리즘을 학습시키기 때문에 데이터나 알고리즘에 다음과 같은 문제가 있다면, 머신러닝을 사용했을 때, 제대로 된 결과를 기대하기 어렵다.

 

 

 

 

1. 충분하지 않은 양의 훈련 데이터

  • 머신러닝은 말 그대로, 데이터를 이용해서 그 안에 숨어 있는 패턴을 찾아내는 것이다.
  • 예를 들어, 고양이 사진을 이용해서, 고양이를 찾을 수 있는 모델을 만든다고 가정해보자.
  • 고양이 사진이 매우 적다면, 시스템은 단순하게 꼬리, 다리 4개를 가진 모든 대상을 고양이라고 판단할 수도 있다.
  • 고양이의 종류는 매우 다양하고, 다른 동물과 달리 고양이만 가진 특징을 컴퓨터가 찾아내기 위해서는 매우 많은 양의 데이터가 필요하다.

 

 

 

 

 

2. 대표성이 없는 훈련 데이터

  • 머신러닝은 훈련 데이터를 이용해서 시스템을 학습시키고, 그 학습된 시스템에 새로운 데이터가 들어왔을 때, 학습된 내용을 바탕으로 새로운 데이터를 분류하게 된다.
  • 만약, 훈련에 사용한 데이터가 대표성이 없다면, 새로운 데이터를 처음 보는 종류의 데이터로 보고, 잘못된 분류를 할 가능성이 높다.
  • 예를 들어, 우리가 길거리에서 흔히 볼 수 있는 길고양이 즉, 코리안 숏헤어만 고양이로 학습시킨다면, 고양이는 털이 짧고, 얼굴이 동그랗고, 귀가 뾰족하며, 주황색, 흰색, 회색, 검은색 털을 갖는다라고 패턴을 익혀버릴 수 있다. 이러한 경우, 털이 길거나, 귀가 둥글거나, 접혀있는 다른 종류의 고양이들을 다른 동물로 인식할 수 있다.

2.1. 데이터의 양과 대표성

  • 샘플링 잡음(Sampling noise): 데이터의 양이 매우 적다면, 대표성이 매우 적은 데이터만 뽑힐 확률이 높아진다. 고양이 사진 10,000장보다 고양이 사진 100장에 털이 짧은 고양이 사진만 뽑힐 확률이 매우 높기 때문이다.
  • 샘플링 편향(Sampling bias): 데이터의 양이 많다고해서, 반드시 대표성이 높은 것은 아니다. 10,000장의 고양이 사진에 털이 짧은 고양이 사진이 9,500장, 500장에만 털이 긴 고양이 사진이 들어갈 가능성도 있기 때문이다.
  • 물론, 추출한 데이터의 양이 매우 많고, 데이터를 생성하는 방법에도 이상이 없는 경우, 위와 같은 문제가 발생할 가능성은 크게 낮아진다.
  • 예를 들어, 어떤 TV 프로를 즐겨보는지 설문 조사를 할 때, 유치원생들만을 대상으로 한다면, 한국인들은 뽀로로와 핑크퐁을 굉장히 좋아하는 특이한 사람들이라고 판단할 위험이 있으며, 면접을 보는 사람들을 대상으로 학업 성적에 대해 설문 조사를 하면, 자신에게 불이익이 갈까 봐 응답을 하지 않거나, 거짓 대답을 할 가능성이 있다.

 

 

 

 

 

3. 품질이 낮은 훈련 데이터

  • 훈련 데이터에 결측값(Missing value)이나 이상 값(Outlier) 등이 굉장히 많이 존재한다면, 머신러닝을 통해 제대로 된 패턴을 찾아내지 못할 수 있다.
  • 예를 들어, 수질 검사기의 센서에 수중 식물이나 오염 물질이 붙어 있어, 실제 수치와 전혀 다른 결과가 생성될 수 있다.
  • 혹은, 키를 적는 문항에 몸무게를 적거나, 손으로 쓴 설문 문항을 전산화하는 과정에서 180을 1800이라고 잘못 입력할 수도 있다.
  • 때문에 데이터 분석가는 분석을 하기 전에, 결측값이나 이상 값들이 발생한 이유를 파악하고, 측정 도구의 신뢰도 검사를 통해, 제대로 된 데이터가 수집되고 있는지 파악하고, 그 성격에 맞는 조치를 취해야 한다.
  • 예를 들어, 결측값이 발생한 이유가 어떤 의도에 의해 발생하여, 결측 값에 숨겨진 패턴이 존재할 가능성이 높다면, 설문조사를 재실시해 누락된 부분을 채워 넣거나, 그 양이 매우 적으며 단순 실수에 의해 발생한 결측 값이라면, 일괄 제거 혹은 다중 대체법(Multiple Imputation) 등으로, 누락된 결측 값을 추론하여 계산할 수도 있다.

 

 

 

 

 

4. 관련 없는 특성이 들어가 있는 경우


Garbarge in, garbage out - "쓰레기가 들어가면 쓰레기가 나온다."


  • 정보통신분야에서 유명한 위 문구는 머신러닝 분야에서도 그대로 통용된다.
  • 고양이 사진을 이용해서 고양이를 학습시키려고 하는데, 쓸 데 없이 모든 고양이 사진에 화분이 들어가 있다면, 화분을 중요한 패턴으로 인식할 수 있다.
  • 때문에 훈련 데이터에는 관련 없는 특성을 최소화시키고, 관련 있는 특성을 최대화할 필요가 있다.

4.1. 특성 공학(Feature engineering)

  • 머신러닝을 포함한 모든 데이터 분석에서 가장 중요한 부분은 전처리로, 훈련에 사용할 좋은 특성을 찾는 과정을 특성 공학이라고 한다.
  • 특성 선택(Feature selection): 전체 특성 중 훈련에 가장 유용한 특성을 선택한다.
  • 특성 추출(Feature extraction): 특성을 결합하여 더 유용한 특성을 만들어낸다. 특성 간 성격을 이용하여, 특성의 정보를 축소시킬 수도 있고, 차원축소(PCA) 알고리즘을 사용하여 특성을 추출할 수도 있다.

 

 

 

 

 

5. 훈련 데이터 과대적합(Overfitting)

  • 과대적합(Overfitting)은 훈련 데이터에 대해 지나치게 최적화되어, 훈련 데이터는 잘 분류하지만, 새로운 데이터는 제대로 분류하지 못하는 현상을 말한다.
  • 과대적합은 데이터의 양이 적거나, 편향되어 있는 경우, 필요 이상으로 학습을 시키는 경우(Epochs가 필요치 보다 큰 경우), 주로 발생한다.
  • 예를 들어, 훈련 데이터의 모든 고양이 사진의 고양이 목에 리본이 달려 있다면, 목에 리본이 없는 고양이들을 고양이가 아니라고 분류할 가능성이 올라간다.

5.1. 과대적합을 피하는 방법

  • 과대적합은 훈련 데이터에 있는 잡음의 양에 비해 모델이 너무 복잡할 때 발생한다. 이를 해결하는 방법은 다음과 같다.
  • 규제(Regularization)를 사용하여 모델을 단순화시키거나, 모델 파라미터의 수가 적은 보다 단순한 모델을 사용한다.
  • 훈련 데이터에 있는 특성의 수를 차원축소(PCA)와 같은 방법으로 줄인다.
  • 훈련 데이터의 잡음을 줄인다(오류 데이터 수정 및 이상치 제거).
  • 훈련 데이터를 더 많이 모은다.

 

 

 

 

 

6. 훈련 데이터 과소적합(Underfitting)

  • 과소적합은 과대적합의 반대로 모델이 너무 단순해서 학습 데이터에 숨겨진 패턴을 제대로 찾아내진 못한 경우이다.

6.1. 과소적합을 피하는 방법

  • 모델 파라미터의 수가 더 많은 강력한 모델을 사용한다.
  • 보다 더 좋은 특성을 제공한다.
  • 규제의 양을 줄인다.

 

 

[참고 서적]

 

 

 

 지금까지 머신러닝 시 주의 사항에 대해 알아보았다. 머신러닝은 기본적으로 데이터를 이용해서, 데이터 속에 숨어 있는 패턴을 찾아내는 것이므로, 데이터의 품질이 떨어지거나, 데이터의 양이 지나치게 적은 경우, 그 역할을 제대로 해낼 수 없다. 

 데이터 분석에서 제일 중요한 것은 데이터의 품질 관리이므로, 데이터에 대한 파악과 사용하고자 하는 머신러닝 알고리즘에 대한 지식이 선행되어야만, 머신러닝을 사용한 데이터 분석을 제대로 수행할 수 있다.

728x90
반응형
728x90
반응형

사례 기반 학습과 모델 기반 학습

 지난 포스트에서는 데이터의 양, 분석 환경 등에 따라 학습 방법을 달리하는 배치 학습과 온라인 학습에 대해 알아보았다.

 이번 포스트에서는 머신러닝 모델이 새로운 데이터가 들어왔을 때, 학습된 내용을 기반으로 어떤 방식으로 일반화(Generalize)하는지에 따라 분류되는 "사례 기반 학습"과 "모델 기반 학습"에 대해 알아보도록 하겠다.

 

 

 

 

1. 사례 기반 학습(Instance-based learning)

  • 사례 기반 학습은 말 그대로 학습된 사례들을 기억하는 것으로, 시스템이 훈련 데이터를 기억함으로써 학습한다.
  • 새로운 데이터가 들어오는 경우, 학습된 데이터(일부 또는 전체)와 새로운 데이터의 유사도를 측정하여, 입력된 새로운 데이터를 가장 유사도가 높은 기존 학습 데이터의 클래스로 분류한다.
  • 유사도 측정(Similarty measurement)은 데이터에 따라 약간 다르게 진행되며, 텍스트 데이터가 대상인 경우, 공통으로 포함된 단어의 수를 기반으로 분류한다.
  • 예를 들어, 스팸 메일과 공통으로 포함된 단어가 많은 경우, 스팸 메일로 분류한다.

 

 

 

 

 

2. 모델 기반 학습(Model-based learning)

  • 모델 기반 학습은 데이터 셋에 적합한 모델을 사용하여 모델을 만들고, 모델을 이용하여 새로운 데이터를 어떻게 분류할지 예측(Prediction)한다.
  • 모델 기반 학습은 데이터 셋에 따라 사용되는 모델이 다르므로, 데이터 셋에 적합한 모델을 찾는 모델 선택(Model selection) 과정이 필요하다.
  • 모델에는 다양한 모델 파라미터(Model parameter)가 존재하며, 데이터에 최적화되는 모델 파라미터를 찾아내는 것이 모델 훈련(Training)이다.
  • 학습 데이터 셋을 훈련시키는 과정에서 선택된 모델의 성능이 얼마나 좋은지를 "적합도 함수(Fitness function, 효용 함수 - Utility function)"로 평가할 수 있으며, 얼마나 나쁜지에 대해서는 "비용 함수(Cost function, 손실 함수 - loss function)"로 평가할 수 있다.
  • 모델 파라미터는 일반적으로 손실 함수를 최소화시키는 방향으로 진행되며, 손실 함수를 최소화시키는 파라미터를 찾는 과정을 훈련(Training)이라 한다.
  • 모델 파라미터는 머신러닝 모델이 학습 과정에서 찾아가는 파라미터이며, 사용자가 직접 설정해주는 학습률(Learning rate) 같은 파라미터는 모델 파라미터와 구분하기 위해, 하이퍼 파라미터(Hyper parameter)라고 한다.
  • 모델 기반 학습은 다음과 같이 진행된다.
  1. 데이터 분석
  2. 데이터에 적합한 모델 선택
  3. 훈련 데이터(Training data)로 모델 훈련 - 비용 함수를 최소화하는 모델 파라미터 탐색
  4. 학습이 끝난 모델에 새로운 데이터를 적용해 예측(Predict) - 학습 데이터로 학습된 모델을 이용해서 학습 데이터와 분리된 시험 데이터(Test data)가 얼마나 잘 예측되는지를 본다.
  • 모델 학습 기반은 모델에 종속되어 새로운 데이터를 추론하므로, 모델이 적합하지 않거나, 데이터가 부족한 경우 더 좋은 모델을 탐색해야 하고, 그 모델에 맞는 데이터를 더 수집해야 한다.
  • 모델의 코드 작성 난이도는 낮지만, 그 모델이 구동되는 정확한 원리를 이해하지 못한다면, 제대로 된 결과를 도출하지 못할 가능성이 높다.

 

 

[참고 서적]

 

 

 

 지금까지 사례 기반 학습과 모델 기반 학습에 대해 알아보았다. 사례 기반 학습과 모델 기반 학습 모두 데이터를 기반으로 하지만, 사례 기반 학습은 데이터에서 찾아낸 특정 패턴이 얼마나 유사한지(동일 패턴이 얼마나 많이 등장하는지)에 따라 분류를 하고, 모델 기반 학습은 수많은 머신러닝 모델의 모델 파라미터를 데이터에 맞게 만들어 예측을 한다.

 추후 학습하게 될 머신러닝 모델들은 모두 모델 기반 학습이므로, 사례 기반 학습보다는 모델 기반 학습을 눈여겨보도록 하자.

728x90
반응형
728x90
반응형

배치 학습과 온라인 학습

 지난 포스트에서는 학습 데이터를 어떻게 입력하는지에 따라 분류되는 지도 학습, 비지도 학습, 준지도 학습, 강화 학습에 대해 알아보았다.

 이번 포스트에서는 실시간으로 학습이 가능한지 여부에 따라 나뉘는 배치 학습과 온라인 학습에 대해 알아보도록 하겠다.

 

 

 

 

1. 배치 학습(Batch learning)

  • 배치 학습은 한번에 모든 훈련 데이터를 학습시키는 방법으로, 시간과 자원을 많이 소모하므로, 일반적으로 오프라인 환경에서 수행되므로, 오프라인 학습(Offline learning)이라고도 한다.
  • 학습은 런칭 전에 일어나고, 제품에 학습된 내용을 적용하면, 더 이상의 학습 없이 사용만 된다.
  • 새로운 데이터가 등장하여, 머신을 재학습 하고자 하는 경우, 이전 데이터에 새로운 데이터를 포함한 전체 데이터를 학습시키고, 학습된 새로운 모델을 사용해야 한다.

  • 이전 포스트("머신러닝-1.0. 전통적인 기법과 머신러닝의 차이")에서 말한 것처럼 새로운 패턴에 대하여 자동화 하는 경우, 주기적으로 자동적으로 재학습을 할 수 있다.
  • 그러나, 빅데이터에 대하여, 머신러닝 알고리즘을 사용하는 경우, 그 데이터의 양이 지나치게 많기 때문에 학습 시간이 작게는 몇 시간에서 길게는 몇 주, 한 달 이상의 시간이 소모될 수도 있기 때문에 쉬운 작업은 아니다.
  • 데이터의 양이 매우 많아 학습 시간이 지나치게 길거나, 탐사 로봇 같이 자원이 지나치게 한정된 상황, 주식 가격표 같은 실시간 반영이 필요한 상황에서는 배치학습이 아닌 능동적인 학습이 필요하다.

 

 

 

 

 

2. 온라인 학습(Online learning)

  • 온라인 학습은 일반적으로, 학습이 끝나 제품화가 된 모델에 대하여, 미니배치(Mini-batch)라 부르는 작은 묶음 단위의 데이터를 주입하여 모델을 학습시키는 방법이다.
  • 미니 배치의 크기가 작기 때문에 학습 단계가 빠르고 비용이 적게 들기 때문에 모델은 데이터가 도착하는 대로 즉시 학습을 할 수 있다.
  • 점진적으로 학습이 일어나기 때문에 점진적 학습(Incremental learning)이라고도 하며, 온라인 학습은 오프라인으로도 시행되기도 하므로(외부 메모리 학습), 온라인 학습이라는 용어보다 점진적 학습이 정확한 명칭이라 할 수 있다.
  • 온라인 학습은 연속적으로 데이터를 받고 빠른 변화에 스스로 적응해야 하거나, 자원이 매우 한정된 환경에 적합하다.
  • 온라인 학습 모델은 새로운 데이터 샘플을 학습하면, 학습이 끝난 데이터는 더 이상 필요하지 않기 떄문에 보관하지 않아도 되므로, 저장 공간을 많이 아낄 수 있다.

 

2.1. 외부 메모리 학습(Out-of-core learning)

  • 빅 데이터 분석 시, 데이터의 양이 지나치게 커서, 컴퓨터의 메모리로 감당되지 않는 경우가 종종 있으며, 이때에도 온라인 학습 알고리즘이 사용된다.
  • 데이터 일부를 읽어 들여 머신러닝 알고리즘이 학습하며, 전체 데이터가 모두 적용될 때까지 일부를 학습하는 과정을 반복한다.
  • 온라인 학습 모델에서는 학습률(Learning Rate)이 가장 중요한 하이퍼 파라미터로 작동하며, 이는 모델이 변화하는 데이터에 얼마나 빠르게 적응할지를 이야기한다.
  • 학습률이 높은 경우, 시스템이 데이터에 빠르게 적응하나, 과거의 데이터를 금방 잊는다.
  • 학습률이 낮은 경우, 시스템의 관성이 커져 더 느리게 학습되지만, 노이즈나 대표성이 없는 데이터 포인트에 덜 민감해진다.

 

2.2. 온라인 학습의 문제점

  • 나쁜 데이터가 주입되었을 때, 시스템의 성능이 점진적으로 감소하게 된다.
  • 예를 들어, 실시간 추천 알고리즘에서 누군가가 자신이 만든 콘텐츠나 제품을 상위에 노출시키고자, 자신의 의도를 담은 데이터를 다량 생성할 수 있다. 이 경우, 모델은 이 데이터를 학습하여, 실제 알고리즘에서 노출시키고자 대상이 아닌, 누군가가 의도적으로 생성한 데이터를 노출시킬 수 있다.
  • 때문에, 온라인 학습에서는 시스템을 지속적으로 모니터링하고, 성능 감소가 감지되면, 즉시 학습을 중지시키고, 성능 감소가 이루어지기 전 상태로 되돌려야 한다.
  • 또는 이상 감지 머신러닝 알고리즘을 데이터 수집 앞에 넣어, 비정상 데이터가 온라인 학습 모델에 들어가지 않게 막을 수도 있다.

 

 

[참고 서적]

 

 

 지금까지 배치 학습과 온라인 학습에 대해 알아보았다. 배치 학습은 일반적인 머신러닝 알고리즘 학습 방법이나, 수많은 환경(특히 빅데이터를 사용하는 환경)에서 배치 학습을 사용하지 못하는 경우가 많다. 이런 경우에는 온라인 학습 방법을 사용하지 않는다면, 제대로 된 학습을 하지 못할 수 있다.

728x90
반응형
728x90
반응형

지도 학습 & 비지도 학습 & 준지도 학습 & 강화 학습

 지난 포스트에서는 전통적인 프로그래밍 기법과 머신러닝 기법을 직접 비교해보았다. 이번 포스트에서는 머신러닝 시스템에서 사람이 감독을 하는지 하지 않는지에 따라 나뉘는 지도 학습, 비지도 학습, 준지도 학습, 강화 학습이 무엇인지 알아보도록 하겠다.

 지도, 비지도, 준지도, 강화 학습은 사람의 감독 형태가 어떠한지와 정보량에 따라 구분된다.

 

 

 

1. 지도 학습(Supervised Learning)

  • 지도 학습에는 답지인 레이블(Label) 데이터가 필요하다.
  • 지도 학습의 대표적인 예는 분류(Classification)로, 숫자 손글씨 인식 모델을 만드는 경우, 손으로 쓴 숫자에 해당하는 클래스(Class) 0, 1, 2, 3,..., 9를 레이블로 붙이고, 머신은 각 레이블에 대한 패턴을 학습한다. 새로 들어온 데이터가 특정 클래스에 속하는 패턴을 가지고 있는 경우, 각 클래스에 속할 확률을 계산한다.
  • 또는, 사진을 바탕으로, 고양이를 인식하는 모델을 만들기 위해, 고양이 사진에는 고양이라는 의미로 0으로 범주화(Class) 하고, 나머지는 고양이가 아니다는 의미로 1로 범주화한다.
  • 통계분석의 꽃인 회귀분석(Regression Analysis)은 대표적인 지도 학습 방법 중 하나이다. 독립변수(Independent variable)인 성별, 하루 평균 운동 시간, 식사량, 간식 섭취량, 음료수 섭취량 등이 종속변수(Dependent variable)인 체질량지수(BMI)에 영향을 주는 정도를 학습하여, 체질량지수 모델을 만들어낼 수도 있다.
  • 대표적인 지도 학습 알고리즘은 다음과 같다.
  1. K-최근접 이웃(K-nearest neighbors)
  2. 선형 회귀(Linear ragression)
  3. 로지스틱 회귀(Logistic regression)
  4. 서포트 벡터 머신(Support vector machine, SVM)
  5. 의사 결정 나무(Decision tree)와 랜덤 포레스트(Random forest)
  6. 신경망(Neural networks)

 

 

 

 

 

2. 비지도 학습(Unsupervised learning)

  • 훈련 데이터에 레이블 데이터가 없이 학습을 진행하므로, 머신은 어떤 도움 없이 학습을 해야 한다.
  • 비지도 학습은 크게 3개 알고리즘에 특화되어 있다.
  1. 군집화(Clustering)
  2. 시각화와 차원 축소(Visualization & Dimensionality reduction) 
  3. 연관 규칙 학습(Association rule learning)

 

2.1. 군집화(Clustering)

  • 군집화는 데이터에 있는 패턴을 찾고, 그 패턴을 기반으로 n개의 집단으로 데이터를 나눈다.
  • 예를 들어, 특정 온라인 쇼핑몰에 방문하는 사람의 메타 데이터를 이용하여, 군집화를 실시한다면, 해당 온라인 쇼핑몰의 시간대별 방문자의 특징을 이용해, 각 집단이 선호하는 것에 대해 마케팅을 할 수 있다.
  • 대표적인 알고리즘은 다음과 같다.
  1. k-평균(k-means)
  2. DBSCAN
  3. 계층 군집 분석(Hierarchical cluster analysis, HCA)
  4. 원-클래스 서포트 벡터 머신(One-class SVM)
  5. 아이솔레이션 포레스트(Isolation forest)

 

2.1.1 이상치 탐지와 특이값 탐지(Outlier detection & Novelty detection)

  • 이상치 탐지(Outlier detection): 학습 시, 대부분의 데이터를 정상 데이터로 입력하여(모든 데이터를 정상 데이터로 사용하는 것보다. 소량의 이상치 데이터를 섞어 넣는 것이 보다 좋은 결과를 낸다는 이슈가 있다), 새로 입력된 데이터가 정상 데이터로부터 벗어나는 경우, 이상치로 탐지한다.
  • 특이값 탐지(Novelty detection): 모든 데이터를 특이값이 아닌 데이터로 학습시켜, 새로운 데이터가 들어갔을 때, 학습되지 않은 데이터가 들어간다면, 이를 특이값으로 탐지한다. 예를 들어, 시내에서 돌아다니는 사람들에 대해 학습을 하는 경우, 인형탈을 쓰고 돌아다니는 사람을 제외하고 학습을 시킨다면, 인형탈을 쓰고 돌아다니는 사람을 인식했을 때, 다른 사람들과 크게 다른 특이값으로 탐지하게 된다.
  • 이상치 탐지와 특이값 탐지는 학습 데이터에서 벗어나는 대상을 탐지하는 것이므로, 이 역시 전체 군집과 그 밖의 군집으로 나누는 군집화라고 할 수 있다.

 

2.2. 시각화와 차원 축소(Visualization & Dimensionality reduction) 

  • 시각화(Visualization): 레이블이 존재하지 않는 대규모의 고차원 데이터를 입력하면, 도식화가 가능한 2D나 3D 표현을 만들어내며, 해당 알고리즘은 가능한 구조를 그대로 유지(입력 공간에서 떨어져 있던 클러스터는 시각화된 그래프에서 겹쳐지지 않게 유지된다.)하려고 하기 때문에 데이터가 어떻게 조직되어 있고, 예상하지 못했던 패턴의 존재 등을 탐색할 수 있다.
  • 차원 축소(Dimensionality reduction): 정보의 손실을 최소화시키면서, 데이터를 간소화시키는 것으로, 관련이 깊은 특성을 하나로 합치는 작업이다.
  • 주어진 특성을 조합해 새로운 특성을 생성하므로 특성 추출(Feature extraction)이라고도 한다.
  • 시각화와 차원 축소 모두, 전체 데이터에서 정보 손실을 최소화하면서, 데이터를 간소화시키는 과정이 존재하기 때문에 상당히 유사하다.
  • 대표적인 알고리즘은 다음과 같다.
  1. 주성분 분석(Principal component analysis, PCA)
  2. 커널 주성분 분석(Kernel PCA)
  3. 지역적 선형 임베딩(Locally-Linear embedding, LLE)
  4. t-SNE(t-distributed stochastic neighnor embedding)

 

2.3. 연관 규칙 학습(Assiciation rule learning)

  • 대량의 학습 데이터에서 특성(Feature) 간 관계를 찾아내는 것이다.
  • 예를 들어, 마트에서 치킨이나 감자튀김 같은 기름진 음식을 구매한 사람은 맥주를 구매하는 경향이 있으므로, 고객의 동선에 맥주와 맥주 안주거리를 순차적으로 배치할 수 있다.
  • 대표적인 알고리즘은 다음과 같다.
  1. 어프라이어리(Apriori)
  2. 이클렛(Eclat)

 

 

 

 

 

3. 준지도 학습(Semisuplervised learning)

  • 데이터에 레이블을 다는 것은 시간과 비용이 매우 많이 드는 작업이기 때문에 레이블이 있는 샘플을 찾는 것은 쉽지 않다. 준지도 학습은 지도 학습과 비지도 학습의 사이에 있는 학습 방법으로, 레이블이 존재하는 데이터와 존재하지 않는 데이터 모두를 훈련에 사용하는 것이다.
  • 레이블이 없는 다량의 데이터에 적은 양의 레이블이 존재하는 데이터를 포함시키는 경우, 학습 정확도가 개선되는 경향이 있다고 한다.
  • 사용자가 올린 사진을 자동으로 정리해주는 구글 포토(Google Photo)가 대표적인 예로, 사람들이 다량의 사진을 올리는 경우, 학습된 패턴을 이용해 사진들을 군집화하고, 사진에 등장한 사람들에 대해 Labeling을 해주면, 특정 인물이 있는 사진들만 찾을 수도 있다.
  • 대부분의 준지도 학습 알고리즘은 지도 학습과 비지도 학습 알고리즘의 조합으로 이루어져 있으며, 대표적인 준지도 학습인 심층 신뢰 신경망(Deep belief networks, DBN)제한된 볼츠만 머신(Restricted Boltzmann machine, RBM)을 기반으로 순차적으로 학습한 후, 지도 학습 방식으로 세밀하게 조정된다.

 

 

 

 

 

4. 강화 학습(Reinforcement learning)

  • 강화 학습은 지도 학습, 비지도 학습과 상당히 다른 알고리즘으로, 학습하는 머신을 에이전트(Agent)라 부르고, 환경(Environment)을 관찰해 행동(Action)을 실행하고, 그 결과로 보상(Reward)이나 벌점(Penalty)을 받는다.
  • 강화 학습은 시간이 지나면서 가장 큰 보상을 얻기 위해, 정책(Policy)이라 부르는 최상의 전략을 스스로 학습한다.
  • 정책은 주어진 상황에서 에이전트가 어떤 행동을 선택해야 할지 정의한다.
  • 강화 학습은 딥러닝이라는 단어를 전 세계에 알린 딥마인드(DeepMind)의 알파고(AlphaGo)에서 사용되었다.

 

 

[참고 서적]

 

 

 지금까지 머신러닝의 학습 방식에 따른 차이인 지도 학습, 비지도 학습, 준지도 학습, 강화 학습에 대해 가볍게 알아보았다. 다음 포스트에서는 모델이 점진적으로 학습을 할 수 있는지 여부에 대하여, 머신러닝을 분류하는 배치 학습과 온라인 학습에 대해 알아보도록 하겠다.

728x90
반응형
728x90
반응형

머신러닝이란?

 앞서 인공지능, 머신러닝, 딥러닝에 대해 간략하게 언급해보았다. 해당 내용만으로는 앞으로 우리가 학습해나갈 머신러닝에 대해 구체적으로 알기 어려우므로, 이번에는 전통적인 알고리즘과 머신러닝 알고리즘을 직접 비교해보도록 하겠다.


명시적인 프로그래밍 없이 컴퓨터가 학습하는 능력을 갖추게 하는 연구 분야
- 아서 새뮤얼(Arthur Samuel), 1959 - 

어떤 작업 T에 대한 컴퓨터 프로그램의 성능을 P로 측정하였을 때, 경험 E로 인해 성능이 향상됐다면, 이 컴퓨터 프로그램은 작업 T와 성능 측정 P에 대해 경험 E로 학습한 것이다.
- 톰 미첼(Tom Mitchell) 1998 - 


 머신러닝(Machine Learning)이라는 용어를 만든 아서 새뮤얼은 머신러닝에 대해 컴퓨터가 학습하는 능력을 갖추게 하는 연구 분야라고 하였다. AI 분야의 권위자인 톰 미첼은 경험 E에 의해 어떤 작업 T의 성능을 측정한 P가 향상되게 하는 것이 머신러닝이라 했다.

 머신러닝은 전통적인 프로그래밍 기법과 달리 데이터만을 가지고 그 안에서 패턴을 찾아낸다는 학습이라는 개념을 사용하여, 사용자가 원하는 목적을 이루어내는 것으로, 이번 포스트에서는 전통적인 프로그래밍 기법과 머신러닝 기법을 두고, 어째서 머신러닝 기법이 그토록 강조되고 있는지에 대해 알아보도록 하겠다.

 

 

 

 

 

0. 예시 및 용어 정리

  • 예시 1. 우편에 쓰인 손글씨 인식 모델
  • 예시 2. 이메일 이용자가 스팸 처리한 스팸 메일과 정상 메일 분류 모델

0.1. 용어 정리

  • 훈련 데이터(Training data): 시스템을 학습시키는 데 사용하는 데이터
  • 훈련 사례(Training instance): 각각의 훈련 데이터셋
  • 정확도(Accuracy): 모델의 성능을 측정하는 도구

0.2. 톰 미첼의 정의와 연결

  • 어떤 작업 T: 어떤 목적을 위해 만들어진 알고리즘
  • 경험 E: 훈련 데이터
  • 성능 측정 P: 알고리즘 작성자가 정의한 모델 성능 평가 도구 - 정확도(Accuracy)

 

 

 

 

 

1. 전통적인 프로그래밍 기법

A. 문제 연구

  • 훈련 데이터(training data)에 대하여 통계 분석 방법, 특정 집단에서 공통적으로 발생하는 패턴을 파악한다.
  • "예시 1. 손글씨"의 경우, 손 글씨를 텐서화하고, 그 텐서에서 각 글자마다 각 Row에서 수치들이 어디서 등장하는지. 수치가 흩어진 정도를 이용하여, 각 글자의 경향성을 찾아냈다.
  • "예시 2. 스팸 메일"의 경우, 사용자가 스팸 메일 처리한 메일들에서 "무료", "기회", "굉장한", "대단한"과 같은 특정 단어가 메일의 제목이나 본문에서 자주 등장하는 경향이 있다.

B. 규칙 작성

  • 문제 연구에서 찾아낸 패턴을 바탕으로, 알고리즘을 만들어낸다.
  • "예시 1. 손글씨"의 경우, 앞에서 찾아낸 각 글자의 경향성을 기반으로, 그 패턴에 속하는 경우 해당 단어로, 속하지 않는 경우, 가장 가까운 패턴에 해당하는 단어를 찾아낸다.
  • "예시 2. 스팸 메일" 스팸 메일에서 주로 발생하는 단어들이 제목이나, 본문에서 등장하는 경우, 이를 스팸 메일로 분류한다.

C. 평가

  • 훈련 데이터를 기반으로 만든 알고리즘에 시험 데이터(Test data)를 넣었을 때, 실제 분류와 어느정도 일치하는 분류가 나오는지 확인한다.

 

1.1. 전통적인 프로그래밍 기법의 한계점

  • 연구자가 찾아낸 패턴은 표면적인 패턴일 가능성이 매우 높으므로, 반례가 존재할 가능성이 매우 높으며, 그 반례의 양도 많아, 이들을 하나하나 해결하다 보면 코드도 길어지고, 복잡해진다.
  • 새로운 패턴을 갖는 데이터 셋이 추가 되면, 기존에 해결했던 반례가 되려 문제를 일으키거나, 새로운 반례가 생기는 등 알고리즘에 단순하게 반영하기가 매우 어렵다.
  • 즉, 유지보수 난이도가 매우 높다(산출물 관리를 아주 아주 잘해야 한다.)

 

 

 

 

 

2. 머신러닝 접근 방법

  • 머신러닝은 데이터를 기반으로 하여, 머신러닝 알고리즘 스스로 패턴을 찾아낸다.
  • 물론, 알고리즘이 그 패턴을 제대로 찾아내게 하기 위해서는 연구자가 패턴이 잘 드러나는 방향으로 전처리를 해줘야 한다.
    (아무리 머신러닝 알고리즘의 성능이 뛰어나다 할지라도, 연구자가 그 알고리즘에 맞게 데이터를 세팅해주지 않는다면, 머신러닝 알고리즘의 성능을 전부 이끌어낼 수 없다.)
  • 머신러닝 알고리즘 자체가 패턴을 찾아내기 때문에 사용자가 패턴을 찾아내는 과정이 따로 필요하지 않으며, 머신러닝 알고리즘은 데이터에서 연구자가 인식하지 못하는 패턴마저도 뽑아내므로, 정확도가 상당히 높다.
  • 그러나, 머신러닝 알고리즘이 분류한 것에 대하여 연구자가 인식하는 것은 불가능에 가깝기 때문에 분류 과정에 대해 설명할 수 없다.

 

2.1. 새로운 패턴이 등장하는 경우

  • "예시 2. 스팸 메일"의 경우, "무료", "기회", "굉장한", "대단한"과 같은 특정 단어가 들어간 메일이 자동 스펨 처리되는 것을 눈치챈 발송자가 해당 단어를 사용하지 않고 "Free", "흔치않은", "오늘만"과 같은 기존 사전에 없던 단어를 이용할 수 있다.
  • 또는, "F.R.E.E", "!기!회!", "!@!기!@! 오.늘!! !@!무.료!@! !@!회!@!" 같이 정규표현식이나, 비교적 단순한 알고리즘으로 잡기 어렵게 패턴에 노이즈를 주어 스펨 메일을 보낼 수도 있다.
  • 머신러닝 알고리즘 기반으로 이를 해결하는 경우엔, 머신러닝 알고리즘 스스로 스펨 메일에 자주 등장하는 패턴을 찾아내 해당 패턴이 등장하는 경우를 스펨 처리하므로, 새로운 규칙을 추가하지 않고 동일한 모델로 패턴을 찾아낼 수 있다.
    (물론, 머신이 노이즈를 잘 인지할 수 있도록, 전처리를 해줘야 한다.)

 

2.2. 전통적인 방식에는 없는 알고리즘이 필요한 경우

  • "예시 1. 손글씨 인식"의 경우, 텐서의 Row 별로 수치가 변하는 위치와 편차를 이용해서 구해보겠다고 하였다. 그러나 해당 방법은 사람마다 손글씨의 차이가 매우 크기 때문에 제대로 작동하기 힘들 가능성이 높다.
  • 머신러닝, 그중에서도 딥러닝의 CNN을 이용한다면, 이미지 데이터의 특징 자체를 찾아낼 수 있다.
  • 즉, 전통적인 알고리즘은 특정 상황에 맞는 특정 알고리즘을 써야 하지만, 머신러닝을 사용하는 경우, 데이터 성격에 맞는 머신러닝 알고리즘을 사용하면, 수치로 표현하기 힘든 특징도 찾아낼 수 있다.

 

2.3. 데이터 마이닝(Data Mining)

  • 연구자는 머신러닝을 통해 그동안 알지 못했던 새로운 사실을 알 수도 있다.
  • 빅데이터를 사용하여 머신러닝 알고리즘을 훈련하는 경우, 기존에 노이즈인지 알고 제거했던 데이터에서 패턴이 존재한다는 것을 알 수 있다.
  • 또는, 어떤 패턴이 분류에서 가장 결정적인 역할을 했는지 찾아낼 수도 있다.

 

2.4. 머신러닝의 장점 정리

  1. 전통적인 프로그래밍 기법보다 머신러닝 모델의 코드가 간단하며, 유지보수가 쉽다.
  2. 전통적인 알고리즘의 반례로 인한 알고리즘 복잡도 증가가 없다.
  3. 전통적인 방식으로 해결할 수 없는 문제도 해결할 수 있다.
  4. 데이터의 변화에 쉽게 적응할 수 있다.
  5. 복잡한 문제와 대량의 데이터를 통해 그간 알지 못했던 아이디어를 얻을 수 있다.

 

 

[참고 서적]

 

 

 지금까지 전통적인 프로그래밍 기법과 머신러닝의 차이점에 대해 알아보았다. 다음 포스트에서는 지도 학습과 비지도 학습, 준지도 학습, 강화 학습에 대해 알아보도록 하겠다.

728x90
반응형
728x90
반응형

 이전 포스트까지 Wide & Deep Learning 모델을 사용해서 다중 입력 모델을 만들어보았다. 이번엔 이전 모델에 추가로 출력층을 추가하여 출력층을 2개로 만들어 보도록 하자.

 

 

다중 출력 모델

  • 일반적으로 다중 출력 모델을 사용하는 이유는 다음과 같다.

  1. 프로세스가 서로 독립적인 경우:
    동일한 데이터 셋에서 두 개 이상의 출력층을 생성하지만, 출력된 결과는 전혀 다른 작업을 위해 실행되는 경우로, 예를 들어 인터넷 사용 Log 데이터를 기반으로, 대상의 관심사 파악(분류 모델)과 인터넷 사용률 예측(회귀 모델)은 별개의 목적을 위해 진행된다.
  2. 프로세스가 한 목적을 위해 상호 보완적으로 이루어지는 경우:
    하나의 목적을 이루기 위해 두 개 이상의 출력 값이 필요하여, 동일한 데이터 셋에서 두 개 이상의 출력층을 뽑아내는 경우로, 예를 들어 영상 속 물건을 인식하는 모델을 만든다면, 영상 속 물건의 위치(회귀 모델)와 물건의 종류(분류 모델)를 동시에 파악해야 한다.
  3. 규제 도구로써의 다중 출력 모델:
    하위 네트워크가 나머지 네트워크에 의존하지 않고, 그 자체로 유용한 성능을 내는지 확인을 하기 위한 규제 기법으로 사용

 이번 포스트에서는 Wide & Deep Learning model에 다중 출력 모델을 규제 도구로써 사용해보도록 하자.

 

 

 

 

1. 규제 도구로써의 다중 출력 모델

  • 보조 출력층(Auxiliary Output)을 통해, 하위 네트워크가 나머지 네트워크에 의존하지 않고 그 자체로 유용한 것을 학습하는지 확인한다. 위 모델대로라면, Deep model에서의 출력된 값이 Wide model과의 연결로 인해, 원하는 결과를 뽑아내는 것인지, Deep model으로 인해 그러한 결과가 나왔는지 확인할 수 있다.
  • 이를 통해, 과대 적합을 감소시키고, 모델의 일반화 성능이 높이도록 학습에 제약을 가할 수 있다.

 

 

 

 

2. 모델 생성 이전까지의 코드 가져오기

  • 이전 포스트에서 만들었던, 모델 이전까지의 코드를 모두 가지고 온다.
  • 입력층이 2개일 때는 입력 데이터를 목적에 맞게 나눴어야 했지만, 이번엔 출력층이 다른 데이터를 출력하는 것이 아니라, 보조 출력층(Auxiliary Output)과 주 출력층(Main Output)이 동일한 정답에 대해 어느 정도의 손실 값과 정확도를 내보내는지 알고 싶은 것이므로, Label dataset을 나누지 않아도 된다.
#################################### Import Module ####################################
from sklearn.datasets import fetch_california_housing
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import callbacks
from tensorflow.keras.optimizers import Adam
from tensorflow.keras.layers import (Input, Dense, concatenate)
########################################################################################





#################################### Import Dataset ####################################
# 캘리포니아 데이터 가져오기
Rawdict = fetch_california_housing()
CaliFornia_DF = pd.DataFrame(Rawdict.data, columns=Rawdict.feature_names)
########################################################################################




#################################### Data Handling #####################################
# 데이터를 쪼개기 좋게 변수의 순서를 바꾸자
CaliFornia_DF = CaliFornia_DF[["HouseAge", "Population", "Latitude", "Longitude", "MedInc",
                               "AveRooms", "AveBedrms", "AveOccup"]]

# train, validation, test set으로 쪼갠다.
X_train_all, X_test, y_train_all, y_test = train_test_split(CaliFornia_DF.values, Rawdict.target, test_size = 0.3)
X_train, X_valid, y_train, y_valid = train_test_split(X_train_all, y_train_all, test_size = 0.2)

# 정규화시킨다.
scaler = StandardScaler()
X_train = scaler.fit_transform(X_train)
X_valid = scaler.transform(X_valid)
X_test = scaler.transform(X_test)

# 데이터 셋을 input layer의 수만큼 쪼갠다.
X_train_A, X_train_B = X_train[:, :1], X_train[:,1:]
X_valid_A, X_valid_B = X_valid[:, :1], X_valid[:,1:]
X_test_A, X_test_B = X_test[:, :1], X_test[:,1:]
########################################################################################

 

 

 

 

3. 모델 생성

######################################## Model ########################################
input_A = Input(shape=[1], name = "deep_input")
input_B = Input(shape=[7], name = "wide_input")
hidden1 = Dense(30, activation="relu", name = "hidden1")(input_A)
hidden2 = Dense(30, activation="relu", name = "hidden2")(hidden1)
concat = concatenate([input_B, hidden2], name = "concat")
output = Dense(1, name="main_output")(concat)

# 보조 출력층 생성
aux_output = Dense(1, name="aux_output")(hidden2)
model = keras.Model(inputs=[input_A, input_B], outputs=[output, aux_output])
########################################################################################
>>> model.summary()
Model: "model"
__________________________________________________________________________________________________
Layer (type)                    Output Shape         Param #     Connected to                     
==================================================================================================
deep_input (InputLayer)         [(None, 1)]          0                                            
__________________________________________________________________________________________________
hidden1 (Dense)                 (None, 30)           60          deep_input[0][0]                 
__________________________________________________________________________________________________
wide_input (InputLayer)         [(None, 7)]          0                                            
__________________________________________________________________________________________________
hidden2 (Dense)                 (None, 30)           930         hidden1[0][0]                    
__________________________________________________________________________________________________
concat (Concatenate)            (None, 37)           0           wide_input[0][0]                 
                                                                 hidden2[0][0]                    
__________________________________________________________________________________________________
main_output (Dense)             (None, 1)            38          concat[0][0]                     
__________________________________________________________________________________________________
aux_output (Dense)              (None, 1)            31          hidden2[0][0]                    
==================================================================================================
Total params: 1,059
Trainable params: 1,059
Non-trainable params: 0
__________________________________________________________________________________________________
  • 앞서 학습했던 방법처럼 보조 출력층 생성 역시 Keras API 함수로 구현하는 것은 꽤 단순하다.
  • 보조 출력층(aux_output)과 데이터를 받는 층(hidden2)을 연결시키고, model에서 outputs을 2개 다 잡아주면 된다.

 

 

 

 

4. 모델 컴파일

  • 다중 입력 모델과 달리 다중 출력 모델에서는 모델 컴파일 방법이 바뀌게 된다.
  • 이는, 컴파일에서 출력층에서 모델을 평가하게 되는 손실 함수를 결정하기 때문이고, 이 손실 함수는 출력층마다 다르게 설정해야 하기 때문이다.
# 모델 컴파일
model.compile(optimizer=Adam(learning_rate=0.005),
              loss = ["msle", "msle"],
              metrics=["accuracy"],
              loss_weights=[0.9, 0.1])
  • 최적화나 모델 평가 지표는 출력층의 수와 상관없기 때문에 바뀌지 않는다.
  • 손실 함수는 출력층이 2개가 되었으므로, 2개를 잡아줘야 한다(만약, 손실 함수를 하나만 잡아준다면, 모든 출력의 손실 함수가 같다고 가정한다 - 위 경우에는 출력층이 회귀모형이므로, msle로 같은 손실 함수를 사용하므로, msle 하나만 사용해도 된다).
  • loss_weights: 출력 층별 손실 값의 가중치를 정해준다. 케라스는 기본적으로 출력된 손실 값들을 모두 더해 최종 손실을 구하며, 이를 기반으로 학습을 한다. 여기서 사용된 보조 출력은 규제로 사용되었기 때문에 주 출력이 더 중요하다. 그러므로, 주 손실 값에 더 많은 가중치를 부여하였다.

 

 

 

 

5. 모델 학습 및 평가

  • 모델 학습과 평가에서의 차이는 Label 역시 각 출력층에 맞게 2개가 들어가야 한다는 것이다.
  • 해당 다중 출력 모델은 규제 목적으로 보조 출력층을 추가한 것이므로, 동일한 데이터를 label로 사용해도 된다.
early_stop = keras.callbacks.EarlyStopping(monitor='val_loss', patience=30, restore_best_weights=True)

# 학습
history = model.fit([X_train_A, X_train_B], [y_train, y_train],
                    epochs=300,
                    batch_size = 32,
                    validation_data=([X_valid_A, X_valid_B], [y_valid, y_valid]),
                    callbacks=[early_stop])
Epoch 1/300
362/362 [==============================] - 3s 6ms/step - loss: 0.2162 - main_output_loss: 0.1988 - aux_output_loss: 0.3726 - main_output_accuracy: 0.0027 - aux_output_accuracy: 0.0023 - val_loss: 0.0697 - val_main_output_loss: 0.0633 - val_aux_output_loss: 0.1276 - val_main_output_accuracy: 0.0048 - val_aux_output_accuracy: 0.0048
Epoch 2/300
362/362 [==============================] - 1s 2ms/step - loss: 0.0650 - main_output_loss: 0.0584 - aux_output_loss: 0.1243 - main_output_accuracy: 0.0026 - aux_output_accuracy: 0.0026 - val_loss: 0.0619 - val_main_output_loss: 0.0544 - val_aux_output_loss: 0.1295 - val_main_output_accuracy: 0.0048 - val_aux_output_accuracy: 0.0048
Epoch 3/300
362/362 [==============================] - 1s 2ms/step - loss: 0.0584 - main_output_loss: 0.0509 - aux_output_loss: 0.1260 - main_output_accuracy: 0.0027 - aux_output_accuracy: 0.0027 - val_loss: 0.0598 - val_main_output_loss: 0.0522 - val_aux_output_loss: 0.1279 - val_main_output_accuracy: 0.0048 - val_aux_output_accuracy: 0.0048
Epoch 4/300
362/362 [==============================] - 1s 2ms/step - loss: 0.0555 - main_output_loss: 0.0480 - aux_output_loss: 0.1229 - main_output_accuracy: 0.0018 - aux_output_accuracy: 0.0018 - val_loss: 0.0591 - val_main_output_loss: 0.0514 - val_aux_output_loss: 0.1281 - val_main_output_accuracy: 0.0045 - val_aux_output_accuracy: 0.0048

...

Epoch 54/300
362/362 [==============================] - 0s 1ms/step - loss: 0.0522 - main_output_loss: 0.0444 - aux_output_loss: 0.1227 - main_output_accuracy: 0.0024 - aux_output_accuracy: 0.0024 - val_loss: 0.0556 - val_main_output_loss: 0.0476 - val_aux_output_loss: 0.1276 - val_main_output_accuracy: 0.0045 - val_aux_output_accuracy: 0.0048
Epoch 55/300
362/362 [==============================] - 1s 1ms/step - loss: 0.0524 - main_output_loss: 0.0446 - aux_output_loss: 0.1225 - main_output_accuracy: 0.0026 - aux_output_accuracy: 0.0026 - val_loss: 0.0540 - val_main_output_loss: 0.0460 - val_aux_output_loss: 0.1263 - val_main_output_accuracy: 0.0045 - val_aux_output_accuracy: 0.0048
Epoch 56/300
362/362 [==============================] - 1s 2ms/step - loss: 0.0525 - main_output_loss: 0.0448 - aux_output_loss: 0.1215 - main_output_accuracy: 0.0024 - aux_output_accuracy: 0.0024 - val_loss: 0.0539 - val_main_output_loss: 0.0458 - val_aux_output_loss: 0.1265 - val_main_output_accuracy: 0.0048 - val_aux_output_accuracy: 0.0048
Epoch 57/300
362/362 [==============================] - 0s 1ms/step - loss: 0.0535 - main_output_loss: 0.0457 - aux_output_loss: 0.1234 - main_output_accuracy: 0.0033 - aux_output_accuracy: 0.0033 - val_loss: 0.0543 - val_main_output_loss: 0.0463 - val_aux_output_loss: 0.1264 - val_main_output_accuracy: 0.0045 - val_aux_output_accuracy: 0.0048
>>> model.evaluate([X_test_A, X_test_B], [y_test, y_test])

194/194 [==============================] - 0s 1ms/step - loss: 0.0533 - main_output_loss: 0.0454 - aux_output_loss: 0.1241 - main_output_accuracy: 0.0029 - aux_output_accuracy: 0.0032
[0.05328081175684929,
 0.04541592299938202,
 0.12406466901302338,
 0.0029069767333567142,
 0.0032299740705639124]
  • 이전과 달리 값이 굉장히 많이 나오기 때문에 이를 파악하기 어려울 수 있는데, 그 내용은 생각보다 상당히 단순하다.
  • 손실 값은 loss: 0.0533, main_output_loss: 0.0454, aux_output_loss: 0.1241이 나왔다.
  • 여기서 loss만 신경 쓰면 된다. 위에서 우리는 main_output_loss와 aus_output_loss의 가중치를 0.9, 0.1로 부여하였는데, loss는 각 손실 값에 해당하는 가중치를 곱하여 합한 값이기 때문이다.

$$ 0.0533 = 0.9*0.0454 + 0.1*0.1241 $$

  • Deep model의 손실 값과 Wide & Deep Learning model의 손실 값을 동시에 반영하여, 총 손실 값을 계산하였으므로, Deep model이 Wide model과의 결합 없이도 우수한 성능을 보이는 것을 알 수 있다.
  • 주 출력층의 Accuracy는 0.0029, 보조 출력층의 Accuracy도 0.0032로 Deep model, Wide & Deep Learning model 모두 Accuracy가 괜찮게 나왔다. 이로 인해 Deep model 자체만으로도 우수한 성능을 보이는 것을 알 수 있다.

5.1. 손실 값과 정확도를 시각화해보자.

def Drawing_Scalars(history_name):
    
    history_DF = pd.DataFrame(history_name.history)
    # 그래프의 크기와 선의 굵기를 설정해주었다.
    history_DF.plot(figsize=(12, 8), linewidth=3)

    # 교차선을 그린다.
    plt.grid(True)

    plt.legend(loc = "upper right", fontsize =15)

    plt.title("Learning Curve", fontsize=30, pad = 30)
    plt.xlabel('Epoch', fontsize = 20, loc = 'center', labelpad = 20)
    plt.ylabel('Variable', fontsize = 20, rotation = 0, loc='center', labelpad = 40)

    # 위 테두리 제거
    ax=plt.gca()
    ax.spines["right"].set_visible(False) # 오른쪽 테두리 제거
    ax.spines["top"].set_visible(False) # 위 테두리 제거
    
    plt.show()
    
    
Drawing_Scalars(history)

  • 굉장히 많은 지표들이 추가되었지만, Early Stopping의 기준으로 사용한 val_loss를 본다면, patience가 30이었으므로, epochs 27에서 모델이 수렴하였다는 것을 알 수 있다.

 

[참고 자료]

 

 

 이번 포스트에서는 다중 출력 모델을 Wide & Deep Learning model에서의 규제 기법으로 사용해보았다. Deep model의 결괏값에 대한 평가가 모델 전체 평가에 반영되었으므로, Deep Model의 일반화가 잘 이루어진 모델이 만들어졌다고 할 수 있다. 

 지금까지 Wide & Deep Learning 모델을 기반으로 다중 입력, 다중 출력 모델을 만드는 방법과 이를 통해 Wide & Deep Learning Model을 더 잘 사용할 수 있도록 해보았다.

728x90
반응형

+ Recent posts