[파이토치 첫 걸음] Linear Regression

HEEJOON MOON·2021년 8월 25일
0

파이토치 첫 걸음

목록 보기
2/3

3.1 선형회귀(linear-regression)이란 무엇인가?

선형회귀(linear regression)이란 데이터를 가장 잘 표현하는 직선을 찾는 과정이라 할 수 있습니다. 독립변수가 1개인 경우에는 단순 선형회귀, 여러개인 경우에는 다중 선형회귀라고 합니다. 아래의 그래프에서 선형회귀란, y = w * x + b에서 data x, y를 가장 잘 표현하는 w와 b를 찾는 과정이라 할 수 있습니다 이때, w와 b를 각각 가중치(weights), 편차(bias)라 부릅니다.

3.2 손실 함수(Loss Function) 및 경사하강법(Gradient Descent)

여러가지 w와 b 중에서 데이터를 가장 잘 표현하는 값을 찾기 위해서는 일종의 척도를 통한 비교가 필요할 것입니다. 가장 많이 사용하는 척도 중에 하나는, MSE(Mean Squared Error)가 있습니다. ^y를 예측값(prediction)이라 하고, y를 실제 정답(label)라 하면, MSE는 다음과 같이 구할 수 있습니다.

각 데이터에 대해, 예측값과 실제 정답 간의 오차를 구한 후 평균을 내는 방식이 MSE입니다.

이제 척도를 통해 비교가 가능하게 되었습니다. 그럼 어떻게 하면 이상적인 w와 b를 구할 수 있을까요? 바로 예측값과 정답의 차이인 오차를 나타내는, 손실(비용) 함수(Loss function)을 최소화 할 때가 가장 이상적이라 할 수 있을 것입니다

위와 같이 MSE를 손실 함수로 사용하는 경우를 살펴보겠습니다 Error는 w에 대한 2차 함수 형태임을 알 수 있고, w에 대한 미분을 통해 값이 0인 지점을 찾으면 될 것입니다. 하지만 실제 딥러닝 모델에서는 데이터의 차원이 크기 때문에, 최적의 w를 구하기 위해서는 역행렬을 이용한 w = (xTx)-1xTy라는 식을 풀면 됩니다. 이와 같이 한번에 구하는 방식은 하지만 계산 복잡도가 O(n3)이고, 차원이 커질수록 더욱 증가하기 때문에 비효율적이라 할 수 있습니다

많은 딥러닝 모델들은 경사 하강법(Gradient Descent)방법을 사용합니다. 주어진 w에서 경사를 구하고, 이를 계속 update하여서 극소값을 찾아가는 방법입니다.

3.3 파이토치에서의 경사하강법

파이토치는 연산 그래프를 만들고 경사를 계산하는 방식을 사용합니다. 파이토치의 기본 데이터 단위는 텐서(Tensor)를 사용합니다. Tensor는 다차원 배열이라 할 수 있습니다.

텐서는 다음과 같이 만들 수 있습니다.

import torch
X = torch.Tensor(2, 3)
print(X)
실행결과 : tensor([[4.4413e+21, 3.0677e-41, 5.0447e-44],
        [0.0000e+00,        nan, 0.0000e+00]])

위의 코드의 경우 2 x 3 shape의 텐서가 생성되며, 임의의 난수가 들어가게 됩니다.
임의의 값이 아닌, 원하는 값을 대입할 수도 있습니다.

X = torch.Tensor([[1,2,3],[4,5,6]])
print(X)
실행결과 : tensor([[1., 2., 3.], [4., 5., 6.]])

torch.Tensor함수는 인수로 data, dtype, device, requires_grad를 받습니다. data는 배열, dtype은 데이터를 저장할 자료형이, device에는 텐서를 어느 기기에 올릴 것인지, requires_grad에는 텐서에 대한 기울기를 저장할 지 여부를 결정합니다.

import torch

x = torch.tensor(data=[2.0, 3.0], requires_grad=True)
y = x**2
z = 2*y + 3

target = torch.tensor([3.0, 4.0])
loss = torch.sum(torch.abs(z-target)) 
loss.backward()

print(x.grad, y.grad, z.grad)
실행결과 : tensor([ 8., 12.]) None None

z = 2 x x + 3이라는 식에서 x에 대한 기울기를 구하는 코드입니다.
x라는 텐서를 만들어 값을 저장하게 하고, torch.sum함수를 이용하여, z와 target의 오차를 구하고, loss.backward를 통해 leaf node(다른 변수를 통해 계산되지 않는 변수) x에 대한 기울기를 계산합니다 따라서 위의 결과로, y.grad, z.grad는 None이 나오게 됩니다.

다음은 선형회귀 분석 모델을 만들어 기울기를 계산하고, w, b를 업데이트하는 과정입니다

import torch
import torch.nn as nn # 신경망 모델을 불러옴
import torch.optim as optim # 최적화 방법을 불러옴
import torch.nn.init as init # 텐서에 초기값을 주기 위한 함수들을 불러옴

num_data = 1000
num_epoch = 500

x = init.uniform(torch.Tensor(num_data, 1), -10, 10) # -10~10의 원소를 갖는 [num_data, 1] 모양의 텐서를 생성
noise = init.normal_(torch.FloatTensor(num_data, 1), mean=0, std=1) # 가우시안 노이즈
y = 2*x + 3 
y_noise = 2*(x+noise) + 3 # noise가 추가된 y

model = nn.Linear(1, 1) # 파이토치에 구현된 선형회귀 모델
loss_func = nn.L1Loss() # L1 loss를 손실함수로 사용

Linear class는 들어오는 feature수, 결과의 feature수, 편차 사용 여부를 입력으로 받으며, 모델의 변수로는 가중치와 편차를 가지고 있습니다. 위의 경우 x,y는 데이터는 1개의 특성을 가진 데이터 1000개이므로, Linear의 입력 변수로 (1,1)를 갖게 됩니다.

L1Loss를 손실 함수로 사용하게 되는데, L1loss는 차이의 절댓값의 평균입니다

optimizer = optim.SGD(model.parameters(), lr=0.01) # SGD optimzer를 사용하며, 학습률을 0.01로 설정

SGD optimizer는 stochastic gradient descent의 약자로, 한 번에 들어오는 데이터의 수대로 경사하강법 알고리즘을 적용하는 최적화 함수입니다. model.parameter()를 통해 최적화할 변수인 w,b를 전달하고 있습니다.

label = y_noise
for i in range(num_epoch):
  optimizer.zero_grad() # 기울기 0으로 초기화
  output = model(x) # 결과 예측

  loss = loss_func(output, label) # loss 구하기
  loss.backward() # w, b에 대한 기울기 계산
  optimizer.step() # w, b에 위에서 구한 gradient와 leraning_rate를 곱한 값을 update

  if i % 10 == 0:
    print(loss.data)
  
  param_list = list(model.parameters())
  print(param_list[0].item(), param_list[1].item())

epoch는 데이터 전체를 한 번 사용하는 주기를 말합니다.
위의 코드는 num_epoch만큼 업데이트가 이뤄지는 과정을 나타내는 코드입니다.

아래 그림은 선형 모델의 학습 결과이며, 빨간점이 data, 검은 직선이 학습을 통해 얻은 직선입니다.

아래 그림은 loss값을 시각화 한 그림입니다. 학습이 될수록 loss값이 줄어드는 것을 알 수 있습니다.

Reference

  • 파이토치 첫 걸음 : 딥러닝 기초부터 RNN, 오토인코더, GAN 실전 기법까지
profile
Robotics, 3D-Vision, Deep-Learning에 관심이 있습니다

0개의 댓글