wooing

선형 회귀(Linear Regression) 본문

인공지능/머신러닝

선형 회귀(Linear Regression)

우잉_ 2022. 10. 25. 15:57

회귀(Regression)란?

종속 변수(목표)와 하나 이상의 독립 변수(예측 변수)간의 상관관계를 예측하는 것.


 

선형 회귀(Linear Regression)

Concept

선형 회귀는 훈련 데이터셋을 관통하는 가장 합리적인 직선을 찾아내는 모델이다.


가설(Hypothesis)

분포를 가장 잘 나타내는 그래프, 선형 회귀에서는 직선의 방정식을 가설이라고 한다.

가설 H(x)에서 W는 가중치(기울기) b는 편향(y절편)이다.


비용 함수(Cost function)

 

위에 사진과 같이 데이터 셋이 분포해 있다고 가정해보자. 우리의 목표는 데이터 분포를 가장 잘 나타내는 직선을 찾는 것이다. 직선을 찾는 데 사용하는 방법으로는 데이터 값과 직선 값의 오차를 이용하는 것인데 이를  비용 함수라고 한다.

비용 함수 = 손실 함수 = 오차 함수 = 목적 함수

실제값과 예측값을 표현한 그래프

직선을 찾는데 사용하는 방법으로는 데이터 값과 직선의 값의 오차를 이용하는 것이다. 여기서 오차는 음수 값이 나올 수 있으므로 제곱하여 오차를 양수 값으로 바꾸어 준다.

예측값과 실제값, 오차를 나타낸 표
오차의 합을 구하는 수식
직선의 오차 평균을 구하는 수식

N개의 데이터셋에서의 오차를 구해 n으로 나누어주면 해당 직선의 오차 평균을 구할 수 있다.

Cost(W,b)는 직선의 오차 평균값이고, W와 b의 최적의 값을 구하는 것이 선형 회귀의 목표이다.

임의의 직선 방정식 H(x)는 위와 같이 나타낼 수 있다.


 

옵티마이저 - 경사 하강법(Gradient Descent)

앞에서 설명한 비용 함수를 최소화하는 W와 b를 찾는것을 옵티마이저, 최적화 알고리즘이라고 한다.

각 예측 직선의 기울기에 따른 비용 함수의 값에 대한 그래프

위의 그래프는 임의의 W값에 대한 cost의 값을 그래프로 나타낸 모습이다. 일반적으로 위와 같은 형태의 그래프를 그린다. 이때 가장 cost가 낮은 부분은 맨 아래 지점인 기울기가 0인 지점이 된다. 기울기를 구하는 방법으로는 미분하여 구할 수 있다.

경사 하강법은 이 그래프에서 접선의 기울기가 0인 지점을 찾기 위해 W값을 점점 조정하는 방법이다.

 

기울기가 음수일 때 : W값을 증가시킨다.

기울기가 양수일 때 : W값을 감소시킨다. 

여기서 알파(a)는 학습률(Learning rate)이라고 하고, W값을 변경할 때의 값을 크게 변경하는 역할을 한다.

학습률의 값이 너무 클때는 발산하여 값을 찾을 수 없고, 너무 작을 때는 학습 속도가 느려진다.

 

지금까지 설명에서 b는 배제시키고 설명했는데 실제 경사 하강법은 W와 b를 동시에 값을 찾아간다.


 

다중 선형 회귀(Multivariable Linear Regression)

앞에서 설명한 선형 회귀에서 x가 여러개일때 y를 예측하는 방법이다.

 

데이터가 위의 표와같이 있을때, 훈련 데이터를 선언하려면 아래와 같은 수식을 만들어야 한다.

이를 코드로 구현하려면 x와 w를 각각 선언해주어야 한다. 예시와 같은 상황에서는 변수가 3가지 이기 때문에 직접 선언하여 구현 할 수 있겠지만 2000개의 x가 존재할때는 상당히 비효율적이다.

 

이를 개선하기 위해 행렬곱을 사용한다.

행렬곱에 대한 설명(좌), 행렬곱을 활용하여 함수 H(x)표현(우)

오른쪽 그림과 같이 H(X)를 표현할 수 있고, 각각 벡터를 XW로 표현하면 H(X) = XW로 표현이 가능하다.

 

이를 행렬로 표현하면 아래의 그림과 같이 표현할 수 있다.

H(X) = XW

위의 식에 편향 b를 더해주면 H(X) = XW + B의 식이 완성된다.

H(X) = XW + B


단순 선형 회귀 코드:

import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim

torch.manual_seed(1)

#훈련 데이터셋
# 단순 선형 회귀
x_train = torch.FloatTensor(([1], [2], [3]))
y_train = torch.FloatTensor(([2], [4], [6]))

#가설 수립을 위한 변수 선언(0으로 초기화)
W = torch.zeros(1, requires_grad = True)
b = torch.zeros(1, requires_grad = True)

# 최적화 알고리즘 
#SGD: 경사하강법알고리즘
#가설의 파라미터와 학습률을 인자로 넣음
optimizer = optim.SGD([W, b], lr= 0.01)

#epoch : 학습 주기(2000번 학습)
nb_epochs = 2000
for epoch in range(nb_epochs + 1):

    #가설 수립
    hypothesis = x_train * W + b

    #비용함수
    cost = torch.mean((hypothesis - y_train) ** 2)

    # 기울기(미분값) 0으로 초기화, 초기화 하는 이유는 미분값이 누적되기 때문이다.
    optimizer.zero_grad()
    #backward - 자동미분 
    cost.backward()
    optimizer.step()
    
    if epoch % 100 == 0:
        print('Epoch {:4d}/{} W: {:.3f}, b: {:.3f} Cost: {:.6f}'.format(
            epoch, nb_epochs, W.item(), b.item(), cost.item() ))

print('Epoch {:4d}/{} W: {:.3f}, b: {:.3f} Cost: {:.6f}'.format(
            epoch, nb_epochs, W.item(), b.item(), cost.item()))

Result: 

Epoch    0/2000 W: 0.187, b: 0.080 Cost: 18.666666
Epoch  100/2000 W: 1.746, b: 0.578 Cost: 0.048171
Epoch  200/2000 W: 1.800, b: 0.454 Cost: 0.029767
Epoch  300/2000 W: 1.843, b: 0.357 Cost: 0.018394
Epoch  400/2000 W: 1.876, b: 0.281 Cost: 0.011366
Epoch  500/2000 W: 1.903, b: 0.221 Cost: 0.007024
Epoch  600/2000 W: 1.924, b: 0.174 Cost: 0.004340
Epoch  700/2000 W: 1.940, b: 0.136 Cost: 0.002682
Epoch  800/2000 W: 1.953, b: 0.107 Cost: 0.001657
Epoch  900/2000 W: 1.963, b: 0.084 Cost: 0.001024
Epoch 1000/2000 W: 1.971, b: 0.066 Cost: 0.000633
Epoch 1100/2000 W: 1.977, b: 0.052 Cost: 0.000391
Epoch 1200/2000 W: 1.982, b: 0.041 Cost: 0.000242
Epoch 1300/2000 W: 1.986, b: 0.032 Cost: 0.000149
Epoch 1400/2000 W: 1.989, b: 0.025 Cost: 0.000092
Epoch 1500/2000 W: 1.991, b: 0.020 Cost: 0.000057
Epoch 1600/2000 W: 1.993, b: 0.016 Cost: 0.000035
Epoch 1700/2000 W: 1.995, b: 0.012 Cost: 0.000022
Epoch 1800/2000 W: 1.996, b: 0.010 Cost: 0.000013
Epoch 1900/2000 W: 1.997, b: 0.008 Cost: 0.000008
Epoch 2000/2000 W: 1.997, b: 0.006 Cost: 0.000005
Epoch 2000/2000 W: 1.997, b: 0.006 Cost: 0.000005

다중 선형 회귀 코드

import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim

torch.manual_seed(1)

x_train  =  torch.FloatTensor([[73,  80,  75], 
                               [93,  88,  93], 
                               [89,  91,  80], 
                               [96,  98,  100],   
                               [73,  66,  70]])  
y_train  =  torch.FloatTensor([[152],  [185],  [180],  [196],  [142]])


#가설 수립을 위한 변수 선언(0으로 초기화)
W = torch.zeros((3,1), requires_grad = True)
b = torch.zeros(1, requires_grad = True)

# 최적화 알고리즘 
#SGD: 경사하강법알고리즘
#가설의 파라미터와 학습률을 인자로 넣음
optimizer = optim.SGD([W, b], lr= 1e-5)

#epoch : 학습 주기(2000번 학습)
nb_epochs = 50
for epoch in range(nb_epochs + 1):

    #가설 수립
    hypothesis = x_train.matmul(W) + b

    #비용함수
    cost = torch.mean((hypothesis - y_train) ** 2)

    # 기울기(미분값) 0으로 초기화, 초기화 하는 이유는 미분값이 누적되기 때문이다.
    optimizer.zero_grad()
    #backward - 자동미분 
    cost.backward()
    optimizer.step()
    
   # if epoch % 100 == 0:
    # print('Epoch {:4d}/{} W: {:.3f}, b: {:.3f} Cost: {:.6f}'.format(
    #     epoch, nb_epochs, W.item(), b.item(), cost.item() ))
    
    print('Epoch {:4d}/{} hypothesis: {} Cost: {:.6f}'.format(
        epoch, nb_epochs, hypothesis.squeeze().detach(), cost.item()
    ))

print('Epoch {:4d}/{} hypothesis: {} Cost: {:.6f}'.format(
        epoch, nb_epochs, hypothesis.squeeze().detach(), cost.item()
    ))

 

 

 

References:

https://wikidocs.net/53560

https://www.appier.com/ko-kr/blog/5-types-of-regression-analysis-and-when-to-use-them

https://bioinformaticsandme.tistory.com/130