Linear Regression

개요

Andrew Ng. 교수님 ML(Machine Learning) 강의 중 Linear Regression 관련 내용을 정리해 두고자 합니다. 

만약 어떤 학생들의 공부시간(X) 대비 점수(y) 자료가 아래와 같다면,

1 시간 공부 : 10점

5 시간 공부 : 50점

10시간 공부: 100점 

공부시간 대비 획득점수

만약 7시간쯤 공부하면 어떤 점수가 나올까요? 🤔

물론 공부시간 대비 성적이 항상 일정하게 나오는 것은 아니지만 쉽게 설명하기위해 위 그래프가 작성되었음을 참조 바랍니다.

(머리가 좋고 집중력이 뛰어난 학생은 그렇지 않은 학생의 공부시간 대비 성적이 우수할 것입니다.)

 

선형 회귀란?

선형 회귀는 종속 변수(예측하려는 값)와 독립 변수(설명 변수) 간의 선형적인 관계를 찾는 통계 및 머신러닝 기법입니다. 즉, 데이터를 통해 직선을 찾아서 미래 값을 예측하는 방법입니다.

  • y : 예측값 (종속변수)

  • x : 입력값 (독립변수)

  • w : 기울기 (weight)

  • b : 절편 (bias)

 

1. Training Dataset

학습에 사용할 데이터셋 정의.

1시간 공부하면 10점, 5시간 50점, 10시간 100점.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import numpy as np
import matplotlib.pyplot as plt
 
X = np.array([1, 5, 10])
y = np.array([10, 50, 100])
 
m = X.shape[0]
print('Number of datasets:', m)
 
plt.xlabel('Learning time')
plt.ylabel('Score')
plt.plot(X, y)
plt.scatter(X, y, color='red')
plt.show()

 

2. Model

중학교때 공부한 1차함수 입니다.

fw,b(x)=wx+b

 

3. Parameters

w,b

 

4. Cost function

J(w,b)=12mi=1m(y^(i)y(i))2

아래 그림을 보면 이해가 쉽다.

Source : Andrew Ng. Cousera

좌측 그래프 가설에 의해 그어진 보라색선 예측치(y hat)에서 실측치 붉은색 X표시 (y)를 뺀 오차값을 제곱.

(부호가 모두 양수로 변경)

오차를 모두 더해, 평균제곱오차를 구하고 이를 최소하하는 w, b를 찾는 것이 목적.

(평균을 나눌때 m이 아니라 2m으로 나누는 이유는 후술할 미분 도함수의 간략화를 위함) 

우측 그래프를 보면 오차를 최소화하는 w(가중치, 기울기)가 1임을 확인. 


비용함수를 코드로 구현하면 아래와 같다.

1
2
3
4
5
6
7
8
9
def compute_cost(X, y, w, b):
    m = X.shape[0]
    cost = 0
    for i in range(m):
        f_wb = w * X[i] + b
        cost += (f_wb - y[i]) ** 2
 
    total_cost = 1 / (2*m) * cost
    return total_cost

 

5. Goal

minimize J(w,b) with respect to w,b

만약 위 그래프에서 X축이 공부시간이고, Y축이 성적이면,

공부시간 대비 성적이 가장 비슷하게 들어맞는 선을 찾는 문제입니다. (하늘색)

그래야만 다른 X값을 예측할때 오차가 줄어들기 때문입니다.

그런데 사람이 하나씩 선을 그어볼 수는 없고, 경사하강법을 통해 주어진 데이터에서 오차가 가장 작은 w(기울기), b(가중치)를 찾도록 시키는 것이 기계학습의 기본원리입니다.

 

6. Gradient descent

w, b가 최소화되는 값을 찾는 경사하강법.

w=wαwJ(w,b)b=bαbJ(w,b)

1
2
3
4
5
6
7
8
9
10
11
12
13
def compute_gradient(X, y, w, b):
    m = X.shape[0]
    dj_dw = 0
    dj_db = 0
 
    for i in range(m):
        f_wb = w * X[i] + b
        dj_dw += (f_wb - y[i]) * X[i]
        dj_db += f_wb - y[i]
 
    dj_dw /= m
    dj_db /= m
    return dj_dw, dj_db

 

7. Derivative

비용 함수 J(w,b)를 미분(편미분)하여 기울기(gradient)를 계산하고, 이를 이용해 경사 하강법(Gradient Descent) 으로 w와 b를 업데이트. (num_iters 번 수행) 


w=wα1mi=1m(fw,b(x(i))y(i))x(i)b=bα1mi=1m(fw,b(x(i))y(i))


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
def gradient_descent(X, y, w_in, b_in, alpha, num_iters):
    w = copy.deepcopy(w_in)
    b = b_in
 
    J_history = []
    p_hiistory = []
 
    for i in range(num_iters):
        dj_dw, dj_db = compute_gradient(X, y, w, b)
 
        w = w - alpha * dj_dw
        b = b - alpha * dj_db
 
        J_history.append( compute_cost(X, y, w, b) )
        p_hiistory.append( (w, b) )
 
        if i % 100 == 0:
            print(f':Iteration {i}: w = {w}, b = {b}, cost = {J_history[-1]}')
 
    return w, b, J_history, p_hiistory

 

8. Derivative of cost (w, b)

weight, bias 미분과정이며 미적분학을 공부하지 않았다면 그냥 넘어가도 무관합니다. 

 

1. Weight Update -1

w=wαwJ(w,b)

 

2. Weight update -2

wJ(w,b)=w(12mi=1m(fw,b(x(i))y(i))2)

 

3. Weight update -3

wJ(w,b)=w12mi=1m(wx(i)+by(i))2=12mi=1mw(wx(i)+by(i))2=12mi=1m2(wx(i)+by(i))w(wx(i)+by(i))=1mi=1m(wx(i)+by(i))x(i)=1mi=1m(fw,b(x(i))y(i))x(i)

 

4. Bias update -1

b=bαbJ(w,b)

 

5. Bias update -2

bJ(w,b)=b(12mi=1m(fw,b(x(i))y(i))2)

 

6. Bias update -3

bJ(w,b)=b12mi=1m(wx(i)+by(i))2=12mi=1mb(wx(i)+by(i))2=12mi=1m2(wx(i)+by(i))b(wx(i)+by(i))=1mi=1m(wx(i)+by(i))1=1mi=1m(fw,b(x(i))y(i)) 

Machine Learning

이제 초기값을 설정하고 모델에 대한 학습을 진행합니다.

1
2
3
4
5
6
7
8
9
# init parameters
w = 0
b = 0
alpha = 0.01
num_iters = 1000
 
# machine learning
w_final, b_final, J_history, p_history = gradient_descent(X, y, w, b, alpha, num_iters)
print(f'Final w: {w_final}, Final b: {b_final}')

 

학습이 진행됨에 따라 비용이 감소함을 확인할 수 있으며, 비용이 가장 낮은 지점의 w, b를 이용해 예측을 진행합니다.


Prediction

이제 위 모든 과정을 진행한 결실을 맺을 시간입니다.

이제 2시간, 4시간, 6시간, 8시간을 공부하면 몇점이 나올까요?  

1
2
3
4
5
6
7
8
# prediction
def predict(w, b, x):
    return w * x + b
 
X_test = np.array([2, 4, 6, 8])
for x in X_test:
    y_pred = predict(w_final, b_final, x)
    print(f'Predicted score for {x} hours of learning: {y_pred:.2f}')

[코드 수행결과]

학습이 잘 진행되었음을 확인 할 수 있었습니다.😀

Cost를 최소화하는 w, b를 찾았다는 의미입니다.

 

Full code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
import numpy as np
import matplotlib.pyplot as plt
import copy
 
X = np.array([1, 5, 10])
y = np.array([10, 50, 100])
 
m = X.shape[0]
print('Number of datasets:', m)
 
plt.xlabel('Learning time')
plt.ylabel('Score')
plt.plot(X, y)
plt.scatter(X, y, color='red')
plt.show()
 
def compute_cost(X, y, w, b):
    m = X.shape[0]
    cost = 0
    for i in range(m):
        f_wb = w * X[i] + b
        cost += (f_wb - y[i]) ** 2
 
    total_cost = 1 / (2*m) * cost
    return total_cost
 
def compute_gradient(X, y, w, b):
    m = X.shape[0]
    dj_dw = 0
    dj_db = 0
 
    for i in range(m):
        f_wb = w * X[i] + b
        dj_dw += (f_wb - y[i]) * X[i]
        dj_db += f_wb - y[i]
 
    dj_dw /= m
    dj_db /= m
    return dj_dw, dj_db
 
def gradient_descent(X, y, w_in, b_in, alpha, num_iters):
    w = copy.deepcopy(w_in)
    b = b_in
 
    J_history = []
    p_hiistory = []
 
    for i in range(num_iters):
        dj_dw, dj_db = compute_gradient(X, y, w, b)
 
        w = w - alpha * dj_dw
        b = b - alpha * dj_db
 
        J_history.append( compute_cost(X, y, w, b) )
        p_hiistory.append( (w, b) )
 
        if i%10 == 0:
            print(f'Iteration {i}: w = {w:.3f}, b = {b:.3f}, cost = {J_history[-1]:.3f}')
 
    return w, b, J_history, p_hiistory
 
# init parameters
w = 0
b = 0
alpha = 0.01
num_iters = 1000
 
# machine learning
w_final, b_final, J_history, p_history = gradient_descent(X, y, w, b, alpha, num_iters)
print(f'Final w: {w_final}, Final b: {b_final}')
 
# prediction
def predict(w, b, x):
    return w * x + b
 
X_test = np.array([2, 4, 6, 8])
for x in X_test:
    y_pred = predict(w_final, b_final, x)
    print(f'Predicted score for {x} hours of learning: {y_pred:.2f}')


느낀점

만약 시험성적이 공부시간(x1) 뿐 아니라 지능(x2), 집중력(x3) 등 여러 입력 특징을 가진다면 어떻게 처리해야 할까요?

위 예시는 단순히 공부시간과 시험 성적이 1:1 로 매칭되는 심플한 예시였습니다만, 현실은 그렇지 않습니다. 

 

다중 선형 회귀의 행렬 표현

데이터가 다음과 같다고 가정하면,

샘플
x1 x2 x3 y
1 2 3 4 20
2 5 6 7 38
3 8 9 10 56

 

입력 특징 행렬 (X)

x1, x2, x3, .. xn 들을 행열(Matrix)로 선언.

X=[2345678910]


가중치 벡터 (W)

입력값의 열 개수만큼 가중치 벡터를 선언.

W=[123]

 

출력 벡터 (Y)

X (행렬) 와 W(벡터) 의 내적 계산후 bias를 더한 값. (b=0라면)

Y=[203856]

 

여러 입력을 처리하는 선형 회귀 모델

fw,b(x)=WX+b

벡터의 내적을 이용한 행렬연산으로만 처리하면 나머지는 단일 특성을 갖는 선형회귀모델과 동일합니다.😮 


Sample Code

행렬의 내적 계산시 Numpy를 활용하면 내부적으로 CPU를 활용, C언어 기반의 멀티쓰레딩을 통해 병렬처리되므로 반복문 이용해 계산하는 것보다 수십배 빠른 연산이 가능합니다.

(하지만 직접 행렬내적 함수를 코드로 만들어 서로 비교해보면 더 좋겠죠 👌)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import numpy as np
 
W = np.array([[1], [2], [3]])
b = 0
 
X = np.array([
    [2, 3, 4],
    [5, 6, 7],
    [8, 9, 10]
])
 
Y = np.dot(X, W) + b
 
print("벡터 W:", W)
print("행렬 X:\n", X)
print("벡터와 행렬의 내적 결과:", Y)

[코드수행결과]

W는 벡터(1차원)지만 Tensorflow 는 행렬(Matrix), 2D Tensor사용이 여러모로 유리합니다.

그래서 습관적으로 벡터를 행렬(2차원)로 바꾸어 많이씁니다.

 

이상으로 모든 설명을 마칩니다.


Reference

https://www.coursera.org/specializations/machine-learning-introduction

 

댓글

이 블로그의 인기 게시물

Qt Designer 설치하기

PyQt5 기반 동영상 플레이어앱 만들기