본문 바로가기
딥러닝 입문

선형 회귀(Linear Regression) - 2

by 소글소글 2022. 10. 23.
반응형

이번 포스트는 이전 포스트의 코드를 이어 사용할 것이기 때문에 아래의 링크를 통해 이전 포스트를 참고해주세요!

 

 

선형 회귀(Linear Regression) - 1

선형 회귀란 무엇일까? 선형 회귀는 y = ax + b와 같은 간단한 1차 함수로 표현할 수 있다. 선형 회귀의 목표는 기울기(a)와 절편(b)을 찾아내는 것이다. 선형 회귀 과정 쉽게 알기 다음 세 그래프를

hmku1.tistory.com

목차

1. 경사 하강법이란?
2. 예측값과 변화율 
3. 올바른 모델 찾는 과정
4. 변화율로 가중치, 절편 업데이트하기
5. 오차 역전파로 가중치, 절편 업데이트하기

1. 경사 하강법이란?

 

이전 포스트에서 설명했던 선형 회귀의 목표는 입력 데이터와 타깃 데이터를 통해 기울기와 절편을 찾는 것이었다.

즉, 산점도 그래프를 잘 표현하는 직선의 방정식을 찾는 것이 선형 회귀의 목표였다.

경사 하강법은 직선의 방정식을 찾는 방법 중 하나이고, 기울기(변화율)를 사용하여 모델을 조금씩 조정하는 최적화 알고리즘이다.


2. 예측값(ŷ, 와이-햇)

 

딥러닝에서는 기울기 a를 가중치를 의미하는 w나, 계수를 의미하는 θ로 표기한다. 

그리고 (와이 햇)은 우리가 예측한 값을 의미한다.

따라서 예측한 값을 나타내는 방정식은 ŷ = wx + b이다.

 

예측값이란 우리가 입력과 출력 데이터를 통해 규칙을 발견하면 모델을 만들었다고 하는데, 그 모델에 대해 새로운 입력값을 넣고 그에 대한 출력 값을 예측값이라고 한다.


3. 올바른 모델 찾는 과정(실습)

 

🟩 1. w와 b 초기화하기

 

w = 1.0
b = 1.0

y_hat = x[0] * w + b
print(y_hat)

print(y[0])
1.0616962065186886
151.0

 

w와 b를 임의로 초기화한다. 물론 w의 개수가 많아지면 바람직한 방법은 아니지만, 지금은 임의로 예제를 위해 1.0으로 직접 지정한다.

 

그리고 w, b를 사용해 임시로 만든 모델로 첫 번째 샘픈 x[0]에 대한 계산을 하고, ŷ(와이-햇)에 저장한 후 출력한다.

 

출력 결과를 보면, 예측값인 y_hat은 약 1.06, 타깃값인 y[0]는 151.0으로 차이가 크다. 이는 w와 b를 임의로 정했기 때문에 예측 결과가 잘 나오지 않은 것이 당연하다.

 

 

🟩 2. w 값 조절하기

 

어떻게 하면 y_hat이 y[0]에 가까워질 수 있을까? 가장 쉬운 방법은 w와 b를 조금씩 변경해서 y_hat이 증가하는지, 감소하는지 살펴보는 것이다. w를 0.1만큼 증가시키고 y_hat의 변화량을 살펴보자.

 

w_inc = w + 0.1
y_hat_inc = x[0] * w_inc + b
print(y_hat_inc)
1.0678658271705574

 

확실히 이전(1.0616962065186886)보다 증가한 것을 확인할 수 있다. 

 

 

🟩 3. w 값 조정 후 예측값 변화율 확인하기

 

그러면 w가 0.1 증가했을 때 y_hat이 얼마나 증가했는지 계산해보자.

 

w_rate = (y_hat_inc - y_hat) / (w_inc - w)
print(w_rate)
0.061696206518688734

 

y_hat의 증가량을 w가 증가한 양으로 나눈 결과를 출력하였고, 이를 x[0]에 대한 w의 변화율이라고 한다. 

그런데 w_rate에 대한 코드를 수식으로 적어 정리하면 변화율은 결국 훈련 데이터의 첫 번째 샘플인 x[0]라는 것을 알 수 있다. 

w 변화율 = 샘플 입력값

y_hat 값을 증가시켜야 한다면 w 값을 증가시키면 y_hat 값을 증가시킬 수 있다. 

만약, 변화율이 음수일 때 y_hat 값을 증가시켜야 한다면 w값을 감소시키면 된다. 

하지만 다음 내용을 읽어보면 변화율의 음수와 양수를 구분하는 위의 방법은 조금 번거롭다고 느낄 것이다. 


4. 변화율로 가중치, 절편 업데이트하기

 

🟩 가중치 업데이트

 

이번에는 w와 b로 변화율로 업데이트하는 방법을 알아보자.

앞서 본 것과 같이 y_hat은 y[0]에 한참 못 미치는 값이었다. 즉, y_hat의 증가폭이 더 커져야 한다.

이 상황에서 w가 증가하면 y_hat도 증가한다.

이때 변화율이 양수인 점을 이용하여 변화율을 w에 더하는 방법으로 w를 증가시킬 수도 있다. 

 

y = w * x + b에서 변화율(=x값)이 음수일 때 w가 증가하면 y_hat은 감소한다. 반대로 w가 감소하면 y_hat은 증가한다. 

이때 변화율이 음수인 점을 이용하면 앞에서 한 것과 동일하게 변화율을 더하는 방법으로 y_hat 값을 증가시킬 수 있다.

 

따라서 두 경우 모두 변화율을 w에 더하면 된다!

 

w_new = w + w_rate
print(w_new)
1.0616962065186888

 

 

🟩 절편 업데이트

 

절편 b에 대해서도 0.1을 증가시킨 후 y_hat이 얼마나 증가했는지 계산하고, 변화율도 확인한다.

 

b_inc = b + 0.1
y_hat_inc = x[0] * w + b_inc
print(y_hat_inc)

b_rate = (y_hat_inc - y_hat) / (b_inc - b)
print(b_rate)
1.1616962065186887
1.0

 

변화율의 값은 1인 것이 당연한 것이, 1차 함수의 절편 b가 1 증가하면 y_hat의 값도 1 증가한다. 

즉, b를 업데이트하기 위해서는 단순히 1을 더하면 된다.

 

b_new = b + 1
print(b_new)
2.0

5. 오차 역전파로 가중치, 절편 업데이트

 

신경망은 <입력 - 신경망 - 출력>과 같이 좌측에서 우측으로의 진행방향을 갖는다.

하지만 훈련을 위해서 오차 역전파(Back-propagation) 과정에서는 이와는 반대의 진행방향을 갖습니다. 

 

즉, ŷ과 y의 차이를 이용하여 w와 b를 업데이트한다. 

이번 예제에서는 오차가 연이어 전파되는 모습은 잘 보이지 않지만, 추후에 해당 예시를 다뤄보겠다.

 

앞선 예시에서는 변화율만으로 w와 b를 업데이트했다.

하지만 만약 ŷ과 y의 차이가 크면 어떻게 해야 할까? w와 b를 더 많이 증가시켜야 하지 않을까?

ŷ이 y보다 커져서 ŷ을 감소시켜야 한다면 어떻게 해야 할까? w와 b를 감소시켜야 하지 않을까?

 

이번 예시에서는 y에서 ŷ을 뺀 오차의 양을 변화율에 곱하는 방법으로 w를 업데이트해보자.

이렇게 하면 ŷ이 y보다 많이 작은 경우에도 w와 b를 많이 변화시킬 수 있다.

또 ŷ이 y를 지나치면 w와 b의 방향도 바꿀 수 있다.

 

 

🟩 1. 오차와 변화율을 곱하여 가중치 업데이트

 

err = y[0] - y_hat
w_new = w + w_rate * err
b_new = b + 1 * err
print(err)
print(w_new, b_new)
149.9383037934813
10.250624555904514 150.9383037934813

 

우선 x[0]일 때 w의 변화율과 b의 변화율에 오차를 곱한 다음 업데이트된 w_new와 b_new를 출력하면 각각 큰 폭으로 바뀌었음을 알 수 있다.

 

만약 ŷ이 y보다 커지면 오차는 음수가 되어 자동으로 w와 b가 줄어드는 방향으로 업데이트된다.

반대로 ŷ이 y보다 작으면 오차는 양수가 되고 w와 b는 더 커지도록 업데이트된다.

 

다음으로 두 번째 샘플 x[1]을 사용하여 오차를 구하고 새로운 w와 b를 구해보자. 앞에서 w 변화율 식을 정리했을 때 샘플값이 된다는 것을 알았으므로 여기서 x[1] = w_rate이다.

 

y_hat = x[1] * w_new + b_new
err = y[1] - y_hat
w_rate = x[1]
w_new = w_new + w_rate * err
b_new = b_new + 1 * err
print(err)
print(w_new, b_new)
-75.41066251735467
14.132317616381767 75.52764127612664

 

 

🟩 2. 전체 샘플 반복하기

 

for x_i, y_i in zip(x,y):
    y_hat = x_i * w + b
    err = y_i - y_hat
    w_rate = x_i
    w = w + w_rate * err
    b = b + 1 * err
print(w, b)
587.8654539985689 99.40935564531424

 

파이썬의 zip() 함수는 여러 개의 배열에서 동시에 요소를 하나씩 꺼내 준다.

 

여기서 단지 for문을 사용했을 뿐 이전과 코드는 동일하다.

 

이제 그래프를 그려서 위의 과정을 통해 얻은 모델이 데이터 세트를 잘 표현하는지 확인해보자.

 

plt.scatter(x, y)
pt1 = (-0.1, -0.1 * w + b)
pt2 = (0.15, 0.15 * w + b)
plt.plot([pt1[0], pt2[0]], [pt1[1], pt2[1]])
plt.xlabel('x')
plt.ylabel('y')
plt.show()

경향을 나타내는 것 같지만 만족스럽지는 못하다. 

이럴 때에는 조금 더 반복하는 방법이 있다.

 

 

🟩 3. 여러 에포크 반복하기

 

보통 경사 하강법에서는 주어진 훈련 데이터로 학습을 여러 번 반복한다.

이렇게 전체 훈련 데이터를 모두 이용하여 한 단위의 작업을 진행하는 것을 특별히 에포크(epoch)라고 한다.

일반적으로 수십, 수천 번의 에포크를 반복한다. 

 

그럼 예시에 100번의 에포크를 반복해보자.

 

for i in range(1,100):
    for x_i, y_i in zip(x,y):
        y_hat = x_i * w + b
        err = y_i - y_hat
        w_rate = x_i
        w = w + w_rate * err
        b = b + 1 * err
print(w, b)
913.5973364345905 123.39414383177204
plt.scatter(x,y)
pt1 = (-0.1, -0.1 * w + b)
pt2 = (0.15, 0.15 * w + b)
plt.plot([pt1[0], pt2[0]], [pt1[1], pt2[1]])
plt.xlabel('x')
plt.ylabel('y')
plt.show()

 

확실히 에포크 반복 이전보다 전체 데이터의 경향을 잘 따라가는 것을 확인할 수 있다.

그럼 다음과 같은 머신러닝 모델을 찾았다고 할 수 있다.

 

ŷ = 913.6x + 123.4

 

이제 그러면 새로운 데이터로 예측을 해보자.

 

x_new = 0.18
y_pred = x_new * w + b
print(y_pred)
287.8416643899983
plt.scatter(x,y)
plt.scatter(x_new, y_pred)
plt.xlabel('x')
plt.ylabel('y')
plt.show()

예측한 ŷ의 값을 산점도 그래프에 찍어보니 꽤나 경향을 잘 예측한 것으로 보인다. 

 

다음 포스트에서는 손실 함수와 경사 하강법의 관계에 대해 알아보자.

반응형

'딥러닝 입문' 카테고리의 다른 글

선형 회귀(Linear Regression) - 1  (0) 2022.10.22

댓글