이 글을 보기 전에 이해를 돕기 위해 전 글을 보고 오기를 추천한다.
필요 라이브러리 다운로드
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import numpy as np
import torch
import torch.nn as nn
기본적으로 pytorch를 사용하고, mpl_toolkits는 기본적으로 maplotlib에 딸려오기 때문에 따로 설치할 필요는 없다!
다변수 함수의 가짜 데이터 생성
def f(x, w, b):
return np.matmul(x, w) + b
이변수 함수 여서 x에는 2열의 행렬(matrix)이 들어간다. x는 input이며 3차원 좌표공간에서 x, y축에 점이다.
w는 xn1과 xn2에 곱해지는 weight을 갖고 있으며, 2행 1열의 형태를 가지며 행렬곱을 실행하면 scalar 값이 나온다. 선형대수학은 많은 데이터를 다루는 컴퓨터에서 필수적인 학문이므로 학습이 필수다!
b = 스칼라 값이다.
w = np.array([[3.],
[4.]])
b = 2.
x1 = np.linspace(-1, 1, 100)
x2 = np.linspace(-1, 1, 100)
X2, X1 = np.meshgrid(x1, x2) # X2: y coordinate, X1: x coordinate
X1.shape # (100, 100) [[-1., -1., -1. ... -1.],
# [-0.9, -0.9, -0.9 ... , -0.9],
# ....]]
x_true = np.concatenate([np.reshape(X1, (-1, 1)), np.reshape(X2, (-1,1))], axis=1) # concatenate by column
x_true.shape
y_true = f(x_true, w, b)
y_true.shape
Y = np.reshape(y_true, [100, 100])
n = 100
x_data = np.random.uniform(-1, 1, size=(n,2)) # np.random.uniform -> from uniform distribution
x_data.shape # (100,2)
y_data = f(x_data, w, b) + np.random.randn(n,1) # np.random.randn -> standard norm distribution
fig = plt.figure(figsize=(16,12)) # <Figure size 1600x1200 with 0 Axes>
for i in range(2):
ax = fig.add_subplot(1, 2, i+1, projection='3d')
surf = ax.plot_surface(X1, X2, Y, color='c')
scat = ax.plot3D(x_data[:,0].flatten(), x_data[:,1].flatten(), y_data.flatten(), 'r*', label='Data')
plt.legend()
ax.set_title("Ground Truth")
ax.set_xlim3d(-1, 1)
ax.set_ylim3d(-1, 1)
ax.set_zlim3d(-5, 9)
ax.view_init(20, 0) # to only last ax = fig.add_subplot(1,2,2, projection='3d)
plt.draw()
plt.show()
matplotlib 라이브러리에 3차원 그래프를 조작하는 mpl_toolkits를 활용하여 그래프를 다양한 각도로 돌려서 볼 수 있다.
numpy 라이브러리에 기본으로 내장되어 있는 함수들을 사용하여 만들었으니 numpy 페이지를 참고 바란다!
Pytorch에 맞는 type(tensor)로 변형
Pytorch는 계산 기본 단위가 tensor이다. 현재 데이터는 numpy 형태이니 tensor 형태로 바꿔주자.
# To use Pytorch computation, transoform numpy to tensor
type(x_data) # numpy.ndarray
type(y_data) # numpy.ndarray
tensor_x_data = torch.from_numpy(x_data.astype(np.float32))
tensor_y_data = torch.from_numpy(y_data.astype(np.float32))
type(tensor_x_data) # torch.Tensor
type(tensor_y_data) # torch.Tensor
tensor_x_data.shape # torch.Size([100, 2])
tensor_y_data.shape # torch.Size([100, 1])
주석을 단 부분은 내가 jupyter notebook 각 cell에서 출력하여 확인한 내용으로, 한 번쯤은 확인해 보기를 바란다!
Linear Regression을 실행할 Linear Model 생성
우리가 만든 데이터는 x1, x2, 즉 2개의 input을 받고 output으로 3차원 공간상에서 z값이 나온다. 데이터에 적합한 모델을 생성해 주자.
input_features = 2
output_features = 1
model = nn.Linear(input_features, output_features)
model # Linear(in_features=2, out_features=1, bias=True)
model.weight # Parameter containing: tensor([[ 0.1347, -0.1048]], requires_grad=True)
model.bias # Parameter containing: tensor([0.3500], requires_grad=True)
# requires_grad: allows automatic gradient computation in Pytorch
참고로 모델 생성 시에 자동으로 model의 weight (기울기)와 bias(절편)은 랜덤 한 값으로 설정된다.
Linear Regression을 위한 Loss Function과 Optimizer 정의
대중적이고 효과적인 Mean Square Error를 Loss Function으로, Stochastic Gradient Descent를 Optimizer로 사용한다.
# Set loss function and optimizer
# Let's use Mean Square Error for loss function
# and Stochastic Gradient Descent for optimizer.
learning_rate = 0.01
loss_method = nn.MSELoss()
model.parameters() # <generator object Module.parameters at 0x7f7898f020b0>
for i in model.parameters():
print(i)
'''
Parameter containing:
tensor([[ 0.5931, -0.4006]], requires_grad=True)
Parameter containing:
tensor([-0.5163], requires_grad=True)
'''
# optimizer detects those tensors and computes automatic gradient
optimizer = torch.optim.SGD(lr=learning_rate, params=model.parameters())
각각의 class나 function의 parameter는 전 글에서 다뤘으니 설명은 생략한다.
모든 설정이 끝났으니, 학습 시작!
# Now we set the loss function and optimizer
# Let's train the model
num_epochs = 3000
for epoch in range(num_epochs):
# Forward Propagation
tensor_y_pred = model(tensor_x_data)
# Loss Function
loss = loss_method(tensor_y_pred, tensor_y_data)
# Backward Propagation
loss.backward()
# Update weight and bias (model.parameters())
optimizer.step()
optimizer.zero_grad() # for every epoch, reset gradient to 0 for next epoch
# Print Progress
if (epoch + 1) % 200 == 0:
print(f"Epoch: {epoch+1}, Loss: {loss.item():5f}")
output:
Epoch: 200, Loss: 1.563034
Epoch: 400, Loss: 0.993226
Epoch: 600, Loss: 0.952213
Epoch: 800, Loss: 0.949169
Epoch: 1000, Loss: 0.948937
Epoch: 1200, Loss: 0.948919
Epoch: 1400, Loss: 0.948918
Epoch: 1600, Loss: 0.948917
Epoch: 1800, Loss: 0.948917
Epoch: 2000, Loss: 0.948917
Epoch: 2200, Loss: 0.948917
Epoch: 2400, Loss: 0.948917
Epoch: 2600, Loss: 0.948917
Epoch: 2800, Loss: 0.948917
Epoch: 3000, Loss: 0.948917
일정 epoch가 지나면, loss가 더 이상 줄어들지 않음을 볼 수 있다.
Ground Truth와 우리가 Linear Regression을 통해 예측한 Predicted 비교
tensor_x_true = torch.from_numpy(x_true.astype(np.float32))
model(tensor_x_true)
'''
'\ntensor([[-5.1627],\n [-5.0764],\n [-4.9902],\n ...,\n [ 8.8746],\n [ 8.9608],\n [ 9.0471]], grad_fn=<AddmmBackward0>)\n'
'''
model(tensor_x_true).detach() # makes tensor to not require grad.
'''
tensor([[-5.1627],
[-5.0764],
[-4.9902],
...,
[ 8.8746],
[ 8.9608],
[ 9.0471]], grad_fn=<AddmmBackward0>)
'''
tensor_y_pred = model(tensor_x_true).detach()
y_pred = tensor_y_pred.numpy()
# True parameters (weight and bias)
print(w[0,0], w[1,0], b, sep=' ')
# Trained parameters (weight and bias). Similar to above.
model.weight[0,0] # tensor(2.8339, grad_fn=<SelectBackward0>)
model.weight[0,1] # tensor(4.2710, grad_fn=<SelectBackward0>)
model.bias # tensor([1.9422], requires_grad=True)
# Compute Y_pred for surface plot
Y_pred = np.reshape(y_pred, (100,100))
# Plot the results
fig = plt.figure(figsize=(20,20))
for i in range(4):
ax = fig.add_subplot(2,2,i+1, projection='3d')
if i < 2:
ax.set_title('Ground Truth')
surf = ax.plot_surface(X1, X2, Y, color='c')
else:
ax.set_title("Linear Regression")
pred = ax.plot_surface(X1, X2, Y_pred, color='y')
scat = ax.plot3D(x_data[:,0].flatten(), x_data[:,1], y_data.flatten(), 'r*', label='Data')
plt.legend()
ax.set_xlim3d(-1,1)
ax.set_ylim3d(-1,1)
ax.set_zlim3d(-5,9)
if i % 2 == 1:
ax.view_init(20,0)
plt.draw()
plt.show()
만족스러운 정도로 예측이 되었음을 확인한다! 이상!