저번시간에 X = [1, 2, 3], Y = [3, 5, 7] 의 입력(X)와 출력(Y)가 있을 때 이 둘의 관계를 Y = wx + b로 가정하고 w, b를 구하기 위해 도함수를 구하고 경사하강법을 이용한다고 하였다.
입력값 input X, 목표값 target Y, weight W, bias B를 Numpy로 표현하면 아래와 같다.
(아래 learning_rate는 추후에 설명)
import numpy as np
np.random.seed(220102)
input = np.array([[1.],
[2.],
[3.]])
target = np.array([[3.],
[5.],
[7.]])
W = np.random.randn(1, 1) # [[0.97213265]] correct value = 2
B = np.random.randn(1, 1) # [[0.28608494]] correct value = 1
learning_rate = 0.001
1. 순방향 계산(forward) -> 예측(Predict)
순방향 계산으로 g(W, B)의 출력값이 Y와 같도록 학습해야 한다.
2. 오차 함수(loss function) L(g(W, B)는 아래와 같이 정의하였었다.
순방향 계산(pred)와 오차값(loss)를 표현한 코드는 아래와 같다.
def linear_forward(X, Y, W, B):
XW = np.dot(X, W)
pred = XW + B
loss = np.mean(np.power(Y - pred, 2))
return pred, loss
3. 오차값의 기울기 즉 오차함수의 도함수를 구한다. 이전 시간에 아래와 같이
(헷갈린다면 선형회귀 구현하기(이론) 편을 같이 보길 바란다)
오차 함수의 W, B에 대한 도함수를 구하였었다. 이를 파이썬 코드로 표현해 보자.
def loss_gradient(X, Y, W, B):
XWB = np.dot(X, W) + B
#∂L(g(X,W,B)) / ∂g(X,W,B)
dL_dg = 2*(XWB - Y)
#∂g(X,W,B) / ∂W
dg_dW = np.transpose(X, (1, 0))
#∂L(G(X, W, B)) / ∂W
dL_dW = np.dot(dg_dW, dL_dg)
#∂L(g(X,W,B)) / dB
dL_dB = np.sum(dL_dg, axis=0)
return dL_dW, dL_dB
경사하강법(Gradient descent)를 아래와 같이 표현했었다.
도함수값을 구하고, 경사하강법을 적용하는 것을 반복한다면 목표로 하는 W, B를 구할 수 있을 것이다.
아래는 경사하강법을 이용한 학습과정이다.
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 : [[1.25821759]
[2.23035024]
[3.20248289]]
loss : 8.375300661720344
before weight : [[0.97213265]] correct value = 2
before bias : [[0.28608494]] correct value = 1
#학습 후
pred : [[2.83083978]
[4.88249283]
[6.93414589]]
loss : 0.015586626144295427
after weight : [[2.05165305]]
after bias : [[0.77918673]]
정말 학습이 잘 되고 있는지 눈으로 확인해 볼 수 있다.
loss-weight 그래프를 그려보자. 대략 2 부근에서 loss값은 최소가 될 것이다. 코드와 결과는 아래와 같다.
weights = np.arange(0, 5, 0.2)
loss_array = []
for weight in weights:
_, loss = linear_forward(input, target, weight, B)
loss_array.append(loss)
fig = plt.figure()
ax = plt.axes(xlim=(-0, 4))
ax.plot(weights, loss_array)
ax.set_xlabel('weight')
ax.set_ylabel('loss')
plt.show()
학습 시 W가 어떻게 변하는지 애니매이션을 통해 관찰해 보자
(https://toyourlight.tistory.com/3 여기 링크 참고)
일단 훈련 부분을 수정해야 한다. W의 훈련 결과를 저장해야 한다.
train_W = []
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
train_W.append(W)
애니메이션을 설정한다.
redDot, = plt.plot([], [], 'ro')
def animate(frame):
_, loss = linear_forward(input, target, frame, B)
redDot.set_data(frame, loss)
return redDot
ani = FuncAnimation(fig, animate, frames=train_W)
plt.show()
빨간 점은 학습될 때 마다 갱신되는 W 값이다. B도 동일하게 구할 수 있다.
아래는 전체 코드이다.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
#import matplotlib.animation as animation
np.random.seed(220102)
input = np.array([[1.],
[2.],
[3.]])
target = np.array([[3.],
[5.],
[7.]])
W = np.random.randn(1, 1) # [[0.97213265]] correct value = 2
B = np.random.randn(1, 1) # [[0.28608494]] correct value = 1
learning_rate = 0.001
def linear_forward(X, Y, W, B):
XW = np.dot(X, W)
pred = XW + 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
#∂L(g(X,W,B)) / ∂g(X,W,B)
dL_dg = 2*(XWB - Y)
#∂g(X,W,B) / ∂W
dg_dW = np.transpose(X, (1, 0))
#∂L(G(X, W, B)) / ∂W
dL_dW = np.dot(dg_dW, dL_dg)
#∂L(g(X,W,B)) / dB
dL_dB = np.sum(dL_dg, axis=0)
return dL_dW, dL_dB
pred, loss = linear_forward(input, target, W, B)
"""
print('pred : ', pred)
print('loss : ', loss)
pred : [[1.25821759]
[2.23035024]
[3.20248289]]
loss : 8.375300661720344
"""
"""
dL_dW, dL_dB = loss_gradient(input, output, W, B)
print('before weight : ', W)
print('before bias : ', B)
before weight : [[0.97213265]] correct value = 2
before bias : [[0.28608494]] correct value = 1
"""
train_W = []
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
train_W.append(W)
"""
print('after weight : ', W)
print('after bias : ', B)
after weight : [[2.05165305]]
after bias : [[0.77918673]]
"""
pred, loss = linear_forward(input, target, W, B)
"""
print('pred : ', pred)
print('loss : ', loss)
pred : [[2.83083978]
[4.88249283]
[6.93414589]]
loss : 0.015586626144295427
"""
weights = np.arange(0, 5, 0.2)
loss_array = []
for weight in weights:
_, loss = linear_forward(input, target, weight, B)
loss_array.append(loss)
fig = plt.figure()
ax = plt.axes(xlim=(-0, 4))
ax.plot(weights, loss_array)
ax.set_xlabel('weight')
ax.set_ylabel('loss')
redDot, = plt.plot([], [], 'ro')
def animate(frame):
_, loss = linear_forward(input, target, frame, B)
redDot.set_data(frame, loss)
return redDot
ani = FuncAnimation(fig, animate, frames=train_W)
#FFwriter = animation.FFMpegWriter(fps=10)
#ani.save('animation.mp4', writer = FFwriter)
plt.show()
3. Pytorch로 위에 작성한 코드가 올바른지 확인해 보자.
학습이 너무 느려 learning_rate를 0.001 -> 0.01로 조절하였다.
기존 Numpy 코드에서는 기울기값을 매번 새로 정의하지만
Pytorch에서는 기울기값(grad)가 누적되므로 .grad.zero_()로 매번 초기화시켜주어야 한다.
이것을 주의하면서 확인해 보자.
import torch
import numpy as np
np.random.seed(220102)
input = np.array([[1.],
[2.],
[3.]])
target = np.array([[3.],
[5.],
[7.]])
W = np.random.randn(1, 1) # [[0.97213265]] correct value = 2
B = np.random.randn(1, 1) # [[0.28608494]] correct value = 1
input = torch.tensor(input, requires_grad = False)
target = torch.tensor(target, requires_grad = False)
W = torch.tensor(W, requires_grad=True)
B = torch.tensor(B, requires_grad=True)
learning_rate = 0.01
pred = torch.matmul(input, W) + B
"""
print('before pred : ', pred)
before pred : tensor([[1.2582],
[2.2304],
[3.2025]], dtype=torch.float64, grad_fn=<AddBackward0>)
"""
for i in range(100):
pred = torch.matmul(input, W) + B
loss = torch.mean(torch.pow((pred - target), 2))
loss.backward()
W.data = W.data - learning_rate * W.grad.data
B.data = B.data - learning_rate * B.grad.data
W.grad.zero_()
B.grad.zero_()
pred = torch.matmul(input, W) + B
"""
print('after pred : ', pred)
after pred : tensor([[2.9034],
[4.9793],
[7.0551]], dtype=torch.float64, grad_fn=<AddBackward0>)
"""
'파이썬 프로그래밍 > Numpy 딥러닝' 카테고리의 다른 글
6. 소프트맥스(softmax) 함수 탐구 (2) | 2022.01.07 |
---|---|
5. 로지스틱 회귀 구현하기(실습) (0) | 2022.01.04 |
4. 로지스틱 회귀 구현하기(이론) (2) | 2022.01.04 |
3. 입력 특성이 2개인 선형회귀 구현(이론, 실습) (0) | 2022.01.03 |
1. 선형회귀 구현하기(이론) (0) | 2022.01.03 |