AI/PyTorch

[PyTorch] 파이토치(PyTorch)로 선형 회귀 모델 만들기

caramel-bottle 2024. 1. 8.

2024.01.06 - [AI/딥러닝] - [딥러닝] 파이토치(Pytorch)

 

[딥러닝] 파이토치(Pytorch)

1. 파이토치란? Tensorflow와 함께 머신러닝, 딥러닝에서 가장 널리 사용되는 프레임워크입니다. 초기에는 Torch라는 이름으로 Lua언어 기반으로 만들어졌으나, 파이썬 기반으로 변경한 것이 Pytorch입

caramelbottle.tistory.com

파이토치(PyTorch)의 기본 사용법을 배웠으니 이제 적용해봅시다.

 

적용 예제로 간단한 선형 회귀 모델을 만들어 보도록 하겠습니다.


1. 단항 선형 회귀

단항 선형 회귀란 한 개의 입력에 한 개의 출력이 나오는 구조입니다.

$$y = ax + b$$

기울기가 a이고 y절편이 b인 일차함수 형태랑 같습니다.


1-1. 라이브러리

필요한 라이브러리부터 가져옵시다.

import torch
import torch.nn as nn
import torch.optim as optim
import matplotlib.pyplot as plt

 

torch.nn의 nn은 neural network의 약자로, 신경망을 구성하고 학습시키기 위한 도구들을 포함하고 있습니다.

 

torch.optim은 최적화 알고리즘을 포함하는 모듈입니다.

 

matplotlib은 파이썬에서 데이터 시각화를 위해 사용하는 라이브러리입니다.


1-2. 랜덤 시드

랜덤 시드란 딥러닝에서 랜덤한 값을 사용할 때 항상 같은 값을 뽑기 위해 사용됩니다.

 

코드를 여러번 실행했을 때 난수값이 매번 바뀌지 않게 하고싶을 때 사용합니다.

torch.manual_seed(2024)

 


1-3. 데이터 생성

만든 모델의 학습에 사용할 데이터를 만들어줍니다.

 

이미 만들어진 데이터셋을 사용해도 괜찮습니다.

 

지금은 단항 선형 회귀 모델을 만들 것이기 때문에 입력 데이터 하나에 출력 데이터 하나로 만들어야 합니다.

x_train = torch.FloatTensor([[1], [2], [3]])
y_train = torch.FloatTensor([[2], [4], [6]])
print(x_train, x_train.shape)
print(y_train, y_train.shape)

output>>

tensor([[1.],
        [2.],
        [3.]]) torch.Size([3, 1])
tensor([[2.],
        [4.],
        [6.]]) torch.Size([3, 1])

라벨링한 이산데이터를 제외한 수치데이터는 보통 Float형식으로 처리합니다. 따라서 FloatTensor()를 사용한 것이지요.

 

 

만든 데이터를 가로축 x_train, 새로축 y_train인 점 그래프로 시각화해봅시다.

# 시각화
plt.figure(figsize=(6, 4))
plt.scatter(x_train, y_train)

output>>

기대되는 선형 모델을 미리 예측해봅시다.

$$y=2x$$

 


1-4. 선형 모델 만들기

우리가 만들 선형 모델은 기울기 a, 절편 b를 가지는 일차함수입니다.

$$y = ax + b$$

딥러닝에서 기울기는 보통 W를 사용합니다.

 

Weight의 W를 의미합니다.

 

b는 bias라고 합니다.

$$y = Wx + b$$

torch.nn의 Linear 레이어를 사용하면 위의 수식과 동일한 기능을 하는 모델을 만들 수 있습니다.

model = nn.Linear(1, 1)
# model = nn.Linear(1, 1, bias=False) -> bias를 사용하지 않는다

print(model)

output>>

Linear(in_features=1, out_features=1, bias=True)

1-5. 학습 전 모델

위에서 만든 모델은 아직 학습전이라 x_train을 입력해도 원하는 값인 y_train을 얻을 수 없습니다.

y_pred = model(x_train)
print(y_pred)
# y_train: [[2], [4], [6]]

output>>

tensor([[0.7260],
        [0.7894],
        [0.8528]], grad_fn=<AddmmBackward0>)

 

파라미터도 랜덤인 상태입니다.

print(list(model.parameters()))

output>>

[Parameter containing:
tensor([[0.0634]], requires_grad=True), Parameter containing:
tensor([0.6625], requires_grad=True)]

 

파라미터의 리스트에는 W, b에 대한 정보가 순서대로 들어있습니다.

$$W = 0.0634$$

$$b = 0.6625$$

학습전인 이 모델은 다음과 같습니다.

$$y=0.0634x+0.6625$$


1-6. MSE(Mean Squre Error)

MSE(Mean Squre Error)는 평균 제곱 오차로 회귀모델의 성능지표중 하나입니다.

 

2023.12.25 - [AI/머신러닝] - [머신러닝] MSE, MAE, RMSE

 

[머신러닝] MSE, MAE, RMSE

평가지표 선형회귀 모델의 성능을 평가하고 비교하기 위해 사용되는 평가지표. 평가지표 예시에 사용될 값. p = np.array([3, 4, 5]) # 예측값 act = np.array([1, 2, 3]) # 실제값 MSE(Mean Squared Error) 예측값과

caramelbottle.tistory.com

 

 

입력값 [[2], [4], [6]]에 해당하는 예측값은

tensor([[0.7260],
        [0.7894],
        [0.8528]], grad_fn=<AddmmBackward0>)

 

 

직접 계산하면

$$((2 - 0.7259)^2 + (4 - 0.7894)^2 + (6 - 0.8528)^2)/3 = 12.8083$$

 

파이썬에서 계산하면

print(((y_pred - y_train) ** 2).mean())

output>>

tensor(12.8082, grad_fn=<MeanBackward0>)

 

하지만 위 방법들을 매번 하긴 힘들겠죠?

 

torch.nn의 MSELoss() 는 평균 제곱 오차를 계산하는 손실함수 객체를 생성합니다.

loss = nn.MSELoss()(y_pred, y_train)

print(loss)

output>>

tensor(12.8082, grad_fn=<MseLossBackward0>)

 

MSELoss() 손실 함수는 학습하는 과정에서 사용됩니다.


1-7. SGD(Stochastic Gradient Descent)

경사하강법(Gradient Descent)은 함수의 기울기를 통해 함수의 최소값을 찾아가는 알고리즘입니다.

 

SGD는 경사하강법의 한 종류로 데이터를 랜덤하게 뽑아서 loss를 만드는 과정을 반복합니다.

 

torch.optim에서 SGD를 사용할 수 있습니다.

optimizer = optim.SGD(model.parameters(), lr=0.01)

 

SGD는 params를 필수 매개변수로 받습니다.

 

params에는 반복 가능한 객체나 미리 정의된 파라미터 리스트를 넣을 수 있습니다.

 

model.parameters()의 경우 W와 b를 포함하는 generator 객체로 반복 가능한 객체입니다.

 

lr은 학습률(Learning Rate)을 의미합니다.

 

학습률이란 한 번 움직이는 거리를 의미합니다.

 

경사하강법을 통해 함수의 최소값을 찾는 과정에서 최소값으로 잘 수렴하도록 학습률을 적절하게 설정해야합니다.

 

* 최적의 학습률

여러 학습률을 적용해 보며 최적의 결과를 만드는 학습률을 찾아낸 것입니다. 최적의 학습률은 데이터와 딥러닝 모델에 따라 다르므로 그때그때 찾아내야 합니다. 앞으로 배우게 될 딥러닝 프로젝트에서는 자동으로 최적의 학습률을 찾아 주는 최적화 알고리즘들을 사용합니다. - 모두의 딥러닝

1-8. 학습

이제 본격적으로 모델을 학습시켜봅시다.

 

학습에 기본적으로 사용되는 함수에 대해 간단하게 알아봅시다.

# gradient를 초기화
optimizer.zero_grad()

 

미분을 통해 얻은 기울기를 0으로 초기화합니다. 초기화를 해주지 않으면 이전에 계산된 기울기가 남아있어 새로운 값에 영향을 줄 수 있기 때문에 초기화를 해주어야합니다.

 

# 역전파: 비용 함수를 미분하여 gradient(기울기) 계산
loss.backward()

 

 

W와 b에 대한 기울기가 계산됩니다. 변하는 W와 b에 따라 기울기값도 계속해서 변합니다.

 

 

# W와 b를 업데이트
optimizer.step()

 

새로운 기울기를 구하기 위해 기존의 기울기와 Learning Rate를 통해 W와 b를 업데이트합니다.

 

학습을 돌리기 전에 W와 b의 변화를 확인해봅시다.

print(f'이전값 W: {list(model.parameters())[0].data}, b: {list(model.parameters())[1].data}')

# gradient를 초기화
optimizer.zero_grad()

# 역전파: 비용 함수를 미분하여 gradient(기울기) 계산
loss.backward()

# W와 b를 업데이트
optimizer.step()


print(f'현재값 W: {list(model.parameters())[0].data}, b: {list(model.parameters())[1].data}')

output>>

이전값 W: tensor([[0.0634]]), b: tensor([0.6625])
현재값 W: tensor([[0.2177]]), b: tensor([0.7267])

 

최적의 함수 최소값을 찾기 위해 단 한 걸음 걸어보았습니다.

 

이제 최소값을 찾아봅시다.

epochs = 1000

for epoch in range(epochs + 1):
    y_pred = model(x_train)
    loss = nn.MSELoss()(y_pred, y_train)
    optimizer.zero_grad()
    loss.backward()
    optimizer.step()

    if epoch % 100 == 0:
        print(f'Epoch: {epoch}/{epochs} Loss: {loss:.6f}')

output>>

Epoch: 0/1000 Loss: 10.171454
Epoch: 100/1000 Loss: 0.142044
Epoch: 200/1000 Loss: 0.087774
Epoch: 300/1000 Loss: 0.054239
Epoch: 400/1000 Loss: 0.033517
Epoch: 500/1000 Loss: 0.020711
Epoch: 600/1000 Loss: 0.012798
Epoch: 700/1000 Loss: 0.007909
Epoch: 800/1000 Loss: 0.004887
Epoch: 900/1000 Loss: 0.003020
Epoch: 1000/1000 Loss: 0.001866

 

epochs는 학습의 반복 횟수를 의미합니다.

 

print(f'최종값 W: {list(model.parameters())[0].data}, b: {list(model.parameters())[1].data}')

output>>

최종값 W: tensor([[1.9499]]), b: tensor([0.1138])

 

예측까지 바로 해보겠습니다.

x_test = torch.FloatTensor([[8]])
y_pred = model(x_test)
print(y_pred)

output>>

tensor([[15.7134]], grad_fn=<AddmmBackward0>)

 

W는 1.9499 b는 0.1138로 학습된 모델이 탄생했습니다.

$$y=1.9499x+0.1138$$

우리가 기대했던 식과 조금 유사하게 학습이 된 것 같습니다.

$$y=2x$$

 

입력 8에 대한 예상되는 출력 16에 조금 못 미치는 결과를 얻었습니다.

 

더 정확한 결과를 얻기 위해선 적절한 lr과 epochs를 설정해주어야합니다.


2. 다중 선형 회귀

다중 선형 회귀란 여러 개의 입력이 들어가서 한 개의 출력이 나오는 구조입니다.

$$y=a_1x + a_2x + a_3x + ... + a_nx + b$$

단항 선형 회귀 모델을 만들 때와 같은 방법으로 다중 선형 회귀 모델을 만들어봅시다. 


2-1. 데이터 생성

x_train = torch.FloatTensor([[73, 80, 75],
                             [93, 88, 93],
                             [89, 91, 90],
                             [96, 98, 100],
                             [73, 66, 70]])
y_train = torch.FloatTensor([[150], [190], [180], [200], [130]])

마찬가지로 FloatTensor를 사용하여 데이터를 만들어줍니다.

 

추가로 학습시킬 데이터에 실수가 하나라도 있다면 무조건 FloatTensor로 하는것이 좋습니다.

 

만약 실수로 사용될 데이터가 하나도 없다면 LongTensor를 사용할 수 있습니다.


2-2. 모델 선언

feature가 3개, label이 1개인 데이터입니다.

 

선형 모델에 3차원 입력에 1차원 출력을 준다고 선언하면 되겠네요

model = nn.Linear(3, 1)
print(model)

output>>

Linear(in_features=3, out_features=1, bias=True)

2-2. 최적화 알고리즘(SGD)

최적화 알고리즘으로 SGD(Stochastic Gradient Descent)를 사용하겠습니다.

 

* 최적화 알고리즘

최적화 알고리즘은 주어진 조건 또는 목적 함수를 최대화하거나 최소화하기 위해 사용되는 알고리즘 입니다.
최적화 알고리즘은 SGD말고도 Adagrad, Adam등 여러가지가 있습니다.
다양한 알고리즘은 앞으로 하나씩 천천히 알아보도록 하겠습니다.

 

model의 param들을 매개변수로 하고, 학습률(lr)은 0.00004로 하겠습니다.

optimizer = optim.SGD(model.parameters(), lr=0.00004)

2-3. 학습

# y = a1x + a2x + a3x + b

epochs = 5000

for epoch in range(epochs + 1):
    y_pred = model(x_train)
    loss = nn.MSELoss()(y_pred, y_train)
    optimizer.zero_grad()
    loss.backward()
    optimizer.step()

    if epoch % 500 == 0:
        print(f'Epoch: {epoch}/{epochs} Loss: {loss:.6f}')

output>>

Epoch: 0/5000 Loss: 54016.085938
Epoch: 500/5000 Loss: 30.944128
Epoch: 1000/5000 Loss: 29.184668
Epoch: 1500/5000 Loss: 27.645382
Epoch: 2000/5000 Loss: 26.267630
Epoch: 2500/5000 Loss: 25.023743
Epoch: 3000/5000 Loss: 23.896992
Epoch: 3500/5000 Loss: 22.875132
Epoch: 4000/5000 Loss: 21.947914
Epoch: 4500/5000 Loss: 21.106461
Epoch: 5000/5000 Loss: 20.342630

Loss가 조금 더 수렴할 것으로 보이지만 여기서 멈추고 성능 테스트를 해보도록 하겠습니다.


2-4. 예측

x_test = torch.FloatTensor([[92, 90, 89]])
y_pred = model(x_test)
print(y_pred)

output>>

tensor([[170.8808]], grad_fn=<AddmmBackward0>)

적절한 입력값을 설정해주고 예측을 했습니다.

 

 

임의의 값으로 진행한 예시이기 때문에 정확한 성능은 판단할 수 없지만 어느정도 근사한 값을 얻은 것 같습니다.


끝내며

단항 선형 회귀 모델에 이어 다중 선형 회귀 모델을 만들고 예측을 해보았습니다.

 

제목엔 딥러닝이라고 적어두었지만 사실 여기까진 딥러닝이라고 할 수 없습니다.

 

그래도 머신러닝에 사용되는 모델을 직접 만들며 PyTorch 사용법과 딥러닝에 사용되는 개념들을 익히셨을 것입니다.

 

다음번엔 이미 만들어진 데이터셋을 가지고 모델을 만들고 성능을 테스트하는 시간을 갖도록 하겠습니다.

 

그 이후 진짜 딥러닝에 발을 들여보도록 합시다!


참고

https://wikidocs.net/53560

 

03-01 선형 회귀(Linear Regression)

이번 챕터에서는 선형 회귀 이론에 대해서 이해하고, 파이토치(PyTorch)를 이용하여 선형 회귀 모델을 만들어보겠습니다. * **데이터에 대한 이해(Data Definitio…

wikidocs.net

 

댓글