본문 바로가기
파이썬 프로그래밍/Numpy 딥러닝

12. 단층 퍼셉트론의 한계-2.선형 회귀식의 한계

by Majestyblue 2022. 1. 12.

저번 차시에서 단층 퍼셉트론으로는 XOR 문제를 해결할 수 없다는 것을 알았다. 이번엔 실제로 와 닫는 문제를 가져왔다. 이번 목표는 단층 퍼셉트론으로 몸무게에 따른 키를 예측하는 모델을 만들어 보자.

 

몸무게(weight)에 따른 키(height)

 

몸무게와 키는 대략적으로 비례하는 것을 다들 알고 있을 것이다. 아기를 키워보면 알게되는 사실이 있는데 몸무게와 키가 정비례하는 것처럼 쭉쭉 큰다는 것이다. 그러다가 성장기가 지나면 키는 성장이 멈추지만 몸무게는 몸이 허락하는 한 한계가 없다(...) 이런 관계를 단층 퍼셉트론으로 모델을 만들 수 있을까?

 

 

weight_height.csv
0.00MB

 

 

1. 데이터 준비 및 확인

위의 파일을 임포트 하고 입력값, 목표값 등을 나누고 그래프를 그려보자

import numpy as np
import matplotlib.pyplot as plt
from numpy import genfromtxt


np.random.seed(220112)

data = genfromtxt('weight_height.csv', delimiter=',', skip_header=1)
input = data[:, 0]
target = data[:, 1]

input = input.reshape(-1, 1)
target = target.reshape(-1, 1)

plt.plot(input, target, 'ro')
plt.xlabel('weight', size=15)
plt.ylabel('height', size=15)
plt.show()

 

 

몸무게에 따른 키 그래프

 

 

 

2. pred = wx +b의 선형회귀식

이 식을 단층 퍼셉트론 중 하나인 선형회귀식으로 표현할 수 있을까? pred = X*W + B 모델로 만들어 보겠다. 전체 코드이다. 구현에 대해서는 (https://toyourlight.tistory.com/12) 여기를 참고하길 바란다.

 

import numpy as np
import matplotlib.pyplot as plt
from numpy import genfromtxt


np.random.seed(220112)

data = genfromtxt('weight_height.csv', delimiter=',', skip_header=1)
input = data[:, 0] * 0.1 # np.power 연산에서 overflow 방지
target = data[:, 1] * 0.1 # np.power 연산에서 overflow 방지

input = input.reshape(-1, 1)
target = target.reshape(-1, 1)

W = np.random.randn(1, 1)
B = np.random.randn(1, 1)

learning_rate = 0.001

def linear_forward(X, Y, W, B):
    pred = np.dot(X, W) + B
    loss = np.mean(np.power(Y - pred, 2))
    
    return pred, loss

def loss_gradient(X, Y, W, B): 
    XWB = np.dot(X, W) + B 
    dL_dg = 2*(XWB - Y) 
    dg_dW = np.transpose(X, (1, 0)) 
    dL_dW = np.dot(dg_dW, dL_dg) 
    dL_dB = np.sum(dL_dg, axis=0) 
    return dL_dW, dL_dB

_, loss = linear_forward(input, target, W, B)
print('loss : ', loss)

    
for i in range(100): 
    dL_dW, dL_dB = loss_gradient(input, target, W, B) 
    W = W + -1*learning_rate * dL_dW 
    B = B + -1*learning_rate * dL_dB 
    
pred, loss = linear_forward(input, target, W, B)
print('loss : ', loss)
print('W : ', W)
print('B : ', B)

plt.plot(input, target, 'ro', label='target')
plt.plot(input, pred, 'b', label='pred')
plt.xlabel('weight', size=15)
plt.ylabel('height', size=15)
plt.legend()
plt.show()

 

 

 

결과 확인

loss :  30.267319253924295
loss :  1.626183134825925

W :  2.00994212
B :  5.15076958

목표값인 target과 선형회귀식으로 예측한 pred

 

파란색 선은 몸무게가 x일 때 pred = 2.00*x + 5.15 로 키를 예측하는 선형회귀식을 표현한 것이다.

목표값은 완만한 곡선 형태를 띄는데 우리의 모델인 pred는 직선 형태이므로 잘 예측하지 못할 것 같다.

 

 

 

 

3. pred = w_0x^{2} + w_1x + b 의 선형회귀식

 

오른쪽은 2차함수 그래프, 왼쪽 그래프와 모양이 좀 비슷하다?

 

목표값을 보아하니 2차 방정식과 좀 비슷하지 않을까? 생각해 볼 수 있다. 즉, 모델인 선형회귀식을

 

 

형태로 예측할 수 있지 않을까? 그러면 좀 더 정확해 지려나? 한번 해 보자. 이를 구현한 아이디어를 코드로 표현해 보았다.

 

input2 = np.hstack((np.power(input, 2), input))

W2 = np.random.randn(2, 1)
B2 = np.random.randn(1, 1)

learning_rate2 = 0.00005

pred2, loss2 = linear_forward(input2, target, W2, B2)

for i in range(100): 
    dL_dW, dL_dB = loss_gradient(input2, target, W2, B2) 
    W2 = W2 + -1*learning_rate2 * dL_dW 
    B2 = B2 + -1*learning_rate2 * dL_dB

 

input2 = [x^(2), x] 로 표현하기 위해 np.hstack을 사용하였다.

 

입력 특성이 2개이므로 weight의 shape는 당연히 (2, 1)이여야 한다.

 

학습률을 설정한다. 왜 이렇게 작냐고 하면 x^(2) 이므로 학습을 천천히 시켜야 한다.

 

이 값으로 forward 연산, backward 연산과 훈련을 실시한다. 아래는 전체코드와 결과이다. 어떻게 되었을까?

 

import numpy as np
import matplotlib.pyplot as plt
from numpy import genfromtxt


np.random.seed(220112)

data = genfromtxt('weight_height.csv', delimiter=',', skip_header=1)
input = data[:, 0] * 0.1 # np.power 연산에서 overflow 방지
target = data[:, 1] * 0.1 # np.power 연산에서 overflow 방지

input = input.reshape(-1, 1)
input2 = np.hstack((np.power(input, 2), input))
target = target.reshape(-1, 1)

W = np.random.randn(1, 1)
B = np.random.randn(1, 1)

W2 = np.random.randn(2, 1)
B2 = np.random.randn(1, 1)

learning_rate = 0.001
learning_rate2 = 0.00005

def linear_forward(X, Y, W, B):
    pred = np.dot(X, W) + B
    loss = np.mean(np.power(Y - pred, 2))
    
    return pred, loss

def loss_gradient(X, Y, W, B): 
    XWB = np.dot(X, W) + B 
    dL_dg = 2*(XWB - Y) 
    dg_dW = np.transpose(X, (1, 0)) 
    dL_dW = np.dot(dg_dW, dL_dg) 
    dL_dB = np.sum(dL_dg, axis=0) 
    return dL_dW, dL_dB

pred, loss = linear_forward(input, target, W, B)
pred2, loss2 = linear_forward(input2, target, W2, B2)
print('loss : ', loss2)

for i in range(100): 
    dL_dW, dL_dB = loss_gradient(input, target, W, B) 
    W = W + -1*learning_rate * dL_dW 
    B = B + -1*learning_rate * dL_dB 
    
for i in range(100): 
    dL_dW, dL_dB = loss_gradient(input2, target, W2, B2) 
    W2 = W2 + -1*learning_rate2 * dL_dW 
    B2 = B2 + -1*learning_rate2 * dL_dB 
    
pred, loss = linear_forward(input, target, W, B)
pred2, loss2 = linear_forward(input2, target, W2, B2)
print('loss : ', loss2)


print('W2 : ', W2)
print('B2 : ', B2)

plt.plot(input, target, 'ro', label='target')
plt.plot(input, pred, 'b', label='pred')
plt.plot(input, pred2, 'g', label='pred2')
plt.xlabel('weight', size=15)
plt.ylabel('height', size=15)
plt.legend()
plt.show()

 

loss :  1902.0074420334786
loss :  18.148144748416307
W2 :  [[0.29567561]       
 [0.56796051]]
B2 :  [[2.71944333]]   

 

초록색 선 pred2가 이차함수 형태의 선형회귀 모델

 

 

오차가 더 심해진것 같다(...) 차라리 일차 선형회귀식이 더 나은 것 같기도 하다.

 

 

 

 

 

4. 선형회귀식의 문제점

 

일단 선형회귀식의 정의부터 알아야겠지? 엄밀한 수학적 정의부터 보자.

 

선형회귀식은 계수선형결합을 만족하는 방정식이다. 구체적으로 다음과 같다.

 

 

 

1) 계수는 weight과 bias를 말한다. (입력값 x가 아니다! 이거 은근히 헷갈리던데...)

 

2) 선형결합이란 다음 두 조건을 만족한다. f가 weight 의 함수일 때

 

 

 

 

 

예를 들어 보자. f(w, b) = wx + b의 함수가 있을 때

 

(1) 첫 번째 규칙 : 성립한다.

 

 

 

 

 

(2) 두 번째 규칙 : 성립한다.

 

 

 

 

 

 

 

아래의 방정식은 입력에 대해 2차 방정식이지만 계수에 대해서는 1차 방정식이다. 위의 선형결합이 성립한다. 궁금하면 직접 해 보자

 

 

 

 

 

 

 

 

 

선형회귀식은 굉장히 단순한 식이고, 선형회귀가 아닌 방정식도 많다. 예를 들어 아래의 식은 선형회귀식일까?

 

 

 

 

선형회귀식 조건을 확인해 보자

 

 

 

성립되지 않는다.

 

 

 

 

5. 결론

선형회귀식으로 이루어진 단층퍼셉트론은 데이터가 복잡해질 수록 학습이 잘 되지 않는다.

 

 

 

 

다음시간에는 XOR 문제를 어떻게 해결했는지 알아보자.